diff --git a/ui/zenoedit/viewport/cameracontrol.cpp b/ui/zenoedit/viewport/cameracontrol.cpp index 1a0627291e..4a47b46e5f 100644 --- a/ui/zenoedit/viewport/cameracontrol.cpp +++ b/ui/zenoedit/viewport/cameracontrol.cpp @@ -123,7 +123,7 @@ void CameraControl::fakeMousePressEvent(QMouseEvent *event) } } if (event->buttons() & Qt::MiddleButton) { - m_lastPos = event->pos(); + m_lastMidButtonPos = event->pos(); } else if (event->buttons() & Qt::LeftButton) { m_boundRectStartPos = event->pos(); // check if clicked a selected object @@ -266,11 +266,11 @@ void CameraControl::fakeMouseMoveEvent(QMouseEvent *event) { auto session = m_zenovis->getSession(); auto scene = session->get_scene(); + float xpos = event->x(), ypos = event->y(); if (event->buttons() & Qt::MiddleButton) { float ratio = QApplication::desktop()->devicePixelRatio(); - float xpos = event->x(), ypos = event->y(); - float dx = xpos - m_lastPos.x(), dy = ypos - m_lastPos.y(); + float dx = xpos - m_lastMidButtonPos.x(), dy = ypos - m_lastMidButtonPos.y(); dx *= ratio / m_res[0]; dy *= ratio / m_res[1]; bool shift_pressed = event->modifiers() & Qt::ShiftModifier; @@ -295,20 +295,25 @@ void CameraControl::fakeMouseMoveEvent(QMouseEvent *event) setTheta(getTheta() - dy * M_PI); setPhi(getPhi() + dx * M_PI); } - m_lastPos = QPointF(xpos, ypos); + m_lastMidButtonPos = QPointF(xpos, ypos); } else if (event->buttons() & Qt::LeftButton) { if (m_transformer) { if (m_transformer->isTransforming()) { auto dir = screenToWorldRay(event->pos().x() / res().x(), event->pos().y() / res().y()); auto camera_pos = realPos(); - auto x = event->x() * 1.0f; - auto y = event->y() * 1.0f; - x = (2 * x / res().x()) - 1; - y = 1 - (2 * y / res().y()); - auto mouse_pos = glm::vec2(x, y); + + // mouse pos + auto mouse_pos = glm::vec2(xpos, ypos); + mouse_pos[0] = (2 * mouse_pos[0] / res().x()) - 1; + mouse_pos[1] = 1 - (2 * mouse_pos[1] / res().y()); + // mouse start + auto mouse_start = glm::vec2(m_boundRectStartPos.x(), m_boundRectStartPos.y()); + mouse_start[0] = (2 * mouse_start[0] / res().x()) - 1; + mouse_start[1] = 1 - (2 * mouse_start[1] / res().y()); + auto vp = scene->camera->m_proj * scene->camera->m_view; - m_transformer->transform(camera_pos, mouse_pos, dir, scene->camera->m_lodfront, vp); + m_transformer->transform(camera_pos, dir, mouse_start, mouse_pos, scene->camera->m_lodfront, vp); zenoApp->getMainWindow()->updateViewport(); } else { float min_x = std::min((float)m_boundRectStartPos.x(), (float)event->x()) / m_res.x(); diff --git a/ui/zenoedit/viewport/cameracontrol.h b/ui/zenoedit/viewport/cameracontrol.h index 792df1c492..64a5ecd726 100644 --- a/ui/zenoedit/viewport/cameracontrol.h +++ b/ui/zenoedit/viewport/cameracontrol.h @@ -55,7 +55,7 @@ class CameraControl : public QObject void resizeTransformHandler(int dir); private: - QPointF m_lastPos; + QPointF m_lastMidButtonPos; QPoint m_boundRectStartPos; QVector2D m_res; QSet m_pressedKeys; diff --git a/ui/zenoedit/viewportinteraction/transform.cpp b/ui/zenoedit/viewportinteraction/transform.cpp index 82ba3d0ddb..222ed3faa5 100644 --- a/ui/zenoedit/viewportinteraction/transform.cpp +++ b/ui/zenoedit/viewportinteraction/transform.cpp @@ -159,7 +159,7 @@ bool FakeTransformer::clickedAnyHandler(QVector3D ori, QVector3D dir, glm::vec3 return m_operation_mode != zenovis::INTERACT_NONE; } -void FakeTransformer::transform(QVector3D camera_pos, glm::vec2 mouse_pos, QVector3D ray_dir, glm::vec3 front, glm::mat4 vp) { +void FakeTransformer::transform(QVector3D camera_pos, QVector3D ray_dir, glm::vec2 mouse_start, glm::vec2 mouse_pos, glm::vec3 front, glm::mat4 vp) { if (m_operation == NONE) return; auto pZenovis = m_viewport->getZenoVis(); @@ -256,9 +256,17 @@ void FakeTransformer::transform(QVector3D camera_pos, glm::vec2 mouse_pos, QVect } } else if (m_operation == SCALE) { + // make a circle, center is m_objects_center + // when mouse press, get the circle's radius, r = len(m_objects_center - mouse_start) + // when mouse move, get the len from center to mouse_pos, d = len(m_objects_center - mouse_pos) + // so the scale is d / r auto t_ctr = vp * glm::vec4(m_objects_center, 1.0f); glm::vec2 ctr = t_ctr / t_ctr[3]; - auto scale_size = glm::length(ctr - mouse_pos); + auto len_ctr_start = glm::length(ctr - mouse_start); + if (len_ctr_start < 0.001) return; + + auto len_ctr_pos = glm::length(ctr - mouse_pos); + auto scale_size = len_ctr_pos / len_ctr_start; if (m_operation_mode == zenovis::INTERACT_X) { scale(scale_size, {1, 0, 0}); } @@ -635,8 +643,8 @@ void FakeTransformer::translate(glm::vec3 start, glm::vec3 end, glm::vec3 axis) void FakeTransformer::scale(float scale_size, vec3i axis) { glm::vec3 scale(1.0f); - for (int i=0; i<3; i++) - if (axis[i] == 1) scale[i] = fmax(scale_size * 3, 0.1); + for (int i = 0; i < 3; i++) + if (axis[i] == 1) scale[i] = std::max(scale_size, 0.1f); m_scale = scale; doTransform(); } diff --git a/ui/zenoedit/viewportinteraction/transform.h b/ui/zenoedit/viewportinteraction/transform.h index 60de12923c..b5cb237146 100644 --- a/ui/zenoedit/viewportinteraction/transform.h +++ b/ui/zenoedit/viewportinteraction/transform.h @@ -32,7 +32,7 @@ class FakeTransformer { void removeObject(const std::unordered_set& names); bool calcTransformStart(glm::vec3 ori, glm::vec3 dir, glm::vec3 front); bool clickedAnyHandler(QVector3D ori, QVector3D dir, glm::vec3 front); - void transform(QVector3D camera_pos, glm::vec2 mouse_pos, QVector3D ray_dir, glm::vec3 front, glm::mat4 vp); + void transform(QVector3D camera_pos, QVector3D ray_dir, glm::vec2 mouse_start, glm::vec2 mouse_pos, glm::vec3 front, glm::mat4 vp); void startTransform(); void endTransform(bool moved); bool isTransforming() const; diff --git a/zenovis/include/zenovis/bate/GraphicHandlerUtils.h b/zenovis/include/zenovis/bate/GraphicHandlerUtils.h index 4226aacbce..253eb08231 100644 --- a/zenovis/include/zenovis/bate/GraphicHandlerUtils.h +++ b/zenovis/include/zenovis/bate/GraphicHandlerUtils.h @@ -116,10 +116,11 @@ void drawSquare(vec3f pos, vec3f a, vec3f b, vec3f color, float size, std::uniqu } void drawCircle(vec3f pos, vec3f a, vec3f b, vec3f color, float size, std::unique_ptr &vbo) { + constexpr int point_num = 100; + constexpr double step = 1.0 / point_num; std::vector mem; + mem.reserve(point_num * 2); - double point_num = 100; - double step = 1.0 / point_num; for (double t = 0; t < 1.0; t += step) { double theta = 2.0 * PI * t; auto p = pos + size * cos(theta) * a + size * sin(theta) * b; @@ -133,13 +134,43 @@ void drawCircle(vec3f pos, vec3f a, vec3f b, vec3f color, float size, std::uniqu vbo->attribute(0, sizeof(float) * 0, sizeof(float) * 6, GL_FLOAT, 3); vbo->attribute(1, sizeof(float) * 3, sizeof(float) * 6, GL_FLOAT, 3); - CHECK_GL(glDrawArrays(GL_LINE_STRIP, 0, vertex_count)); + CHECK_GL(glDrawArrays(GL_LINE_LOOP, 0, vertex_count)); vbo->disable_attribute(0); vbo->disable_attribute(1); vbo->unbind(); } +void drawCircle(vec3f pos, vec3f a, vec3f b, vec3f color, float size, float width, std::unique_ptr &vbo) { + constexpr int point_num = 100; + constexpr double step = 1.0 / point_num; + std::vector mem; + mem.reserve(point_num * 4); + + float inner_size = size - width / 2.0; + float outer_size = size + width / 2.0; + for (double t = 0; t < 1.0 + step; t += step) { + double theta = 2.0 * PI * t; + auto p1 = pos + inner_size * cos(theta) * a + inner_size * sin(theta) * b; + auto p2 = pos + outer_size * cos(theta) * a + outer_size * sin(theta) * b; + mem.push_back(p1); + mem.push_back(color); + mem.push_back(p2); + mem.push_back(color); + } + + auto vertex_count = mem.size() / 2; + vbo->bind_data(mem.data(), mem.size() * sizeof(mem[0])); + + vbo->attribute(0, sizeof(float) * 0, sizeof(float) * 6, GL_FLOAT, 3); + vbo->attribute(1, sizeof(float) * 3, sizeof(float) * 6, GL_FLOAT, 3); + + CHECK_GL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count)); + + vbo->disable_attribute(0); + vbo->disable_attribute(1); + vbo->unbind(); +} void drawCube(vec3f pos, vec3f a, vec3f b, vec3f color, float size, std::unique_ptr &vbo) { std::vector mem; diff --git a/zenovis/src/bate/GraphicScaleHandler.cpp b/zenovis/src/bate/GraphicScaleHandler.cpp index 403e958e75..6168f15972 100644 --- a/zenovis/src/bate/GraphicScaleHandler.cpp +++ b/zenovis/src/bate/GraphicScaleHandler.cpp @@ -74,6 +74,12 @@ struct ScaleHandler final : IGraphicHandler { std::unique_ptr lines_ebo; size_t lines_count; + static constexpr float cube_factor = 0.08f; + static constexpr float axis_factor = 0.8f; + static constexpr float square_factor = 0.1f; + static constexpr float icircle_factor = 0.2f; // inner circle + static constexpr float ocircle_factor = 1.0f; // outer circle + explicit ScaleHandler(Scene *scene_, vec3f ¢er_, float scale_) : scene(scene_), center(center_), scale(scale_), mode(INTERACT_NONE) { vbo = std::make_unique(GL_ARRAY_BUFFER); @@ -97,33 +103,50 @@ struct ScaleHandler final : IGraphicHandler { auto y_axis = vec3f(0, 1, 0); auto z_axis = vec3f(0, 0, 1); + auto axis_size = bound * axis_factor; + auto cube_size = bound * cube_factor; + auto square_size = bound * square_factor; + auto icircle_size = bound * icircle_factor; + auto ocircle_size = bound * ocircle_factor; + // x axis if (mode == INTERACT_NONE || mode == INTERACT_X) { - drawAxis(center, x_axis, r, bound, vbo); - drawCube(center + bound * x_axis, y_axis, z_axis, r, bound * 0.08f, vbo); + drawAxis(center, x_axis, r, axis_size, vbo); + drawCube(center + axis_size * x_axis, y_axis, z_axis, r, cube_size, vbo); } if (mode == INTERACT_NONE || mode == INTERACT_Y) { - drawAxis(center, y_axis, g, bound, vbo); - drawCube(center + bound * y_axis, z_axis, x_axis, g, bound * 0.08f, vbo); + drawAxis(center, y_axis, g, axis_size, vbo); + drawCube(center + axis_size * y_axis, z_axis, x_axis, g, cube_size, vbo); } if (mode == INTERACT_NONE || mode == INTERACT_Z) { - drawAxis(center, z_axis, b, bound, vbo); - drawCube(center + bound * z_axis, x_axis, y_axis, b, bound * 0.08f, vbo); + drawAxis(center, z_axis, b, axis_size, vbo); + drawCube(center + axis_size * z_axis, x_axis, y_axis, b, cube_size, vbo); } if (mode == INTERACT_NONE || mode == INTERACT_YZ) - drawSquare(center, y_axis, z_axis, {0.6, 0.2, 0.2}, bound * 0.1f, vbo); + drawSquare(center, y_axis, z_axis, {0.6, 0.2, 0.2}, square_size, vbo); if (mode == INTERACT_NONE || mode == INTERACT_XZ) - drawSquare(center, z_axis, x_axis, {0.2, 0.6, 0.2}, bound * 0.1f, vbo); + drawSquare(center, z_axis, x_axis, {0.2, 0.6, 0.2}, square_size, vbo); if (mode == INTERACT_NONE || mode == INTERACT_XY) - drawSquare(center, x_axis, y_axis, {0.2, 0.2, 0.6}, bound * 0.1f, vbo); - - if (mode == INTERACT_NONE || mode == INTERACT_XYZ) - drawCube(center, y_axis, z_axis, {0.51, 0.17, 0.85}, bound * 0.08f, vbo); + drawSquare(center, x_axis, y_axis, {0.2, 0.2, 0.6}, square_size, vbo); + + const auto& view = scene->camera->m_view; + if (mode == INTERACT_NONE || mode == INTERACT_XYZ) { + // http://www.opengl-tutorial.org/cn/intermediate-tutorials/billboards-particles/billboards/ + // always face camera + // This is equivalent to mlutiplying (1,0,0) and (0,1,0) by inverse(ViewMatrix). + // ViewMatrix is orthogonal (it was made this way), + // so its inverse is also its transpose, + // and transposing a matrix is "free" (inversing is slooow) + auto right_world = vec3f(view[0][0], view[1][0], view[2][0]); + auto up_world = vec3f(view[0][1], view[1][1], view[2][1]); + drawCircle(center, right_world, up_world, { 1.0, 1.0, 1.0 }, icircle_size, bound * 0.01f, vbo); + drawCircle(center, right_world, up_world, { 1.0, 1.0, 1.0 }, ocircle_size, bound * 0.01f, vbo); + } } virtual int collisionTest(glm::vec3 ori, glm::vec3 dir) override { @@ -131,19 +154,13 @@ struct ScaleHandler final : IGraphicHandler { auto y_axis = glm::vec3(0, 1, 0); auto z_axis = glm::vec3(0, 0, 1); - auto model_matrix =glm::translate(zeno::vec_to_other(center)); - - // xyz handler - float cube_size = bound * 0.08f; float t; - auto xyz_handler_max = cube_size * glm::vec3(1.0f); - auto xyz_handler_min = -xyz_handler_max; - if (rayIntersectOBB(ori, dir, xyz_handler_min, xyz_handler_max, model_matrix, t)) { - mode = INTERACT_XYZ; - return INTERACT_XYZ; - } + auto model_matrix = glm::translate(zeno::vec_to_other(center)); + const auto& view = scene->camera->m_view; + + float t; // axis handlers - float axis_handler_l = bound * 1.1f; + float axis_handler_l = bound * axis_factor; float axis_handler_w = bound * 0.1f; // x handler @@ -172,7 +189,7 @@ struct ScaleHandler final : IGraphicHandler { // plane handlers float square_ctr_offset = bound; - float square_size = bound * 0.1f; + float square_size = bound * square_factor; // xy handler auto xy_base = glm::normalize(x_axis + y_axis); @@ -201,6 +218,19 @@ struct ScaleHandler final : IGraphicHandler { return INTERACT_XZ; } + // xyz handler + // the other handlers are in the range of xyz handler, so check xyz at last + float i_radius = bound * icircle_factor; + float o_radius = bound * ocircle_factor; + float thickness = bound * 0.1f; + auto right_world = vec3f(view[0][0], view[1][0], view[2][0]); + auto up_world = vec3f(view[0][1], view[1][1], view[2][1]); + if (rayIntersectRing(ori, dir, glm::vec3(0), o_radius, i_radius, zeno::vec_to_other(right_world), + zeno::vec_to_other(up_world), thickness, model_matrix)) { + mode = INTERACT_XYZ; + return INTERACT_XYZ; + } + mode = INTERACT_NONE; return INTERACT_NONE; }