add directional light component and skylight component, add PCSS and hard shadow

This commit is contained in:
2026-01-02 22:46:29 +08:00
parent abf0a65bd6
commit 9e1474e643
55 changed files with 3032 additions and 1121 deletions

View File

@ -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<Scene>::Create("untitled Scene", true);
ScriptEngine::SetSceneContext(m_EditorScene);
m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(m_EditorScene);
m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1));
m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1));
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<MeshComponent>())
{
if (auto& meshComp = entity.GetComponent<MeshComponent>(); meshComp.Mesh)
{
selection.Mesh = &meshComp.Mesh->GetSubmeshes()[0];
}
if (auto mesh = entity.GetComponent<MeshComponent>().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<Scene>::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<Scene> newScene = Ref<Scene>::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<Scene> newScene = Ref<Scene>::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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Pi; d+=Pi/Directions)
{
for(float i=1.0/Quality; i<=1.0; i+=1.0/Quality)
{
result += texture( u_Texture, uv+vec2(cos(d),sin(d))*Radius*i).rgb;
}
}
// Output to screen
result /= Quality * Directions - 15.0;
o_Color = vec4(result, 1.0);
#endif
}

View File

@ -4,13 +4,10 @@
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
out vec2 v_TexCoord;
void main()
{
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);

View File

@ -0,0 +1,36 @@
// Outline 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 color;
void main()
{
color = vec4(1.0, 0.5, 0.0, 1.0);
}

View File

@ -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)
@ -22,34 +22,50 @@ layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
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;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
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()
{
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];
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);
vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0));
vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * a_Normal;
vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * 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.Binormal = mat3(boneTransform) * a_Binormal;
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 * localPosition;
}
@ -65,7 +81,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;
@ -74,15 +91,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 = 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
@ -98,6 +119,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;
@ -105,7 +145,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;
@ -151,23 +190,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.
@ -178,26 +217,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)
@ -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;
}
}
}

View File

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

View File

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

View File

@ -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()
{
}

View File

@ -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()
{
}

View File

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

View File

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

View File

@ -31,6 +31,8 @@ namespace Prism
m_Window = std::unique_ptr<Window>(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);

View File

