add serialization (use yaml-cpp), add stencil to render outline for selected entity, add play mode, UUIDS, add orthographic and perspective camera, add editor camera

This commit is contained in:
2025-12-09 16:30:53 +08:00
parent 3ffb4cc449
commit dc53f9517a
49 changed files with 2801 additions and 796 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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<Scene>::Create("Model Scene");
m_CameraEntity = m_Scene->CreateEntity("Camera Entity");
m_CameraEntity.AddComponent<CameraComponent>(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<Mesh>::Create("assets/meshes/TestScene.fbx");
// auto mesh = Ref<Mesh>::Create("assets/models/Apollo AE/Apollo AE.fbx");
// auto mesh = Ref<Mesh>::Create("assets/models/tafi/塔菲.pmx");
m_MeshEntity.AddComponent<MeshComponent>(mesh);
m_MeshMaterial = mesh->GetMaterial();
m_MeshEntity.AddComponent<ScriptComponent>("Example.Script");
// Test Sandbox
auto mapGenerator = m_Scene->CreateEntity("Map Generator");
mapGenerator.AddComponent<ScriptComponent>("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<Mesh>::Create("assets/models/m1911/M1911Materials.fbx");
secondEntity->SetMesh(mesh);
*/
}
// Sphere Scene
{
m_SphereScene = Ref<Scene>::Create("PBR Sphere Scene");
auto cameraEntity = m_SphereScene->CreateEntity("camera");
cameraEntity.AddComponent<CameraComponent>(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)));
m_SphereScene->SetEnvironment(environment);
auto sphereMesh = Ref<Mesh>::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<MaterialInstance> mi = Ref<MaterialInstance>::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<MaterialInstance> 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<SceneHierarchyPanel>(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<Scene>::Create();
ScriptEngine::SetSceneContext(m_EditorScene);
m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(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,74 +148,63 @@ 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;
switch (m_SceneState)
{
case SceneState::Edit:
{
if (m_ViewportPanelFocused)
m_EditorCamera.OnUpdate(deltaTime);
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);
m_EditorScene->OnRenderEditor(deltaTime, m_EditorCamera);
if (m_DrawOnTopBoundingBoxes)
{
Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false);
auto viewProj = m_CameraEntity.GetComponent<CameraComponent>().Camera.GetViewProjection();
auto viewProj = m_EditorCamera.GetViewProjection();
Renderer2D::BeginScene(viewProj, false);
Renderer::DrawAABB(m_MeshEntity.GetComponent<MeshComponent>(), m_MeshEntity.GetComponent<TransformComponent>());
// TODO: Renderer::DrawAABB(m_MeshEntity.GetComponent<MeshComponent>(), m_MeshEntity.GetComponent<TransformComponent>());
Renderer2D::EndScene();
Renderer::EndRenderPass();
}
if (!m_SelectionContext.empty())
if (m_SelectionContext.size() && false)
{
auto& selection = m_SelectionContext[0];
if (selection.Mesh && selection.Entity.HasComponent<MeshComponent>())
{
Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false);
const auto viewProj = m_CameraEntity.GetComponent<CameraComponent>().Camera.GetViewProjection();
auto viewProj = m_EditorCamera.GetViewProjection();
Renderer2D::BeginScene(viewProj, false);
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 };
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<TransformComponent>().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;
}
}
}
void EditorLayer::OnImGuiRender()
{
@ -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<Scene> newScene = Ref<Scene>::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<CameraComponent>().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<MeshComponent>();
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<CameraComponent>().Camera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f));
m_CameraEntity.GetComponent<CameraComponent>().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<CameraComponent>().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<TransformComponent>().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<KeyPressedEvent>(PM_BIND_EVENT_FN(EditorLayer::OnKeyPressedEvent));
@ -759,6 +674,8 @@ namespace Prism
}
bool EditorLayer::OnKeyPressedEvent(KeyPressedEvent& e)
{
if (m_ViewportPanelFocused)
{
switch (e.GetKeyCode())
{
@ -774,21 +691,41 @@ namespace Prism
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
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))
{
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;
}
}
return false;
}
@ -803,10 +740,11 @@ namespace Prism
auto [origin, direction] = CastRay(mouseX, mouseY);
m_SelectionContext.clear();
const auto meshEntities = m_Scene->GetAllEntitiesWith<MeshComponent>();
m_EditorScene->SetSelectedEntity({});
const auto meshEntities = m_EditorScene->GetAllEntitiesWith<MeshComponent>();
for (auto e : meshEntities)
{
Entity entity = { e, m_Scene.Raw() };
Entity entity = { e, m_EditorScene.Raw() };
auto mesh = entity.GetComponent<MeshComponent>().Mesh;
if (!mesh)
@ -857,6 +795,20 @@ namespace Prism
m_DrawOnTopBoundingBoxes = show && onTop;
}
void EditorLayer::SelectEntity(Entity entity)
{
SelectedSubmesh selection;
if (entity.HasComponent<MeshComponent>())
{
selection.Mesh = &entity.GetComponent<MeshComponent>().Mesh->GetSubmeshes()[0];
}
selection.Entity = entity;
m_SelectionContext.clear();
m_SelectionContext.push_back(selection);
m_EditorScene->SetSelectedEntity(entity);
}
std::pair<float, float> 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<CameraComponent>().Camera.GetProjectionMatrix());
const auto inverseView = glm::inverse(glm::mat3(m_CameraEntity.GetComponent<CameraComponent>().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<CameraComponent>().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<Scene>::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);
}
}

