editor.h

cross platform rendering playground

src/editor/editor.h

6.89 KB
#pragma once
#include "backend/rhi.h"
#include "core/math_rtm.h"
#include "imgui.h"
#include "passes/fg.h"
#include "scene/camera.h"
#include "scene/light.h"

struct Scene;
struct SceneGraph;
struct RenderData;
struct DebugDraw;
struct Pipelines;
struct PassInspectorEntry;
struct PassContext;
struct QuickSubmit;
struct Arena;
struct Platform;
class AssetManager;
class PassTimings;

namespace rhi {
struct ShaderCompiler;
struct Swapchain;
} // namespace rhi

namespace editor {

constexpr ImGuiWindowFlags PANEL_FLAGS = ImGuiWindowFlags_NoCollapse;

enum GizmoTool : int {
    GIZMO_TOOL_MOVE = 0,
    GIZMO_TOOL_ROTATE = 1,
    GIZMO_TOOL_SCALE = 2,
};

struct TextureViewerDesc {
    rhi::Image *image = nullptr;
    rhi::Sampler *sampler = nullptr;
    const char *name = nullptr;
    const char *format_name = nullptr;
};

bool draw_texture_inspector(rhi::Device &device, QuickSubmit &qs, const char *id, const TextureViewerDesc &desc);
void shutdown_texture_inspectors(rhi::Device &device);

// single-texel GPU readback (staging copy + timeline sync wait) into `out`.
// image TransferSrc usage and sample_count 1.
bool readback_texel_raw(
    rhi::Device &device,
    QuickSubmit &qs,
    rhi::ImageView &view,
    u32 mip,
    u32 layer,
    i64 tx,
    i64 ty,
    void *out,
    usize out_size
);

void init_log_viewer();

void apply_style(f32 scale, ImGuiStyle &style);

// just a throttle to apply the euler -> quat only once per frame
// afaik translate/scale wont need something like this, because they're 1:1
// whereas rot for euler -> quat is many:1
struct EulerEdit {
    int id = -1;
    vec3 euler = vec3(0.0f);
    quat sync;
};

struct Editor {
    bool dock_layout_built = false;
    ImFont *font_ui = nullptr;
    ImFont *font_mono = nullptr;
    rhi::Sampler viewport_sampler = {};
    rhi::RenderPipeline *imgui_pipeline = nullptr;
    u32 viewport_width = 0;
    u32 viewport_height = 0;
    bool viewport_resized = false;

    rhi::Device *device = nullptr;
    AssetManager *assets = nullptr;
    QuickSubmit *qs = nullptr; // retained for one-off GPU work (readbacks); set in init.

    bool visible = false;
    bool enabled = true;

    bool show_viewport = true;
    bool show_hierarchy = true;
    bool show_inspector = true;
    bool show_texture_viewer = true;
    bool show_timings = true;
    bool show_log = true;

    Platform *platform = nullptr;

    // DdgiConfig ddgi_config;
    u32 ddgi_frame_counter = 0;
    int selected_node = -1;

    // debug draw
    bool show_debug_lines = true;
    bool force_xray = false;
    bool show_cat_default = true;
    bool show_cat_grid = false;
    bool show_cat_proxy = true;
    bool show_cat_selection = true;
    bool show_cat_light = false;
    bool show_cat_ddgi = false;
    bool show_cat_gizmo = true;

    // shader reload
    bool reload_requested_ = false;

    GizmoTool gizmo_tool = GIZMO_TOOL_MOVE; // active tool (Blender-like)
    bool gizmo_world = true;                // true = world orientation, false = local
    bool gizmo_snap = false;
    f32 gizmo_snap_step = 0.1f;   // move, world units
    f32 gizmo_snap_angle = 15.0f; // rotate, degrees
    f32 gizmo_snap_scale = 0.1f;  // scale, factor step