@ -0,0 +1,273 @@
//
// Created by sfd on 26-1-1.
//
#ifndef IMGUI_H
#define IMGUI_H
#include <cstdint>
#include <imgui.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
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<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);
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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<MeshComponent>();
SetSelected(newEntity);
}
ImGui::Separator();
if (ImGui::MenuItem("Directional Light"))
{
auto newEntity = m_Context->CreateEntity("Directional Light");
newEntity.AddComponent<DirectionalLightComponent>();
SetSelected(newEntity);
}
if (ImGui::MenuItem("Sky Light"))
{
auto newEntity = m_Context->CreateEntity("Sky Light");
newEntity.AddComponent<SkyLightComponent>();
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<typename T, typename UIFunction>
static void DrawComponent(const std::string& name, Entity entity, UIFunction uiFunction)
@ -622,6 +429,8 @@ namespace Prism
{
AddComponentPopup<CameraComponent>("Camera");
AddComponentPopup<MeshComponent>("Mesh");
AddComponentPopup<DirectionalLightComponent>("Directional Light");
AddComponentPopup<SkyLightComponent>("sky Light");
AddComponentPopup<ScriptComponent>("Script");
AddComponentPopup<SpriteRendererComponent>("SpriteRenderer");
AddComponentPopup<RigidBody2DComponent>("RigidBody2D");
@ -682,6 +491,7 @@ namespace Prism
if (!file.empty())
meshComponent.Mesh = Ref<Mesh>::Create(file);
}
ImGui::Columns(1);
});
DrawComponent<CameraComponent>("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<DirectionalLightComponent>("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<SkyLightComponent>("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<SpriteRendererComponent>("Sprite Renderer", entity, [](SpriteRendererComponent& mc)
@ -744,9 +593,9 @@ namespace Prism
});
DrawComponent<ScriptComponent>("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<int>() : field.GetStoredValue<int>();
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<float>() : field.GetStoredValue<float>();
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<glm::vec2>() : field.GetStoredValue<glm::vec2>();
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<glm::vec3>() : field.GetStoredValue<glm::vec3>();
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<glm::vec4>() : field.GetStoredValue<glm::vec4>();
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<BoxCollider2DComponent>("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<CircleCollider2DComponent>("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<RigidBodyComponent>("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<PhysicsMaterialComponent>("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<BoxColliderComponent>("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<SphereColliderComponent>("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<CapsuleColliderComponent>("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<MeshColliderComponent>("Mesh Collider", entity, [](MeshColliderComponent& mcc)
@ -1049,14 +898,28 @@ namespace Prism
if (!file.empty())
{
mcc.CollisionMesh = Ref<Mesh>::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();
});
}

View File

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

View File

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

View File

@ -9,10 +9,12 @@
#include <PxPhysicsAPI.h>
#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);

View File

@ -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<physx::PxConvexMesh*> 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<physx::PxTriangleMesh*> 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<physx::PxTriangleMesh*> PhysicsWrappers::CreateTriangleMesh(MeshColliderComponent& collider, bool invalidateOld)
{
std::vector<physx::PxTriangleMesh*> meshes;
// 设置烹饪参数,可以根据需要调整
physx::PxCookingParams cookingParams(s_Physics->getTolerancesScale());
// 可以添加额外的烹饪参数设置,如:
// cookingParams.meshPreprocessParams = physx::PxMeshPreprocessingFlag::eWELD_VERTICES;
// cookingParams.meshWeldTolerance = 0.01f;
// 获取顶点和索引数据
const std::vector<Vertex>& vertices = collider.CollisionMesh->GetStaticVertices();
const std::vector<Index>& indices = collider.CollisionMesh->GetIndices();
// 提取顶点位置(如果需要)
std::vector<glm::vec3> 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<Vertex> processedVertices;
std::vector<Index> 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<const physx::PxU16*>(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<const physx::PxU32*>(triangleMesh->getTriangles());
for (uint32_t tri = 0; tri < nbTriangles; tri++)
{
Index index;
index.V1 = static_cast<uint32_t>(tris[3 * tri + 0]);
index.V2 = static_cast<uint32_t>(tris[3 * tri + 1]);
index.V3 = static_cast<uint32_t>(tris[3 * tri + 2]);
processedIndices.push_back(index);
}
}
// 为当前子网格创建渲染网格
collider.ProcessedMeshes.push_back(Ref<Mesh>::Create(processedVertices, processedIndices));
}
}
return meshes;
}
std::vector<physx::PxConvexMesh*> PhysicsWrappers::CreateConvexMesh(MeshColliderComponent& collider, bool invalidateOld)
{
std::vector<physx::PxConvexMesh*> 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<Vertex>& vertices = collider.CollisionMesh->GetStaticVertices();
const std::vector<Index>& indices = collider.CollisionMesh->GetIndices();
// 准备顶点位置数组(如果需要的话)
std::vector<glm::vec3> 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<Vertex> collisionVertices;
std::vector<Index> 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<Mesh>::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;
}

View File

@ -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<physx::PxTriangleMesh*> CreateTriangleMesh(MeshColliderComponent& collider, bool invalidateOld = false);
static std::vector<physx::PxConvexMesh*> CreateConvexMesh(MeshColliderComponent& collider, bool invalidateOld = false);
static physx::PxMaterial* CreateMaterial(const PhysicsMaterialComponent& material);

View File

@ -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<OpenGLFrameBuffer> 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<const OpenGLFrameBuffer> 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<OpenGLFrameBuffer> 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<GLsizei>(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<uint32_t>(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<GLsizei>(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<const OpenGLFrameBuffer> instance = this;
Renderer::Submit([instance, attachmentIndex, slot]() {
glBindTextureUnit(slot, instance->m_ColorAttachments[attachmentIndex]);
});
}
}

View File

@ -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<RendererID> m_ColorAttachments;
RendererID m_DepthAttachment;
std::vector<FramebufferTextureFormat> m_ColorAttachmentFormats;
FramebufferTextureFormat m_DepthAttachmentFormat = FramebufferTextureFormat::None;
uint32_t m_Width = 0, m_Height = 0;
};
}

View File

@ -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<std::string> Tokenize(const std::string& string)
{
return SplitString(string, " \t\n");
return SplitString(string, " \t\n\r");
}
std::vector<std::string> 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<GLchar> 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<GLchar> 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<OpenGLShaderUniformBufferDeclaration>& 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<uint32_t, uint32_t> GetSize() const override { return { m_Data.Width, m_Data.Height }; }

View File

@ -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<FramebufferTextureSpecification>& attachments)
: Attachments(attachments) {}
std::vector<FramebufferTextureSpecification> 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;
};

View File

@ -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<Shader> GetShader() { return m_Shader; }
template <typename T>
T& Get(const std::string& name)
{
@ -83,13 +87,14 @@ namespace Prism
{
Set(name, (const Ref<Texture>&)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 <typename T>
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);

View File

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

View File

@ -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<Ref<Texture2D>>& GetTextures() const { return m_Textures; }
const std::string& GetFilePath() const { return m_FilePath; }
bool IsAnimated() const { return m_IsAnimated; }
std::vector<Triangle> GetTriangleCache(const uint32_t index) const { return m_TriangleCache.at(index); }
private:
void BoneTransform(float time);

View File

@ -135,31 +135,46 @@ namespace Prism
void Renderer::SubmitQuad(Ref<MaterialInstance>& 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<MaterialInstance> 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> mesh, const glm::mat4& transform, Ref<Shader> 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);
});
}

