Update positions on 60tps, position of objects can be modified while physic simulation is running. Position is owned by transform comps but the truth is held by the physics engine for affected entities
618 lines
23 KiB
C++
Executable File
618 lines
23 KiB
C++
Executable File
#ifndef EDITOR_HPP
|
|
#define EDITOR_HPP
|
|
|
|
#include <open_engine.hpp>
|
|
|
|
#include "open_engine/renderer/renderer3d.hpp"
|
|
#include "open_engine/scene/components.hpp"
|
|
#include "panels/content_browser.hpp"
|
|
#include "panels/scene_hierarchy.hpp"
|
|
|
|
#include <Jolt/Physics/Collision/Shape/Shape.h>
|
|
#include <Jolt/Math/Real.h>
|
|
#include <Jolt/Math/Vec3.h>
|
|
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
|
#include <glm/ext/matrix_transform.hpp>
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
#include <glm/matrix.hpp>
|
|
#include <entt/entt.hpp>
|
|
#include <glad/glad.h>
|
|
#include <GLFW/glfw3.h>
|
|
#include <ImGuizmo.h>
|
|
#include <filesystem>
|
|
#include <cstdint>
|
|
#include <imgui.h>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
namespace OpenEngine {
|
|
|
|
enum class PlayState
|
|
{
|
|
Edit = 0,
|
|
Play = 1
|
|
};
|
|
|
|
class CameraController : public NativeScriptableEntity
|
|
{
|
|
public:
|
|
void OnCreate()
|
|
{
|
|
};
|
|
|
|
void OnUpdate()
|
|
{
|
|
auto delta = Time::DeltaTime();
|
|
|
|
if (HasComponent<TransformComponent>()) {
|
|
auto& position = GetComponent<TransformComponent>().translation;
|
|
|
|
if (Input::IsKeyPressed(KeyCode::Up))
|
|
position.y += 5.0 * delta;
|
|
if (Input::IsKeyPressed(KeyCode::Down))
|
|
position.y -= 5.0 * delta;
|
|
if (Input::IsKeyPressed(KeyCode::Right))
|
|
position.x += 5.0 * delta;
|
|
if (Input::IsKeyPressed(KeyCode::Left))
|
|
position.x -= 5.0 * delta;
|
|
}
|
|
};
|
|
|
|
void OnDestroy()
|
|
{
|
|
};
|
|
};
|
|
|
|
class EditorLayer : public Layer
|
|
{
|
|
public:
|
|
EditorLayer()
|
|
: Layer("editor_layer")
|
|
{
|
|
}
|
|
~EditorLayer() {};
|
|
|
|
void OnAttach() override
|
|
{
|
|
OE_PROFILE_FUNCTION();
|
|
|
|
FramebufferSpecification specs;
|
|
specs.attachments = { FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::RED_INTEGER, FramebufferTextureFormat::Depth };
|
|
specs.width = 1280;
|
|
specs.height = 720;
|
|
|
|
framebuffer = FrameBuffer::Create(specs);
|
|
|
|
scene = CreateRef<Scene>();
|
|
|
|
auto command_line_args = Application::Get().GetCommandLineArgs();
|
|
if (command_line_args.count > 1) {
|
|
auto scene_path = command_line_args[1];
|
|
SceneSerializer serializer(scene);
|
|
serializer.Deserialize(scene_path);
|
|
}
|
|
|
|
editor_camera = EditorCamera(30.0f, 1920.0f/1080.0f, 0.1f, 1000.0f);
|
|
|
|
// TODO: Add license
|
|
icons["play"] = Texture2D::Create("resources/textures/icons/play.png");
|
|
icons["stop"] = Texture2D::Create("resources/textures/icons/stop.png");
|
|
|
|
// =============================================
|
|
Entity cube = scene->CreateEntity("cube");
|
|
|
|
auto& tc = cube.AddComponent<TransformComponent>();
|
|
|
|
Ref<Mesh> mesh = CreateCube(cube);
|
|
|
|
auto& mc = cube.AddComponent<MeshComponent>(mesh);
|
|
mc.primitive_type = PrimitiveType::Cube;
|
|
|
|
// =============================================
|
|
Entity quad = scene->CreateEntity("quad");
|
|
quad.AddComponent<TransformComponent>();
|
|
quad.GetComponents<TransformComponent>().translation = {0, 0, 0};
|
|
|
|
Ref<Mesh> quad_mesh = CreateQuad(quad, true);
|
|
|
|
auto& mc3 = quad.AddComponent<MeshComponent>(quad_mesh);
|
|
mc3.primitive_type = PrimitiveType::Quad;
|
|
|
|
// =============================================
|
|
Entity cube2 = scene->CreateEntity("cube2");
|
|
cube2.AddComponent<TransformComponent>();
|
|
cube2.GetComponents<TransformComponent>().translation = {2, 0, 0};
|
|
|
|
Ref<Mesh> mesh2 = CreateCube(cube2);
|
|
|
|
auto& mc2 = cube2.AddComponent<MeshComponent>(mesh2);
|
|
mc2.primitive_type = PrimitiveType::Cube;
|
|
|
|
/*
|
|
auto view = scene->GetRegistry().view<TagComponent>();
|
|
|
|
for (auto& entity : view) {
|
|
auto tag = view->get(entity);
|
|
OE_DEBUG("entity: {}, tag: {}", (uint32_t)entity, tag.tag);
|
|
}
|
|
*/
|
|
|
|
//icons["folder"] = Texture2D::Create("resources/textures/icons/folder.png");
|
|
/*
|
|
for (float i = 0; i < 200; i++) {
|
|
for (float y = 0; y < 200; y++) {
|
|
Entity entity = scene->CreateEntity("entity");
|
|
entities.push_back(entity);
|
|
auto& tc = entity.AddComponents<TransformComponent>();
|
|
tc.translation = { i / 10, y / 10, 0.0f };
|
|
tc.scale = { 0.1f, 0.1f, 1.0f };
|
|
auto& sprite = entity.AddComponents<SpriteRendererComponent>();
|
|
sprite.color = { i / 100.0f, y / 100.0f, 1.0f, 1.0f };
|
|
}
|
|
}
|
|
*/
|
|
|
|
scene_hierarchy.Init(scene);
|
|
}
|
|
|
|
Ref<Texture2D> GetIcon(const char* name)
|
|
{
|
|
if (icons.contains(name))
|
|
return icons[name];
|
|
return nullptr;
|
|
}
|
|
|
|
void OnDetach() override
|
|
{
|
|
}
|
|
|
|
void OnUpdate() override
|
|
{
|
|
FramebufferSpecification spec = framebuffer->GetSpecification();
|
|
|
|
if (viewport_size.x > 0.0f && viewport_size.y > 0.0f &&
|
|
(spec.width != viewport_size.x || spec.height != viewport_size.y)) {
|
|
OE_PROFILE_SCOPE("Setting up Rendering");
|
|
|
|
framebuffer->Resize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y);
|
|
|
|
editor_camera.SetViewportSize(viewport_size.x, viewport_size.y);
|
|
scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y);
|
|
}
|
|
|
|
framebuffer->Bind();
|
|
//Renderer2D::ResetStats();
|
|
RenderCommand::SetClearColor({0.11f, 0.11f, 0.15f, 1.0f});
|
|
RenderCommand::Clear();
|
|
framebuffer->ClearBufferI(1, -1);
|
|
|
|
switch (state) {
|
|
case PlayState::Play: {
|
|
scene->OnUpdateRuntime();
|
|
break;
|
|
}
|
|
case PlayState::Edit: {
|
|
if (viewport_focused)
|
|
editor_camera.OnUpdate();
|
|
scene->OnUpdateEditor(editor_camera);
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto [mx, my] = ImGui::GetMousePos();
|
|
|
|
mx -= viewport_bounds[0].x;
|
|
my -= viewport_bounds[0].y;
|
|
auto viewport_size= viewport_bounds[1] - viewport_bounds[0];
|
|
my = viewport_size.y - my;
|
|
|
|
int mouse_x = (int)mx;
|
|
int mouse_y = (int)my;
|
|
|
|
static bool clicked = false;
|
|
// Mouse Picking
|
|
if (Input::IsMouseButtonPressed(MouseCode::ButtonLeft)
|
|
&& !Application::Get().GetImGuiLayer()->GetBlockEvents()
|
|
&& mouse_x >= 0 && mouse_y >= 0
|
|
&& mouse_x < (int)viewport_size.x
|
|
&& mouse_y < (int)viewport_size.y
|
|
&& !ImGuizmo::IsOver()
|
|
&& !Input::IsKeyPressed(KeyCode::LeftAlt)) {
|
|
if (!clicked) {
|
|
int id = framebuffer->ReadPixel(1, mouse_x, mouse_y);
|
|
|
|
selected_entity = id == -1 ?
|
|
Entity() : Entity((entt::entity)id, scene.get());
|
|
|
|
scene_hierarchy.SetSelectedEntity(selected_entity);
|
|
|
|
}
|
|
clicked = true;
|
|
} else {
|
|
clicked = false;
|
|
selected_entity = scene_hierarchy.GetSelectedEntity();
|
|
}
|
|
|
|
framebuffer->Unbind();
|
|
|
|
scene->UpdateEntities();
|
|
}
|
|
|
|
bool EditorKeyBinds(KeyPressedEvent& event)
|
|
{
|
|
if (event.GetRepeatCount() > 0)
|
|
return false;
|
|
bool control = Input::IsKeyPressed(Key::LeftControl) || Input::IsKeyPressed(Key::RightControl);
|
|
bool shift = Input::IsKeyPressed(Key::LeftShift) || Input::IsKeyPressed(Key::RightShift);
|
|
|
|
switch(event.GetKeyCode()) {
|
|
case KeyCode::S: {
|
|
if (control) {
|
|
std::string file = FileDialogs::SaveFile("useless");
|
|
OE_TRACE("saving to filename: {}", file);
|
|
SaveScene(file);
|
|
}
|
|
break;
|
|
}
|
|
case KeyCode::N: {
|
|
if (control) {
|
|
scene = CreateRef<Scene>();
|
|
scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y);
|
|
scene_hierarchy.Init(scene);
|
|
}
|
|
break;
|
|
}
|
|
case KeyCode::O: {
|
|
if (control) {
|
|
std::string file = FileDialogs::OpenFile("useless");
|
|
OE_DEBUG("loading scene: {}", file);
|
|
OpenScene(file);}
|
|
break;
|
|
}
|
|
case KeyCode::Q: {
|
|
guizmo_operation = -1;
|
|
break;
|
|
}
|
|
case KeyCode::W: {
|
|
guizmo_operation = ImGuizmo::OPERATION::TRANSLATE;
|
|
break;
|
|
}
|
|
case KeyCode::E: {
|
|
guizmo_operation = ImGuizmo::OPERATION::ROTATE;
|
|
break;
|
|
}
|
|
case KeyCode::R: {
|
|
guizmo_operation = ImGuizmo::OPERATION::SCALE;
|
|
break;
|
|
}
|
|
case KeyCode::Delete: {
|
|
scene->MarkEntityForDeletion(selected_entity);
|
|
scene_hierarchy.ClearSelection();
|
|
selected_entity = {};
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
void OnEvent(Event& event) override
|
|
{
|
|
OE_PROFILE_FUNCTION();
|
|
|
|
editor_camera.OnEvent(event);
|
|
|
|
EventDispatcher dispatcher(event);
|
|
dispatcher.Dispatch<KeyPressedEvent>(BIND_EVENT_FN(EditorLayer::EditorKeyBinds));
|
|
};
|
|
|
|
float remap(float value, float minInput, float maxInput, float minOutput, float maxOutput) {
|
|
// 1. Normalize the input to a 0.0 - 1.0 range
|
|
float t = (value - minInput) / (maxInput - minInput);
|
|
|
|
// 2. Use glm::mix to interpolate between the output range
|
|
return glm::mix(minOutput, maxOutput, t);
|
|
};
|
|
|
|
glm::vec3 ScreenToWorld(
|
|
float screenX,
|
|
float screenY,
|
|
float screenZ, // depth value (0.0 = near plane, 1.0 = far plane)
|
|
const glm::mat4& view,
|
|
const glm::mat4& projection,
|
|
int viewportWidth,
|
|
int viewportHeight)
|
|
{
|
|
// 1. Convert screen coordinates to normalized device coordinates (NDC)
|
|
// Screen space: origin at top-left, Y points down
|
|
// NDC space: origin at center, range [-1, 1] for x and y
|
|
|
|
float x = (2.0f * screenX) / viewportWidth - 1.0f;
|
|
float y = 1.0f - (2.0f * screenY) / viewportHeight; // Flip Y
|
|
float z = 2.0f * screenZ - 1.0f; // [0,1] -> [-1,1]
|
|
|
|
glm::vec4 clipCoords(x, y, z, 1.0f);
|
|
|
|
// 2. Transform from clip space to view space
|
|
glm::mat4 invProjection = glm::inverse(projection);
|
|
glm::vec4 viewCoords = invProjection * clipCoords;
|
|
|
|
// 3. Transform from view space to world space
|
|
glm::mat4 invView = glm::inverse(view);
|
|
glm::vec4 worldCoords = invView * viewCoords;
|
|
|
|
// 4. Perspective divide
|
|
if (worldCoords.w != 0.0f) {
|
|
worldCoords /= worldCoords.w;
|
|
}
|
|
|
|
return glm::vec3(worldCoords);
|
|
};
|
|
|
|
void DrawViewport()
|
|
{
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 });
|
|
ImGui::Begin("Viewport");
|
|
|
|
bool was_focused = viewport_focused;
|
|
viewport_focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow);
|
|
viewport_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow);
|
|
Application::Get().GetImGuiLayer()->SetBlockEvents(ImGui::IsAnyItemActive());
|
|
|
|
if (viewport_focused && !was_focused)
|
|
editor_camera.ResetMousePosition();
|
|
|
|
ImVec2 viewport_panel_size = ImGui::GetContentRegionAvail();
|
|
|
|
viewport_size = { viewport_panel_size.x, viewport_panel_size.y };
|
|
|
|
ImVec2 imgui_cursor_position = ImGui::GetCursorScreenPos();
|
|
|
|
uint32_t texture_id = framebuffer->GetColorAttachmentRendererID();
|
|
ImGui::Image((void*)(uint64_t)texture_id, ImVec2{ viewport_size.x, viewport_size.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 });
|
|
|
|
if (ImGui::BeginDragDropTarget()) {
|
|
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("CONTENT_BROWSER_PAYLOAD")) {
|
|
std::filesystem::path path = (const char*)payload->Data;
|
|
if (path.extension() == ".oes")
|
|
OpenScene(path);
|
|
}
|
|
ImGui::EndDragDropTarget();
|
|
}
|
|
|
|
viewport_bounds[0] = { imgui_cursor_position.x, imgui_cursor_position.y };
|
|
viewport_bounds[1] = { imgui_cursor_position.x + viewport_size.x,
|
|
imgui_cursor_position.y + viewport_size.y };
|
|
|
|
DrawGuizmos();
|
|
|
|
ImGui::End();
|
|
ImGui::PopStyleVar();
|
|
};
|
|
|
|
void DrawStats()
|
|
{
|
|
//auto stats = Renderer2D::GetStats();
|
|
static float time = 0;
|
|
|
|
time += Time::DeltaTime();
|
|
|
|
if (time >= 1) {
|
|
time = 0;
|
|
}
|
|
|
|
ImGui::Begin("Statistics");
|
|
ImGui::SeparatorText("Performance");
|
|
ImGui::Text("FPS: %0.0f", 1 / Time::DeltaTime());
|
|
//ImGui::Text("Renderer2D:");
|
|
//ImGui::Text("\t\tDraw calls: %d", stats.draw_calls);
|
|
//ImGui::Text("\t\tQuad count: %d", stats.quad_count);
|
|
//ImGui::Text("\t\tVertices count: %d", stats.GetTotalVertexCount());
|
|
//ImGui::Text("\t\tIndices count: %d", stats.GetTotalIndexCount());
|
|
//if (selected_entity) {
|
|
// ImGui::SeparatorText("Entities");
|
|
// ImGui::Text("Selected entity:");
|
|
// ImGui::Text("\t\tname: %s", selected_entity.GetComponents<TagComponent>().tag.c_str());
|
|
// ImGui::Text("\t\tid: %d", (uint32_t)selected_entity);
|
|
//}
|
|
ImGui::End();
|
|
};
|
|
|
|
void DrawGuizmos()
|
|
{
|
|
Entity selected_entity = scene_hierarchy.GetSelectedEntity();
|
|
if (selected_entity && selected_entity.HasComponent<TransformComponent>() && guizmo_operation != -1) {
|
|
ImGuizmo::SetOrthographic(false);
|
|
ImGuizmo::SetDrawlist();
|
|
ImGuizmo::Enable(!editor_camera.GetMoving());
|
|
|
|
ImGuizmo::SetRect(viewport_bounds[0].x, viewport_bounds[0].y,
|
|
viewport_bounds[1].x - viewport_bounds[0].x,
|
|
viewport_bounds[1].y - viewport_bounds[0].y);
|
|
|
|
|
|
const glm::mat4& camera_projection = editor_camera.GetProjection();
|
|
glm::mat4 camera_view = editor_camera.GetViewMatrix();
|
|
|
|
auto& transform_comp = selected_entity.GetComponents<TransformComponent>();
|
|
glm::mat4 transform = GetTransformFromComp(transform_comp);
|
|
|
|
bool snap = Input::IsKeyPressed(KeyCode::LeftControl);
|
|
float snap_value = 0.1f;
|
|
if (guizmo_operation == ImGuizmo::OPERATION::ROTATE)
|
|
snap_value = 10.0f;
|
|
|
|
float snap_values[] = { snap_value, snap_value, snap_value };
|
|
|
|
ImGuizmo::Manipulate(glm::value_ptr(camera_view), glm::value_ptr(camera_projection),
|
|
(ImGuizmo::OPERATION)guizmo_operation, ImGuizmo::LOCAL,
|
|
glm::value_ptr(transform), nullptr,
|
|
snap ? snap_values : nullptr);
|
|
|
|
if (ImGuizmo::IsUsing()) {
|
|
glm::vec3 translation, rotation, scale;
|
|
Math::DecomposeTransform(transform, translation, rotation, scale);
|
|
|
|
glm::vec3 delta_rotation = rotation - transform_comp.rotation;
|
|
transform_comp.translation = translation;
|
|
transform_comp.rotation += delta_rotation;
|
|
transform_comp.scale = scale;
|
|
}
|
|
} else
|
|
guizmo_operation = -1;
|
|
};
|
|
|
|
void OpenScene(const std::string& file)
|
|
{
|
|
if (!file.empty()) {
|
|
scene = CreateRef<Scene>();
|
|
scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y);
|
|
scene_hierarchy.Init(scene);
|
|
|
|
SceneSerializer serializer(scene);
|
|
serializer.Deserialize(file);
|
|
}
|
|
}
|
|
|
|
void SaveScene(const std::string& file)
|
|
{
|
|
if (!file.empty()) {
|
|
SceneSerializer serializer(scene);
|
|
serializer.Serialize(file);
|
|
}
|
|
}
|
|
|
|
void DrawPlayBar()
|
|
{
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0, 2 });
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, { 0, 0 });
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, 0 });
|
|
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.200f, 0.207f, 0.286f, 1.0f });
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0, 0, 0, 0 });
|
|
|
|
ImGui::Begin("##play_state_bar", nullptr, ImGuiWindowFlags_NoDecoration
|
|
| ImGuiWindowFlags_NoScrollWithMouse
|
|
| ImGuiWindowFlags_NoScrollbar
|
|
| ImGuiWindowFlags_NoTitleBar);
|
|
|
|
float size = ImGui::GetContentRegionAvail().y;
|
|
|
|
auto icon = state == PlayState::Play ? icons["stop"] : icons["play"];
|
|
|
|
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x * 0.5) - (size * 0.5) );
|
|
if (ImGui::ImageButton("##play_button",
|
|
(ImTextureID)icon->GetID(), { size, size })) {
|
|
switch (state) {
|
|
case PlayState::Play : {
|
|
OnSceneStop();
|
|
break;
|
|
}
|
|
case PlayState::Edit : {
|
|
OnScenePlay();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleColor(3);
|
|
ImGui::PopStyleVar(3);
|
|
}
|
|
|
|
void OnImGuiRender() override
|
|
{
|
|
OE_PROFILE_FUNCTION();
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(450, 35));
|
|
{
|
|
if (ImGui::BeginMainMenuBar()) {
|
|
if (ImGui::BeginMenu("File")) {
|
|
if (ImGui::MenuItem("Save Scene As", "Ctrl+S")) {
|
|
std::string file = FileDialogs::SaveFile("useless");
|
|
OE_TRACE("saving to filename: {}", file);
|
|
SaveScene(file);
|
|
}
|
|
if (ImGui::MenuItem("Open Scene", "Ctrl+O")) {
|
|
std::string file = FileDialogs::OpenFile("useless");
|
|
OE_DEBUG("loading scene: {}", file);
|
|
OpenScene(file);
|
|
}
|
|
if (ImGui::MenuItem("New Scene", "Ctrl+N")) {
|
|
scene = CreateRef<Scene>();
|
|
scene->OnViewportResize((uint32_t)viewport_size.x, (uint32_t)viewport_size.y);
|
|
scene_hierarchy.Init(scene);
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Exit"))
|
|
Application::Get().Close();
|
|
ImGui::EndMenu();
|
|
}
|
|
ImGui::EndMainMenuBar();
|
|
}
|
|
}
|
|
|
|
ImGui::DockSpaceOverViewport();
|
|
|
|
DrawStats();
|
|
scene_hierarchy.OnImGuiRender();
|
|
browser.OnImGuiRender();
|
|
DrawViewport();
|
|
DrawPlayBar();
|
|
|
|
ImGui::ShowDemoWindow();
|
|
ImGui::PopStyleVar();
|
|
};
|
|
|
|
void OnScenePlay()
|
|
{
|
|
state = PlayState::Play;
|
|
scene->OnRuntimeStart();
|
|
}
|
|
void OnSceneStop()
|
|
{
|
|
state = PlayState::Edit;
|
|
scene->OnRuntimeStop();
|
|
}
|
|
|
|
private:
|
|
ContentBrowserPanel browser;
|
|
std::vector<Time::ProfilingResult> profiling_results;
|
|
|
|
glm::vec2 world_pos_cursor = { 0.0f, 0.0f };
|
|
|
|
glm::vec2 viewport_size = { 0.0f, 0.0f };
|
|
Ref<OpenEngine::FrameBuffer> framebuffer;
|
|
glm::vec2 viewport_bounds[2];
|
|
|
|
bool viewport_focused = false, viewport_hovered = false;
|
|
|
|
SceneHierarchy scene_hierarchy;
|
|
|
|
EditorCamera editor_camera;
|
|
|
|
int guizmo_operation = -1;
|
|
|
|
// TODO: Rename this to GameState
|
|
PlayState state = PlayState::Edit;
|
|
|
|
std::unordered_map<const char*, Ref<Texture2D>> icons;
|
|
Ref<Scene> scene;
|
|
Entity selected_entity;
|
|
|
|
std::vector<Entity> entities;
|
|
};
|
|
}
|
|
|
|
class EditorApp : public OpenEngine::Application
|
|
{
|
|
public:
|
|
EditorApp(OpenEngine::ApplicationCommandLineArgs args);
|
|
~EditorApp();
|
|
};
|
|
|
|
#endif // EDITOR_HPP
|