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

@@ -1039,6 +1039,7 @@ Here's a list of what's available in this release.
<image path="data/ui/line.png" width="100%" height="100%" align="center" justify="flex-end"/>
</button-custom>
<button id="btn-grid" width="50" height="100%" margin="0 0 0 5" text="Grid"/>
<button id="btn-transform" width="80" height="100%" margin="0 0 0 5" text="Transform"/>
<!-- <button id="btn-fill" width="50" height="100%" margin="0 0 0 0" text="Fill"/> -->
<button-custom id="btn-mask-free" width="50" height="100%" margin="0 0 0 5" thickness="1" border-color="0 0 0 1" pad="2">
<image path="data/ui/sel-free.png" width="100%" height="100%" align="center" justify="flex-end"/>

View File

@@ -251,6 +251,7 @@ void select_button(Node* main, T* button) {
main->find<NodeButtonCustom>("btn-line")->set_color(color_button_normal);
main->find<NodeButton>("btn-cam")->set_color(color_button_normal);
main->find<NodeButton>("btn-grid")->set_color(color_button_normal);
main->find<NodeButton>("btn-transform")->set_color(color_button_normal);
//main->find<NodeButton>("btn-fill")->set_color(color_button_normal);
main->find<NodeButtonCustom>("btn-mask-free")->set_color(color_button_normal);
main->find<NodeButtonCustom>("btn-mask-line")->set_color(color_button_normal);
@@ -312,6 +313,13 @@ void App::init_toolbar_draw()
Canvas::set_mode(Canvas::kCanvasMode::Grid);
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-transform"))
{
button->on_click = [this, button](Node*) {
select_button(layout[main_id], button);
Canvas::set_mode(Canvas::kCanvasMode::Transform);
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-fill"))
{
button->on_click = [this, button](Node*) {

View File

@@ -27,7 +27,6 @@ void App::initShaders()
"}";
static const char* shader_uv_f =
SHADER_VERSION
"uniform sampler2D tex;"
"in mediump vec3 uv;"
"out mediump vec4 frag;"
"void main(){"

View File

@@ -62,7 +62,6 @@ class BrushMesh
public:
GLuint buffers[3]{ 0 };
GLuint vao{ 0 };
struct vertex_t { glm::vec4 pos; glm::vec2 uvs; };
struct instance_t { glm::mat4 mvp; float flow; };
int loc_flow = 0;
int loc_mvp = 0;

View File

@@ -18,6 +18,7 @@ std::vector<CanvasMode*> ui::Canvas::modes[] = {
{ new CanvasModeLine, new CanvasModeBasicCamera },
{ new CanvasModeCamera, new CanvasModeBasicCamera },
{ new CanvasModeGrid, new CanvasModeBasicCamera },
{ new CanvasModeTransform, new CanvasModeBasicCamera },
{ new CanvasModeFill, new CanvasModeBasicCamera },
{ new CanvasModeMaskFree, new CanvasModeBasicCamera },
{ new CanvasModeMaskLine, new CanvasModeBasicCamera },
@@ -46,6 +47,7 @@ glm::vec3 ui::Canvas::m_plane_tangent[6] = {
{0, 0,-1}, // top
{0, 0, 1}, // bottom
};
// only rotation
glm::mat4 ui::Canvas::m_plane_transform[6] = {
glm::lookAt(glm::vec3(), { 0, 0,-1}, {0, 1, 0}), // front
glm::lookAt(glm::vec3(), {-1, 0, 0}, {0, 1, 0}), // right
@@ -440,7 +442,8 @@ void ui::Canvas::stroke_draw()
}
glm::vec3 hit;
if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit))
float hit_t;
if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t))
{
glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]);
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
@@ -539,9 +542,10 @@ bool ui::Canvas::point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ra
{
point_unproject(loc, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir);
glm::vec3 hit;
float hit_t;
for (int i = 0; i < 6; i++)
{
if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit))
if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t))
{
glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]);
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
@@ -566,8 +570,9 @@ bool ui::Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::ve
point_unproject(loc, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir);
glm::vec3 hit;
glm::vec2 fb_pos;
float hit_t;
if (ray_intersect(ray_origin, ray_dir, m_plane_origin[plane_id],
m_plane_normal[plane_id], m_plane_tangent[plane_id], hit))
m_plane_normal[plane_id], m_plane_tangent[plane_id], hit, hit_t))
{
glm::mat4 plane_camera = glm::lookAt(m_plane_origin[plane_id], m_plane_normal[plane_id], m_plane_tangent[plane_id]);
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
@@ -579,21 +584,6 @@ bool ui::Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::ve
}
return false;
}
bool ui::Canvas::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 den = glm::dot(ray_dir, plane_normal);
if (den == 0)
return false; // no intersection
float num = glm::dot(plane_origin - ray_origin, plane_normal);
float t = num / den;
if (t > 0)
out_hit = ray_origin + ray_dir * t;
else
// negative intersection
return false;
return true;
};
void ui::Canvas::point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj,
glm::vec3& out_origin, glm::vec3& out_dir)
{
@@ -604,6 +594,15 @@ void ui::Canvas::point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera,
out_origin = xyz(wp0 / wp0.w);
out_dir = glm::normalize(xyz(wp1 / wp1.w) - out_origin);
};
void ui::Canvas::point_unproject(glm::vec2 loc, glm::vec3& out_origin, glm::vec3& out_dir)
{
auto clip_space = glm::vec2(loc.x, m_vp.w - loc.y - 1.f) / zw(m_vp) * 2.f - 1.f;
auto inv = glm::inverse(m_proj * m_mv);
auto wp0 = inv * glm::vec4(clip_space, 0, 1);
auto wp1 = inv * glm::vec4(clip_space, .5, 1);
out_origin = xyz(wp0 / wp0.w);
out_dir = glm::normalize(xyz(wp1 / wp1.w) - out_origin);
};
void ui::Canvas::stroke_commit()
{
if (!m_dirty || m_layers.empty())
@@ -1014,7 +1013,7 @@ void ui::Canvas::import_equirectangular_thread(std::string file_path)
if (img.width == img.height / 6)
{
Texture2D tex;
static GLint indices[] = { 5, 1, 4, 0, 2, 3 };
static GLint indices[] = { 5, 0, 4, 1, 2, 3 };
static GLint formats[] = { GL_RED, GL_RG, GL_RGB, GL_RGBA };
static GLint iformats[] = { GL_R8, GL_RG8, GL_RGB8, GL_RGBA8 };
tex.create(img.width, img.width, iformats[img.comp - 1], formats[img.comp - 1]);
@@ -1028,7 +1027,7 @@ void ui::Canvas::import_equirectangular_thread(std::string file_path)
tex.bind();
ShaderManager::use(ui::kShader::Texture);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(1, -1, 1)));
ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(-1, -1, 1)));
plane.draw_fill();
tex.unbind();
m_sampler.unbind();
@@ -1048,7 +1047,7 @@ void ui::Canvas::import_equirectangular_thread(std::string file_path)
ShaderManager::use(ui::kShader::Texture);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera *
glm::eulerAngleY(glm::radians(-90.f)) * glm::scale(glm::vec3(1, -1, 1)));
glm::eulerAngleY(glm::radians(180.f)) * glm::scale(glm::vec3(1, -1, 1)));
sphere.draw_fill();
tex.unbind();
m_sampler.unbind();
@@ -1988,7 +1987,7 @@ void ui::Canvas::draw_objects(std::function<void(const glm::mat4& camera, const
draw_objects(observer, m_layers[m_current_layer_idx]);
}
void ui::Canvas::project2Dpoints(std::vector<ui::Shape::vertex_t>& vertices)
void ui::Canvas::project2Dpoints(std::vector<vertex_t>& vertices)
{
for (auto& p : vertices)
{
@@ -2000,7 +1999,92 @@ void ui::Canvas::project2Dpoints(std::vector<ui::Shape::vertex_t>& vertices)
}
}
std::vector<ui::Shape::vertex_t> ui::Canvas::triangulate(const std::vector<std::shared_ptr<p2t::Point>>& points)
glm::vec3 ui::Canvas::project2Dpoint(glm::vec2 pt)
{
glm::vec3 ro, rd, hit_o, hit_d;
glm::vec2 hit_fb;
int plane_id;
if (point_trace(pt, ro, rd, hit_o, hit_fb, hit_d, plane_id))
return glm::vec4(hit_o, 1);
return glm::vec3(0);
}
// return the 2d shape of the faces based on the current camera
// this can be used for screen space shapes clipping
std::vector<glm::vec2> ui::Canvas::face_to_shape2D(int plane_index)
{
static std::array<glm::vec4, 4> corners{
glm::vec4(-1.f, +1.f, -1.f, 1.f), // A top-left
glm::vec4(+1.f, +1.f, -1.f, 1.f), // B top-right
glm::vec4(+1.f, -1.f, -1.f, 1.f), // C bottom-right
glm::vec4(-1.f, -1.f, -1.f, 1.f), // D bottom-left
};
// compute points in camera space
std::vector<glm::vec3> pt_cam;
for (auto c : corners)
{
auto pt_world = m_plane_transform[plane_index] * c;
pt_cam.push_back(m_mv * pt_world);
}
// clip at near plane
pt_cam = poly_clip_near(pt_cam, 0.01);
// compute windows space
std::vector<glm::vec2> points;
for (auto p : pt_cam)
{
auto pt_clip = m_proj * glm::vec4(p, 1);
pt_clip = pt_clip / pt_clip.w;
glm::vec2 pt_screen = (glm::vec2(pt_clip) * glm::vec2(1,-1) * 0.5f + 0.5f) * zw(m_vp);
//pt_screen.y = m_vp.w - pt_screen.y - 1;
points.push_back(pt_screen);
}
return points;
}
std::vector<vertex_t> ui::Canvas::triangulate_simple(const std::vector<vertex_t>& vertices)
{
std::vector<vertex_t> ret;
std::vector<p2t::Point> points(vertices.size());
std::vector<p2t::Point*> points_ptr(vertices.size());
for (size_t i = 0; i < vertices.size(); i++)
{
points[i] = { vertices[i].pos.x, vertices[i].pos.y };
points_ptr[i] = &points[i];
}
auto cdt = std::make_unique<p2t::CDT>(points_ptr);
cdt->Triangulate();
auto tr = cdt->GetTriangles();
for (auto t : tr)
{
vertex_t vertex;
for (int i = 0; i < 3; i++)
{
auto p = t->GetPoint(i);
auto index = std::distance(points.data(), p);
vertex.pos = glm::vec4(p->x, p->y, 0, 1);
vertex.uvs = vertices[index].uvs;
ret.push_back(vertex);
}
}
return ret;
}
std::vector<vertex_t> ui::Canvas::triangulate(const std::vector<glm::vec2>& points)
{
std::vector<std::shared_ptr<p2t::Point>> tmp;
for (auto pt : points)
tmp.push_back(std::make_shared<p2t::Point>(pt.x, pt.y));
return triangulate(tmp);
}
std::vector<vertex_t> ui::Canvas::triangulate(const std::vector<std::shared_ptr<p2t::Point>>& points)
{
struct Segment
{
@@ -2056,8 +2140,9 @@ std::vector<ui::Shape::vertex_t> ui::Canvas::triangulate(const std::vector<std::
glm::vec2 s0b(node->b->x, node->b->y);
glm::vec2 s1a(other->a->x, other->a->y);
glm::vec2 s1b(other->b->x, other->b->y);
glm::vec2 hit_uv;
glm::vec2 is;
if (segments_intersect(s0a, s0b, s1a, s1b, is))
if (segments_intersect(s0a, s0b, s1a, s1b, is, hit_uv))
{
auto p = std::make_shared<p2t::Point>(is.x, is.y);
auto poly_root = std::make_shared<Segment>();
@@ -2083,7 +2168,7 @@ std::vector<ui::Shape::vertex_t> ui::Canvas::triangulate(const std::vector<std::
}
}
std::vector<ui::Shape::vertex_t> vertices;
std::vector<vertex_t> vertices;
for (auto poly : polys)
{
std::vector<p2t::Point*> outline;
@@ -2095,7 +2180,7 @@ std::vector<ui::Shape::vertex_t> ui::Canvas::triangulate(const std::vector<std::
node = node->end ? nullptr : node->next;
current->next = nullptr;
}
LOG("poly %zu", outline.size());
//LOG("poly %zu", outline.size());
if (outline.size() > 2)
{
@@ -2104,7 +2189,7 @@ std::vector<ui::Shape::vertex_t> ui::Canvas::triangulate(const std::vector<std::
auto tr = cdt->GetTriangles();
for (auto t : tr)
{
ui::Shape::vertex_t vertex;
vertex_t vertex;
for (int i = 0; i < 3; i++)
{
auto p = t->GetPoint(i);

View File

@@ -162,7 +162,7 @@ public:
Brush m_current_brush;
enum class kCanvasMode { Draw, Erase, Line, Camera, Grid, Fill, MaskFree, MaskLine, COUNT };
enum class kCanvasMode { Draw, Erase, Line, Camera, Grid, Transform, Fill, MaskFree, MaskLine, COUNT };
kCanvasMode m_state{ kCanvasMode::Draw };
static std::vector<CanvasMode*> modes[];
std::vector<CanvasMode*>* m_mode = nullptr;
@@ -223,16 +223,19 @@ public:
ui::Image thumbnail_read(std::string file_path);
void draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>);
void draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)>, Layer& layer);
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);
void point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj,
glm::vec3 &out_origin, glm::vec3 &out_dir);
void point_unproject(glm::vec2 loc, glm::vec3 &out_origin, glm::vec3 &out_dir);
bool point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id);
bool point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id);
std::vector<ui::Shape::vertex_t> triangulate(const std::vector<std::shared_ptr<p2t::Point>>& points);
void project2Dpoints(std::vector<ui::Shape::vertex_t>& vertices);
std::vector<vertex_t> triangulate_simple(const std::vector<vertex_t>& vertices);
std::vector<vertex_t> triangulate(const std::vector<std::shared_ptr<p2t::Point>>& points);
std::vector<vertex_t> triangulate(const std::vector<glm::vec2>& points);
void project2Dpoints(std::vector<vertex_t>& vertices);
glm::vec3 project2Dpoint(glm::vec2 pt);
std::vector<glm::vec2> face_to_shape2D(int plane_index);
};
class ActionStroke : public Action

View File

@@ -471,7 +471,7 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
oldpos = loc;
oldvec = {1.f, 0.f};
acc = 0;
ui::Shape::vertex_t vert;
vertex_t vert;
vert.pos = glm::vec4(loc, 0, 1);
m_points2d.push_back(loc);
m_points2d.push_back(loc);
@@ -486,17 +486,10 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
m_dragging = false;
if (m_points2d.size() > 3)
{
std::vector<std::shared_ptr<p2t::Point>> points;
for (int i = 0; i < (int)m_points2d.size() - 1; i++)
points.emplace_back(std::make_shared<p2t::Point>(m_points2d[i].x, m_points2d[i].y));
auto v = canvas->triangulate(points);
canvas->project2Dpoints(v);
LOG("%d points", (int)v.size());
m_shape.update_vertices(v.data(), (int)v.size());
if (!m_points.empty())
{
//m_points2d = poly_intersect(poly_remove_duplicate(m_points2d), canvas->face_to_shape2D(0));
auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
//glEnable(GL_BLEND);
ui::ShaderManager::use(ui::kShader::Color);
@@ -504,14 +497,16 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, {1, 1, 1, 1});
m_shape.draw_fill();
};
// use m_shape to render the mask polygon
auto v = canvas->triangulate(poly_remove_duplicate(m_points2d));
canvas->project2Dpoints(v);
m_shape.update_vertices(v.data(), (int)v.size());
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), canvas->m_smask);
//m_points.clear();
// close the path
m_points.push_back(m_points[m_points.size() - 2]);
// close the path and reset m_shape to contour rendering
m_points.push_back(m_points.back());
m_points.push_back(m_points.front());
canvas->project2Dpoints(m_points);
// reset m_shape to contour rendering
m_shape.update_vertices(m_points.data(), (int)m_points.size());
}
}
@@ -524,7 +519,7 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
if (m_dragging)
{
auto v = loc-oldpos;
auto v = loc - oldpos;
float len = glm::length(v);
if (len > 5)
{
@@ -532,7 +527,7 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
m_points2d.back() = loc;
v = glm::normalize(v);
float d = 1-glm::dot(v, oldvec);
float d = 1.f - glm::dot(v, oldvec);
acc += d;
oldpos = loc;
oldvec = v;
@@ -542,7 +537,7 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
acc = 0;
m_points2d.push_back(loc);
ui::Shape::vertex_t vert;
vertex_t vert;
vert.pos = glm::vec4(loc, 0, 1);
m_points.push_back(vert);
m_points.push_back(vert);
@@ -573,6 +568,8 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
void CanvasModeMaskFree::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
bool depth = glIsEnabled(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
if (m_points.size() > 3)
{
if (m_dragging)
@@ -591,6 +588,7 @@ void CanvasModeMaskFree::on_Draw(const glm::mat4& ortho, const glm::mat4& proj,
m_shape.draw_stroke();
}
}
if (depth) glEnable(GL_DEPTH_TEST);
}
@@ -662,7 +660,7 @@ void CanvasModeMaskLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
node->mouse_capture();
m_dragging = true;
m_points2d.push_back(loc);
ui::Shape::vertex_t vert;
vertex_t vert;
vert.pos = glm::vec4(loc, 0, 1);
m_points.push_back(vert);
m_shape.update_vertices(m_points.data(), (int)m_points.size());
@@ -771,7 +769,7 @@ void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
if (canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0))
{
m_dirty_planes[plane_id]++;
ui::Shape::vertex_t v;
vertex_t v;
v.pos = glm::vec4(hit_o, 1);
v.uvs = glm::vec2(0);
if (m_points.size() < 3)
@@ -801,7 +799,7 @@ void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
int plane_id;
if (m_dragging && canvas->point_trace(loc, ro, rd, hit_o, fb_pos, hit_d, plane_id))
{
ui::Shape::vertex_t v;
vertex_t v;
v.pos = glm::vec4(hit_o, 1);
v.uvs = glm::vec2(0);
m_points.back() = v;
@@ -838,3 +836,99 @@ void CanvasModeFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, cons
m_dragging ? m_shape.draw_fill() : m_shape.draw_stroke();
}
}
////////////////////////////////////////////////////////////////////
void CanvasModeTransform::init()
{
m_sphere.create(1.f, glm::radians(-10.f), glm::radians(10.f), glm::radians(-10.f), glm::radians(10.f), 1.f);
m_circle.create<16>(1.f);
m_shape.create();
}
void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
{
static float theta = 0;
theta += glm::radians(1.f);
bool depth = glIsEnabled(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
auto uv = m_center_point_uv;
float x = glm::sin(uv.x) * glm::cos(uv.y);
float y = glm::sin(uv.x) * glm::sin(uv.y);
float z = glm::cos(uv.x);
auto p = glm::vec3(x, y, z);
//auto m = glm::inverse(glm::lookAt({ 0, 0, 0 }, m_center_pos, { 0, 1, 0 }));
//auto m = static_cast<CanvasModeMaskFree*>(canvas->modes[(int)ui::Canvas::kCanvasMode::MaskFree][0]);
auto rot = glm::inverse(glm::lookAt(glm::vec3(0), m_origin, { 0, 1, 0 }));
ui::ShaderManager::use(ui::kShader::Color);
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera * rot);
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 });
m_sphere.draw_fill();
for (auto pt : m_points2d)
{
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho * glm::translate(glm::vec3(pt, 0)) * glm::scale(glm::vec3(5.f)));
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 1, 1, 1 });
m_circle.draw_fill();
}
ui::ShaderManager::use(ui::kShader::UVs);
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho);
m_shape.draw_fill();
if (depth) glEnable(GL_DEPTH_TEST);
}
void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
switch (me->m_type)
{
case kEventType::MouseDownR:
//m_origin = canvas->project2Dpoint(loc);
break;
case kEventType::MouseMove:
case kEventType::MouseUpL:
case kEventType::MouseDownL:
{
auto m = glm::lookAt(glm::vec3(0), m_origin, { 0, 1, 0 });
m_center_point = m * glm::vec4(canvas->project2Dpoint(loc), 1);
float r = glm::length(m_center_point);
float lat = glm::acos(m_center_point.z / r);
float lon = glm::atan(m_center_point.y, m_center_point.x);
m_center_point_uv = { lat, lon };
//LOG("pt lat-lon %f %f", lat, lon);
auto face = canvas->face_to_shape2D(0);
if (face.size() > 2)
{
std::vector<vertex_t> quad{
vertex_t{{200, 200}, {0, 0}},
vertex_t{{200, 400}, {0, 1}},
vertex_t{{400, 400}, {1, 1}},
vertex_t{{400, 200}, {1, 0}},
};
auto poly = poly_intersect(quad, face);
m_points2d.clear();
if (!poly.empty())
{
//LOG("size %d", (int)is.size());
std::vector<std::shared_ptr<p2t::Point>> points;
for (auto& v : poly)
{
v.pos.y = canvas->m_box.w - v.pos.y - 1;
m_points2d.push_back(v.pos);
points.push_back(std::make_shared<p2t::Point>(v.pos.x, v.pos.y));
}
auto vert = canvas->triangulate_simple(poly);
m_shape.update_vertices(vert.data(), vert.size());
}
}
}
break;
default:
break;
}
}