    // drag runtime (owned by update_*_gizmo; handles only)
    bool gizmo_dragging = false;
    int gizmo_grab_node = -1;           // node the active drag belongs to
    u8 gizmo_drag_axis = 0;             // handle id gizmo axis id
    int gizmo_hover_axis = -1;          // screen-space handle hover (-1 = none)
    vec3 gizmo_grab_point = vec3(0.0f); // world-space ref point at grab
    vec3 gizmo_base_local = vec3(0.0f); // node local translation at grab
    vec3 gizmo_base_world = vec3(0.0f); // node world origin at grab
    vec3 gizmo_plane_n = vec3(0.0f);    // drag plane normal captured at grab

    // extended drag state for rotate/scale
    mat4 gizmo_base_world_mat = identity(); // node world matrix at grab
    mat4 gizmo_base_local_mat = identity(); // node local matrix at grab
    vec3 gizmo_base_scale = vec3(1.0f);     // node local scale at grab
    f32 gizmo_grab_angle = 0.0f;            // rotate: angle of grab point around axis
    f32 gizmo_grab_dist = 0.0f;             // scale: radial/axial distance at grab
    vec3 gizmo_drag_u = vec3(0.0f);         // rotate: basis u, perpendicular drag axis
    vec3 gizmo_drag_v = vec3(0.0f);         // rotate: basis v, perpendicular drag axis
    int gizmo_tool_grab = -1;               // tool active at grab (cancels cross-tool drags)
    bool gizmo_world_grab = true;           // orientation at grab (rebase on toggle)
    ImVec2 gizmo_view_min = ImVec2(0, 0);
    ImVec2 gizmo_view_size = ImVec2(0, 0);
    // inspector rotation edit-state (see EulerEdit above)
    EulerEdit node_rot; // id = node index
    EulerEdit inst_rot; // id = instance slot (base + k), single-active-instance cache
};

inline bool reload_requested(Editor &e) {
    if (e.reload_requested_) {
        e.reload_requested_ = false;
        return true;
    }
    return false;
}
inline void request_reload(Editor &e) {
    e.reload_requested_ = true;
}

void init(
    Editor &e,
    rhi::Device &device,
    Platform &platform,
    QuickSubmit &qs,
    Arena &staging,
    FrameGraph &fg,
    AssetManager &assets
);
void shutdown(Editor &e, rhi::Device &device);
void begin_frame(Editor &e, RenderData &rd);
void end_frame(Editor &e, Scene &scene, RenderData &rd, const PassInspectorEntry *passes, u32 pass_count);

void draw_viewport_central(Editor &e, RenderData &rd);
void draw_log_window(ImFont *mono_font);
void draw_timings_window(
    Scene &scene, RenderData &rd, const PassInspectorEntry *passes, u32 pass_count, Editor &e, ImFont *mono_font
);
void draw_texture_viewer_window(rhi::Device &device, Editor &editor, RenderData &rd);
void draw_scene_graph_window(Scene &scene, const AssetManager &mgr, int &selected_node);
void draw_inspector_window(Scene &scene, const AssetManager &mgr, ImFont *mono_font, Editor &e);

void update_gizmo(Editor &e, Scene &scene, DebugDraw &draw);

void handle_frame_begin(
    Editor &e,
    RenderData &rd,
    rhi::Device &device,
    rhi::Swapchain &swapchain,
    FrameGraph &fg,
    ImageHandle swapchain_target,
    rhi::Extent2D &render_size,
    Scene &scene,
    Pipelines &pipelines,
    PassTimings &timings,
    u32 frame_slot
);

void eval_render_size(u32 viewport_w, u32 viewport_h, rhi::Swapchain &swapchain, rhi::Extent2D &extent);

struct PresentTargets {
    ImageHandle display;
    ImageHandle id_blit;
    ImageHandle irradiance;
    ImageHandle distance;
    ImageHandle cascade[Light::CASCADE_COUNT];
    ImageHandle swapchain;
};
void record_present(
    Editor &e,
    Pipelines &pipelines,
    rhi::ShaderCompiler &sc,
    FrameGraph &fg,
    rhi::Swapchain &swapchain,
    const PresentTargets &targets,
    PassTimings &timings
);

void apply_debug_settings(const Editor &e, DebugDraw &draw);
void debug_draw_selection(DebugDraw &draw, const Scene &scene, const RenderData &rd, int selected_node);

} // namespace editor