From 9e1474e64312691bb8b9a66aa844c3733142e2cf Mon Sep 17 00:00:00 2001 From: Atdunbg Date: Fri, 2 Jan 2026 22:46:29 +0800 Subject: [PATCH] add directional light component and skylight component, add PCSS and hard shadow --- Editor/Editor/EditorLayer.cpp | 125 ++-- Editor/Editor/EditorLayer.h | 2 + Editor/assets/scenes/2DTest.scene | 155 ----- Editor/assets/scenes/FPSDemo.scene | 15 +- Editor/assets/scenes/Physics2DTest.hsc | 174 ------ Editor/assets/scenes/Physics3DTest.scene | 119 ---- Editor/assets/scenes/PinkSunrise.hsc | 66 -- Editor/assets/shaders/BloomBlend.glsl | 63 ++ Editor/assets/shaders/BloomBlur.glsl | 79 +++ Editor/assets/shaders/Outline.glsl | 3 - Editor/assets/shaders/Outline_Anim.glsl | 36 ++ Editor/assets/shaders/PBRShader_Anim.glsl | 400 ++++++++++-- Editor/assets/shaders/PBRShader_Static.glsl | 387 ++++++++++-- Editor/assets/shaders/SceneComposite.glsl | 42 +- Editor/assets/shaders/ShadowMap.glsl | 23 + Editor/assets/shaders/ShadowMap_Anim.glsl | 35 ++ Editor/assets/shaders/Skybox.glsl | 5 +- ExampleApp/Src/FPSPlayer.cs | 2 +- Prism/src/Prism/Core/Application.cpp | 2 + Prism/src/Prism/Core/ImGui/ImGui.h | 273 ++++++++ Prism/src/Prism/Core/Window.h | 2 + Prism/src/Prism/Editor/EditorCamera.cpp | 29 +- Prism/src/Prism/Editor/EditorCamera.h | 3 +- .../Prism/Editor/PhysicsSettingsWindow.cpp | 1 + Prism/src/Prism/Editor/SceneHierachyPanel.cpp | 433 +++++-------- Prism/src/Prism/Physics/Physics3D.cpp | 2 +- Prism/src/Prism/Physics/PhysicsUtils.cpp | 6 + Prism/src/Prism/Physics/PhysicsUtils.h | 2 + Prism/src/Prism/Physics/PhysicsWrappers.cpp | 275 +++++++- Prism/src/Prism/Physics/PhysicsWrappers.h | 3 +- .../Platform/OpenGL/OpenGLFrameBuffer.cpp | 301 +++++++-- .../Prism/Platform/OpenGL/OpenGLFrameBuffer.h | 17 +- .../Prism/Platform/OpenGL/OpenGLShader.cpp | 48 +- .../src/Prism/Platform/OpenGL/OpenGLShader.h | 2 + .../Platform/OpenGL/OpenGLShaderUniform.cpp | 18 +- .../Platform/OpenGL/OpenGLShaderUniform.h | 2 +- .../Prism/Platform/OpenGL/OpenGLTexture.cpp | 2 +- .../Prism/Platform/Windows/WindowsWindow.cpp | 7 +- .../Prism/Platform/Windows/WindowsWindow.h | 3 + Prism/src/Prism/Renderer/FrameBuffer.h | 48 +- Prism/src/Prism/Renderer/Material.h | 14 +- Prism/src/Prism/Renderer/Mesh.cpp | 5 +- Prism/src/Prism/Renderer/Mesh.h | 3 + Prism/src/Prism/Renderer/Renderer.cpp | 47 +- Prism/src/Prism/Renderer/Renderer.h | 1 + Prism/src/Prism/Renderer/Renderer2D.cpp | 2 +- Prism/src/Prism/Renderer/SceneEnvironment.cpp | 15 + Prism/src/Prism/Renderer/SceneEnvironment.h | 24 + Prism/src/Prism/Renderer/SceneRenderer.cpp | 590 +++++++++++++++++- Prism/src/Prism/Renderer/SceneRenderer.h | 9 + Prism/src/Prism/Renderer/Shader.h | 6 +- Prism/src/Prism/Scene/Components.h | 29 +- Prism/src/Prism/Scene/Scene.cpp | 110 +++- Prism/src/Prism/Scene/Scene.h | 31 +- Prism/src/Prism/Scene/SceneSerializer.cpp | 57 +- 55 files changed, 3032 insertions(+), 1121 deletions(-) delete mode 100644 Editor/assets/scenes/2DTest.scene delete mode 100644 Editor/assets/scenes/Physics2DTest.hsc delete mode 100644 Editor/assets/scenes/Physics3DTest.scene delete mode 100644 Editor/assets/scenes/PinkSunrise.hsc create mode 100644 Editor/assets/shaders/BloomBlend.glsl create mode 100644 Editor/assets/shaders/BloomBlur.glsl create mode 100644 Editor/assets/shaders/Outline_Anim.glsl create mode 100644 Editor/assets/shaders/ShadowMap.glsl create mode 100644 Editor/assets/shaders/ShadowMap_Anim.glsl create mode 100644 Prism/src/Prism/Core/ImGui/ImGui.h create mode 100644 Prism/src/Prism/Renderer/SceneEnvironment.cpp create mode 100644 Prism/src/Prism/Renderer/SceneEnvironment.h diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index b8056b7..fb28358 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -161,7 +161,7 @@ namespace Prism } EditorLayer::EditorLayer() - : m_SceneType(SceneType::Model), m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)) + : m_SceneType(SceneType::Model), m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f)) { } @@ -175,12 +175,12 @@ namespace Prism m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga"); m_PlayButtonTex = Texture2D::Create("assets/editor/PlayButton.png"); - m_EditorScene = Ref::Create("untitled Scene", true); - ScriptEngine::SetSceneContext(m_EditorScene); m_SceneHierarchyPanel = CreateScope(m_EditorScene); m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1)); m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1)); UpdateWindowTitle("untitled Scene"); + + OpenScene("assets/scenes/FPSDemo.scene"); } void EditorLayer::OnDetach() @@ -190,6 +190,10 @@ namespace Prism void EditorLayer::OnUpdate(const TimeStep deltaTime) { + auto [x, y] = GetMouseViewportSpace(); + + SceneRenderer::SetFocusPoint({ x * 0.5f + 0.5f, y * 0.5f + 0.5f }); + switch (m_SceneState) { case SceneState::Edit: @@ -330,9 +334,11 @@ namespace Prism { if (ImGui::BeginMenu("file")) { - if (ImGui::MenuItem("New Scene")) + if (ImGui::MenuItem("New Scene", "Ctrl+N")) { + NewScene(); } + if (ImGui::MenuItem("Open Scene...", "Ctrl+O")) { OpenScene(); @@ -390,6 +396,8 @@ namespace Prism m_SceneHierarchyPanel->OnImGuiRender(); PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings); + SceneRenderer::OnImGuiRender(); + // m_EditorCamera.OnImGuiRender(); ImGui::Begin("Materials"); @@ -475,6 +483,7 @@ namespace Prism } { // Normals + if (ImGui::CollapsingHeader("Normals", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); @@ -606,13 +615,6 @@ namespace Prism ImGui::Begin("Model"); ImGui::Begin("Environment"); - if (ImGui::Button("Load Environment Map")) - { - std::string filename = Application::Get().OpenFile("*.hdr"); - if (!filename.empty()) - m_EditorScene->SetEnvironment(Environment::Load(filename)); - } - ImGui::SliderFloat("Skybox LOD", &m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f); @@ -623,7 +625,6 @@ namespace Prism Property("Light Direction", light.Direction, PropertyFlag::SliderProperty); Property("Light Radiance", light.Radiance, PropertyFlag::ColorProperty); Property("Light Multiplier", light.Multiplier, 0.0f, 5.0f, PropertyFlag::SliderProperty); - Property("Exposure", m_EditorCamera.GetExposure(), 0.0f, 5.0f, PropertyFlag::SliderProperty); Property("Radiance Prefiltering", m_RadiancePrefilter); Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f, PropertyFlag::SliderProperty); @@ -758,7 +759,7 @@ namespace Prism 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.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 1000.0f)); m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); @@ -846,7 +847,7 @@ namespace Prism bool EditorLayer::OnKeyPressedEvent(KeyPressedEvent& e) { - if (m_ViewportPanelFocused) + if (m_ViewportPanelHovered) { switch (e.GetKeyCode()) { @@ -862,19 +863,23 @@ 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; } } + switch (e.GetKeyCode()) + { + 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)) { const bool IsShiftPressed = Input::IsKeyPressed(KeyCode::LEFT_SHIFT); @@ -888,7 +893,9 @@ namespace Prism case KeyCode::S: SaveScene(); break; - + case KeyCode::N: + NewScene(); + break; case KeyCode::B: // Toggle bounding boxes m_UIShowBoundingBoxes = !m_UIShowBoundingBoxes; @@ -998,10 +1005,8 @@ namespace Prism SelectedSubmesh selection; if (entity.HasComponent()) { - if (auto& meshComp = entity.GetComponent(); meshComp.Mesh) - { - selection.Mesh = &meshComp.Mesh->GetSubmeshes()[0]; - } + if (auto mesh = entity.GetComponent().Mesh) + selection.Mesh = &mesh->GetSubmeshes()[0]; } selection.Entity = entity; m_SelectionContext.clear(); @@ -1068,33 +1073,56 @@ namespace Prism return Ray::Zero(); } + void EditorLayer::NewScene() + { + m_EditorScene = Ref::Create("Untitled Scene", true); + m_SceneHierarchyPanel->SetContext(m_EditorScene); + ScriptEngine::SetSceneContext(m_EditorScene); + UpdateWindowTitle("Untitled Scene"); + m_SceneFilePath = std::string(); + + m_EditorCamera = EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f)); + m_SelectionContext.clear(); + } + void EditorLayer::OpenScene() { const auto& app = Application::Get(); - const std::string filepath = app.OpenFile("Prism Scene (*.hsc)\0*.hsc\0"); + const std::string filepath = app.OpenFile("Hazel Scene (*.hsc)\0*.hsc\0"); if (!filepath.empty()) - { - const Ref newScene = Ref::Create("EditorScene", true); - SceneSerializer serializer(newScene); - serializer.Deserialize(filepath); - m_EditorScene = newScene; - const std::filesystem::path path = filepath; - UpdateWindowTitle(path.filename().string()); - m_SceneHierarchyPanel->SetContext(m_EditorScene); - ScriptEngine::SetSceneContext(m_EditorScene); + OpenScene(filepath); + } - m_EditorScene->SetSelectedEntity({}); - m_SelectionContext.clear(); + void EditorLayer::OpenScene(const std::string& filepath) + { + const std::string fileName = std::filesystem::path(filepath).filename().string(); + UpdateWindowTitle(fileName); - m_SceneFilePath = filepath; - } + const Ref newScene = Ref::Create(fileName, true); + SceneSerializer serializer(newScene); + serializer.Deserialize(filepath); + m_EditorScene = newScene; + m_SceneFilePath = filepath; + + m_SceneHierarchyPanel->SetContext(m_EditorScene); + ScriptEngine::SetSceneContext(m_EditorScene); + + m_EditorScene->SetSelectedEntity({}); + m_SelectionContext.clear(); } void EditorLayer::SaveScene() { - PM_CLIENT_INFO("Saving scene to: {0}", m_SceneFilePath); - SceneSerializer serializer(m_EditorScene); - serializer.Serialize(m_SceneFilePath); + if (!m_SceneFilePath.empty()) + { + PM_CLIENT_INFO("Saving scene to: {0}", m_SceneFilePath); + SceneSerializer serializer(m_EditorScene); + serializer.Serialize(m_SceneFilePath); + } + else + { + SaveSceneAs(); + } } void EditorLayer::SaveSceneAs() @@ -1110,6 +1138,9 @@ namespace Prism std::filesystem::path path = filepath; UpdateWindowTitle(path.filename().string()); m_SceneFilePath = filepath; + }else + { + PM_CLIENT_INFO("cancelled"); } } @@ -1140,8 +1171,6 @@ namespace Prism m_SelectionContext.clear(); ScriptEngine::SetSceneContext(m_EditorScene); m_SceneHierarchyPanel->SetContext(m_EditorScene); - - Input::SetCursorMode(CursorMode::Normal); } float EditorLayer::GetSnapValue() diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index cb90fdc..984ed35 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -44,7 +44,9 @@ namespace Prism void OnEntityDeleted(Entity e); Ray CastMouseRay(); + void NewScene(); void OpenScene(); + void OpenScene(const std::string& filepath); void SaveScene(); void SaveSceneAs(); diff --git a/Editor/assets/scenes/2DTest.scene b/Editor/assets/scenes/2DTest.scene deleted file mode 100644 index 09c0b82..0000000 --- a/Editor/assets/scenes/2DTest.scene +++ /dev/null @@ -1,155 +0,0 @@ -Scene: Scene Name -Environment: - AssetPath: assets/env/pink_sunrise_4k.hdr - Light: - Direction: [-0.787, -0.73299998, 1] - Radiance: [1, 1, 1] - Multiplier: 0.514999986 -Entities: - - Entity: 12498244675852797835 - TagComponent: - Tag: Box - TransformComponent: - Position: [-12.0348625, 6.59647179, 9.60061925e-07] - Rotation: [1, 0, 0, 0] - Scale: [3.00000024, 0.300000012, 1] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 0 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [1.5, 0.150000006] - Density: 1 - Friction: 1 - - Entity: 5178862374589434728 - TagComponent: - Tag: Camera - TransformComponent: - Position: [-21.7406311, 9.70659542, 15] - Rotation: [0.999910355, -0.0133911213, 0, 0] - Scale: [1, 1, 1] - ScriptComponent: - ModuleName: Example.BasicController - StoredFields: - - Name: Speed - Type: 1 - Data: 12 - CameraComponent: - Camera: some camera data... - Primary: true - - Entity: 1289165777996378215 - TagComponent: - Tag: Cube - TransformComponent: - Position: [500, 0, 0] - Rotation: [1, 0, 0, 0] - Scale: [1200, 1, 5] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 0 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [600, 0.5] - Density: 1 - Friction: 2 - - Entity: 14057422478420564497 - TagComponent: - Tag: Player - TransformComponent: - Position: [-23.6932545, 1.59184527, -1.96369365e-06] - Rotation: [1, 0, 0, 0] - Scale: [1, 1, 1] - ScriptComponent: - ModuleName: Example.PlayerCube - StoredFields: - - Name: HorizontalForce - Type: 1 - Data: 0.5 - - Name: MaxSpeed - Type: 5 - Data: [7, 10] - - Name: JumpForce - Type: 1 - Data: 3 - MeshComponent: - AssetPath: assets/meshes/Sphere1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 29.2000008 - CircleCollider2DComponent: - Offset: [0, 0] - Radius: 0.5 - Density: 1 - Friction: 1 - - Entity: 1352995477042327524 - TagComponent: - Tag: Box - TransformComponent: - Position: [-29.6808929, 29.7597198, 0] - Rotation: [0.707106769, 0, 0, 0.707106769] - Scale: [58.4179001, 4.47999144, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 0 - Mass: 3 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [29.7000008, 2.24000001] - Density: 1 - Friction: 1 - - Entity: 15223077898852293773 - TagComponent: - Tag: Box - TransformComponent: - Position: [6.12674046, 45.5617676, 0] - Rotation: [0.977883637, 0, 0, -0.209149584] - Scale: [4.47999668, 4.47999668, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [2.24000001, 2.24000001] - Density: 1 - Friction: 1 - - Entity: 5421735812495444456 - TagComponent: - Tag: Box - TransformComponent: - Position: [-20.766222, 2.29431438, 0] - Rotation: [1, 0, 0, 0] - Scale: [3.00000024, 0.300000012, 1] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 0 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [1.5, 0.150000006] - Density: 1 - Friction: 1 - - Entity: 2842299641876190180 - TagComponent: - Tag: Box - TransformComponent: - Position: [-16.6143265, 4.39151001, 6.43359499e-09] - Rotation: [1, 0, 0, 0] - Scale: [3.00000024, 0.300000012, 1] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 0 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [1.5, 0.150000006] - Density: 1 - Friction: 1 diff --git a/Editor/assets/scenes/FPSDemo.scene b/Editor/assets/scenes/FPSDemo.scene index 3b6aa03..498445e 100644 --- a/Editor/assets/scenes/FPSDemo.scene +++ b/Editor/assets/scenes/FPSDemo.scene @@ -1,6 +1,6 @@ Scene: Scene Name Environment: - AssetPath: assets/env/pink_sunrise_4k.hdr + AssetPath: assets\env\birchwood_4k.hdr Light: Direction: [-0.787, -0.733, 1] Radiance: [1, 1, 1] @@ -51,6 +51,7 @@ Entities: Bounciness: 0.1 MeshColliderComponent: AssetPath: assets/meshes/Capsule.fbx + IsConvex: true IsTrigger: false - Entity: 11149966982516343187 TagComponent: @@ -79,6 +80,7 @@ Entities: Bounciness: 0.1 MeshColliderComponent: AssetPath: assets/meshes/Sphere1m.fbx + IsConvex: true IsTrigger: false - Entity: 10169503531257462571 TagComponent: @@ -176,6 +178,17 @@ Entities: Offset: [0, 0, 0] Size: [1, 1, 1] IsTrigger: false + - Entity: 2025484417758554619 + TagComponent: + Tag: Sky Light + TransformComponent: + Position: [0, 0, 0] + Rotation: [0, 0, 0] + Scale: [1, 1, 1] + SkyLightComponent: + EnvironmentAssetPath: assets\env\birchwood_4k.hdr + Intensity: 1 + Angle: 0 PhysicsLayers: - Name: Box CollidesWith: diff --git a/Editor/assets/scenes/Physics2DTest.hsc b/Editor/assets/scenes/Physics2DTest.hsc deleted file mode 100644 index f8b607a..0000000 --- a/Editor/assets/scenes/Physics2DTest.hsc +++ /dev/null @@ -1,174 +0,0 @@ -Scene: Scene Name -Environment: - AssetPath: assets/env/pink_sunrise_4k.hdr - Light: - Direction: [-0.787, -0.73299998, 1] - Radiance: [1, 1, 1] - Multiplier: 0.514999986 -Entities: - - Entity: 15861629587505754 - TagComponent: - Tag: Box - TransformComponent: - Position: [-18.2095661, 39.2518234, 0] - Rotation: [0.967056513, 0, 0, -0.254561812] - Scale: [4.47999525, 4.47999525, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [2.24000001, 2.24000001] - - Entity: 15223077898852293773 - TagComponent: - Tag: Box - TransformComponent: - Position: [5.37119865, 43.8762894, 0] - Rotation: [0.977883637, 0, 0, -0.209149718] - Scale: [4.47999668, 4.47999668, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [2.24000001, 2.24000001] - - Entity: 2157107598622182863 - TagComponent: - Tag: Box - TransformComponent: - Position: [-7.60411549, 44.1442184, 0] - Rotation: [0.989285827, 0, 0, 0.145991713] - Scale: [4.47999287, 4.47999287, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 0.5 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [2.24000001, 2.24000001] - - Entity: 8080964283681139153 - TagComponent: - Tag: Box - TransformComponent: - Position: [-0.739211679, 37.7653275, 0] - Rotation: [0.956475914, 0, 0, -0.291811317] - Scale: [5, 2, 2] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 0.25 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [2.5, 1] - - Entity: 1352995477042327524 - TagComponent: - Tag: Box - TransformComponent: - Position: [-8.32969856, 30.4078159, 0] - Rotation: [0.781595349, 0, 0, 0.623785794] - Scale: [14.000001, 4.47999334, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 3 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [7, 2.24000001] - - Entity: 935615878363259513 - TagComponent: - Tag: Box - TransformComponent: - Position: [6.88031197, 31.942337, 0] - Rotation: [0.986578286, 0, 0, 0.163288936] - Scale: [4.47999954, 4.47999954, 4.48000002] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [2.24000001, 2.24000001] - - Entity: 14057422478420564497 - TagComponent: - Tag: Player - TransformComponent: - Position: [0, 22.774044, 0] - Rotation: [0.942591429, 0, 0, -0.333948225] - Scale: [6.00000048, 6.00000048, 4.48000002] - ScriptComponent: - ModuleName: Example.PlayerCube - StoredFields: - - Name: HorizontalForce - Type: 1 - Data: 10 - - Name: VerticalForce - Type: 1 - Data: 10 - MeshComponent: - AssetPath: assets/meshes/Sphere1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 1 - CircleCollider2DComponent: - Offset: [0, 0] - Radius: 3 - - Entity: 1289165777996378215 - TagComponent: - Tag: Cube - TransformComponent: - Position: [0, 0, 0] - Rotation: [1, 0, 0, 0] - Scale: [50, 1, 50] - ScriptComponent: - ModuleName: Example.Sink - StoredFields: - - Name: SinkSpeed - Type: 1 - Data: 0 - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 0 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [25, 0.5] - - Entity: 5178862374589434728 - TagComponent: - Tag: Camera - TransformComponent: - Position: [0, 25, 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: 3948844418381294888 - TagComponent: - Tag: Box - TransformComponent: - Position: [-1.48028564, 49.5945244, -2.38418579e-07] - Rotation: [0.977883637, 0, 0, -0.209149733] - Scale: [1.99999976, 1.99999976, 2] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBody2DComponent: - BodyType: 1 - Mass: 1 - BoxCollider2DComponent: - Offset: [0, 0] - Size: [1, 1] \ No newline at end of file diff --git a/Editor/assets/scenes/Physics3DTest.scene b/Editor/assets/scenes/Physics3DTest.scene deleted file mode 100644 index 3d0e70e..0000000 --- a/Editor/assets/scenes/Physics3DTest.scene +++ /dev/null @@ -1,119 +0,0 @@ -Scene: Scene Name -Environment: - AssetPath: assets/env/pink_sunrise_4k.hdr - Light: - Direction: [-0.787, -0.73299998, 1] - Radiance: [1, 1, 1] - Multiplier: 0.514999986 -Entities: - - Entity: 10169503531257462571 - TagComponent: - Tag: Box - TransformComponent: - Position: [0, 1.5, 0] - Rotation: [1, 0, 0, 0] - Scale: [2, 2, 2] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBodyComponent: - BodyType: 1 - Mass: 0.5 - IsKinematic: false - Constraints: - LockPositionX: false - LockPositionY: false - LockPositionZ: false - LockRotationX: true - LockRotationY: true - LockRotationZ: true - PhysicsMaterialComponent: - StaticFriction: 1 - DynamicFriction: 1 - Bounciness: 0 - BoxColliderComponent: - Offset: [0, 0, 0] - Size: [2, 2, 2] - - Entity: 14057422478420564497 - TagComponent: - Tag: Player - TransformComponent: - Position: [-19.43363, 4.50874043, -1.96695328e-06] - Rotation: [1, 0, 0, 0] - Scale: [1, 1, 1] - ScriptComponent: - ModuleName: Example.PlayerSphere - StoredFields: - - Name: HorizontalForce - Type: 1 - Data: 10 - - Name: MaxSpeed - Type: 6 - Data: [10, 10, 10] - - Name: JumpForce - Type: 1 - Data: 200 - MeshComponent: - AssetPath: assets/meshes/Sphere1m.fbx - RigidBodyComponent: - BodyType: 1 - Mass: 1 - IsKinematic: false - Constraints: - LockPositionX: false - LockPositionY: false - LockPositionZ: false - LockRotationX: true - LockRotationY: true - LockRotationZ: true - PhysicsMaterialComponent: - StaticFriction: 1 - DynamicFriction: 1 - Bounciness: 0 - SphereColliderComponent: - Radius: 0.5 - - Entity: 5178862374589434728 - TagComponent: - Tag: Camera - TransformComponent: - Position: [-21.7406311, 9.70659542, 15] - Rotation: [0.999910355, -0.0133911213, 0, 0] - Scale: [1, 1, 1] - ScriptComponent: - ModuleName: Example.BasicController - StoredFields: - - Name: Speed - Type: 1 - Data: 12 - - Name: DistanceFromPlayer - Type: 1 - Data: 15 - CameraComponent: - Camera: some camera data... - Primary: true - - Entity: 18306113171518048249 - TagComponent: - Tag: Box - TransformComponent: - Position: [0, 0, 0] - Rotation: [1, 0, 0, 0] - Scale: [50, 1, 50] - MeshComponent: - AssetPath: assets/meshes/Cube1m.fbx - RigidBodyComponent: - BodyType: 0 - Mass: 1 - IsKinematic: false - Constraints: - LockPositionX: false - LockPositionY: false - LockPositionZ: false - LockRotationX: true - LockRotationY: true - LockRotationZ: true - PhysicsMaterialComponent: - StaticFriction: 1 - DynamicFriction: 1 - Bounciness: 0 - BoxColliderComponent: - Offset: [0, 0, 0] - Size: [50, 1, 50] diff --git a/Editor/assets/scenes/PinkSunrise.hsc b/Editor/assets/scenes/PinkSunrise.hsc deleted file mode 100644 index c720bf3..0000000 --- a/Editor/assets/scenes/PinkSunrise.hsc +++ /dev/null @@ -1,66 +0,0 @@ -Scene: Scene Name -Environment: - AssetPath: assets/env/birchwood_4k.hdr - Light: - Direction: [-0.5, -0.5, 1] - Radiance: [1, 1, 1] - Multiplier: 1 -Entities: - - Entity: 1289165777996378215 - TagComponent: - Tag: Sphere - TransformComponent: - Position: [0, 21.9805069, -1.64006281] - Rotation: [1, 0, 0, 0] - Scale: [0.100000024, 0.100000024, 0.100000024] - ScriptComponent: - ModuleName: Example.Sink - StoredFields: - - Name: SinkSpeed - Type: 1 - Data: 5 - MeshComponent: - AssetPath: assets/meshes/Sphere1m.fbx - - Entity: 5178862374589434728 - TagComponent: - Tag: Camera - TransformComponent: - Position: [0, 14.75, 79.75] - Rotation: [0.995602965, -0.0936739072, 0, 0] - Scale: [1, 0.999999821, 0.999999821] - ScriptComponent: - ModuleName: Example.BasicController - StoredFields: - - Name: Speed - Type: 1 - Data: 12 - CameraComponent: - Camera: some camera data... - Primary: true - - Entity: 9095450049242347594 - TagComponent: - Tag: Test Entity - TransformComponent: - Position: [0.248109579, -1.90734863e-06, -0.268640995] - Rotation: [1, 0, 0, 0] - Scale: [1, 1, 1] - ScriptComponent: - ModuleName: Example.Script - StoredFields: - - Name: VerticalSpeed - Type: 1 - Data: 0 - - Name: SinkRate - Type: 1 - Data: 0 - - Name: Speed - Type: 1 - Data: 1 - - Name: Rotation - Type: 1 - Data: 0 - - Name: Velocity - Type: 6 - Data: [0, 0, 0] - MeshComponent: - AssetPath: assets/meshes/TestScene.fbx \ No newline at end of file diff --git a/Editor/assets/shaders/BloomBlend.glsl b/Editor/assets/shaders/BloomBlend.glsl new file mode 100644 index 0000000..6b838b7 --- /dev/null +++ b/Editor/assets/shaders/BloomBlend.glsl @@ -0,0 +1,63 @@ +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec2 a_TexCoord; + +out vec2 v_TexCoord; + +void main() +{ + vec4 position = vec4(a_Position.xy, 0.0, 1.0); + v_TexCoord = a_TexCoord; + gl_Position = position; +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 o_Color; + +in vec2 v_TexCoord; + +uniform sampler2D u_SceneTexture; +uniform sampler2D u_BloomTexture; + +uniform float u_Exposure; +uniform bool u_EnableBloom; + +void main() +{ +#if 1 + const float gamma = 2.2; + const float pureWhite = 1.0; + + // Tonemapping + vec3 color = texture(u_SceneTexture, v_TexCoord).rgb; + if (u_EnableBloom) + { + vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb; + color += bloomColor; + } + + // Reinhard tonemapping + float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722)); + float mappedLuminance = (luminance * (1.0 + luminance / (pureWhite * pureWhite))) / (1.0 + luminance); + + // Scale color by ratio of average luminances. + vec3 mappedColor = (mappedLuminance / luminance) * color* u_Exposure; + + // Gamma correction. + o_Color = vec4(color, 1.0); +#else + const float gamma = 2.2; + vec3 hdrColor = texture(u_SceneTexture, v_TexCoord).rgb; + vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb; + hdrColor += bloomColor; // additive blending + // tone mapping + vec3 result = vec3(1.0) - exp(-hdrColor * u_Exposure); + // also gamma correct while we're at it + result = pow(result, vec3(1.0 / gamma)); + o_Color = vec4(result, 1.0); +#endif +} diff --git a/Editor/assets/shaders/BloomBlur.glsl b/Editor/assets/shaders/BloomBlur.glsl new file mode 100644 index 0000000..03bc805 --- /dev/null +++ b/Editor/assets/shaders/BloomBlur.glsl @@ -0,0 +1,79 @@ +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec2 a_TexCoord; + +out vec2 v_TexCoord; + +void main() +{ + vec4 position = vec4(a_Position.xy, 0.0, 1.0); + v_TexCoord = a_TexCoord; + gl_Position = position; +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 o_Color; + +in vec2 v_TexCoord; + +uniform sampler2D u_Texture; +uniform bool u_Horizontal; + +void main() +{ +#if 1 + // From learnopengl.com + float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); + + vec2 tex_offset = 1.0 / textureSize(u_Texture, 0); // gets size of single texel + vec3 result = texture(u_Texture, v_TexCoord).rgb * weight[0]; // current fragment's contribution + if (u_Horizontal) + { + for(int i = 1; i < 5; ++i) + { + result += texture(u_Texture, v_TexCoord + vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; + result += texture(u_Texture, v_TexCoord - vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; + } + } + else + { + for(int i = 1; i < 5; ++i) + { + result += texture(u_Texture, v_TexCoord + vec2(0.0, tex_offset.y * i)).rgb * weight[i]; + result += texture(u_Texture, v_TexCoord - vec2(0.0, tex_offset.y * i)).rgb * weight[i]; + } + } + o_Color = vec4(result, 1.0); +#else + // From https://www.shadertoy.com/view/Xltfzj + + float Pi = 6.28318530718; // Pi*2 + + // GAUSSIAN BLUR SETTINGS {{{ + float Directions =32.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float Quality = 6.0; // BLUR QUALITY (Default 4.0 - More is better but slower) + float Size = 16.0; // BLUR SIZE (Radius) + // GAUSSIAN BLUR SETTINGS }}} + + vec2 Radius = Size/textureSize(u_Texture, 0); + + vec3 result = texture(u_Texture, v_TexCoord).rgb; + vec2 uv = v_TexCoord; + // Blur calculations + for( float d=0.0; d> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), RadicalInverse_VdC(i)); + return vec2(float(i)/float(N), RadicalInverse_VdC(i)); } vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) @@ -244,11 +283,11 @@ vec3 PrefilterEnvMap(float Roughness, vec3 R) vec3 RotateVectorAboutY(float angle, vec3 vec) { - angle = radians(angle); - mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), - vec3(0.0,1.0,0.0), - vec3(-sin(angle),0.0,cos(angle))}; - return rotationMatrix * vec; + angle = radians(angle); + mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), + vec3(0.0,1.0,0.0), + vec3(-sin(angle),0.0,cos(angle))}; + return rotationMatrix * vec; } vec3 Lighting(vec3 F0) @@ -256,8 +295,8 @@ vec3 Lighting(vec3 F0) vec3 result = vec3(0.0); for(int i = 0; i < LightCount; i++) { - vec3 Li = -lights.Direction; - vec3 Lradiance = lights.Radiance * lights.Multiplier; + vec3 Li = u_DirectionalLights.Direction; + vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier; vec3 Lh = normalize(Li + m_Params.View); // Calculate angles between surface normal and various light vectors. @@ -298,13 +337,188 @@ vec3 IBL(vec3 F0, vec3 Lr) return kd * diffuseIBL + specularIBL; } +///////////////////////////////////////////// +// PCSS +///////////////////////////////////////////// + +uint CascadeIndex = 0; +float ShadowFade = 1.0; + +float GetShadowBias() +{ + const float MINIMUM_SHADOW_BIAS = 0.002; + float bias = max(MINIMUM_SHADOW_BIAS * (1.0 - dot(m_Params.Normal, u_DirectionalLights.Direction)), MINIMUM_SHADOW_BIAS); + return bias; +} + +float HardShadows_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords) +{ + float bias = GetShadowBias(); + float z = texture(shadowMap, shadowCoords.xy).x; + return 1.0 - step(z + bias, shadowCoords.z) * ShadowFade; +} + +// Penumbra + +// this search area estimation comes from the following article: +// http://developer.download.nvidia.com/whitepapers/2008/PCSS_DirectionalLight_Integration.pdf +float SearchWidth(float uvLightSize, float receiverDistance) +{ + const float NEAR = 0.1; + return uvLightSize * (receiverDistance - NEAR) / u_CameraPosition.z; +} + +float u_light_zNear = 0.0; // 0.01 gives artifacts? maybe because of ortho proj? +float u_light_zFar = 10000.0; +vec2 u_lightRadiusUV = vec2(0.05); + +vec2 searchRegionRadiusUV(float zWorld) +{ + return u_lightRadiusUV * (zWorld - u_light_zNear) / zWorld; +} + +const vec2 PoissonDistribution[64] = vec2[]( +vec2(-0.884081, 0.124488), +vec2(-0.714377, 0.027940), +vec2(-0.747945, 0.227922), +vec2(-0.939609, 0.243634), +vec2(-0.985465, 0.045534), +vec2(-0.861367, -0.136222), +vec2(-0.881934, 0.396908), +vec2(-0.466938, 0.014526), +vec2(-0.558207, 0.212662), +vec2(-0.578447, -0.095822), +vec2(-0.740266, -0.095631), +vec2(-0.751681, 0.472604), +vec2(-0.553147, -0.243177), +vec2(-0.674762, -0.330730), +vec2(-0.402765, -0.122087), +vec2(-0.319776, -0.312166), +vec2(-0.413923, -0.439757), +vec2(-0.979153, -0.201245), +vec2(-0.865579, -0.288695), +vec2(-0.243704, -0.186378), +vec2(-0.294920, -0.055748), +vec2(-0.604452, -0.544251), +vec2(-0.418056, -0.587679), +vec2(-0.549156, -0.415877), +vec2(-0.238080, -0.611761), +vec2(-0.267004, -0.459702), +vec2(-0.100006, -0.229116), +vec2(-0.101928, -0.380382), +vec2(-0.681467, -0.700773), +vec2(-0.763488, -0.543386), +vec2(-0.549030, -0.750749), +vec2(-0.809045, -0.408738), +vec2(-0.388134, -0.773448), +vec2(-0.429392, -0.894892), +vec2(-0.131597, 0.065058), +vec2(-0.275002, 0.102922), +vec2(-0.106117, -0.068327), +vec2(-0.294586, -0.891515), +vec2(-0.629418, 0.379387), +vec2(-0.407257, 0.339748), +vec2(0.071650, -0.384284), +vec2(0.022018, -0.263793), +vec2(0.003879, -0.136073), +vec2(-0.137533, -0.767844), +vec2(-0.050874, -0.906068), +vec2(0.114133, -0.070053), +vec2(0.163314, -0.217231), +vec2(-0.100262, -0.587992), +vec2(-0.004942, 0.125368), +vec2(0.035302, -0.619310), +vec2(0.195646, -0.459022), +vec2(0.303969, -0.346362), +vec2(-0.678118, 0.685099), +vec2(-0.628418, 0.507978), +vec2(-0.508473, 0.458753), +vec2(0.032134, -0.782030), +vec2(0.122595, 0.280353), +vec2(-0.043643, 0.312119), +vec2(0.132993, 0.085170), +vec2(-0.192106, 0.285848), +vec2(0.183621, -0.713242), +vec2(0.265220, -0.596716), +vec2(-0.009628, -0.483058), +vec2(-0.018516, 0.435703) +); + +vec2 SamplePoisson(int index) +{ + return PoissonDistribution[index % 64]; +} + +float FindBlockerDistance_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize) +{ + float bias = GetShadowBias(); + + int numBlockerSearchSamples = 64; + int blockers = 0; + float avgBlockerDistance = 0; + + float zEye = -(u_LightView * vec4(vs_Input.WorldPosition, 1.0)).z; + vec2 searchWidth = searchRegionRadiusUV(zEye); + for (int i = 0; i < numBlockerSearchSamples; i++) + { + float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * searchWidth).r; + if (z < (shadowCoords.z - bias)) + { + blockers++; + avgBlockerDistance += z; + } + } + + if (blockers > 0) + return avgBlockerDistance / float(blockers); + + return -1; +} + +float PenumbraWidth(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize) +{ + float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize); + if (blockerDistance == -1) + return -1; + + return (shadowCoords.z - blockerDistance) / blockerDistance; +} + +float PCF_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvRadius) +{ + float bias = GetShadowBias(); + int numPCFSamples = 64; + float sum = 0; + for (int i = 0; i < numPCFSamples; i++) + { + float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * uvRadius).r; + sum += (z < (shadowCoords.z - bias)) ? 1 : 0; + } + return sum / numPCFSamples; +} + +float PCSS_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize) +{ + float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize); + if (blockerDistance == -1) + return 1; + + float penumbraWidth = (shadowCoords.z - blockerDistance) / blockerDistance; + + float NEAR = 0.01; // Should this value be tweakable? + float uvRadius = penumbraWidth * uvLightSize * NEAR / shadowCoords.z; + return 1.0 - PCF_DirectionalLight(shadowMap, shadowCoords, uvRadius) * ShadowFade; +} + +///////////////////////////////////////////// + void main() { // Standard PBR inputs - m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; + m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness; m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness; - m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight + m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight // Normals (either from vertex or map) m_Params.Normal = normalize(vs_Input.Normal); @@ -316,15 +530,109 @@ void main() m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition); m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0); - + // Specular reflection vector vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View; // Fresnel reflectance, metals use albedo vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); - vec3 lightContribution = Lighting(F0); - vec3 iblContribution = IBL(F0, Lr); + const uint SHADOW_MAP_CASCADE_COUNT = 4; + for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; i++) + { + if(vs_Input.ViewPosition.z < u_CascadeSplits[i]) + CascadeIndex = i + 1; + } + + float shadowDistance = u_MaxShadowDistance;//u_CascadeSplits[3]; + float transitionDistance = u_ShadowFade; + float distance = length(vs_Input.ViewPosition); + ShadowFade = distance - (shadowDistance - transitionDistance); + ShadowFade /= transitionDistance; + ShadowFade = clamp(1.0 - ShadowFade, 0.0, 1.0); + + bool fadeCascades = u_CascadeFading; + float shadowAmount = 1.0; + if (fadeCascades) + { + float cascadeTransitionFade = u_CascadeTransitionFade; + + float c0 = smoothstep(u_CascadeSplits[0] + cascadeTransitionFade * 0.5f, u_CascadeSplits[0] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z); + float c1 = smoothstep(u_CascadeSplits[1] + cascadeTransitionFade * 0.5f, u_CascadeSplits[1] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z); + float c2 = smoothstep(u_CascadeSplits[2] + cascadeTransitionFade * 0.5f, u_CascadeSplits[2] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z); + if (c0 > 0.0 && c0 < 1.0) + { + // Sample 0 & 1 + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[0].xyz / vs_Input.ShadowMapCoords[0].w); + float shadowAmount0 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords); + shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w); + float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords); + + shadowAmount = mix(shadowAmount0, shadowAmount1, c0); + } + else if (c1 > 0.0 && c1 < 1.0) + { + // Sample 1 & 2 + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w); + float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords); + shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w); + float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords); + + shadowAmount = mix(shadowAmount1, shadowAmount2, c1); + } + else if (c2 > 0.0 && c2 < 1.0) + { + // Sample 2 & 3 + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w); + float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords); + shadowMapCoords = (vs_Input.ShadowMapCoords[3].xyz / vs_Input.ShadowMapCoords[3].w); + float shadowAmount3 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords); + + shadowAmount = mix(shadowAmount2, shadowAmount3, c2); + } + else + { + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w); + shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords); + } + } + else + { + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w); + shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords); + } + + float NdotL = dot(m_Params.Normal, u_DirectionalLights.Direction); + NdotL = smoothstep(0.0, 0.4, NdotL + 0.2); + shadowAmount *= (NdotL * 1.0); + + vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution; + vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0f ? (Lighting(F0) * shadowAmount) : vec3(0.0f); color = vec4(lightContribution + iblContribution, 1.0); + + // Bloom + float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + o_BloomColor = vec4(0.0, 0.0, 0.0, 1.0); + if (brightness > u_BloomThreshold) + o_BloomColor = color; + + if (u_ShowCascades) + { + switch(CascadeIndex) + { + case 0: + color.rgb *= vec3(1.0f, 0.25f, 0.25f); + break; + case 1: + color.rgb *= vec3(0.25f, 1.0f, 0.25f); + break; + case 2: + color.rgb *= vec3(0.25f, 0.25f, 1.0f); + break; + case 3: + color.rgb *= vec3(1.0f, 1.0f, 0.25f); + break; + } + } } diff --git a/Editor/assets/shaders/PBRShader_Static.glsl b/Editor/assets/shaders/PBRShader_Static.glsl index 8695a84..7434ea0 100644 --- a/Editor/assets/shaders/PBRShader_Static.glsl +++ b/Editor/assets/shaders/PBRShader_Static.glsl @@ -1,8 +1,8 @@ // ----------------------------- -// -- From Hazel Engine PBR shader -- +// -- 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. +// Currently heavily updated. // // References upon which this is based: // - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf) @@ -19,27 +19,41 @@ layout(location = 3) in vec3 a_Binormal; layout(location = 4) in vec2 a_TexCoord; uniform mat4 u_ViewProjectionMatrix; +uniform mat4 u_ViewMatrix; uniform mat4 u_Transform; +uniform mat4 u_LightMatrixCascade0; +uniform mat4 u_LightMatrixCascade1; +uniform mat4 u_LightMatrixCascade2; +uniform mat4 u_LightMatrixCascade3; + out VertexOutput { vec3 WorldPosition; - vec3 Normal; + vec3 Normal; vec2 TexCoord; mat3 WorldNormals; mat3 WorldTransform; vec3 Binormal; + vec4 ShadowMapCoords[4]; + vec3 ViewPosition; } vs_Output; void main() { vs_Output.WorldPosition = vec3(u_Transform * vec4(a_Position, 1.0)); - vs_Output.Normal = mat3(u_Transform) * a_Normal; + vs_Output.Normal = mat3(u_Transform) * a_Normal; vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal); vs_Output.WorldTransform = mat3(u_Transform); vs_Output.Binormal = a_Binormal; + vs_Output.ShadowMapCoords[0] = u_LightMatrixCascade0 * vec4(vs_Output.WorldPosition, 1.0); + vs_Output.ShadowMapCoords[1] = u_LightMatrixCascade1 * vec4(vs_Output.WorldPosition, 1.0); + vs_Output.ShadowMapCoords[2] = u_LightMatrixCascade2 * vec4(vs_Output.WorldPosition, 1.0); + vs_Output.ShadowMapCoords[3] = u_LightMatrixCascade3 * vec4(vs_Output.WorldPosition, 1.0); + vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0)); + gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0); } @@ -54,7 +68,8 @@ const int LightCount = 1; // Constant normal incidence Fresnel factor for all dielectrics. const vec3 Fdielectric = vec3(0.04); -struct Light { +struct DirectionalLight +{ vec3 Direction; vec3 Radiance; float Multiplier; @@ -63,16 +78,19 @@ struct Light { in VertexOutput { vec3 WorldPosition; - vec3 Normal; + vec3 Normal; vec2 TexCoord; mat3 WorldNormals; mat3 WorldTransform; vec3 Binormal; + vec4 ShadowMapCoords[4]; + vec3 ViewPosition; } vs_Input; layout(location = 0) out vec4 color; +layout(location = 1) out vec4 o_BloomColor; -uniform Light lights; +uniform DirectionalLight u_DirectionalLights; uniform vec3 u_CameraPosition; // PBR texture inputs @@ -88,6 +106,25 @@ uniform samplerCube u_EnvIrradianceTex; // BRDF LUT uniform sampler2D u_BRDFLUTTexture; +// PCSS +uniform sampler2D u_ShadowMapTexture[4]; +uniform mat4 u_LightView; +uniform bool u_ShowCascades; +uniform bool u_SoftShadows; +uniform float u_LightSize; +uniform float u_MaxShadowDistance; +uniform float u_ShadowFade; +uniform bool u_CascadeFading; +uniform float u_CascadeTransitionFade; + +uniform vec4 u_CascadeSplits; + +uniform float u_IBLContribution; + +uniform float u_BloomThreshold; + +//////////////////////////////////////// + uniform vec3 u_AlbedoColor; uniform float u_Metalness; uniform float u_Roughness; @@ -95,7 +132,6 @@ uniform float u_Roughness; uniform float u_EnvMapRotation; // Toggles -uniform float u_RadiancePrefilter; uniform float u_AlbedoTexToggle; uniform float u_NormalTexToggle; uniform float u_MetalnessTexToggle; @@ -141,23 +177,23 @@ float gaSchlickGGX(float cosLi, float NdotV, float roughness) float GeometrySchlickGGX(float NdotV, float roughness) { - float r = (roughness + 1.0); - float k = (r*r) / 8.0; + float r = (roughness + 1.0); + float k = (r*r) / 8.0; - float nom = NdotV; - float denom = NdotV * (1.0 - k) + k; + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; - return nom / denom; + return nom / denom; } float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { - float NdotV = max(dot(N, V), 0.0); - float NdotL = max(dot(N, L), 0.0); - float ggx2 = GeometrySchlickGGX(NdotV, roughness); - float ggx1 = GeometrySchlickGGX(NdotL, roughness); + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); - return ggx1 * ggx2; + return ggx1 * ggx2; } // Shlick's approximation of the Fresnel factor. @@ -168,26 +204,26 @@ vec3 fresnelSchlick(vec3 F0, float cosTheta) vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) { - return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); -} + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} // --------------------------------------------------------------------------------------------------- // The following code (from Unreal Engine 4's paper) shows how to filter the environment map // for different roughnesses. This is mean to be computed offline and stored in cube map mips, // so turning this on online will cause poor performance -float RadicalInverse_VdC(uint bits) +float RadicalInverse_VdC(uint bits) { - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), RadicalInverse_VdC(i)); + return vec2(float(i)/float(N), RadicalInverse_VdC(i)); } vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) @@ -234,11 +270,11 @@ vec3 PrefilterEnvMap(float Roughness, vec3 R) vec3 RotateVectorAboutY(float angle, vec3 vec) { - angle = radians(angle); - mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), - vec3(0.0,1.0,0.0), - vec3(-sin(angle),0.0,cos(angle))}; - return rotationMatrix * vec; + angle = radians(angle); + mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), + vec3(0.0,1.0,0.0), + vec3(-sin(angle),0.0,cos(angle))}; + return rotationMatrix * vec; } vec3 Lighting(vec3 F0) @@ -246,8 +282,8 @@ vec3 Lighting(vec3 F0) vec3 result = vec3(0.0); for(int i = 0; i < LightCount; i++) { - vec3 Li = -lights.Direction; - vec3 Lradiance = lights.Radiance * lights.Multiplier; + vec3 Li = u_DirectionalLights.Direction; + vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier; vec3 Lh = normalize(Li + m_Params.View); // Calculate angles between surface normal and various light vectors. @@ -273,7 +309,6 @@ vec3 IBL(vec3 F0, vec3 Lr) { vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb; vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness); -// vec3 F = fresnelSchlickR(F0, m_Params.NdotV); vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); vec3 diffuseIBL = m_Params.Albedo * irradiance; @@ -289,13 +324,188 @@ vec3 IBL(vec3 F0, vec3 Lr) return kd * diffuseIBL + specularIBL; } +///////////////////////////////////////////// +// PCSS +///////////////////////////////////////////// + +uint CascadeIndex = 0; +float ShadowFade = 1.0; + +float GetShadowBias() +{ + const float MINIMUM_SHADOW_BIAS = 0.002; + float bias = max(MINIMUM_SHADOW_BIAS * (1.0 - dot(m_Params.Normal, u_DirectionalLights.Direction)), MINIMUM_SHADOW_BIAS); + return bias; +} + +float HardShadows_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords) +{ + float bias = GetShadowBias(); + float z = texture(shadowMap, shadowCoords.xy).x; + return 1.0 - step(z + bias, shadowCoords.z) * ShadowFade; +} + +// Penumbra + +// this search area estimation comes from the following article: +// http://developer.download.nvidia.com/whitepapers/2008/PCSS_DirectionalLight_Integration.pdf +float SearchWidth(float uvLightSize, float receiverDistance) +{ + const float NEAR = 0.1; + return uvLightSize * (receiverDistance - NEAR) / u_CameraPosition.z; +} + +float u_light_zNear = 0.0; // 0.01 gives artifacts? maybe because of ortho proj? +float u_light_zFar = 10000.0; +vec2 u_lightRadiusUV = vec2(0.05); + +vec2 searchRegionRadiusUV(float zWorld) +{ + return u_lightRadiusUV * (zWorld - u_light_zNear) / zWorld; +} + +const vec2 PoissonDistribution[64] = vec2[]( +vec2(-0.884081, 0.124488), +vec2(-0.714377, 0.027940), +vec2(-0.747945, 0.227922), +vec2(-0.939609, 0.243634), +vec2(-0.985465, 0.045534), +vec2(-0.861367, -0.136222), +vec2(-0.881934, 0.396908), +vec2(-0.466938, 0.014526), +vec2(-0.558207, 0.212662), +vec2(-0.578447, -0.095822), +vec2(-0.740266, -0.095631), +vec2(-0.751681, 0.472604), +vec2(-0.553147, -0.243177), +vec2(-0.674762, -0.330730), +vec2(-0.402765, -0.122087), +vec2(-0.319776, -0.312166), +vec2(-0.413923, -0.439757), +vec2(-0.979153, -0.201245), +vec2(-0.865579, -0.288695), +vec2(-0.243704, -0.186378), +vec2(-0.294920, -0.055748), +vec2(-0.604452, -0.544251), +vec2(-0.418056, -0.587679), +vec2(-0.549156, -0.415877), +vec2(-0.238080, -0.611761), +vec2(-0.267004, -0.459702), +vec2(-0.100006, -0.229116), +vec2(-0.101928, -0.380382), +vec2(-0.681467, -0.700773), +vec2(-0.763488, -0.543386), +vec2(-0.549030, -0.750749), +vec2(-0.809045, -0.408738), +vec2(-0.388134, -0.773448), +vec2(-0.429392, -0.894892), +vec2(-0.131597, 0.065058), +vec2(-0.275002, 0.102922), +vec2(-0.106117, -0.068327), +vec2(-0.294586, -0.891515), +vec2(-0.629418, 0.379387), +vec2(-0.407257, 0.339748), +vec2(0.071650, -0.384284), +vec2(0.022018, -0.263793), +vec2(0.003879, -0.136073), +vec2(-0.137533, -0.767844), +vec2(-0.050874, -0.906068), +vec2(0.114133, -0.070053), +vec2(0.163314, -0.217231), +vec2(-0.100262, -0.587992), +vec2(-0.004942, 0.125368), +vec2(0.035302, -0.619310), +vec2(0.195646, -0.459022), +vec2(0.303969, -0.346362), +vec2(-0.678118, 0.685099), +vec2(-0.628418, 0.507978), +vec2(-0.508473, 0.458753), +vec2(0.032134, -0.782030), +vec2(0.122595, 0.280353), +vec2(-0.043643, 0.312119), +vec2(0.132993, 0.085170), +vec2(-0.192106, 0.285848), +vec2(0.183621, -0.713242), +vec2(0.265220, -0.596716), +vec2(-0.009628, -0.483058), +vec2(-0.018516, 0.435703) +); + +vec2 SamplePoisson(int index) +{ + return PoissonDistribution[index % 64]; +} + +float FindBlockerDistance_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize) +{ + float bias = GetShadowBias(); + + int numBlockerSearchSamples = 64; + int blockers = 0; + float avgBlockerDistance = 0; + + float zEye = -(u_LightView * vec4(vs_Input.WorldPosition, 1.0)).z; + vec2 searchWidth = searchRegionRadiusUV(zEye); + for (int i = 0; i < numBlockerSearchSamples; i++) + { + float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * searchWidth).r; + if (z < (shadowCoords.z - bias)) + { + blockers++; + avgBlockerDistance += z; + } + } + + if (blockers > 0) + return avgBlockerDistance / float(blockers); + + return -1; +} + +float PenumbraWidth(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize) +{ + float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize); + if (blockerDistance == -1) + return -1; + + return (shadowCoords.z - blockerDistance) / blockerDistance; +} + +float PCF_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvRadius) +{ + float bias = GetShadowBias(); + int numPCFSamples = 64; + float sum = 0; + for (int i = 0; i < numPCFSamples; i++) + { + float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * uvRadius).r; + sum += (z < (shadowCoords.z - bias)) ? 1 : 0; + } + return sum / numPCFSamples; +} + +float PCSS_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize) +{ + float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize); + if (blockerDistance == -1) + return 1; + + float penumbraWidth = (shadowCoords.z - blockerDistance) / blockerDistance; + + float NEAR = 0.01; // Should this value be tweakable? + float uvRadius = penumbraWidth * uvLightSize * NEAR / shadowCoords.z; + return 1.0 - PCF_DirectionalLight(shadowMap, shadowCoords, uvRadius) * ShadowFade; +} + +///////////////////////////////////////////// + void main() { // Standard PBR inputs - m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; + m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness; m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness; - m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight + m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight // Normals (either from vertex or map) m_Params.Normal = normalize(vs_Input.Normal); @@ -307,16 +517,109 @@ void main() m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition); m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0); - + // Specular reflection vector vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View; // Fresnel reflectance, metals use albedo vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); - vec3 lightContribution = Lighting(F0); - vec3 iblContribution = IBL(F0, Lr); + const uint SHADOW_MAP_CASCADE_COUNT = 4; + for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; i++) + { + if(vs_Input.ViewPosition.z < u_CascadeSplits[i]) + CascadeIndex = i + 1; + } + + float shadowDistance = u_MaxShadowDistance;//u_CascadeSplits[3]; + float transitionDistance = u_ShadowFade; + float distance = length(vs_Input.ViewPosition); + ShadowFade = distance - (shadowDistance - transitionDistance); + ShadowFade /= transitionDistance; + ShadowFade = clamp(1.0 - ShadowFade, 0.0, 1.0); + + bool fadeCascades = u_CascadeFading; + float shadowAmount = 1.0; + if (fadeCascades) + { + float cascadeTransitionFade = u_CascadeTransitionFade; + + float c0 = smoothstep(u_CascadeSplits[0] + cascadeTransitionFade * 0.5f, u_CascadeSplits[0] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z); + float c1 = smoothstep(u_CascadeSplits[1] + cascadeTransitionFade * 0.5f, u_CascadeSplits[1] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z); + float c2 = smoothstep(u_CascadeSplits[2] + cascadeTransitionFade * 0.5f, u_CascadeSplits[2] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z); + if (c0 > 0.0 && c0 < 1.0) + { + // Sample 0 & 1 + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[0].xyz / vs_Input.ShadowMapCoords[0].w); + float shadowAmount0 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords); + shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w); + float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords); + + shadowAmount = mix(shadowAmount0, shadowAmount1, c0); + } + else if (c1 > 0.0 && c1 < 1.0) + { + // Sample 1 & 2 + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w); + float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords); + shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w); + float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords); + + shadowAmount = mix(shadowAmount1, shadowAmount2, c1); + } + else if (c2 > 0.0 && c2 < 1.0) + { + // Sample 2 & 3 + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w); + float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords); + shadowMapCoords = (vs_Input.ShadowMapCoords[3].xyz / vs_Input.ShadowMapCoords[3].w); + float shadowAmount3 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords); + + shadowAmount = mix(shadowAmount2, shadowAmount3, c2); + } + else + { + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w); + shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords); + } + } + else + { + vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w); + shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords); + } + + float NdotL = dot(m_Params.Normal, u_DirectionalLights.Direction); + NdotL = smoothstep(0.0, 0.4, NdotL + 0.2); + shadowAmount *= (NdotL * 1.0); + + vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution; + vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0f ? (Lighting(F0) * shadowAmount) : vec3(0.0f); color = vec4(lightContribution + iblContribution, 1.0); -// color = vec4(iblContribution, 1.0); + + // Bloom + float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + o_BloomColor = vec4(0.0, 0.0, 0.0, 1.0); + if (brightness > u_BloomThreshold) + o_BloomColor = color; + + if (u_ShowCascades) + { + switch(CascadeIndex) + { + case 0: + color.rgb *= vec3(1.0f, 0.25f, 0.25f); + break; + case 1: + color.rgb *= vec3(0.25f, 1.0f, 0.25f); + break; + case 2: + color.rgb *= vec3(0.25f, 0.25f, 1.0f); + break; + case 3: + color.rgb *= vec3(1.0f, 1.0f, 0.25f); + break; + } + } } diff --git a/Editor/assets/shaders/SceneComposite.glsl b/Editor/assets/shaders/SceneComposite.glsl index a1d0e24..4fdbbd8 100644 --- a/Editor/assets/shaders/SceneComposite.glsl +++ b/Editor/assets/shaders/SceneComposite.glsl @@ -17,20 +17,34 @@ void main() #version 430 layout(location = 0) out vec4 o_Color; +layout(location = 1) out vec4 o_BloomTexture; in vec2 v_TexCoord; uniform sampler2DMS u_Texture; + uniform float u_Exposure; uniform int u_TextureSamples; -vec4 MultiSampleTexture(sampler2DMS tex, ivec2 texCoord, int samples) +uniform bool u_EnableBloom; +uniform float u_BloomThreshold; + +const float uFar = 1.0; + +vec4 SampleTexture(sampler2D tex, vec2 texCoord) { + return texture(tex, texCoord); +} + +vec4 MultiSampleTexture(sampler2DMS tex, vec2 tc) +{ + ivec2 texSize = textureSize(tex); + ivec2 texCoord = ivec2(tc * texSize); vec4 result = vec4(0.0); - for (int i = 0; i < samples; i++) + for (int i = 0; i < u_TextureSamples; i++) result += texelFetch(tex, texCoord, i); - result /= float(samples); + result /= float(u_TextureSamples); return result; } @@ -39,10 +53,18 @@ void main() const float gamma = 2.2; const float pureWhite = 1.0; - ivec2 texSize = textureSize(u_Texture); - ivec2 texCoord = ivec2(v_TexCoord * texSize); - vec4 msColor = MultiSampleTexture(u_Texture, texCoord, u_TextureSamples); - vec3 color = msColor.rgb * u_Exposure;//texture(u_Texture, v_TexCoord).rgb * u_Exposure; + // Tonemapping + vec4 msColor = MultiSampleTexture(u_Texture, v_TexCoord); + + vec3 color = msColor.rgb; + + if (u_EnableBloom) + { + vec3 bloomColor = MultiSampleTexture(u_Texture, v_TexCoord).rgb; + color += bloomColor; + } + + color *= u_Exposure; // Reinhard tonemapping operator. // see: "Photographic Tone Reproduction for Digital Images", eq. 4 @@ -54,4 +76,8 @@ void main() // Gamma correction. o_Color = vec4(pow(mappedColor, vec3(1.0 / gamma)), 1.0); -} \ No newline at end of file + + // Show over-exposed areas + // if (o_Color.r > 1.0 || o_Color.g > 1.0 || o_Color.b > 1.0) + // o_Color.rgb *= vec3(1.0, 0.25, 0.25); +} diff --git a/Editor/assets/shaders/ShadowMap.glsl b/Editor/assets/shaders/ShadowMap.glsl new file mode 100644 index 0000000..69352c4 --- /dev/null +++ b/Editor/assets/shaders/ShadowMap.glsl @@ -0,0 +1,23 @@ +// Shadow Map shader + +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; + +uniform mat4 u_ViewProjection; +uniform mat4 u_Transform; + +void main() +{ + gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0); +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 o_Color; + +void main() +{ +} diff --git a/Editor/assets/shaders/ShadowMap_Anim.glsl b/Editor/assets/shaders/ShadowMap_Anim.glsl new file mode 100644 index 0000000..0668c62 --- /dev/null +++ b/Editor/assets/shaders/ShadowMap_Anim.glsl @@ -0,0 +1,35 @@ +// Shadow Map shader + +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; + +layout(location = 5) in ivec4 a_BoneIndices; +layout(location = 6) in vec4 a_BoneWeights; + +uniform mat4 u_ViewProjection; +uniform mat4 u_Transform; + +const int MAX_BONES = 100; +uniform mat4 u_BoneTransforms[100]; + +void main() +{ + mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0]; + boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1]; + boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2]; + boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3]; + + vec4 localPosition = boneTransform * vec4(a_Position, 1.0); + gl_Position = u_ViewProjection * u_Transform * localPosition; +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 o_Color; + +void main() +{ +} diff --git a/Editor/assets/shaders/Skybox.glsl b/Editor/assets/shaders/Skybox.glsl index 9cad508..c6a005f 100644 --- a/Editor/assets/shaders/Skybox.glsl +++ b/Editor/assets/shaders/Skybox.glsl @@ -24,10 +24,13 @@ layout(location = 0) out vec4 finalColor; uniform samplerCube u_Texture; uniform float u_TextureLod; +uniform float u_SkyIntensity; in vec3 v_Position; void main() { - finalColor = textureLod(u_Texture, v_Position, u_TextureLod); + vec3 color = textureLod(u_Texture, v_Position, u_TextureLod).rgb * u_SkyIntensity; + finalColor = vec4(color, 1.0); + } \ No newline at end of file diff --git a/ExampleApp/Src/FPSPlayer.cs b/ExampleApp/Src/FPSPlayer.cs index 8d05e55..28c5b81 100644 --- a/ExampleApp/Src/FPSPlayer.cs +++ b/ExampleApp/Src/FPSPlayer.cs @@ -121,7 +121,7 @@ namespace FPSExample private void UpdateCameraTransform(){ Vec3 position = m_Transform.Position; - position.Y += 1.5f; + position.Y += m_Transform.Position.Y + 1.5f; m_CameraTransform.Position = position; } } diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index 64a3d6d..9868535 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -31,6 +31,8 @@ namespace Prism m_Window = std::unique_ptr(Window::Create(WindowProps{props.Name, props.Width, props.Height})); m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent)); + m_Window->SetVSync(true); + m_Window->Maximize(); m_ImGuiLayer = new ImGuiLayer("ImGui Layer"); PushOverlay(m_ImGuiLayer); diff --git a/Prism/src/Prism/Core/ImGui/ImGui.h b/Prism/src/Prism/Core/ImGui/ImGui.h new file mode 100644 index 0000000..efaf543 --- /dev/null +++ b/Prism/src/Prism/Core/ImGui/ImGui.h @@ -0,0 +1,273 @@ +// +// Created by sfd on 26-1-1. +// + +#ifndef IMGUI_H +#define IMGUI_H +#include +#include +#include +#include + +namespace Prism::UI { + + static int s_UIContextID = 0; + static uint32_t s_Counter = 0; + static char s_IDBuffer[16]; + + static void PushID() + { + ImGui::PushID(s_UIContextID++); + s_Counter = 0; + } + + static void PopID() + { + ImGui::PopID(); + s_UIContextID--; + } + + static void BeginPropertyGrid() + { + PushID(); + ImGui::Columns(2); + } + + static bool Property(const char* label, std::string& value, const bool error = false) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + char buffer[256]; + snprintf(buffer, sizeof(buffer), "%s", value.c_str()); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + + 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(); + + return modified; + } + + static void Property(const char* label, const char* value) + { + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + ImGui::InputText(s_IDBuffer, const_cast(value), 256, ImGuiInputTextFlags_ReadOnly); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + + static bool Property(const char* label, bool& value) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::Checkbox(s_IDBuffer, &value)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool Property(const char* label, int& value) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::DragInt(s_IDBuffer, &value)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool PropertySlider(const char* label, int& value, const int min, const int max) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::SliderInt(s_IDBuffer, &value, min, max)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool Property(const char* label, float& value, const float delta = 0.1f, const float min = 0.0f, const float max = 0.0f) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::DragFloat(s_IDBuffer, &value, delta, min, max)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool Property(const char* label, glm::vec2& value, const 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); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::DragFloat3(s_IDBuffer, glm::value_ptr(value), delta)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool PropertyColor(const char* label, glm::vec3& value) + { + bool modified = false; + + ImGui::Text(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + s_IDBuffer[0] = '#'; + s_IDBuffer[1] = '#'; + memset(s_IDBuffer + 2, 0, 14); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::ColorEdit3(s_IDBuffer, glm::value_ptr(value))) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static bool Property(const char* label, glm::vec3& value, const 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); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + 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, const 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); + snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); + if (ImGui::DragFloat4(s_IDBuffer, glm::value_ptr(value), delta)) + modified = true; + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + + return modified; + } + + static void EndPropertyGrid() + { + ImGui::Columns(1); + PopID(); + } + + static bool BeginTreeNode(const char* name, const bool defaultOpen = true) + { + ImGuiTreeNodeFlags treeNodeFlags = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_FramePadding; + if (defaultOpen) + treeNodeFlags |= ImGuiTreeNodeFlags_DefaultOpen; + + return ImGui::TreeNodeEx(name, treeNodeFlags); + } + + static void EndTreeNode() + { + ImGui::TreePop(); + } + +} + +#endif //IMGUI_H diff --git a/Prism/src/Prism/Core/Window.h b/Prism/src/Prism/Core/Window.h index 5830818..7c18486 100644 --- a/Prism/src/Prism/Core/Window.h +++ b/Prism/src/Prism/Core/Window.h @@ -45,6 +45,8 @@ namespace Prism virtual void SetVSync(bool enable) = 0; virtual bool const IsVSync() const = 0; + virtual void Maximize() = 0; + static Window* Create(const WindowProps& props); virtual const std::string& GetTitle() const = 0; diff --git a/Prism/src/Prism/Editor/EditorCamera.cpp b/Prism/src/Prism/Editor/EditorCamera.cpp index 0f1482e..e78f69a 100644 --- a/Prism/src/Prism/Editor/EditorCamera.cpp +++ b/Prism/src/Prism/Editor/EditorCamera.cpp @@ -44,13 +44,15 @@ namespace Prism const glm::vec2& mouse{ Input::GetMouseX(), Input::GetMouseY() }; const glm::vec2 delta = (mouse - m_InitialMousePosition) * 0.003f; m_InitialMousePosition = mouse; - - if (Input::IsMouseButtonPressed(MouseButton::Middle)) - MousePan(delta); - else if (Input::IsMouseButtonPressed(MouseButton::Left)) - MouseRotate(delta); - else if (Input::IsMouseButtonPressed(MouseButton::Right)) - MouseZoom(delta.y); + if (delta.x != 0.0f || delta.y != 0.0f) + { + if (Input::IsMouseButtonPressed(MouseButton::Middle)) + MousePan(delta); + else if (Input::IsMouseButtonPressed(MouseButton::Left)) + MouseRotate(delta); + else if (Input::IsMouseButtonPressed(MouseButton::Right)) + MouseZoom(delta.y); + } } UpdateCameraView(); @@ -77,6 +79,19 @@ namespace Prism return glm::rotate(GetOrientation(), glm::vec3(0.0f, 0.0f, -1.0f)); } + void EditorCamera::OnImGuiRender() + { + ImGui::Begin("Camera Info"); + UI::Property("yaw", m_Yaw); + UI::Property("pitch", m_Pitch); + UI::Property("focus", m_FocalPoint); + UI::Property("distance", m_Distance); + UI::Property("rotation", m_Rotation); + UI::Property("focus", m_FocalPoint); + UI::Property("position", m_Position); + ImGui::End(); + } + void EditorCamera::UpdateCameraView() { m_Position = CalculatePosition(); diff --git a/Prism/src/Prism/Editor/EditorCamera.h b/Prism/src/Prism/Editor/EditorCamera.h index c2e01c2..d75109a 100644 --- a/Prism/src/Prism/Editor/EditorCamera.h +++ b/Prism/src/Prism/Editor/EditorCamera.h @@ -40,12 +40,11 @@ namespace Prism 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; } + void OnImGuiRender(); public: inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; } private: diff --git a/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp b/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp index 3a6baa0..aa5af5d 100644 --- a/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp +++ b/Prism/src/Prism/Editor/PhysicsSettingsWindow.cpp @@ -6,6 +6,7 @@ #include "imgui.h" #include "imgui_internal.h" +#include "Prism/Core/ImGui/imgui.h" #include "Prism/Physics/Physics3D.h" #include "Prism/Physics/PhysicsLayer.h" diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp index 9b72134..8e61666 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp @@ -167,9 +167,34 @@ namespace Prism if (ImGui::BeginPopupContextWindow()) { - if (ImGui::MenuItem("Create Empty Entity")) + + if (ImGui::BeginMenu("Create")) { - m_Context->CreateEntity("Empty Entity"); + if (ImGui::MenuItem("Empty Entity")) + { + auto newEntity = m_Context->CreateEntity("Empty Entity"); + SetSelected(newEntity); + } + if (ImGui::MenuItem("Mesh")) + { + auto newEntity = m_Context->CreateEntity("Mesh"); + newEntity.AddComponent(); + SetSelected(newEntity); + } + ImGui::Separator(); + if (ImGui::MenuItem("Directional Light")) + { + auto newEntity = m_Context->CreateEntity("Directional Light"); + newEntity.AddComponent(); + SetSelected(newEntity); + } + if (ImGui::MenuItem("Sky Light")) + { + auto newEntity = m_Context->CreateEntity("Sky Light"); + newEntity.AddComponent(); + SetSelected(newEntity); + } + ImGui::EndMenu(); } ImGui::EndPopup(); } @@ -322,224 +347,6 @@ namespace Prism } - static int s_UIContextID = 0; - static uint32_t s_Counter = 0; - static char s_IDBuffer[16]; - - static void PushID() - { - ImGui::PushID(s_UIContextID++); - s_Counter = 0; - } - - static void PopID() - { - ImGui::PopID(); - s_UIContextID--; - } - - static void BeginPropertyGrid() - { - PushID(); - ImGui::Columns(2); - ImGui::SetColumnWidth(0, 100); - } - - static bool Property(const char* label, std::string& value, bool error = false) - { - bool modified = false; - - ImGui::Text(label); - ImGui::NextColumn(); - ImGui::PushItemWidth(-1); - - char buffer[256]; - snprintf(buffer, sizeof(buffer), "%s", value.c_str()); - - s_IDBuffer[0] = '#'; - s_IDBuffer[1] = '#'; - memset(s_IDBuffer + 2, 0, 14); - // itoa(s_Counter++, s_IDBuffer + 2, 16); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - 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(); - - return modified; - } - - static void Property(const char* label, const char* value) - { - 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - ImGui::InputText(s_IDBuffer, (char*)value, 256, ImGuiInputTextFlags_ReadOnly); - - ImGui::PopItemWidth(); - ImGui::NextColumn(); - } - - static bool Property(const char* label, bool& value) - { - 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - if (ImGui::Checkbox(s_IDBuffer, &value)) - modified = true; - - ImGui::PopItemWidth(); - ImGui::NextColumn(); - - return modified; - } - - static bool Property(const char* label, int& value) - { - 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - if (ImGui::DragInt(s_IDBuffer, &value)) - modified = true; - - ImGui::PopItemWidth(); - ImGui::NextColumn(); - - return modified; - } - - static bool Property(const char* label, float& value, float delta = 0.1f, float min = 0.0f, float max = 0.0f) - { - 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - if (ImGui::DragFloat(s_IDBuffer, &value, delta, min, max)) - modified = true; - - ImGui::PopItemWidth(); - ImGui::NextColumn(); - - return modified; - } - - static bool Property(const char* label, glm::vec2& 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - if (ImGui::DragFloat2(s_IDBuffer, glm::value_ptr(value), delta)) - modified = true; - - ImGui::PopItemWidth(); - ImGui::NextColumn(); - - 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - 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); - snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++); - - if (ImGui::DragFloat4(s_IDBuffer, glm::value_ptr(value), delta)) - modified = true; - - ImGui::PopItemWidth(); - ImGui::NextColumn(); - - return modified; - } - - static void EndPropertyGrid() - { - ImGui::Columns(1); - PopID(); - } template static void DrawComponent(const std::string& name, Entity entity, UIFunction uiFunction) @@ -622,6 +429,8 @@ namespace Prism { AddComponentPopup("Camera"); AddComponentPopup("Mesh"); + AddComponentPopup("Directional Light"); + AddComponentPopup("sky Light"); AddComponentPopup("Script"); AddComponentPopup("SpriteRenderer"); AddComponentPopup("RigidBody2D"); @@ -682,6 +491,7 @@ namespace Prism if (!file.empty()) meshComponent.Mesh = Ref::Create(file); } + ImGui::Columns(1); }); DrawComponent("Camera", entity, [](CameraComponent& cameraComponent) { @@ -704,20 +514,20 @@ namespace Prism ImGui::EndCombo(); } - BeginPropertyGrid(); + UI::BeginPropertyGrid(); // Perspective parameters if (cameraComponent.Camera.GetProjectionType() == SceneCamera::ProjectionType::Perspective) { float verticalFOV = cameraComponent.Camera.GetPerspectiveVerticalFOV(); - if (Property("Vertical FOV", verticalFOV)) + if (UI::Property("Vertical FOV", verticalFOV)) cameraComponent.Camera.SetPerspectiveVerticalFOV(verticalFOV); float nearClip = cameraComponent.Camera.GetPerspectiveNearClip(); - if (Property("Near Clip", nearClip)) + if (UI::Property("Near Clip", nearClip)) cameraComponent.Camera.SetPerspectiveNearClip(nearClip); ImGui::SameLine(); float farClip = cameraComponent.Camera.GetPerspectiveFarClip(); - if (Property("Far Clip", farClip)) + if (UI::Property("Far Clip", farClip)) cameraComponent.Camera.SetPerspectiveFarClip(farClip); } @@ -725,18 +535,57 @@ namespace Prism else if (cameraComponent.Camera.GetProjectionType() == SceneCamera::ProjectionType::Orthographic) { float orthoSize = cameraComponent.Camera.GetOrthographicSize(); - if (Property("Size", orthoSize)) + if (UI::Property("Size", orthoSize)) cameraComponent.Camera.SetOrthographicSize(orthoSize); float nearClip = cameraComponent.Camera.GetOrthographicNearClip(); - if (Property("Near Clip", nearClip)) + if (UI::Property("Near Clip", nearClip)) cameraComponent.Camera.SetOrthographicNearClip(nearClip); ImGui::SameLine(); float farClip = cameraComponent.Camera.GetOrthographicFarClip(); - if (Property("Far Clip", farClip)) + if (UI::Property("Far Clip", farClip)) cameraComponent.Camera.SetOrthographicFarClip(farClip); } - EndPropertyGrid(); + UI::EndPropertyGrid(); + }); + + DrawComponent("Directional Light", entity, [](DirectionalLightComponent& dlc) + { + UI::BeginPropertyGrid(); + UI::PropertyColor("Radiance", dlc.Radiance); + UI::Property("Intensity", dlc.Intensity); + UI::Property("Cast Shadows", dlc.CastShadows); + UI::Property("Soft Shadows", dlc.SoftShadows); + UI::Property("Source Size", dlc.LightSize); + UI::EndPropertyGrid(); + }); + + DrawComponent("Sky Light", entity, [](SkyLightComponent& slc) + { + 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 (!slc.SceneEnvironment.FilePath.empty()) + ImGui::InputText("##envfilepath", (char*)slc.SceneEnvironment.FilePath.c_str(), 256, ImGuiInputTextFlags_ReadOnly); + else + ImGui::InputText("##envfilepath", (char*)"Empty", 256, ImGuiInputTextFlags_ReadOnly); + ImGui::PopItemWidth(); + ImGui::NextColumn(); + if (ImGui::Button("...##openenv")) + { + std::string file = Application::Get().OpenFile("*.hdr"); + if (!file.empty()) + slc.SceneEnvironment = Environment::Load(file); + } + ImGui::Columns(1); + + UI::BeginPropertyGrid(); + UI::Property("Intensity", slc.Intensity, 0.01f, 0.0f, 5.0f); + UI::EndPropertyGrid(); }); DrawComponent("Sprite Renderer", entity, [](SpriteRendererComponent& mc) @@ -744,9 +593,9 @@ namespace Prism }); DrawComponent("Script", entity, [=](ScriptComponent& scriptComponent) mutable { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); const std::string oldName = scriptComponent.ModuleName; - if (Property("Module Name", scriptComponent.ModuleName, !ScriptEngine::ModuleExists(scriptComponent.ModuleName))) // TODO: no live edit + if (UI::Property("Module Name", scriptComponent.ModuleName, !ScriptEngine::ModuleExists(scriptComponent.ModuleName))) // TODO: no live edit { // Shutdown old script if (ScriptEngine::ModuleExists(oldName)) @@ -772,7 +621,7 @@ namespace Prism case FieldType::Int: { int value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); - if (Property(field.Name.c_str(), value)) + if (UI::Property(field.Name.c_str(), value)) { if (isRuntime) field.SetRuntimeValue(value); @@ -784,7 +633,7 @@ namespace Prism case FieldType::Float: { float value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); - if (Property(field.Name.c_str(), value, 0.2f)) + if (UI::Property(field.Name.c_str(), value, 0.2f)) { if (isRuntime) field.SetRuntimeValue(value); @@ -796,7 +645,7 @@ namespace Prism case FieldType::Vec2: { glm::vec2 value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); - if (Property(field.Name.c_str(), value, 0.2f)) + if (UI::Property(field.Name.c_str(), value, 0.2f)) { if (isRuntime) field.SetRuntimeValue(value); @@ -808,7 +657,7 @@ namespace Prism case FieldType::Vec3: { glm::vec3 value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); - if (Property(field.Name.c_str(), value, 0.2f)) + if (UI::Property(field.Name.c_str(), value, 0.2f)) { if (isRuntime) field.SetRuntimeValue(value); @@ -820,7 +669,7 @@ namespace Prism case FieldType::Vec4: { glm::vec4 value = isRuntime ? field.GetRuntimeValue() : field.GetStoredValue(); - if (Property(field.Name.c_str(), value, 0.2f)) + if (UI::Property(field.Name.c_str(), value, 0.2f)) { if (isRuntime) field.SetRuntimeValue(value); @@ -834,7 +683,7 @@ namespace Prism } } - EndPropertyGrid(); + UI::EndPropertyGrid(); #if TODO if (ImGui::Button("Run Script")) { @@ -866,34 +715,34 @@ namespace Prism if (rb2dComponent.BodyType == RigidBody2DComponent::Type::Dynamic) { - BeginPropertyGrid(); - Property("Fixed Rotation", rb2dComponent.FixedRotation); - EndPropertyGrid(); + UI::BeginPropertyGrid(); + UI::Property("Fixed Rotation", rb2dComponent.FixedRotation); + UI::EndPropertyGrid(); } }); DrawComponent("Box Collider 2D", entity, [](BoxCollider2DComponent& bc2dComponent) { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); - Property("Offset", bc2dComponent.Offset); - Property("Size", bc2dComponent.Size); - Property("Density", bc2dComponent.Density); - Property("Friction", bc2dComponent.Friction); + UI::Property("Offset", bc2dComponent.Offset); + UI::Property("Size", bc2dComponent.Size); + UI::Property("Density", bc2dComponent.Density); + UI::Property("Friction", bc2dComponent.Friction); - EndPropertyGrid(); + UI::EndPropertyGrid(); }); DrawComponent("Circle Collider 2D", entity, [](CircleCollider2DComponent& cc2dComponent) { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); - Property("Offset", cc2dComponent.Offset); - Property("Radius", cc2dComponent.Radius); - Property("Density", cc2dComponent.Density); - Property("Friction", cc2dComponent.Friction); + UI::Property("Offset", cc2dComponent.Offset); + UI::Property("Radius", cc2dComponent.Radius); + UI::Property("Density", cc2dComponent.Density); + UI::Property("Friction", cc2dComponent.Friction); - EndPropertyGrid(); + UI::EndPropertyGrid(); }); DrawComponent("Rigidbody", entity, [](RigidBodyComponent& rbc) @@ -948,21 +797,21 @@ namespace Prism if (rbc.BodyType == RigidBodyComponent::Type::Dynamic) { - BeginPropertyGrid(); - Property("Mass", rbc.Mass); - Property("Is Kinematic", rbc.IsKinematic); - EndPropertyGrid(); + UI::BeginPropertyGrid(); + UI::Property("Mass", rbc.Mass); + UI::Property("Is Kinematic", rbc.IsKinematic); + UI::EndPropertyGrid(); if (ImGui::TreeNode("RigidBodyConstraints", "Constraints")) { - BeginPropertyGrid(); - Property("Position: X", rbc.LockPositionX); - Property("Position: Y", rbc.LockPositionY); - Property("Position: Z", rbc.LockPositionZ); - Property("Rotation: X", rbc.LockRotationX); - Property("Rotation: Y", rbc.LockRotationY); - Property("Rotation: Z", rbc.LockRotationZ); - EndPropertyGrid(); + UI::BeginPropertyGrid(); + UI::Property("Position: X", rbc.LockPositionX); + UI::Property("Position: Y", rbc.LockPositionY); + UI::Property("Position: Z", rbc.LockPositionZ); + UI::Property("Rotation: X", rbc.LockRotationX); + UI::Property("Rotation: Y", rbc.LockRotationY); + UI::Property("Rotation: Z", rbc.LockRotationZ); + UI::EndPropertyGrid(); ImGui::TreePop(); } @@ -972,60 +821,60 @@ namespace Prism DrawComponent("Physics Material", entity, [](PhysicsMaterialComponent& pmc) { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); - Property("Static Friction", pmc.StaticFriction, 0.01f, 0.0f, 1.0f); - Property("Dynamic Friction", pmc.DynamicFriction, 0.01f, 0.0f, 1.0f); - Property("Bounciness", pmc.Bounciness, 0.01f, 0.0f, 1.0f); + UI::Property("Static Friction", pmc.StaticFriction, 0.01f, 0.0f, 1.0f); + UI::Property("Dynamic Friction", pmc.DynamicFriction, 0.01f, 0.0f, 1.0f); + UI::Property("Bounciness", pmc.Bounciness, 0.01f, 0.0f, 1.0f); - EndPropertyGrid(); + UI::EndPropertyGrid(); }); DrawComponent("Box Collider", entity, [](BoxColliderComponent& bcc) { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); - if (Property("Size", bcc.Size)) + if (UI::Property("Size", bcc.Size)) { bcc.DebugMesh = MeshFactory::CreateBox(bcc.Size); } - Property("IsTrigger", bcc.IsTrigger); + UI::Property("IsTrigger", bcc.IsTrigger); - EndPropertyGrid(); + UI::EndPropertyGrid(); }); DrawComponent("Sphere Collider", entity, [](SphereColliderComponent& scc) { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); - if (Property("Radius", scc.Radius)) + if (UI::Property("Radius", scc.Radius)) { scc.DebugMesh = MeshFactory::CreateSphere(scc.Radius); } - Property("IsTrigger", scc.IsTrigger); + UI::Property("IsTrigger", scc.IsTrigger); - EndPropertyGrid(); + UI::EndPropertyGrid(); }); DrawComponent("Capsule Collider", entity, [](CapsuleColliderComponent& ccc) { - BeginPropertyGrid(); + UI::BeginPropertyGrid(); bool changed = false; - if (Property("Radius", ccc.Radius)) changed = true; - if (Property("Height", ccc.Height)) changed = true; + if (UI::Property("Radius", ccc.Radius)) changed = true; + if (UI::Property("Height", ccc.Height)) changed = true; - Property("Is Trigger", ccc.IsTrigger); + UI::Property("Is Trigger", ccc.IsTrigger); if (changed) { ccc.DebugMesh = MeshFactory::CreateCapsule(ccc.Radius, ccc.Height); } - EndPropertyGrid(); + UI::EndPropertyGrid(); }); DrawComponent("Mesh Collider", entity, [](MeshColliderComponent& mcc) @@ -1049,14 +898,28 @@ namespace Prism if (!file.empty()) { mcc.CollisionMesh = Ref::Create(file); - PhysicsWrappers::CreateConvexMesh(mcc); + if (mcc.IsConvex) + PhysicsWrappers::CreateConvexMesh(mcc, true); + else + PhysicsWrappers::CreateTriangleMesh(mcc, true); } } ImGui::EndColumns(); - BeginPropertyGrid(); - Property("Is Trigger", mcc.IsTrigger); - EndPropertyGrid(); + UI::BeginPropertyGrid(); + if (UI::Property("Is Convex", mcc.IsConvex)) + { + if (mcc.CollisionMesh) + { + if (mcc.IsConvex) + PhysicsWrappers::CreateConvexMesh(mcc, true); + else + PhysicsWrappers::CreateTriangleMesh(mcc, true); + } + } + + UI::Property("Is Trigger", mcc.IsTrigger); + UI::EndPropertyGrid(); }); } diff --git a/Prism/src/Prism/Physics/Physics3D.cpp b/Prism/src/Prism/Physics/Physics3D.cpp index 0bbd33f..265f1ee 100644 --- a/Prism/src/Prism/Physics/Physics3D.cpp +++ b/Prism/src/Prism/Physics/Physics3D.cpp @@ -146,7 +146,7 @@ namespace Prism s_SimulationTime -= s_Settings.FixedTimestep; - for (Entity& e : s_SimulatedEntities) + for (const Entity& e : s_SimulatedEntities) { if (ScriptEngine::IsEntityModuleValid(e)) ScriptEngine::OnPhysicsUpdateEntity(e, s_Settings.FixedTimestep); diff --git a/Prism/src/Prism/Physics/PhysicsUtils.cpp b/Prism/src/Prism/Physics/PhysicsUtils.cpp index 70b3e93..a198ab1 100644 --- a/Prism/src/Prism/Physics/PhysicsUtils.cpp +++ b/Prism/src/Prism/Physics/PhysicsUtils.cpp @@ -10,6 +10,12 @@ namespace Prism { + physx::PxTransform ToPhysXTransform(const TransformComponent& transform) + { + const physx::PxQuat r = ToPhysXQuat(glm::normalize(glm::quat(transform.Rotation))); + const physx::PxVec3 p = ToPhysXVector(transform.Translation); + return physx::PxTransform(p, r); + } physx::PxTransform ToPhysXTransform(const glm::mat4& matrix) { diff --git a/Prism/src/Prism/Physics/PhysicsUtils.h b/Prism/src/Prism/Physics/PhysicsUtils.h index 660c83a..53d0d14 100644 --- a/Prism/src/Prism/Physics/PhysicsUtils.h +++ b/Prism/src/Prism/Physics/PhysicsUtils.h @@ -9,10 +9,12 @@ #include #include "glm/glm.hpp" +#include "Prism/Scene/Components.h" namespace Prism { + physx::PxTransform ToPhysXTransform(const TransformComponent& transform); physx::PxTransform ToPhysXTransform(const glm::mat4& matrix); physx::PxMat44 ToPhysXMatrix(const glm::mat4& matrix); physx::PxVec3 ToPhysXVector(const glm::vec3& vector); diff --git a/Prism/src/Prism/Physics/PhysicsWrappers.cpp b/Prism/src/Prism/Physics/PhysicsWrappers.cpp index 5025d8f..9975b5c 100644 --- a/Prism/src/Prism/Physics/PhysicsWrappers.cpp +++ b/Prism/src/Prism/Physics/PhysicsWrappers.cpp @@ -284,21 +284,278 @@ namespace Prism shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); - shape->setLocalPose(physx::PxTransform(physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0, 0, 1)))); + // shape->setLocalPose(physx::PxTransform(physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0, 0, 1)))); } void PhysicsWrappers::AddMeshCollider(physx::PxRigidActor& actor, const physx::PxMaterial& material,MeshColliderComponent& collider, const glm::vec3& scale) { - physx::PxConvexMeshGeometry convexGeometry = physx::PxConvexMeshGeometry(CreateConvexMesh(collider)); - convexGeometry.meshFlags = physx::PxConvexMeshGeometryFlag::eTIGHT_BOUNDS; - physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, convexGeometry, material); - shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); - shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); + if (collider.IsConvex) + { + std::vector meshes = CreateConvexMesh(collider, true); + + for (const auto mesh : meshes) + { + physx::PxConvexMeshGeometry convexGeometry = physx::PxConvexMeshGeometry(mesh, physx::PxMeshScale(ToPhysXVector(scale))); + convexGeometry.meshFlags = physx::PxConvexMeshGeometryFlag::eTIGHT_BOUNDS; + physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, convexGeometry, material); + shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); + shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); + } + } + else + { + std::vector meshes = CreateTriangleMesh(collider, true); + + for (const auto mesh : meshes) + { + physx::PxTriangleMeshGeometry convexGeometry = physx::PxTriangleMeshGeometry(mesh, physx::PxMeshScale(ToPhysXVector(scale))); + physx::PxShape* shape = physx::PxRigidActorExt::createExclusiveShape(actor, convexGeometry, material); + shape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !collider.IsTrigger); + shape->setFlag(physx::PxShapeFlag::eTRIGGER_SHAPE, collider.IsTrigger); + } + } } - physx::PxConvexMesh* PhysicsWrappers::CreateConvexMesh(MeshColliderComponent& collider) + std::vector PhysicsWrappers::CreateTriangleMesh(MeshColliderComponent& collider, bool invalidateOld) { + std::vector meshes; + + // 设置烹饪参数,可以根据需要调整 + physx::PxCookingParams cookingParams(s_Physics->getTolerancesScale()); + // 可以添加额外的烹饪参数设置,如: + // cookingParams.meshPreprocessParams = physx::PxMeshPreprocessingFlag::eWELD_VERTICES; + // cookingParams.meshWeldTolerance = 0.01f; + + // 获取顶点和索引数据 + const std::vector& vertices = collider.CollisionMesh->GetStaticVertices(); + const std::vector& indices = collider.CollisionMesh->GetIndices(); + + // 提取顶点位置(如果需要) + std::vector vertexPositions; + if (vertices[0].Position != glm::vec3()) { + vertexPositions.reserve(vertices.size()); + for (const auto& vertex : vertices) { + vertexPositions.push_back(vertex.Position); + } + } + + // 清空旧的处理网格(如果需要) + if (invalidateOld) { + collider.ProcessedMeshes.clear(); + } + + // 处理每个子网格 + for (const auto& submesh : collider.CollisionMesh->GetSubmeshes()) + { + // 准备三角形网格描述 + physx::PxTriangleMeshDesc triangleDesc; + triangleDesc.points.count = submesh.VertexCount; + triangleDesc.points.stride = sizeof(glm::vec3); + + // 使用顶点位置数组或原始顶点数据 + if (!vertexPositions.empty()) { + triangleDesc.points.data = &vertexPositions[submesh.BaseVertex]; + } else { + triangleDesc.points.stride = sizeof(Vertex); + triangleDesc.points.data = &vertices[submesh.BaseVertex]; + } + + triangleDesc.triangles.count = submesh.IndexCount / 3; + triangleDesc.triangles.data = &indices[submesh.BaseIndex / 3]; + triangleDesc.triangles.stride = sizeof(Index); // 假设Index包含三个值 + + // 如果需要,设置三角形标志 + // triangleDesc.flags = physx::PxMeshFlag::e16_BIT_INDICES; // 如果使用16位索引 + + // 创建三角形网格 + physx::PxTriangleMeshCookingResult::Enum result; + physx::PxTriangleMesh* triangleMesh = PxCreateTriangleMesh( + cookingParams, + triangleDesc, + *PxGetStandaloneInsertionCallback(), + &result + ); + + if (!triangleMesh) { + PM_CORE_ERROR("Failed to create triangle mesh for submesh: {0}", submesh.MeshName); + continue; + } + + meshes.push_back(triangleMesh); + + // 为可视化生成渲染网格(如果需要) + if (collider.ProcessedMeshes.empty() || invalidateOld) + { + const uint32_t nbVerts = triangleMesh->getNbVertices(); + const physx::PxVec3* meshVertices = triangleMesh->getVertices(); + const uint32_t nbTriangles = triangleMesh->getNbTriangles(); + + std::vector processedVertices; + std::vector processedIndices; + + // 获取顶点数据 + processedVertices.reserve(nbVerts); + for (uint32_t v = 0; v < nbVerts; v++) + { + Vertex vertex; + vertex.Position = FromPhysXVector(meshVertices[v]); + processedVertices.push_back(vertex); + } + + // 获取三角形索引 + processedIndices.reserve(nbTriangles); + + // 检查索引格式(16位或32位) + if (triangleMesh->getTriangleMeshFlags() & physx::PxTriangleMeshFlag::e16_BIT_INDICES) + { + const physx::PxU16* tris = static_cast(triangleMesh->getTriangles()); + for (uint32_t tri = 0; tri < nbTriangles; tri++) + { + Index index; + index.V1 = tris[3 * tri + 0]; + index.V2 = tris[3 * tri + 1]; + index.V3 = tris[3 * tri + 2]; + processedIndices.push_back(index); + } + } + else + { + const physx::PxU32* tris = static_cast(triangleMesh->getTriangles()); + for (uint32_t tri = 0; tri < nbTriangles; tri++) + { + Index index; + index.V1 = static_cast(tris[3 * tri + 0]); + index.V2 = static_cast(tris[3 * tri + 1]); + index.V3 = static_cast(tris[3 * tri + 2]); + processedIndices.push_back(index); + } + } + + // 为当前子网格创建渲染网格 + collider.ProcessedMeshes.push_back(Ref::Create(processedVertices, processedIndices)); + } + } + + return meshes; + } + + std::vector PhysicsWrappers::CreateConvexMesh(MeshColliderComponent& collider, bool invalidateOld) + { + std::vector meshes; + + // 设置烹饪参数,保持与原始版本相同的配置 + physx::PxCookingParams cookingParams(s_Physics->getTolerancesScale()); + cookingParams.planeTolerance = 0.0F; + cookingParams.meshPreprocessParams = physx::PxMeshPreprocessingFlags(physx::PxMeshPreprocessingFlag::eWELD_VERTICES); + cookingParams.meshWeldTolerance = 0.01f; + + // 获取顶点数据 + const std::vector& vertices = collider.CollisionMesh->GetStaticVertices(); + const std::vector& indices = collider.CollisionMesh->GetIndices(); + + // 准备顶点位置数组(如果需要的话) + std::vector vertexPositions; + if (vertices[0].Position != glm::vec3()) { + vertexPositions.reserve(vertices.size()); + for (const auto& vertex : vertices) { + vertexPositions.push_back(vertex.Position); + } + } + + if (invalidateOld) { + collider.ProcessedMeshes.clear(); + } + + + // 处理每个子网格 + for (const auto& submesh : collider.CollisionMesh->GetSubmeshes()) + { + // 准备凸包描述 + physx::PxConvexMeshDesc convexDesc; + convexDesc.points.count = submesh.VertexCount; + convexDesc.points.stride = sizeof(glm::vec3); + + // 使用顶点位置数组或原始顶点数据 + if (!vertexPositions.empty()) { + convexDesc.points.data = &vertexPositions[submesh.BaseVertex]; + } else { + convexDesc.points.stride = sizeof(Vertex); + convexDesc.points.data = &vertices[submesh.BaseVertex]; + } + + convexDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX | + physx::PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES | + physx::PxConvexFlag::eSHIFT_VERTICES; + + // 创建凸包网格 + physx::PxConvexMeshCookingResult::Enum result; + physx::PxConvexMesh* convexMesh = PxCreateConvexMesh( + cookingParams, + convexDesc, + *PxGetStandaloneInsertionCallback(), + &result + ); + + if (!convexMesh) { + PM_CORE_ERROR("Failed to create convex mesh for submesh: {0}", submesh.MeshName); + continue; + } + + meshes.push_back(convexMesh); + + // 为可视化生成渲染网格(如果需要) + if (collider.ProcessedMeshes.empty() || invalidateOld) + { + const uint32_t nbPolygons = convexMesh->getNbPolygons(); + const physx::PxVec3* convexVertices = convexMesh->getVertices(); + const physx::PxU8* convexIndices = convexMesh->getIndexBuffer(); + + std::vector collisionVertices; + std::vector collisionIndices; + uint32_t vertCounter = 0; + uint32_t indexCounter = 0; + + for (uint32_t i = 0; i < nbPolygons; i++) + { + physx::PxHullPolygon polygon; + convexMesh->getPolygonData(i, polygon); + + const uint32_t vI0 = vertCounter; + + // 收集当前多边形的所有顶点 + for (uint32_t vI = 0; vI < polygon.mNbVerts; vI++) + { + Vertex vertex; + vertex.Position = FromPhysXVector( + convexVertices[convexIndices[polygon.mIndexBase + vI]] + ); + // 如果需要坐标系转换,可以在这里添加 + // vertex.Position = glm::rotate(vertex.Position, glm::radians(90.0f), {1.0f, 0.0f, 0.0f}); + + collisionVertices.push_back(vertex); + vertCounter++; + } + + // 为当前多边形生成三角形索引(扇形三角剖分) + for (auto vI = 1; vI < polygon.mNbVerts - 1; vI++) + { + Index index; + index.V1 = vI0; + index.V2 = vI0 + vI + 1; + index.V3 = vI0 + vI; + collisionIndices.push_back(index); + indexCounter++; + } + } + + // 为当前子网格创建渲染网格 + collider.ProcessedMeshes.push_back(Ref::Create(collisionVertices, collisionIndices)); + } + } + + return meshes; +#if 0 const auto& vertices = collider.CollisionMesh->GetStaticVertices(); @@ -373,6 +630,7 @@ namespace Prism } return mesh; +#endif } physx::PxMaterial* PhysicsWrappers::CreateMaterial(const PhysicsMaterialComponent& material) @@ -498,7 +756,8 @@ namespace Prism PM_CORE_WARN("PVD is already connected, reconnecting"); s_VisualDebugger->disconnect(); } - isConnect = s_VisualDebugger->connect(*transport, physx::PxPvdInstrumentationFlag::eALL); + s_VisualDebugger->connect(*transport, physx::PxPvdInstrumentationFlag::eALL); + isConnect = s_VisualDebugger->isConnected(); #endif return isConnect; } diff --git a/Prism/src/Prism/Physics/PhysicsWrappers.h b/Prism/src/Prism/Physics/PhysicsWrappers.h index 56b79e1..0fec25e 100644 --- a/Prism/src/Prism/Physics/PhysicsWrappers.h +++ b/Prism/src/Prism/Physics/PhysicsWrappers.h @@ -45,7 +45,8 @@ namespace Prism static void AddMeshCollider(::physx::PxRigidActor& actor, const ::physx::PxMaterial& material, ::Prism::MeshColliderComponent& collider, const glm::vec3& scale = glm::vec3(0.0f)); - static physx::PxConvexMesh* CreateConvexMesh(MeshColliderComponent& collider); + static std::vector CreateTriangleMesh(MeshColliderComponent& collider, bool invalidateOld = false); + static std::vector CreateConvexMesh(MeshColliderComponent& collider, bool invalidateOld = false); static physx::PxMaterial* CreateMaterial(const PhysicsMaterialComponent& material); diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp index 1faa6ba..c79097b 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp @@ -10,74 +10,231 @@ namespace Prism { - OpenGLFrameBuffer::OpenGLFrameBuffer(const FramebufferSpecification& spec) - : m_Specification(spec) - { - OpenGLFrameBuffer::Resize(spec.Width, spec.Height, true); - } + namespace Utils + { + static GLenum TextureTarget(bool multisampled) + { + return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + } + static void CreateTextures(bool multisampled, RendererID* outID, uint32_t count) + { + glCreateTextures(TextureTarget(multisampled), count, outID); + } + + static void BindTexture(bool multisampled, RendererID id) + { + glBindTexture(TextureTarget(multisampled), id); + } + + static GLenum DataType(GLenum format) + { + switch (format) + { + case GL_RGBA8: return GL_UNSIGNED_BYTE; + case GL_RG16F: + case GL_RG32F: + case GL_RGBA16F: + case GL_RGBA32F: return GL_FLOAT; + case GL_DEPTH24_STENCIL8: return GL_UNSIGNED_INT_24_8; + } + + PM_CORE_ASSERT(false, "Unknown format!"); + return 0; + } + + static void AttachColorTexture(RendererID id, int samples, GLenum format, uint32_t width, uint32_t height, int index) + { + const bool multisampled = samples > 1; + if (multisampled) + { + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE); + } + else + { + // Only RGBA access for now + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, DataType(format), nullptr); + + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0); + } + + static void AttachDepthTexture(RendererID id, int samples, GLenum format, GLenum attachmentType, uint32_t width, uint32_t height) + { + bool multisampled = samples > 1; + if (multisampled) + { + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE); + } + else + { + glTexStorage2D(GL_TEXTURE_2D, 1, format, width, height); + + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(TextureTarget(multisampled), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentType, TextureTarget(multisampled), id, 0); + } + + static bool IsDepthFormat(FramebufferTextureFormat format) + { + switch (format) + { + case FramebufferTextureFormat::DEPTH24STENCIL8: + case FramebufferTextureFormat::DEPTH32F: + return true; + } + return false; + } + + } + + OpenGLFrameBuffer::OpenGLFrameBuffer(const FramebufferSpecification& spec) + : m_Specification(spec), m_Width(spec.Width), m_Height(spec.Height) + { + for (auto format : m_Specification.Attachments.Attachments) + { + if (!Utils::IsDepthFormat(format.TextureFormat)) + m_ColorAttachmentFormats.emplace_back(format.TextureFormat); + else + m_DepthAttachmentFormat = format.TextureFormat; + } + + Resize(spec.Width, spec.Height, true); + } OpenGLFrameBuffer::~OpenGLFrameBuffer() { - GLuint rendererID = m_RendererID; - Renderer::Submit([rendererID](){ - glDeleteFramebuffers(1, &rendererID); - }); + Ref instance = this; + Renderer::Submit([instance]() { + glDeleteFramebuffers(1, &instance->m_RendererID); + }); } void OpenGLFrameBuffer::Bind() const { - Renderer::Submit([=](){ - glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); - glViewport(0, 0, m_Specification.Width, m_Specification.Height); - }); + Ref instance = this; + Renderer::Submit([instance]() { + glBindFramebuffer(GL_FRAMEBUFFER, instance->m_RendererID); + glViewport(0, 0, instance->m_Width, instance->m_Height); + }); } void OpenGLFrameBuffer::Unbind() const { - Renderer::Submit([=](){ + Renderer::Submit([](){ glBindFramebuffer(GL_FRAMEBUFFER, 0); }); } void OpenGLFrameBuffer::Resize(const uint32_t width, const uint32_t height, const bool forceReCreate) { - if (!forceReCreate && (m_Specification.Width == width && m_Specification.Height == height)) + if (!forceReCreate && (m_Width == width && m_Height == height)) return; - m_Specification.Width = width; - m_Specification.Height = height; - Renderer::Submit([this]() + m_Width = width; + m_Height = height; + + Ref instance = this; + Renderer::Submit([instance]() mutable { - if (m_RendererID) + if (instance->m_RendererID) { - glDeleteFramebuffers(1, &m_RendererID); - glDeleteTextures(1, &m_ColorAttachment); - glDeleteTextures(1, &m_DepthAttachment); + glDeleteFramebuffers(1, &instance->m_RendererID); + glDeleteTextures(static_cast(instance->m_ColorAttachments.size()), instance->m_ColorAttachments.data()); + glDeleteTextures(1, &instance->m_DepthAttachment); + + instance->m_ColorAttachments.clear(); + instance->m_DepthAttachment = 0; } - glGenFramebuffers(1, &m_RendererID); - glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID); + glGenFramebuffers(1, &instance->m_RendererID); + glBindFramebuffer(GL_FRAMEBUFFER, instance->m_RendererID); + + bool multisample = instance->m_Specification.Samples > 1; + + if (instance->m_ColorAttachmentFormats.size()) + { + instance->m_ColorAttachments.resize(instance->m_ColorAttachmentFormats.size()); + Utils::CreateTextures(multisample, instance->m_ColorAttachments.data(), static_cast(instance->m_ColorAttachments.size())); + + // Create color attachments + for (int i = 0; i < instance->m_ColorAttachments.size(); i++) + { + Utils::BindTexture(multisample, instance->m_ColorAttachments[i]); + switch (instance->m_ColorAttachmentFormats[i]) + { + case FramebufferTextureFormat::RGBA8: + Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RGBA8, instance->m_Width, instance->m_Height, i); + break; + case FramebufferTextureFormat::RGBA16F: + Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RGBA16F, instance->m_Width, instance->m_Height, i); + break; + case FramebufferTextureFormat::RGBA32F: + Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RGBA32F, instance->m_Width, instance->m_Height, i); + break; + case FramebufferTextureFormat::RG32F: + Utils::AttachColorTexture(instance->m_ColorAttachments[i], instance->m_Specification.Samples, GL_RG32F, instance->m_Width, instance->m_Height, i); + break; + } + + } + } + + if (instance->m_DepthAttachmentFormat != FramebufferTextureFormat::None) + { + Utils::CreateTextures(multisample, &instance->m_DepthAttachment, 1); + Utils::BindTexture(multisample, instance->m_DepthAttachment); + switch (instance->m_DepthAttachmentFormat) + { + case FramebufferTextureFormat::DEPTH24STENCIL8: + Utils::AttachDepthTexture(instance->m_DepthAttachment, instance->m_Specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, instance->m_Width, instance->m_Height); + break; + case FramebufferTextureFormat::DEPTH32F: + Utils::AttachDepthTexture(instance->m_DepthAttachment, instance->m_Specification.Samples, GL_DEPTH_COMPONENT32F, GL_DEPTH_ATTACHMENT, instance->m_Width, instance->m_Height); + break; + } + + } + + if (instance->m_ColorAttachments.size() > 1) + { + PM_CORE_ASSERT(instance->m_ColorAttachments.size() <= 4); + GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; + glDrawBuffers(static_cast(instance->m_ColorAttachments.size()), buffers); + } + else if (instance->m_ColorAttachments.size() == 0) + { + // Only depth-pass + glDrawBuffer(GL_NONE); + } + +#if 0 - bool multisample = m_Specification.Samples > 1; if (multisample) { glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_ColorAttachment); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment); - // TODO: Create Prism texture object based on format here + // TODO: Create Hazel 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_TRUE); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA16F, m_Specification.Width, m_Specification.Height, GL_FALSE); } else if (m_Specification.Format == FramebufferFormat::RGBA8) { - // glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_TRUE); - glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_TRUE); + glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_FALSE); } // glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); } else @@ -85,30 +242,51 @@ namespace Prism glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment); glBindTexture(GL_TEXTURE_2D, m_ColorAttachment); - // TODO: Create texture object based on format here + // TODO: Create Hazel 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); } + else if (m_Specification.Format == FramebufferFormat::RG32F) // "Shadow" for now + { + glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, m_Specification.Width, m_Specification.Height); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_ColorAttachment, 0); + glDrawBuffer(GL_NONE); + } else if (m_Specification.Format == FramebufferFormat::RGBA8) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0); + else if (m_Specification.Format == FramebufferFormat::COMP) + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment2); + glBindTexture(GL_TEXTURE_2D, m_ColorAttachment2); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + } + + if (m_Specification.Format != FramebufferFormat::RG32F) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0); + } } if (multisample) { glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_DepthAttachment); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment); - // glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 8, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_TRUE); - glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_TRUE); + glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_FALSE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); - // glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment, 0); } - else + else if (m_Specification.Format != FramebufferFormat::RG32F) { glCreateTextures(GL_TEXTURE_2D, 1, &m_DepthAttachment); glBindTexture(GL_TEXTURE_2D, m_DepthAttachment); @@ -116,15 +294,31 @@ namespace Prism GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL ); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0); } - // glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0); - if (multisample) - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0); - else - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_DepthAttachment, 0); + if (m_Specification.Format != FramebufferFormat::RG32F) + { + if (multisample) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0); + } + else + { + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0); + if (m_Specification.Format == FramebufferFormat::COMP) + { + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_ColorAttachment2, 0); + const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers(2, buffers); + } + } + + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_DepthAttachment, 0); + } +#endif PM_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!"); @@ -132,18 +326,11 @@ namespace Prism }); } - void OpenGLFrameBuffer::BindTexture(const uint32_t slot) const + void OpenGLFrameBuffer::BindTexture(uint32_t attachmentIndex, uint32_t slot) const { - Renderer::Submit([=](){ - glActiveTexture(GL_TEXTURE0 + slot); - - if (m_Specification.Samples > 1) { - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment); - } else { - glBindTexture(GL_TEXTURE_2D, m_ColorAttachment); - } - - // glBindTexture(GL_TEXTURE_2D, m_ColorAttachment); - }); + Ref instance = this; + Renderer::Submit([instance, attachmentIndex, slot]() { + glBindTextureUnit(slot, instance->m_ColorAttachments[attachmentIndex]); + }); } } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.h b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.h index 30ffc35..09a2d5e 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.h @@ -20,19 +20,28 @@ namespace Prism void Resize(uint32_t width, uint32_t height, bool forceReCreate) override; - void BindTexture(uint32_t slot) const override; + void BindTexture(uint32_t attachmentIndex, uint32_t slot) const override; + + uint32_t GetWidth() const override { return m_Specification.Width; } + uint32_t GetHeight() const override { return m_Specification.Height; } RendererID GetRendererID() const override { return m_RendererID; } - RendererID GetColorAttachmentRendererID() const override { return m_ColorAttachment; } + RendererID GetColorAttachmentRendererID(const int index = 0) const override { return m_ColorAttachments[index]; } RendererID GetDepthAttachmentRendererID() const override { return m_DepthAttachment; } virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; } private: - FramebufferSpecification m_Specification; + FramebufferSpecification m_Specification; RendererID m_RendererID = 0; - RendererID m_ColorAttachment = 0, m_DepthAttachment = 0; + std::vector m_ColorAttachments; + RendererID m_DepthAttachment; + + std::vector m_ColorAttachmentFormats; + FramebufferTextureFormat m_DepthAttachmentFormat = FramebufferTextureFormat::None; + + uint32_t m_Width = 0, m_Height = 0; }; } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index bbf9f3a..fe733d2 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -118,6 +118,13 @@ namespace Prism m_ShaderReloadedCallbacks.push_back(callback); } + void OpenGLShader::SetBool(const std::string& name, bool value) + { + Renderer::Submit([=]() { + UploadUniformInt(name, value); + }); + } + void OpenGLShader::SetFloat(const std::string& name, float value) { Renderer::Submit([=]() { @@ -125,7 +132,14 @@ namespace Prism }); } - void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value) + void OpenGLShader::SetFloat2(const std::string& name, const glm::vec2& value) + { + Renderer::Submit([=]() { + UploadUniformFloat2(name, value); + }); + } + + void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value) { Renderer::Submit([=]() { @@ -299,7 +313,7 @@ namespace Prism std::vector Tokenize(const std::string& string) { - return SplitString(string, " \t\n"); + return SplitString(string, " \t\n\r"); } std::vector GetLines(const std::string& string) @@ -372,6 +386,7 @@ namespace Prism static bool IsTypeStringResource(const std::string& type) { + if (type == "sampler1D") return true; if (type == "sampler2D") return true; if (type == "sampler2DMS") return true; if (type == "samplerCube") return true; @@ -630,11 +645,11 @@ namespace Prism } else if (resource->GetCount() > 1) { - resource->m_Register = 0; + resource->m_Register = sampler; uint32_t count = resource->GetCount(); int* samplers = new int[count]; for (uint32_t s = 0; s < count; s++) - samplers[s] = s; + samplers[s] = sampler++; UploadUniformIntArray(resource->GetName(), samplers, count); delete[] samplers; } @@ -671,7 +686,7 @@ namespace Prism std::vector infoLog(maxLength); glGetShaderInfoLog(shaderRendererID, maxLength, &maxLength, &infoLog[0]); - PM_CORE_ERROR("Shader compile failed: \n{0}", &infoLog[0]); + PM_CORE_ERROR("Shader compilation failed ({0}):\n{1}", m_AssetPath, &infoLog[0]); glDeleteShader(shaderRendererID); @@ -692,7 +707,7 @@ namespace Prism std::vector infoLog(maxLength); glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); - PM_CORE_ERROR("Shader compilation failed:\n{0}", &infoLog[0]); + PM_CORE_ERROR("Shader link failed ({0}):\n{1}", m_AssetPath, &infoLog[0]); glDeleteProgram(program); @@ -727,7 +742,7 @@ namespace Prism if (location != -1) glUniform1i(location, value); else - PM_CORE_WARN("Uniform '{0}' not found!", name); + PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); } void OpenGLShader::UploadUniformFloat(const std::string& name, const float value) const @@ -737,7 +752,7 @@ namespace Prism if (location != -1) glUniform1f(location, value); else - PM_CORE_WARN("Uniform '{0}' not found!", name); + PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); } void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& values) const @@ -747,7 +762,7 @@ namespace Prism if (location != -1) glUniform2fv(location,1, glm::value_ptr(values)); else - PM_CORE_WARN("Uniform '{0}' not found!", name); + PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); } void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& values) const @@ -757,7 +772,7 @@ namespace Prism if (location != -1) glUniform3fv(location,1, glm::value_ptr(values)); else - PM_CORE_WARN("Uniform '{0}' not found!", name); + PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); } void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& values) const @@ -767,7 +782,7 @@ namespace Prism if (location != -1) glUniform4fv(location,1, glm::value_ptr(values)); else - PM_CORE_WARN("Uniform '{0}' not found!", name); + PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); } void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& values) const @@ -777,7 +792,7 @@ namespace Prism if (location != -1) glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(values)); else - PM_CORE_WARN("Uniform '{0}' not found!", name); + PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); } void OpenGLShader::ResolveAndSetUniforms(const Ref& decl, Buffer buffer) @@ -803,6 +818,9 @@ namespace Prism uint32_t offset = uniform->GetOffset(); switch (uniform->GetType()) { + case OpenGLShaderUniformDeclaration::Type::BOOL: + UploadUniformFloat(uniform->GetLocation(), *(bool*)&buffer.Data[offset]); + break; case OpenGLShaderUniformDeclaration::Type::FLOAT32: UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]); break; @@ -839,6 +857,9 @@ namespace Prism uint32_t offset = uniform->GetOffset(); switch (uniform->GetType()) { + case OpenGLShaderUniformDeclaration::Type::BOOL: + UploadUniformFloat(uniform->GetLocation(), *(bool*)&buffer.Data[offset]); + break; case OpenGLShaderUniformDeclaration::Type::FLOAT32: UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]); break; @@ -872,6 +893,9 @@ namespace Prism { switch (field.GetType()) { + case OpenGLShaderUniformDeclaration::Type::BOOL: + UploadUniformFloat(field.GetLocation(), *(bool*)&data[offset]); + break; case OpenGLShaderUniformDeclaration::Type::FLOAT32: UploadUniformFloat(field.GetLocation(), *(float*)&data[offset]); break; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h index 8a2c9fb..a8eb036 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h @@ -27,7 +27,9 @@ namespace Prism virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) override; virtual void AddShaderReloadedCallback(const ShaderReloadedCallback& callback) override; + virtual void SetBool(const std::string& name, bool value) override; virtual void SetFloat(const std::string& name, float value) override; + virtual void SetFloat2(const std::string& name, const glm::vec2& value) override; virtual void SetFloat3(const std::string& name, const glm::vec3& value) override; virtual void SetInt(const std::string& name, int value) override; virtual void SetMat4(const std::string& name, const glm::mat4& value) override; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp index 53709a0..a1f2882 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp @@ -35,6 +35,7 @@ namespace Prism { switch (type) { + case OpenGLShaderUniformDeclaration::Type::BOOL: return 1; case OpenGLShaderUniformDeclaration::Type::INT32: return 4; case OpenGLShaderUniformDeclaration::Type::FLOAT32: return 4; case OpenGLShaderUniformDeclaration::Type::VEC2: return 4 * 2; @@ -48,14 +49,15 @@ namespace Prism OpenGLShaderUniformDeclaration::Type OpenGLShaderUniformDeclaration::StringToType(const std::string& type) { - if (type == "int") return Type::INT32; - if (type == "int32") return Type::INT32; - if (type == "float") return Type::FLOAT32; - if (type == "vec2") return Type::VEC2; - if (type == "vec3") return Type::VEC3; - if (type == "vec4") return Type::VEC4; - if (type == "mat3") return Type::MAT3; - if (type == "mat4") return Type::MAT4; + if (type == "bool") return OpenGLShaderUniformDeclaration::Type::BOOL; + if (type == "int") return OpenGLShaderUniformDeclaration::Type::INT32; + if (type == "int32") return OpenGLShaderUniformDeclaration::Type::INT32; + if (type == "float") return OpenGLShaderUniformDeclaration::Type::FLOAT32; + if (type == "vec2") return OpenGLShaderUniformDeclaration::Type::VEC2; + if (type == "vec3") return OpenGLShaderUniformDeclaration::Type::VEC3; + if (type == "vec4") return OpenGLShaderUniformDeclaration::Type::VEC4; + if (type == "mat3") return OpenGLShaderUniformDeclaration::Type::MAT3; + if (type == "mat4") return OpenGLShaderUniformDeclaration::Type::MAT4; return Type::NONE; } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h index 24ea51b..598600b 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h @@ -44,7 +44,7 @@ namespace Prism public: enum class Type { - NONE, FLOAT32, VEC2, VEC3, VEC4, MAT3, MAT4, INT32, STRUCT + NONE, FLOAT32, VEC2, VEC3, VEC4, MAT3, MAT4, INT32, STRUCT, BOOL }; private: std::string m_Name; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp index 0fff672..b0674fa 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp @@ -67,7 +67,7 @@ namespace Prism { PM_CORE_INFO("Loading texture {0}, srgb={1}", path, srgb); m_ImageData.Data = stbi_load(path.c_str(), &width, &height, &channels, srgb ? STBI_rgb : STBI_rgb_alpha); - PM_CORE_ASSERT(m_ImageData.Data, "Could not read image!"); + if (!m_ImageData.Data) PM_CORE_ERROR("Could not read image file: {0}", path); m_Format = TextureFormat::RGBA; } diff --git a/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp b/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp index ab0fa83..6321917 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp +++ b/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp @@ -47,7 +47,12 @@ namespace Prism glfwSetCursor(m_Window, m_ImGuiMouseCursors[imgui_cursor] ? m_ImGuiMouseCursors[imgui_cursor] : m_ImGuiMouseCursors[ImGuiMouseCursor_Arrow]); } - void WindowsWindow::SetVSync(bool enable) + void WindowsWindow::Maximize() + { + glfwMaximizeWindow(m_Window); + } + + void WindowsWindow::SetVSync(const bool enable) { if (enable) glfwSwapInterval(1); diff --git a/Prism/src/Prism/Platform/Windows/WindowsWindow.h b/Prism/src/Prism/Platform/Windows/WindowsWindow.h index 35282c5..6576267 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsWindow.h +++ b/Prism/src/Prism/Platform/Windows/WindowsWindow.h @@ -22,6 +22,9 @@ namespace Prism inline uint32_t GetHeight() const override { return m_Data.Height; } inline void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; } + + virtual void Maximize() override; + bool const IsVSync() const override { return m_Data.VSync; } void SetVSync(bool enable) override; virtual std::pair GetSize() const override { return { m_Data.Width, m_Data.Height }; } diff --git a/Prism/src/Prism/Renderer/FrameBuffer.h b/Prism/src/Prism/Renderer/FrameBuffer.h index 5355716..65899ed 100644 --- a/Prism/src/Prism/Renderer/FrameBuffer.h +++ b/Prism/src/Prism/Renderer/FrameBuffer.h @@ -11,11 +11,40 @@ namespace Prism { - enum class FramebufferFormat + enum class FramebufferTextureFormat { None = 0, + + // Color RGBA8 = 1, - RGBA16F = 2 + RGBA16F = 2, + RGBA32F = 3, + RG32F = 4, + + // Depth/stencil + DEPTH32F = 5, + DEPTH24STENCIL8 = 6, + + // Defaults + Depth = DEPTH24STENCIL8 + }; + + struct FramebufferTextureSpecification + { + FramebufferTextureSpecification() = default; + FramebufferTextureSpecification(FramebufferTextureFormat format) : TextureFormat(format) {} + + FramebufferTextureFormat TextureFormat; + // TODO: filtering/wrap + }; + + struct FramebufferAttachmentSpecification + { + FramebufferAttachmentSpecification() = default; + FramebufferAttachmentSpecification(const std::initializer_list& attachments) + : Attachments(attachments) {} + + std::vector Attachments; }; struct FramebufferSpecification @@ -23,13 +52,17 @@ namespace Prism uint32_t Width = 1280; uint32_t Height = 720; glm::vec4 ClearColor; - FramebufferFormat Format; - uint32_t Samples = 1; + FramebufferAttachmentSpecification Attachments; + uint32_t Samples = 1; // multisampling + + // TODO: Temp, needs scale + bool NoResize = false; // SwapChainTarget = screen buffer (i.e. no framebuffer) bool SwapChainTarget = false; }; + class PRISM_API FrameBuffer : public RefCounted { public: @@ -44,10 +77,13 @@ namespace Prism virtual void Resize(uint32_t width, uint32_t height, bool forceReCreate = false) = 0; - virtual void BindTexture(uint32_t slot = 0) const = 0; + virtual void BindTexture(uint32_t attachmentIndex = 0, uint32_t slot = 0) const = 0; + + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; virtual RendererID GetRendererID() const = 0; - virtual RendererID GetColorAttachmentRendererID() const = 0; + virtual RendererID GetColorAttachmentRendererID(int index = 0) const = 0; virtual RendererID GetDepthAttachmentRendererID() const = 0; }; diff --git a/Prism/src/Prism/Renderer/Material.h b/Prism/src/Prism/Renderer/Material.h index 1b64433..27cd18d 100644 --- a/Prism/src/Prism/Renderer/Material.h +++ b/Prism/src/Prism/Renderer/Material.h @@ -21,7 +21,8 @@ namespace Prism { None = BIT(0), DepthTest = BIT(1), - Blend = BIT(2) + Blend = BIT(2), + TwoSided = BIT(3), }; class PRISM_API Material : public RefCounted @@ -38,6 +39,9 @@ namespace Prism uint32_t GetFlags() const { return m_MaterialFlags; } void SetFlag(MaterialFlag flag) { m_MaterialFlags |= (uint32_t)flag; } + Ref GetShader() { return m_Shader; } + + template T& Get(const std::string& name) { @@ -83,13 +87,14 @@ namespace Prism { Set(name, (const Ref&)texture); } + + ShaderResourceDeclaration* FindResourceDeclaration(const std::string& name); private: void AllocateStorage(); void OnShaderReloaded(); void BindTextures() const; ShaderUniformDeclaration* FindUniformDeclaration(const std::string& name); - ShaderResourceDeclaration* FindResourceDeclaration(const std::string& name); Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration); private: @@ -153,8 +158,8 @@ namespace Prism void Set(const std::string& name, const T& value) { const auto decl = m_Material->FindUniformDeclaration(name); - if (!decl) PM_CORE_WARN("Could not find uniform with name {0}", name); - auto& buffer = GetUniformBufferTarget(decl); + if (!decl) PM_CORE_ERROR("Could not find uniform with name {0}", name); + const auto& buffer = GetUniformBufferTarget(decl); buffer.Write(&value, decl->GetSize(), decl->GetOffset()); m_OverriddenValues.insert(name); @@ -207,6 +212,7 @@ namespace Prism template void Material::Set(const std::string& name, const T& value) { + auto decl = FindUniformDeclaration(name); PM_CORE_ASSERT(decl, "Could not find uniform with name x"); const auto& buffer = GetUniformBufferTarget(decl); diff --git a/Prism/src/Prism/Renderer/Mesh.cpp b/Prism/src/Prism/Renderer/Mesh.cpp index ca13b8c..9803ff4 100644 --- a/Prism/src/Prism/Renderer/Mesh.cpp +++ b/Prism/src/Prism/Renderer/Mesh.cpp @@ -50,7 +50,7 @@ namespace Prism virtual void write(const char* message) override { - PM_CORE_ERROR("Assimp: {0}", message); + PM_CORE_WARN("Assimp: {0}", message); } }; @@ -131,10 +131,11 @@ namespace Prism submesh.BaseIndex = indexCount; submesh.MaterialIndex = mesh->mMaterialIndex; submesh.IndexCount = mesh->mNumFaces * 3; + submesh.VertexCount = mesh->mNumVertices; // m_Submeshes.push_back(submesh); submesh.MeshName = mesh->mName.C_Str(); - vertexCount += mesh->mNumVertices; + vertexCount += submesh.VertexCount; indexCount += submesh.IndexCount; PM_CORE_ASSERT(mesh->HasPositions(), "Meshes require positions."); diff --git a/Prism/src/Prism/Renderer/Mesh.h b/Prism/src/Prism/Renderer/Mesh.h index 697d038..b54ca74 100644 --- a/Prism/src/Prism/Renderer/Mesh.h +++ b/Prism/src/Prism/Renderer/Mesh.h @@ -94,6 +94,7 @@ namespace Prism uint32_t BaseIndex; uint32_t MaterialIndex; uint32_t IndexCount; + uint32_t VertexCount; glm::mat4 Transform; AABB BoundingBox; @@ -126,6 +127,8 @@ namespace Prism const std::vector>& GetTextures() const { return m_Textures; } const std::string& GetFilePath() const { return m_FilePath; } + bool IsAnimated() const { return m_IsAnimated; } + std::vector GetTriangleCache(const uint32_t index) const { return m_TriangleCache.at(index); } private: void BoneTransform(float time); diff --git a/Prism/src/Prism/Renderer/Renderer.cpp b/Prism/src/Prism/Renderer/Renderer.cpp index 4bb0708..7cfabb9 100644 --- a/Prism/src/Prism/Renderer/Renderer.cpp +++ b/Prism/src/Prism/Renderer/Renderer.cpp @@ -135,31 +135,46 @@ namespace Prism void Renderer::SubmitQuad(Ref& material, const glm::mat4& transform) { bool depthTest = true; + bool cullFace = true; + if (material) { material->Bind(); depthTest = material->GetFlag(MaterialFlag::DepthTest); + cullFace = !material->GetFlag(MaterialFlag::TwoSided); auto shader = material->GetShader(); shader->SetMat4("u_Transform", transform); } + if (cullFace) + Submit([]() { glEnable(GL_CULL_FACE); }); + else + Submit([]() { glDisable(GL_CULL_FACE); }); + s_Data.m_FullscreenQuadVertexBuffer->Bind(); s_Data.m_FullscreenQuadPipeline->Bind(); s_Data.m_FullscreenQuadIndexBuffer->Bind(); - Renderer::DrawIndexed(6, PrimitiveType::Triangles, depthTest); + DrawIndexed(6, PrimitiveType::Triangles, depthTest); } void Renderer::SubmitFullscreenQuad(Ref material) { bool depthTest = true; + bool cullFace = true; if (material) { material->Bind(); depthTest = material->GetFlag(MaterialFlag::DepthTest); + cullFace = !material->GetFlag(MaterialFlag::TwoSided); } + if (cullFace) + Submit([]() { glEnable(GL_CULL_FACE); }); + else + Submit([]() { glDisable(GL_CULL_FACE); }); + s_Data.m_FullscreenQuadVertexBuffer->Bind(); s_Data.m_FullscreenQuadPipeline->Bind(); s_Data.m_FullscreenQuadIndexBuffer->Bind(); @@ -201,6 +216,36 @@ namespace Prism else glDisable(GL_DEPTH_TEST); + if (!material->GetFlag(MaterialFlag::TwoSided)) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + + glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex); + }); + } + } + + void Renderer::SubmitMeshWithShader(Ref mesh, const glm::mat4& transform, Ref shader) + { + mesh->m_VertexBuffer->Bind(); + mesh->m_Pipeline->Bind(); + mesh->m_IndexBuffer->Bind(); + + for (Submesh& submesh : mesh->m_Submeshes) + { + if (mesh->m_IsAnimated) + { + for (size_t i = 0; i < mesh->m_BoneTransforms.size(); i++) + { + std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); + shader->SetMat4(uniformName, mesh->m_BoneTransforms[i]); + } + } + shader->SetMat4("u_Transform", transform * submesh.Transform); + + Submit([submesh]() { glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex); }); } diff --git a/Prism/src/Prism/Renderer/Renderer.h b/Prism/src/Prism/Renderer/Renderer.h index 3eaf274..9cdd006 100644 --- a/Prism/src/Prism/Renderer/Renderer.h +++ b/Prism/src/Prism/Renderer/Renderer.h @@ -55,6 +55,7 @@ namespace Prism static void SubmitQuad(Ref& material, const glm::mat4& transform = glm::mat4(1.0f)); static void SubmitFullscreenQuad(Ref material); static void SubmitMesh(Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial = nullptr); + static void SubmitMeshWithShader(Ref mesh, const glm::mat4& transform, Ref shader); static void DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f)); static void DrawAABB(const Ref& mesh,const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f)); diff --git a/Prism/src/Prism/Renderer/Renderer2D.cpp b/Prism/src/Prism/Renderer/Renderer2D.cpp index 90ad4e2..a3a90cf 100644 --- a/Prism/src/Prism/Renderer/Renderer2D.cpp +++ b/Prism/src/Prism/Renderer/Renderer2D.cpp @@ -187,7 +187,7 @@ namespace Prism s_Data.QuadVertexBuffer->Bind(); s_Data.QuadPipeline->Bind(); s_Data.QuadIndexBuffer->Bind(); - Renderer::DrawIndexed(s_Data.QuadIndexCount, PrimitiveType::Triangles, s_Data.DepthTest); + Renderer::DrawIndexed(s_Data.QuadIndexCount, PrimitiveType::Triangles, false); s_Data.Stats.DrawCalls++; } diff --git a/Prism/src/Prism/Renderer/SceneEnvironment.cpp b/Prism/src/Prism/Renderer/SceneEnvironment.cpp new file mode 100644 index 0000000..b3e5a4a --- /dev/null +++ b/Prism/src/Prism/Renderer/SceneEnvironment.cpp @@ -0,0 +1,15 @@ +// +// Created by sfd on 26-1-1. +// + +#include "SceneEnvironment.h" +#include "SceneRenderer.h" + +namespace Prism +{ + Environment Environment::Load(const std::string& filepath) + { + auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath); + return { filepath, radiance, irradiance }; + } +} diff --git a/Prism/src/Prism/Renderer/SceneEnvironment.h b/Prism/src/Prism/Renderer/SceneEnvironment.h new file mode 100644 index 0000000..640fe1d --- /dev/null +++ b/Prism/src/Prism/Renderer/SceneEnvironment.h @@ -0,0 +1,24 @@ +// +// Created by sfd on 26-1-1. +// + +#ifndef SCENEENVIRONMENT_H +#define SCENEENVIRONMENT_H +#include "Texture.h" +#include "Prism/Core/Ref.h" + + +namespace Prism +{ + struct Environment + { + std::string FilePath; + Ref RadianceMap; + Ref IrradianceMap; + + static PRISM_API Environment Load(const std::string& filepath); + }; +} + + +#endif //SCENEENVIRONMENT_H diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index cbb81bd..75ec1d4 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -12,6 +12,7 @@ #include "Renderer2D.h" #include "RenderPass.h" #include "glad/glad.h" +#include "Prism/Core/Timer.h" namespace Prism { @@ -21,6 +22,8 @@ namespace Prism struct SceneInfo { SceneRendererCamera SceneCamera; + float SceneEnvironmentIntensity; + LightEnvironment SceneLightEnvironment; // Resources Ref SkyboxMaterial; @@ -30,9 +33,37 @@ namespace Prism Ref BRDFLUT; Ref CompositeShader; + Ref BloomBlurShader; + Ref BloomBlendShader; Ref GeoPass; Ref CompositePass; + Ref BloomBlurPass[2]; + Ref BloomBlendPass; + + Ref ShadowMapShader, ShadowMapAnimShader; + Ref ShadowMapRenderPass[4]; + float ShadowMapSize = 20.0f; + float LightDistance = 0.1f; + glm::mat4 LightMatrices[4]; + glm::mat4 LightViewMatrix; + float CascadeSplitLambda = 0.91f; + glm::vec4 CascadeSplits; + float CascadeFarPlaneOffset = 15.0f, CascadeNearPlaneOffset = -15.0f; + bool ShowCascades = false; + bool SoftShadows = true; + float LightSize = 0.5f; + float MaxShadowDistance = 200.0f; + float ShadowFade = 25.0f; + float CascadeTransitionFade = 1.0f; + bool CascadeFading = true; + + bool EnableBloom = false; + float BloomThreshold = 1.5f; + + glm::vec2 FocusPoint = { 0.5f, 0.5f }; + + RendererID ShadowMapSampler; struct DrawCommand { @@ -43,23 +74,34 @@ namespace Prism std::vector DrawList; std::vector SelectedMeshDrawList; std::vector ColliderDrawList; + std::vector ShadowPassDrawList; // Grid Ref GridMaterial; - Ref OutlineMaterial; + Ref OutlineMaterial, OutlineAnimMaterial; Ref ColliderMaterial; SceneRendererOptions Options; }; + struct SceneRendererStats + { + float ShadowPass = 0.0f; + float GeometryPass = 0.0f; + float CompositePass = 0.0f; + + Timer ShadowPassTimer; + Timer GeometryPassTimer; + Timer CompositePassTimer; + }; + static SceneRendererData s_Data; + static SceneRendererStats s_Stats; void SceneRenderer::Init() { FramebufferSpecification geoFramebufferSpec; - geoFramebufferSpec.Width = 1280; - geoFramebufferSpec.Height = 720; - geoFramebufferSpec.Format = FramebufferFormat::RGBA16F; + geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::Depth }; geoFramebufferSpec.Samples = 8; geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; @@ -68,16 +110,34 @@ namespace Prism s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec); FramebufferSpecification compFramebufferSpec; - compFramebufferSpec.Width = 1280; - compFramebufferSpec.Height = 720; - compFramebufferSpec.Format = FramebufferFormat::RGBA8; - compFramebufferSpec.ClearColor = { 0.5f, 0.1f, 0.1f, 1.0f }; + compFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 }; + compFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; RenderPassSpecification compRenderPassSpec; compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec); s_Data.CompositePass = RenderPass::Create(compRenderPassSpec); + FramebufferSpecification bloomBlurFramebufferSpec; + bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; + bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + + RenderPassSpecification bloomBlurRenderPassSpec; + bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec); + s_Data.BloomBlurPass[0] = RenderPass::Create(bloomBlurRenderPassSpec); + bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec); + s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec); + + FramebufferSpecification bloomBlendFramebufferSpec; + bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 }; + bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + + RenderPassSpecification bloomBlendRenderPassSpec; + bloomBlendRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlendFramebufferSpec); + s_Data.BloomBlendPass = RenderPass::Create(bloomBlendRenderPassSpec); + s_Data.CompositeShader = Shader::Create("assets/shaders/SceneComposite.glsl"); + s_Data.BloomBlurShader = Shader::Create("assets/shaders/BloomBlur.glsl"); + s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl"); s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga"); @@ -98,6 +158,41 @@ namespace Prism const auto colliderShader = Shader::Create("assets/shaders/Collider.glsl"); s_Data.ColliderMaterial = MaterialInstance::Create(Material::Create(colliderShader)); s_Data.ColliderMaterial->SetFlag(MaterialFlag::DepthTest, false); + + auto outlineAnimShader = Shader::Create("assets/shaders/Outline_Anim.glsl"); + s_Data.OutlineAnimMaterial = MaterialInstance::Create(Material::Create(outlineAnimShader)); + s_Data.OutlineAnimMaterial->SetFlag(MaterialFlag::DepthTest, false); + + // Shadow Map + s_Data.ShadowMapShader = Shader::Create("assets/shaders/ShadowMap.glsl"); + s_Data.ShadowMapAnimShader = Shader::Create("assets/shaders/ShadowMap_Anim.glsl"); + + FramebufferSpecification shadowMapFramebufferSpec; + shadowMapFramebufferSpec.Width = 4096; + shadowMapFramebufferSpec.Height = 4096; + shadowMapFramebufferSpec.Attachments = { FramebufferTextureFormat::DEPTH32F }; + shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + shadowMapFramebufferSpec.NoResize = true; + + // 4 cascades + for (int i = 0; i < 4; i++) + { + RenderPassSpecification shadowMapRenderPassSpec; + shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); + s_Data.ShadowMapRenderPass[i] = RenderPass::Create(shadowMapRenderPassSpec); + } + + Renderer::Submit([]() + { + glGenSamplers(1, &s_Data.ShadowMapSampler); + + // Setup the shadowmap depth sampler + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + }); + } void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height) @@ -115,7 +210,9 @@ namespace Prism s_Data.SceneData.SceneCamera = camera; s_Data.SceneData.SkyboxMaterial = scene->m_SkyboxMaterial; s_Data.SceneData.SceneEnvironment = scene->m_Environment; + s_Data.SceneData.SceneEnvironmentIntensity = scene->m_EnvironmentIntensity; s_Data.SceneData.ActiveLight = scene->m_Light; + s_Data.SceneData.SceneLightEnvironment = scene->m_LightEnvironment; } void SceneRenderer::EndScene() @@ -132,11 +229,13 @@ namespace Prism // TODO: Culling, sorting, etc. s_Data.DrawList.push_back({ mesh, overrideMaterial, transform }); + s_Data.ShadowPassDrawList.push_back({ mesh, overrideMaterial, transform }); } void SceneRenderer::SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform) { s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform }); + s_Data.ShadowPassDrawList.push_back({ mesh, nullptr, transform }); } void SceneRenderer::SubmitColliderMesh(const BoxColliderComponent& component, const glm::mat4& parentTransform) @@ -156,7 +255,8 @@ namespace Prism void SceneRenderer::SubmitColliderMesh(const MeshColliderComponent& component, const glm::mat4& parentTransform) { - s_Data.ColliderDrawList.push_back({ component.ProcessedMesh, nullptr, parentTransform }); + for (const auto debugMesh : component.ProcessedMeshes) + s_Data.ColliderDrawList.push_back({ debugMesh, nullptr, parentTransform }); } @@ -237,6 +337,11 @@ namespace Prism return nullptr; } + void SceneRenderer::SetFocusPoint(const glm::vec2& point) + { + s_Data.FocusPoint = point; + } + uint32_t SceneRenderer::GetFinalColorBufferRendererID() { return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(); @@ -247,16 +352,54 @@ namespace Prism return s_Data.Options; } + void SceneRenderer::FlushDrawList() { PM_CORE_ASSERT(!s_Data.ActiveScene); - GeometryPass(); - CompositePass(); + memset(&s_Stats, 0, sizeof(SceneRendererStats)); + + { + Renderer::Submit([]() + { + s_Stats.ShadowPassTimer.Reset(); + }); + ShadowMapPass(); + Renderer::Submit([] + { + s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis(); + }); + } + { + Renderer::Submit([]() + { + s_Stats.GeometryPassTimer.Reset(); + }); + GeometryPass(); + Renderer::Submit([] + { + s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis(); + }); + } + { + Renderer::Submit([]() + { + s_Stats.CompositePassTimer.Reset(); + }); + + CompositePass(); + Renderer::Submit([] + { + s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis(); + }); + + // BloomBlurPass(); + } s_Data.DrawList.clear(); s_Data.SelectedMeshDrawList.clear(); s_Data.ColliderDrawList.clear(); + s_Data.ShadowPassDrawList.clear(); s_Data.SceneData = {}; } @@ -283,21 +426,43 @@ namespace Prism }); } - 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]; + const auto& sceneCamera = s_Data.SceneData.SceneCamera; + + const auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix; + const glm::vec3 cameraPosition = glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)[3]; // TODO: Negate instead + // Skybox auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader(); s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(viewProjection)); + s_Data.SceneData.SkyboxMaterial->Set("u_SkyIntensity", s_Data.SceneData.SceneEnvironmentIntensity); // s_Data.SceneInfo.EnvironmentIrradianceMap->Bind(0); Renderer::SubmitFullscreenQuad(s_Data.SceneData.SkyboxMaterial); + const float aspectRatio = (float)s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetWidth() / (float)s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetHeight(); + float frustumSize = 2.0f * sceneCamera.Near * glm::tan(sceneCamera.FOV * 0.5f) * aspectRatio; + // Render entities for (auto& dc : s_Data.DrawList) { auto baseMaterial = dc.mesh->GetMaterial(); baseMaterial->Set("u_ViewProjectionMatrix", viewProjection); + baseMaterial->Set("u_ViewMatrix", sceneCamera.ViewMatrix); baseMaterial->Set("u_CameraPosition", cameraPosition); + baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]); + baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]); + baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]); + baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]); + baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades); + baseMaterial->Set("u_LightView", s_Data.LightViewMatrix); + baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits); + baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows); + baseMaterial->Set("u_LightSize", s_Data.LightSize); + baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance); + baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade); + baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading); + baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade); + baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity); // Environment (TODO: don't do this per mesh) baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment.RadianceMap); @@ -305,12 +470,109 @@ namespace Prism 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 directionalLight = s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]; + baseMaterial->Set("u_DirectionalLights", directionalLight); - auto overrideMaterial = nullptr; // dc.Material; + auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMapTexture"); + if (rd) + { + auto reg = rd->GetRegister(); + + auto tex = s_Data.ShadowMapRenderPass[0]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + auto tex1 = s_Data.ShadowMapRenderPass[1]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + auto tex2 = s_Data.ShadowMapRenderPass[2]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + auto tex3 = s_Data.ShadowMapRenderPass[3]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + + Renderer::Submit([reg, tex, tex1, tex2, tex3]() mutable + { + // 4 cascades + glBindTextureUnit(reg, tex); + glBindSampler(reg++, s_Data.ShadowMapSampler); + + glBindTextureUnit(reg, tex1); + glBindSampler(reg++, s_Data.ShadowMapSampler); + + glBindTextureUnit(reg, tex2); + glBindSampler(reg++, s_Data.ShadowMapSampler); + + glBindTextureUnit(reg, tex3); + glBindSampler(reg++, s_Data.ShadowMapSampler); + }); + } + + + constexpr auto overrideMaterial = nullptr; // dc.Material; 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_ViewMatrix", sceneCamera.ViewMatrix); + baseMaterial->Set("u_CameraPosition", cameraPosition); + baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits); + baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades); + baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows); + baseMaterial->Set("u_LightSize", s_Data.LightSize); + baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance); + baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade); + baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading); + baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade); + baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity); + + // 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); + + baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]); + baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]); + baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]); + baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]); + + // Set lights (TODO: move to light environment and don't do per mesh) + baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]); + + auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMapTexture"); + if (rd) + { + auto reg = rd->GetRegister(); + + auto tex = s_Data.ShadowMapRenderPass[0]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + auto tex1 = s_Data.ShadowMapRenderPass[1]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + auto tex2 = s_Data.ShadowMapRenderPass[2]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + auto tex3 = s_Data.ShadowMapRenderPass[3]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + + Renderer::Submit([reg, tex, tex1, tex2, tex3]() mutable + { + // 4 cascades + glBindTextureUnit(reg, tex); + glBindSampler(reg++, s_Data.ShadowMapSampler); + + glBindTextureUnit(reg, tex1); + glBindSampler(reg++, s_Data.ShadowMapSampler); + + glBindTextureUnit(reg, tex2); + glBindSampler(reg++, s_Data.ShadowMapSampler); + + glBindTextureUnit(reg, tex3); + glBindSampler(reg++, s_Data.ShadowMapSampler); + }); + } + + constexpr auto overrideMaterial = nullptr; // dc.Material; + Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial); + } + if (outline) { @@ -327,9 +589,11 @@ namespace Prism // Draw outline here s_Data.OutlineMaterial->Set("u_ViewProjection", viewProjection); + s_Data.OutlineAnimMaterial->Set("u_ViewProjection", viewProjection); for (auto& dc : s_Data.SelectedMeshDrawList) { - Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + // Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.mesh->IsAnimated() ? s_Data.OutlineAnimMaterial : s_Data.OutlineMaterial); } Renderer::Submit([]() @@ -339,7 +603,8 @@ namespace Prism }); for (auto& dc : s_Data.SelectedMeshDrawList) { - Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + // Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.mesh->IsAnimated() ? s_Data.OutlineAnimMaterial : s_Data.OutlineMaterial); } Renderer::Submit([]() @@ -414,12 +679,303 @@ namespace Prism void SceneRenderer::CompositePass() { + auto& compositeBuffer = s_Data.CompositePass->GetSpecification().TargetFramebuffer; + Renderer::BeginRenderPass(s_Data.CompositePass); s_Data.CompositeShader->Bind(); 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.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight())); + // s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint); + s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); + // s_Data.CompositeShader->SetFloat("u_BloomThreshold", s_Data.BloomThreshold); + s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); + Renderer::Submit([]() + { + glBindTextureUnit(1, s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID()); + }); + Renderer::SubmitFullscreenQuad(nullptr); Renderer::EndRenderPass(); } + + void SceneRenderer::BloomBlurPass() + { + int amount = 10; + int index = 0; + + int horizontalCounter = 0, verticalCounter = 0; + for (int i = 0; i < amount; i++) + { + index = i % 2; + Renderer::BeginRenderPass(s_Data.BloomBlurPass[index]); + s_Data.BloomBlurShader->Bind(); + s_Data.BloomBlurShader->SetBool("u_Horizontal", index); + if (index) + horizontalCounter++; + else + verticalCounter++; + if (i > 0) + { + auto fb = s_Data.BloomBlurPass[1 - index]->GetSpecification().TargetFramebuffer; + fb->BindTexture(); + } + else + { + auto fb = s_Data.CompositePass->GetSpecification().TargetFramebuffer; + auto id = fb->GetColorAttachmentRendererID(1); + Renderer::Submit([id]() + { + glBindTextureUnit(0, id); + }); + } + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } + + // Composite bloom + { + Renderer::BeginRenderPass(s_Data.BloomBlendPass); + s_Data.BloomBlendShader->Bind(); + s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); + s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom); + + s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0); + s_Data.BloomBlurPass[index]->GetSpecification().TargetFramebuffer->BindTexture(1); + + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } + } + + struct FrustumBounds + { + float r, l, b, t, f, n; + }; + + struct CascadeData + { + glm::mat4 ViewProj; + glm::mat4 View; + float SplitDepth; + }; + + static void CalculateCascades(CascadeData* cascades, const glm::vec3& lightDirection) + { + // FrustumBounds frustumBounds[3]; + + auto& sceneCamera = s_Data.SceneData.SceneCamera; + auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix; + + constexpr int SHADOW_MAP_CASCADE_COUNT = 4; + float cascadeSplits[SHADOW_MAP_CASCADE_COUNT]; + + // TODO: less hard-coding! + float nearClip = 0.1f; + float farClip = 1000.0f; + float clipRange = farClip - nearClip; + + float minZ = nearClip; + float maxZ = nearClip + clipRange; + + float range = maxZ - minZ; + float ratio = maxZ / minZ; + + // Calculate split depths based on view camera frustum + // Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html + for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) + { + float p = (i + 1) / static_cast(SHADOW_MAP_CASCADE_COUNT); + float log = minZ * std::pow(ratio, p); + float uniform = minZ + range * p; + float d = s_Data.CascadeSplitLambda * (log - uniform) + uniform; + cascadeSplits[i] = (d - nearClip) / clipRange; + } + + cascadeSplits[3] = 0.3f; + + // Manually set cascades here + // cascadeSplits[0] = 0.05f; + // cascadeSplits[1] = 0.15f; + // cascadeSplits[2] = 0.3f; + // cascadeSplits[3] = 1.0f; + + // Calculate orthographic projection matrix for each cascade + float lastSplitDist = 0.0; + for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) + { + float splitDist = cascadeSplits[i]; + + glm::vec3 frustumCorners[8] = + { + glm::vec3(-1.0f, 1.0f, -1.0f), + glm::vec3( 1.0f, 1.0f, -1.0f), + glm::vec3( 1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, 1.0f, 1.0f), + glm::vec3( 1.0f, 1.0f, 1.0f), + glm::vec3( 1.0f, -1.0f, 1.0f), + glm::vec3(-1.0f, -1.0f, 1.0f), + }; + + // Project frustum corners into world space + glm::mat4 invCam = glm::inverse(viewProjection); + for (uint32_t i = 0; i < 8; i++) + { + glm::vec4 invCorner = invCam * glm::vec4(frustumCorners[i], 1.0f); + frustumCorners[i] = invCorner / invCorner.w; + } + + for (uint32_t i = 0; i < 4; i++) + { + glm::vec3 dist = frustumCorners[i + 4] - frustumCorners[i]; + frustumCorners[i + 4] = frustumCorners[i] + (dist * splitDist); + frustumCorners[i] = frustumCorners[i] + (dist * lastSplitDist); + } + + // Get frustum center + glm::vec3 frustumCenter = glm::vec3(0.0f); + for (uint32_t i = 0; i < 8; i++) + frustumCenter += frustumCorners[i]; + + frustumCenter /= 8.0f; + + //frustumCenter *= 0.01f; + + float radius = 0.0f; + for (uint32_t i = 0; i < 8; i++) + { + float distance = glm::length(frustumCorners[i] - frustumCenter); + radius = glm::max(radius, distance); + } + radius = std::ceil(radius * 16.0f) / 16.0f; + + glm::vec3 maxExtents = glm::vec3(radius); + glm::vec3 minExtents = -maxExtents; + + glm::vec3 lightDir = -lightDirection; + glm::mat4 lightViewMatrix = glm::lookAt(frustumCenter - lightDir * -minExtents.z, frustumCenter, glm::vec3(0.0f, 0.0f, 1.0f)); + glm::mat4 lightOrthoMatrix = glm::ortho(minExtents.x, maxExtents.x, minExtents.y, maxExtents.y, 0.0f + s_Data.CascadeNearPlaneOffset, maxExtents.z - minExtents.z + s_Data.CascadeFarPlaneOffset); + + // Store split distance and matrix in cascade + cascades[i].SplitDepth = (nearClip + splitDist * clipRange) * -1.0f; + cascades[i].ViewProj = lightOrthoMatrix * lightViewMatrix; + cascades[i].View = lightViewMatrix; + + lastSplitDist = cascadeSplits[i]; + } + } + + void SceneRenderer::ShadowMapPass() + { + const auto& directionalLights = s_Data.SceneData.SceneLightEnvironment.DirectionalLights; + if (directionalLights[0].Multiplier == 0.0f || !directionalLights[0].CastShadows) + { + for (int i = 0; i < 4; i++) + { + // Clear shadow maps + Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass[i]); + Renderer::EndRenderPass(); + } + return; + } + + CascadeData cascades[4]; + CalculateCascades(cascades, directionalLights[0].Direction); + s_Data.LightViewMatrix = cascades[0].View; + + Renderer::Submit([]() + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + }); + + for (int i = 0; i < 4; i++) + { + s_Data.CascadeSplits[i] = cascades[i].SplitDepth; + + Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass[i]); + + glm::mat4 shadowMapVP = cascades[i].ViewProj; + // s_Data.ShadowMapShader->SetMat4("u_ViewProjection", shadowMapVP); + + static glm::mat4 scaleBiasMatrix = glm::scale(glm::mat4(1.0f), { 0.5f, 0.5f, 0.5f }) * glm::translate(glm::mat4(1.0f), { 1, 1, 1 }); + s_Data.LightMatrices[i] = scaleBiasMatrix * cascades[i].ViewProj; + + + // Render entities + for (auto& dc : s_Data.ShadowPassDrawList) + { + Ref shader = dc.mesh->IsAnimated() ? s_Data.ShadowMapAnimShader : s_Data.ShadowMapShader; + shader->SetMat4("u_ViewProjection", shadowMapVP); + Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader); + // Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, s_Data.ShadowMapShader); + } + + Renderer::EndRenderPass(); + } + } + + void SceneRenderer::OnImGuiRender() + { + ImGui::Begin("Scene Renderer"); + + if (UI::BeginTreeNode("Shadows")) + { + UI::BeginPropertyGrid(); + UI::Property("Soft Shadows", s_Data.SoftShadows); + UI::Property("Light Size", s_Data.LightSize, 0.01f); + UI::Property("Max Shadow Distance", s_Data.MaxShadowDistance, 1.0f); + UI::Property("Shadow Fade", s_Data.ShadowFade, 5.0f); + UI::EndPropertyGrid(); + if (UI::BeginTreeNode("Cascade Settings")) + { + UI::BeginPropertyGrid(); + UI::Property("Show Cascades", s_Data.ShowCascades); + UI::Property("Cascade Fading", s_Data.CascadeFading); + UI::Property("Cascade Transition Fade", s_Data.CascadeTransitionFade, 0.05f, 0.0f, FLT_MAX); + UI::Property("Cascade Split", s_Data.CascadeSplitLambda, 0.01f); + UI::Property("CascadeNearPlaneOffset", s_Data.CascadeNearPlaneOffset, 0.1f, -FLT_MAX, 0.0f); + UI::Property("CascadeFarPlaneOffset", s_Data.CascadeFarPlaneOffset, 0.1f, 0.0f, FLT_MAX); + UI::EndPropertyGrid(); + UI::EndTreeNode(); + } + if (UI::BeginTreeNode("Shadow Map", false)) + { + static int cascadeIndex = 0; + auto fb = s_Data.ShadowMapRenderPass[cascadeIndex]->GetSpecification().TargetFramebuffer; + auto id = fb->GetDepthAttachmentRendererID(); + + float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f + UI::BeginPropertyGrid(); + UI::PropertySlider("Cascade Index", cascadeIndex, 0, 3); + UI::EndPropertyGrid(); + ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 }); + UI::EndTreeNode(); + } + + UI::EndTreeNode(); + } + + if (UI::BeginTreeNode("Bloom")) + { + UI::BeginPropertyGrid(); + UI::Property("Bloom", s_Data.EnableBloom); + UI::Property("Bloom threshold", s_Data.BloomThreshold, 0.05f); + UI::EndPropertyGrid(); + + auto fb = s_Data.BloomBlurPass[0]->GetSpecification().TargetFramebuffer; + auto id = fb->GetColorAttachmentRendererID(); + + float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f + float w = size; + float h = w / ((float)fb->GetWidth() / (float)fb->GetHeight()); + ImGui::Image((ImTextureID)id, { w, h }, { 0, 1 }, { 1, 0 }); + UI::EndTreeNode(); + } + + + ImGui::End(); + } } diff --git a/Prism/src/Prism/Renderer/SceneRenderer.h b/Prism/src/Prism/Renderer/SceneRenderer.h index 26bc4bd..f36fdc5 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.h +++ b/Prism/src/Prism/Renderer/SceneRenderer.h @@ -23,6 +23,9 @@ namespace Prism { Prism::Camera Camera; glm::mat4 ViewMatrix; + + float Near, Far; + float FOV; }; class PRISM_API SceneRenderer @@ -48,15 +51,21 @@ namespace Prism static Ref GetFinalRenderPass(); static Ref GetFinalColorBuffer(); + static void SetFocusPoint(const glm::vec2& point); + // TODO: Temp static uint32_t GetFinalColorBufferRendererID(); static SceneRendererOptions& GetOptions(); + static void OnImGuiRender(); private: static void FlushDrawList(); static void GeometryPass(); static void CompositePass(); + static void BloomBlurPass(); + + static void ShadowMapPass(); }; } diff --git a/Prism/src/Prism/Renderer/Shader.h b/Prism/src/Prism/Renderer/Shader.h index 43dcedf..a5eb9c2 100644 --- a/Prism/src/Prism/Renderer/Shader.h +++ b/Prism/src/Prism/Renderer/Shader.h @@ -110,9 +110,11 @@ namespace Prism virtual void Bind() = 0; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0; - virtual void SetFloat(const std::string& name, float value) = 0; - virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0; virtual void SetInt(const std::string& name, int value) = 0; + virtual void SetBool(const std::string& name, bool value) = 0; + virtual void SetFloat(const std::string& name, float value) = 0; + virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0; + virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0; virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0; virtual void SetMat4FromRenderThread(const std::string& name, const glm::mat4& value, bool bind = true) = 0; diff --git a/Prism/src/Prism/Scene/Components.h b/Prism/src/Prism/Scene/Components.h index 92a6239..960bc65 100644 --- a/Prism/src/Prism/Scene/Components.h +++ b/Prism/src/Prism/Scene/Components.h @@ -18,6 +18,8 @@ #include #include +#include "Prism/Renderer/SceneEnvironment.h" + namespace Prism { struct IDComponent @@ -222,7 +224,9 @@ namespace Prism struct MeshColliderComponent { Ref CollisionMesh; - Ref ProcessedMesh; + std::vector> ProcessedMeshes; + bool IsConvex = false; + bool IsTrigger = false; MeshColliderComponent() = default; @@ -235,6 +239,29 @@ namespace Prism operator Ref() { return CollisionMesh; } }; + // Lights + + // TODO: Move to renderer + enum class LightType + { + None = 0, Directional = 1, Point = 2, Spot = 3 + }; + + struct DirectionalLightComponent + { + glm::vec3 Radiance = { 1.0f, 1.0f, 1.0f }; + float Intensity = 1.0f; + bool CastShadows = true; + bool SoftShadows = true; + float LightSize = 0.5f; // For PCSS + }; + + struct SkyLightComponent + { + Environment SceneEnvironment; + float Intensity = 1.0f; + float Angle = 0.0f; + }; } diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp index 9ef56a5..4ae9af6 100644 --- a/Prism/src/Prism/Scene/Scene.cpp +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -18,6 +18,8 @@ #define PX_PHYSX_STATIC_LIB #include +#include "Prism/Core/Input.h" + namespace Prism { @@ -106,11 +108,6 @@ namespace Prism ScriptEngine::OnScriptComponentDestroyed(sceneID, entityID); } - Environment Environment::Load(const std::string& filepath) - { - auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath); - return { filepath, radiance, irradiance }; - } Scene::Scene(const std::string& debugName, const bool isEditorScene) : m_SceneID(++s_SceneIDCounter), m_DebugName(debugName) @@ -160,16 +157,6 @@ namespace Prism void Scene::OnUpdate(TimeStep ts) { - // Update all entities - { - const auto view = m_Registry.view(); - for (const auto entity : view) - { - Entity e = { entity, this }; - if (ScriptEngine::ModuleExists(e.GetComponent().ModuleName)) - ScriptEngine::OnUpdateEntity(e, ts); - } - } // Box2D physics @@ -202,6 +189,17 @@ namespace Prism } Physics3D::Simulate(ts); + + // Update all entities + { + const auto view = m_Registry.view(); + for (const auto entity : view) + { + Entity e = { entity, this }; + if (ScriptEngine::ModuleExists(e.GetComponent().ModuleName)) + ScriptEngine::OnUpdateEntity(e, ts); + } + } } void Scene::OnRenderRuntime(TimeStep ts) @@ -218,6 +216,39 @@ namespace Prism SceneCamera& camera = cameraEntity.GetComponent(); camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight); + // Process lights + { + m_LightEnvironment = LightEnvironment(); + auto lights = m_Registry.group(entt::get); + uint32_t directionalLightIndex = 0; + for (auto entity : lights) + { + auto [transformComponent, lightComponent] = lights.get(entity); + glm::vec3 direction = -glm::normalize(glm::mat3(transformComponent.GetTransform()) * glm::vec3(1.0f)); + m_LightEnvironment.DirectionalLights[directionalLightIndex++] = + { + direction, + lightComponent.Radiance, + lightComponent.Intensity, + lightComponent.CastShadows + }; + } + } + + // TODO: only one sky light at the moment! + { + m_Environment = Environment(); + auto lights = m_Registry.group(entt::get); + for (auto entity : lights) + { + auto [transformComponent, skyLightComponent] = lights.get(entity); + m_Environment = skyLightComponent.SceneEnvironment; + m_EnvironmentIntensity = skyLightComponent.Intensity; + SetSkybox(m_Environment.RadianceMap); + } + } + + m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); auto group = m_Registry.group(entt::get); @@ -236,6 +267,7 @@ namespace Prism SceneRenderer::EndScene(); ///////////////////////////////////////////////////////////////////// + #if 0 // Render all sprites Renderer2D::BeginScene(*camera); @@ -260,10 +292,44 @@ namespace Prism ///////////////////////////////////////////////////////////////////// // RENDER 3D SCENE // ///////////////////////////////////////////////////////////////////// + + // Process lights + { + m_LightEnvironment = LightEnvironment(); + auto lights = m_Registry.group(entt::get); + uint32_t directionalLightIndex = 0; + for (auto entity : lights) + { + auto [transformComponent, lightComponent] = lights.get(entity); + glm::vec3 direction = -glm::normalize(glm::mat3(transformComponent.GetTransform()) * glm::vec3(1.0f)); + m_LightEnvironment.DirectionalLights[directionalLightIndex++] = + { + direction, + lightComponent.Radiance, + lightComponent.Intensity, + lightComponent.CastShadows + }; + } + } + + // TODO: only one sky light at the moment! + { + m_Environment = Environment(); + auto lights = m_Registry.group(entt::get); + for (auto entity : lights) + { + auto [transformComponent, skyLightComponent] = lights.get(entity); + m_Environment = skyLightComponent.SceneEnvironment; + m_EnvironmentIntensity = skyLightComponent.Intensity; + SetSkybox(m_Environment.RadianceMap); + } + } + m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); auto group = m_Registry.group(entt::get); - SceneRenderer::BeginScene(this, { static_cast(editorCamera), editorCamera.GetViewMatrix() }); + // SceneRenderer::BeginScene(this, { static_cast(editorCamera), editorCamera.GetViewMatrix() }); + SceneRenderer::BeginScene(this, { static_cast(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f }); // TODO: real values for (auto entity : group) { const auto& [transformComponent, meshComponent] = group.get(entity); @@ -474,6 +540,8 @@ namespace Prism void Scene::OnRuntimeStop() { + Input::SetCursorMode(CursorMode::Normal); + Physics3D::DestroyScene(); delete[] m_Physics3DBodyEntityBuffer; @@ -487,12 +555,6 @@ namespace Prism m_ViewportHeight = height; } - void Scene::SetEnvironment(const Environment& environment) - { - m_Environment = environment; - SetSkybox(environment.RadianceMap); - } - void Scene::SetSkybox(const Ref& skybox) { m_SkyboxTexture = skybox; @@ -580,6 +642,8 @@ namespace Prism CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); + CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); CopyComponentIfExists(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry); @@ -629,6 +693,8 @@ namespace Prism CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); + CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); CopyComponent(target->m_Registry, m_Registry, enttMap); diff --git a/Prism/src/Prism/Scene/Scene.h b/Prism/src/Prism/Scene/Scene.h index bfbad15..b0c140a 100644 --- a/Prism/src/Prism/Scene/Scene.h +++ b/Prism/src/Prism/Scene/Scene.h @@ -13,19 +13,11 @@ #include "Prism/Core/UUID.h" #include "Prism/Editor/EditorCamera.h" +#include "Prism/Renderer/SceneEnvironment.h" namespace Prism { - struct PRISM_API Environment - { - std::string FilePath; - Ref RadianceMap; - Ref IrradianceMap; - - static Environment Load(const std::string& filepath); - }; - struct Light { glm::vec3 Direction{-0.314f, -0.941f, -0.209f}; @@ -34,6 +26,23 @@ namespace Prism float Multiplier = 1.0f; }; + struct DirectionalLight + { + glm::vec3 Direction = { 0.0f, 0.0f, 0.0f }; + glm::vec3 Radiance = { 0.0f, 0.0f, 0.0f }; + float Multiplier = 0.0f; + + // C++ only + bool CastShadows = true; + }; + + struct LightEnvironment + { + DirectionalLight DirectionalLights[4]; + }; + + + class Entity; using EntityMap = std::unordered_map; @@ -56,7 +65,6 @@ namespace Prism void SetViewportSize(uint32_t width, uint32_t height); - void SetEnvironment(const Environment& environment); const Environment& GetEnvironment() const { return m_Environment; } void SetSkybox(const Ref& skybox); @@ -115,9 +123,12 @@ namespace Prism Light m_Light; float m_LightMultiplier = 0.3f; + LightEnvironment m_LightEnvironment; + bool m_IsPlaying = false; Environment m_Environment; + float m_EnvironmentIntensity = 1.0f; Ref m_SkyboxTexture; Ref m_SkyboxMaterial; diff --git a/Prism/src/Prism/Scene/SceneSerializer.cpp b/Prism/src/Prism/Scene/SceneSerializer.cpp index e38a28e..492cbc9 100644 --- a/Prism/src/Prism/Scene/SceneSerializer.cpp +++ b/Prism/src/Prism/Scene/SceneSerializer.cpp @@ -244,6 +244,33 @@ namespace Prism out << YAML::EndMap; // MeshComponent } + if (entity.HasComponent()) + { + out << YAML::Key << "DirectionalLightComponent"; + out << YAML::BeginMap; // DirectionalLightComponent + + auto& directionalLightComponent = entity.GetComponent(); + out << YAML::Key << "Radiance" << YAML::Value << directionalLightComponent.Radiance; + out << YAML::Key << "CastShadows" << YAML::Value << directionalLightComponent.CastShadows; + out << YAML::Key << "SoftShadows" << YAML::Value << directionalLightComponent.SoftShadows; + out << YAML::Key << "LightSize" << YAML::Value << directionalLightComponent.LightSize; + + out << YAML::EndMap; // DirectionalLightComponent + } + + if (entity.HasComponent()) + { + out << YAML::Key << "SkyLightComponent"; + out << YAML::BeginMap; // SkyLightComponent + + auto& skyLightComponent = entity.GetComponent(); + out << YAML::Key << "EnvironmentAssetPath" << YAML::Value << skyLightComponent.SceneEnvironment.FilePath; + out << YAML::Key << "Intensity" << YAML::Value << skyLightComponent.Intensity; + out << YAML::Key << "Angle" << YAML::Value << skyLightComponent.Angle; + + out << YAML::EndMap; // SkyLightComponent + } + if (entity.HasComponent()) { out << YAML::Key << "CameraComponent"; @@ -394,6 +421,7 @@ namespace Prism auto meshColliderComponent = entity.GetComponent(); out << YAML::Key << "AssetPath" << YAML::Value << meshColliderComponent.CollisionMesh->GetFilePath(); + out << YAML::Key << "IsConvex" << YAML::Value << meshColliderComponent.IsConvex; out << YAML::Key << "IsTrigger" << YAML::Value << meshColliderComponent.IsTrigger; out << YAML::EndMap; // MeshColliderComponent @@ -489,7 +517,7 @@ namespace Prism if (environment) { std::string envPath = environment["AssetPath"].as(); - m_Scene->SetEnvironment(Environment::Load(envPath)); + // m_Scene->SetEnvironment(Environment::Load(envPath)); auto lightNode = environment["Light"]; if (lightNode) @@ -606,7 +634,7 @@ namespace Prism if (auto meshComponent = entity["MeshComponent"]) { - std::string meshPath = meshComponent["AssetPath"].as(); + const std::string meshPath = meshComponent["AssetPath"].as(); // TEMP (because script creates mesh component...) if (!deserializedEntity.HasComponent()) deserializedEntity.AddComponent(Ref::Create(meshPath)); @@ -614,6 +642,25 @@ namespace Prism PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath); } + if (auto directionalLightComponent = entity["DirectionalLightComponent"]) + { + auto& component = deserializedEntity.AddComponent(); + component.Radiance = directionalLightComponent["Radiance"].as(); + component.CastShadows = directionalLightComponent["CastShadows"].as(); + component.SoftShadows = directionalLightComponent["SoftShadows"].as(); + component.LightSize = directionalLightComponent["LightSize"].as(); + } + + if (auto skyLightComponent = entity["SkyLightComponent"]) + { + auto& component = deserializedEntity.AddComponent(); + const std::string env = skyLightComponent["EnvironmentAssetPath"].as(); + if (!env.empty()) + component.SceneEnvironment = Environment::Load(env); + component.Intensity = skyLightComponent["Intensity"].as(); + component.Angle = skyLightComponent["Angle"].as(); + } + if (auto cameraComponent = entity["CameraComponent"]) { auto& component = deserializedEntity.AddComponent(); @@ -712,8 +759,12 @@ namespace Prism { auto meshPath = meshColliderComponent["AssetPath"].as(); auto& component = deserializedEntity.AddComponent(Ref::Create(meshPath)); + component.IsConvex = meshColliderComponent["IsConvex"] ? meshColliderComponent["IsConvex"].as() : false; component.IsTrigger = meshColliderComponent["IsTrigger"] ? meshColliderComponent["IsTrigger"].as() : false; - PhysicsWrappers::CreateConvexMesh(component); + if (component.IsConvex) + PhysicsWrappers::CreateConvexMesh(component); + else + PhysicsWrappers::CreateTriangleMesh(component); PM_CORE_INFO(" Mesh Collider Asset Path: {0}", meshPath); }