adding and integrating ImGuizmo
This commit is contained in:
@@ -1,35 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <sophia/aabb.hpp>
|
||||
#include <sophia/entity.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/math.hpp>
|
||||
#include <sophia/scene.hpp>
|
||||
#include <sophia/resource_registry_ref.hpp>
|
||||
#include <sophia/scene_view_registry.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <sophia/ui/ui.hpp>
|
||||
|
||||
#include "../editor_camera.hpp"
|
||||
#include "../editor_context.hpp"
|
||||
|
||||
/**
|
||||
* Shows the scene through its active camera. Click a sprite to select it,
|
||||
* drag it to move it (in Move mode).
|
||||
* Shows the active scene through the editor's own camera.
|
||||
*
|
||||
* Picking and moving are 2D: they assume an orthographic camera rotated only
|
||||
* around Z, and work in the z = 0 plane.
|
||||
* 3D: right-drag orbits, middle-drag pans, the wheel zooms, right-drag with
|
||||
* WASD/QE flies. 2D: right- or middle-drag pans, the wheel zooms. F frames
|
||||
* the selection in both.
|
||||
*
|
||||
* The selection gets a transform gizmo: W moves, E rotates, R scales, and
|
||||
* holding Ctrl snaps. Clicking selects the mesh or sprite under the mouse;
|
||||
* in 2D, dragging it also moves it.
|
||||
*/
|
||||
class ScenePanel : public sophia::Panel {
|
||||
public:
|
||||
ScenePanel(sophia::UI& ui, sophia::RenderTargetHandle target,
|
||||
sophia::Scene& scene, sophia::Entity& selected)
|
||||
: Panel{{.name = "Scene",
|
||||
.windowFlags = ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse}},
|
||||
// Renders into `target` through `view`, a SceneView this panel keeps
|
||||
// pointed at the active scene. `camera` is the entity the view looks
|
||||
// through; it belongs to a scene of the editor's own, and this panel
|
||||
// drives it from its EditorCamera.
|
||||
ScenePanel(sophia::UI& ui, sophia::ResourceRegistryRef resources,
|
||||
EditorContext& context, sophia::SceneViewRegistry& sceneViews,
|
||||
sophia::SceneViewHandle view, sophia::RenderTargetHandle target,
|
||||
sophia::Entity camera)
|
||||
: Panel{{.name = "Scene", .windowFlags = WINDOW_FLAGS}},
|
||||
ui{ui},
|
||||
resources{resources},
|
||||
context{context},
|
||||
sceneViews{sceneViews},
|
||||
view{view},
|
||||
target{target},
|
||||
scene{scene},
|
||||
selected{selected} {}
|
||||
camera{camera} {}
|
||||
|
||||
void onUpdate(const sophia::FrameInfo& frameInfo) override;
|
||||
|
||||
private:
|
||||
static constexpr ImGuiWindowFlags WINDOW_FLAGS =
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
||||
|
||||
enum class GizmoOperation { Translate, Rotate, Scale };
|
||||
|
||||
// A half-line in world space. `direction` is normalized, so distances
|
||||
// along it are in world units.
|
||||
struct Ray {
|
||||
glm::vec3 origin;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
|
||||
// Where the scene image is on screen this frame, and the camera looking
|
||||
// through it.
|
||||
struct ViewportTransform {
|
||||
@@ -38,22 +67,57 @@ class ScenePanel : public sophia::Panel {
|
||||
glm::mat4 viewProjection;
|
||||
glm::mat4 inverseViewProjection;
|
||||
|
||||
ImVec2 worldToScreen(const glm::vec3& world) const;
|
||||
// Null for points behind the camera.
|
||||
std::optional<ImVec2> worldToScreen(const glm::vec3& world) const;
|
||||
// Where `screen` is on the camera's near plane. Only the x and y, which
|
||||
// is all that's needed in 2D.
|
||||
glm::vec2 screenToWorld(const ImVec2& screen) const;
|
||||
// From the near plane through `screen`, into the scene.
|
||||
Ray screenToRay(const ImVec2& screen) const;
|
||||
|
||||
// Draws a world-space line, cut off where it passes behind the camera.
|
||||
void drawLine(ImDrawList* drawList, const glm::vec3& a, const glm::vec3& b,
|
||||
ImU32 color, float thickness) const;
|
||||
|
||||
private:
|
||||
ImVec2 clipToScreen(const glm::vec4& clip) const;
|
||||
};
|
||||
|
||||
void drawToolbar();
|
||||
void drawGrid(ImVec2 origin, ImVec2 size);
|
||||
void handleInput(const ViewportTransform& viewport);
|
||||
void handleCameraInput(const sophia::FrameInfo& frameInfo, ImVec2 size);
|
||||
void handleSelectionInput(const ViewportTransform& viewport);
|
||||
void handleGizmoHotkeys();
|
||||
// Draws the transform gizmo on the selection, and applies any change made
|
||||
// with it. `view` is the editor camera's, and `aspect` the viewport's
|
||||
// width over its height.
|
||||
void drawGizmo(const glm::mat4& view, float aspect);
|
||||
void drawSelection(const ViewportTransform& viewport);
|
||||
void frameSelection();
|
||||
|
||||
// The top-most sprite under `world`, or a null Entity.
|
||||
sophia::Entity pick(glm::vec2 world);
|
||||
// The bounds of the entity's mesh in the mesh's own space, if it has one.
|
||||
std::optional<sophia::AABB> getMeshBounds(sophia::Entity entity);
|
||||
|
||||
// The entity drawn in front along `ray`, or a null Entity. Only meshes and
|
||||
// sprites can be hit.
|
||||
sophia::Entity pick(const Ray& ray);
|
||||
|
||||
sophia::UI& ui;
|
||||
sophia::ResourceRegistryRef resources;
|
||||
EditorContext& context;
|
||||
sophia::SceneViewRegistry& sceneViews;
|
||||
sophia::SceneViewHandle view;
|
||||
sophia::RenderTargetHandle target;
|
||||
sophia::Scene& scene;
|
||||
sophia::Entity& selected;
|
||||
sophia::Entity camera;
|
||||
|
||||
EditorCamera editorCamera;
|
||||
|
||||
GizmoOperation gizmoOperation = GizmoOperation::Translate;
|
||||
// Whether the gizmo works along the entity's own axes or the world's.
|
||||
bool gizmoLocal = true;
|
||||
// Whether the viewport button was active last frame, i.e. a camera drag or
|
||||
// click is under way, which the gizmo mustn't interrupt.
|
||||
bool viewportActive = false;
|
||||
|
||||
bool showGrid = true;
|
||||
// Set while a drag that started on the selected entity is moving it.
|
||||
|
||||
Reference in New Issue
Block a user