diff --git a/.gitmodules b/.gitmodules index f73a0f7..e7a690a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -20,3 +20,6 @@ [submodule "Prism/vendor/EnTT"] path = Prism/vendor/EnTT url = https://github.com/skypjack/entt.git +[submodule "Prism/vendor/yaml-cpp"] + path = Prism/vendor/yaml-cpp + url = https://github.com/jbeder/yaml-cpp diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index 731ffa2..683e33a 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -6,6 +6,7 @@ #include "ImGuizmo.h" #include "Prism/Core/Input.h" #include "Prism/Renderer/Renderer2D.h" +#include "Prism/Script/ScriptEngine.h" namespace Prism { @@ -119,7 +120,7 @@ namespace Prism } EditorLayer::EditorLayer() - : m_SceneType(SceneType::Model) + : m_SceneType(SceneType::Model), m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)) { } @@ -129,103 +130,16 @@ namespace Prism void EditorLayer::OnAttach() { - const auto environment = Environment::Load("assets/env/birchwood_4k.hdr"); - - // Model Scene - { - m_Scene = Ref::Create("Model Scene"); - m_CameraEntity = m_Scene->CreateEntity("Camera Entity"); - m_CameraEntity.AddComponent(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))); - - m_Scene->SetEnvironment(environment); - - m_MeshEntity = m_Scene->CreateEntity("test Entity"); - auto mesh = Ref::Create("assets/meshes/TestScene.fbx"); - // auto mesh = Ref::Create("assets/models/Apollo AE/Apollo AE.fbx"); - // auto mesh = Ref::Create("assets/models/tafi/塔菲.pmx"); - m_MeshEntity.AddComponent(mesh); - - m_MeshMaterial = mesh->GetMaterial(); - - m_MeshEntity.AddComponent("Example.Script"); - - // Test Sandbox - auto mapGenerator = m_Scene->CreateEntity("Map Generator"); - mapGenerator.AddComponent("Example.MapGenerator"); - - /* - auto secondEntity = m_Scene->CreateEntity("Gun Entity"); - secondEntity->Transform() = glm::translate(glm::mat4(1.0f), { 5, 5, 5 }) * glm::scale(glm::mat4(1.0f), {10, 10, 10}); - mesh = Ref::Create("assets/models/m1911/M1911Materials.fbx"); - secondEntity->SetMesh(mesh); - */ - } - - // Sphere Scene - { - m_SphereScene = Ref::Create("PBR Sphere Scene"); - auto cameraEntity = m_SphereScene->CreateEntity("camera"); - cameraEntity.AddComponent(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))); - - m_SphereScene->SetEnvironment(environment); - - auto sphereMesh = Ref::Create("assets/models/Sphere1m.fbx"); - m_SphereBaseMaterial = sphereMesh->GetMaterial(); - - float x = -4.0f; - float roughness = 0.0f; - for (int i = 0; i < 8; i++) - { - auto sphereEntity = m_SphereScene->CreateEntity(); - - Ref mi = Ref::Create(m_SphereBaseMaterial); - mi->Set("u_Metalness", 1.0f); - mi->Set("u_Roughness", roughness); - x += 1.1f; - roughness += 0.15f; - m_MetalSphereMaterialInstances.push_back(mi); - - /* - sphereEntity->SetMesh(sphereMesh); - sphereEntity->SetMaterial(mi); - sphereEntity->Transform() = glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f)); - */ - } - - x = -4.0f; - roughness = 0.0f; - for (int i = 0; i < 8; i++) - { - auto sphereEntity = m_SphereScene->CreateEntity(); - - Ref mi(new MaterialInstance(m_SphereBaseMaterial)); - mi->Set("u_Metalness", 0.0f); - mi->Set("u_Roughness", roughness); - x += 1.1f; - roughness += 0.15f; - m_DielectricSphereMaterialInstances.push_back(mi); - - /* - sphereEntity->SetMesh(sphereMesh); - sphereEntity->SetMaterial(mi); - sphereEntity->Transform() = glm::translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f)); - */ - } - } - - m_ActiveScene = m_Scene; - m_SceneHierarchyPanel = CreateScope(m_ActiveScene); - - // Editor m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga"); + m_PlayButtonTex = Texture2D::Create("assets/editor/PlayButton.png"); - // set lights - auto& light = m_Scene->GetLight(); - light.Direction = { -0.5f, -0.5f, 1.0f }; - light.Radiance = { 1.0f, 1.0f, 1.0f }; + m_EditorScene = Ref::Create(); + ScriptEngine::SetSceneContext(m_EditorScene); + m_SceneHierarchyPanel = CreateScope(m_EditorScene); + m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1)); + m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1)); - m_CurrentlySelectedTransform = &m_MeshEntity.Transform(); } void EditorLayer::OnDetach() @@ -234,72 +148,61 @@ namespace Prism void EditorLayer::OnUpdate(const TimeStep deltaTime) { - // THINGS TO LOOK AT: - // - BRDF LUT - // - Tonemapping and proper HDR pipeline - using namespace Prism; - using namespace glm; - - m_MeshMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color); - m_MeshMaterial->Set("u_Metalness", m_MetalnessInput.Value); - m_MeshMaterial->Set("u_Roughness", m_RoughnessInput.Value); - m_MeshMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f); - m_MeshMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f); - m_MeshMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f); - m_MeshMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f); - m_MeshMaterial->Set("u_EnvMapRotation", m_EnvMapRotation); - - m_SphereBaseMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color); - // m_SphereBaseMaterial->Set("lights", m_Light); - m_SphereBaseMaterial->Set("lights", m_Scene->GetLight()); - m_SphereBaseMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f); - m_SphereBaseMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f); - m_SphereBaseMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f); - m_SphereBaseMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f); - m_SphereBaseMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f); - m_SphereBaseMaterial->Set("u_EnvMapRotation", m_EnvMapRotation); - - - if (m_AlbedoInput.TextureMap) - m_MeshMaterial->Set("u_AlbedoTexture", m_AlbedoInput.TextureMap); - if (m_NormalInput.TextureMap) - m_MeshMaterial->Set("u_NormalTexture", m_NormalInput.TextureMap); - if (m_MetalnessInput.TextureMap) - m_MeshMaterial->Set("u_MetalnessTexture", m_MetalnessInput.TextureMap); - if (m_RoughnessInput.TextureMap) - m_MeshMaterial->Set("u_RoughnessTexture", m_RoughnessInput.TextureMap); - - /* - if (m_AllowViewportCameraEvents) - m_Scene->GetCamera().OnUpdate(deltaTime); - */ - - m_ActiveScene->OnUpdate(deltaTime); - - if (m_DrawOnTopBoundingBoxes) + switch (m_SceneState) { - Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); - auto viewProj = m_CameraEntity.GetComponent().Camera.GetViewProjection(); - Renderer2D::BeginScene(viewProj, false); - Renderer::DrawAABB(m_MeshEntity.GetComponent(), m_MeshEntity.GetComponent()); + case SceneState::Edit: + { + if (m_ViewportPanelFocused) + m_EditorCamera.OnUpdate(deltaTime); - Renderer2D::EndScene(); - Renderer::EndRenderPass(); - } + m_EditorScene->OnRenderEditor(deltaTime, m_EditorCamera); - if (!m_SelectionContext.empty()) - { - auto& selection = m_SelectionContext[0]; + if (m_DrawOnTopBoundingBoxes) + { + Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); + auto viewProj = m_EditorCamera.GetViewProjection(); + Renderer2D::BeginScene(viewProj, false); + // TODO: Renderer::DrawAABB(m_MeshEntity.GetComponent(), m_MeshEntity.GetComponent()); + Renderer2D::EndScene(); + Renderer::EndRenderPass(); + } - Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); - const auto viewProj = m_CameraEntity.GetComponent().Camera.GetViewProjection(); - Renderer2D::BeginScene(viewProj, false); + if (m_SelectionContext.size() && false) + { + auto& selection = m_SelectionContext[0]; - const glm::vec4 color = (m_SelectionMode == SelectionMode::Entity) ? glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f } : glm::vec4{ 0.2f, 0.9f, 0.2f, 1.0f }; - Renderer::DrawAABB(selection.Mesh->BoundingBox, selection.Entity.GetComponent().Transform * selection.Mesh->Transform, color); + if (selection.Mesh && selection.Entity.HasComponent()) + { + Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); + auto viewProj = m_EditorCamera.GetViewProjection(); + Renderer2D::BeginScene(viewProj, false); + glm::vec4 color = (m_SelectionMode == SelectionMode::Entity) ? glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f } : glm::vec4{ 0.2f, 0.9f, 0.2f, 1.0f }; + Renderer::DrawAABB(selection.Mesh->BoundingBox, selection.Entity.GetComponent().Transform * selection.Mesh->Transform, color); + Renderer2D::EndScene(); + Renderer::EndRenderPass(); + } + } + break; + } + + case SceneState::Play: + { + if (m_ViewportPanelFocused) + m_EditorCamera.OnUpdate(deltaTime); + + m_RuntimeScene->OnUpdate(deltaTime); + m_RuntimeScene->OnRenderRuntime(deltaTime); + break; + } + case SceneState::Pause: + { + if (m_ViewportPanelFocused) + m_EditorCamera.OnUpdate(deltaTime); + + m_RuntimeScene->OnRenderRuntime(deltaTime); + break; + } - Renderer2D::EndScene(); - Renderer::EndRenderPass(); } } @@ -365,119 +268,80 @@ namespace Prism // Show demo options and help if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Options")) + if (ImGui::BeginMenu("file")) { - // Disabling fullscreen would allow the window to be moved to the front of other windows, - // which we can't undo at the moment without finer window depth/z control. - ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen); - ImGui::MenuItem("Padding", NULL, &opt_padding); + if (ImGui::MenuItem("New Scene")) + { + + } + if (ImGui::MenuItem("Open Scene...")) + { + auto& app = Application::Get(); + std::string filepath = app.OpenFile(R"(Scene file (*.hsc)*.hsc)"); + if (!filepath.empty()) + { + Ref newScene = Ref::Create(); + SceneSerializer serializer(newScene); + serializer.Deserialize(filepath); + m_EditorScene = newScene; + m_SceneHierarchyPanel->SetContext(m_EditorScene); + ScriptEngine::SetSceneContext(m_EditorScene); + + m_EditorScene->SetSelectedEntity({}); + m_SelectionContext.clear(); + } + } + if (ImGui::MenuItem("Save Scene...")) + { + auto& app = Application::Get(); + std::string filepath = app.SaveFile(R"(Scene file (*.hsc)*.hsc)"); + SceneSerializer serializer(m_EditorScene); + serializer.Serialize(filepath); + } + ImGui::Separator(); - if (ImGui::MenuItem("Flag: NoDockingOverCentralNode", "", - (dockspace_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) != 0)) - { - dockspace_flags ^= ImGuiDockNodeFlags_NoDockingOverCentralNode; - } - if (ImGui::MenuItem("Flag: NoDockingSplit", "", - (dockspace_flags & ImGuiDockNodeFlags_NoDockingSplit) != 0)) - { - dockspace_flags ^= ImGuiDockNodeFlags_NoDockingSplit; - } - if (ImGui::MenuItem("Flag: NoUndocking", "", (dockspace_flags & ImGuiDockNodeFlags_NoUndocking) != 0)) - { - dockspace_flags ^= ImGuiDockNodeFlags_NoUndocking; - } - if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) - { - dockspace_flags ^= ImGuiDockNodeFlags_NoResize; - } - if (ImGui::MenuItem("Flag: AutoHideTabBar", "", - (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) - { - dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; - } - if (ImGui::MenuItem("Flag: PassthruCentralNode", "", - (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, opt_fullscreen)) - { - dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; - } + if (ImGui::MenuItem("Reload C# Assembly")) + ScriptEngine::ReloadAssembly("assets/scripts/ExampleApp.dll"); ImGui::Separator(); - - if (ImGui::MenuItem("Close", NULL, false, p_open != false)) + if (ImGui::MenuItem("Exit")) p_open = false; ImGui::EndMenu(); } - if (ImGui::BeginMenu("Help")) - { - ImGui::TextUnformatted( - "This demo has nothing to do with enabling docking!" "\n" - "This demo only demonstrate the use of ImGui::DockSpace() which allows you to manually\ncreate a docking node _within_ another window." - "\n" - "Most application can simply call ImGui::DockSpaceOverViewport() and be done with it."); - ImGui::Separator(); - ImGui::TextUnformatted( - "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n" - "- Drag from window title bar or their tab to dock/undock." "\n" - "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" - "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n" - "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)"); - ImGui::Separator(); - ImGui::TextUnformatted("More details:"); - ImGui::Bullet(); - ImGui::SameLine(); - ImGui::TextLinkOpenURL("Docking Wiki page", "https://github.com/ocornut/imgui/wiki/Docking"); - ImGui::BulletText("Read comments in ShowExampleAppDockSpace()"); - ImGui::EndMenu(); - } - - ImGuiShowHelpMarker( - "You can _always_ dock _any_ window into another by holding the SHIFT key while moving a window. Try it now!" - "\n" - "This demo app has nothing to do with it!" "\n\n" - "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." - "\n\n" - "ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." - "\n\n" - "(NB: because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node, because that window is submitted as part of the NewFrame() call. An easy workaround is that you can create your own implicit \"Debug##2\" window after calling DockSpace() and leave it in the window stack for anyone to use.)" - ); ImGui::EndMenuBar(); } m_SceneHierarchyPanel->OnImGuiRender(); + + ScriptEngine::OnImGuiRender(); + ImGui::End(); #endif // Editor Panel ------------------------------------------------------------------------------ ImGui::Begin("Model"); - - if (ImGui::RadioButton("Spheres", (int*)&m_SceneType, (int)SceneType::Spheres)) - m_ActiveScene = m_SphereScene; - ImGui::SameLine(); - if (ImGui::RadioButton("Model", (int*)&m_SceneType, (int)SceneType::Model)) - m_ActiveScene = m_Scene; - ImGui::Begin("Environment"); if (ImGui::Button("Load Environment Map")) { std::string filename = Application::Get().OpenFile("*.hdr"); if (!filename.empty()) - m_ActiveScene->SetEnvironment(Environment::Load(filename)); + m_EditorScene->SetEnvironment(Environment::Load(filename)); } - ImGui::SliderFloat("Skybox LOD", &m_Scene->GetSkyboxLod(), 0.0f, 11.0f); + ImGui::SliderFloat("Skybox LOD", &m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f); ImGui::Columns(2); ImGui::AlignTextToFramePadding(); - auto& light = m_Scene->GetLight(); + auto& light = m_EditorScene->GetLight(); Property("Light Direction", light.Direction); Property("Light Radiance", light.Radiance, PropertyFlag::ColorProperty); Property("Light Multiplier", light.Multiplier, 0.0f, 5.0f); - Property("Exposure", m_CameraEntity.GetComponent().Camera.GetExposure(), 0.0f, 5.0f); + Property("Exposure", m_EditorCamera.GetExposure(), 0.0f, 5.0f); Property("Radiance Prefiltering", m_RadiancePrefilter); Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f); @@ -500,6 +364,7 @@ namespace Prism ImGui::Separator(); { ImGui::Text("Mesh"); + /* auto meshComponent = m_MeshEntity.GetComponent(); std::string fullpath = meshComponent.Mesh ? meshComponent.Mesh->GetFilePath() : "None"; size_t found = fullpath.find_last_of("/\\"); @@ -517,6 +382,7 @@ namespace Prism meshComponent.Mesh = newMesh; } } + */ } ImGui::Separator(); @@ -679,14 +545,57 @@ namespace Prism ImGui::End(); + + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 4)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8f, 0.8f, 0.8f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0)); + ImGui::Begin("Toolbar"); + if (m_SceneState == SceneState::Edit) + { + if (ImGui::ImageButton("editbutton", (ImTextureID)(m_PlayButtonTex->GetRendererID()), ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0,0,0,0), ImVec4(0.9f, 0.9f, 0.9f, 1.0f))) + { + OnScenePlay(); + } + } + else if (m_SceneState == SceneState::Play) + { + if (ImGui::ImageButton("playbutton", (ImTextureID)(m_PlayButtonTex->GetRendererID()), ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), ImVec4(1.0f, 1.0f, 1.0f, 0.2f))) + { + OnSceneStop(); + } + } + ImGui::SameLine(); + if (ImGui::ImageButton("a", (ImTextureID)(m_PlayButtonTex->GetRendererID()), ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1.0f, 1.0f, 1.0f, 0.6f))) + { + PM_CORE_INFO("PLAY!"); + } + ImGui::End(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::Begin("Viewport"); + m_ViewportPanelMouseOver = ImGui::IsWindowHovered(); + m_ViewportPanelFocused = ImGui::IsWindowFocused(); + auto viewportOffset = ImGui::GetCursorPos(); // includes tab bar auto viewportSize = ImGui::GetContentRegionAvail(); SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_CameraEntity.GetComponent().Camera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); - m_CameraEntity.GetComponent().Camera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + if (m_RuntimeScene) + m_RuntimeScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); + m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); @@ -702,7 +611,7 @@ namespace Prism m_AllowViewportCameraEvents = ImGui::IsMouseHoveringRect(minBound, maxBound); // ImGuizmo - if (m_GizmoType != -1 && m_CurrentlySelectedTransform) + if (m_GizmoType != -1 && !m_SelectionContext.empty()) { auto& selection = m_SelectionContext[0]; @@ -712,25 +621,24 @@ namespace Prism ImGuizmo::SetDrawlist(); ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y, rw, rh); - const auto& camera = m_CameraEntity.GetComponent().Camera; bool snap = Input::IsKeyPressed(Key::LEFT_CONTROL); auto& entityTransform = selection.Entity.Transform(); float snapValue[3] = { m_SnapValue, m_SnapValue, m_SnapValue }; if (m_SelectionMode == SelectionMode::Entity) { - ImGuizmo::Manipulate(glm::value_ptr(camera.GetViewMatrix()), - glm::value_ptr(camera.GetProjectionMatrix()), + ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), + glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, ImGuizmo::LOCAL, - glm::value_ptr(m_MeshEntity.GetComponent().Transform), + glm::value_ptr(entityTransform), nullptr, snap ? snapValue : nullptr); }else { glm::mat4 transformBase = entityTransform * selection.Mesh->Transform; - ImGuizmo::Manipulate(glm::value_ptr(camera.GetViewMatrix()), - glm::value_ptr(camera.GetProjectionMatrix()), + ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), + glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, ImGuizmo::LOCAL, glm::value_ptr(transformBase), @@ -748,10 +656,17 @@ namespace Prism void EditorLayer::OnEvent(Event& e) { - /* - if (m_AllowViewportCameraEvents) - m_Scene->GetCamera().OnEvent(e); - */ + if (m_SceneState == SceneState::Edit) + { + if (m_ViewportPanelMouseOver) + m_EditorCamera.OnEvent(e); + + m_EditorScene->OnEvent(e); + } + else if (m_SceneState == SceneState::Play) + { + m_RuntimeScene->OnEvent(e); + } EventDispatcher dispatcher(e); dispatcher.Dispatch(PM_BIND_EVENT_FN(EditorLayer::OnKeyPressedEvent)); @@ -760,35 +675,57 @@ namespace Prism bool EditorLayer::OnKeyPressedEvent(KeyPressedEvent& e) { - switch (e.GetKeyCode()) + if (m_ViewportPanelFocused) { - case KeyCode::Q: - m_GizmoType = -1; - break; - case KeyCode::W: - m_GizmoType = ImGuizmo::OPERATION::TRANSLATE; - break; - case KeyCode::E: - m_GizmoType = ImGuizmo::OPERATION::ROTATE; - break; - case KeyCode::R: - m_GizmoType = ImGuizmo::OPERATION::SCALE; - break; - case KeyCode::G: - // Toggle grid - if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL)) - SceneRenderer::GetOptions().ShowGrid = !SceneRenderer::GetOptions().ShowGrid; - break; - case KeyCode::B: - // Toggle bounding boxes - if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL)) + switch (e.GetKeyCode()) { + case KeyCode::Q: + m_GizmoType = -1; + break; + case KeyCode::W: + m_GizmoType = ImGuizmo::OPERATION::TRANSLATE; + break; + case KeyCode::E: + m_GizmoType = ImGuizmo::OPERATION::ROTATE; + break; + case KeyCode::R: + m_GizmoType = ImGuizmo::OPERATION::SCALE; + break; + case KeyCode::DELETE: + if (m_SelectionContext.size()) + { + const Entity selectedEntity = m_SelectionContext[0].Entity; + m_EditorScene->DestroyEntity(selectedEntity); + m_SelectionContext.clear(); + m_EditorScene->SetSelectedEntity({}); + m_SceneHierarchyPanel->SetSelected({}); + } + break; + } + } + if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL)) + { + switch (e.GetKeyCode()) + { + case KeyCode::G: + // Toggle grid + SceneRenderer::GetOptions().ShowGrid = !SceneRenderer::GetOptions().ShowGrid; + break; + case KeyCode::B: + // Toggle bounding boxes m_UIShowBoundingBoxes = !m_UIShowBoundingBoxes; ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop); + break; + case KeyCode::D: + if (m_SelectionContext.size()) + { + Entity selectedEntity = m_SelectionContext[0].Entity; + m_EditorScene->DuplicateEntity(selectedEntity); + } + break; } - break; - } + return false; } @@ -803,10 +740,11 @@ namespace Prism auto [origin, direction] = CastRay(mouseX, mouseY); m_SelectionContext.clear(); - const auto meshEntities = m_Scene->GetAllEntitiesWith(); + m_EditorScene->SetSelectedEntity({}); + const auto meshEntities = m_EditorScene->GetAllEntitiesWith(); for (auto e : meshEntities) { - Entity entity = { e, m_Scene.Raw() }; + Entity entity = { e, m_EditorScene.Raw() }; auto mesh = entity.GetComponent().Mesh; if (!mesh) @@ -857,6 +795,20 @@ namespace Prism m_DrawOnTopBoundingBoxes = show && onTop; } + void EditorLayer::SelectEntity(Entity entity) + { + SelectedSubmesh selection; + if (entity.HasComponent()) + { + selection.Mesh = &entity.GetComponent().Mesh->GetSubmeshes()[0]; + } + selection.Entity = entity; + m_SelectionContext.clear(); + m_SelectionContext.push_back(selection); + + m_EditorScene->SetSelectedEntity(entity); + } + std::pair EditorLayer::GetMouseViewportSpace() const { auto [mx, my] = ImGui::GetMousePos(); // Input::GetMousePosition(); @@ -872,11 +824,11 @@ namespace Prism { const glm::vec4 mouseClipPos = { mx, my, -1.0f, 1.0f }; - const auto inverseProj = glm::inverse(m_CameraEntity.GetComponent().Camera.GetProjectionMatrix()); - const auto inverseView = glm::inverse(glm::mat3(m_CameraEntity.GetComponent().Camera.GetViewMatrix())); + const auto inverseProj = glm::inverse(m_EditorCamera.GetProjectionMatrix()); + const auto inverseView = glm::inverse(glm::mat3(m_EditorCamera.GetViewMatrix())); const glm::vec4 ray = inverseProj * mouseClipPos; - glm::vec3 rayPos = m_CameraEntity.GetComponent().Camera.GetPosition(); + glm::vec3 rayPos = m_EditorCamera.GetPosition(); glm::vec3 rayDir = inverseView * glm::vec3(ray); return { rayPos, rayDir }; @@ -885,6 +837,16 @@ namespace Prism void EditorLayer::OnSelected(const SelectedSubmesh& selectionContext) { m_SceneHierarchyPanel->SetSelected(selectionContext.Entity); + m_EditorScene->SetSelectedEntity(selectionContext.Entity); + } + + void EditorLayer::OnEntityDeleted(Entity e) + { + if (m_SelectionContext[0].Entity == e) + { + m_SelectionContext.clear(); + m_EditorScene->SetSelectedEntity({}); + } } Ray EditorLayer::CastMouseRay() @@ -897,4 +859,30 @@ namespace Prism } return Ray::Zero(); } + + void EditorLayer::OnScenePlay() + { + m_SelectionContext.clear(); + + m_SceneState = SceneState::Play; + + m_RuntimeScene = Ref::Create(); + m_EditorScene->CopyTo(m_RuntimeScene); + + m_RuntimeScene->OnRuntimeStart(); + m_SceneHierarchyPanel->SetContext(m_RuntimeScene); + } + + void EditorLayer::OnSceneStop() + { + m_RuntimeScene->OnRuntimeStop(); + m_SceneState = SceneState::Edit; + + // Unload runtime scene + m_RuntimeScene = nullptr; + + m_SelectionContext.clear(); + ScriptEngine::SetSceneContext(m_EditorScene); + m_SceneHierarchyPanel->SetContext(m_EditorScene); + } } diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index ce2d4e6..03530df 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -27,7 +27,7 @@ namespace Prism bool OnMouseButtonPressedEvent(MouseButtonPressedEvent& e); void ShowBoundingBoxes(bool show, bool onTop = false); - + void SelectEntity(Entity entity); private: std::pair GetMouseViewportSpace() const; std::pair CastRay(float mx, float my); @@ -35,23 +35,24 @@ namespace Prism struct SelectedSubmesh { Prism::Entity Entity; - Submesh* Mesh; - float Distance; + Submesh* Mesh = nullptr; + float Distance = 0.0f; }; void OnSelected(const SelectedSubmesh& selectionContext); + void OnEntityDeleted(Entity e); Ray CastMouseRay(); + + void OnScenePlay(); + void OnSceneStop(); private: Scope m_SceneHierarchyPanel; - Ref m_Scene; - Ref m_SphereScene; Ref m_ActiveScene; + Ref m_RuntimeScene, m_EditorScene; - Entity m_MeshEntity; - Entity m_CameraEntity; + EditorCamera m_EditorCamera; Ref m_SphereBaseMaterial; - Ref m_MeshMaterial; std::vector> m_MetalSphereMaterialInstances; @@ -71,14 +72,6 @@ namespace Prism std::vector m_SelectionContext; glm::mat4* m_RelativeTransform = nullptr; - glm::mat4* m_CurrentlySelectedTransform = nullptr; - - // configure button - bool m_AllowViewportCameraEvents = false; - bool m_DrawOnTopBoundingBoxes = false; - - bool m_UIShowBoundingBoxes = false; - bool m_UIShowBoundingBoxesOnTop = false; struct AlbedoInput @@ -119,14 +112,33 @@ namespace Prism float m_EnvMapRotation = 0.0f; + + // Editor resources + Ref m_CheckerboardTex; + Ref m_PlayButtonTex; + + + // configure button + bool m_AllowViewportCameraEvents = false; + bool m_DrawOnTopBoundingBoxes = false; + + bool m_UIShowBoundingBoxes = false; + bool m_UIShowBoundingBoxesOnTop = false; + enum class SceneType : uint32_t { Spheres = 0, Model = 1 }; SceneType m_SceneType; - // Editor resources - Ref m_CheckerboardTex; + bool m_ViewportPanelMouseOver = false; + bool m_ViewportPanelFocused = false; + + enum class SceneState + { + Edit = 0, Play = 1, Pause = 2 + }; + SceneState m_SceneState = SceneState::Edit; }; } diff --git a/Editor/assets/editor/PlayButton.png b/Editor/assets/editor/PlayButton.png new file mode 100644 index 0000000..e5d7ef4 Binary files /dev/null and b/Editor/assets/editor/PlayButton.png differ diff --git a/Editor/assets/scenes/PinkSunrise.hsc b/Editor/assets/scenes/PinkSunrise.hsc new file mode 100644 index 0000000..fb59c8f --- /dev/null +++ b/Editor/assets/scenes/PinkSunrise.hsc @@ -0,0 +1,66 @@ +Scene: Scene Name +Environment: + AssetPath: assets\env\birchwood_4k.hdr + Light: + Direction: [-0.5, -0.5, 1] + Radiance: [1, 1, 1] + Multiplier: 1 +Entities: + - Entity: 1289165777996378215 + TagComponent: + Tag: Sphere + TransformComponent: + Position: [0, 21.9805069, -1.64006281] + Rotation: [1, 0, 0, 0] + Scale: [0.100000024, 0.100000024, 0.100000024] + ScriptComponent: + ModuleName: Example.Sink + StoredFields: + - Name: SinkSpeed + Type: 1 + Data: 5 + MeshComponent: + AssetPath: assets\meshes\Sphere1m.fbx + - Entity: 5178862374589434728 + TagComponent: + Tag: Camera + TransformComponent: + Position: [0, 14.75, 79.75] + Rotation: [0.995602965, -0.0936739072, 0, 0] + Scale: [1, 0.999999821, 0.999999821] + ScriptComponent: + ModuleName: Example.BasicController + StoredFields: + - Name: Speed + Type: 1 + Data: 12 + CameraComponent: + Camera: some camera data... + Primary: true + - Entity: 9095450049242347594 + TagComponent: + Tag: Test Entity + TransformComponent: + Position: [0.248109579, -1.90734863e-06, -0.268640995] + Rotation: [1, 0, 0, 0] + Scale: [1, 1, 1] + ScriptComponent: + ModuleName: Example.Script + StoredFields: + - Name: VerticalSpeed + Type: 1 + Data: 0 + - Name: SinkRate + Type: 1 + Data: 0 + - Name: Speed + Type: 1 + Data: 1 + - Name: Rotation + Type: 1 + Data: 0 + - Name: Velocity + Type: 6 + Data: [0, 0, 0] + MeshComponent: + AssetPath: assets\meshes\TestScene.fbx \ No newline at end of file diff --git a/Editor/assets/shaders/Outline.glsl b/Editor/assets/shaders/Outline.glsl new file mode 100644 index 0000000..c33df2e --- /dev/null +++ b/Editor/assets/shaders/Outline.glsl @@ -0,0 +1,27 @@ +// Outline Shader + +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec2 a_TexCoord; + +uniform mat4 u_ViewProjection; +uniform mat4 u_Transform; + +out vec2 v_TexCoord; + +void main() +{ + gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0); +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 color; + +void main() +{ + color = vec4(1.0, 0.5, 0.0, 1.0); +} \ No newline at end of file diff --git a/Editor/assets/shaders/PBRShader_Anim.glsl b/Editor/assets/shaders/PBRShader_Anim.glsl index a195cec..ca0bc7a 100644 --- a/Editor/assets/shaders/PBRShader_Anim.glsl +++ b/Editor/assets/shaders/PBRShader_Anim.glsl @@ -1,5 +1,5 @@ // ----------------------------- -// -- Hazel Engine PBR shader -- +// -- From Hazel Engine PBR shader -- // ----------------------------- // Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in. // Currently heavily updated. diff --git a/Editor/assets/shaders/PBRShader_Static.glsl b/Editor/assets/shaders/PBRShader_Static.glsl index 8255708..8695a84 100644 --- a/Editor/assets/shaders/PBRShader_Static.glsl +++ b/Editor/assets/shaders/PBRShader_Static.glsl @@ -1,5 +1,5 @@ // ----------------------------- -// -- Hazel Engine PBR shader -- +// -- From Hazel Engine PBR shader -- // ----------------------------- // Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in. // Currently heavily updated. diff --git a/ExampleApp/Src/BasicContorller.cs b/ExampleApp/Src/BasicContorller.cs new file mode 100644 index 0000000..5dfe641 --- /dev/null +++ b/ExampleApp/Src/BasicContorller.cs @@ -0,0 +1,36 @@ +using Prism; + +namespace Example +{ + public class BasicController : Entity + { + public float Speed; + + public void OnCreate() + { + } + + public void OnUpdate(float ts) + { + Mat4 transform = GetTransform(); + Vec3 translation = transform.Translation; + + float speed = Speed * ts; + + if (Input.IsKeyPressed(KeyCode.Up)) + translation.Y += speed; + else if (Input.IsKeyPressed(KeyCode.Down)) + translation.Y -= speed; + if (Input.IsKeyPressed(KeyCode.Right)) + translation.X += speed; + else if (Input.IsKeyPressed(KeyCode.Left)) + translation.X -= speed; + + + transform.Translation = translation; + SetTransform(transform); + } + + + } +} diff --git a/ExampleApp/Src/MapGenerator.cs b/ExampleApp/Src/MapGenerator.cs index a2f19bb..78c2400 100644 --- a/ExampleApp/Src/MapGenerator.cs +++ b/ExampleApp/Src/MapGenerator.cs @@ -10,7 +10,7 @@ namespace Example { public class MapGenerator : Entity { - // TODO: [EditorSlider("MapWidth Custom Name", 2, 0, 1024)] + // [EditorSlider("MapWidth Custom Name", 2, 0, 1024)] public int MapWidth = 128; public int MapHeight = 128; public int Octaves = 4; @@ -20,6 +20,8 @@ namespace Example public Vec2 Offset = new Vec2(13.4f, 6.26f); public float NoiseScale = 0.5f; + public float speed = 0.0f; + public void GenerateMap() { // float[,] noiseMap = Noise.GenerateNoiseMap(MapWidth, MapHeight, NoiseScale); @@ -76,7 +78,19 @@ namespace Example void OnUpdate(float ts) { - + Mat4 transform = GetTransform(); + Vec3 translation = transform.Translation; + translation.Y += ts * speed; + + if (Input.IsKeyPressed(KeyCode.Space)) + { + Console.WriteLine("Space Pressed"); + translation.Y -= 10.0f; + } + + transform.Translation = translation; + + SetTransform(transform); } diff --git a/ExampleApp/Src/Script.cs b/ExampleApp/Src/Script.cs index 6890cf6..474233a 100644 --- a/ExampleApp/Src/Script.cs +++ b/ExampleApp/Src/Script.cs @@ -6,8 +6,13 @@ namespace Example { public class Script : Entity { + public float VerticalSpeed = 5.0f; public float Speed = 5.0f; - + public float Rotation = 0.0f; + public Vec3 Velocity; + public float SinkRate = 1.0f; + + public void OnCreate() { Console.WriteLine("Script.OnCreate"); @@ -16,11 +21,20 @@ namespace Example public void OnUpdate(float ts) { + Rotation += ts; + + Mat4 transform = GetTransform(); Vec3 translation = transform.Translation; float speed = Speed * ts; + translation.X += Velocity.X * ts; + translation.Y += Velocity.Y * ts; + translation.Z += Velocity.Z * ts; + + translation.Y -= SinkRate * ts; + /* if (Input.IsKeyPressed(KeyCode.Up)) translation.Y += speed; else if (Input.IsKeyPressed(KeyCode.Down)) @@ -29,6 +43,7 @@ namespace Example translation.X += speed; else if (Input.IsKeyPressed(KeyCode.Left)) translation.X -= speed; + */ transform.Translation = translation; SetTransform(transform); diff --git a/ExampleApp/Src/Sink.cs b/ExampleApp/Src/Sink.cs new file mode 100644 index 0000000..a02fce6 --- /dev/null +++ b/ExampleApp/Src/Sink.cs @@ -0,0 +1,27 @@ +using Prism; + +namespace Example +{ + class Sink : Entity + { + + public float SinkSpeed = 1.0f; + + void OnCreate() + { + + } + + void OnUpdate(float ts) + { + Mat4 transform = GetTransform(); + Vec3 translation = transform.Translation; + + translation.Y -= SinkSpeed * ts; + + transform.Translation = translation; + SetTransform(transform); + } + + } +} \ No newline at end of file diff --git a/Prism-ScriptCore/Src/Prism/Entity.cs b/Prism-ScriptCore/Src/Prism/Entity.cs index 7e808ca..e29f53c 100644 --- a/Prism-ScriptCore/Src/Prism/Entity.cs +++ b/Prism-ScriptCore/Src/Prism/Entity.cs @@ -4,17 +4,15 @@ namespace Prism { public class Entity { - public uint SceneID { get; private set; } - public uint EntityID { get; private set; } + public ulong ID { get; private set; } ~Entity() { - Console.WriteLine("Destroyed Entity {0}:{1}", SceneID, EntityID); } public T CreateComponent() where T : Component, new() { - CreateComponent_Native(SceneID, EntityID, typeof(T)); + CreateComponent_Native(ID, typeof(T)); T component = new T(); component.Entity = this; return component; @@ -22,7 +20,7 @@ namespace Prism public bool HasComponent() where T : Component, new() { - return HasComponent_Native(SceneID, EntityID, typeof(T)); + return HasComponent_Native(ID, typeof(T)); } public T GetComponent() where T : Component, new() @@ -39,23 +37,23 @@ namespace Prism public Mat4 GetTransform() { Mat4 mat4Instance; - GetTransform_Native(SceneID, EntityID, out mat4Instance); + GetTransform_Native(ID, out mat4Instance); return mat4Instance; } public void SetTransform(Mat4 transform) { - SetTransform_Native(SceneID, EntityID, ref transform); + SetTransform_Native(ID, ref transform); } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void CreateComponent_Native(uint sceneID, uint entityID, Type type); + private static extern void CreateComponent_Native(ulong entityID, Type type); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool HasComponent_Native(uint sceneID, uint entityID, Type type); + private static extern bool HasComponent_Native(ulong entityID, Type type); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void GetTransform_Native(uint sceneID, uint entityID, out Mat4 matrix); + private static extern void GetTransform_Native(ulong entityID, out Mat4 matrix); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void SetTransform_Native(uint sceneID, uint entityID, ref Mat4 matrix); + private static extern void SetTransform_Native(ulong entityID, ref Mat4 matrix); } } diff --git a/Prism-ScriptCore/Src/Prism/Scene/Component.cs b/Prism-ScriptCore/Src/Prism/Scene/Component.cs index 3383b16..8df6510 100644 --- a/Prism-ScriptCore/Src/Prism/Scene/Component.cs +++ b/Prism-ScriptCore/Src/Prism/Scene/Component.cs @@ -14,7 +14,7 @@ namespace Prism { get { - return GetTag_Native(Entity.SceneID, Entity.EntityID); + return GetTag_Native(Entity.ID); } set { @@ -23,7 +23,7 @@ namespace Prism } [MethodImpl(MethodImplOptions.InternalCall)] - public static extern string GetTag_Native(uint sceneID, uint entityID); + public static extern string GetTag_Native(ulong entityID); [MethodImpl(MethodImplOptions.InternalCall)] public static extern void SetTag_Native(string tag); @@ -37,20 +37,20 @@ namespace Prism get { Mat4 result; - GetTransform_Native(Entity.SceneID, Entity.EntityID, out result); + GetTransform_Native(Entity.ID, out result); return result; } set { - SetTransform_Native(Entity.SceneID, Entity.EntityID, ref value); + SetTransform_Native(Entity.ID, ref value); } } [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void GetTransform_Native(uint sceneID, uint entityID, out Mat4 result); + public static extern void GetTransform_Native(ulong entityID, out Mat4 result); [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void SetTransform_Native(uint sceneID, uint entityID, ref Mat4 result); + public static extern void SetTransform_Native(ulong entityID, ref Mat4 result); } @@ -60,21 +60,21 @@ namespace Prism { get { - Mesh result = new Mesh(GetMesh_Native(Entity.SceneID, Entity.EntityID)); + Mesh result = new Mesh(GetMesh_Native(Entity.ID)); return result; } set { IntPtr ptr = value == null ? IntPtr.Zero : value.m_UnmanagedInstance; - SetMesh_Native(Entity.SceneID, Entity.EntityID, ptr); + SetMesh_Native(Entity.ID, ptr); } } [MethodImpl(MethodImplOptions.InternalCall)] - public static extern IntPtr GetMesh_Native(uint sceneID, uint entityID); + public static extern IntPtr GetMesh_Native(ulong entityID); [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void SetMesh_Native(uint sceneID, uint entityID, IntPtr unmanagedInstance); + public static extern void SetMesh_Native(ulong entityID, IntPtr unmanagedInstance); } diff --git a/Prism/CMakeLists.txt b/Prism/CMakeLists.txt index 5516872..2d29362 100644 --- a/Prism/CMakeLists.txt +++ b/Prism/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(vendor/TinyFileDialog EXCLUDE_FROM_ALL) add_subdirectory(vendor/EnTT EXCLUDE_FROM_ALL) add_subdirectory(vendor/mono EXCLUDE_FROM_ALL) add_subdirectory(vendor/FastNoise EXCLUDE_FROM_ALL) +add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL) # ------------- imgui ------------- @@ -52,6 +53,7 @@ set(LINK_LIBRARIES_PRIVATE stb tinyFileDialogs FastNoise + yaml-cpp ) set(LINK_LIBRARIES_PUBLIC diff --git a/Prism/src/Prism.h b/Prism/src/Prism.h index fd7f5e7..5bd99ad 100644 --- a/Prism/src/Prism.h +++ b/Prism/src/Prism.h @@ -36,5 +36,8 @@ // Scene #include "Prism/Scene/Entity.h" #include "Prism/Scene/Scene.h" +#include "Prism/Scene/SceneCamera.h" +#include "Prism/Scene/SceneSerializer.h" +#include "Prism/Scene/Components.h" #endif //PRISM_H diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index aa73fb8..5784f73 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -42,6 +42,7 @@ namespace Prism Application::~Application() { + ScriptEngine::Shutdown(); } void Application::Run() @@ -104,6 +105,7 @@ namespace Prism m_ImGuiLayer->End(); } + // TODO: fix this to prase filter std::string Application::OpenFile(const std::string& filter) const { // TODO: will move it to other folder @@ -151,7 +153,17 @@ namespace Prism filterDesc = ss.str(); } + const char* result = tinyfd_openFileDialog( + "Open File", // 对话框标题 + nullptr, // 初始路径,nullptr 表示使用默认 + 0, // 过滤器数量 (注意:这里需要根据解析情况调整,见下文) + nullptr, // 过滤器模式数组 + nullptr, // 过滤器描述 + 0 // 是否允许多选:0 为单选,1 为多选 + ); + // 调用文件对话框 + /* const char* result = tinyfd_openFileDialog( "Open File", // 标题 nullptr, // 初始目录 @@ -160,11 +172,17 @@ namespace Prism filterDesc.empty() ? nullptr : filterDesc.c_str(), // 描述 0 // 单选 ); + */ return result ? std::string(result) : std::string(); } + std::string Application::SaveFile(const std::string& filter) const + { + return OpenFile(filter); + } + Application& Application::Get() { return *s_Instance; diff --git a/Prism/src/Prism/Core/Application.h b/Prism/src/Prism/Core/Application.h index c122806..a278d7c 100644 --- a/Prism/src/Prism/Core/Application.h +++ b/Prism/src/Prism/Core/Application.h @@ -35,7 +35,8 @@ namespace Prism void RenderImGui(); - std::string OpenFile(const std::string& filter) const; + std::string OpenFile(const std::string& filter = "All\0*.*\0") const; + std::string SaveFile(const std::string& filter = "All\0*.*\0") const; inline Window& GetWindow() { return *m_Window; } diff --git a/Prism/src/Prism/Core/Events/MouseEvent.h b/Prism/src/Prism/Core/Events/MouseEvent.h index 1e5e699..7d2a96c 100644 --- a/Prism/src/Prism/Core/Events/MouseEvent.h +++ b/Prism/src/Prism/Core/Events/MouseEvent.h @@ -6,6 +6,7 @@ #define MOUSEEVENT_H #include "Event.h" +#include namespace Prism { diff --git a/Prism/src/Prism/Core/UUID.cpp b/Prism/src/Prism/Core/UUID.cpp new file mode 100644 index 0000000..b3817e5 --- /dev/null +++ b/Prism/src/Prism/Core/UUID.cpp @@ -0,0 +1,29 @@ +// +// Created by sfd on 25-12-9. +// + +#include "UUID.h" + +#include + +namespace Prism +{ + static std::random_device s_RandomDevice; + static std::mt19937_64 eng(s_RandomDevice()); + static std::uniform_int_distribution s_UniformDistribution; + + UUID::UUID() + : m_UUID(s_UniformDistribution(eng)) + { + } + + UUID::UUID(const uint64_t uuid) + : m_UUID(uuid) + { + } + + UUID::UUID(const UUID& other) + : m_UUID(other.m_UUID) + { + } +} diff --git a/Prism/src/Prism/Core/UUID.h b/Prism/src/Prism/Core/UUID.h new file mode 100644 index 0000000..8d75886 --- /dev/null +++ b/Prism/src/Prism/Core/UUID.h @@ -0,0 +1,42 @@ +// +// Created by sfd on 25-12-9. +// + +#ifndef UUID_H +#define UUID_H + +#include + + +namespace Prism +{ + class UUID + { + public: + UUID(); + UUID(uint64_t uuid); + UUID(const UUID& other); + + operator uint64_t() { return m_UUID; } + operator const uint64_t() const { return m_UUID; } + + private: + uint64_t m_UUID; + }; + +} + +namespace std +{ + template<> + struct hash + { + std::size_t operator()(const Prism::UUID& uuid) const + { + return hash()((uint64_t)uuid); + } + }; +} + + +#endif //UUID_H diff --git a/Prism/src/Prism/Editor/EditorCamera.cpp b/Prism/src/Prism/Editor/EditorCamera.cpp new file mode 100644 index 0000000..cceb4db --- /dev/null +++ b/Prism/src/Prism/Editor/EditorCamera.cpp @@ -0,0 +1,161 @@ +// +// Created by sfd on 25-12-9. +// + +#include "EditorCamera.h" + +#include "GLFW/glfw3.h" +#include "glm/detail/type_quat.hpp" + +#include "glm/gtc/quaternion.hpp" +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/gtx/quaternion.hpp" +#include "Prism/Core/Application.h" +#include "Prism/Core/Input.h" +#include "Prism/Core/Events/MouseEvent.h" + +#ifndef M_PI +#define M_PI 3.1415926f +#endif + +namespace Prism +{ + EditorCamera::EditorCamera(const glm::mat4& projectionMatrix) + : Camera(projectionMatrix) + { + m_Rotation = glm::vec3(90.0f, 0.0f, 0.0f); + + m_FocalPoint = glm::vec3(0.0f); + + glm::vec3 position = { -5, 5, 5}; + m_Distance = glm::distance(position, m_FocalPoint); + + m_Yaw = 3.0f * (float)M_PI / 4.0f; + m_Pitch = M_PI / 4.0f; + + UpdateCameraView(); + } + + void EditorCamera::Focus() + { + } + + void EditorCamera::OnUpdate(TimeStep deltaTime) + { + if (Input::IsKeyPressed(Key::LEFT_ALT)) + { + const glm::vec2& mouse{ Input::GetMouseX(), Input::GetMouseY() }; + const glm::vec2 delta = (mouse - m_InitialMousePosition) * 0.003f; + m_InitialMousePosition = mouse; + + if (Input::IsMouseButtonPressed(GLFW_MOUSE_BUTTON_MIDDLE)) + MousePan(delta); + else if (Input::IsMouseButtonPressed(GLFW_MOUSE_BUTTON_LEFT)) + MouseRotate(delta); + else if (Input::IsMouseButtonPressed(GLFW_MOUSE_BUTTON_RIGHT)) + MouseZoom(delta.y); + } + + UpdateCameraView(); + } + + void EditorCamera::OnEvent(Event& e) + { + EventDispatcher dispatcher(e); + dispatcher.Dispatch(PM_BIND_EVENT_FN(EditorCamera::OnMouseScroll)); + } + + glm::vec3 EditorCamera::GetUpDirection() const + { + return glm::rotate(GetOrientation(), glm::vec3(0.0f, 1.0f, 0.0f)); + } + + glm::vec3 EditorCamera::GetRightDirection() const + { + return glm::rotate(GetOrientation(), glm::vec3(1.0f, 0.0f, 0.0f)); + } + + glm::vec3 EditorCamera::GetForwardDirection() const + { + return glm::rotate(GetOrientation(), glm::vec3(0.0f, 0.0f, -1.0f)); + } + + void EditorCamera::UpdateCameraView() + { + m_Position = CalculatePosition(); + + const glm::quat orientation = GetOrientation(); + m_Rotation = glm::eulerAngles(orientation) * (180.0f / (float)M_PI); + m_ViewMatrix = glm::translate(glm::mat4(1.0f), m_Position) * glm::toMat4(orientation); + m_ViewMatrix = glm::inverse(m_ViewMatrix); + } + + bool EditorCamera::OnMouseScroll(MouseScrolledEvent& e) + { + const float delta = e.GetOffsetY() * 0.1f; + MouseZoom(delta); + return false; + } + + void EditorCamera::MousePan(const glm::vec2& delta) + { + auto [xSpeed, ySpeed] = PanSpeed(); + // PM_CORE_TRACE("{0}, {1}", xSpeed, ySpeed); + m_FocalPoint += -GetRightDirection() * delta.x * xSpeed * m_Distance; + m_FocalPoint += GetUpDirection() * delta.y * ySpeed * m_Distance; + } + + void EditorCamera::MouseRotate(const glm::vec2& delta) + { + float yawSign = GetUpDirection().y < 0 ? -1.0f : 1.0f; + m_Yaw += yawSign * delta.x * RotationSpeed(); + m_Pitch += delta.y * RotationSpeed(); + } + + void EditorCamera::MouseZoom(const float delta) + { + m_Distance -= delta * ZoomSpeed(); + if (m_Distance < 1.0f) + { + m_FocalPoint += GetForwardDirection() * delta; + m_Distance = 1.0f; + } + } + + glm::vec3 EditorCamera::CalculatePosition() const + { + return m_FocalPoint - GetForwardDirection() * m_Distance; + } + + glm::quat EditorCamera::GetOrientation() const + { + return glm::quat(glm::vec3(-m_Pitch, -m_Yaw, 0.0f)); + } + + std::pair EditorCamera::PanSpeed() const + { + const float x = std::min(m_ViewportWidth / 1000.0f, 2.4f); // max = 2.4f + float xFactor = 0.0366f * (x * x) - 0.1778f * x + 0.3021f; + + const float y = std::min(m_ViewportHeight / 1000.0f, 2.4f); // max = 2.4f + float yFactor = 0.0366f * (y * y) - 0.1778f * y + 0.3021f; + + return { xFactor, yFactor }; + } + + float EditorCamera::RotationSpeed() const + { + return 0.8f; + } + + float EditorCamera::ZoomSpeed() const + { + float distance = m_Distance * 0.2f; + distance = std::max(distance, 0.0f); + float speed = distance * distance; + speed = std::min(speed, 100.0f); // max speed = 100 + return speed; + } + + +} \ No newline at end of file diff --git a/Prism/src/Prism/Editor/EditorCamera.h b/Prism/src/Prism/Editor/EditorCamera.h new file mode 100644 index 0000000..b210160 --- /dev/null +++ b/Prism/src/Prism/Editor/EditorCamera.h @@ -0,0 +1,84 @@ +// +// Created by sfd on 25-12-9. +// + +#ifndef EDITORCAMERA_H +#define EDITORCAMERA_H +#include "glm/fwd.hpp" +#include "glm/vec2.hpp" +#include "glm/vec3.hpp" +#include "Prism/Core/TimeStep.h" +#include "Prism/Core/Events/Event.h" +#include "Prism/Core/Events/MouseEvent.h" +#include "Prism/Renderer/Camera.h" + + +namespace Prism +{ + class PRISM_API EditorCamera : public Camera + { + public: + EditorCamera() = default; + EditorCamera(const glm::mat4& projectionMatrix); + + void Focus(); + void OnUpdate(TimeStep deltaTime); + void OnEvent(Event& e); + + inline float GetDistance() const { return m_Distance; } + inline void SetDistance(float distance) { m_Distance = distance; } + + inline void SetProjectionMatrix(const glm::mat4& projectionMatrix) { m_ProjectionMatrix = projectionMatrix; } + + const glm::mat4& GetProjectionMatrix() const { return m_ProjectionMatrix; } + const glm::mat4& GetViewMatrix() const { return m_ViewMatrix; } + glm::mat4 GetViewProjection() const { return m_ProjectionMatrix * m_ViewMatrix; } + + glm::vec3 GetUpDirection() const; + glm::vec3 GetRightDirection() const; + glm::vec3 GetForwardDirection() const; + const glm::vec3& GetPosition() const { return m_Position; } + glm::quat GetOrientation() const; + + float GetExposure() const { return m_Exposure; } + float& GetExposure() { return m_Exposure; } + + float GetPitch() const { return m_Pitch; } + float GetYaw() const { return m_Yaw; } + + public: + inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; } + private: + void UpdateCameraView(); + + bool OnMouseScroll(MouseScrolledEvent& e); + void MousePan(const glm::vec2& delta); + void MouseRotate(const glm::vec2& delta); + void MouseZoom(float delta); + + glm::vec3 CalculatePosition() const; + + std::pair PanSpeed() const; + float RotationSpeed() const; + float ZoomSpeed() const; + private: + glm::mat4 m_ViewMatrix; + glm::vec3 m_Position, m_Rotation, m_FocalPoint; + + bool m_Panning, m_Rotating; + glm::vec2 m_InitialMousePosition; + glm::vec3 m_InitialFocalPoint, m_InitialRotation; + + uint32_t m_ViewportWidth = 1280, m_ViewportHeight = 720; + + float m_Distance; + + float m_Exposure = 0.8f; + + float m_Pitch, m_Yaw; + }; + +} + + +#endif //EDITORCAMERA_H diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp index 59f9011..f0186fb 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp @@ -9,6 +9,8 @@ #define GLM_ENABLE_EXPERIMENTAL #include "assimp/scene.h" #include "glm/gtx/matrix_decompose.hpp" +#include "glm/gtx/quaternion.hpp" +#include "Prism/Core/Application.h" #include "Prism/Script/ScriptEngine.h" namespace Prism @@ -24,6 +26,15 @@ namespace Prism void SceneHierarchyPanel::SetContext(const Ref& scene) { m_Context = scene; + m_SelectionContext = {}; + if (m_SelectionContext && false) + { + // Try and find same entity in new scene + auto& entityMap = m_Context->GetEntityMap(); + UUID selectedEntityID = m_SelectionContext.GetUUID(); + if (entityMap.find(selectedEntityID) != entityMap.end()) + m_SelectionContext = entityMap.at(selectedEntityID); + } } void SceneHierarchyPanel::SetSelected(Entity entity) @@ -34,34 +45,76 @@ namespace Prism void SceneHierarchyPanel::OnImGuiRender() { ImGui::Begin("Scene Hierarchy"); - - uint32_t entityCount = 0, meshCount = 0; - for(const auto entity : m_Context->m_Registry.view()) + if (m_Context) { - DrawEntityNode(Entity(entity, m_Context.Raw())); - } + uint32_t entityCount = 0, meshCount = 0; + m_Context->m_Registry.view().each([&](auto entity) + { + const Entity e(entity, m_Context.Raw()); + if (e.HasComponent()) + DrawEntityNode(e); + }); - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::MenuItem("Create Empty Entity")) + if (ImGui::BeginPopupContextWindow()) { - m_Context->CreateEntity("Empty Entity"); + if (ImGui::MenuItem("Create Empty Entity")) + { + m_Context->CreateEntity("Empty Entity"); + } + ImGui::EndPopup(); + } + + ImGui::End(); + + + ImGui::Begin("Properties"); + + if (m_SelectionContext) + { + DrawComponents(m_SelectionContext); + + if (ImGui::Button("Add Component")) + ImGui::OpenPopup("AddComponentPanel"); + + if (ImGui::BeginPopup("AddComponentPanel")) + { + if (!m_SelectionContext.HasComponent()) + { + if (ImGui::Button("Camera")) + { + m_SelectionContext.AddComponent(); + ImGui::CloseCurrentPopup(); + } + } + if (!m_SelectionContext.HasComponent()) + { + if (ImGui::Button("Mesh")) + { + m_SelectionContext.AddComponent(); + ImGui::CloseCurrentPopup(); + } + } + if (!m_SelectionContext.HasComponent()) + { + if (ImGui::Button("Script")) + { + m_SelectionContext.AddComponent(); + ImGui::CloseCurrentPopup(); + } + } + if (!m_SelectionContext.HasComponent()) + { + if (ImGui::Button("Sprite Renderer")) + { + m_SelectionContext.AddComponent(); + ImGui::CloseCurrentPopup(); + } + } + ImGui::EndPopup(); + } } - ImGui::EndPopup(); } - - - ImGui::End(); - - ImGui::Begin("Properties"); - - if (m_SelectionContext) - { - DrawComponents(m_SelectionContext); - - } - ImGui::End(); @@ -95,7 +148,11 @@ namespace Prism const ImGuiTreeNodeFlags node_flags = (entity == m_SelectionContext ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow; const bool opened = ImGui::TreeNodeEx((void*)(uint32_t)entity, node_flags, name); if (ImGui::IsItemClicked()) + { m_SelectionContext = entity; + if (m_SelectionChangedCallback) + m_SelectionChangedCallback(m_SelectionContext); + } bool entityDeleted = false; if (ImGui::BeginPopupContextItem()) @@ -123,6 +180,8 @@ namespace Prism m_Context->DestroyEntity(entity); if (entity == m_SelectionContext) m_SelectionContext = {}; + + m_EntityDeletedCallback(entity); } @@ -211,7 +270,7 @@ namespace Prism ImGui::Columns(2); } - static bool Property(const char* label, std::string& value) + static bool Property(const char* label, std::string& value, bool error = false) { bool modified = false; @@ -226,12 +285,19 @@ namespace Prism s_IDBuffer[1] = '#'; memset(s_IDBuffer + 2, 0, 14); itoa(s_Counter++, s_IDBuffer + 2, 16); + + if (error) + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.2f, 0.2f, 1.0f)); + if (ImGui::InputText(s_IDBuffer, buffer, 256)) { value = buffer; modified = true; } + if (error) + ImGui::PopStyleColor(); + ImGui::PopItemWidth(); ImGui::NextColumn(); @@ -317,6 +383,48 @@ namespace Prism return modified; } + static bool Property(const char* label, glm::vec3& value, float delta = 0.1f) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + itoa(s_Counter++, s_IDBuffer + 2, 16); + if (ImGui::DragFloat3(s_IDBuffer, glm::value_ptr(value), delta)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool Property(const char* label, glm::vec4& value, float delta = 0.1f) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + itoa(s_Counter++, s_IDBuffer + 2, 16); + if (ImGui::DragFloat4(s_IDBuffer, glm::value_ptr(value), delta)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + static void EndPropertyGrid() { ImGui::Columns(1); @@ -327,6 +435,8 @@ namespace Prism { ImGui::AlignTextToFramePadding(); + auto id = entity.GetComponent().ID; + if (entity.HasComponent()) { auto& tag = entity.GetComponent().Tag; @@ -337,25 +447,47 @@ namespace Prism { tag = std::string(buffer); } - - ImGui::Separator(); } + // ID + ImGui::SameLine(); + ImGui::TextDisabled("%llx", id); + + ImGui::Separator(); + + if (entity.HasComponent()) { auto& tc = entity.GetComponent(); - if (ImGui::TreeNodeEx((void*)((uint32_t)entity | typeid(MeshComponent).hash_code()), ImGuiTreeNodeFlags_DefaultOpen, "Transform")) + if (ImGui::TreeNodeEx((void*)((uint32_t)entity | typeid(TransformComponent).hash_code()), ImGuiTreeNodeFlags_DefaultOpen, "Transform")) { - auto [translation, rotation, scale] = GetTransformDecomposition(tc); + auto [translation, rotationQuat, scale] = GetTransformDecomposition(tc); + glm::vec3 rotation = glm::degrees(glm::eulerAngles(rotationQuat)); ImGui::Columns(2); ImGui::Text("Translation"); ImGui::NextColumn(); ImGui::PushItemWidth(-1); + bool updateTransform = false; + if (ImGui::DragFloat3("##translation", glm::value_ptr(translation), 0.25f)) { - tc.Transform[3] = glm::vec4(translation, 1.0f); + // tc.Transform[3] = glm::vec4(translation, 1.0f); + updateTransform = true; + } + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + ImGui::Text("Rotation"); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + if (ImGui::DragFloat3("##rotation", glm::value_ptr(rotation), 0.25f)) + { + updateTransform = true; + // tc.Transform[3] = glm::vec4(translation, 1.0f); } ImGui::PopItemWidth(); @@ -367,7 +499,7 @@ namespace Prism if (ImGui::DragFloat3("##scale", glm::value_ptr(scale), 0.25f)) { - + updateTransform = true; } ImGui::PopItemWidth(); @@ -375,8 +507,13 @@ namespace Prism ImGui::Columns(1); - // ImGui::Text("Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z); - // ImGui::Text("Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z); + + if (updateTransform) + { + tc.Transform = glm::translate(glm::mat4(1.0f), translation) * + glm::toMat4(glm::quat(glm::radians(rotation))) * + glm::scale(glm::mat4(1.0f), scale); + } ImGui::TreePop(); } ImGui::Separator(); @@ -386,12 +523,32 @@ namespace Prism if (entity.HasComponent()) { auto& mc = entity.GetComponent(); - if (ImGui::TreeNodeEx((void*)((uint32_t)entity | typeid(TransformComponent).hash_code()), ImGuiTreeNodeFlags_DefaultOpen, "Mesh")) + if (ImGui::TreeNodeEx((void*)((uint32_t)entity | typeid(MeshComponent).hash_code()), ImGuiTreeNodeFlags_DefaultOpen, "Mesh")) { + ImGui::Columns(3); + ImGui::SetColumnWidth(0, 100); + ImGui::SetColumnWidth(1, 300); + ImGui::SetColumnWidth(2, 40); + ImGui::Text("File Path"); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + if (mc.Mesh) - ImGui::InputText("File Path", (char*)mc.Mesh->GetFilePath().c_str(), 256, ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##meshFilePath", (char*)mc.Mesh->GetFilePath().c_str(), 256, ImGuiInputTextFlags_ReadOnly); else - ImGui::InputText("File Path", (char*)"Null", 256, ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##meshFilePath", (char*)"Null", 256, ImGuiInputTextFlags_ReadOnly); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + if (ImGui::Button("...##openmesh")) + { + const std::string file = Application::Get().OpenFile(); + if (!file.empty()) + mc.Mesh = Ref::Create(file); + } + ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::TreePop(); } ImGui::Separator(); @@ -402,6 +559,59 @@ namespace Prism auto& cc = entity.GetComponent(); if (ImGui::TreeNodeEx((void*)((uint32_t)entity | typeid(CameraComponent).hash_code()), ImGuiTreeNodeFlags_DefaultOpen, "Camera")) { + // Projection Type + const char* projTypeStrings[] = { "Perspective", "Orthographic" }; + const char* currentProj = projTypeStrings[(int)cc.Camera.GetProjectionType()]; + if (ImGui::BeginCombo("Projection", currentProj)) + { + for (int type = 0; type < 2; type++) + { + bool is_selected = (currentProj == projTypeStrings[type]); + if (ImGui::Selectable(projTypeStrings[type], is_selected)) + { + currentProj = projTypeStrings[type]; + cc.Camera.SetProjectionType((SceneCamera::ProjectionType)type); + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + BeginPropertyGrid(); + // Perspective parameters + if (cc.Camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective) + { + float verticalFOV = cc.Camera.GetPerspectiveVerticalFOV(); + if (Property("Vertical FOV", verticalFOV)) + cc.Camera.SetPerspectiveVerticalFOV(verticalFOV); + + float nearClip = cc.Camera.GetPerspectiveNearClip(); + if (Property("Near Clip", nearClip)) + cc.Camera.SetPerspectiveNearClip(nearClip); + ImGui::SameLine(); + float farClip = cc.Camera.GetPerspectiveFarClip(); + if (Property("Far Clip", farClip)) + cc.Camera.SetPerspectiveFarClip(farClip); + } + + // Orthographic parameters + else if (cc.Camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic) + { + float orthoSize = cc.Camera.GetOrthographicSize(); + if (Property("Size", orthoSize)) + cc.Camera.SetOrthographicSize(orthoSize); + + float nearClip = cc.Camera.GetOrthographicNearClip(); + if (Property("Near Clip", nearClip)) + cc.Camera.SetOrthographicNearClip(nearClip); + ImGui::SameLine(); + float farClip = cc.Camera.GetOrthographicFarClip(); + if (Property("Far Clip", farClip)) + cc.Camera.SetOrthographicFarClip(farClip); + } + + EndPropertyGrid(); ImGui::TreePop(); } @@ -425,53 +635,105 @@ namespace Prism if (ImGui::TreeNodeEx((void*)((uint32_t)entity | typeid(ScriptComponent).hash_code()), ImGuiTreeNodeFlags_DefaultOpen, "Script")) { BeginPropertyGrid(); - Property("Module Name", sc.ModuleName.c_str()); + std::string oldName = sc.ModuleName; - // Public Fields - auto& fieldMap = ScriptEngine::GetFieldMap(); - if (fieldMap.find(sc.ModuleName) != fieldMap.end()) + if (Property("Module Name", sc.ModuleName, !ScriptEngine::ModuleExists(sc.ModuleName))) // TODO: no live edit { - auto& publicFields = fieldMap.at(sc.ModuleName); - for (auto& field : publicFields) + // Shutdown old script + if (ScriptEngine::ModuleExists(oldName)) + ScriptEngine::ShutdownScriptEntity(entity, oldName); + + if (ScriptEngine::ModuleExists(sc.ModuleName)) + ScriptEngine::InitScriptEntity(entity); + } + + if (ScriptEngine::ModuleExists(sc.ModuleName)) + { + EntityInstanceData& entityInstanceData = ScriptEngine::GetEntityInstanceData(entity.GetSceneUUID(), id); + auto& moduleFieldMap = entityInstanceData.ModuleFieldMap; + if (moduleFieldMap.find(sc.ModuleName) != moduleFieldMap.end()) { - switch (field.Type) + auto& publicFields = moduleFieldMap.at(sc.ModuleName); + for (auto& [name, field] : publicFields) { - case FieldType::Int: - { - int value = field.GetValue(); - if (Property(field.Name.c_str(), value)) + bool isRuntime = m_Context->m_IsPlaying && field.IsRuntimeAvailable(); + switch (field.Type) { - field.SetValue(value); - } - break; - } - case FieldType::Float: - { - float value = field.GetValue(); - if (Property(field.Name.c_str(), value, 0.2f)) + case FieldType::Int: { - field.SetValue(value); + int value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); + if (Property(field.Name.c_str(), value)) + { + if (isRuntime) + field.SetRuntimeValue(value); + else + field.SetStoredValue(value); + } + break; } - break; - } - case FieldType::Vec2: - { - glm::vec2 value = field.GetValue(); - if (Property(field.Name.c_str(), value, 0.2f)) + case FieldType::Float: { - field.SetValue(value); + float value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); + if (Property(field.Name.c_str(), value, 0.2f)) + { + if (isRuntime) + field.SetRuntimeValue(value); + else + field.SetStoredValue(value); + } + break; + } + case FieldType::Vec2: + { + glm::vec2 value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); + if (Property(field.Name.c_str(), value, 0.2f)) + { + if (isRuntime) + field.SetRuntimeValue(value); + else + field.SetStoredValue(value); + } + break; + } + case FieldType::Vec3: + { + glm::vec3 value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); + if (Property(field.Name.c_str(), value, 0.2f)) + { + if (isRuntime) + field.SetRuntimeValue(value); + else + field.SetStoredValue(value); + } + break; + } + case FieldType::Vec4: + { + glm::vec4 value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); + if (Property(field.Name.c_str(), value, 0.2f)) + { + if (isRuntime) + field.SetRuntimeValue(value); + else + field.SetStoredValue(value); + } + break; + } } - break; - } } } } EndPropertyGrid(); + +#if TODO + if (ImGui::Button("Run Script")) { ScriptEngine::OnCreateEntity(entity); } +#endif + ImGui::TreePop(); } ImGui::Separator(); diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.h b/Prism/src/Prism/Editor/SceneHierachyPanel.h index 1fff2b8..3ac9ca9 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.h +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.h @@ -18,6 +18,8 @@ namespace Prism void SetContext(const Ref& scene); void SetSelected(Entity entity); + void SetSelectionChangedCallback(const std::function& func) { m_SelectionChangedCallback = func; } + void SetEntityDeletedCallback(const std::function& func) { m_EntityDeletedCallback = func; } void OnImGuiRender(); private: @@ -28,6 +30,8 @@ namespace Prism private: Ref m_Context; Entity m_SelectionContext; + + std::function m_SelectionChangedCallback, m_EntityDeletedCallback; }; } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLBuffer.cpp index ff3793b..e8996d5 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLBuffer.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLBuffer.cpp @@ -51,8 +51,9 @@ namespace Prism #ifdef __MINGW32__ glDeleteBuffers(1, &m_RendererID); #else - Renderer::Submit([this](){ - glDeleteBuffers(1, &m_RendererID); + GLuint rendererID = m_RendererID; + Renderer::Submit([rendererID](){ + glDeleteBuffers(1, &rendererID); }); #endif } @@ -69,13 +70,9 @@ namespace Prism void OpenGLVertexBuffer::Bind() const { - Renderer::Submit([this](){ - glBindBuffer(GL_ARRAY_BUFFER, m_RendererID); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (const void*)(3 * sizeof(float))); + Ref instance = this; + Renderer::Submit([instance]() { + glBindBuffer(GL_ARRAY_BUFFER, instance->m_RendererID); }); } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp index b150938..7115466 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp @@ -68,7 +68,7 @@ namespace Prism glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_ColorAttachment); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment); - // TODO: Create Hazel texture object based on format here + // TODO: Create Prism texture object based on format here if (m_Specification.Format == FramebufferFormat::RGBA16F) { glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA16F, m_Specification.Width, m_Specification.Height, GL_FALSE); @@ -89,7 +89,7 @@ namespace Prism glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment); glBindTexture(GL_TEXTURE_2D, m_ColorAttachment); - // TODO: Create Hazel texture object based on format here + // TODO: Create texture object based on format here if (m_Specification.Format == FramebufferFormat::RGBA16F) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr); diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp index 4b0b5b4..4b77b7a 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp @@ -120,6 +120,7 @@ namespace Prism glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_MULTISAMPLE); + glEnable(GL_STENCIL_TEST); auto& caps = GetCapabilities(); @@ -147,7 +148,7 @@ namespace Prism void RendererAPI::Clear(const float r, const float g, const float b, const float a) { glClearColor(r, g, b, a); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } void RendererAPI::SetClearColor(const float r, const float g, const float b, const float a) diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index 3ae8eef..bbf9f3a 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -207,7 +207,7 @@ namespace Prism { PM_CORE_ASSERT(false, "Could not load shader file"); } - + file.close(); return result; } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLVertexArray.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLVertexArray.cpp index f453064..0c04aa3 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLVertexArray.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLVertexArray.cpp @@ -3,6 +3,8 @@ // #include "OpenGLVertexArray.h" + +#include "OpenGLBuffer.h" #include "glad/glad.h" #include "Prism/Renderer/Renderer.h" @@ -31,7 +33,7 @@ namespace Prism OpenGLVertexArray::OpenGLVertexArray() { - Renderer::Submit([this](){ + Renderer::Submit([this]() { glCreateVertexArrays(1, &m_RendererID); }); } @@ -41,8 +43,9 @@ namespace Prism #ifdef __MINGW32__ glDeleteVertexArrays(1, &m_RendererID); #else - Renderer::Submit([this](){ - glDeleteVertexArrays(1, &m_RendererID); + GLuint rendererID = m_RendererID; + Renderer::Submit([rendererID](){ + glDeleteVertexArrays(1, &rendererID); }); #endif } diff --git a/Prism/src/Prism/Renderer/Camera.cpp b/Prism/src/Prism/Renderer/Camera.cpp index 69f235a..fb562ee 100644 --- a/Prism/src/Prism/Renderer/Camera.cpp +++ b/Prism/src/Prism/Renderer/Camera.cpp @@ -4,157 +4,13 @@ #include "Camera.h" -#include "GLFW/glfw3.h" -#include "glm/detail/type_quat.hpp" - -#include "glm/gtc/quaternion.hpp" -#define GLM_ENABLE_EXPERIMENTAL -#include "glm/gtx/quaternion.hpp" -#include "Prism/Core/Application.h" -#include "Prism/Core/Input.h" -#include "Prism/Core/Events/MouseEvent.h" - -#ifndef M_PI -#define M_PI 3.1415926f -#endif namespace Prism { - Camera::Camera(const glm::mat4& projectionMatrix) - : m_ProjectionMatrix(projectionMatrix) - { - m_Rotation = glm::vec3(90.0f, 0.0f, 0.0f); - - m_FocalPoint = glm::vec3(0.0f); - - glm::vec3 position = { -5, 5, 5}; - m_Distance = glm::distance(position, m_FocalPoint); - - m_Yaw = 3.0f * (float)M_PI / 4.0f; - m_Pitch = M_PI / 4.0f; - - UpdateCameraView(); - } - - void Camera::Focus() - { - } - - void Camera::OnUpdate(TimeStep deltaTime) - { - if (Input::IsKeyPressed(Key::LEFT_ALT)) + Camera::Camera(const glm::mat4& projectionMatrix) + : m_ProjectionMatrix(projectionMatrix) { - const glm::vec2& mouse{ Input::GetMouseX(), Input::GetMouseY() }; - const glm::vec2 delta = (mouse - m_InitialMousePosition) * 0.003f; - m_InitialMousePosition = mouse; - if (Input::IsMouseButtonPressed(GLFW_MOUSE_BUTTON_MIDDLE)) - MousePan(delta); - else if (Input::IsMouseButtonPressed(GLFW_MOUSE_BUTTON_LEFT)) - MouseRotate(delta); - else if (Input::IsMouseButtonPressed(GLFW_MOUSE_BUTTON_RIGHT)) - MouseZoom(delta.y); } - - UpdateCameraView(); - } - - void Camera::OnEvent(Event& e) - { - EventDispatcher dispatcher(e); - dispatcher.Dispatch(PM_BIND_EVENT_FN(Camera::OnMouseScroll)); - } - - glm::vec3 Camera::GetUpDirection() const - { - return glm::rotate(GetOrientation(), glm::vec3(0.0f, 1.0f, 0.0f)); - } - - glm::vec3 Camera::GetRightDirection() const - { - return glm::rotate(GetOrientation(), glm::vec3(1.0f, 0.0f, 0.0f)); - } - - glm::vec3 Camera::GetForwardDirection() const - { - return glm::rotate(GetOrientation(), glm::vec3(0.0f, 0.0f, -1.0f)); - } - - void Camera::UpdateCameraView() - { - m_Position = CalculatePosition(); - - const glm::quat orientation = GetOrientation(); - m_Rotation = glm::eulerAngles(orientation) * (180.0f / (float)M_PI); - m_ViewMatrix = glm::translate(glm::mat4(1.0f), m_Position) * glm::toMat4(orientation); - m_ViewMatrix = glm::inverse(m_ViewMatrix); - } - - bool Camera::OnMouseScroll(MouseScrolledEvent& e) - { - const float delta = e.GetOffsetY() * 0.1f; - MouseZoom(delta); - return false; - } - - void Camera::MousePan(const glm::vec2& delta) - { - auto [xSpeed, ySpeed] = PanSpeed(); - // PM_CORE_TRACE("{0}, {1}", xSpeed, ySpeed); - m_FocalPoint += -GetRightDirection() * delta.x * xSpeed * m_Distance; - m_FocalPoint += GetUpDirection() * delta.y * ySpeed * m_Distance; - } - - void Camera::MouseRotate(const glm::vec2& delta) - { - float yawSign = GetUpDirection().y < 0 ? -1.0f : 1.0f; - m_Yaw += yawSign * delta.x * RotationSpeed(); - m_Pitch += delta.y * RotationSpeed(); - } - - void Camera::MouseZoom(const float delta) - { - m_Distance -= delta * ZoomSpeed(); - if (m_Distance < 1.0f) - { - m_FocalPoint += GetForwardDirection() * delta; - m_Distance = 1.0f; - } - } - - glm::vec3 Camera::CalculatePosition() const - { - return m_FocalPoint - GetForwardDirection() * m_Distance; - } - - glm::quat Camera::GetOrientation() const - { - return glm::quat(glm::vec3(-m_Pitch, -m_Yaw, 0.0f)); - } - - std::pair Camera::PanSpeed() const - { - const float x = std::min(m_ViewportWidth / 1000.0f, 2.4f); // max = 2.4f - float xFactor = 0.0366f * (x * x) - 0.1778f * x + 0.3021f; - - const float y = std::min(m_ViewportHeight / 1000.0f, 2.4f); // max = 2.4f - float yFactor = 0.0366f * (y * y) - 0.1778f * y + 0.3021f; - - return { xFactor, yFactor }; - } - - float Camera::RotationSpeed() const - { - return 0.8f; - } - - float Camera::ZoomSpeed() const - { - float distance = m_Distance * 0.2f; - distance = std::max(distance, 0.0f); - float speed = distance * distance; - speed = std::min(speed, 100.0f); // max speed = 100 - return speed; - } } diff --git a/Prism/src/Prism/Renderer/Camera.h b/Prism/src/Prism/Renderer/Camera.h index cff387f..b10436d 100644 --- a/Prism/src/Prism/Renderer/Camera.h +++ b/Prism/src/Prism/Renderer/Camera.h @@ -18,62 +18,18 @@ namespace Prism public: Camera() = default; Camera(const glm::mat4& projectionMatrix); - - void Focus(); - void OnUpdate(TimeStep deltaTime); - void OnEvent(Event& e); - - inline float GetDistance() const { return m_Distance; } - inline void SetDistance(float distance) { m_Distance = distance; } - - inline void SetProjectionMatrix(const glm::mat4& projectionMatrix) { m_ProjectionMatrix = projectionMatrix; } + ~Camera() = default; const glm::mat4& GetProjectionMatrix() const { return m_ProjectionMatrix; } - const glm::mat4& GetViewMatrix() const { return m_ViewMatrix; } - glm::mat4 GetViewProjection() const { return m_ProjectionMatrix * m_ViewMatrix; } - glm::vec3 GetUpDirection() const; - glm::vec3 GetRightDirection() const; - glm::vec3 GetForwardDirection() const; - const glm::vec3& GetPosition() const { return m_Position; } - glm::quat GetOrientation() const; + void SetProjectionMatrix(const glm::mat4& projectionMatrix) { m_ProjectionMatrix = projectionMatrix; } float GetExposure() const { return m_Exposure; } float& GetExposure() { return m_Exposure; } - float GetPitch() const { return m_Pitch; } - float GetYaw() const { return m_Yaw; } - - public: - inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; } - private: - void UpdateCameraView(); - - bool OnMouseScroll(MouseScrolledEvent& e); - void MousePan(const glm::vec2& delta); - void MouseRotate(const glm::vec2& delta); - void MouseZoom(float delta); - - glm::vec3 CalculatePosition() const; - - std::pair PanSpeed() const; - float RotationSpeed() const; - float ZoomSpeed() const; - private: - glm::mat4 m_ProjectionMatrix, m_ViewMatrix; - glm::vec3 m_Position, m_Rotation, m_FocalPoint; - - bool m_Panning, m_Rotating; - glm::vec2 m_InitialMousePosition; - glm::vec3 m_InitialFocalPoint, m_InitialRotation; - - uint32_t m_ViewportWidth = 1280, m_ViewportHeight = 720; - - float m_Distance; - + protected: + glm::mat4 m_ProjectionMatrix; float m_Exposure = 0.8f; - - float m_Pitch, m_Yaw; }; } diff --git a/Prism/src/Prism/Renderer/Mesh.cpp b/Prism/src/Prism/Renderer/Mesh.cpp index 9c4f429..fa32c26 100644 --- a/Prism/src/Prism/Renderer/Mesh.cpp +++ b/Prism/src/Prism/Renderer/Mesh.cpp @@ -283,7 +283,7 @@ namespace Prism bool hasAlbedoMap = aiMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &aiTexPath) == AI_SUCCESS; if (hasAlbedoMap) { - // TODO: Temp - this should be handled by Hazel's filesystem + // TODO: Temp - this should be handled by Prism's filesystem std::filesystem::path path = filename; auto parentPath = path.parent_path(); parentPath /= std::string(aiTexPath.data); @@ -315,7 +315,7 @@ namespace Prism if (aiMaterial->GetTexture(aiTextureType_NORMALS, 0, &aiTexPath) == AI_SUCCESS) { - // TODO: Temp - this should be handled by Hazel's filesystem + // TODO: Temp - this should be handled by Prism's filesystem std::filesystem::path path = filename; auto parentPath = path.parent_path(); parentPath /= std::string(aiTexPath.data); @@ -343,7 +343,7 @@ namespace Prism // mi->Set("u_RoughnessTexToggle", 0.0f); if (aiMaterial->GetTexture(aiTextureType_SHININESS, 0, &aiTexPath) == AI_SUCCESS) { - // TODO: Temp - this should be handled by Hazel's filesystem + // TODO: Temp - this should be handled by Prism's filesystem std::filesystem::path path = filename; auto parentPath = path.parent_path(); parentPath /= std::string(aiTexPath.data); @@ -375,7 +375,7 @@ namespace Prism // mi->Set("u_MetalnessTexToggle", 0.0f); if (aiMaterial->Get("$raw.ReflectionFactor|file", aiPTI_String, 0, aiTexPath) == AI_SUCCESS) { - // TODO: Temp - this should be handled by Hazel's filesystem + // TODO: Temp - this should be handled by Prism's filesystem std::filesystem::path path = filename; auto parentPath = path.parent_path(); parentPath /= std::string(aiTexPath.data); @@ -437,7 +437,7 @@ namespace Prism { metalnessTextureFound = true; - // TODO: Temp - this should be handled by Hazel's filesystem + // TODO: Temp - this should be handled by Prism's filesystem std::filesystem::path path = filename; auto parentPath = path.parent_path(); parentPath /= str; diff --git a/Prism/src/Prism/Renderer/Renderer.cpp b/Prism/src/Prism/Renderer/Renderer.cpp index 6da71fe..681891b 100644 --- a/Prism/src/Prism/Renderer/Renderer.cpp +++ b/Prism/src/Prism/Renderer/Renderer.cpp @@ -170,7 +170,7 @@ namespace Prism for (Submesh& submesh : mesh->m_Submeshes) { // Material - auto material = materials[submesh.MaterialIndex]; + auto material = overrideMaterial ? overrideMaterial : materials[submesh.MaterialIndex]; auto shader = material->GetShader(); material->Bind(); diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index c4050fd..7d58673 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -20,7 +20,7 @@ namespace Prism const Scene* ActiveScene = nullptr; struct SceneInfo { - Camera SceneCamera; + SceneRendererCamera SceneCamera; // Resources Ref SkyboxMaterial; @@ -41,9 +41,11 @@ namespace Prism glm::mat4 Transform; }; std::vector DrawList; + std::vector SelectedMeshDrawList; // Grid Ref GridMaterial; + Ref OutlineMaterial; SceneRendererOptions Options; }; @@ -85,6 +87,12 @@ namespace Prism constexpr float gridSize = 0.025f; s_Data.GridMaterial->Set("u_Scale", gridScale); s_Data.GridMaterial->Set("u_Res", gridSize); + + // outline + const auto outlineShader = Shader::Create("assets/shaders/Outline.glsl"); + s_Data.OutlineMaterial = MaterialInstance::Create(Material::Create(outlineShader)); + s_Data.OutlineMaterial->SetFlag(MaterialFlag::DepthTest, false); + } void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height) @@ -93,7 +101,7 @@ namespace Prism s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height); } - void SceneRenderer::BeginScene(const Scene* scene, const Camera& camera) + void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera) { PM_CORE_ASSERT(!s_Data.ActiveScene); @@ -121,6 +129,11 @@ namespace Prism s_Data.DrawList.push_back({ mesh, overrideMaterial, transform }); } + void SceneRenderer::SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform) + { + s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform }); + } + static Ref equirectangularConversionShader, envFilteringShader, envIrradianceShader; @@ -217,14 +230,34 @@ namespace Prism CompositePass(); s_Data.DrawList.clear(); + s_Data.SelectedMeshDrawList.clear(); s_Data.SceneData = {}; } void SceneRenderer::GeometryPass() { + bool outline = s_Data.SelectedMeshDrawList.size() > 0; + + if (outline) + { + Renderer::Submit([]() + { + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + }); + } + Renderer::BeginRenderPass(s_Data.GeoPass); - const auto viewProjection = s_Data.SceneData.SceneCamera.GetViewProjection(); + if (outline) + { + Renderer::Submit([]() + { + glStencilMask(0); + }); + } + + const auto viewProjection = s_Data.SceneData.SceneCamera.Camera.GetProjectionMatrix() * s_Data.SceneData.SceneCamera.ViewMatrix; + glm::vec3 cameraPosition = glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)[3]; // Skybox auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader(); @@ -237,7 +270,7 @@ namespace Prism { auto baseMaterial = dc.mesh->GetMaterial(); baseMaterial->Set("u_ViewProjectionMatrix", viewProjection); - baseMaterial->Set("u_CameraPosition", s_Data.SceneData.SceneCamera.GetPosition()); + baseMaterial->Set("u_CameraPosition", cameraPosition); // Environment (TODO: don't do this per mesh) baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap); @@ -251,6 +284,73 @@ namespace Prism Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial); } + if (outline) + { + Renderer::Submit([]() + { + glStencilFunc(GL_ALWAYS, 1, 0xff); + glStencilMask(0xff); + }); + } + + for (auto& dc : s_Data.SelectedMeshDrawList) + { + auto baseMaterial = dc.mesh->GetMaterial(); + baseMaterial->Set("u_ViewProjectionMatrix", viewProjection); + baseMaterial->Set("u_CameraPosition", cameraPosition); + + // Environment (TODO: don't do this per mesh) + baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap); + baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment.IrradianceMap); + baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT); + + // Set lights (TODO: move to light environment and don't do per mesh) + baseMaterial->Set("lights", s_Data.SceneData.ActiveLight); + + auto overrideMaterial = nullptr; // dc.Material; + Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial); + } + + if (outline) + { + Renderer::Submit([]() + { + glStencilFunc(GL_NOTEQUAL, 1, 0xff); + glStencilMask(0); + + glLineWidth(10); + glEnable(GL_LINE_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDisable(GL_DEPTH_TEST); + }); + + // Draw outline here + s_Data.OutlineMaterial->Set("u_ViewProjection", viewProjection); + for (auto& dc : s_Data.SelectedMeshDrawList) + { + Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + } + + Renderer::Submit([]() + { + glPointSize(10); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + }); + for (auto& dc : s_Data.SelectedMeshDrawList) + { + Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + } + + Renderer::Submit([]() + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glStencilMask(0xff); + glStencilFunc(GL_ALWAYS, 1, 0xff); + glEnable(GL_DEPTH_TEST); + }); + } + + // Grid const auto option = GetOptions(); if (option.ShowGrid) @@ -274,7 +374,7 @@ namespace Prism { Renderer::BeginRenderPass(s_Data.CompositePass); s_Data.CompositeShader->Bind(); - s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.GetExposure()); + s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); Renderer::SubmitFullscreenQuad(nullptr); diff --git a/Prism/src/Prism/Renderer/SceneRenderer.h b/Prism/src/Prism/Renderer/SceneRenderer.h index 8d081c7..d9ea37f 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.h +++ b/Prism/src/Prism/Renderer/SceneRenderer.h @@ -18,6 +18,12 @@ namespace Prism bool ShowBoundingBoxes = false; }; + struct SceneRendererCamera + { + Prism::Camera Camera; + glm::mat4 ViewMatrix; + }; + class PRISM_API SceneRenderer { public: @@ -25,10 +31,11 @@ namespace Prism static void SetViewportSize(uint32_t width, uint32_t height); - static void BeginScene(const Scene* scene, const Camera& camera); + static void BeginScene(const Scene* scene, const SceneRendererCamera& camera); static void EndScene(); static void SubmitMesh(const Ref& mesh, const glm::mat4& transform = glm::mat4(1.0f), const Ref& overrideMaterial = nullptr); + static void SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform = glm::mat4(1.0f)); static std::pair, Ref> CreateEnvironmentMap(const std::string& filepath); diff --git a/Prism/src/Prism/Scene/Components.h b/Prism/src/Prism/Scene/Components.h index de87460..ec32bbe 100644 --- a/Prism/src/Prism/Scene/Components.h +++ b/Prism/src/Prism/Scene/Components.h @@ -7,18 +7,30 @@ #include +#include "SceneCamera.h" #include "glm/glm.hpp" #include "Prism/Core/Ref.h" +#include "Prism/Core/UUID.h" #include "Prism/Renderer/Camera.h" #include "Prism/Renderer/Mesh.h" namespace Prism { + struct IDComponent + { + UUID ID = 0; + }; struct TagComponent { std::string Tag; + TagComponent() = default; + TagComponent(const TagComponent& other) + : Tag(other.Tag) {} + TagComponent(const std::string& tag) + : Tag(tag) {} + explicit operator std::string& () { return Tag; } explicit operator const std::string& () const { return Tag; } }; @@ -28,6 +40,12 @@ namespace Prism { glm::mat4 Transform; + TransformComponent() = default; + TransformComponent(const TransformComponent& other) + : Transform(other.Transform) {} + TransformComponent(const glm::mat4& transform) + : Transform(transform) {} + operator glm::mat4& () { return Transform; } operator const glm::mat4& () const { return Transform; } }; @@ -37,25 +55,39 @@ namespace Prism { Ref Mesh; + MeshComponent() = default; + MeshComponent(const MeshComponent& other) + : Mesh(other.Mesh) {} + MeshComponent(const Ref& mesh) + : Mesh(mesh) {} + operator Ref () { return Mesh; } }; struct ScriptComponent { - // TODO: C# script std::string ModuleName; + + ScriptComponent() = default; + ScriptComponent(const ScriptComponent& other) + : ModuleName(other.ModuleName) {} + ScriptComponent(const std::string& moduleName) + : ModuleName(moduleName) {} }; struct CameraComponent { - //OrthographicCamera Camera; - Prism::Camera Camera; + SceneCamera Camera; bool Primary = true; - explicit operator Prism::Camera& () { return Camera; } - explicit operator const Prism::Camera& () const { return Camera; } + CameraComponent() = default; + CameraComponent(const CameraComponent& other) + : Camera(other.Camera), Primary(other.Primary) {} + + operator SceneCamera& () { return Camera; } + operator const SceneCamera& () const { return Camera; } }; @@ -64,6 +96,10 @@ namespace Prism glm::vec4 Color = { 1.0f, 1.0f, 1.0f, 1.0f }; Ref Texture; float TilingFactor = 1.0f; + + SpriteRendererComponent() = default; + SpriteRendererComponent(const SpriteRendererComponent& other) + : Color(other.Color), Texture(other.Texture), TilingFactor(other.TilingFactor) {} }; } diff --git a/Prism/src/Prism/Scene/Entity.h b/Prism/src/Prism/Scene/Entity.h index 8a1bebf..cf84712 100644 --- a/Prism/src/Prism/Scene/Entity.h +++ b/Prism/src/Prism/Scene/Entity.h @@ -44,6 +44,7 @@ namespace Prism const glm::mat4& Transform() const { return m_Scene->m_Registry.get(m_EntityHandle); } operator uint32_t () const { return (uint32_t)m_EntityHandle; } + operator entt::entity() const{ return m_EntityHandle; } operator bool () const { return (uint32_t)m_EntityHandle && m_Scene; } bool operator==(const Entity& other) const @@ -55,6 +56,10 @@ namespace Prism { return !(*this == other); } + + UUID GetUUID() { return GetComponent().ID; } + UUID GetSceneUUID() { return m_Scene->GetUUID(); } + private: explicit Entity(const std::string& name); private: @@ -63,6 +68,7 @@ namespace Prism private: friend class Scene; + friend class SceneSerializer; friend class ScriptEngine; }; } diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp index 53c3cf2..93f8db8 100644 --- a/Prism/src/Prism/Scene/Scene.cpp +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -11,13 +11,13 @@ namespace Prism { - std::unordered_map s_ActiveScenes; + std::unordered_map s_ActiveScenes; static uint32_t s_SceneIDCounter = 0; struct SceneComponent { - uint32_t SceneID; + UUID SceneID; }; void OnTransformConstruct(entt::registry& registry, entt::entity entity) @@ -28,21 +28,20 @@ namespace Prism void OnScriptComponentConstruct(entt::registry& registry, entt::entity entity) { // Note: there should be exactly one scene component per registry - auto view = registry.view(); - uint32_t sceneID = 0; - for (const auto aEntity : view) - { - const auto& scene = registry.get(aEntity); - sceneID = scene.SceneID; - } + const auto sceneView = registry.view(); + UUID sceneID = registry.get(sceneView.front()).SceneID; - ScriptEngine::OnInitEntity(registry.get(entity), (uint32_t)entity, sceneID); + Scene* scene = s_ActiveScenes[sceneID]; + + auto entityID = registry.get(entity).ID; + PM_CORE_ASSERT(scene->m_EntityIDMap.find(entityID) != scene->m_EntityIDMap.end()); + ScriptEngine::InitScriptEntity(scene->m_EntityIDMap.at(entityID)); } Environment Environment::Load(const std::string& filepath) { auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath); - return { radiance, irradiance }; + return { filepath, radiance, irradiance }; } Scene::Scene(const std::string& debugName) @@ -75,30 +74,52 @@ namespace Prism void Scene::OnUpdate(TimeStep ts) { - - Camera* camera = nullptr; - { - auto view = m_Registry.view(); - for (auto entity : view) - { - auto& comp = view.get(entity); - camera = &comp.Camera; - break; - } - } - - PM_CORE_ASSERT(camera, "Scene does not contain any cameras!"); - camera->OnUpdate(ts); - // Update all entities { auto view = m_Registry.view(); for (auto entity : view) - ScriptEngine::OnUpdateEntity((uint32_t)entity, ts); + { + UUID entityID = m_Registry.get(entity).ID; + Entity e = { entity, this }; + if (ScriptEngine::ModuleExists(e.GetComponent().ModuleName)) + ScriptEngine::OnUpdateEntity(m_SceneID, entityID, ts); + } } + } + + void Scene::OnRenderRuntime(TimeStep ts) + { + ///////////////////////////////////////////////////////////////////// + // RENDER 3D SCENE // + ///////////////////////////////////////////////////////////////////// + Entity cameraEntity = GetMainCameraEntity(); + if (!cameraEntity) + return; + + glm::mat4 cameraViewMatrix = glm::inverse(cameraEntity.GetComponent().Transform); + PM_CORE_ASSERT(cameraEntity, "Scene does not contain any cameras!"); + SceneCamera& camera = cameraEntity.GetComponent(); + camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight); + + m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); + + auto group = m_Registry.group(entt::get); + SceneRenderer::BeginScene(this, { camera, cameraViewMatrix }); + for (auto entity : group) + { + auto [transformComponent, meshComponent] = group.get(entity); + if (meshComponent.Mesh) + { + meshComponent.Mesh->OnUpdate(ts); + + // TODO: Should we render (logically) + SceneRenderer::SubmitMesh(meshComponent, transformComponent, nullptr); + } + } + SceneRenderer::EndScene(); + ///////////////////////////////////////////////////////////////////// #if 0 - // Render all sprites Renderer2D::BeginScene(*camera); { @@ -115,22 +136,17 @@ namespace Prism Renderer2D::EndScene(); #endif + } + void Scene::OnRenderEditor(TimeStep ts, const EditorCamera& editorCamera) + { ///////////////////////////////////////////////////////////////////// // RENDER 3D SCENE // ///////////////////////////////////////////////////////////////////// - m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); - auto entities = m_Registry.view(); - for (auto entity : entities) - { - auto meshComponent = m_Registry.try_get(entity); - } - - auto group = m_Registry.group(entt::get); - SceneRenderer::BeginScene(this, *camera); + SceneRenderer::BeginScene(this, { static_cast(editorCamera), editorCamera.GetViewMatrix() }); for (auto entity : group) { auto [transformComponent, meshComponent] = group.get(entity); @@ -139,23 +155,63 @@ namespace Prism meshComponent.Mesh->OnUpdate(ts); // TODO: Should we render (logically) - SceneRenderer::SubmitMesh(meshComponent, transformComponent, nullptr); + + if (m_SelectedEntity == entity) + SceneRenderer::SubmitSelectedMesh(meshComponent, transformComponent); + else + SceneRenderer::SubmitMesh(meshComponent, transformComponent, nullptr); + } + } + SceneRenderer::EndScene(); + ///////////////////////////////////////////////////////////////////// + +#if 0 + // Render all sprites + Renderer2D::BeginScene(*camera); + { + auto group = m_Registry.group(entt::get); + for (auto entity : group) + { + auto [transformComponent, spriteRendererComponent] = group.get(entity); + if (spriteRendererComponent.Texture) + Renderer2D::DrawQuad(transformComponent.Transform, spriteRendererComponent.Texture, spriteRendererComponent.TilingFactor); + else + Renderer2D::DrawQuad(transformComponent.Transform, spriteRendererComponent.Color); } } - SceneRenderer::EndScene(); + Renderer2D::EndScene(); +#endif } void Scene::OnEvent(Event& e) { - const auto view = m_Registry.view(); - for (const auto entity : view) + } + + void Scene::OnRuntimeStart() + { + ScriptEngine::SetSceneContext(this); + + auto view = m_Registry.view(); + for (auto entity : view) { - auto& comp = view.get(entity); - comp.Camera.OnEvent(e); - break; + Entity e = { entity, this }; + if (ScriptEngine::ModuleExists(e.GetComponent().ModuleName)) + ScriptEngine::InstantiateEntityClass(e); } - m_Camera.OnEvent(e); + + m_IsPlaying = true; + } + + void Scene::OnRuntimeStop() + { + m_IsPlaying = false; + } + + void Scene::SetViewportSize(const uint32_t width, const uint32_t height) + { + m_ViewportWidth = width; + m_ViewportHeight = height; } void Scene::SetEnvironment(const Environment& environment) @@ -170,6 +226,18 @@ namespace Prism m_SkyboxMaterial->Set("u_Texture", skybox); } + Entity Scene::GetMainCameraEntity() + { + auto view = m_Registry.view(); + for (auto entity : view) + { + auto& comp = view.get(entity); + if (comp.Primary) + return { entity, this }; + } + return {}; + } + void Scene::AddEntity(Entity* entity) { m_Entities.push_back(entity); @@ -178,14 +246,117 @@ namespace Prism Entity Scene::CreateEntity(const std::string& name) { auto entity = Entity{ m_Registry.create(), this }; + auto& idComponent = entity.AddComponent(); + idComponent.ID = {}; + entity.AddComponent(glm::mat4(1.0f)); if (!name.empty()) entity.AddComponent(name); + + m_EntityIDMap[idComponent.ID] = entity; return entity; } - void Scene::DestroyEntity(const Entity entity) + Entity Scene::CreateEntityWithID(UUID uuid, const std::string& name, bool runtimeMap) { - m_Registry.destroy(entity.m_EntityHandle); + auto entity = Entity{ m_Registry.create(), this }; + auto& idComponent = entity.AddComponent(); + idComponent.ID = uuid; + + entity.AddComponent(glm::mat4(1.0f)); + if (!name.empty()) + entity.AddComponent(name); + + PM_CORE_ASSERT(m_EntityIDMap.find(uuid) == m_EntityIDMap.end()); + m_EntityIDMap[uuid] = entity; + return entity; + } + + + template + static void CopyComponent(entt::registry& dstRegistry, entt::registry& srcRegistry, const std::unordered_map& enttMap) + { + auto components = srcRegistry.view(); + for (auto srcEntity : components) + { + entt::entity destEntity = enttMap.at(srcRegistry.get(srcEntity).ID); + + auto& srcComponent = srcRegistry.get(srcEntity); + auto& destComponent = dstRegistry.emplace_or_replace(destEntity, srcComponent); + } + } + + template + static void CopyComponentIfExists(entt::entity dst, entt::entity src, entt::registry& registry) + { + if (registry.storage().contains(src)) + { + auto& srcComponent = registry.get(src); + registry.emplace_or_replace(dst, srcComponent); + } + } + + + void Scene::DuplicateEntity(Entity entity) + { + Entity newEntity; + if (entity.HasComponent()) + newEntity = CreateEntity(entity.GetComponent().Tag); + else + newEntity = CreateEntity(); + + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + } + + void Scene::CopyTo(Ref& target) + { + target->m_Light = m_Light; + target->m_LightMultiplier = m_LightMultiplier; + + target->m_Environment = m_Environment; + target->m_SkyboxTexture = m_SkyboxTexture; + target->m_SkyboxMaterial = m_SkyboxMaterial; + target->m_SkyboxLod = m_SkyboxLod; + + std::unordered_map enttMap; + auto idComponents = m_Registry.view(); + for (auto entity : idComponents) + { + auto uuid = m_Registry.get(entity).ID; + Entity e = target->CreateEntityWithID(uuid, "", true); + enttMap[uuid] = e.m_EntityHandle; + } + + CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); + + const auto& entityInstanceMap = ScriptEngine::GetEntityInstanceMap(); + if (entityInstanceMap.find(target->GetUUID()) != entityInstanceMap.end()) + ScriptEngine::CopyEntityScriptData(target->GetUUID(), m_SceneID); + } + + Ref Scene::GetScene(const UUID& uuid) + { + if (s_ActiveScenes.find(uuid) != s_ActiveScenes.end()) + return s_ActiveScenes.at(uuid); + + return {}; + } + + + void Scene::DestroyEntity(Entity entity) + { + if (entity.HasComponent()) + ScriptEngine::OnScriptComponentDestroyed(m_SceneID, entity.GetUUID()); + + m_Registry.destroy(entity.m_EntityHandle); } } diff --git a/Prism/src/Prism/Scene/Scene.h b/Prism/src/Prism/Scene/Scene.h index c1aa3ef..303219d 100644 --- a/Prism/src/Prism/Scene/Scene.h +++ b/Prism/src/Prism/Scene/Scene.h @@ -11,11 +11,15 @@ #include +#include "Prism/Core/UUID.h" +#include "Prism/Editor/EditorCamera.h" + namespace Prism { struct PRISM_API Environment { + std::string FilePath; Ref RadianceMap; Ref IrradianceMap; @@ -24,13 +28,14 @@ namespace Prism struct Light { - glm::vec3 Direction; - glm::vec3 Radiance; + glm::vec3 Direction{0.0f, 0.0f, 0.0f}; + glm::vec3 Radiance{ 0.0f, 0.0f, 0.0f}; float Multiplier = 1.0f; }; class Entity; + using EntityMap = std::unordered_map; class PRISM_API Scene : public RefCounted { @@ -41,36 +46,68 @@ namespace Prism void Init(); void OnUpdate(TimeStep ts); + void OnRenderRuntime(TimeStep ts); + void OnRenderEditor(TimeStep ts, const EditorCamera& editorCamera); void OnEvent(Event& e); + void OnRuntimeStart(); + void OnRuntimeStop(); + + void SetViewportSize(uint32_t width, uint32_t height); void SetEnvironment(const Environment& environment); + const Environment& GetEnvironment() const { return m_Environment; } void SetSkybox(const Ref& skybox); - Light& GetLight() { return m_Light; } float& GetSkyboxLod() { return m_SkyboxLod; } + Light& GetLight() { return m_Light; } + const Light& GetLight() const { return m_Light; } + + Entity GetMainCameraEntity(); + void AddEntity(Entity* entity); Entity CreateEntity(const std::string& name = ""); + Entity CreateEntityWithID(UUID uuid, const std::string& name = "", bool runtimeMap = false); + void DestroyEntity(Entity entity); + void DuplicateEntity(Entity entity); + template auto GetAllEntitiesWith() { return m_Registry.view(); } + const EntityMap& GetEntityMap() const { return m_EntityIDMap; } + void CopyTo(Ref& target); + + UUID GetUUID() const { return m_SceneID; } + + static Ref GetScene(const UUID& uuid); + + // Editor-specific + void SetSelectedEntity(entt::entity entity) { m_SelectedEntity = entity; } + private: - uint32_t m_SceneID; + UUID m_SceneID; entt::entity m_SceneEntity; entt::registry m_Registry; - std::string m_DebugName; std::vector m_Entities; Camera m_Camera; + std::string m_DebugName; + uint32_t m_ViewportWidth = 0, m_ViewportHeight = 0; + + EntityMap m_EntityIDMap; + + entt::entity m_SelectedEntity; + Light m_Light; float m_LightMultiplier = 0.3f; + bool m_IsPlaying = false; Environment m_Environment; Ref m_SkyboxTexture; @@ -81,7 +118,9 @@ namespace Prism private: friend class Entity; friend class SceneRenderer; + friend class SceneSerializer; friend class SceneHierarchyPanel; + friend void OnScriptComponentConstruct(entt::registry& registry, entt::entity entity); }; } diff --git a/Prism/src/Prism/Scene/SceneCamera.cpp b/Prism/src/Prism/Scene/SceneCamera.cpp new file mode 100644 index 0000000..6ee8c03 --- /dev/null +++ b/Prism/src/Prism/Scene/SceneCamera.cpp @@ -0,0 +1,50 @@ +// +// Created by sfd on 25-12-9. +// + +#include "SceneCamera.h" + +#include "glm/ext/matrix_clip_space.hpp" + +namespace Prism +{ + SceneCamera::SceneCamera() + { + } + + SceneCamera::~SceneCamera() + { + } + + void SceneCamera::SetPerspective(const float verticalFOV, const float nearClip, const float farClip) + { + m_ProjectionType = ProjectionType::Perspective; + m_PerspectiveFOV = verticalFOV; + m_PerspectiveNear = nearClip; + m_PerspectiveFar = farClip; + } + + void SceneCamera::SetOrthographic(const float size, const float nearClip, const float farClip) + { + m_ProjectionType = ProjectionType::Orthographic; + m_OrthographicSize = size; + m_OrthographicNear = nearClip; + m_OrthographicFar = farClip; + } + + void SceneCamera::SetViewportSize(const uint32_t width, const uint32_t height) + { + switch (m_ProjectionType) + { + case ProjectionType::Perspective: + m_ProjectionMatrix = glm::perspectiveFov(m_PerspectiveFOV, (float)width, (float)height, m_PerspectiveNear, m_PerspectiveFar); + break; + case ProjectionType::Orthographic: + const float aspect = (float)width / (float)height; + const float w = m_OrthographicSize * aspect; + const float h = m_OrthographicSize; + m_ProjectionMatrix = glm::ortho(-w * 0.5f, w * 0.5f, -h * 0.5f, h * 0.5f); + break; + } + } +} diff --git a/Prism/src/Prism/Scene/SceneCamera.h b/Prism/src/Prism/Scene/SceneCamera.h new file mode 100644 index 0000000..a08959b --- /dev/null +++ b/Prism/src/Prism/Scene/SceneCamera.h @@ -0,0 +1,54 @@ +// +// Created by sfd on 25-12-9. +// + +#ifndef SCENECAMERA_H +#define SCENECAMERA_H +#include "Prism/Renderer/Camera.h" + + +namespace Prism +{ + class SceneCamera : public Camera + { + public: + enum class ProjectionType { Perspective = 0, Orthographic = 1 }; + public: + SceneCamera(); + virtual ~SceneCamera(); + + + void SetPerspective(float verticalFOV, float nearClip = 0.01f, float farClip = 10000.0f); + void SetOrthographic(float size, float nearClip = -1.0f, float farClip = 1.0f); + void SetViewportSize(uint32_t width, uint32_t height); + + + void SetPerspectiveVerticalFOV(const float verticalFov) { m_PerspectiveFOV = glm::radians(verticalFov); } + float GetPerspectiveVerticalFOV() const { return glm::degrees(m_PerspectiveFOV); } + void SetPerspectiveNearClip(const float nearClip) { m_PerspectiveNear = nearClip; } + float GetPerspectiveNearClip() const { return m_PerspectiveNear; } + void SetPerspectiveFarClip(const float farClip) { m_PerspectiveFar = farClip; } + float GetPerspectiveFarClip() const { return m_PerspectiveFar; } + + void SetOrthographicSize(const float size) { m_OrthographicSize = size; } + float GetOrthographicSize() const { return m_OrthographicSize; } + void SetOrthographicNearClip(const float nearClip) { m_OrthographicNear = nearClip; } + float GetOrthographicNearClip() const { return m_OrthographicNear; } + void SetOrthographicFarClip(const float farClip) { m_OrthographicFar = farClip; } + float GetOrthographicFarClip() const { return m_OrthographicFar; } + + void SetProjectionType(const ProjectionType type) { m_ProjectionType = type; } + ProjectionType GetProjectionType() const { return m_ProjectionType; } + private: + ProjectionType m_ProjectionType = ProjectionType::Perspective; + + float m_PerspectiveFOV = glm::radians(45.0f); + float m_PerspectiveNear = 0.01f, m_PerspectiveFar = 10000.0f; + + float m_OrthographicSize = 10.0f; + float m_OrthographicNear = -1.0f, m_OrthographicFar = 1.0f; + + }; +} + +#endif //SCENECAMERA_H diff --git a/Prism/src/Prism/Scene/SceneSerializer.cpp b/Prism/src/Prism/Scene/SceneSerializer.cpp new file mode 100644 index 0000000..e66558d --- /dev/null +++ b/Prism/src/Prism/Scene/SceneSerializer.cpp @@ -0,0 +1,515 @@ +// +// Created by sfd on 25-12-9. +// + +#include "SceneSerializer.h" + +#include "SceneSerializer.h" + +#include "Entity.h" +#include "Components.h" +#include "Prism/Script/ScriptEngine.h" + +#include "yaml-cpp/yaml.h" + +#define GLM_ENABLE_EXPERIMENTAL +#include +#include +#include + +#include +#include + +namespace YAML +{ + + template<> + struct convert + { + static Node encode(const glm::vec2& rhs) + { + Node node; + node.push_back(rhs.x); + node.push_back(rhs.y); + return node; + } + + static bool decode(const Node& node, glm::vec2& rhs) + { + if (!node.IsSequence() || node.size() != 2) + return false; + + rhs.x = node[0].as(); + rhs.y = node[1].as(); + return true; + } + }; + + template<> + struct convert + { + static Node encode(const glm::vec3& rhs) + { + Node node; + node.push_back(rhs.x); + node.push_back(rhs.y); + node.push_back(rhs.z); + return node; + } + + static bool decode(const Node& node, glm::vec3& rhs) + { + if (!node.IsSequence() || node.size() != 3) + return false; + + rhs.x = node[0].as(); + rhs.y = node[1].as(); + rhs.z = node[2].as(); + return true; + } + }; + + template<> + struct convert + { + static Node encode(const glm::vec4& rhs) + { + Node node; + node.push_back(rhs.x); + node.push_back(rhs.y); + node.push_back(rhs.z); + node.push_back(rhs.w); + return node; + } + + static bool decode(const Node& node, glm::vec4& rhs) + { + if (!node.IsSequence() || node.size() != 4) + return false; + + rhs.x = node[0].as(); + rhs.y = node[1].as(); + rhs.z = node[2].as(); + rhs.w = node[3].as(); + return true; + } + }; + + template<> + struct convert + { + static Node encode(const glm::quat& rhs) + { + Node node; + node.push_back(rhs.w); + node.push_back(rhs.x); + node.push_back(rhs.y); + node.push_back(rhs.z); + return node; + } + + static bool decode(const Node& node, glm::quat& rhs) + { + if (!node.IsSequence() || node.size() != 4) + return false; + + rhs.w = node[0].as(); + rhs.x = node[1].as(); + rhs.y = node[2].as(); + rhs.z = node[3].as(); + return true; + } + }; +} + +namespace Prism +{ + + YAML::Emitter& operator<<(YAML::Emitter& out, const glm::vec2& v) + { + out << YAML::Flow; + out << YAML::BeginSeq << v.x << v.y << YAML::EndSeq; + return out; + } + + YAML::Emitter& operator<<(YAML::Emitter& out, const glm::vec3& v) + { + out << YAML::Flow; + out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq; + return out; + } + + + YAML::Emitter& operator<<(YAML::Emitter& out, const glm::vec4& v) + { + out << YAML::Flow; + out << YAML::BeginSeq << v.x << v.y << v.z << v.w << YAML::EndSeq; + return out; + } + + YAML::Emitter& operator<<(YAML::Emitter& out, const glm::quat& v) + { + out << YAML::Flow; + out << YAML::BeginSeq << v.w << v.x << v.y << v.z << YAML::EndSeq; + return out; + } + + SceneSerializer::SceneSerializer(const Ref& scene) + : m_Scene(scene) + { + } + + static std::tuple GetTransformDecomposition(const glm::mat4& transform) + { + glm::vec3 scale, translation, skew; + glm::vec4 perspective; + glm::quat orientation; + glm::decompose(transform, scale, orientation, translation, skew, perspective); + + return { translation, orientation, scale }; + } + + static void SerializeEntity(YAML::Emitter& out, Entity entity) + { + UUID uuid = entity.GetComponent().ID; + out << YAML::BeginMap; // Entity + out << YAML::Key << "Entity"; + out << YAML::Value << uuid; + + if (entity.HasComponent()) + { + out << YAML::Key << "TagComponent"; + out << YAML::BeginMap; // TagComponent + + auto& tag = entity.GetComponent().Tag; + out << YAML::Key << "Tag" << YAML::Value << tag; + + out << YAML::EndMap; // TagComponent + } + + if (entity.HasComponent()) + { + out << YAML::Key << "TransformComponent"; + out << YAML::BeginMap; // TransformComponent + + auto& transform = entity.GetComponent().Transform; + auto[pos, rot, scale] = GetTransformDecomposition(transform); + out << YAML::Key << "Position" << YAML::Value << pos; + out << YAML::Key << "Rotation" << YAML::Value << rot; + out << YAML::Key << "Scale" << YAML::Value << scale; + + out << YAML::EndMap; // TransformComponent + } + + if (entity.HasComponent()) + { + out << YAML::Key << "ScriptComponent"; + out << YAML::BeginMap; // ScriptComponent + + auto& moduleName = entity.GetComponent().ModuleName; + out << YAML::Key << "ModuleName" << YAML::Value << moduleName; + + EntityInstanceData& data = ScriptEngine::GetEntityInstanceData(entity.GetSceneUUID(), uuid); + const auto& moduleFieldMap = data.ModuleFieldMap; + if (moduleFieldMap.find(moduleName) != moduleFieldMap.end()) + { + const auto& fields = moduleFieldMap.at(moduleName); + out << YAML::Key << "StoredFields" << YAML::Value; + out << YAML::BeginSeq; + for (const auto& [name, field] : fields) + { + out << YAML::BeginMap; // Field + out << YAML::Key << "Name" << YAML::Value << name; + out << YAML::Key << "Type" << YAML::Value << (uint32_t)field.Type; + out << YAML::Key << "Data" << YAML::Value; + + switch (field.Type) + { + case FieldType::Int: + out << field.GetStoredValue(); + break; + case FieldType::UnsignedInt: + out << field.GetStoredValue(); + break; + case FieldType::Float: + out << field.GetStoredValue(); + break; + case FieldType::Vec2: + out << field.GetStoredValue(); + break; + case FieldType::Vec3: + out << field.GetStoredValue(); + break; + case FieldType::Vec4: + out << field.GetStoredValue(); + break; + } + out << YAML::EndMap; // Field + } + out << YAML::EndSeq; + } + + out << YAML::EndMap; // ScriptComponent + } + + if (entity.HasComponent()) + { + out << YAML::Key << "MeshComponent"; + out << YAML::BeginMap; // MeshComponent + + auto mesh = entity.GetComponent().Mesh; + out << YAML::Key << "AssetPath" << YAML::Value << mesh->GetFilePath(); + + out << YAML::EndMap; // MeshComponent + } + + if (entity.HasComponent()) + { + out << YAML::Key << "CameraComponent"; + out << YAML::BeginMap; // CameraComponent + + auto cameraComponent = entity.GetComponent(); + out << YAML::Key << "Camera" << YAML::Value << "some camera data..."; + out << YAML::Key << "Primary" << YAML::Value << cameraComponent.Primary; + + out << YAML::EndMap; // CameraComponent + } + + if (entity.HasComponent()) + { + out << YAML::Key << "SpriteRendererComponent"; + out << YAML::BeginMap; // SpriteRendererComponent + + auto spriteRendererComponent = entity.GetComponent(); + out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color; + if (spriteRendererComponent.Texture) + out << YAML::Key << "TextureAssetPath" << YAML::Value << "path/to/asset"; + out << YAML::Key << "TilingFactor" << YAML::Value << spriteRendererComponent.TilingFactor; + + out << YAML::EndMap; // SpriteRendererComponent + } + + out << YAML::EndMap; // Entity + } + + static void SerializeEnvironment(YAML::Emitter& out, const Ref& scene) + { + out << YAML::Key << "Environment"; + out << YAML::Value; + out << YAML::BeginMap; // Environment + out << YAML::Key << "AssetPath" << YAML::Value << scene->GetEnvironment().FilePath; + const auto& light = scene->GetLight(); + out << YAML::Key << "Light" << YAML::Value; + out << YAML::BeginMap; // Light + out << YAML::Key << "Direction" << YAML::Value << light.Direction; + out << YAML::Key << "Radiance" << YAML::Value << light.Radiance; + out << YAML::Key << "Multiplier" << YAML::Value << light.Multiplier; + out << YAML::EndMap; // Light + out << YAML::EndMap; // Environment + } + + void SceneSerializer::Serialize(const std::string& filepath) + { + YAML::Emitter out; + out << YAML::BeginMap; + out << YAML::Key << "Scene"; + out << YAML::Value << "Scene Name"; + SerializeEnvironment(out, m_Scene); + out << YAML::Key << "Entities"; + out << YAML::Value << YAML::BeginSeq; + m_Scene->m_Registry.view().each([&](auto entityID) + { + const Entity entity = { entityID, m_Scene.Raw() }; + if (!entity || !entity.HasComponent()) + return; + + SerializeEntity(out, entity); + }); + out << YAML::EndSeq; + out << YAML::EndMap; + + std::ofstream fout(filepath); + fout << out.c_str(); + } + + void SceneSerializer::SerializeRuntime(const std::string& filepath) + { + // Not implemented + PM_CORE_ASSERT(false); + } + + bool SceneSerializer::Deserialize(const std::string& filepath) + { + std::ifstream stream(filepath); + std::stringstream strStream; + strStream << stream.rdbuf(); + + YAML::Node data = YAML::Load(strStream.str()); + if (!data["Scene"]) + return false; + + std::string sceneName = data["Scene"].as(); + PM_CORE_INFO("Deserializing scene '{0}'", sceneName); + + auto environment = data["Environment"]; + if (environment) + { + std::string envPath = environment["AssetPath"].as(); + m_Scene->SetEnvironment(Environment::Load(envPath)); + + auto lightNode = environment["Light"]; + if (lightNode) + { + auto& light = m_Scene->GetLight(); + light.Direction = lightNode["Direction"].as(); + light.Radiance = lightNode["Radiance"].as(); + light.Multiplier = lightNode["Multiplier"].as(); + } + } + + auto entities = data["Entities"]; + if (entities) + { + for (auto entity : entities) + { + uint64_t uuid = entity["Entity"].as(); + + std::string name; + auto tagComponent = entity["TagComponent"]; + if (tagComponent) + name = tagComponent["Tag"].as(); + + PM_CORE_INFO("Deserialized entity with ID = {0}, name = {1}", uuid, name); + + Entity deserializedEntity = m_Scene->CreateEntityWithID(uuid, name); + + auto transformComponent = entity["TransformComponent"]; + if (transformComponent) + { + // Entities always have transforms + auto& transform = deserializedEntity.GetComponent().Transform; + glm::vec3 translation = transformComponent["Position"].as(); + glm::quat rotation = transformComponent["Rotation"].as(); + glm::vec3 scale = transformComponent["Scale"].as(); + + transform = glm::translate(glm::mat4(1.0f), translation) * + glm::toMat4(rotation) * glm::scale(glm::mat4(1.0f), scale); + + PM_CORE_INFO(" Entity Transform:"); + PM_CORE_INFO(" Translation: {0}, {1}, {2}", translation.x, translation.y, translation.z); + PM_CORE_INFO(" Rotation: {0}, {1}, {2}, {3}", rotation.w, rotation.x, rotation.y, rotation.z); + PM_CORE_INFO(" Scale: {0}, {1}, {2}", scale.x, scale.y, scale.z); + } + + auto scriptComponent = entity["ScriptComponent"]; + if (scriptComponent) + { + std::string moduleName = scriptComponent["ModuleName"].as(); + deserializedEntity.AddComponent(moduleName); + + PM_CORE_INFO(" Script Module: {0}", moduleName); + + if (ScriptEngine::ModuleExists(moduleName)) + { + auto storedFields = scriptComponent["StoredFields"]; + if (storedFields) + { + for (auto field : storedFields) + { + std::string name = field["Name"].as(); + FieldType type = (FieldType)field["Type"].as(); + EntityInstanceData& data = ScriptEngine::GetEntityInstanceData(m_Scene->GetUUID(), uuid); + auto& moduleFieldMap = data.ModuleFieldMap; + auto& publicFields = moduleFieldMap[moduleName]; + if (publicFields.find(name) == publicFields.end()) + { + PublicField pf = { name, type }; + publicFields.emplace(name, std::move(pf)); + } + auto dataNode = field["Data"]; + switch (type) + { + case FieldType::Float: + { + publicFields.at(name).SetStoredValue(dataNode.as()); + break; + } + case FieldType::Int: + { + publicFields.at(name).SetStoredValue(dataNode.as()); + break; + } + case FieldType::UnsignedInt: + { + publicFields.at(name).SetStoredValue(dataNode.as()); + break; + } + case FieldType::String: + { + PM_CORE_ASSERT(false, "Unimplemented"); + break; + } + case FieldType::Vec2: + { + publicFields.at(name).SetStoredValue(dataNode.as()); + break; + } + case FieldType::Vec3: + { + publicFields.at(name).SetStoredValue(dataNode.as()); + break; + } + case FieldType::Vec4: + { + publicFields.at(name).SetStoredValue(dataNode.as()); + break; + } + } + } + } + } + } + + auto meshComponent = entity["MeshComponent"]; + if (meshComponent) + { + std::string meshPath = meshComponent["AssetPath"].as(); + // TEMP (because script creates mesh component...) + if (!deserializedEntity.HasComponent()) + deserializedEntity.AddComponent(Ref::Create(meshPath)); + + PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath); + } + + auto cameraComponent = entity["CameraComponent"]; + if (cameraComponent) + { + auto& component = deserializedEntity.AddComponent(); + component.Camera = SceneCamera(); + component.Primary = cameraComponent["Primary"].as(); + + PM_CORE_INFO(" Primary Camera: {0}", component.Primary); + } + + auto spriteRendererComponent = entity["SpriteRendererComponent"]; + if (spriteRendererComponent) + { + auto& component = deserializedEntity.AddComponent(); + component.Color = spriteRendererComponent["Color"].as(); + component.TilingFactor = spriteRendererComponent["TilingFactor"].as(); + + PM_CORE_INFO(" SpriteRendererComponent present."); + } + } + } + return true; + } + + bool SceneSerializer::DeserializeRuntime(const std::string& filepath) + { + // Not implemented + PM_CORE_ASSERT(false); + return false; + } + +} diff --git a/Prism/src/Prism/Scene/SceneSerializer.h b/Prism/src/Prism/Scene/SceneSerializer.h new file mode 100644 index 0000000..9e2e9f6 --- /dev/null +++ b/Prism/src/Prism/Scene/SceneSerializer.h @@ -0,0 +1,30 @@ +// +// Created by sfd on 25-12-9. +// + +#ifndef SCENESERIALIZER_H +#define SCENESERIALIZER_H +#include "Scene.h" +#include "Prism/Core/Ref.h" + + +namespace Prism +{ + class PRISM_API SceneSerializer + { + public: + SceneSerializer(const Ref& scene); + + void Serialize(const std::string& filepath); + void SerializeRuntime(const std::string& filepath); + + bool Deserialize(const std::string& filepath); + bool DeserializeRuntime(const std::string& filepath); + private: + Ref m_Scene; + + }; +} + + +#endif //SCENESERIALIZER_H diff --git a/Prism/src/Prism/Script/ScriptEngine.cpp b/Prism/src/Prism/Script/ScriptEngine.cpp index 7cc4f65..85669f6 100644 --- a/Prism/src/Prism/Script/ScriptEngine.cpp +++ b/Prism/src/Prism/Script/ScriptEngine.cpp @@ -16,14 +16,17 @@ #include #include #include +#include +#include "imgui.h" namespace Prism { static MonoDomain* s_MonoDomain = nullptr; static std::string s_AssemblyPath; + static Ref s_SceneContext; - static ScriptModuleFieldMap s_PublicFields; + static EntityInstanceMap s_EntityInstanceMap; static MonoMethod* GetMethod(MonoImage* image, const std::string& methodDesc); @@ -33,10 +36,10 @@ namespace Prism std::string ClassName; std::string NamespaceName; - MonoClass* Class; - MonoMethod* OnCreateMethod; - MonoMethod* OnDestroyMethod; - MonoMethod* OnUpdateMethod; + MonoClass* Class = nullptr; + MonoMethod* OnCreateMethod = nullptr; + MonoMethod* OnDestroyMethod = nullptr; + MonoMethod* OnUpdateMethod = nullptr; void InitClassMethods(MonoImage* image) { @@ -45,21 +48,8 @@ namespace Prism } }; - struct EntityInstance - { - EntityScriptClass* ScriptClass; - - uint32_t Handle; - Scene* SceneInstance; - - MonoObject* GetInstance() - { - return mono_gchandle_get_target(Handle); - } - }; static std::unordered_map s_EntityClassMap; - static std::unordered_map s_EntityInstanceMap; MonoAssembly* LoadAssemblyFromFile(const char* filepath) @@ -100,17 +90,11 @@ namespace Prism return nullptr; } - MonoAssembly* assembly = mono_assembly_load_from_full( - image, - filepath, - &status, - 0 - ); + const auto assembly = mono_assembly_load_from_full(image, filepath, &status, 0); mono_image_close(image); return assembly; - } static void InitMono() @@ -125,6 +109,11 @@ namespace Prism s_MonoDomain = mono_domain_create_appdomain(name, nullptr); } + static void ShutdownMono() + { + mono_jit_cleanup(s_MonoDomain); + } + static MonoAssembly* LoadAssembly(const std::string& path) { MonoAssembly* assembly = LoadAssemblyFromFile(path.c_str()); //mono_domain_assembly_open(s_MonoDomain, path.c_str()); @@ -242,6 +231,89 @@ namespace Prism ScriptEngineRegistry::RegisterAll(); } + static uint32_t GetFieldSize(FieldType type) + { + switch (type) + { + case FieldType::Float: return 4; + case FieldType::Int: return 4; + case FieldType::UnsignedInt: return 4; + // case FieldType::String: return 8; // TODO + case FieldType::Vec2: return 4 * 2; + case FieldType::Vec3: return 4 * 3; + case FieldType::Vec4: return 4 * 4; + } + PM_CORE_ASSERT(false, "Unknown field type!"); + return 0; + } + + static FieldType GetPrismFieldType(MonoType* monoType) + { + int type = mono_type_get_type(monoType); + switch (type) + { + case MONO_TYPE_R4: return FieldType::Float; + case MONO_TYPE_I4: return FieldType::Int; + case MONO_TYPE_U4: return FieldType::UnsignedInt; + case MONO_TYPE_STRING: return FieldType::String; + case MONO_TYPE_VALUETYPE: + { + const char* name = mono_type_get_name(monoType); + if (strcmp(name, "Prism.Vec2") == 0) return FieldType::Vec2; + if (strcmp(name, "Prism.Vec3") == 0) return FieldType::Vec3; + if (strcmp(name, "Prism.Vec4") == 0) return FieldType::Vec4; + } + } + return FieldType::None; + } + + const char* FieldTypeToString(FieldType type) + { + switch (type) + { + case FieldType::Float: return "Float"; + case FieldType::Int: return "Int"; + case FieldType::UnsignedInt: return "UnsignedInt"; + case FieldType::String: return "String"; + case FieldType::Vec2: return "Vec2"; + case FieldType::Vec3: return "Vec3"; + case FieldType::Vec4: return "Vec4"; + } + return "Unknown"; + } + + uint8_t* PublicField::AllocateBuffer(FieldType type) + { + uint32_t size = GetFieldSize(type); + uint8_t* buffer = new uint8_t[size]; + memset(buffer, 0, size); + return buffer; + } + + void PublicField::SetStoredValue_Internal(void* value) const + { + const uint32_t size = GetFieldSize(Type); + memcpy(m_StoredValueBuffer, value, size); + } + + void PublicField::GetStoredValue_Internal(void* outValue) const + { + const uint32_t size = GetFieldSize(Type); + memcpy(outValue, m_StoredValueBuffer, size); + } + + void PublicField::SetRuntimeValue_Internal(void* value) const + { + PM_CORE_ASSERT(m_EntityInstance->GetInstance()); + mono_field_set_value(m_EntityInstance->GetInstance(), m_MonoClassField, value); + } + + void PublicField::GetRuntimeValue_Internal(void* outValue) const + { + PM_CORE_ASSERT(m_EntityInstance->GetInstance()); + mono_field_get_value(m_EntityInstance->GetInstance(), m_MonoClassField, outValue); + } + void ScriptEngine::Init(const std::string& assemblyPath) { s_AssemblyPath = assemblyPath; @@ -254,85 +326,193 @@ namespace Prism void ScriptEngine::Shutdown() { // shutdown mono + s_SceneContext = nullptr; + s_EntityInstanceMap.clear(); + ShutdownMono(); + } + + void ScriptEngine::OnSceneDestruct(UUID sceneID) + { + if (s_EntityInstanceMap.find(sceneID) != s_EntityInstanceMap.end()) + { + s_EntityInstanceMap.at(sceneID).clear(); + s_EntityInstanceMap.erase(sceneID); + } + } + + void ScriptEngine::LoadPrismRuntimeAssembly(const std::string& path) + { + MonoDomain* domain = nullptr; + bool cleanup = false; + if (s_MonoDomain) + { + domain = mono_domain_create_appdomain((char*)"Prism Runtime", nullptr); + mono_domain_set(domain, false); + + cleanup = true; + } + + s_CoreAssembly = LoadAssembly("assets/scripts/Prism-ScriptCore.dll"); + s_CoreAssemblyImage = GetAssemblyImage(s_CoreAssembly); + + auto appAssembly = LoadAssembly(path); + auto appAssemblyImage = GetAssemblyImage(appAssembly); + ScriptEngineRegistry::RegisterAll(); + + if (cleanup) + { + mono_domain_unload(s_MonoDomain); + s_MonoDomain = domain; + } + + s_AppAssembly = appAssembly; + s_AppAssemblyImage = appAssemblyImage; + } + + void ScriptEngine::ReloadAssembly(const std::string& path) + { + LoadPrismRuntimeAssembly(path); + if (s_EntityInstanceMap.size()) + { + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + if (s_EntityInstanceMap.find(scene->GetUUID()) != s_EntityInstanceMap.end()) + { + auto& entityMap = s_EntityInstanceMap.at(scene->GetUUID()); + for (auto&[entityID, entityInstanceData]: entityMap) + { + const auto& interalEntityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); + InitScriptEntity(interalEntityMap.at(entityID)); + } + } + } + } + + void ScriptEngine::SetSceneContext(const Ref& scene) + { + s_SceneContext = scene; + } + + const Ref& ScriptEngine::GetCurrentSceneContext() + { + return s_SceneContext; + } + + void ScriptEngine::CopyEntityScriptData(UUID dst, UUID src) + { + PM_CORE_ASSERT(s_EntityInstanceMap.find(dst) != s_EntityInstanceMap.end()); + PM_CORE_ASSERT(s_EntityInstanceMap.find(src) != s_EntityInstanceMap.end()); + + auto& dstEntityMap = s_EntityInstanceMap.at(dst); + auto& srcEntityMap = s_EntityInstanceMap.at(src); + + for (auto& [entityID, entityInstanceData] : srcEntityMap) + { + for (auto& [moduleName, srcFieldMap] : srcEntityMap[entityID].ModuleFieldMap) + { + auto& dstModuleFieldMap = dstEntityMap[entityID].ModuleFieldMap; + for (auto& [fieldName, field] : srcFieldMap) + { + PM_CORE_ASSERT(dstModuleFieldMap.find(moduleName) != dstModuleFieldMap.end()); + auto& fieldMap = dstModuleFieldMap.at(moduleName); + PM_CORE_ASSERT(fieldMap.find(fieldName) != fieldMap.end()); + fieldMap.at(fieldName).SetStoredValueRaw(field.m_StoredValueBuffer); + } + } + } } void ScriptEngine::OnCreateEntity(Entity entity) { - EntityInstance& entityInstance = s_EntityInstanceMap[(uint32_t)entity.m_EntityHandle]; + OnCreateEntity(entity.m_Scene->GetUUID(), entity.GetComponent().ID); + } + + void ScriptEngine::OnCreateEntity(UUID sceneID, UUID entityID) + { + EntityInstance& entityInstance = GetEntityInstanceData(sceneID, entityID).Instance; if (entityInstance.ScriptClass->OnCreateMethod) CallMethod(entityInstance.GetInstance(), entityInstance.ScriptClass->OnCreateMethod); } - void ScriptEngine::OnUpdateEntity(uint32_t entityID, TimeStep ts) + void ScriptEngine::OnUpdateEntity(UUID sceneID, UUID entityID, TimeStep ts) { - PM_CORE_ASSERT(s_EntityInstanceMap.find(entityID) != s_EntityInstanceMap.end(), "Could not find entity in instance map!"); - - auto& entity = s_EntityInstanceMap[entityID]; - - if (entity.ScriptClass->OnUpdateMethod) + EntityInstance& entityInstance = GetEntityInstanceData(sceneID, entityID).Instance; + if (entityInstance.ScriptClass->OnUpdateMethod) { void* args[] = { &ts }; - CallMethod(entity.GetInstance(), entity.ScriptClass->OnUpdateMethod, args); + CallMethod(entityInstance.GetInstance(), entityInstance.ScriptClass->OnUpdateMethod, args); } } - static FieldType GetPrismFieldType(MonoType* monoType) + void ScriptEngine::OnScriptComponentDestroyed(UUID sceneID, UUID entityID) { - int type = mono_type_get_type(monoType); - switch (type) - { - case MONO_TYPE_R4: return FieldType::Float; - case MONO_TYPE_I4: return FieldType::Int; - case MONO_TYPE_U4: return FieldType::UnsignedInt; - case MONO_TYPE_STRING: return FieldType::String; - case MONO_TYPE_VALUETYPE: - { - char* name = mono_type_get_name(monoType); - if (strcmp(name, "Prism.Vector2") == 0) return FieldType::Vec2; - if (strcmp(name, "Prism.Vector3") == 0) return FieldType::Vec3; - if (strcmp(name, "Prism.Vector4") == 0) return FieldType::Vec4; - } - } - return FieldType::None; + PM_CORE_ASSERT(s_EntityInstanceMap.find(sceneID) != s_EntityInstanceMap.end()); + auto& entityMap = s_EntityInstanceMap.at(sceneID); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end()); + entityMap.erase(entityID); } - void ScriptEngine::OnInitEntity(ScriptComponent& script, uint32_t entityID, uint32_t sceneID) + bool ScriptEngine::ModuleExists(const std::string& moduleName) { - EntityScriptClass& scriptClass = s_EntityClassMap[script.ModuleName]; - scriptClass.FullName = script.ModuleName; - if (script.ModuleName.find('.') != std::string::npos) + std::string NamespaceName, ClassName; + if (moduleName.find('.') != std::string::npos) { - scriptClass.NamespaceName = script.ModuleName.substr(0, script.ModuleName.find_last_of('.')); - scriptClass.ClassName = script.ModuleName.substr(script.ModuleName.find_last_of('.') + 1); + NamespaceName = moduleName.substr(0, moduleName.find_last_of('.')); + ClassName = moduleName.substr(moduleName.find_last_of('.') + 1); } else { - scriptClass.ClassName = script.ModuleName; + ClassName = moduleName; + } + + const MonoClass* monoClass = mono_class_from_name(s_AppAssemblyImage, NamespaceName.c_str(), ClassName.c_str()); + return monoClass != nullptr; + } + + void ScriptEngine::InitScriptEntity(Entity entity) + { + const Scene* scene = entity.m_Scene; + const UUID id = entity.GetComponent().ID; + auto& moduleName = entity.GetComponent().ModuleName; + if (moduleName.empty()) + return; + + if (!ModuleExists(moduleName)) + { + PM_CORE_ERROR("Entity references non-existent script module '{0}'", moduleName); + return; + } + + EntityScriptClass& scriptClass = s_EntityClassMap[moduleName]; + scriptClass.FullName = moduleName; + if (moduleName.find('.') != std::string::npos) + { + scriptClass.NamespaceName = moduleName.substr(0, moduleName.find_last_of('.')); + scriptClass.ClassName = moduleName.substr(moduleName.find_last_of('.') + 1); + } + else + { + scriptClass.ClassName = moduleName; } scriptClass.Class = GetClass(s_AppAssemblyImage, scriptClass); scriptClass.InitClassMethods(s_AppAssemblyImage); - EntityInstance& entityInstance = s_EntityInstanceMap[entityID]; + EntityInstanceData& entityInstanceData = s_EntityInstanceMap[scene->GetUUID()][id]; + EntityInstance& entityInstance = entityInstanceData.Instance; entityInstance.ScriptClass = &scriptClass; - entityInstance.Handle = Instantiate(scriptClass); + ScriptModuleFieldMap& moduleFieldMap = entityInstanceData.ModuleFieldMap; + auto& fieldMap = moduleFieldMap[moduleName]; - MonoProperty* entityIDPropery = mono_class_get_property_from_name(scriptClass.Class, "EntityID"); - mono_property_get_get_method(entityIDPropery); - MonoMethod* entityIDSetMethod = mono_property_get_set_method(entityIDPropery); - void* param[] = { &entityID }; - CallMethod(entityInstance.GetInstance(), entityIDSetMethod, param); + // Save old fields + std::unordered_map oldFields; + oldFields.reserve(fieldMap.size()); + for (auto& [fieldName, field] : fieldMap) + oldFields.emplace(fieldName, std::move(field)); + fieldMap.clear(); - MonoProperty* sceneIDPropery = mono_class_get_property_from_name(scriptClass.Class, "SceneID"); - mono_property_get_get_method(sceneIDPropery); - MonoMethod* sceneIDSetMethod = mono_property_get_set_method(sceneIDPropery); - param[0] = { &sceneID }; - CallMethod(entityInstance.GetInstance(), sceneIDSetMethod, param); - - if (scriptClass.OnCreateMethod) - CallMethod(entityInstance.GetInstance(), scriptClass.OnCreateMethod); - - // Retrieve public fields + // Retrieve public fields (TODO: cache these fields if the module is used more than once) { MonoClassField* iter; void* ptr = 0; @@ -340,35 +520,171 @@ namespace Prism { const char* name = mono_field_get_name(iter); const uint32_t flags = mono_field_get_flags(iter); - if ((flags & MONO_FIELD_ATTR_PUBLIC )== 0) + if ((flags & MONO_FIELD_ATTR_PUBLIC) == 0) continue; MonoType* fieldType = mono_field_get_type(iter); - FieldType hazelFieldType = GetPrismFieldType(fieldType); + const FieldType prismFieldType = GetPrismFieldType(fieldType); // TODO: Attributes - // MonoCustomAttrInfo* attr = mono_custom_attrs_from_field(scriptClass.Class, iter); + MonoCustomAttrInfo* attr = mono_custom_attrs_from_field(scriptClass.Class, iter); - auto& publicField = s_PublicFields[script.ModuleName].emplace_back(name, hazelFieldType); - publicField.m_EntityInstance = &entityInstance; - publicField.m_MonoClassField = iter; - // mono_field_set_value(entityInstance.Instance, iter, ) + if (oldFields.find(name) != oldFields.end()) + { + fieldMap.emplace(name, std::move(oldFields.at(name))); + } + else + { + PublicField field = { name, prismFieldType }; + field.m_EntityInstance = &entityInstance; + field.m_MonoClassField = iter; + fieldMap.emplace(name, std::move(field)); + } } } } - const ScriptModuleFieldMap& ScriptEngine::GetFieldMap() + void ScriptEngine::ShutdownScriptEntity(Entity entity, const std::string& moduleName) { - return s_PublicFields; + EntityInstanceData& entityInstanceData = GetEntityInstanceData(entity.GetSceneUUID(), entity.GetUUID()); + ScriptModuleFieldMap& moduleFieldMap = entityInstanceData.ModuleFieldMap; + if (moduleFieldMap.find(moduleName) != moduleFieldMap.end()) + moduleFieldMap.erase(moduleName); } - void PublicField::SetValue_Internal(void* value) const + void ScriptEngine::InstantiateEntityClass(Entity entity) { - mono_field_set_value(m_EntityInstance->GetInstance(), m_MonoClassField, value); + const Scene* scene = entity.m_Scene; + UUID id = entity.GetComponent().ID; + const auto& moduleName = entity.GetComponent().ModuleName; + + EntityInstanceData& entityInstanceData = GetEntityInstanceData(scene->GetUUID(), id); + EntityInstance& entityInstance = entityInstanceData.Instance; + PM_CORE_ASSERT(entityInstance.ScriptClass); + entityInstance.Handle = Instantiate(*entityInstance.ScriptClass); + + MonoProperty* entityIDPropery = mono_class_get_property_from_name(entityInstance.ScriptClass->Class, "ID"); + mono_property_get_get_method(entityIDPropery); + MonoMethod* entityIDSetMethod = mono_property_get_set_method(entityIDPropery); + void* param[] = { &id }; + CallMethod(entityInstance.GetInstance(), entityIDSetMethod, param); + + // Set all public fields to appropriate values + ScriptModuleFieldMap& moduleFieldMap = entityInstanceData.ModuleFieldMap; + if (moduleFieldMap.find(moduleName) != moduleFieldMap.end()) + { + auto& publicFields = moduleFieldMap.at(moduleName); + for (auto&[name, field] : publicFields) + field.CopyStoredValueToRuntime(); + } + + // Call OnCreate function (if exists) + OnCreateEntity(entity); } - void PublicField::GetValue_Internal(void* outValue) const + EntityInstanceMap& ScriptEngine::GetEntityInstanceMap() { - mono_field_get_value(m_EntityInstance->GetInstance(), m_MonoClassField, outValue); + return s_EntityInstanceMap; + } + + EntityInstanceData& ScriptEngine::GetEntityInstanceData(UUID sceneID, UUID entityID) + { + PM_CORE_ASSERT(s_EntityInstanceMap.find(sceneID) != s_EntityInstanceMap.end(), "Invalid scene ID!"); + auto& entityIDMap = s_EntityInstanceMap.at(sceneID); + PM_CORE_ASSERT(entityIDMap.find(entityID) != entityIDMap.end(), "Invalid entity ID!"); + return entityIDMap.at(entityID); + } + + void ScriptEngine::OnImGuiRender() + { + ImGui::Begin("Script Engine Debug"); + for (auto& [sceneID, entityMap] : s_EntityInstanceMap) + { + bool opened = ImGui::TreeNode((void*)(uint64_t)sceneID, "Scene (%llx)", sceneID); + if (opened) + { + Ref scene = Scene::GetScene(sceneID); + for (auto& [entityID, entityInstanceData] : entityMap) + { + Entity entity = scene->GetScene(sceneID)->GetEntityMap().at(entityID); + std::string entityName = "Unnamed Entity"; + if (entity.HasComponent()) + entityName = entity.GetComponent().Tag; + opened = ImGui::TreeNode((void*)(uint64_t)entityID, "%s (%llx)", entityName.c_str(), entityID); + if (opened) + { + for (auto& [moduleName, fieldMap] : entityInstanceData.ModuleFieldMap) + { + opened = ImGui::TreeNode(moduleName.c_str()); + if (opened) + { + for (auto& [fieldName, field] : fieldMap) + { + + opened = ImGui::TreeNodeEx((void*)&field, ImGuiTreeNodeFlags_Leaf , fieldName.c_str()); + if (opened) + { + + ImGui::TreePop(); + } + } + ImGui::TreePop(); + } + } + ImGui::TreePop(); + } + } + ImGui::TreePop(); + } + } + ImGui::End(); + } + + + MonoObject* EntityInstance::GetInstance() + { + PM_CORE_ASSERT(Handle, "Entity has not been instantiated!"); + return mono_gchandle_get_target(Handle); + } + + PublicField::PublicField(const std::string& name, FieldType type) + : Name(name), Type(type) + { + m_StoredValueBuffer = AllocateBuffer(type); + } + + PublicField::PublicField(PublicField&& other) + { + Name = std::move(other.Name); + Type = other.Type; + m_EntityInstance = other.m_EntityInstance; + m_MonoClassField = other.m_MonoClassField; + m_StoredValueBuffer = other.m_StoredValueBuffer; + + other.m_EntityInstance = nullptr; + other.m_MonoClassField = nullptr; + other.m_StoredValueBuffer = nullptr; + } + + PublicField::~PublicField() + { + delete[] m_StoredValueBuffer; + } + + void PublicField::CopyStoredValueToRuntime() + { + PM_CORE_ASSERT(m_EntityInstance->GetInstance()); + mono_field_set_value(m_EntityInstance->GetInstance(), m_MonoClassField, m_StoredValueBuffer); + } + + bool PublicField::IsRuntimeAvailable() const + { + return m_EntityInstance->Handle != 0; + } + + void PublicField::SetStoredValueRaw(void* src) + { + uint32_t size = GetFieldSize(Type); + memcpy(m_StoredValueBuffer, src, size); } } diff --git a/Prism/src/Prism/Script/ScriptEngine.h b/Prism/src/Prism/Script/ScriptEngine.h index 3da1864..582db18 100644 --- a/Prism/src/Prism/Script/ScriptEngine.h +++ b/Prism/src/Prism/Script/ScriptEngine.h @@ -19,56 +19,118 @@ namespace Prism None = 0, Float, Int, UnsignedInt, String, Vec2, Vec3, Vec4 }; - struct EntityInstance; + const char* FieldTypeToString(FieldType type); + + struct EntityScriptClass; + struct EntityInstance + { + EntityScriptClass* ScriptClass = nullptr; + + uint32_t Handle = 0; + Scene* SceneInstance = nullptr; + + MonoObject* GetInstance(); + }; struct PublicField { std::string Name; FieldType Type; - PublicField(const std::string& name, FieldType type) - : Name(name), Type(type) + PublicField(const std::string& name, FieldType type); + PublicField(const PublicField&) = delete; + PublicField(PublicField&& other); + ~PublicField(); + + void CopyStoredValueToRuntime(); + bool IsRuntimeAvailable() const; + + template + T GetStoredValue() const { + T value; + GetStoredValue_Internal(&value); + return value; + } + + template + void SetStoredValue(T value) const + { + SetStoredValue_Internal(&value); } template - T GetValue() const + T GetRuntimeValue() const { T value; - GetValue_Internal(&value); + GetRuntimeValue_Internal(&value); return value; } template - void SetValue(T value) const + void SetRuntimeValue(T value) const { - SetValue_Internal(&value); + SetRuntimeValue_Internal(&value); } + void SetStoredValueRaw(void* src); private: EntityInstance* m_EntityInstance; MonoClassField* m_MonoClassField; + uint8_t* m_StoredValueBuffer = nullptr; - void SetValue_Internal(void* value) const; - void GetValue_Internal(void* outValue) const; + uint8_t* AllocateBuffer(FieldType type); + void SetStoredValue_Internal(void* value) const; + void GetStoredValue_Internal(void* outValue) const; + void SetRuntimeValue_Internal(void* value) const; + void GetRuntimeValue_Internal(void* outValue) const; friend class ScriptEngine; }; - using ScriptModuleFieldMap = std::unordered_map>; + // using ScriptModuleFieldMap = std::unordered_map>; + using ScriptModuleFieldMap = std::unordered_map>; + struct EntityInstanceData + { + EntityInstance Instance; + ScriptModuleFieldMap ModuleFieldMap; + }; - class ScriptEngine + using EntityInstanceMap = std::unordered_map>; + + class PRISM_API ScriptEngine { public: static void Init(const std::string& assemblyPath); static void Shutdown(); + static void OnSceneDestruct(UUID sceneID); + + static void LoadPrismRuntimeAssembly(const std::string& path); + static void ReloadAssembly(const std::string& path); + + static void SetSceneContext(const Ref& scene); + static const Ref& GetCurrentSceneContext(); + + static void CopyEntityScriptData(UUID dst, UUID src); + static void OnCreateEntity(Entity entity); - static void OnUpdateEntity(uint32_t entityID, TimeStep ts); + static void OnCreateEntity(UUID sceneID, UUID entityID); + static void OnUpdateEntity(UUID sceneID, UUID entityID, TimeStep ts); - static void OnInitEntity(ScriptComponent& script, uint32_t entityID, uint32_t sceneID); + static void OnScriptComponentDestroyed(UUID sceneID, UUID entityID); - static const ScriptModuleFieldMap& GetFieldMap(); + static bool ModuleExists(const std::string& moduleName); + static void InitScriptEntity(Entity entity); + + static void ShutdownScriptEntity(Entity entity, const std::string& moduleName); + static void InstantiateEntityClass(Entity entity); + + static EntityInstanceMap& GetEntityInstanceMap(); + static EntityInstanceData& GetEntityInstanceData(UUID sceneID, UUID entityID); + + // Debug + static void OnImGuiRender(); }; } diff --git a/Prism/src/Prism/Script/ScriptWarppers.cpp b/Prism/src/Prism/Script/ScriptWarppers.cpp index 1c1aa8e..7aecc4e 100644 --- a/Prism/src/Prism/Script/ScriptWarppers.cpp +++ b/Prism/src/Prism/Script/ScriptWarppers.cpp @@ -4,6 +4,7 @@ #include "ScriptWarppers.h" +#include "ScriptEngine.h" #include "mono/metadata/metadata.h" #include "mono/metadata/object.h" #include "mono/metadata/reflection.h" @@ -14,7 +15,6 @@ namespace Prism { - extern std::unordered_map s_ActiveScenes; extern std::unordered_map> s_HasComponentFuncs; extern std::unordered_map> s_CreateComponentFuncs; } @@ -58,63 +58,75 @@ namespace Prism { namespace Script { // Entity ////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// - void Prism_Entity_GetTransform(uint32_t sceneID, uint32_t entityID, glm::mat4* outTransform) + void Prism_Entity_GetTransform(uint64_t entityID, glm::mat4* outTransform) { - PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!"); + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + const auto& entityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); - Scene* scene = s_ActiveScenes[sceneID]; - Entity entity((entt::entity)entityID, scene); + Entity entity = entityMap.at(entityID); auto& transformComponent = entity.GetComponent(); memcpy(outTransform, glm::value_ptr(transformComponent.Transform), sizeof(glm::mat4)); } - void Prism_Entity_SetTransform(uint32_t sceneID, uint32_t entityID, glm::mat4* inTransform) + void Prism_Entity_SetTransform(uint64_t entityID, glm::mat4* inTransform) { - PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!"); + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + const auto& entityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); - Scene* scene = s_ActiveScenes[sceneID]; - Entity entity((entt::entity)entityID, scene); + Entity entity = entityMap.at(entityID); auto& transformComponent = entity.GetComponent(); memcpy(glm::value_ptr(transformComponent.Transform), inTransform, sizeof(glm::mat4)); } - void Prism_Entity_CreateComponent(uint32_t sceneID, uint32_t entityID, void* type) + void Prism_Entity_CreateComponent(uint64_t entityID, void* type) { - PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!"); - MonoType* monoType = mono_reflection_type_get_type((MonoReflectionType*)type); + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + const auto& entityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); - Scene* scene = s_ActiveScenes[sceneID]; - Entity entity((entt::entity)entityID, scene); + Entity entity = entityMap.at(entityID); + MonoType* monoType = mono_reflection_type_get_type((MonoReflectionType*)type); s_CreateComponentFuncs[monoType](entity); } - bool Prism_Entity_HasComponent(uint32_t sceneID, uint32_t entityID, void* type) + bool Prism_Entity_HasComponent(uint64_t entityID, void* type) { - PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!"); - MonoType* monoType = mono_reflection_type_get_type((MonoReflectionType*)type); + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + const auto& entityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); - Scene* scene = s_ActiveScenes[sceneID]; - Entity entity((entt::entity)entityID, scene); + Entity entity = entityMap.at(entityID); + MonoType* monoType = mono_reflection_type_get_type((MonoReflectionType*)type); bool result = s_HasComponentFuncs[monoType](entity); return result; } - void* Prism_MeshComponent_GetMesh(uint32_t sceneID, uint32_t entityID) + void* Prism_MeshComponent_GetMesh(uint64_t entityID) { - PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!"); + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + const auto& entityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); - Scene* scene = s_ActiveScenes[sceneID]; - Entity entity((entt::entity)entityID, scene); - auto& meshComponent = entity.GetComponent(); + Entity entity = entityMap.at(entityID); + const auto& meshComponent = entity.GetComponent(); return new Ref(meshComponent.Mesh); } - void Prism_MeshComponent_SetMesh(uint32_t sceneID, uint32_t entityID, Ref* inMesh) + void Prism_MeshComponent_SetMesh(uint64_t entityID, Ref* inMesh) { - PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!"); + Ref scene = ScriptEngine::GetCurrentSceneContext(); + PM_CORE_ASSERT(scene, "No active scene!"); + const auto& entityMap = scene->GetEntityMap(); + PM_CORE_ASSERT(entityMap.find(entityID) != entityMap.end(), "Invalid entity ID or entity doesn't exist in scene!"); - Scene* scene = s_ActiveScenes[sceneID]; - Entity entity((entt::entity)entityID, scene); + Entity entity = entityMap.at(entityID); auto& meshComponent = entity.GetComponent(); meshComponent.Mesh = inMesh ? *inMesh : nullptr; } diff --git a/Prism/src/Prism/Script/ScriptWarppers.h b/Prism/src/Prism/Script/ScriptWarppers.h index 02824d0..261616b 100644 --- a/Prism/src/Prism/Script/ScriptWarppers.h +++ b/Prism/src/Prism/Script/ScriptWarppers.h @@ -24,13 +24,13 @@ namespace Prism { namespace Script { bool Prism_Input_IsKeyPressed(KeyCode key); // Entity - void Prism_Entity_GetTransform(uint32_t sceneID, uint32_t entityID, glm::mat4* outTransform); - void Prism_Entity_SetTransform(uint32_t sceneID, uint32_t entityID, glm::mat4* inTransform); - void Prism_Entity_CreateComponent(uint32_t sceneID, uint32_t entityID, void* type); - bool Prism_Entity_HasComponent(uint32_t sceneID, uint32_t entityID, void* type); + void Prism_Entity_GetTransform(uint64_t entityID, glm::mat4* outTransform); + void Prism_Entity_SetTransform(uint64_t entityID, glm::mat4* inTransform); + void Prism_Entity_CreateComponent(uint64_t entityID, void* type); + bool Prism_Entity_HasComponent(uint64_t entityID, void* type); - void* Prism_MeshComponent_GetMesh(uint32_t sceneID, uint32_t entityID); - void Prism_MeshComponent_SetMesh(uint32_t sceneID, uint32_t entityID, Ref* inMesh); + void* Prism_MeshComponent_GetMesh(uint64_t entityID); + void Prism_MeshComponent_SetMesh(uint64_t entityID, Ref* inMesh); // Renderer // Texture2D diff --git a/Prism/vendor/yaml-cpp b/Prism/vendor/yaml-cpp new file mode 160000 index 0000000..bbf8bdb --- /dev/null +++ b/Prism/vendor/yaml-cpp @@ -0,0 +1 @@ +Subproject commit bbf8bdb087bb3f3621ca0a5ace06047805f4e9f3