add transform mode and tollbar button, implement polygon clipping with uvs interpolation and cube faces projection with near plane clipping, add duplicate points removal template function, implement Spere mesh surface section creation.

This commit is contained in:
2018-11-12 18:19:03 +01:00
parent eb1c8d6b7a
commit 18067726b5
12 changed files with 512 additions and 81 deletions

View File

@@ -2,6 +2,18 @@
#include "log.h"
#include "util.h"
template<>
std::vector<vertex_t> poly_remove_duplicate<vertex_t>(const std::vector<vertex_t>& v, const float tollerance)
{
std::vector<vertex_t> ret;
for (size_t i = 0; i < v.size(); i++)
{
if (glm::distance2(v[i].pos, v[(i + 1) % v.size()].pos) > tollerance)
ret.push_back(v[i]);
}
return ret;
}
bool point_in_rect(const glm::vec2& p, const glm::vec4& r)
{
return p.x > r.x && p.x < r.x+r.z && p.y > r.y && p.y < r.y+r.w;
@@ -33,9 +45,25 @@ glm::vec4 rect_union(glm::vec4 a, glm::vec4 b)
return o;
}
bool ray_intersect(glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin,
glm::vec3 plane_normal, glm::vec3 plane_tangent, glm::vec3& out_hit, float& out_t)
{
float den = glm::dot(ray_dir, plane_normal);
if (den == 0)
return false; // no intersection
float num = glm::dot(plane_origin - ray_origin, plane_normal);
out_t = num / den;
if (out_t > 0)
out_hit = ray_origin + ray_dir * out_t;
else
// negative intersection
return false;
return true;
};
// see: https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
bool segments_intersect(const glm::vec2& p0a, const glm::vec2& p0b,
const glm::vec2& p1a, const glm::vec2& p1b, glm::vec2& out_pt)
const glm::vec2& p1a, const glm::vec2& p1b, glm::vec2& out_pt, glm::vec2& out_hit_uv)
{
auto cross2d = [](const glm::vec2& v, const glm::vec2& w)
{ return (v.x * w.y) - (v.y * w.x); };
@@ -50,16 +78,151 @@ bool segments_intersect(const glm::vec2& p0a, const glm::vec2& p0b,
out_pt = xy(is) + zw(is) * 0.5f;
return glm::all(glm::greaterThan(zw(is), glm::vec2(0, 0)));
}
float t = cross2d(q - p, s) / den;
float u = cross2d(q - p, r) / den;
if (t >= 0 && t <= 1 && u >= 0 && u <= 1)
out_hit_uv.x = cross2d(q - p, s) / den;
out_hit_uv.y = cross2d(q - p, r) / den;
out_pt = p + out_hit_uv.x * r;
if (out_hit_uv.x >= 0 && out_hit_uv.x <= 1 && out_hit_uv.y >= 0 && out_hit_uv.y <= 1)
{
out_pt = p + t * r;
return true;
}
return false;
}
// return true if the point p in the right halfspace of the ab line
// computed using the 2d cross product
bool point_side(glm::vec2 a, glm::vec2 b, glm::vec2 p)
{
return (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x) >= 0.f;
}
// intersect 2 closed polygons
// a is a convex polygon
// a and b are a list of non repeating points
// returns the resulting intersection polygon points
std::vector<vertex_t> poly_intersect(const std::vector<vertex_t>& poly, const std::vector<glm::vec2>& clip)
{
// implementing the Sutherland-Hodgman algorithm
// see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
std::vector<vertex_t> ret = poly;
for (int i = 0; i < clip.size(); i++)
{
std::vector<vertex_t> tmp;
glm::vec2 edge[2] = { clip[i], clip[(i + 1) % clip.size()] };
for (int j = 0; j < ret.size(); j++)
{
vertex_t s[2] = { ret[j], ret[(j + 1) % ret.size()] };
bool side0 = point_side(edge[0], edge[1], s[0].pos);
bool side1 = point_side(edge[0], edge[1], s[1].pos);
if (side0 != side1) // intersecting
{
glm::vec2 pt;
glm::vec2 hit_uv;
segments_intersect(edge[0], edge[1], s[0].pos, s[1].pos, pt, hit_uv);
vertex_t v;
v.pos = glm::vec4(pt, 0, 1);
v.uvs = glm::lerp(s[0].uvs, s[1].uvs, hit_uv.y);
v.uvs2 = glm::lerp(s[0].uvs2, s[1].uvs2, hit_uv.y);
if (side0) // outgoing
{
tmp.push_back(s[0]);
tmp.push_back(v);
}
else // ingoing
{
tmp.push_back(v);
}
}
else if (side0 && side1)
{
tmp.push_back(s[0]);
}
}
ret = std::move(tmp);
}
return poly_remove_duplicate(ret);
}
std::vector<glm::vec2> poly_intersect(const std::vector<glm::vec2>& poly, const std::vector<glm::vec2>& clip)
{
// implementing the Sutherland-Hodgman algorithm
// see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
std::vector<glm::vec2> ret = poly;
for (int i = 0; i < clip.size(); i++)
{
std::vector<glm::vec2> tmp;
glm::vec2 edge[2] = { clip[i], clip[(i + 1) % clip.size()] };
for (int j = 0; j < ret.size(); j++)
{
glm::vec2 s[2] = { ret[j], ret[(j + 1) % ret.size()] };
bool side0 = point_side(edge[0], edge[1], s[0]);
bool side1 = point_side(edge[0], edge[1], s[1]);
if (side0 != side1) // intersecting
{
glm::vec2 pt;
glm::vec2 hit_uv;
segments_intersect(edge[0], edge[1], s[0], s[1], pt, hit_uv);
if (side0) // outgoing
{
tmp.push_back(s[0]);
tmp.push_back(pt);
}
else // ingoing
{
tmp.push_back(pt);
}
}
else if (side0 && side1)
{
tmp.push_back(s[0]);
}
}
ret = std::move(tmp);
}
return poly_remove_duplicate(ret);
}
// clip the polygon to the near clip plane
// poly is the polygon in camera coordinates
std::vector<glm::vec3> poly_clip_near(const std::vector<glm::vec3>& poly, float near_plane_distance)
{
// implementing the Sutherland-Hodgman algorithm in 3D
// see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
auto o = glm::vec3(0, 0, -near_plane_distance);
auto n = glm::vec3(0, 0, -1);
auto t = glm::vec3(0, 1, 0);
std::vector<glm::vec3> ret;
for (int j = 0; j < poly.size(); j++)
{
glm::vec3 s[2] = { poly[j], poly[(j + 1) % poly.size()] };
bool side0 = glm::dot(n, s[0] - o) >= 0.f;
bool side1 = glm::dot(n, s[1] - o) >= 0.f;
if (side0 != side1) // intersecting
{
glm::vec3 pt;
float hit_t;
if (!ray_intersect(s[0], glm::normalize(s[1] - s[0]), o, n, t, pt, hit_t))
{
LOG("error ray_intersect");
}
if (side0) // outgoing
{
ret.push_back(s[0]);
ret.push_back(pt);
}
else // ingoing
{
ret.push_back(pt);
}
}
else if (side0 && side1)
{
ret.push_back(s[0]);
}
}
return poly_remove_duplicate(ret);
}
glm::vec4 rand_color()
{
float r = (rand() % 256) / 256.f;