#pragma once #include #include #include #include #include #include #include #include #include #include #include "../editor_camera.hpp" #include "../editor_context.hpp" /** * Shows the active scene through the editor's own camera. * * 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: // 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}, 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 { ImVec2 origin; ImVec2 size; glm::mat4 viewProjection; glm::mat4 inverseViewProjection; // Null for points behind the camera. std::optional 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 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 bounds of the entity's mesh in the mesh's own space, if it has one. std::optional 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::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. bool dragging = false; };