View File

@ -27,7 +27,7 @@ namespace Prism
bool OnMouseButtonPressedEvent(MouseButtonPressedEvent& e);
void ShowBoundingBoxes(bool show, bool onTop = false);
void SelectEntity(Entity entity);
private:
std::pair<float, float> GetMouseViewportSpace() const;
std::pair<glm::vec3, glm::vec3> 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<SceneHierarchyPanel> m_SceneHierarchyPanel;
Ref<Scene> m_Scene;
Ref<Scene> m_SphereScene;
Ref<Scene> m_ActiveScene;
Ref<Scene> m_RuntimeScene, m_EditorScene;
Entity m_MeshEntity;
Entity m_CameraEntity;
EditorCamera m_EditorCamera;
Ref<Material> m_SphereBaseMaterial;
Ref<Material> m_MeshMaterial;
std::vector<Ref<MaterialInstance>> m_MetalSphereMaterialInstances;
@ -71,14 +72,6 @@ namespace Prism
std::vector<SelectedSubmesh> 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<Texture2D> m_CheckerboardTex;
Ref<Texture2D> 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<Texture2D> m_CheckerboardTex;
bool m_ViewportPanelMouseOver = false;
bool m_ViewportPanelFocused = false;
enum class SceneState
{
Edit = 0, Play = 1, Pause = 2
};
SceneState m_SceneState = SceneState::Edit;
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

View File

@ -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

View File

@ -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);
}

View File

@ -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.

View File

@ -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.

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -6,7 +6,12 @@ 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()
{
@ -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);

27
ExampleApp/Src/Sink.cs Normal file
View File

@ -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);
}
}
}

View File

@ -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<T>() 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<T>() where T : Component, new()
{
return HasComponent_Native(SceneID, EntityID, typeof(T));
return HasComponent_Native(ID, typeof(T));
}
public T GetComponent<T>() 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);
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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; }

View File

@ -6,6 +6,7 @@
#define MOUSEEVENT_H
#include "Event.h"
#include <sstream>
namespace Prism {

View File

@ -0,0 +1,29 @@
//
// Created by sfd on 25-12-9.
//
#include "UUID.h"
#include <random>
namespace Prism
{
static std::random_device s_RandomDevice;
static std::mt19937_64 eng(s_RandomDevice());
static std::uniform_int_distribution<uint64_t> 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)
{
}
}

View File

@ -0,0 +1,42 @@
//
// Created by sfd on 25-12-9.
//
#ifndef UUID_H
#define UUID_H
#include <functional>
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<Prism::UUID>
{
std::size_t operator()(const Prism::UUID& uuid) const
{
return hash<uint64_t>()((uint64_t)uuid);
}
};
}
#endif //UUID_H

View File

@ -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<MouseScrolledEvent>(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<float, float> 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;
}
}

View File

@ -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<float, float> 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

View File