View File

@ -55,6 +55,7 @@ namespace Prism
static void SubmitQuad(Ref<MaterialInstance>& material, const glm::mat4& transform = glm::mat4(1.0f));
static void SubmitFullscreenQuad(Ref<MaterialInstance> material);
static void SubmitMesh(Ref<Mesh>& mesh, const glm::mat4& transform, const Ref<MaterialInstance>& overrideMaterial = nullptr);
static void SubmitMeshWithShader(Ref<Mesh> mesh, const glm::mat4& transform, Ref<Shader> 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>& mesh,const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f));

View File

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

View File

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

View File

@ -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<TextureCube> RadianceMap;
Ref<TextureCube> IrradianceMap;
static PRISM_API Environment Load(const std::string& filepath);
};
}
#endif //SCENEENVIRONMENT_H

View File

@ -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<MaterialInstance> SkyboxMaterial;
@ -30,9 +33,37 @@ namespace Prism
Ref<Texture2D> BRDFLUT;
Ref<Shader> CompositeShader;
Ref<Shader> BloomBlurShader;
Ref<Shader> BloomBlendShader;
Ref<RenderPass> GeoPass;
Ref<RenderPass> CompositePass;
Ref<RenderPass> BloomBlurPass[2];
Ref<RenderPass> BloomBlendPass;
Ref<Shader> ShadowMapShader, ShadowMapAnimShader;
Ref<RenderPass> 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<DrawCommand> DrawList;
std::vector<DrawCommand> SelectedMeshDrawList;
std::vector<DrawCommand> ColliderDrawList;
std::vector<DrawCommand> ShadowPassDrawList;
// Grid
Ref<MaterialInstance> GridMaterial;
Ref<MaterialInstance> OutlineMaterial;
Ref<MaterialInstance> OutlineMaterial, OutlineAnimMaterial;
Ref<MaterialInstance> 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>& 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<float>(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> 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();
}
}

View File

@ -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<RenderPass> GetFinalRenderPass();
static Ref<Texture2D> 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();
};
}

View File

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

View File