View File

@@ -103,7 +103,7 @@ class CanvasModeFill : public CanvasMode
{
ui::DynamicShape m_shape;
bool m_dragging = false;
std::vector<ui::Shape::vertex_t> m_points;
std::vector<vertex_t> m_points;
std::map<int, int> m_dirty_planes;
public:
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
@@ -116,7 +116,7 @@ class CanvasModeMaskFree : public CanvasMode
{
ui::DynamicShape m_shape;
bool m_dragging = false;
std::vector<ui::Shape::vertex_t> m_points;
std::vector<vertex_t> m_points;
std::vector<glm::vec2> m_points2d;
std::map<int, int> m_dirty_planes;
public:
@@ -130,7 +130,7 @@ class CanvasModeMaskLine : public CanvasMode
{
ui::DynamicShape m_shape;
bool m_dragging = false;
std::vector<ui::Shape::vertex_t> m_points;
std::vector<vertex_t> m_points;
std::vector<glm::vec2> m_points2d;
std::map<int, int> m_dirty_planes;
bool m_active_tool = false;
@@ -141,3 +141,26 @@ public:
virtual void enter() override;
virtual void leave() override;
};
class CanvasModeTransform : public CanvasMode
{
ui::DynamicShape m_shape;
ui::Sphere m_sphere;
ui::Circle m_circle;
glm::vec3 m_origin{0, 0, 1 };
glm::vec3 m_center_point;
glm::vec2 m_center_point_uv;
int m_counter = 0;
bool m_dragging = false;
std::vector<vertex_t> m_points;
std::vector<glm::vec2> m_points2d;
std::map<int, int> m_dirty_planes;
bool m_active_tool = false;
public:
CanvasModeTransform() = default;
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;;
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;;
virtual void init() override;;
//virtual void enter() override;
//virtual void leave() override;
};

View File

@@ -185,7 +185,7 @@ bool RectShape::create(float w, float h)
return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices));
}
void Plane::create_impl(float w, float h, int div, GLushort *idx, Shape::vertex_t *vertices)
void Plane::create_impl(float w, float h, int div, GLushort *idx, vertex_t *vertices)
{
count[0] = div * div * 6;
count[1] = (div + 1) * 4;
@@ -317,8 +317,8 @@ void ui::Plane::update_vertices(const glm::vec4* data, const glm::vec2* uvs, con
{
static vertex_t vertices[4];
glm::vec2 mid;
segments_intersect(xy(data[0]), xy(data[2]), xy(data[1]), xy(data[3]), mid);
glm::vec2 mid, hit_uv;
segments_intersect(xy(data[0]), xy(data[2]), xy(data[1]), xy(data[3]), mid, hit_uv);
static float d[4];
for (int i = 0; i < 4; i++)
d[i] = glm::distance(xy(data[i]), mid);
@@ -442,7 +442,7 @@ void Circle::create_impl(float radius_out, float radius_in, int div, GLushort* i
//}
}
void Rounded::create_impl(float w, float h, float r, int div, GLushort* idx, GLushort* idx_tmp, Shape::vertex_t* vertices)
void Rounded::create_impl(float w, float h, float r, int div, GLushort* idx, GLushort* idx_tmp, vertex_t* vertices)
{
count[0] = (10 + div * 4) * 3;
count[1] = (4 + div * 4) * 2;
@@ -454,7 +454,7 @@ void Rounded::create_impl(float w, float h, float r, int div, GLushort* idx, GLu
float X[] = { -w/2, -w/2+r, w/2-r, w/2 };
float Y[] = { -h/2, -h/2+r, h/2-r, h/2 };
auto V = [&](int x, int y) -> Shape::vertex_t {
auto V = [&](int x, int y) -> vertex_t {
return { glm::vec4(X[x], Y[y], 0, 1), glm::vec2(X[x]/w, Y[y]/h) + 0.5f };
};
*vertices++ = V(1,0);
@@ -522,7 +522,7 @@ void Rounded::create_impl(float w, float h, float r, int div, GLushort* idx, GLu
*idx2++ = 2;
}
void Slice9::create_impl(float w, float h, float r, float tr, GLushort *idx, Shape::vertex_t *vertices)
void Slice9::create_impl(float w, float h, float r, float tr, GLushort *idx, vertex_t *vertices)
{
count[0] = 3 * 3 * 6;
count[1] = 4 * 2;
@@ -533,7 +533,7 @@ void Slice9::create_impl(float w, float h, float r, float tr, GLushort *idx, Sha
float Y[] = { -h/2, -h/2+r, h/2-r, h/2 };
float T[] = { 0, tr, 1-tr, 1 };
auto V = [&](int x, int y) -> Shape::vertex_t {
auto V = [&](int x, int y) -> vertex_t {
return { glm::vec4(X[x], Y[y], 0, 1), glm::vec2(T[x], T[y]) };
};
@@ -565,22 +565,31 @@ void Slice9::create_impl(float w, float h, float r, float tr, GLushort *idx, Sha
*idx++ = 12; // D
*idx++ = 0; // A
}
void Sphere::create_impl(int rings, int sectors, float radius, GLushort *idx, Shape::vertex_t *vertices)
void Sphere::create_impl(int rings, int sectors, float radius,
float lat_start, float lat_end, float lon_start, float lon_end,
GLushort *idx, vertex_t *vertices)
{
count[0] = rings * sectors * 6;
count[1] = 0;
ioff[0] = (GLvoid*)0;
ioff[1] = (GLvoid*)0;
lat_start += M_PI_2;
lat_end += M_PI_2;
lon_start -= M_PI_2;
lon_end -= M_PI_2;
float lat_size = (lat_end - lat_start);
float lon_size = (lon_end - lon_start);
float const R = 1.f / (float)(rings-1);
float const S = 1.f / (float)(sectors-1);
int r, s;
auto v = vertices;
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = (float)sin( -M_PI_2 + M_PI * r * R );
float const x = (float)cos(2*M_PI * s * S) * (float)sin( M_PI * r * R );
float const z = (float)sin(2*M_PI * s * S) * (float)sin( M_PI * r * R );
float lat = lat_start + r * lat_size * R;
float lon = lon_start + s * lon_size * S;
float const y = (float)sin(lat - M_PI_2);
float const x = (float)cos(lon) * (float)sin(lat);
float const z = (float)sin(lon) * (float)sin(lat);
*v++ = { glm::vec4(x, y, z, 1) * radius, glm::vec2(s*S, r*R) };
}

View File

@@ -22,7 +22,6 @@ protected:
bool use_idx = true;
bool create_buffers_imp(GLvoid* idx, GLvoid* vertices, int isize, int vsize);
public:
struct vertex_t { glm::vec4 pos; glm::vec2 uvs; glm::vec2 uvs2; };
bool create_buffers(GLushort* idx, GLvoid* vertices, int isize, int vsize);
bool create_buffers(GLuint* idx, GLvoid* vertices, int isize, int vsize);
bool create_buffers(GLvoid* vertices, int vsize);
@@ -198,16 +197,28 @@ public:
class Sphere : public Shape
{
void create_impl(int rings, int sectors, float radius, GLushort* idx, vertex_t* vertices);
void create_impl(int rings, int sectors, float radius,
float lat_start, float lat_end, float lon_start, float lon_end,
GLushort* idx, vertex_t* vertices);
public:
template<int rings, int sectors>
bool create(float radius)
{
static GLushort idx[rings * sectors * 6];
static vertex_t vertices[rings * sectors];
create_impl(rings, sectors, radius, idx, vertices);
create_impl(rings, sectors, radius, -M_PI_2, M_PI_2, 0, M_PI * 2.0, idx, vertices);
return create_buffers(idx, vertices, sizeof(idx), sizeof(vertices));
}
bool create(float radius, float lat_start, float lat_end,
float lon_start, float lon_end, float polys_per_angle)
{
int rings = glm::ceil(glm::degrees(lat_end - lat_start) * polys_per_angle);
int sectors = glm::ceil(glm::degrees(lon_end - lon_start) * polys_per_angle);
auto idx = std::make_unique<GLushort[]>(rings * sectors * 6);
auto vertices = std::make_unique<vertex_t[]>(rings * sectors);
create_impl(rings, sectors, radius, lat_start, lat_end, lon_start, lon_end, idx.get(), vertices.get());
return create_buffers(idx.get(), vertices.get(), rings * sectors * 6 * sizeof(GLushort), rings * sectors * sizeof(vertex_t));
}
};
}

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;

View File

@@ -6,6 +6,21 @@
#define GL(stmt) stmt
#endif
struct vertex_t
{
glm::vec4 pos;
glm::vec2 uvs;
glm::vec2 uvs2;
vertex_t() : pos(0), uvs(0), uvs2(0) {}
vertex_t(glm::vec2 p) : pos(p, 0, 1), uvs(0), uvs2(0) {}
vertex_t(glm::vec2 p, glm::vec2 uv) : pos(p, 0, 1), uvs(uv), uvs2(0) {}
vertex_t(glm::vec3 p) : pos(p, 1), uvs(0), uvs2(0) {}
vertex_t(glm::vec3 p, glm::vec2 uv) : pos(p, 1), uvs(uv), uvs2(0) {}
vertex_t(glm::vec4 p) : pos(p), uvs(0), uvs2(0) {}
vertex_t(glm::vec4 p, glm::vec2 uv) : pos(p), uvs(uv), uvs2(0) {}
vertex_t(glm::vec4 p, glm::vec2 uv, glm::vec2 uv2) : pos(p), uvs(uv), uvs2(uv2) {}
};
uint16_t constexpr const_hash(const char* input)
{
return *input ?
@@ -13,11 +28,32 @@ uint16_t constexpr const_hash(const char* input)
5381;
}
template<class T>
std::vector<T> poly_remove_duplicate(const std::vector<T>& v, const float tollerance = 0.001)
{
std::vector<T> ret;
for (size_t i = 0; i < v.size(); i++)
{
if (glm::distance2(v[i], v[(i + 1) % v.size()]) > tollerance)
ret.push_back(v[i]);
}
return ret;
}
template<>
std::vector<vertex_t> poly_remove_duplicate<vertex_t>(const std::vector<vertex_t>& v, const float tollerance);
bool point_in_rect(const glm::vec2& point, const glm::vec4& rect);
glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b);
glm::vec4 rect_union(glm::vec4 a, glm::vec4 b);
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);
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);
bool point_side(glm::vec2 a, glm::vec2 b, glm::vec2 p);
std::vector<vertex_t> poly_intersect(const std::vector<vertex_t>& poly, const std::vector<glm::vec2>& clip);
std::vector<glm::vec2> poly_intersect(const std::vector<glm::vec2>& poly, const std::vector<glm::vec2>& clip);
std::vector<glm::vec3> poly_clip_near(const std::vector<glm::vec3>& poly, float near_plane_distance);
glm::vec4 rand_color();
glm::vec3 convert_hsv2rgb(const glm::vec3 c);
glm::vec3 convert_rgb2hsv(const glm::vec3 c);