@ -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>& 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,12 +45,15 @@ namespace Prism
void SceneHierarchyPanel::OnImGuiRender()
{
ImGui::Begin("Scene Hierarchy");
uint32_t entityCount = 0, meshCount = 0;
for(const auto entity : m_Context->m_Registry.view<entt::entity>())
if (m_Context)
{
DrawEntityNode(Entity(entity, m_Context.Raw()));
}
uint32_t entityCount = 0, meshCount = 0;
m_Context->m_Registry.view<entt::entity>().each([&](auto entity)
{
const Entity e(entity, m_Context.Raw());
if (e.HasComponent<IDComponent>())
DrawEntityNode(e);
});
if (ImGui::BeginPopupContextWindow())
@ -51,17 +65,56 @@ namespace Prism
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<CameraComponent>())
{
if (ImGui::Button("Camera"))
{
m_SelectionContext.AddComponent<CameraComponent>();
ImGui::CloseCurrentPopup();
}
}
if (!m_SelectionContext.HasComponent<MeshComponent>())
{
if (ImGui::Button("Mesh"))
{
m_SelectionContext.AddComponent<MeshComponent>();
ImGui::CloseCurrentPopup();
}
}
if (!m_SelectionContext.HasComponent<ScriptComponent>())
{
if (ImGui::Button("Script"))
{
m_SelectionContext.AddComponent<ScriptComponent>();
ImGui::CloseCurrentPopup();
}
}
if (!m_SelectionContext.HasComponent<SpriteRendererComponent>())
{
if (ImGui::Button("Sprite Renderer"))
{
m_SelectionContext.AddComponent<SpriteRendererComponent>();
ImGui::CloseCurrentPopup();
}
}
ImGui::EndPopup();
}
}
}
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<IDComponent>().ID;
if (entity.HasComponent<TagComponent>())
{
auto& tag = entity.GetComponent<TagComponent>().Tag;
@ -337,25 +447,47 @@ namespace Prism
{
tag = std::string(buffer);
}
}
// ID
ImGui::SameLine();
ImGui::TextDisabled("%llx", id);
ImGui::Separator();
}
if (entity.HasComponent<TransformComponent>())
{
auto& tc = entity.GetComponent<TransformComponent>();
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<MeshComponent>())
{
auto& mc = entity.GetComponent<MeshComponent>();
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<Mesh>::Create(file);
}
ImGui::NextColumn();
ImGui::Columns(1);
ImGui::TreePop();
}
ImGui::Separator();
@ -402,6 +559,59 @@ namespace Prism
auto& cc = entity.GetComponent<CameraComponent>();
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())
{
auto& publicFields = moduleFieldMap.at(sc.ModuleName);
for (auto& [name, field] : publicFields)
{
bool isRuntime = m_Context->m_IsPlaying && field.IsRuntimeAvailable();
switch (field.Type)
{
case FieldType::Int:
{
int value = field.GetValue<int>();
int value = isRuntime ? field.GetRuntimeValue<int>() : field.GetStoredValue<int>();
if (Property(field.Name.c_str(), value))
{
field.SetValue(value);
if (isRuntime)
field.SetRuntimeValue(value);
else
field.SetStoredValue(value);
}
break;
}
case FieldType::Float:
{
float value = field.GetValue<float>();
float value = isRuntime ? field.GetRuntimeValue<float>() : field.GetStoredValue<float>();
if (Property(field.Name.c_str(), value, 0.2f))
{
field.SetValue(value);
if (isRuntime)
field.SetRuntimeValue(value);
else
field.SetStoredValue(value);
}
break;
}
case FieldType::Vec2:
{
glm::vec2 value = field.GetValue<glm::vec2>();
glm::vec2 value = isRuntime ? field.GetRuntimeValue<glm::vec2>() : field.GetStoredValue<glm::vec2>();
if (Property(field.Name.c_str(), value, 0.2f))
{
field.SetValue(value);
if (isRuntime)
field.SetRuntimeValue(value);
else
field.SetStoredValue(value);
}
break;
}
case FieldType::Vec3:
{
glm::vec3 value = isRuntime ? field.GetRuntimeValue<glm::vec3>() : field.GetStoredValue<glm::vec3>();
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<glm::vec4>() : field.GetStoredValue<glm::vec4>();
if (Property(field.Name.c_str(), value, 0.2f))
{
if (isRuntime)
field.SetRuntimeValue(value);
else
field.SetStoredValue(value);
}
break;
}
}
}
}
}
EndPropertyGrid();
#if TODO
if (ImGui::Button("Run Script"))
{
ScriptEngine::OnCreateEntity(entity);
}
#endif
ImGui::TreePop();
}
ImGui::Separator();

View File

@ -18,6 +18,8 @@ namespace Prism
void SetContext(const Ref<Scene>& scene);
void SetSelected(Entity entity);
void SetSelectionChangedCallback(const std::function<void(Entity)>& func) { m_SelectionChangedCallback = func; }
void SetEntityDeletedCallback(const std::function<void(Entity)>& func) { m_EntityDeletedCallback = func; }
void OnImGuiRender();
private:
@ -28,6 +30,8 @@ namespace Prism
private:
Ref<Scene> m_Context;
Entity m_SelectionContext;
std::function<void(Entity)> m_SelectionChangedCallback, m_EntityDeletedCallback;
};
}

View File

@ -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<const OpenGLVertexBuffer> instance = this;
Renderer::Submit([instance]() {
glBindBuffer(GL_ARRAY_BUFFER, instance->m_RendererID);
});
}

View File

@ -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);

View File

@ -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)

View File

@ -207,7 +207,7 @@ namespace Prism
{
PM_CORE_ASSERT(false, "Could not load shader file");
}
file.close();
return result;
}

View File

@ -3,6 +3,8 @@
//
#include "OpenGLVertexArray.h"
#include "OpenGLBuffer.h"
#include "glad/glad.h"
#include "Prism/Renderer/Renderer.h"
@ -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
}

View File

@ -4,19 +4,6 @@
#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
@ -24,137 +11,6 @@ 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))
{
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<MouseScrolledEvent>(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<float, float> 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;
}
}

