diff --git a/samples/core/editors/level_editor/camera.lua b/samples/core/editors/level_editor/camera.lua index e95db01a4..dda3cd7cb 100644 --- a/samples/core/editors/level_editor/camera.lua +++ b/samples/core/editors/level_editor/camera.lua @@ -17,7 +17,7 @@ local function camera_tumble(self, x, y) local camera_forward = Matrix4x4.z(camera_pose) local rotation_up = Quaternion.from_axis_angle(Vector3.up(), drag_delta.x * self._rotation_speed) - local rotation_right = Quaternion.from_axis_angle(camera_right, drag_delta.y * self._rotation_speed) + local rotation_right = Quaternion.from_axis_angle(camera_right, -drag_delta.y * self._rotation_speed) local rotate_delta = Matrix4x4.from_quaternion(Quaternion.multiply(rotation_up, rotation_right)) local target_position = camera_position + (camera_forward * self._target_distance) @@ -58,9 +58,9 @@ local function camera_track(self, x, y) end local delta = (drag_delta.y * pan_speed) * camera_up - - (drag_delta.x * pan_speed) * camera_right + + (drag_delta.x * pan_speed) * camera_right local tr = SceneGraph.instance(self._sg, self._unit) - SceneGraph.set_local_position(self._sg, tr, camera_position + delta) + SceneGraph.set_local_position(self._sg, tr, camera_position - delta) end local function camera_dolly(self, x, y) @@ -70,7 +70,7 @@ local function camera_dolly(self, x, y) -- Zoom speed is proportional to initial orthographic size local zoom_speed = self._drag_start_orthographic_size / 400 local zoom_delta = drag_delta.y * zoom_speed - self._orthographic_size = math.max(1, self._drag_start_orthographic_size + zoom_delta) + self._orthographic_size = math.max(1, self._drag_start_orthographic_size - zoom_delta) World.camera_set_orthographic_size(self._world, self:camera(), self._orthographic_size) else @@ -78,7 +78,7 @@ local function camera_dolly(self, x, y) local move_speed = self._drag_start_target_distance / 400 local mouse_delta = drag_delta.y * move_speed - self._target_distance = math.max(1, self._drag_start_target_distance + mouse_delta) + self._target_distance = math.max(1, self._drag_start_target_distance - mouse_delta) local move_delta = self._target_distance - self._drag_start_target_distance local camera_pose = self._drag_start_camera_pose:unbox() diff --git a/samples/core/editors/level_editor/level_editor.lua b/samples/core/editors/level_editor/level_editor.lua index fac5b3eda..a3efbadce 100644 --- a/samples/core/editors/level_editor/level_editor.lua +++ b/samples/core/editors/level_editor/level_editor.lua @@ -41,9 +41,11 @@ function dot_alpha(dot, fadeout_threshold, hidden_threshold) -- | | -- 00000fffff11111 -- - if dot < h0 then -- The axis is parallel to the viewer. + if dot < h0 then + -- The axis is parallel to the viewer. return 0 - elseif dot < f0 then -- The axis is starting to get perpendicular to the viewer. + elseif dot < f0 then + -- The axis is starting to get perpendicular to the viewer. return (dot - h0) / (f0 - h0) end @@ -452,10 +454,10 @@ function SelectTool:mouse_move(x, y) -- | | -- | | -- p0 ---- p1 - local p0, rd0 = LevelEditor:camera():camera_ray(rect_start.x, rect_end.y) - local p1, rd1 = LevelEditor:camera():camera_ray(rect_end.x , rect_end.y) - local p2, rd2 = LevelEditor:camera():camera_ray(rect_end.x , rect_start.y) - local p3, rd3 = LevelEditor:camera():camera_ray(rect_start.x, rect_start.y) + local p0, rd0 = LevelEditor:camera():camera_ray(rect_start.x, rect_start.y) + local p1, rd1 = LevelEditor:camera():camera_ray(rect_end.x, rect_start.y) + local p2, rd2 = LevelEditor:camera():camera_ray(rect_end.x, rect_end.y) + local p3, rd3 = LevelEditor:camera():camera_ray(rect_start.x, rect_end.y) local camera_near = LevelEditor:camera():near_clip_distance() local camera_far = LevelEditor:camera():far_clip_distance() @@ -500,9 +502,7 @@ function SelectTool:mouse_move(x, y) -- Draw the selection rectangle. local fill_color = Color4(140, 140, 140, 20) local border_color = Color4(180, 180, 180, 200) - -- Invert y-coord due to Gui having origin at bottom-left corner. - local resol_x, resol_y = Device.resolution() - local gui_rect_start = Vector3(rect_start.x, resol_y - rect_start.y - rect_size.y, 0) + local gui_rect_start = Vector3(rect_start.x, rect_start.y, 0) Gui.rect(LevelEditor._screen_gui , gui_rect_start , rect_size @@ -1495,6 +1495,141 @@ function LevelEditor:update(dt) self._camera:mouse_wheel(self._mouse.wheel.delta) self._camera:update(dt, self._mouse.dx, self._mouse.dy, self._keyboard, self._mouse) + -- Draw origin axes. + local camera_pose = self._camera:local_pose() + local camera_position = Matrix4x4.translation(camera_pose) + local camera_forward = Matrix4x4.z(camera_pose) + local axis_len = 40 + local axis_tip_radius = 8 + local gizmo_margin_right = 8 + local len = self._camera:screen_length_to_world_length(camera_position + camera_forward, axis_len) + local x_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward + len*Vector3(1, 0, 0)) + local y_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward + len*Vector3(0, 1, 0)) + local z_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward + len*Vector3(0, 0, 1)) + local o_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward) + local neg_x_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward + len*Vector3(-1, 0, 0)) + local neg_y_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward + len*Vector3(0, -1, 0)) + local neg_z_screen = World.camera_world_to_screen(self._world, self._camera:camera(), camera_position + camera_forward + len*Vector3(0, 0, -1)) + local resol_x, resol_y = Device.resolution() + local gizmo_origin_x = resol_x * 0.5 - (axis_len + axis_tip_radius + gizmo_margin_right) + local gizmo_origin_y = resol_y * 0.5 - (axis_len + axis_tip_radius + gizmo_margin_right) + local screen_translation = Vector2(gizmo_origin_x, gizmo_origin_y) + + local x_axis_alpha = math.max(0.6, 1 - (1 + Vector3.dot(Vector3(1, 0, 0), camera_forward)) * 0.5) + local y_axis_alpha = math.max(0.6, 1 - (1 + Vector3.dot(Vector3(0, 1, 0), camera_forward)) * 0.5) + local z_axis_alpha = math.max(0.6, 1 - (1 + Vector3.dot(Vector3(0, 0, 1), camera_forward)) * 0.5) + local neg_x_axis_alpha = math.max(0.4, 1 - (1 + Vector3.dot(Vector3(-1, 0, 0), camera_forward)) * 0.5) + local neg_y_axis_alpha = math.max(0.4, 1 - (1 + Vector3.dot(Vector3(0, -1, 0), camera_forward)) * 0.5) + local neg_z_axis_alpha = math.max(0.4, 1 - (1 + Vector3.dot(Vector3(0, 0, -1), camera_forward)) * 0.5) + + function draw_circle(gui, center, radius, color) + local segments = 36 + for i=1,segments do + local x0 = math.cos(2 * math.pi * ((i - 1) / segments)) * radius + local y0 = math.sin(2 * math.pi * ((i - 1) / segments)) * radius + local x1 = math.cos(2 * math.pi * ((i - 0) / segments)) * radius + local y1 = math.sin(2 * math.pi * ((i - 0) / segments)) * radius + + Gui.triangle(gui + , center + , center + Vector2(x0, y0) + , center + Vector2(x1, y1) + , color + , center.z + ) + end + end + + local x_axis_color = Color4.lerp_alpha(Colors.axis_x(), x_axis_alpha) + local y_axis_color = Color4.lerp_alpha(Colors.axis_y(), y_axis_alpha) + local z_axis_color = Color4.lerp_alpha(Colors.axis_z(), z_axis_alpha) + local neg_x_axis_color = Color4.lerp_alpha(Colors.axis_x(), neg_x_axis_alpha) + local neg_y_axis_color = Color4.lerp_alpha(Colors.axis_y(), neg_y_axis_alpha) + local neg_z_axis_color = Color4.lerp_alpha(Colors.axis_z(), neg_z_axis_alpha) + + -- Draw tips. + draw_circle(self._screen_gui, x_screen + screen_translation, axis_tip_radius, x_axis_color) + draw_circle(self._screen_gui, y_screen + screen_translation, axis_tip_radius, y_axis_color) + draw_circle(self._screen_gui, z_screen + screen_translation, axis_tip_radius, z_axis_color) + draw_circle(self._screen_gui, neg_x_screen + screen_translation, axis_tip_radius, neg_x_axis_color) + draw_circle(self._screen_gui, neg_y_screen + screen_translation, axis_tip_radius, neg_y_axis_color) + draw_circle(self._screen_gui, neg_z_screen + screen_translation, axis_tip_radius, neg_z_axis_color) + + function draw_segment(gui, a, b, depth, color) + local thickness = 1 + local ab_segment = a - b + local ab_normal = Vector3.normalize(Vector2(ab_segment.y, -ab_segment.x)) + + -- Draw segment top-half. + Gui.triangle(gui + , a + ab_normal * thickness + , a - ab_normal * thickness + , b + ab_normal * thickness + , color + , depth + ) + -- Draw segment bottom-half. + Gui.triangle(gui + , a - ab_normal * thickness + , b - ab_normal * thickness + , b + ab_normal * thickness + , color + , depth + ) + end + + -- Draw axes. + draw_segment(self._screen_gui + , o_screen + screen_translation + , x_screen + screen_translation + Vector3.normalize(o_screen - x_screen) * axis_tip_radius + , x_screen.z + , x_axis_color + ) + draw_segment(self._screen_gui + , o_screen + screen_translation + , y_screen + screen_translation + Vector3.normalize(o_screen - y_screen) * axis_tip_radius + , y_screen.z + , y_axis_color + ) + draw_segment(self._screen_gui + , o_screen + screen_translation + , z_screen + screen_translation + Vector3.normalize(o_screen - z_screen) * axis_tip_radius + , z_screen.z + , z_axis_color + ) + + -- Draw labels. + Gui.text(self._screen_gui + , x_screen + screen_translation - Vector2(axis_tip_radius * 0.5, axis_tip_radius * 0.5) + , 14 + , "X" + , "core/game/hud/debug" + , "core/game/hud/debug" + , Color4.black() + ) + Gui.text(self._screen_gui + , y_screen + screen_translation - Vector2(axis_tip_radius * 0.5, axis_tip_radius * 0.5) + , 14 + , "Y" + , "core/game/hud/debug" + , "core/game/hud/debug" + , Color4.black() + ) + Gui.text(self._screen_gui + , z_screen + screen_translation - Vector2(axis_tip_radius * 0.5, axis_tip_radius * 0.5) + , 14 + , "Z" + , "core/game/hud/debug" + , "core/game/hud/debug" + , Color4.black() + ) + + local old_world = Vector3(20, 30, 12) + local screen = World.camera_world_to_screen(self._world, self._camera:camera(), old_world) + local new_world = World.camera_screen_to_world(self._world, self._camera:camera(), screen) + print(Vector3.elements(old_world)) + print(Vector3.elements(new_world)) + if self._camera:is_idle() then self.tool:mouse_move(self._mouse.x, self._mouse.y) end @@ -1536,8 +1671,9 @@ function LevelEditor:reset() end function LevelEditor:set_mouse_state(x, y, left, middle, right) + local resol_x, resol_y = Device.resolution() self._mouse.x = x - self._mouse.y = y + self._mouse.y = resol_y - y self._mouse.left = left self._mouse.middle = middle self._mouse.right = right @@ -1548,14 +1684,16 @@ function LevelEditor:mouse_wheel(delta) end function LevelEditor:mouse_down(x, y) + local resol_x, resol_y = Device.resolution() if self._camera:is_idle() then - self.tool:mouse_down(x, y) + self.tool:mouse_down(x, resol_y - y) end end function LevelEditor:mouse_up(x, y) + local resol_x, resol_y = Device.resolution() if self._camera:is_idle() then - self.tool:mouse_up(x, y) + self.tool:mouse_up(x, resol_y - y) end end diff --git a/src/world/world.cpp b/src/world/world.cpp index 8d70986aa..241810b31 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -418,6 +418,9 @@ void World::camera_set_viewport_metrics(CameraInstance camera, u16 x, u16 y, u16 Vector3 World::camera_screen_to_world(CameraInstance camera, const Vector3 &pos) { + const f32 w = _camera[camera.i].view_width; + const f32 h = _camera[camera.i].view_height; + TransformInstance ti = _scene_graph->instance(_camera[camera.i].unit); Matrix4x4 projection = camera_projection_matrix(camera); @@ -429,12 +432,9 @@ Vector3 World::camera_screen_to_world(CameraInstance camera, const Vector3 &pos) const bgfx::Caps *caps = bgfx::getCaps(); Vector4 ndc; - ndc.x = (2.0f * (pos.x - 0.0f)) / _camera[camera.i].view_width - 1.0f; - ndc.y = (2.0f * (_camera[camera.i].view_height - pos.y)) / _camera[camera.i].view_height - 1.0f; - ndc.z = caps->homogeneousDepth - ? (2.0f * pos.z) - 1.0f - : pos.z - ; + ndc.x = (2.0f * pos.x) / w - 1.0f; + ndc.y = (2.0f * pos.y) / h - 1.0f; + ndc.z = caps->homogeneousDepth ? (2.0f * pos.z) - 1.0f : pos.z; ndc.w = 1.0f; Vector4 tmp = ndc * mvp; @@ -445,6 +445,11 @@ Vector3 World::camera_screen_to_world(CameraInstance camera, const Vector3 &pos) Vector3 World::camera_world_to_screen(CameraInstance camera, const Vector3 &pos) { + const f32 x = _camera[camera.i].view_x; + const f32 y = _camera[camera.i].view_y; + const f32 w = _camera[camera.i].view_width; + const f32 h = _camera[camera.i].view_height; + TransformInstance ti = _scene_graph->instance(_camera[camera.i].unit); Matrix4x4 projection = camera_projection_matrix(camera); @@ -460,13 +465,14 @@ Vector3 World::camera_world_to_screen(CameraInstance camera, const Vector3 &pos) Vector4 clip = xyzw * (world_inv * projection); Vector4 ndc; - ndc.x = clip.x / clip.w; - ndc.y = clip.y / clip.w; + ndc = clip * (1.0f / clip.w); + + const bgfx::Caps *caps = bgfx::getCaps(); Vector3 screen; - screen.x = (_camera[camera.i].view_x + _camera[camera.i].view_width * (ndc.x + 1.0f)) / 2.0f; - screen.y = (_camera[camera.i].view_y + _camera[camera.i].view_height * (1.0f - ndc.y)) / 2.0f; - screen.z = 0.0f; + screen.x = (x + w * (ndc.x + 1.0f)) / 2.0f; + screen.y = h - (y + h * (1.0f - ndc.y)) / 2.0f; + screen.z = caps->homogeneousDepth ? (ndc.z + 1.0f) / 2.0f : ndc.z; return screen; }