@ -18,6 +18,8 @@
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/quaternion.hpp>
#include "Prism/Renderer/SceneEnvironment.h"
namespace Prism
{
struct IDComponent
@ -222,7 +224,9 @@ namespace Prism
struct MeshColliderComponent
{
Ref<Prism::Mesh> CollisionMesh;
Ref<Prism::Mesh> ProcessedMesh;
std::vector<Ref<Prism::Mesh>> ProcessedMeshes;
bool IsConvex = false;
bool IsTrigger = false;
MeshColliderComponent() = default;
@ -235,6 +239,29 @@ namespace Prism
operator Ref<Prism::Mesh>() { 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;
};
}

View File

@ -18,6 +18,8 @@
#define PX_PHYSX_STATIC_LIB
#include <PxPhysicsAPI.h>
#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<ScriptComponent>();
for (const auto entity : view)
{
Entity e = { entity, this };
if (ScriptEngine::ModuleExists(e.GetComponent<ScriptComponent>().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<ScriptComponent>();
for (const auto entity : view)
{
Entity e = { entity, this };
if (ScriptEngine::ModuleExists(e.GetComponent<ScriptComponent>().ModuleName))
ScriptEngine::OnUpdateEntity(e, ts);
}
}
}
void Scene::OnRenderRuntime(TimeStep ts)
@ -218,6 +216,39 @@ namespace Prism
SceneCamera& camera = cameraEntity.GetComponent<CameraComponent>();
camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight);
// Process lights
{
m_LightEnvironment = LightEnvironment();
auto lights = m_Registry.group<DirectionalLightComponent>(entt::get<TransformComponent>);
uint32_t directionalLightIndex = 0;
for (auto entity : lights)
{
auto [transformComponent, lightComponent] = lights.get<TransformComponent, DirectionalLightComponent>(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<SkyLightComponent>(entt::get<TransformComponent>);
for (auto entity : lights)
{
auto [transformComponent, skyLightComponent] = lights.get<TransformComponent, SkyLightComponent>(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<MeshComponent>(entt::get<TransformComponent>);
@ -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<DirectionalLightComponent>(entt::get<TransformComponent>);
uint32_t directionalLightIndex = 0;
for (auto entity : lights)
{
auto [transformComponent, lightComponent] = lights.get<TransformComponent, DirectionalLightComponent>(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<SkyLightComponent>(entt::get<TransformComponent>);
for (auto entity : lights)
{
auto [transformComponent, skyLightComponent] = lights.get<TransformComponent, SkyLightComponent>(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<MeshComponent>(entt::get<TransformComponent>);
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix() });
// SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix() });
SceneRenderer::BeginScene(this, { static_cast<Camera>(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f }); // TODO: real values
for (auto entity : group)
{
const auto& [transformComponent, meshComponent] = group.get<TransformComponent, MeshComponent>(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<TextureCube>& skybox)
{
m_SkyboxTexture = skybox;
@ -580,6 +642,8 @@ namespace Prism
CopyComponentIfExists<TransformComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<MeshComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<DirectionalLightComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<SkyLightComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<ScriptComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<CameraComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
CopyComponentIfExists<SpriteRendererComponent>(newEntity.m_EntityHandle, entity.m_EntityHandle, m_Registry);
@ -629,6 +693,8 @@ namespace Prism
CopyComponent<TagComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<TransformComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<MeshComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<DirectionalLightComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<SkyLightComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<ScriptComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<CameraComponent>(target->m_Registry, m_Registry, enttMap);
CopyComponent<SpriteRendererComponent>(target->m_Registry, m_Registry, enttMap);

View File

@ -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<TextureCube> RadianceMap;
Ref<TextureCube> 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<UUID, Entity>;
@ -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<TextureCube>& 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<TextureCube> m_SkyboxTexture;
Ref<MaterialInstance> m_SkyboxMaterial;

View File

@ -244,6 +244,33 @@ namespace Prism
out << YAML::EndMap; // MeshComponent
}
if (entity.HasComponent<DirectionalLightComponent>())
{
out << YAML::Key << "DirectionalLightComponent";
out << YAML::BeginMap; // DirectionalLightComponent
auto& directionalLightComponent = entity.GetComponent<DirectionalLightComponent>();
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<SkyLightComponent>())
{
out << YAML::Key << "SkyLightComponent";
out << YAML::BeginMap; // SkyLightComponent
auto& skyLightComponent = entity.GetComponent<SkyLightComponent>();
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<CameraComponent>())
{
out << YAML::Key << "CameraComponent";
@ -394,6 +421,7 @@ namespace Prism
auto meshColliderComponent = entity.GetComponent<MeshColliderComponent>();
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<std::string>();
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<std::string>();
const std::string meshPath = meshComponent["AssetPath"].as<std::string>();
// TEMP (because script creates mesh component...)
if (!deserializedEntity.HasComponent<MeshComponent>())
deserializedEntity.AddComponent<MeshComponent>(Ref<Mesh>::Create(meshPath));
@ -614,6 +642,25 @@ namespace Prism
PM_CORE_INFO(" Mesh Asset Path: {0}", meshPath);
}
if (auto directionalLightComponent = entity["DirectionalLightComponent"])
{
auto& component = deserializedEntity.AddComponent<DirectionalLightComponent>();
component.Radiance = directionalLightComponent["Radiance"].as<glm::vec3>();
component.CastShadows = directionalLightComponent["CastShadows"].as<bool>();
component.SoftShadows = directionalLightComponent["SoftShadows"].as<bool>();
component.LightSize = directionalLightComponent["LightSize"].as<float>();
}
if (auto skyLightComponent = entity["SkyLightComponent"])
{
auto& component = deserializedEntity.AddComponent<SkyLightComponent>();
const std::string env = skyLightComponent["EnvironmentAssetPath"].as<std::string>();
if (!env.empty())
component.SceneEnvironment = Environment::Load(env);
component.Intensity = skyLightComponent["Intensity"].as<float>();
component.Angle = skyLightComponent["Angle"].as<float>();
}
if (auto cameraComponent = entity["CameraComponent"])
{
auto& component = deserializedEntity.AddComponent<CameraComponent>();
@ -712,8 +759,12 @@ namespace Prism
{
auto meshPath = meshColliderComponent["AssetPath"].as<std::string>();
auto& component = deserializedEntity.AddComponent<MeshColliderComponent>(Ref<Mesh>::Create(meshPath));
component.IsConvex = meshColliderComponent["IsConvex"] ? meshColliderComponent["IsConvex"].as<bool>() : false;
component.IsTrigger = meshColliderComponent["IsTrigger"] ? meshColliderComponent["IsTrigger"].as<bool>() : false;
PhysicsWrappers::CreateConvexMesh(component);
if (component.IsConvex)
PhysicsWrappers::CreateConvexMesh(component);
else
PhysicsWrappers::CreateTriangleMesh(component);
PM_CORE_INFO(" Mesh Collider Asset Path: {0}", meshPath);
}