View File

@ -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<float, float> 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;
};
}

View File

@ -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;

View File

@ -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();

View File

@ -20,7 +20,7 @@ namespace Prism
const Scene* ActiveScene = nullptr;
struct SceneInfo
{
Camera SceneCamera;
SceneRendererCamera SceneCamera;
// Resources
Ref<MaterialInstance> SkyboxMaterial;
@ -41,9 +41,11 @@ namespace Prism
glm::mat4 Transform;
};
std::vector<DrawCommand> DrawList;
std::vector<DrawCommand> SelectedMeshDrawList;
// Grid
Ref<MaterialInstance> GridMaterial;
Ref<MaterialInstance> 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>& mesh, const glm::mat4& transform)
{
s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform });
}
static Ref<Shader> 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);

View File

@ -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>& mesh, const glm::mat4& transform = glm::mat4(1.0f), const Ref<MaterialInstance>& overrideMaterial = nullptr);
static void SubmitSelectedMesh(const Ref<Mesh>& mesh, const glm::mat4& transform = glm::mat4(1.0f));
static std::pair<Ref<TextureCube>, Ref<TextureCube>> CreateEnvironmentMap(const std::string& filepath);

View File

@ -7,18 +7,30 @@
#include <string>
#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<Prism::Mesh> Mesh;
MeshComponent() = default;
MeshComponent(const MeshComponent& other)
: Mesh(other.Mesh) {}
MeshComponent(const Ref<Prism::Mesh>& mesh)
: Mesh(mesh) {}
operator Ref<Prism::Mesh> () { 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<Texture2D> Texture;
float TilingFactor = 1.0f;
SpriteRendererComponent() = default;
SpriteRendererComponent(const SpriteRendererComponent& other)
: Color(other.Color), Texture(other.Texture), TilingFactor(other.TilingFactor) {}
};
}

View File

@ -44,6 +44,7 @@ namespace Prism
const glm::mat4& Transform() const { return m_Scene->m_Registry.get<TransformComponent>(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<IDComponent>().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;
};
}

View File

