diff --git a/data/layout.xml b/data/layout.xml
index de4d0c5..8d91e62 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -1039,6 +1039,7 @@ Here's a list of what's available in this release.
+
diff --git a/src/app_layout.cpp b/src/app_layout.cpp
index 815ae95..536cf82 100644
--- a/src/app_layout.cpp
+++ b/src/app_layout.cpp
@@ -251,6 +251,7 @@ void select_button(Node* main, T* button) {
main->find("btn-line")->set_color(color_button_normal);
main->find("btn-cam")->set_color(color_button_normal);
main->find("btn-grid")->set_color(color_button_normal);
+ main->find("btn-transform")->set_color(color_button_normal);
//main->find("btn-fill")->set_color(color_button_normal);
main->find("btn-mask-free")->set_color(color_button_normal);
main->find("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("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("btn-fill"))
{
button->on_click = [this, button](Node*) {
diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp
index 82d7e30..e9c4016 100644
--- a/src/app_shaders.cpp
+++ b/src/app_shaders.cpp
@@ -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(){"
diff --git a/src/brush.h b/src/brush.h
index 98f7d14..df73885 100644
--- a/src/brush.h
+++ b/src/brush.h
@@ -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;
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 2510ccf..73e9e75 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -18,6 +18,7 @@ std::vector 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);
@@ -535,13 +538,14 @@ void ui::Canvas::stroke_draw()
}
}
bool ui::Canvas::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)
+ glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id)
{
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);
@@ -561,13 +565,14 @@ bool ui::Canvas::point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ra
return false;
}
bool ui::Canvas::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)
+ glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id)
{
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,23 +584,8 @@ 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)
+ glm::vec3& out_origin, glm::vec3& out_dir)
{
auto clip_space = glm::vec2(loc.x, vp.w - loc.y - 1.f) / zw(vp) * 2.f - 1.f;
auto inv = glm::inverse(proj * camera);
@@ -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& vertices)
+void ui::Canvas::project2Dpoints(std::vector& vertices)
{
for (auto& p : vertices)
{
@@ -2000,7 +1999,92 @@ void ui::Canvas::project2Dpoints(std::vector& vertices)
}
}
-std::vector ui::Canvas::triangulate(const std::vector>& 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 ui::Canvas::face_to_shape2D(int plane_index)
+{
+ static std::array 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 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 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 ui::Canvas::triangulate_simple(const std::vector& vertices)
+{
+ std::vector ret;
+ std::vector points(vertices.size());
+ std::vector 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(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 ui::Canvas::triangulate(const std::vector& points)
+{
+ std::vector> tmp;
+ for (auto pt : points)
+ tmp.push_back(std::make_shared(pt.x, pt.y));
+ return triangulate(tmp);
+}
+
+std::vector ui::Canvas::triangulate(const std::vector>& points)
{
struct Segment
{
@@ -2056,8 +2140,9 @@ std::vector ui::Canvas::triangulate(const std::vectorb->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(is.x, is.y);
auto poly_root = std::make_shared();
@@ -2083,7 +2168,7 @@ std::vector ui::Canvas::triangulate(const std::vector vertices;
+ std::vector vertices;
for (auto poly : polys)
{
std::vector outline;
@@ -2095,7 +2180,7 @@ std::vector ui::Canvas::triangulate(const std::vectorend ? 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::Canvas::triangulate(const std::vectorGetTriangles();
for (auto t : tr)
{
- ui::Shape::vertex_t vertex;
+ vertex_t vertex;
for (int i = 0; i < 3; i++)
{
auto p = t->GetPoint(i);
diff --git a/src/canvas.h b/src/canvas.h
index 29f2d17..886813d 100644
--- a/src/canvas.h
+++ b/src/canvas.h
@@ -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 modes[];
std::vector* m_mode = nullptr;
@@ -223,16 +223,19 @@ public:
ui::Image thumbnail_read(std::string file_path);
void draw_objects(std::function);
void draw_objects(std::function, 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 triangulate(const std::vector>& points);
- void project2Dpoints(std::vector& vertices);
+ std::vector triangulate_simple(const std::vector& vertices);
+ std::vector triangulate(const std::vector>& points);
+ std::vector triangulate(const std::vector& points);
+ void project2Dpoints(std::vector& vertices);
+ glm::vec3 project2Dpoint(glm::vec2 pt);
+ std::vector face_to_shape2D(int plane_index);
};
class ActionStroke : public Action
diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp
index fdb2771..f150b28 100644
--- a/src/canvas_modes.cpp
+++ b/src/canvas_modes.cpp
@@ -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> points;
- for (int i = 0; i < (int)m_points2d.size() - 1; i++)
- points.emplace_back(std::make_shared(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(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 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> 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(v.pos.x, v.pos.y));
+ }
+ auto vert = canvas->triangulate_simple(poly);
+ m_shape.update_vertices(vert.data(), vert.size());
+ }
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/canvas_modes.h b/src/canvas_modes.h
index 131bb97..46e0972 100644
--- a/src/canvas_modes.h
+++ b/src/canvas_modes.h
@@ -103,7 +103,7 @@ class CanvasModeFill : public CanvasMode
{
ui::DynamicShape m_shape;
bool m_dragging = false;
- std::vector m_points;
+ std::vector m_points;
std::map 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 m_points;
+ std::vector m_points;
std::vector m_points2d;
std::map m_dirty_planes;
public:
@@ -130,7 +130,7 @@ class CanvasModeMaskLine : public CanvasMode
{
ui::DynamicShape m_shape;
bool m_dragging = false;
- std::vector m_points;
+ std::vector m_points;
std::vector m_points2d;
std::map 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 m_points;
+ std::vector m_points2d;
+ std::map 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;
+};
diff --git a/src/shape.cpp b/src/shape.cpp
index 51e1a63..2b4c8b7 100644
--- a/src/shape.cpp
+++ b/src/shape.cpp
@@ -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) };
}
diff --git a/src/shape.h b/src/shape.h
index b0fdaf9..1a9b8ac 100644
--- a/src/shape.h
+++ b/src/shape.h
@@ -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
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(rings * sectors * 6);
+ auto vertices = std::make_unique(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));
+ }
};
}
diff --git a/src/util.cpp b/src/util.cpp
index 7440965..d83ac5b 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -2,6 +2,18 @@
#include "log.h"
#include "util.h"
+template<>
+std::vector poly_remove_duplicate(const std::vector& v, const float tollerance)
+{
+ std::vector 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 poly_intersect(const std::vector& poly, const std::vector& clip)
+{
+ // implementing the Sutherland-Hodgman algorithm
+ // see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
+ std::vector ret = poly;
+ for (int i = 0; i < clip.size(); i++)
+ {
+ std::vector 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 poly_intersect(const std::vector& poly, const std::vector& clip)
+{
+ // implementing the Sutherland-Hodgman algorithm
+ // see https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
+ std::vector ret = poly;
+ for (int i = 0; i < clip.size(); i++)
+ {
+ std::vector 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 poly_clip_near(const std::vector& 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 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;
diff --git a/src/util.h b/src/util.h
index 989d385..f82d51a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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
+std::vector poly_remove_duplicate(const std::vector& v, const float tollerance = 0.001)
+{
+ std::vector 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 poly_remove_duplicate(const std::vector& 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 poly_intersect(const std::vector& poly, const std::vector& clip);
+std::vector poly_intersect(const std::vector& poly, const std::vector& clip);
+std::vector poly_clip_near(const std::vector& 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);