feat: add basic canvas / improve inspector
This commit is contained in:
@@ -40,6 +40,7 @@ public:
|
||||
void AddImpulse(const glm::vec3& impulse);
|
||||
|
||||
void SetLinearVelocity(const glm::vec3& velocity);
|
||||
void Teleport(const glm::vec3& worldPosition);
|
||||
[[nodiscard]] glm::vec3 GetLinearVelocity() const;
|
||||
|
||||
[[nodiscard]] RigidbodyType GetType() const { return m_Type; }
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef DESTRUM_CANVAS_H
|
||||
#define DESTRUM_CANVAS_H
|
||||
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
enum class CanvasScaleMode {
|
||||
ConstantPixelSize,
|
||||
ScaleWithScreenHeight,
|
||||
};
|
||||
|
||||
// Draws the UIGraphics on its object and every active child, depth first, so later siblings draw on top.
|
||||
// A child with its own Canvas is skipped and draws itself.
|
||||
class Canvas final : public Component {
|
||||
public:
|
||||
explicit Canvas(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "Canvas"; }
|
||||
|
||||
void Start() override;
|
||||
void Render(const RenderContext& ctx) override;
|
||||
void Draw(const glm::vec2& screenSize);
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
[[nodiscard]] float GetScale(const glm::vec2& screenSize) const;
|
||||
|
||||
[[nodiscard]] static Canvas* FindFor(GameObject& object);
|
||||
|
||||
void SetScaleMode(CanvasScaleMode mode) { m_ScaleMode = mode; }
|
||||
void SetReferenceHeight(float height) { m_ReferenceHeight = height; }
|
||||
void SetScaleFactor(float factor) { m_ScaleFactor = factor; }
|
||||
void SetDrawDebugRects(bool draw) { m_DrawDebugRects = draw; }
|
||||
|
||||
[[nodiscard]] CanvasScaleMode GetScaleMode() const { return m_ScaleMode; }
|
||||
[[nodiscard]] float GetReferenceHeight() const { return m_ReferenceHeight; }
|
||||
[[nodiscard]] float GetScaleFactor() const { return m_ScaleFactor; }
|
||||
|
||||
private:
|
||||
void DrawObject(GameObject& object, const UIRect& rect, float scale, bool visible) const;
|
||||
|
||||
CanvasScaleMode m_ScaleMode{CanvasScaleMode::ScaleWithScreenHeight};
|
||||
float m_ReferenceHeight{1080.0f};
|
||||
float m_ScaleFactor{1.0f};
|
||||
bool m_DrawDebugRects{false};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_CANVAS_H
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef DESTRUM_RECTTRANSFORM_H
|
||||
#define DESTRUM_RECTTRANSFORM_H
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// Screen space rectangle in draw image pixels, origin top left, y down
|
||||
struct UIRect {
|
||||
glm::vec2 min{0.0f};
|
||||
glm::vec2 size{0.0f};
|
||||
|
||||
[[nodiscard]] glm::vec2 Max() const { return min + size; }
|
||||
[[nodiscard]] glm::vec2 Center() const { return min + size * 0.5f; }
|
||||
[[nodiscard]] bool Contains(const glm::vec2& point) const;
|
||||
};
|
||||
|
||||
// Anchors and pivot are normalized (0,0 top left), position and size are in canvas units
|
||||
class RectTransform final : public Component {
|
||||
public:
|
||||
explicit RectTransform(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "RectTransform"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
[[nodiscard]] UIRect ComputeRect(const UIRect& parentRect, float scale) const;
|
||||
|
||||
void SetAnchors(const glm::vec2& anchorMin, const glm::vec2& anchorMax);
|
||||
void SetAnchor(const glm::vec2& anchor) { SetAnchors(anchor, anchor); }
|
||||
void SetPivot(const glm::vec2& pivot) { m_Pivot = pivot; }
|
||||
void SetPosition(const glm::vec2& position) { m_Position = position; }
|
||||
void SetSize(const glm::vec2& size) { m_Size = size; }
|
||||
void Stretch();
|
||||
|
||||
[[nodiscard]] const glm::vec2& GetAnchorMin() const { return m_AnchorMin; }
|
||||
[[nodiscard]] const glm::vec2& GetAnchorMax() const { return m_AnchorMax; }
|
||||
[[nodiscard]] const glm::vec2& GetPivot() const { return m_Pivot; }
|
||||
[[nodiscard]] const glm::vec2& GetPosition() const { return m_Position; }
|
||||
[[nodiscard]] const glm::vec2& GetSize() const { return m_Size; }
|
||||
|
||||
// Where the canvas last drew this object
|
||||
[[nodiscard]] const UIRect& GetScreenRect() const { return m_ScreenRect; }
|
||||
[[nodiscard]] const UIRect& GetParentRect() const { return m_ParentRect; }
|
||||
void SetScreenRect(const UIRect& rect, const UIRect& parentRect, float scale)
|
||||
{
|
||||
m_ScreenRect = rect;
|
||||
m_ParentRect = parentRect;
|
||||
m_LastScale = scale;
|
||||
}
|
||||
|
||||
void ApplyAnchorPreset(const glm::vec2& preset);
|
||||
|
||||
private:
|
||||
glm::vec2 m_AnchorMin{0.5f};
|
||||
glm::vec2 m_AnchorMax{0.5f};
|
||||
glm::vec2 m_Pivot{0.5f};
|
||||
glm::vec2 m_Position{0.0f};
|
||||
glm::vec2 m_Size{100.0f};
|
||||
|
||||
UIRect m_ScreenRect{};
|
||||
UIRect m_ParentRect{};
|
||||
float m_LastScale{1.0f};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_RECTTRANSFORM_H
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef DESTRUM_UIGRAPHIC_H
|
||||
#define DESTRUM_UIGRAPHIC_H
|
||||
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// Anything a Canvas draws into the rect of the object it sits on
|
||||
class UIGraphic : public Component {
|
||||
public:
|
||||
virtual void DrawUI(const UIRect& rect, float scale) = 0;
|
||||
|
||||
[[nodiscard]] const glm::vec4& GetColor() const { return m_Color; }
|
||||
void SetColor(const glm::vec4& color) { m_Color = color; }
|
||||
|
||||
protected:
|
||||
UIGraphic(GameObject& parent, const std::string& name) : Component(parent, name) {}
|
||||
|
||||
glm::vec4 m_Color{1.0f};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_UIGRAPHIC_H
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef DESTRUM_UIIMAGE_H
|
||||
#define DESTRUM_UIIMAGE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <destrum/Components/UI/UIGraphic.h>
|
||||
#include <destrum/Graphics/Managers/Renderer2D.h>
|
||||
#include <destrum/Graphics/ids.h>
|
||||
|
||||
enum class UIImageShape {
|
||||
Rect,
|
||||
Ellipse,
|
||||
};
|
||||
|
||||
class UIImage final : public UIGraphic {
|
||||
public:
|
||||
explicit UIImage(GameObject& parent, const glm::vec4& color = glm::vec4{1.0f});
|
||||
|
||||
std::string GetTypeName() const override { return "UIImage"; }
|
||||
|
||||
void DrawUI(const UIRect& rect, float scale) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetShape(UIImageShape shape) { m_Shape = shape; }
|
||||
void SetOutline(float thickness) { m_Outline = thickness; }
|
||||
void SetCornerRadius(float radius) { m_CornerRadius = radius; }
|
||||
void SetTexture(const std::string& assetPath) { m_TexturePath = assetPath; }
|
||||
void SetFilter(TextureFilter filter) { m_Filter = filter; }
|
||||
|
||||
[[nodiscard]] UIImageShape GetShape() const { return m_Shape; }
|
||||
[[nodiscard]] float GetOutline() const { return m_Outline; }
|
||||
[[nodiscard]] float GetCornerRadius() const { return m_CornerRadius; }
|
||||
[[nodiscard]] const std::string& GetTexture() const { return m_TexturePath; }
|
||||
|
||||
private:
|
||||
ImageID ResolveTexture();
|
||||
|
||||
UIImageShape m_Shape{UIImageShape::Rect};
|
||||
float m_Outline{0.0f};
|
||||
float m_CornerRadius{0.0f};
|
||||
std::string m_TexturePath;
|
||||
TextureFilter m_Filter{TextureFilter::Linear};
|
||||
|
||||
std::string m_LoadedTexturePath;
|
||||
ImageID m_Texture{NULL_IMAGE_ID};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_UIIMAGE_H
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef DESTRUM_UITEXT_H
|
||||
#define DESTRUM_UITEXT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <destrum/Components/UI/UIGraphic.h>
|
||||
|
||||
class Font;
|
||||
|
||||
enum class UITextAlign {
|
||||
Start,
|
||||
Center,
|
||||
End,
|
||||
};
|
||||
|
||||
class UIText final : public UIGraphic {
|
||||
public:
|
||||
explicit UIText(GameObject& parent, std::string text = {}, float fontSize = 24.0f);
|
||||
|
||||
std::string GetTypeName() const override { return "UIText"; }
|
||||
|
||||
void DrawUI(const UIRect& rect, float scale) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetText(std::string text) { m_Text = std::move(text); }
|
||||
void SetFont(std::string assetPath) { m_FontPath = std::move(assetPath); }
|
||||
void SetFontSize(float size) { m_FontSize = size; }
|
||||
void SetOutline(float thickness, const glm::vec4& color);
|
||||
void SetAlignment(UITextAlign horizontal, UITextAlign vertical);
|
||||
|
||||
[[nodiscard]] const std::string& GetText() const { return m_Text; }
|
||||
[[nodiscard]] const std::string& GetFont() const { return m_FontPath; }
|
||||
[[nodiscard]] float GetFontSize() const { return m_FontSize; }
|
||||
[[nodiscard]] UITextAlign GetHorizontalAlign() const { return m_HorizontalAlign; }
|
||||
[[nodiscard]] UITextAlign GetVerticalAlign() const { return m_VerticalAlign; }
|
||||
|
||||
[[nodiscard]] glm::vec2 PlaceText(const UIRect& rect, const glm::vec2& textSize) const;
|
||||
|
||||
private:
|
||||
std::string m_Text;
|
||||
std::string m_FontPath;
|
||||
float m_FontSize{24.0f};
|
||||
float m_Outline{0.0f};
|
||||
glm::vec4 m_OutlineColor{0.0f, 0.0f, 0.0f, 1.0f};
|
||||
UITextAlign m_HorizontalAlign{UITextAlign::Center};
|
||||
UITextAlign m_VerticalAlign{UITextAlign::Center};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_UITEXT_H
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef DESTRUM_FONTCACHE_H
|
||||
#define DESTRUM_FONTCACHE_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <destrum/Graphics/Resources/Font.h>
|
||||
#include <destrum/Singleton.h>
|
||||
|
||||
class FontCache final : public Singleton<FontCache> {
|
||||
public:
|
||||
friend class Singleton<FontCache>;
|
||||
|
||||
static constexpr std::string_view DefaultFont = "engine://fonts/Roboto-Medium.ttf";
|
||||
|
||||
[[nodiscard]] const Font* Get(std::string_view assetPath);
|
||||
|
||||
// The atlases belong to RenderResources, call before it is cleaned up
|
||||
void Clear() { fonts.clear(); }
|
||||
|
||||
private:
|
||||
FontCache() = default;
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<Font>> fonts;
|
||||
};
|
||||
|
||||
#endif // DESTRUM_FONTCACHE_H
|
||||
@@ -59,6 +59,9 @@ public:
|
||||
[[nodiscard]] const std::vector<Transform*>& GetChildren() const;
|
||||
[[nodiscard]] GameObject* GetOwner() const;
|
||||
|
||||
[[nodiscard]] int GetSiblingIndex() const;
|
||||
void SetSiblingIndex(int index);
|
||||
|
||||
private:
|
||||
void AddChild(Transform* transform);
|
||||
void RemoveChild(Transform* transform);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <destrum/Event.h>
|
||||
#include <destrum/ObjectModel/ObjectId.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
#include <destrum/Util/SceneInspector.h>
|
||||
|
||||
#include "destrum/Physics/JoltPhysicsWorld.h"
|
||||
#include "destrum/Physics/PhysicsSceneBridge.h"
|
||||
@@ -66,7 +67,15 @@ public:
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<GameObject>>& GetObjects() const {
|
||||
return m_objects;
|
||||
}
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<GameObject>>& GetPendingAdditions() const {
|
||||
return m_pendingAdditions;
|
||||
}
|
||||
[[nodiscard]] GameObject* FindGameObject(ObjectId id) const;
|
||||
|
||||
// Reparents object (nullptr is the scene root) and puts it right before insertBefore, or last when that is
|
||||
// nullptr. Also reorders the object list so the order survives a save. False if the move isn't allowed.
|
||||
bool MoveInHierarchy(GameObject& object, GameObject* parent, const GameObject* insertBefore = nullptr);
|
||||
void SetSelectedObject(ObjectId id) { m_SelectedObjectId = id; }
|
||||
[[nodiscard]] GameObject* GetSelectedObject() const { return FindGameObject(m_SelectedObjectId); }
|
||||
|
||||
void SetRegisterBindings(std::function<void()> registerBindings) {
|
||||
@@ -150,6 +159,7 @@ private:
|
||||
//Imgui vars
|
||||
bool m_ShowDemoWindow{false};
|
||||
ObjectId m_SelectedObjectId{InvalidObjectId};
|
||||
SceneInspector m_Inspector;
|
||||
|
||||
ObjectId m_CurrentCameraId{InvalidObjectId};
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include <destrum/Components/Physics/CapsuleCollider.h>
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/Components/Physics/SphereCollider.h>
|
||||
#include <destrum/Components/UI/Canvas.h>
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/Components/UI/UIImage.h>
|
||||
#include <destrum/Components/UI/UIText.h>
|
||||
|
||||
#define DESTRUM_ENGINE_COMPONENTS(X) \
|
||||
X(MeshRendererComponent) \
|
||||
@@ -25,5 +29,9 @@
|
||||
X(BoxCollider) \
|
||||
X(SphereCollider) \
|
||||
X(CapsuleCollider) \
|
||||
X(Canvas) \
|
||||
X(RectTransform) \
|
||||
X(UIImage) \
|
||||
X(UIText) \
|
||||
|
||||
#endif // DESTRUM_ENGINECOMPONENTLIST_H
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/ObjectId.h>
|
||||
#include <destrum/ObjectModel/ObjectRef.h>
|
||||
|
||||
class GameObject;
|
||||
class Scene;
|
||||
|
||||
using ImGuiInputTextFlags = int;
|
||||
|
||||
namespace ImGuiUtils {
|
||||
[[nodiscard]] bool DrawVec3Control(
|
||||
@@ -18,14 +23,17 @@ namespace ImGuiUtils {
|
||||
glm::vec3& value,
|
||||
float speed = 0.1f);
|
||||
|
||||
void RenderSceneInspector(
|
||||
std::string_view sceneName,
|
||||
const std::vector<std::shared_ptr<GameObject>>& objects,
|
||||
const std::vector<std::shared_ptr<GameObject>>& pendingAdditions,
|
||||
ObjectId& selectedObjectId,
|
||||
const std::function<GameObject*()>& createGameObject,
|
||||
const std::function<void()>& drawSceneSettings,
|
||||
bool* open = nullptr);
|
||||
inline constexpr const char* GameObjectPayload = "DESTRUM_GAMEOBJECT";
|
||||
|
||||
bool InputString(const char* label, std::string& value, ImGuiInputTextFlags flags = 0, const char* hint = nullptr);
|
||||
bool InputStringMultiline(const char* label, std::string& value);
|
||||
|
||||
// Combo over the scene's objects, filter can hide objects that can't be picked
|
||||
bool DrawObjectRefPicker(
|
||||
const char* label,
|
||||
ObjectRef& ref,
|
||||
const Scene& scene,
|
||||
const std::function<bool(const GameObject&)>& filter = {});
|
||||
}
|
||||
|
||||
#endif // DESTRUM_IMGUIUTILS_H
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef DESTRUM_RECTGIZMO_H
|
||||
#define DESTRUM_RECTGIZMO_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class GameObject;
|
||||
|
||||
class RectGizmo final {
|
||||
public:
|
||||
[[nodiscard]] static bool CanManipulate(GameObject& object);
|
||||
|
||||
// drawImageSize is what the canvas renders into, it is stretched over the main ImGui viewport
|
||||
bool Manipulate(GameObject& object, const glm::vec2& drawImageSize);
|
||||
|
||||
[[nodiscard]] bool IsUsing() const { return m_ActiveHandle >= 0; }
|
||||
|
||||
bool snap{false};
|
||||
float snapStep{5.0f};
|
||||
|
||||
private:
|
||||
int m_ActiveHandle{-1};
|
||||
glm::vec2 m_DragStartMouse{0.0f};
|
||||
glm::vec2 m_DragStartPosition{0.0f};
|
||||
glm::vec2 m_DragStartSize{0.0f};
|
||||
};
|
||||
|
||||
#endif // DESTRUM_RECTGIZMO_H
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef DESTRUM_SCENEINSPECTOR_H
|
||||
#define DESTRUM_SCENEINSPECTOR_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/ObjectId.h>
|
||||
|
||||
class Component;
|
||||
class GameObject;
|
||||
class Scene;
|
||||
|
||||
class SceneInspector final {
|
||||
public:
|
||||
void Draw(Scene& scene, ObjectId& selectedObjectId, const std::function<void()>& drawSceneSettings, bool* open);
|
||||
|
||||
private:
|
||||
struct PendingMove {
|
||||
ObjectId object{InvalidObjectId};
|
||||
ObjectId parent{InvalidObjectId};
|
||||
ObjectId anchor{InvalidObjectId};
|
||||
bool after{false};
|
||||
};
|
||||
|
||||
void DrawHierarchy(Scene& scene, ObjectId& selectedObjectId);
|
||||
void DrawNode(Scene& scene, GameObject& object, ObjectId& selectedObjectId, std::unordered_set<const GameObject*>& visited);
|
||||
void DrawRenameField(GameObject& object);
|
||||
void DrawNodeDragDrop(GameObject& object);
|
||||
void DrawObjectMenu(Scene& scene, GameObject* object, ObjectId& selectedObjectId);
|
||||
void HandleShortcuts(Scene& scene, ObjectId& selectedObjectId);
|
||||
void ApplyPendingActions(Scene& scene, ObjectId& selectedObjectId);
|
||||
|
||||
void DrawObject(GameObject& object);
|
||||
void DrawTransform(GameObject& object);
|
||||
void DrawComponents(GameObject& object);
|
||||
void DrawComponentMenu(GameObject& object, Component& component, std::size_t index, bool removable);
|
||||
void DrawAddComponent(GameObject& object);
|
||||
|
||||
void BeginRename(const GameObject& object);
|
||||
|
||||
std::string m_Filter;
|
||||
std::string m_ComponentFilter;
|
||||
|
||||
ObjectId m_RenamingId{InvalidObjectId};
|
||||
std::string m_RenameBuffer;
|
||||
bool m_FocusRename{false};
|
||||
|
||||
ObjectId m_OpenNextFrame{InvalidObjectId};
|
||||
PendingMove m_PendingMove;
|
||||
ObjectId m_PendingDelete{InvalidObjectId};
|
||||
ObjectId m_PendingDuplicate{InvalidObjectId};
|
||||
|
||||
std::string m_ClipboardType;
|
||||
nlohmann::json m_Clipboard;
|
||||
};
|
||||
|
||||
#endif // DESTRUM_SCENEINSPECTOR_H
|
||||
Reference in New Issue
Block a user