@ -11,13 +11,13 @@
namespace Prism
{
std::unordered_map<uint32_t, Scene*> s_ActiveScenes;
std::unordered_map<UUID, Scene*> 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<SceneComponent>();
uint32_t sceneID = 0;
for (const auto aEntity : view)
{
const auto& scene = registry.get<SceneComponent>(aEntity);
sceneID = scene.SceneID;
}
const auto sceneView = registry.view<SceneComponent>();
UUID sceneID = registry.get<SceneComponent>(sceneView.front()).SceneID;
ScriptEngine::OnInitEntity(registry.get<ScriptComponent>(entity), (uint32_t)entity, sceneID);
Scene* scene = s_ActiveScenes[sceneID];
auto entityID = registry.get<IDComponent>(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<CameraComponent>();
for (auto entity : view)
{
auto& comp = view.get<CameraComponent>(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<ScriptComponent>();
for (auto entity : view)
ScriptEngine::OnUpdateEntity((uint32_t)entity, ts);
{
UUID entityID = m_Registry.get<IDComponent>(entity).ID;
Entity e = { entity, this };
if (ScriptEngine::ModuleExists(e.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnUpdateEntity(m_SceneID, entityID, ts);
}
}
}
#if 0
void Scene::OnRenderRuntime(TimeStep ts)
{
/////////////////////////////////////////////////////////////////////
// RENDER 3D SCENE //
/////////////////////////////////////////////////////////////////////
Entity cameraEntity = GetMainCameraEntity();
if (!cameraEntity)
return;
glm::mat4 cameraViewMatrix = glm::inverse(cameraEntity.GetComponent<TransformComponent>().Transform);
PM_CORE_ASSERT(cameraEntity, "Scene does not contain any cameras!");
SceneCamera& camera = cameraEntity.GetComponent<CameraComponent>();
camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight);
m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod);
auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>);
SceneRenderer::BeginScene(this, { camera, cameraViewMatrix });
for (auto entity : group)
{
auto [transformComponent, meshComponent] = group.get<TransformComponent, MeshComponent>(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<TransformComponent>();
for (auto entity : entities)
{
auto meshComponent = m_Registry.try_get<MeshComponent>(entity);
}
auto group = m_Registry.group<MeshComponent>(entt::get<TransformComponent>);
SceneRenderer::BeginScene(this, *camera);
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix() });
for (auto entity : group)
{
auto [transformComponent, meshComponent] = group.get<TransformComponent, MeshComponent>(entity);
@ -139,23 +155,63 @@ namespace Prism
meshComponent.Mesh->OnUpdate(ts);
// TODO: Should we render (logically)
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<TransformComponent>(entt::get<SpriteRenderer>);
for (auto entity : group)
{
auto [transformComponent, spriteRendererComponent] = group.get<TransformComponent, SpriteRenderer>(entity);
if (spriteRendererComponent.Texture)
Renderer2D::DrawQuad(transformComponent.Transform, spriteRendererComponent.Texture, spriteRendererComponent.TilingFactor);
else
Renderer2D::DrawQuad(transformComponent.Transform, spriteRendererComponent.Color);
}
}
Renderer2D::EndScene();
#endif
}
void Scene::OnEvent(Event& e)
{
const auto view = m_Registry.view<CameraComponent>();
for (const auto entity : view)
{
auto& comp = view.get<CameraComponent>(entity);
comp.Camera.OnEvent(e);
break;
}
m_Camera.OnEvent(e);
void Scene::OnRuntimeStart()
{
ScriptEngine::SetSceneContext(this);
auto view = m_Registry.view<ScriptComponent>();
for (auto entity : view)
{
Entity e = { entity, this };
if (ScriptEngine::ModuleExists(e.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::InstantiateEntityClass(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<CameraComponent>();
for (auto entity : view)
{
auto& comp = view.get<CameraComponent>(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>();
idComponent.ID = {};
entity.AddComponent<TransformComponent>(glm::mat4(1.0f));
if (!name.empty())
entity.AddComponent<TagComponent>(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)
{
auto entity = Entity{ m_Registry.create(), this };
auto& idComponent = entity.AddComponent<IDComponent>();
idComponent.ID = uuid;
entity.AddComponent<TransformComponent>(glm::mat4(1.0f));
if (!name.empty())
entity.AddComponent<TagComponent>(name);
PM_CORE_ASSERT(m_EntityIDMap.find(uuid) == m_EntityIDMap.end());
m_EntityIDMap[uuid] = entity;
return entity;
}
template<typename T>
static void CopyComponent(entt::registry& dstRegistry, entt::registry& srcRegistry, const std::unordered_map<UUID, entt::entity>& enttMap)
{
auto components = srcRegistry.view<T>();
for (auto srcEntity : components)
{
entt::entity destEntity = enttMap.at(srcRegistry.get<IDComponent>(srcEntity).ID);
auto& srcComponent = srcRegistry.get<T>(srcEntity);
auto& destComponent = dstRegistry.emplace_or_replace<T>(destEntity, srcComponent);
}
}
template<typename T>
static void CopyComponentIfExists(entt::entity dst, entt::entity src, entt::registry& registry)
{
if (registry.storage<T>().contains(src))
{
auto& srcComponent = registry.get<T>(src);
registry.emplace_or_replace<T>(dst, srcComponent);
}
}
void Scene::DuplicateEntity(Entity entity)
{
Entity newEntity;
if (entity.HasComponent<TagComponent>())
newEntity = CreateEntity(entity.GetComponent<TagComponent>().Tag);
else
newEntity = CreateEntity();
CopyComponentIfExists<TransformComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<MeshComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<ScriptComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<CameraComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<SpriteRendererComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
}
void Scene::CopyTo(Ref<Scene>& 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<UUID, entt::entity> enttMap;
auto idComponents = m_Registry.view<IDComponent>();
for (auto entity : idComponents)
{
auto uuid = m_Registry.get<IDComponent>(entity).ID;
Entity e = target->CreateEntityWithID(uuid, "", true);
enttMap[uuid] = e.m_EntityHandle;
}
CopyComponent<TagComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<TransformComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<MeshComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<ScriptComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<CameraComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<SpriteRendererComponent>(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> 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<ScriptComponent>())
ScriptEngine::OnScriptComponentDestroyed(m_SceneID, entity.GetUUID());
m_Registry.destroy(entity.m_EntityHandle);
}
}

View File

@ -11,11 +11,15 @@
#include <entt/entt.hpp>
#include "Prism/Core/UUID.h"
#include "Prism/Editor/EditorCamera.h"
namespace Prism
{
struct PRISM_API Environment
{
std::string FilePath;
Ref<TextureCube> RadianceMap;
Ref<TextureCube> 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<UUID, Entity>;
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<TextureCube>& 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<typename T>
auto GetAllEntitiesWith()
{
return m_Registry.view<T>();
}
const EntityMap& GetEntityMap() const { return m_EntityIDMap; }
void CopyTo(Ref<Scene>& target);
UUID GetUUID() const { return m_SceneID; }
static Ref<Scene> 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<Entity*> 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<TextureCube> 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);
};
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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 <glm/gtx/quaternion.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <fstream>
namespace YAML
{
template<>
struct convert<glm::vec2>
{
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<float>();
rhs.y = node[1].as<float>();
return true;
}
};
template<>
struct convert<glm::vec3>
{
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<float>();
rhs.y = node[1].as<float>();
rhs.z = node[2].as<float>();
return true;
}
};
template<>
struct convert<glm::vec4>
{
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<float>();
rhs.y = node[1].as<float>();
rhs.z = node[2].as<float>();
rhs.w = node[3].as<float>();
return true;
}
};
template<>
struct convert<glm::quat>
{
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<float>();
rhs.x = node[1].as<float>();
rhs.y = node[2].as<float>();
rhs.z = node[3].as<float>();
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>& scene)
: m_Scene(scene)
{
}
static std::tuple<glm::vec3, glm::quat, glm::vec3> 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<IDComponent>().ID;
out << YAML::BeginMap; // Entity
out << YAML::Key << "Entity";
out << YAML::Value << uuid;
if (entity.HasComponent<TagComponent>())
{
out << YAML::Key << "TagComponent";
out << YAML::BeginMap; // TagComponent
auto& tag = entity.GetComponent<TagComponent>().Tag;
out << YAML::Key << "Tag" << YAML::Value << tag;
out << YAML::EndMap; // TagComponent
}
if (entity.HasComponent<TransformComponent>())
{
out << YAML::Key << "TransformComponent";
out << YAML::BeginMap; // TransformComponent
auto& transform = entity.GetComponent<TransformComponent>().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<ScriptComponent>())
{
out << YAML::Key << "ScriptComponent";
out << YAML::BeginMap; // ScriptComponent
auto& moduleName = entity.GetComponent<ScriptComponent>().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<int>();
break;
case FieldType::UnsignedInt:
out << field.GetStoredValue<uint32_t>();
break;
case FieldType::Float:
out << field.GetStoredValue<float>();
break;
case FieldType::Vec2:
out << field.GetStoredValue<glm::vec2>();
break;
case FieldType::Vec3:
out << field.GetStoredValue<glm::vec3>();
break;
case FieldType::Vec4:
out << field.GetStoredValue<glm::vec4>();
break;
}
out << YAML::EndMap; // Field
}
out << YAML::EndSeq;
}
out << YAML::EndMap; // ScriptComponent
}
if (entity.HasComponent<MeshComponent>())
{
out << YAML::Key << "MeshComponent";
out << YAML::BeginMap; // MeshComponent
auto mesh = entity.GetComponent<MeshComponent>().Mesh;
out << YAML::Key << "AssetPath" << YAML::Value << mesh->GetFilePath();
out << YAML::EndMap; // MeshComponent
}
if (entity.HasComponent<CameraComponent>())
{
out << YAML::Key << "CameraComponent";
out << YAML::BeginMap; // CameraComponent
auto cameraComponent = entity.GetComponent<CameraComponent>();
out << YAML::Key << "Camera" << YAML::Value << "some camera data...";
out << YAML::Key << "Primary" << YAML::Value << cameraComponent.Primary;
out << YAML::EndMap; // CameraComponent
}
if (entity.HasComponent<SpriteRendererComponent>())
{
out << YAML::Key << "SpriteRendererComponent";
out << YAML::BeginMap; // SpriteRendererComponent
auto spriteRendererComponent = entity.GetComponent<SpriteRendererComponent>();
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>& 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<entt::entity>().each([&](auto entityID)
{
const Entity entity = { entityID, m_Scene.Raw() };
if (!entity || !entity.HasComponent<IDComponent>())
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<std::string>();
PM_CORE_INFO("Deserializing scene '{0}'", sceneName);
auto environment = data["Environment"];
if (environment)
{
std::string envPath = environment["AssetPath"].as<std::string>();
m_Scene->SetEnvironment(Environment::Load(envPath));
auto lightNode = environment["Light"];
if (lightNode)
{
auto& light = m_Scene->GetLight();
light.Direction = lightNode["Direction"].as<glm::vec3>();
light.Radiance = lightNode["Radiance"].as<glm::vec3>();
light.Multiplier = lightNode["Multiplier"].as<float>();
}
}
auto entities = data["Entities"];
if (entities)
{
for (auto entity : entities)
{
uint64_t uuid = entity["Entity"].as<uint64_t>();
std::string name;
auto tagComponent = entity["TagComponent"];
if (tagComponent)
name = tagComponent["Tag"].as<std::string>();
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<TransformComponent>().Transform;
glm::vec3 translation = transformComponent["Position"].as<glm::vec3>();
glm::quat rotation = transformComponent["Rotation"].as<glm::quat>();
glm::vec3 scale = transformComponent["Scale"].as<glm::vec3>();
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<std::string>();
deserializedEntity.AddComponent<ScriptComponent>(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<std::string>();
FieldType type = (FieldType)field["Type"].as<uint32_t>();
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<float>());
break;
}
case FieldType::Int:
{
publicFields.at(name).SetStoredValue(dataNode.as<int32_t>());
break;
}
case FieldType::UnsignedInt:
{
publicFields.at(name).SetStoredValue(dataNode.as<uint32_t>());
break;
}
case FieldType::String:
{
PM_CORE_ASSERT(false, "Unimplemented");
break;
}
case FieldType::Vec2:
{
publicFields.at(name).SetStoredValue(dataNode.as<glm::vec2>());
break;
}
case FieldType::Vec3:
{
publicFields.at(name).SetStoredValue(dataNode.as<glm::vec3>());
break;
}
case FieldType::Vec4:
{
publicFields.at(name).SetStoredValue(dataNode.as<glm::vec4>());
break;
}
}
}
}
}
}
auto meshComponent = entity["MeshComponent"];
if (meshComponent)
{
std::string meshPath = meshComponent["AssetPath"].as<std::string>();
// TEMP (because script creates mesh component...)
if (!deserializedEntity.HasComponent<MeshComponent>())
deserializedEntity.AddComponent<MeshComponent>(Ref<Mesh>::Create(meshPath));
PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath);
}
auto cameraComponent = entity["CameraComponent"];
if (cameraComponent)
{
auto& component = deserializedEntity.AddComponent<CameraComponent>();
component.Camera = SceneCamera();
component.Primary = cameraComponent["Primary"].as<bool>();
PM_CORE_INFO(" Primary Camera: {0}", component.Primary);
}
auto spriteRendererComponent = entity["SpriteRendererComponent"];
if (spriteRendererComponent)
{
auto& component = deserializedEntity.AddComponent<SpriteRendererComponent>();
component.Color = spriteRendererComponent["Color"].as<glm::vec4>();
component.TilingFactor = spriteRendererComponent["TilingFactor"].as<float>();
PM_CORE_INFO(" SpriteRendererComponent present.");
}
}
}
return true;
}
bool SceneSerializer::DeserializeRuntime(const std::string& filepath)
{
// Not implemented
PM_CORE_ASSERT(false);
return false;
}
}

View File

@ -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>& 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<Scene> m_Scene;
};
}
#endif //SCENESERIALIZER_H

View File

@ -16,14 +16,17 @@
#include <mono/metadata/appdomain.h>
#include <mono/metadata/class.h>
#include <mono/metadata/object.h>
#include <mono/metadata/reflection.h>
#include "imgui.h"
namespace Prism
{
static MonoDomain* s_MonoDomain = nullptr;
static std::string s_AssemblyPath;
static Ref<Scene> 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<std::string, EntityScriptClass> s_EntityClassMap;
static std::unordered_map<uint32_t, EntityInstance> 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,38 +231,20 @@ namespace Prism
ScriptEngineRegistry::RegisterAll();
}
void ScriptEngine::Init(const std::string& assemblyPath)
static uint32_t GetFieldSize(FieldType type)
{
s_AssemblyPath = assemblyPath;
InitMono();
LoadPrismRuntimeAssembly(s_AssemblyPath);
}
void ScriptEngine::Shutdown()
{
// shutdown mono
}
void ScriptEngine::OnCreateEntity(Entity entity)
{
EntityInstance& entityInstance = s_EntityInstanceMap[(uint32_t)entity.m_EntityHandle];
if (entityInstance.ScriptClass->OnCreateMethod)
CallMethod(entityInstance.GetInstance(), entityInstance.ScriptClass->OnCreateMethod);
}
void ScriptEngine::OnUpdateEntity(uint32_t 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)
{
void* args[] = { &ts };
CallMethod(entity.GetInstance(), entity.ScriptClass->OnUpdateMethod, args);
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)
@ -287,52 +258,261 @@ namespace Prism
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;
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;
}
void ScriptEngine::OnInitEntity(ScriptComponent& script, uint32_t entityID, uint32_t sceneID)
const char* FieldTypeToString(FieldType type)
{
EntityScriptClass& scriptClass = s_EntityClassMap[script.ModuleName];
scriptClass.FullName = script.ModuleName;
if (script.ModuleName.find('.') != std::string::npos)
switch (type)
{
scriptClass.NamespaceName = script.ModuleName.substr(0, script.ModuleName.find_last_of('.'));
scriptClass.ClassName = script.ModuleName.substr(script.ModuleName.find_last_of('.') + 1);
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;
InitMono();
LoadPrismRuntimeAssembly(s_AssemblyPath);
}
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> 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>& scene)
{
s_SceneContext = scene;
}
const Ref<Scene>& 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)
{
OnCreateEntity(entity.m_Scene->GetUUID(), entity.GetComponent<IDComponent>().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(UUID sceneID, UUID entityID, TimeStep ts)
{
EntityInstance& entityInstance = GetEntityInstanceData(sceneID, entityID).Instance;
if (entityInstance.ScriptClass->OnUpdateMethod)
{
void* args[] = { &ts };
CallMethod(entityInstance.GetInstance(), entityInstance.ScriptClass->OnUpdateMethod, args);
}
}
void ScriptEngine::OnScriptComponentDestroyed(UUID sceneID, UUID entityID)
{
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);
}
bool ScriptEngine::ModuleExists(const std::string& moduleName)
{
std::string NamespaceName, ClassName;
if (moduleName.find('.') != std::string::npos)
{
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<IDComponent>().ID;
auto& moduleName = entity.GetComponent<ScriptComponent>().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<std::string, PublicField> 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;
@ -344,31 +524,167 @@ namespace Prism
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, )
}
}
}
const ScriptModuleFieldMap& ScriptEngine::GetFieldMap()
if (oldFields.find(name) != oldFields.end())
{
return s_PublicFields;
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));
}
}
}
}
void PublicField::SetValue_Internal(void* value) const
void ScriptEngine::ShutdownScriptEntity(Entity entity, const std::string& moduleName)
{
mono_field_set_value(m_EntityInstance->GetInstance(), m_MonoClassField, value);
EntityInstanceData& entityInstanceData = GetEntityInstanceData(entity.GetSceneUUID(), entity.GetUUID());
ScriptModuleFieldMap& moduleFieldMap = entityInstanceData.ModuleFieldMap;
if (moduleFieldMap.find(moduleName) != moduleFieldMap.end())
moduleFieldMap.erase(moduleName);
}
void PublicField::GetValue_Internal(void* outValue) const
void ScriptEngine::InstantiateEntityClass(Entity entity)
{
mono_field_get_value(m_EntityInstance->GetInstance(), m_MonoClassField, outValue);
const Scene* scene = entity.m_Scene;
UUID id = entity.GetComponent<IDComponent>().ID;
const auto& moduleName = entity.GetComponent<ScriptComponent>().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);
}
EntityInstanceMap& ScriptEngine::GetEntityInstanceMap()
{
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 = Scene::GetScene(sceneID);
for (auto& [entityID, entityInstanceData] : entityMap)
{
Entity entity = scene->GetScene(sceneID)->GetEntityMap().at(entityID);
std::string entityName = "Unnamed Entity";
if (entity.HasComponent<TagComponent>())
entityName = entity.GetComponent<TagComponent>().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);
}
}

View File

@ -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<typename T>
T GetValue() const
T GetStoredValue() const
{
T value;
GetValue_Internal(&value);
GetStoredValue_Internal(&value);
return value;
}
template<typename T>
void SetValue(T value) const
void SetStoredValue(T value) const
{
SetValue_Internal(&value);
SetStoredValue_Internal(&value);
}
template <typename T>
T GetRuntimeValue() const
{
T value;
GetRuntimeValue_Internal(&value);
return value;
}
template <typename T>
void SetRuntimeValue(T value) const
{
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<std::string, std::vector<PublicField>>;
// using ScriptModuleFieldMap = std::unordered_map<std::string, std::vector<PublicField>>;
using ScriptModuleFieldMap = std::unordered_map<std::string, std::unordered_map<std::string, PublicField>>;
struct EntityInstanceData
{
EntityInstance Instance;
ScriptModuleFieldMap ModuleFieldMap;
};
class ScriptEngine
using EntityInstanceMap = std::unordered_map<UUID, std::unordered_map<UUID, EntityInstanceData>>;
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>& scene);
static const Ref<Scene>& 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();
};
}

View File

@ -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<uint32_t, Scene*> s_ActiveScenes;
extern std::unordered_map<MonoType*, std::function<bool(Entity&)>> s_HasComponentFuncs;
extern std::unordered_map<MonoType*, std::function<void(Entity&)>> 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> 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<TransformComponent>();
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> 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<TransformComponent>();
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> 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> 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> 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<MeshComponent>();
Entity entity = entityMap.at(entityID);
const auto& meshComponent = entity.GetComponent<MeshComponent>();
return new Ref<Mesh>(meshComponent.Mesh);
}
void Prism_MeshComponent_SetMesh(uint32_t sceneID, uint32_t entityID, Ref<Mesh>* inMesh)
void Prism_MeshComponent_SetMesh(uint64_t entityID, Ref<Mesh>* inMesh)
{
PM_CORE_ASSERT(s_ActiveScenes.find(sceneID) != s_ActiveScenes.end(), "Invalid Scene ID!");
Ref<Scene> 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>();
meshComponent.Mesh = inMesh ? *inMesh : nullptr;
}

View File

@ -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<Mesh>* inMesh);
void* Prism_MeshComponent_GetMesh(uint64_t entityID);
void Prism_MeshComponent_SetMesh(uint64_t entityID, Ref<Mesh>* inMesh);
// Renderer
// Texture2D

1
Prism/vendor/yaml-cpp vendored Submodule

Submodule Prism/vendor/yaml-cpp added at bbf8bdb087