From a265e71e1a9e6f122941248254cb749b975888fe Mon Sep 17 00:00:00 2001 From: Atdunbg Date: Wed, 11 Mar 2026 09:19:05 +0800 Subject: [PATCH] add AnimationComponent, Skeleton, AnimationClip; add single Renderer3D class, remove some useless code; problem now renderer2D cannot blend with Renderer3D --- Editor/Editor/EditorLayer.cpp | 36 +- Editor/Editor/EditorLayer.h | 2 - Editor/assets/scenes/demo.scene | 49 +- Editor/assets/shaders/PBRShader_Anim.glsl | 5 +- Prism/src/Prism/Animation/AnimationClip.cpp | 79 ++ Prism/src/Prism/Animation/AnimationClip.h | 63 + .../Prism/Animation/AnimatorController.cpp | 115 ++ .../src/Prism/Animation/AnimatorController.h | 67 + Prism/src/Prism/Animation/Skeleton.cpp | 68 + Prism/src/Prism/Animation/Skeleton.h | 35 + Prism/src/Prism/Asset/AssetsManager.cpp | 1 + Prism/src/Prism/Editor/EditorCamera.cpp | 19 +- Prism/src/Prism/Editor/EditorCamera.h | 2 +- Prism/src/Prism/Editor/SceneHierachyPanel.cpp | 123 +- Prism/src/Prism/Editor/SceneHierachyPanel.h | 1 + Prism/src/Prism/Renderer/Camera.h | 2 + Prism/src/Prism/Renderer/Mesh.cpp | 86 +- Prism/src/Prism/Renderer/Mesh.h | 45 +- Prism/src/Prism/Renderer/Renderer.cpp | 14 +- Prism/src/Prism/Renderer/Renderer2D.cpp | 16 +- Prism/src/Prism/Renderer/Renderer2D.h | 3 +- Prism/src/Prism/Renderer/Renderer3D.cpp | 1244 ++++++++++++++++ Prism/src/Prism/Renderer/Renderer3D.h | 68 + Prism/src/Prism/Renderer/SceneRenderer.cpp | 1260 ++--------------- Prism/src/Prism/Renderer/SceneRenderer.h | 47 +- Prism/src/Prism/Scene/Components.h | 11 + Prism/src/Prism/Scene/Entity.h | 15 + Prism/src/Prism/Scene/Scene.cpp | 265 ++-- Prism/src/Prism/Scene/Scene.h | 3 +- 29 files changed, 2278 insertions(+), 1466 deletions(-) create mode 100644 Prism/src/Prism/Animation/AnimationClip.cpp create mode 100644 Prism/src/Prism/Animation/AnimationClip.h create mode 100644 Prism/src/Prism/Animation/AnimatorController.cpp create mode 100644 Prism/src/Prism/Animation/AnimatorController.h create mode 100644 Prism/src/Prism/Animation/Skeleton.cpp create mode 100644 Prism/src/Prism/Animation/Skeleton.h create mode 100644 Prism/src/Prism/Renderer/Renderer3D.cpp create mode 100644 Prism/src/Prism/Renderer/Renderer3D.h diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index 63089c5..0bbda89 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -76,16 +76,6 @@ namespace Prism m_EditorScene->OnRenderEditor(deltaTime, m_EditorCamera); - if (m_DrawOnTopBoundingBoxes) - { - Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); - const auto viewProj = m_EditorCamera.GetViewProjection(); - Renderer2D::BeginScene(viewProj, false); - // TODO: Renderer::DrawAABB(m_MeshEntity.GetComponent(), m_MeshEntity.GetComponent()); - Renderer2D::EndScene(); - Renderer::EndRenderPass(); - } - if (!m_SelectionContext.empty() && false) { auto& selection = m_SelectionContext[0]; @@ -530,10 +520,7 @@ namespace Prism } } - if (UI::Property("Show Bounding Boxes", m_UIShowBoundingBoxes)) - ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop); - if (m_UIShowBoundingBoxes && UI::Property("On Top", m_UIShowBoundingBoxesOnTop)) - ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop); + UI::Property("Show Bounding Boxes", m_UIShowBoundingBoxes); const char* label = m_SelectionMode == SelectionMode::Entity ? "Entity" : "Mesh"; if (ImGui::Button(label)) @@ -903,7 +890,7 @@ namespace Prism bool EditorLayer::OnKeyPressedEvent(KeyPressedEvent& e) { - if (m_ViewportPanelFocused) + if (m_ViewportPanelFocused || m_ViewportPanelHovered) { switch (e.GetKeyCode()) { @@ -921,7 +908,7 @@ namespace Prism break; case KeyCode::F: { - if (m_SelectionContext.size() == 0) + if (m_SelectionContext.empty()) break; Entity selectedEntity = m_SelectionContext[0].Entity; @@ -964,7 +951,7 @@ namespace Prism case KeyCode::B: // Toggle bounding boxes m_UIShowBoundingBoxes = !m_UIShowBoundingBoxes; - ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop); + ShowBoundingBoxes(m_UIShowBoundingBoxes,false); break; case KeyCode::D: if (m_SelectionContext.size()) @@ -1053,7 +1040,21 @@ namespace Prism return a.Distance < b.Distance; }); if (!m_SelectionContext.empty()) + { + if (auto alreadySelectedEntity = m_SceneHierarchyPanel->GetSelected(); + m_SelectionContext.size() != 1 && alreadySelectedEntity) + { + const auto alreadySelectedEntityID = alreadySelectedEntity.GetUUID(); + + for (const auto& selectedEntity : m_SelectionContext) + { + if (alreadySelectedEntityID == selectedEntity.Entity.GetUUID()) continue; + OnSelected(selectedEntity); + return false; + } + } OnSelected(m_SelectionContext[0]); + } } } return false; @@ -1062,7 +1063,6 @@ namespace Prism void EditorLayer::ShowBoundingBoxes(bool show, bool onTop) { SceneRenderer::GetOptions().ShowBoundingBoxes = show && !onTop; - m_DrawOnTopBoundingBoxes = show && onTop; } void EditorLayer::SelectEntity(Entity entity) diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index e33163c..3ee1aa3 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -138,10 +138,8 @@ namespace Prism // configure button bool m_AllowViewportCameraEvents = false; - bool m_DrawOnTopBoundingBoxes = false; bool m_UIShowBoundingBoxes = false; - bool m_UIShowBoundingBoxesOnTop = false; enum class SceneType : uint32_t { diff --git a/Editor/assets/scenes/demo.scene b/Editor/assets/scenes/demo.scene index 0fec9e8..0adab62 100644 --- a/Editor/assets/scenes/demo.scene +++ b/Editor/assets/scenes/demo.scene @@ -1,62 +1,23 @@ Scene: Scene Name Environment: - AssetHandle: 1249421934001634765 + AssetHandle: 17073147362577408906 Light: Direction: [-0.314, -0.941, -0.209] Radiance: [0, 0, 0] - Multiplier: 0 + Multiplier: 1 Entities: - - Entity: 18090260616187152572 + - Entity: 3696833073589069488 Parent: 0 Children: [] TagComponent: - Tag: Directional Light - TransformComponent: - Position: [0, 0, 0] - Rotation: [0, 0, -2.7340124] - Scale: [0.9999992, 0.9999992, 1] - DirectionalLightComponent: - Radiance: [1, 1, 1] - CastShadows: true - SoftShadows: true - LightSize: 0.5 - - Entity: 18154648535203342017 - Parent: 0 - Children: - [] - TagComponent: - Tag: Cube - TransformComponent: - Position: [0, 6.282704, 0] - Rotation: [0, 0, 0] - Scale: [1, 1, 1] - MeshComponent: - AssetID: 133168951455480052 - - Entity: 386484027514287028 - Parent: 0 - Children: - [] - TagComponent: - Tag: Cube - TransformComponent: - Position: [0, 0, 0] - Rotation: [0, 0, 0] - Scale: [50, 1, 50] - MeshComponent: - AssetID: 133168951455480052 - - Entity: 6827989772960183355 - Parent: 0 - Children: - [] - TagComponent: - Tag: Sky Light + Tag: venice_dawn_1_4k TransformComponent: Position: [0, 0, 0] Rotation: [0, 0, 0] Scale: [1, 1, 1] SkyLightComponent: - EnvironmentMap: 1249421934001634765 + EnvironmentMap: 17073147362577408906 Intensity: 1 Angle: 0 PhysicsLayers: diff --git a/Editor/assets/shaders/PBRShader_Anim.glsl b/Editor/assets/shaders/PBRShader_Anim.glsl index a4bef82..a5dc7ec 100644 --- a/Editor/assets/shaders/PBRShader_Anim.glsl +++ b/Editor/assets/shaders/PBRShader_Anim.glsl @@ -58,11 +58,12 @@ void main() vs_Output.WorldTransform = mat3(u_Transform); vs_Output.Binormal = a_Binormal; - vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * vec4(a_Position, 1.0); + vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * localPosition; vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0)); - gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0); + // gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0); + gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition; } #type fragment diff --git a/Prism/src/Prism/Animation/AnimationClip.cpp b/Prism/src/Prism/Animation/AnimationClip.cpp new file mode 100644 index 0000000..e96cbc1 --- /dev/null +++ b/Prism/src/Prism/Animation/AnimationClip.cpp @@ -0,0 +1,79 @@ +// +// Created by Atdunbg on 2026/3/9. +// + +#include "AnimationClip.h" + +#include "assimp/scene.h" + +struct aiAnimation; + +namespace Prism +{ + AnimationClip::AnimationClip(const aiScene* scene) + { + const aiAnimation* anim = scene->mAnimations[0]; // 先只处理第一个动画 + m_Duration = (float)anim->mDuration; + m_TicksPerSecond = (float)(anim->mTicksPerSecond != 0 ? anim->mTicksPerSecond : 25.0); + + for (uint32_t i = 0; i < anim->mNumChannels; i++) + { + aiNodeAnim* nodeAnim = anim->mChannels[i]; + BoneChannel channel; + channel.BoneName = nodeAnim->mNodeName.C_Str(); + + // 位置关键帧 + for (uint32_t j = 0; j < nodeAnim->mNumPositionKeys; j++) + { + const auto& key = nodeAnim->mPositionKeys[j]; + channel.Positions.emplace_back((float)key.mTime, + glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z)); + } + // 旋转关键帧 + for (uint32_t j = 0; j < nodeAnim->mNumRotationKeys; j++) + { + const auto& key = nodeAnim->mRotationKeys[j]; + channel.Rotations.emplace_back((float)key.mTime, + glm::quat(key.mValue.w, key.mValue.x, key.mValue.y, key.mValue.z)); + } + // 缩放关键帧 + for (uint32_t j = 0; j < nodeAnim->mNumScalingKeys; j++) + { + const auto& key = nodeAnim->mScalingKeys[j]; + channel.Scalings.emplace_back((float)key.mTime, + glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z)); + } + m_BoneChannels.push_back(channel); + } + } + + glm::mat4 AnimationClip::SampleAtTime(const float timeTicks, const std::string& boneName, const glm::mat4& defaultTransform) const + { + for (const auto& channel : m_BoneChannels) { + if (channel.BoneName == boneName) { + const glm::vec3 pos = SampleChannel(channel.Positions, timeTicks, glm::vec3(0.0f)); + const glm::quat rot = SampleChannel(channel.Rotations, timeTicks, glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); + const glm::vec3 scl = SampleChannel(channel.Scalings, timeTicks, glm::vec3(1.0f)); + return glm::translate(glm::mat4(1.0f), pos) * glm::toMat4(rot) * glm::scale(glm::mat4(1.0f), scl); + } + } + return defaultTransform; + } + + template + T AnimationClip::SampleChannel(const std::vector>& keys, float time, const T& defaultValue) const + { + if (keys.empty()) return defaultValue; + if (time <= keys.front().Time) return keys.front().Value; + if (time >= keys.back().Time) return keys.back().Value; + + // 找到当前时间所在的关键帧区间 + for (size_t i = 0; i < keys.size() - 1; ++i) { + if (time >= keys[i].Time && time <= keys[i+1].Time) { + float t = (time - keys[i].Time) / (keys[i+1].Time - keys[i].Time); + return Interpolate(keys[i].Value, keys[i+1].Value, t); + } + } + return defaultValue; + } +} diff --git a/Prism/src/Prism/Animation/AnimationClip.h b/Prism/src/Prism/Animation/AnimationClip.h new file mode 100644 index 0000000..f9dbac0 --- /dev/null +++ b/Prism/src/Prism/Animation/AnimationClip.h @@ -0,0 +1,63 @@ +// +// Created by Atdunbg on 2026/3/9. +// + +#ifndef PRISM_ANIMATIONCLIP_H +#define PRISM_ANIMATIONCLIP_H +#include "Prism/Asset/Asset.h" + +#include +#define GLM_ENABLE_EXPERIMENTAL +#include + + +struct aiScene; + +namespace Prism +{ + template + struct KeyFrame + { + float Time = 0.0f; + T Value; + + KeyFrame() = default; + KeyFrame(const float t, const T& v) : Time(t), Value(v) {} + }; + + struct BoneChannel { + std::string BoneName; + std::vector> Positions; + std::vector> Rotations; + std::vector> Scalings; + }; + + + class PRISM_API AnimationClip : public Asset + { + public: + AnimationClip(const aiScene* scene); + glm::mat4 SampleAtTime(float timeTicks, const std::string& boneName, const glm::mat4& defaultTransform) const; + + private: + // 辅助采样模板函数 + template + T SampleChannel(const std::vector>& keys, float time, const T& defaultValue) const; + + glm::vec3 Interpolate(const glm::vec3& a, const glm::vec3& b, const float t) const { + return glm::mix(a, b, t); + } + glm::quat Interpolate(const glm::quat& a, const glm::quat& b, const float t) const { + return glm::slerp(a, b, t); + } + + public: + float m_Duration = 0.0f; + float m_TicksPerSecond; + std::vector m_BoneChannels; + + }; + +} + +#endif //PRISM_ANIMATIONCLIP_H \ No newline at end of file diff --git a/Prism/src/Prism/Animation/AnimatorController.cpp b/Prism/src/Prism/Animation/AnimatorController.cpp new file mode 100644 index 0000000..2e79a9b --- /dev/null +++ b/Prism/src/Prism/Animation/AnimatorController.cpp @@ -0,0 +1,115 @@ +// +// Created by Atdunbg on 2026/3/9. +// + +#include "AnimatorController.h" + +namespace Prism +{ + AnimationController::~AnimationController() + { + } + + void AnimationController::Update(const float deltaTime) + { + if (m_State != PLAY || !m_currentClip || !m_skeleton) + return; + + const float rawTime = m_AnimationCurrentTime + deltaTime * m_currentClip->m_TicksPerSecond * m_TimeMultiplier; + const float duration = m_currentClip->m_Duration; + + if (!m_Loop && rawTime >= duration) + { + m_AnimationCurrentTime = 0.0f; + m_State = STOP; + } + else + { + m_AnimationCurrentTime = fmod(rawTime, duration); + } + + ComputeBoneTransforms(m_AnimationCurrentTime); + + } + + void AnimationController::ComputeBoneTransforms(float time) + { + const auto boneCount = (uint32_t)m_skeleton->m_Bones.size(); + m_FinalBoneTransforms.resize(boneCount); + + std::vector localTransforms(boneCount, glm::mat4(1.0f)); + for (uint32_t i = 0; i < boneCount; i++) + { + const BoneInfo& bone = m_skeleton->m_Bones[i]; + if (m_currentClip) + { + localTransforms[i] = m_currentClip->SampleAtTime(time, bone.Name, bone.LocalBindPose); + } + else + { + localTransforms[i] = bone.LocalBindPose; + } + } + + // 2. 递归计算全局变换(从根骨骼开始) + std::function computeGlobal = [&](const uint32_t boneIdx, const glm::mat4& parentGlobal) + { + const glm::mat4 global = parentGlobal * localTransforms[boneIdx]; + glm::mat4 final = global * m_skeleton->m_Bones[boneIdx].InverseTransform; + final = m_GlobalInverseTransform * final; + m_FinalBoneTransforms[boneIdx] = final; + + for (uint32_t childIdx = 0; childIdx < boneCount; childIdx++) + { + if (m_skeleton->m_Bones[childIdx].ParentIndex == (int)boneIdx) + { + computeGlobal(childIdx, global); + } + } + }; + + for (uint32_t i = 0; i < boneCount; i++) + { + if (m_skeleton->m_Bones[i].ParentIndex == -1) + { + computeGlobal(i, glm::mat4(1.0f)); + } + } + } + + void AnimationController::ComputeBindPoseTransforms() + { + if (!m_skeleton) return; + + uint32_t boneCount = (uint32_t)m_skeleton->m_Bones.size(); + m_FinalBoneTransforms.resize(boneCount); + + // 1. 收集每个骨骼的绑定姿势局部变换 + std::vector localTransforms(boneCount); + for (uint32_t i = 0; i < boneCount; i++) + localTransforms[i] = m_skeleton->m_Bones[i].LocalBindPose; + + // 2. 递归计算全局变换,并应用逆绑定矩阵和全局逆矩阵 + std::function computeGlobal = + [&](uint32_t idx, const glm::mat4& parentGlobal) + { + glm::mat4 global = parentGlobal * localTransforms[idx]; + glm::mat4 final = m_GlobalInverseTransform * global * m_skeleton->m_Bones[idx].InverseTransform; + m_FinalBoneTransforms[idx] = final; + + // 处理子骨骼 + for (uint32_t child = 0; child < boneCount; child++) + { + if (m_skeleton->m_Bones[child].ParentIndex == (int)idx) + computeGlobal(child, global); + } + }; + + // 从所有根骨骼开始递归 + for (uint32_t i = 0; i < boneCount; i++) + { + if (m_skeleton->m_Bones[i].ParentIndex == -1) + computeGlobal(i, glm::mat4(1.0f)); + } + } +} diff --git a/Prism/src/Prism/Animation/AnimatorController.h b/Prism/src/Prism/Animation/AnimatorController.h new file mode 100644 index 0000000..1e8c275 --- /dev/null +++ b/Prism/src/Prism/Animation/AnimatorController.h @@ -0,0 +1,67 @@ +// +// Created by Atdunbg on 2026/3/9. +// + +#ifndef PRISM_ANIMATORCONTROLLER_H +#define PRISM_ANIMATORCONTROLLER_H +#include "AnimationClip.h" +#include "Skeleton.h" +#include "Prism/Asset/Asset.h" + +namespace Prism +{ + class PRISM_API AnimationController : public Asset + { + public: + enum AnimationState : uint8_t + { + STOP = 0, + PLAY, + PAUSE + }; + + public: + ~AnimationController(); + + void Update(const float deltaTime); + + const std::vector& GetFinalBoneTransforms() const { return m_FinalBoneTransforms; } + + + void AnimationController::SetSkeleton(const Ref& skeleton) + { + m_skeleton = skeleton; + if (m_skeleton) + { + ComputeBindPoseTransforms(); + } + } + void AnimationController::SetAnimationClip(const Ref& clip) { m_currentClip = clip; } + void AnimationController::SetGlobalInverseTransform(const glm::mat4& inv) { m_GlobalInverseTransform = inv; } + + void Play() { m_State = PLAY; } + void Pause() { m_State = PAUSE; } + void Stop() { if (m_State == STOP) return; m_State = STOP; m_AnimationCurrentTime = 0.0f; ComputeBindPoseTransforms(); } + AnimationState GetAnimationState() const { return m_State; } + + float& GetCurrentTime() { return m_AnimationCurrentTime; } + float& GetMultiplierTime() { return m_TimeMultiplier; } + bool& GetLoop() { return m_Loop; } + private: + void ComputeBoneTransforms(float time); + void ComputeBindPoseTransforms(); + + Ref m_skeleton = nullptr; + Ref m_currentClip = nullptr; + AnimationState m_State = STOP; + + float m_AnimationCurrentTime = 0.0f; + float m_TimeMultiplier = 1.0f; + bool m_Loop = false; + std::vector m_FinalBoneTransforms; + + glm::mat4 m_GlobalInverseTransform = glm::mat4(1.0f); + }; +} + +#endif //PRISM_ANIMATORCONTROLLER_H \ No newline at end of file diff --git a/Prism/src/Prism/Animation/Skeleton.cpp b/Prism/src/Prism/Animation/Skeleton.cpp new file mode 100644 index 0000000..898ccf6 --- /dev/null +++ b/Prism/src/Prism/Animation/Skeleton.cpp @@ -0,0 +1,68 @@ +// +// Created by Atdunbg on 2026/3/9. +// + +#include "Skeleton.h" + +#include "assimp/scene.h" + +namespace Prism +{ + + // TODO: this maybe move Utils + extern glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix); + + + Skeleton::Skeleton(const aiScene* scene) + { + // 收集所有骨骼(通过所有 Mesh 中的 aiBone) + for (uint32_t am = 0; am < scene->mNumMeshes; am++) + { + const aiMesh* aMesh = scene->mMeshes[am]; + for (uint32_t i = 0; i < aMesh->mNumBones; i++) + { + const aiBone* bone = aMesh->mBones[i]; + if (std::string boneName = bone->mName.C_Str(); m_NameToIndex.find(boneName) == m_NameToIndex.end()) + { + const auto boneIndex = (uint32_t)m_Bones.size(); + BoneInfo bi; + bi.Name = boneName; + bi.InverseTransform = Mat4FromAssimpMat4(bone->mOffsetMatrix); + bi.ParentIndex = -1; // 稍后设置 + + m_Bones.push_back(bi); + m_NameToIndex[boneName] = boneIndex; + } + } + } + + // 设置骨骼的父子关系及默认局部变换(遍历节点树) + std::unordered_map& nameToIdx = m_NameToIndex; + std::function traverseNodes = [&](const aiNode* node, const int parentBoneIdx) + { + const std::string nodeName = node->mName.C_Str(); + const auto it = nameToIdx.find(nodeName); + int currentBoneIdx = -1; + if (it != nameToIdx.end()) + { + currentBoneIdx = (int)it->second; + BoneInfo& bone = m_Bones[currentBoneIdx]; + bone.ParentIndex = parentBoneIdx; + // 存储默认局部变换(绑定姿势下相对于父节点的变换) + bone.LocalBindPose = Mat4FromAssimpMat4(node->mTransformation); + } + + for (uint32_t i = 0; i < node->mNumChildren; i++) + { + traverseNodes(node->mChildren[i], currentBoneIdx); + } + }; + traverseNodes(scene->mRootNode, -1); + } + + uint32_t Skeleton::GetBoneIndex(const std::string& name) const + { + const auto it = m_NameToIndex.find(name); + return (it != m_NameToIndex.end()) ? it->second : static_cast(-1); + } +} diff --git a/Prism/src/Prism/Animation/Skeleton.h b/Prism/src/Prism/Animation/Skeleton.h new file mode 100644 index 0000000..26accd2 --- /dev/null +++ b/Prism/src/Prism/Animation/Skeleton.h @@ -0,0 +1,35 @@ +// +// Created by Atdunbg on 2026/3/9. +// + +#ifndef PRISM_SKELETON_H +#define PRISM_SKELETON_H +#include "glm/glm.hpp" +#include "Prism/Asset/Asset.h" + +struct aiScene; + +namespace Prism +{ + struct BoneInfo + { + std::string Name; + int ParentIndex; // -1 is Root + glm::mat4 LocalBindPose; + glm::mat4 InverseTransform; // aiScene::mOffsetMatrix + }; + + class PRISM_API Skeleton : public Asset + { + public: + Skeleton(const aiScene* scene); + uint32_t GetBoneIndex(const std::string& name) const; + + public: + std::vector m_Bones; + std::unordered_map m_NameToIndex; + }; +} + + +#endif //PRISM_SKELETON_H \ No newline at end of file diff --git a/Prism/src/Prism/Asset/AssetsManager.cpp b/Prism/src/Prism/Asset/AssetsManager.cpp index 9572aaf..0d8f1ba 100644 --- a/Prism/src/Prism/Asset/AssetsManager.cpp +++ b/Prism/src/Prism/Asset/AssetsManager.cpp @@ -23,6 +23,7 @@ namespace Prism s_Types["scene"] = AssetType::Scene; s_Types["pmx"] = AssetType::Mesh; s_Types["fbx"] = AssetType::Mesh; + s_Types["dae"] = AssetType::Mesh; s_Types["obj"] = AssetType::Mesh; s_Types["png"] = AssetType::Texture; s_Types["tga"] = AssetType::Texture; diff --git a/Prism/src/Prism/Editor/EditorCamera.cpp b/Prism/src/Prism/Editor/EditorCamera.cpp index 535a1e1..7e6422b 100644 --- a/Prism/src/Prism/Editor/EditorCamera.cpp +++ b/Prism/src/Prism/Editor/EditorCamera.cpp @@ -50,10 +50,10 @@ namespace Prism if (Input::IsMouseButtonPressed(MouseButton::Right) && !altPressed) { - Input::SetCursorMode(CursorMode::Disable); if (!m_IsRightButtonDownState) { + Input::SetCursorMode(CursorMode::Disable); m_InitialMousePosition = { Input::GetMouseX(), Input::GetMouseY() }; m_IsRightButtonDownState = true; m_FreeLookActive = true; @@ -183,6 +183,7 @@ namespace Prism UI::Property("focus", m_FocalPoint); UI::Property("position", m_Position); UI::Separator(); + UI::Property("Max Speed", m_MaxSpeed); UI::Property("Current Speed", m_Velocity); UI::EndPropertyGrid(); ImGui::End(); @@ -198,10 +199,20 @@ namespace Prism m_ViewMatrix = glm::inverse(m_ViewMatrix); } - bool EditorCamera::OnMouseScroll(MouseScrolledEvent& e) + bool EditorCamera::OnMouseScroll(const MouseScrolledEvent& e) { - const float delta = e.GetOffsetY() * 0.1f; - MouseZoom(delta); + const float delta = e.GetOffsetY(); + + if (m_IsRightButtonDownState) + { + const float factor = m_MaxSpeed * 0.1f; + m_MaxSpeed += delta * factor; + m_MaxSpeed = std::clamp(m_MaxSpeed, 0.1f, 50.0f); + }else + { + MouseZoom(delta * 0.1f); + } + return false; } diff --git a/Prism/src/Prism/Editor/EditorCamera.h b/Prism/src/Prism/Editor/EditorCamera.h index 2bc35ce..79c47ac 100644 --- a/Prism/src/Prism/Editor/EditorCamera.h +++ b/Prism/src/Prism/Editor/EditorCamera.h @@ -52,7 +52,7 @@ namespace Prism private: void UpdateCameraView(); - bool OnMouseScroll(MouseScrolledEvent& e); + bool OnMouseScroll(const MouseScrolledEvent& e); void MousePan(const glm::vec2& delta); void MouseRotate(const glm::vec2& delta); void MouseZoom(float delta); diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp index ac9474e..2cb343c 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp @@ -139,6 +139,11 @@ namespace Prism m_SelectionContext = entity; } + Entity SceneHierarchyPanel::GetSelected() const + { + return m_SelectionContext; + } + template void SceneHierarchyPanel::AddComponentPopup(const char* componentName) { @@ -311,27 +316,6 @@ namespace Prism } } ImGui::End(); - - -#if TODO - ImGui::Begin("Mesh Debug"); - if (ImGui::CollapsingHeader(mesh->m_FilePath.c_str())) - { - if (mesh->m_IsAnimated) - { - if (ImGui::CollapsingHeader("Animation")) - { - if (ImGui::Button(mesh->m_AnimationPlaying ? "Pause" : "Play")) - mesh->m_AnimationPlaying = !mesh->m_AnimationPlaying; - - ImGui::SliderFloat("##AnimationTime", &mesh->m_AnimationTime, 0.0f, (float)mesh->m_Scene->mAnimations[0]->mDuration); - ImGui::DragFloat("Time Scale", &mesh->m_TimeMultiplier, 0.05f, 0.0f, 10.0f); - } - } - } - ImGui::End(); -#endif - } void SceneHierarchyPanel::DrawEntityNode(Entity entity) @@ -582,10 +566,43 @@ namespace Prism ImGui::SeparatorText("3D Component"); AddComponentPopup("Mesh"); + AddComponentPopup("Animation", [&](AnimationComponent& component) + { + if (m_SelectionContext.HasComponent()) + { + const auto meshComponent = m_SelectionContext.GetComponent(); + if (meshComponent.Mesh->IsAnimated()) + { + component.AnimationController = meshComponent.Mesh->GetAnimatorController(); + component.SkeletonAsset = meshComponent.Mesh->GetSkeleton(); + } + } + }); AddComponentPopup("RigidBody"); - AddComponentPopup("BoxCollider"); - AddComponentPopup("SphereCollider"); - AddComponentPopup("CapsuleCollider"); + AddComponentPopup("BoxCollider", [&](BoxColliderComponent& component) + { + if (m_SelectionContext.HasComponent()) + { + component.DebugMesh = MeshFactory::CreateBox(component.Size); + } + }); + + AddComponentPopup("SphereCollider", [&](SphereColliderComponent& component) + { + if (m_SelectionContext.HasComponent()) + { + component.DebugMesh = MeshFactory::CreateSphere(component.Radius); + } + }); + + AddComponentPopup("CapsuleCollider", [&](CapsuleColliderComponent& component) + { + if (m_SelectionContext.HasComponent()) + { + component.DebugMesh = MeshFactory::CreateCapsule(component.Radius, component.Radius); + } + }); + AddComponentPopup("MeshCollider", [&](MeshColliderComponent& component) { if (m_SelectionContext.HasComponent()) @@ -636,9 +653,67 @@ namespace Prism UI::BeginPropertyGrid(); UI::PropertyAssetReference("Mesh", meshComponent.Mesh, AssetType::Mesh); + UI::Property("AnimationPlaying", meshComponent.Mesh->m_AnimationPlaying); + UI::Property("TimeMultiple", meshComponent.Mesh->m_TimeMultiplier); + UI::Property("AnimationTime", meshComponent.Mesh->m_AnimationTime); + UI::Property("WorldTime", meshComponent.Mesh->m_WorldTime); + UI::EndPropertyGrid(); }); + DrawComponent("AnimatorController", entity, [this](AnimationComponent& animatorComponent) + { + // UI::BeginPropertyGrid(); + // UI::PropertyAssetReference("Animation", animatorComponent.AnimationController, AssetType::Other); + // UI::PropertyAssetReference("", animatorComponent.SkeletonAsset, AssetType::Other); + // UI::EndPropertyGrid(); + + if (animatorComponent.AnimationController) + { + UI::BeginPropertyGrid(); + UI::Property("Current Time", animatorComponent.AnimationController->GetCurrentTime()); + UI::Property("Multiplier Time", animatorComponent.AnimationController->GetMultiplierTime()); + UI::Property("Loop", animatorComponent.AnimationController->GetLoop()); + UI::EndPropertyGrid(); + + const AnimationController::AnimationState animationState = animatorComponent.AnimationController->GetAnimationState(); + if (ImGui::Button("Play")) + { + if (animationState != AnimationController::PLAY) + animatorComponent.AnimationController->Play(); + } + ImGui::SameLine(); + + if (ImGui::Button("Pause")) + { + if (animationState != AnimationController::PAUSE) + animatorComponent.AnimationController->Pause(); + } + ImGui::SameLine(); + + if (ImGui::Button("Stop")) + { + if (animationState != AnimationController::STOP) + animatorComponent.AnimationController->Stop(); + } + } + else + { + ImGui::Text("Load Animation: Not implement"); + if (ImGui::Button("Try get from exist Mesh")) + { + if (const auto meshComponent = m_SelectionContext.TryGetComponent()) + { + if (meshComponent->Mesh->IsAnimated()) + { + animatorComponent.AnimationController = meshComponent->Mesh->GetAnimatorController(); + animatorComponent.SkeletonAsset = meshComponent->Mesh->GetSkeleton(); + } + } + } + } + }); + DrawComponent("Camera", entity, [](CameraComponent& cameraComponent) { // Projection Type const char* projTypeStrings[] = { "Perspective", "Orthographic" }; diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.h b/Prism/src/Prism/Editor/SceneHierachyPanel.h index fc56009..45cec53 100644 --- a/Prism/src/Prism/Editor/SceneHierachyPanel.h +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.h @@ -18,6 +18,7 @@ namespace Prism void SetContext(const Ref& scene); void SetSelected(Entity entity); + Entity GetSelected() const; void SetSelectionChangedCallback(const std::function& func) { m_SelectionChangedCallback = func; } void SetEntityDeletedCallback(const std::function& func) { m_EntityDeletedCallback = func; } diff --git a/Prism/src/Prism/Renderer/Camera.h b/Prism/src/Prism/Renderer/Camera.h index b10436d..dd80ab9 100644 --- a/Prism/src/Prism/Renderer/Camera.h +++ b/Prism/src/Prism/Renderer/Camera.h @@ -6,6 +6,8 @@ #define CAMERA_H #include + +#include "Prism/Core/Ref.h" #include "Prism/Core/TimeStep.h" namespace Prism diff --git a/Prism/src/Prism/Renderer/Mesh.cpp b/Prism/src/Prism/Renderer/Mesh.cpp index fd5e047..30690af 100644 --- a/Prism/src/Prism/Renderer/Mesh.cpp +++ b/Prism/src/Prism/Renderer/Mesh.cpp @@ -10,7 +10,6 @@ #include "assimp/LogStream.hpp" #include "assimp/postprocess.h" #include "assimp/scene.h" -#include "glad/glad.h" #define GLM_ENABLE_EXPERIMENTAL #include @@ -144,6 +143,23 @@ namespace Prism // Vertices if (m_IsAnimated) { + // 1. 创建 Skeleton + m_Skeleton = Ref::Create(scene); + + + // ---------- 2. 如果有动画,创建 AnimationClip ---------- + if (scene->mNumAnimations > 0) + { + Ref clip = Ref::Create(scene); + + // ---------- 3. 创建 AnimatorController ---------- + m_AnimatorController = Ref::Create(); + m_AnimatorController->SetSkeleton(m_Skeleton); + m_AnimatorController->SetAnimationClip(clip); + m_AnimatorController->SetGlobalInverseTransform(m_InverseTransform); // 根节点逆矩阵 + } + + for (size_t i = 0; i < mesh->mNumVertices; i++) { AnimatedVertex vertex; @@ -169,7 +185,7 @@ namespace Prism for (size_t i = 0; i < mesh->mNumVertices; i++) { - Vertex vertex; + Vertex vertex{}; vertex.Position = { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z }; vertex.Normal = { mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z }; @@ -211,41 +227,31 @@ namespace Prism // Bones if (m_IsAnimated) { - for (size_t m = 0; m < scene->mNumMeshes; m++) - { - aiMesh* mesh = scene->mMeshes[m]; - Submesh& submesh = m_Submeshes[m]; + for (size_t m = 0; m < scene->mNumMeshes; m++) + { + aiMesh* mesh = scene->mMeshes[m]; + Submesh& submesh = m_Submeshes[m]; + for (size_t i = 0; i < mesh->mNumBones; i++) + { + aiBone* bone = mesh->mBones[i]; + std::string boneName = bone->mName.C_Str(); - for (size_t i = 0; i < mesh->mNumBones; i++) - { - aiBone* bone = mesh->mBones[i]; - std::string boneName(bone->mName.data); - uint32_t boneIndex = 0; + // 从 Skeleton 获取骨骼索引 + uint32_t boneIndex = m_Skeleton->GetBoneIndex(boneName); + if (boneIndex == uint32_t(-1)) + { + PM_CORE_ERROR("Bone '{}' not found in skeleton!", boneName); + continue; + } - if (m_BoneMapping.find(boneName) == m_BoneMapping.end()) - { - // Allocate an index for a new bone - boneIndex = m_BoneCount; - m_BoneCount++; - BoneInfo bi; - m_BoneInfo.push_back(bi); - m_BoneInfo[boneIndex].BoneOffset = Mat4FromAssimpMat4(bone->mOffsetMatrix); - m_BoneMapping[boneName] = boneIndex; - } - else - { - PM_CORE_TRACE("Found existing bone in map"); - boneIndex = m_BoneMapping[boneName]; - } - - for (size_t j = 0; j < bone->mNumWeights; j++) - { - uint32_t VertexID = submesh.BaseVertex + bone->mWeights[j].mVertexId; - float Weight = bone->mWeights[j].mWeight; - m_AnimatedVertices[VertexID].AddBoneData(boneIndex, Weight); - } - } - } + for (size_t j = 0; j < bone->mNumWeights; j++) + { + uint32_t VertexID = submesh.BaseVertex + bone->mWeights[j].mVertexId; + float Weight = bone->mWeights[j].mWeight; + m_AnimatedVertices[VertexID].AddBoneData(boneIndex, Weight); + } + } + } } // Material @@ -541,6 +547,11 @@ namespace Prism void Mesh::OnUpdate(TimeStep deltaTime) { + if (m_AnimatorController) + { + m_AnimatorController->Update(deltaTime); + } + /* if (m_IsAnimated) { if (m_AnimationPlaying) @@ -555,6 +566,7 @@ namespace Prism // TODO: We only need to recalc bones if rendering has been requested at the current animation frame BoneTransform(m_AnimationTime); } + */ } @@ -594,6 +606,7 @@ namespace Prism PM_MESH_LOG("------------------------------------------------------"); } + /* void Mesh::BoneTransform(float time) { ReadNodeHierarchy(time, m_Scene->mRootNode, glm::mat4(1.0f)); @@ -642,6 +655,7 @@ namespace Prism result += "--"; return result; } + */ void Mesh::TraverseNodes(const aiNode* node, const glm::mat4& parentTransform, uint32_t level) { @@ -662,6 +676,7 @@ namespace Prism } } + /* const aiNodeAnim* Mesh::FindNodeAnim(const aiAnimation* animation, const std::string& nodeName) { for (uint32_t i = 0; i < animation->mNumChannels; i++) @@ -779,4 +794,5 @@ namespace Prism auto aiVec = start + factor * delta; return { aiVec.x, aiVec.y, aiVec.z }; } + */ } diff --git a/Prism/src/Prism/Renderer/Mesh.h b/Prism/src/Prism/Renderer/Mesh.h index fa1ac73..dd49fe6 100644 --- a/Prism/src/Prism/Renderer/Mesh.h +++ b/Prism/src/Prism/Renderer/Mesh.h @@ -14,6 +14,8 @@ #include "Prism/Core/TimeStep.h" #include "Prism/Core/Math/AABB.h" #include "../Asset/Asset.h" +#include "Prism/Animation/AnimatorController.h" +#include "Prism/Animation/Skeleton.h" struct aiNode; struct aiAnimation; @@ -58,12 +60,12 @@ namespace Prism }; static_assert(sizeof(Index) == 3 * sizeof(uint32_t)); - struct BoneInfo + struct MeshBoneInfo { glm::mat4 BoneOffset; glm::mat4 FinalTransformation; - BoneInfo() : BoneOffset(glm::mat4(1.0f)), FinalTransformation(glm::mat4(1.0f)) {} + MeshBoneInfo() : BoneOffset(glm::mat4(1.0f)), FinalTransformation(glm::mat4(1.0f)) {} }; struct VertexBoneData @@ -107,11 +109,9 @@ namespace Prism class PRISM_API Mesh : public Asset { public: - - Mesh(const std::string& filename); Mesh(const std::vector& vertices, const std::vector& indices, const glm::mat4& transform); - ~Mesh(); + ~Mesh() override; void OnUpdate(TimeStep deltaTime); void DumpVertexBuffer(); @@ -131,31 +131,26 @@ namespace Prism bool IsAnimated() const { return m_IsAnimated; } - std::vector GetTriangleCache(const uint32_t index) const { return m_TriangleCache.at(index); } + std::vector GetTriangleCache(const uint32_t index) const { return m_TriangleCache.at(index); } + + Ref GetSkeleton() const { return m_Skeleton; } + Ref GetAnimatorController() const { return m_AnimatorController; } + std::vector m_EmptyTransforms{}; + + const std::vector& GetBoneTransforms() const + { + return m_AnimatorController ? m_AnimatorController->GetFinalBoneTransforms() : m_EmptyTransforms; + } private: - void BoneTransform(float time); - void ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const glm::mat4& ParentTransform); void TraverseNodes(const aiNode* node, const glm::mat4& parentTransform = glm::mat4(1.0f), uint32_t level = 0); - const aiNodeAnim* FindNodeAnim(const aiAnimation* animation, const std::string& nodeName); - uint32_t FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim); - uint32_t FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim); - uint32_t FindScaling(float AnimationTime, const aiNodeAnim* pNodeAnim); - glm::vec3 InterpolateTranslation(float animationTime, const aiNodeAnim* nodeAnim); - glm::quat InterpolateRotation(float animationTime, const aiNodeAnim* nodeAnim); - glm::vec3 InterpolateScale(float animationTime, const aiNodeAnim* nodeAnim); - private: - std::vector m_Submeshes; std::unique_ptr m_Importer; + const aiScene* m_Scene; glm::mat4 m_InverseTransform; - uint32_t m_BoneCount = 0; - std::vector m_BoneInfo; - - // Ref m_VertexArray; Ref m_Pipeline; Ref m_VertexBuffer; Ref m_IndexBuffer; @@ -164,9 +159,11 @@ namespace Prism std::vector m_StaticVertices; std::vector m_AnimatedVertices; std::vector m_Indices; - std::unordered_map m_BoneMapping; - std::vector m_BoneTransforms; - const aiScene* m_Scene; + std::vector m_Submeshes; + + Ref m_Skeleton; + Ref m_AnimatorController; + // Material Ref m_MeshShader; diff --git a/Prism/src/Prism/Renderer/Renderer.cpp b/Prism/src/Prism/Renderer/Renderer.cpp index 22f2099..79ba4b5 100644 --- a/Prism/src/Prism/Renderer/Renderer.cpp +++ b/Prism/src/Prism/Renderer/Renderer.cpp @@ -5,6 +5,7 @@ #include "Renderer.h" #include "Renderer2D.h" +#include "Renderer3D.h" #include "RendererAPI.h" #include "SceneRenderer.h" #include "glad/glad.h" @@ -35,6 +36,7 @@ namespace Prism GetShaderLibrary()->Load("assets/shaders/PBRShader_Static.glsl"); GetShaderLibrary()->Load("assets/shaders/PBRShader_Anim.glsl"); + // this to Init 2D and 3D Renderer SceneRenderer::Init(); // FullScreen Quad @@ -58,8 +60,6 @@ namespace Prism s_Data.m_FullscreenQuadVertexBuffer = VertexBuffer::Create(fullScreenQuadVertex, sizeof(fullScreenQuadVertex[0]) * sizeof(fullScreenQuadVertex)); s_Data.m_FullscreenQuadIndexBuffer = IndexBuffer::Create(fullScreenQuadIndices, sizeof(fullScreenQuadIndices[0]) * sizeof(fullScreenQuadIndices)); - - Renderer2D::Init(); } void Renderer::Clear() @@ -192,10 +192,11 @@ namespace Prism if (mesh->m_IsAnimated) { - for (size_t i = 0; i < mesh->m_BoneTransforms.size(); i++) + const auto& boneTransform = mesh->GetBoneTransforms(); + for (size_t i = 0; i < boneTransform.size(); i++) { std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); - mesh->m_MeshShader->SetMat4(uniformName, mesh->m_BoneTransforms[i]); + shader->SetMat4(uniformName, boneTransform[i]); } } shader->SetMat4("u_Transform", transform * submesh.Transform); @@ -227,10 +228,11 @@ namespace Prism { if (mesh->m_IsAnimated) { - for (size_t i = 0; i < mesh->m_BoneTransforms.size(); i++) + const auto& boneTransform = mesh->GetBoneTransforms(); + for (size_t i = 0; i < boneTransform.size(); i++) { std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); - shader->SetMat4(uniformName, mesh->m_BoneTransforms[i]); + shader->SetMat4(uniformName, boneTransform[i]); } } shader->SetMat4("u_Transform", transform * submesh.Transform); diff --git a/Prism/src/Prism/Renderer/Renderer2D.cpp b/Prism/src/Prism/Renderer/Renderer2D.cpp index 54b15e1..6a3df24 100644 --- a/Prism/src/Prism/Renderer/Renderer2D.cpp +++ b/Prism/src/Prism/Renderer/Renderer2D.cpp @@ -186,14 +186,11 @@ namespace Prism { } - void Renderer2D::BeginScene(const glm::mat4& viewProj, bool depthTest) + void Renderer2D::BeginScene(const glm::mat4& viewProj, const bool depthTest) { s_Data.CameraViewProj = viewProj; s_Data.DepthTest = depthTest; - s_Data.TextureShader->Bind(); - s_Data.TextureShader->SetMat4("u_ViewProjection", viewProj); - s_Data.QuadIndexCount = 0; s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase; @@ -206,8 +203,10 @@ namespace Prism s_Data.TextureSlotIndex = 1; } - void Renderer2D::EndScene() + void Renderer2D::EndScene(const Ref& renderPass) { + if (renderPass) Renderer::BeginRenderPass(renderPass, false); + uint32_t dataSize = (uint32_t)((uint8_t*)s_Data.QuadVertexBufferPtr - (uint8_t*)s_Data.QuadVertexBufferBase); if (dataSize) { @@ -257,6 +256,7 @@ namespace Prism s_Data.Stats.DrawCalls++; } + if (renderPass) Renderer::EndRenderPass(); #if OLD Flush(); #endif @@ -614,6 +614,12 @@ namespace Prism s_Data.QuadIndexCount = 0; s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase; + s_Data.CircleIndexCount = 0; + s_Data.CircleVertexBufferPtr = s_Data.CircleVertexBufferBase; + + s_Data.LineIndexCount = 0; + s_Data.LineVertexBufferPtr = s_Data.LineVertexBufferBase; + s_Data.TextureSlotIndex = 1; } diff --git a/Prism/src/Prism/Renderer/Renderer2D.h b/Prism/src/Prism/Renderer/Renderer2D.h index 418524e..31e6a58 100644 --- a/Prism/src/Prism/Renderer/Renderer2D.h +++ b/Prism/src/Prism/Renderer/Renderer2D.h @@ -7,6 +7,7 @@ #include +#include "RenderPass.h" #include "Texture.h" namespace Prism @@ -18,7 +19,7 @@ namespace Prism static void Shutdown(); static void BeginScene(const glm::mat4& viewProj, bool depthTest = true); - static void EndScene(); + static void EndScene(const Ref& renderPass = nullptr); static void Flush(); // Primitives diff --git a/Prism/src/Prism/Renderer/Renderer3D.cpp b/Prism/src/Prism/Renderer/Renderer3D.cpp new file mode 100644 index 0000000..b9145f5 --- /dev/null +++ b/Prism/src/Prism/Renderer/Renderer3D.cpp @@ -0,0 +1,1244 @@ +// +// Created by Atdunbg on 2026/3/10. +// + +#include "Renderer3D.h" + +#include "Prism/Core/Timer.h" +#include "Prism/Scene/Scene.h" +#include + +#include "Renderer.h" +#include "Renderer2D.h" +#include "SceneRenderer.h" +#include "Prism/Scene/Components.h" + +namespace Prism +{ + struct Renderer3DData + { + struct SceneInfo + { + SceneRendererCamera SceneCamera; + float SceneEnvironmentIntensity; + LightEnvironment SceneLightEnvironment; + + // Resources + Ref SkyboxMaterial; + Ref SceneEnvironment; + Light ActiveLight; + } SceneData; + + Ref BRDFLUT; + Ref CompositeShader; + Ref BloomBlurShader; + Ref BloomBlendShader; + + Ref GeoPass; + // Ref CompositePass; + Ref BloomBlurPass[2]; + Ref BloomBlendPass; + + // Auto exposure + struct AutoExposureData + { + Ref LuminancePass; + + float CurrentExposure = 1.0f; + bool EnableAutoExposure = true; + float Key = 0.11f; // middle gray + float AdaptationSpeed = 5.0f; // stops per second + + Timer ExposureTimer; + float MaxExposure = 5.0f; + }AutoExposureData; + + Ref ShadowMapShader, ShadowMapAnimShader; + Ref ShadowMapRenderPass; + + glm::mat4 LightMatrices; + // float ShadowMapSize = 20.0f; + // float LightDistance = 0.1f; + + // 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; + + bool ShadowEnabled = true; + float ShadowBias = 0.001f; + float ShadowIntensity = 0.0f; + int ShadowSoftness = 0; + + struct DrawCommand + { + Ref mesh; + Ref Material; + glm::mat4 Transform; + }; + std::vector DrawList; + std::vector SelectedMeshDrawList; + std::vector ColliderDrawList; + std::vector ShadowPassDrawList; + + Ref OutlineMaterial, OutlineAnimMaterial; + Ref ColliderMaterial; + + // Grid + struct GridData + { + Ref GridMaterial; + + int GridPlane = 0; + float GridScale = 1.0f; + glm::vec4 GridColorThin = glm::vec4(0.5f, 0.5f, 0.5f, 0.4f); + glm::vec4 GridColorThick = glm::vec4(0.5f, 0.5f, 0.5f, 0.6f); + glm::vec4 AxisColorX = glm::vec4(0.9f, 0.2f, 0.2f, 1.0f); + glm::vec4 AxisColorZ = glm::vec4(0.2f, 0.2f, 0.9f, 1.0f); + float FadeDistance = 500.0f; + }GridData; + + SceneRendererOptions Options; + }; + + struct Renderer3DStats + { + float ShadowPass = 0.0f; + float GeometryPass = 0.0f; + float CompositePass = 0.0f; + float AutoExposurePass = 0.0f; + + Timer ShadowPassTimer; + Timer GeometryPassTimer; + Timer CompositePassTimer; + Timer AutoExposurePassTimer; + }; + + + + Renderer3DData s_Data; + static Renderer3DStats s_Stats; + + void Renderer3D::Init() + { + FramebufferSpecification geoFramebufferSpec; + geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::DEPTH24STENCIL8 }; + geoFramebufferSpec.Samples = 8; + geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + + RenderPassSpecification geoRenderPassSpec; + geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec); + s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec); + + /* + FramebufferSpecification compFramebufferSpec; + compFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 , 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 , FramebufferTextureFormat::RGBA8}; + 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"); + + // Luminance pass: used to compute average scene luminance (for auto exposure) + FramebufferSpecification luminanceFramebufferSpec; + luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; + luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + + RenderPassSpecification luminanceRenderPassSpec; + luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec); + s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec); + + // Grid + // const auto gridShader = Shader::Create("assets/shaders/Grid.glsl"); + const auto gridShader = Shader::Create("assets/shaders/InfiniteGrid.glsl"); + s_Data.GridData.GridMaterial = MaterialInstance::Create(Material::Create(gridShader)); + // constexpr float gridScale = 16.025f; + // constexpr float gridSize = 0.025f; + // s_Data.GridMaterial->Set("u_Scale", gridScale); + // s_Data.GridMaterial->Set("u_Res", gridSize); + s_Data.GridData.GridMaterial->SetFlag(MaterialFlag::TwoSided, true); + + // outline + const auto outlineShader = Shader::Create("assets/shaders/Outline.glsl"); + s_Data.OutlineMaterial = MaterialInstance::Create(Material::Create(outlineShader)); + s_Data.OutlineMaterial->SetFlag(MaterialFlag::DepthTest, false); + + // Collider + 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; + + RenderPassSpecification shadowMapRenderPassSpec; + shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); + s_Data.ShadowMapRenderPass = 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 Renderer3D::SetViewportSize(uint32_t width, uint32_t height) + { + s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height); + // s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height); + if (s_Data.AutoExposureData.LuminancePass) + s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->Resize(width, height); + } + + void Renderer3D::BeginScene(const Scene* scene, const SceneRendererCamera& camera) + { + 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 Renderer3D::EndScene(Ref& renderPass) + { + FlushDrawList(renderPass); + } + + void Renderer3D::SubmitMesh(const Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial) + { + // TODO: Culling, sorting, etc. + + s_Data.DrawList.push_back({ mesh, overrideMaterial, transform }); + s_Data.ShadowPassDrawList.push_back({ mesh, overrideMaterial, transform }); + } + + void Renderer3D::SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform) + { + s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform }); + s_Data.ShadowPassDrawList.push_back({ mesh, nullptr, transform }); + } + + void Renderer3D::SubmitColliderMesh(const BoxColliderComponent& component, const glm::mat4& parentTransform) + { + s_Data.ColliderDrawList.push_back({ component.DebugMesh, nullptr, glm::translate(parentTransform, component.Offset)}); + } + + void Renderer3D::SubmitColliderMesh(const SphereColliderComponent& component, const glm::mat4& parentTransform) + { + s_Data.ColliderDrawList.push_back({ component.DebugMesh, nullptr, parentTransform }); + } + + void Renderer3D::SubmitColliderMesh(const CapsuleColliderComponent& component, const glm::mat4& parentTransform) + { + s_Data.ColliderDrawList.push_back({ component.DebugMesh, nullptr, parentTransform }); + } + + void Renderer3D::SubmitColliderMesh(const MeshColliderComponent& component, const glm::mat4& parentTransform) + { + for (const auto debugMesh : component.ProcessedMeshes) + s_Data.ColliderDrawList.push_back({ debugMesh, nullptr, parentTransform }); + } + + // TODO: temp + static Ref equirectangularConversionShader, envFilteringShader, envIrradianceShader; + + std::pair, Ref> Renderer3D::CreateEnvironmentMap(const std::string& filepath) + { + constexpr uint32_t cubemapSize = 2048; + constexpr uint32_t irradianceMapSize = 32; + + Ref envUnfiltered = TextureCube::Create(TextureFormat::Float16, cubemapSize, cubemapSize); + if (!equirectangularConversionShader) + equirectangularConversionShader = Shader::Create("assets/shaders/EquirectangularToCubeMap.glsl"); + Ref envEquirect = Texture2D::Create(filepath); + if (envEquirect->GetFormat() != TextureFormat::Float16) + PM_CORE_WARN("Texture is not HDR!"); + + equirectangularConversionShader->Bind(); + envEquirect->Bind(); + Renderer::Submit([envUnfiltered, cubemapSize, envEquirect]() + { + glBindImageTexture(0, envUnfiltered->GetRendererID(), 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glDispatchCompute(cubemapSize / 32, cubemapSize / 32, 6); + glGenerateTextureMipmap(envUnfiltered->GetRendererID()); + }); + + + if (!envFilteringShader) + envFilteringShader = Shader::Create("assets/shaders/EnvironmentMipFilter.glsl"); + + Ref envFiltered = TextureCube::Create(TextureFormat::Float16, cubemapSize, cubemapSize); + + Renderer::Submit([envUnfiltered, envFiltered]() + { + glCopyImageSubData(envUnfiltered->GetRendererID(), GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0, + envFiltered->GetRendererID(), GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0, + envFiltered->GetWidth(), envFiltered->GetHeight(), 6); + }); + + envFilteringShader->Bind(); + envUnfiltered->Bind(); + + Renderer::Submit([envUnfiltered, envFiltered, cubemapSize]() { + const float deltaRoughness = 1.0f / glm::max((float)(envFiltered->GetMipLevelCount() - 1.0f), 1.0f); + for (int level = 1, size = cubemapSize / 2; level < (int)envFiltered->GetMipLevelCount(); level++, size /= 2) // <= ? + { + const GLuint numGroups = glm::max(1, size / 32); + glBindImageTexture(0, envFiltered->GetRendererID(), level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glProgramUniform1f(envFilteringShader->GetRendererID(), 0, level * deltaRoughness); + glDispatchCompute(numGroups, numGroups, 6); + } + }); + + if (!envIrradianceShader) + envIrradianceShader = Shader::Create("assets/shaders/EnvironmentIrradiance.glsl"); + + Ref irradianceMap = TextureCube::Create(TextureFormat::Float16, irradianceMapSize, irradianceMapSize); + envIrradianceShader->Bind(); + envFiltered->Bind(); + Renderer::Submit([irradianceMap]() + { + glBindImageTexture(0, irradianceMap->GetRendererID(), 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); + glDispatchCompute(irradianceMap->GetWidth() / 32, irradianceMap->GetHeight() / 32, 6); + glGenerateTextureMipmap(irradianceMap->GetRendererID()); + }); + + return { envFiltered, irradianceMap }; + } + + /* + Ref Renderer3D::GetFinalRenderPass() + { + return s_Data.CompositePass; + } + */ + + Ref Renderer3D::GetGeoPass() + { + return s_Data.GeoPass; + } + + Ref Renderer3D::GetFinalColorBuffer() + { + PM_CORE_ASSERT(false, "Not implemented"); + return nullptr; + } + + void Renderer3D::SetFocusPoint(const glm::vec2& point) + { + s_Data.FocusPoint = point; + } + + /* + uint32_t Renderer3D::GetFinalColorBufferRendererID() + { + return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(); + } + */ + + SceneRendererOptions& Renderer3D::GetOptions() + { + return s_Data.Options; + } + + + void Renderer3D::FlushDrawList(Ref& outRenderPass) + { + memset(&s_Stats, 0, sizeof(Renderer3DStats)); + + { + 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(); }); + } + + // Compute average luminance and update exposure (GPU-copy + mipmap -> read 1 texel) + { + Renderer::Submit([]() { s_Stats.AutoExposurePassTimer.Reset(); }); + AutoExposurePass(); + Renderer::Submit([] { s_Stats.AutoExposurePass = s_Stats.AutoExposurePassTimer.ElapsedMillis(); }); + } + + { + Renderer::Submit([]() { s_Stats.CompositePassTimer.Reset(); }); + // CompositePass(); + { + Renderer::BeginRenderPass(outRenderPass); + + s_Data.CompositeShader->Bind(); + s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure()); + s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); + s_Data.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom); + s_Data.CompositeShader->SetInt("u_DepthTexture", 1); + + s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); + Renderer::Submit([]() + { + glBindTextureUnit(1, s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID()); + }); + + Renderer::SubmitFullscreenQuad(nullptr); + + Renderer::EndRenderPass(); + } + 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 = {}; + } + + void Renderer3D::GeometryPass() + { + const bool outline = !s_Data.SelectedMeshDrawList.empty(); + const bool collider = !s_Data.ColliderDrawList.empty(); + + if (outline) + { + Renderer::Submit([]() + { + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + }); + } + + Renderer::BeginRenderPass(s_Data.GeoPass); + + if (outline) + { + Renderer::Submit([]() + { + glStencilMask(0); + }); + } + + const auto& sceneCamera = s_Data.SceneData.SceneCamera; + + const auto cameraProjection = sceneCamera.Camera.GetProjectionMatrix(); + const auto cameraView = sceneCamera.ViewMatrix; + const auto cameraViewProjection = cameraProjection * cameraView; + 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(cameraViewProjection)); + 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", cameraViewProjection); + 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); + baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment->IrradianceMap); + baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT); + + // Set lights (TODO: move to light environment and don't do per mesh) + + baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]); + // baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight); + + // shadow + baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices); + baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowEnabled); + baseMaterial->Set("u_ShadowBias", s_Data.ShadowBias); + baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowIntensity); + + auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap"); + if (rd) + { + auto reg = rd->GetRegister(); + auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + + Renderer::Submit([reg, tex]() mutable + { + glBindTextureUnit(reg, tex); + glBindSampler(reg++, s_Data.ShadowMapSampler); + }); + } + + /* + 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", cameraViewProjection); + 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]); + // baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight); + + + // shadow + baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices); + baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowEnabled); + baseMaterial->Set("u_ShadowBias", s_Data.ShadowBias); + baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowIntensity); + + + auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap"); + if (rd) + { + auto reg = rd->GetRegister(); + auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + + Renderer::Submit([reg, tex]() mutable + { + glBindTextureUnit(reg, tex); + glBindSampler(reg, s_Data.ShadowMapSampler); + }); + } + /* + 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_NOTEQUAL, 1, 0xff); + glStencilMask(0); + + glLineWidth(10); + glEnable(GL_LINE_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDisable(GL_DEPTH_TEST); + }); + + // Draw outline here + s_Data.OutlineMaterial->Set("u_ViewProjection", cameraViewProjection); + s_Data.OutlineAnimMaterial->Set("u_ViewProjection", cameraViewProjection); + for (auto& dc : s_Data.SelectedMeshDrawList) + { + // 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([]() + { + glPointSize(10); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + }); + for (auto& dc : s_Data.SelectedMeshDrawList) + { + // Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); + Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.mesh->IsAnimated() ? s_Data.OutlineAnimMaterial : s_Data.OutlineMaterial); + } + + Renderer::Submit([]() + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glStencilMask(0xff); + glStencilFunc(GL_ALWAYS, 1, 0xff); + glEnable(GL_DEPTH_TEST); + }); + } + + if (collider) + { + Renderer::Submit([]() + { + // glStencilFunc(GL_NOTEQUAL, 1, 0xff); + // glStencilMask(0); + + glLineWidth(1); + glEnable(GL_LINE_SMOOTH); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDisable(GL_DEPTH_TEST); + }); + + s_Data.ColliderMaterial->Set("u_ViewProjection", cameraViewProjection); + for (auto& dc : s_Data.ColliderDrawList) + { + if (dc.mesh) + Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.ColliderMaterial); + } + + Renderer::Submit([]() + { + glPointSize(1); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + }); + + for (auto& dc : s_Data.ColliderDrawList) + { + if (dc.mesh) + Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.ColliderMaterial); + } + + Renderer::Submit([]() + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + // glStencilMask(0xff); + // glStencilFunc(GL_ALWAYS, 1, 0xff); + glEnable(GL_DEPTH_TEST); + }); + } + + + // Grid + const auto option = GetOptions(); + if (option.ShowGrid) + { + s_Data.GridData.GridMaterial->Set("u_View", sceneCamera.ViewMatrix); + s_Data.GridData.GridMaterial->Set("u_Projection", cameraProjection); + s_Data.GridData.GridMaterial->Set("u_CameraPosition", cameraPosition); + + // grid config + s_Data.GridData.GridMaterial->Set("u_GridPlane", s_Data.GridData.GridPlane); + s_Data.GridData.GridMaterial->Set("u_GridScale", s_Data.GridData.GridScale); + s_Data.GridData.GridMaterial->Set("u_GridColorThin", s_Data.GridData.GridColorThin); + s_Data.GridData.GridMaterial->Set("u_GridColorThick", s_Data.GridData.GridColorThick); + s_Data.GridData.GridMaterial->Set("u_AxisColorX", s_Data.GridData.AxisColorX); + s_Data.GridData.GridMaterial->Set("u_AxisColorZ", s_Data.GridData.AxisColorZ); + s_Data.GridData.GridMaterial->Set("u_FadeDistance", s_Data.GridData.FadeDistance); + + Renderer::SubmitFullscreenQuad(s_Data.GridData.GridMaterial); + } + + if (option.ShowBoundingBoxes) + { + Renderer2D::BeginScene(cameraViewProjection); + for (auto& dc : s_Data.DrawList) + Renderer::DrawAABB(dc.mesh, dc.Transform); + Renderer2D::EndScene(); + } + + Renderer::EndRenderPass(); + } + + void Renderer3D::AutoExposurePass() + { + if (!s_Data.AutoExposureData.EnableAutoExposure) + return; + + auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; + auto dstFB = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer; + if (!srcFB || !dstFB) + return; + + const uint32_t dstID = dstFB->GetColorAttachmentRendererID(); + const uint32_t width = dstFB->GetWidth(); + const uint32_t height = dstFB->GetHeight(); + + Renderer::Submit([dstID, width, height, srcFB]() mutable + { + // Use framebuffer blit to resolve multisampled source into non-multisampled luminance target. + const GLuint srcFBO = srcFB->GetRendererID(); + const GLuint dstFBO = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID(); + + // Bind read/draw FBOs and blit color attachment 0 + glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + + // Source size — try to use srcFB dimensions if available + const int srcWidth = srcFB->GetWidth(); + const int srcHeight = srcFB->GetHeight(); + + glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + // Unbind + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + // Generate mipmaps so the smallest mip is the average color + glGenerateTextureMipmap(dstID); + + // Determine highest mip level + int maxLevel = (int)std::floor(std::log2((float)std::max(width, height))); + if (maxLevel < 0) maxLevel = 0; + + + GLint levelWidth = 0, levelHeight = 0; + glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_WIDTH, &levelWidth); + glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_HEIGHT, &levelHeight); + + if (levelWidth == 0 || levelHeight == 0) return; + + const int bufSize = levelWidth * levelHeight * 4 * sizeof(float); + std::vector pixelData(bufSize / sizeof(float)); + + glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, bufSize, pixelData.data()); + + // Sanitize pixel values (handle NaN/Inf or negative values coming from GPU) + for (int i = 0; i < 3; ++i) + { + if (!std::isfinite(pixelData[i]) || pixelData[i] < 0.0f) + pixelData[i] = 0.0f; + } + + // Convert to luminance + float lum = 0.2126f * pixelData[0] + 0.7152f * pixelData[1] + 0.0722f * pixelData[2]; + if (!std::isfinite(lum) || lum <= 0.0f) + lum = 1e-6f; // fallback minimum luminance + + // Compute desired exposure (simple key/avg approach) + const float key = s_Data.AutoExposureData.Key; + constexpr float minLum = 1e-6f; + float desiredExposure = key / std::max(lum, minLum); + desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure); + + + // Adapt exposure over time (exponential) + const float dt = s_Data.AutoExposureData.ExposureTimer.ElapsedMillis() / 1000.0f; + s_Data.AutoExposureData.ExposureTimer.Reset(); + const float tau = s_Data.AutoExposureData.AdaptationSpeed; + const float adaptFactor = 1.0f - std::exp(-tau * dt); + s_Data.AutoExposureData.CurrentExposure = s_Data.AutoExposureData.CurrentExposure + (desiredExposure - s_Data.AutoExposureData.CurrentExposure) * adaptFactor; + s_Data.AutoExposureData.CurrentExposure = std::clamp(s_Data.AutoExposureData.CurrentExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure); + + // Write exposure directly into composite shader program uniform so the subsequent composite pass uses it + /* + if (const GLuint prog = s_Data.CompositeShader->GetRendererID()) + { + const GLint loc = glGetUniformLocation(prog, "u_Exposure"); + if (loc >= 0) + glProgramUniform1f(prog, loc, s_Data.CurrentExposure); + } + */ + }); + + } + + /* + void Renderer3D::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.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : 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.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom); + + s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); + Renderer::Submit([]() + { + glBindTextureUnit(1, s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID()); + }); + + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } + */ + + /* + void Renderer3D::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(); + auto & adad =s_Data; + // s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); + s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure()); + s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom); + + s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0); + s_Data.BloomBlurPass[index]->GetSpecification().TargetFramebuffer->BindTexture(1); + + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } + } + */ + + /* + struct FrustumBounds + { + float r, l, b, t, f, n; + }; + + struct CascadeData + { + glm::mat4 ViewProj; + glm::mat4 View; + float SplitDepth; + }; + + + static void CalculateCascades(CascadeData* cascades, const glm::vec3& lightDirection) + { + // FrustumBounds frustumBounds[3]; + + auto& sceneCamera = s_Data.SceneData.SceneCamera; + auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix; + + constexpr int SHADOW_MAP_CASCADE_COUNT = 4; + float cascadeSplits[SHADOW_MAP_CASCADE_COUNT]; + + // TODO: less hard-coding! + float nearClip = 0.1f; + float farClip = 1000.0f; + float clipRange = farClip - nearClip; + + float minZ = nearClip; + float maxZ = nearClip + clipRange; + + float range = maxZ - minZ; + float ratio = maxZ / minZ; + + // Calculate split depths based on view camera frustum + // Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html + for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) + { + float p = (i + 1) / static_cast(SHADOW_MAP_CASCADE_COUNT); + float log = minZ * std::pow(ratio, p); + float uniform = minZ + range * p; + float d = s_Data.CascadeSplitLambda * (log - uniform) + uniform; + cascadeSplits[i] = (d - nearClip) / clipRange; + } + + // cascadeSplits[3] = 0.3f; + + // Manually set cascades here + // cascadeSplits[0] = 0.05f; + // cascadeSplits[1] = 0.15f; + // cascadeSplits[2] = 0.3f; + // cascadeSplits[3] = 1.0f; + + // Calculate orthographic projection matrix for each cascade + float lastSplitDist = 1.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); + + // Offset to texel space to avoid shimmering (from https://stackoverflow.com/questions/33499053/cascaded-shadow-map-shimmering) + glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix; + constexpr float ShadowMapResolution = 4096.0f; + glm::vec4 shadowOrigin = (shadowMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) * ShadowMapResolution / 2.0f; + glm::vec4 roundedOrigin = glm::round(shadowOrigin); + glm::vec4 roundOffset = roundedOrigin - shadowOrigin; + roundOffset = roundOffset * 2.0f / ShadowMapResolution; + roundOffset.z = 0.0f; + roundOffset.w = 0.0f; + + lightOrthoMatrix[3] += roundOffset; + + // 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 Renderer3D::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); + Renderer::EndRenderPass(); + } + return; + } + + // TODO: this will not be hardcode + const glm::vec3 lightDir = glm::normalize(directionalLights[0].Direction); // 光线方向(从光源指向场景) + glm::vec3 lightPos = lightDir * 10.0f; + + glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + + float orthoSize = 100.0f; + float nearPlane = 0.01f, farPlane = 1000.0f; + glm::mat4 lightProjection = glm::ortho(-orthoSize, orthoSize, -orthoSize, orthoSize, nearPlane, farPlane); + glm::mat4 lightSpaceMatrix = lightProjection * lightView; + + Renderer::Submit([]() + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + }); + + { + Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass); + + s_Data.LightMatrices = lightSpaceMatrix; + + + // Render entities + for (auto& dc : s_Data.ShadowPassDrawList) + { + Ref shader = dc.mesh->IsAnimated() ? s_Data.ShadowMapAnimShader : s_Data.ShadowMapShader; + shader->SetMat4("u_ViewProjection", lightSpaceMatrix); + Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader); + } + + Renderer::EndRenderPass(); + } + } + + void SceneRenderer::OnImGuiRender() + { + ImGui::Begin("Scene Renderer"); + + UI::Property("Composite Pass time", s_Stats.CompositePass); + UI::Property("Geometry Pass time", s_Stats.GeometryPass); + UI::Property("Shadow Pass time", s_Stats.ShadowPass); + UI::Property("AutoExposure Pass time", s_Stats.AutoExposurePass); + + if (UI::BeginTreeNode("Grid Config", false)) + { + // Grid plane: 0 = XZ (Y up), 1 = XY (Z forward), 2 = YZ (X right) + const char* projTypeStrings[] = { "XZ (Y up)", "XY (Z forward)", "YZ (X right)" }; + const char* currentMode = projTypeStrings[s_Data.GridData.GridPlane]; + if (ImGui::BeginCombo("Grid Plane Mode", currentMode)) + { + for (int type = 0; type < 3; type++) + { + const bool is_selected = (currentMode == projTypeStrings[type]); + if (ImGui::Selectable(projTypeStrings[type], is_selected)) + { + currentMode = projTypeStrings[type]; + s_Data.GridData.GridPlane = type; + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + UI::BeginPropertyGrid(); + UI::Property("GridScale", s_Data.GridData.GridScale); + UI::PropertyColor("GridColorThin", s_Data.GridData.GridColorThin); + UI::PropertyColor("GridColorThick", s_Data.GridData.GridColorThick); + UI::PropertyColor("AxisColorX", s_Data.GridData.AxisColorX); + UI::PropertyColor("AxisColorZ", s_Data.GridData.AxisColorZ); + UI::Property("FadeDistance ", s_Data.GridData.FadeDistance); + UI::EndPropertyGrid(); + UI::EndTreeNode(); + } + + if (UI::BeginTreeNode("Shadows", false)) + { + if (UI::BeginTreeNode("Shadow Map", false)) + { + + UI::BeginPropertyGrid(); + UI::Property("EnableMap", s_Data.ShadowEnabled); + UI::Property("ShadowBias", s_Data.ShadowBias, 0.01f); + UI::Property("ShadowSoftness", s_Data.ShadowSoftness); + UI::Property("ShadowIntensity", s_Data.ShadowIntensity, 0.01f); + UI::EndPropertyGrid(); + + auto fb = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer; + auto id = fb->GetDepthAttachmentRendererID(); + + float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f + ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 }); + UI::EndTreeNode(); + } + + UI::EndTreeNode(); + } + + if (UI::BeginTreeNode("Bloom", false)) + { + 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; + const auto id = fb->GetColorAttachmentRendererID(); + + const 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(); + } + + if (UI::BeginTreeNode("Auto Exposure", false)) + { + UI::BeginPropertyGrid(); + UI::Property("Enable Auto Exposure", s_Data.AutoExposureData.EnableAutoExposure); + UI::Property("Key (middle gray)", s_Data.AutoExposureData.Key, 0.001f, 0.001f, 2.5f); + UI::Property("Adaptation Speed", s_Data.AutoExposureData.AdaptationSpeed, 0.01f, 0.001f, 5.0f); + UI::Property("Current Exposure", s_Data.AutoExposureData.CurrentExposure, 0.01f, 0.0f, 0.0f, true); + UI::EndPropertyGrid(); + + UI::EndTreeNode(); + } + + + ImGui::End(); + } +} diff --git a/Prism/src/Prism/Renderer/Renderer3D.h b/Prism/src/Prism/Renderer/Renderer3D.h new file mode 100644 index 0000000..df42859 --- /dev/null +++ b/Prism/src/Prism/Renderer/Renderer3D.h @@ -0,0 +1,68 @@ +// +// Created by Atdunbg on 2026/3/10. +// + +#ifndef PRISM_RENDERER3D_H +#define PRISM_RENDERER3D_H +#include "Texture.h" +#include "glm/glm.hpp" +#include "Prism/Core/Ref.h" + + +namespace Prism +{ + class Shader; + class RenderPass; + struct SceneRendererOptions; + struct MeshColliderComponent; + struct CapsuleColliderComponent; + struct BoxColliderComponent; + struct SphereColliderComponent; + class MaterialInstance; + class Mesh; + struct SceneRendererCamera; + class Scene; + + class PRISM_API Renderer3D + { + public: + static void Init(); + + static void SetViewportSize(uint32_t width, uint32_t height); + + static void BeginScene(const Scene* scene, const SceneRendererCamera& camera); + static void EndScene(Ref& renderPass); + + static void SubmitMesh(const Ref& mesh, const glm::mat4& transform = glm::mat4(1.0f), const Ref& overrideMaterial = nullptr); + static void SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform = glm::mat4(1.0f)); + + static void SubmitColliderMesh(const BoxColliderComponent& component, const glm::mat4& parentTransform = glm::mat4(1.0f)); + static void SubmitColliderMesh(const SphereColliderComponent& component, const glm::mat4& parentTransform = glm::mat4(1.0f)); + static void SubmitColliderMesh(const CapsuleColliderComponent& component, const glm::mat4& parentTransform = glm::mat4(1.0f)); + static void SubmitColliderMesh(const MeshColliderComponent& component, const glm::mat4& parentTransform = glm::mat4(1.0f)); + static std::pair, Ref> CreateEnvironmentMap(const std::string& filepath); + + // static Ref GetFinalRenderPass(); + static Ref GetGeoPass(); + static Ref GetFinalColorBuffer(); + + static void SetFocusPoint(const glm::vec2& point); + + // TODO: Temp + // static uint32_t GetFinalColorBufferRendererID(); + + static SceneRendererOptions& GetOptions(); + + private: + static void FlushDrawList(Ref& outRenderPass); + + static void AutoExposurePass(); + static void GeometryPass(); + // static void CompositePass(); + // static void BloomBlurPass(); + static void ShadowMapPass(); + }; +} + + +#endif //PRISM_RENDERER3D_H \ No newline at end of file diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index 44e00d8..2b755b4 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -5,1214 +5,206 @@ #include "SceneRenderer.h" #include "Camera.h" -#include "FrameBuffer.h" #include "Material.h" #include "Mesh.h" #include "Renderer.h" #include "Renderer2D.h" +#include "Renderer3D.h" + #include "RenderPass.h" -#include "glad/glad.h" -#include "Prism/Core/Timer.h" -#include -#include + namespace Prism { struct SceneRendererData { - const Scene* ActiveScene = nullptr; - struct SceneInfo - { - SceneRendererCamera SceneCamera; - float SceneEnvironmentIntensity; - LightEnvironment SceneLightEnvironment; - - // Resources - Ref SkyboxMaterial; - Ref SceneEnvironment; - Light ActiveLight; - } SceneData; - - Ref BRDFLUT; - Ref CompositeShader; - Ref BloomBlurShader; - Ref BloomBlendShader; - - Ref GeoPass; - Ref CompositePass; - Ref BloomBlurPass[2]; - Ref BloomBlendPass; - - // Auto exposure - struct AutoExposureData - { - Ref LuminancePass; - - float CurrentExposure = 1.0f; - bool EnableAutoExposure = true; - float Key = 0.11f; // middle gray - float AdaptationSpeed = 5.0f; // stops per second - - Timer ExposureTimer; - float MaxExposure = 5.0f; - }AutoExposureData; - - Ref ShadowMapShader, ShadowMapAnimShader; - Ref ShadowMapRenderPass; - float ShadowMapSize = 20.0f; - float LightDistance = 0.1f; - glm::mat4 LightMatrices; - 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; - - bool ShadowEnabled = true; - float ShadowBias = 0.001f; - float ShadowIntensity = 0.0f; - int ShadowSoftness = 0; - - struct DrawCommand - { - Ref mesh; - Ref Material; - glm::mat4 Transform; - }; - std::vector DrawList; - std::vector SelectedMeshDrawList; - std::vector ColliderDrawList; - std::vector ShadowPassDrawList; - - Ref OutlineMaterial, OutlineAnimMaterial; - Ref ColliderMaterial; - - // Grid - struct GridData - { - Ref GridMaterial; - - int GridPlane = 0; - float GridScale = 1.0f; - glm::vec4 GridColorThin = glm::vec4(0.5f, 0.5f, 0.5f, 0.4f); - glm::vec4 GridColorThick = glm::vec4(0.5f, 0.5f, 0.5f, 0.6f); - glm::vec4 AxisColorX = glm::vec4(0.9f, 0.2f, 0.2f, 1.0f); - glm::vec4 AxisColorZ = glm::vec4(0.2f, 0.2f, 0.9f, 1.0f); - float FadeDistance = 500.0f; - }GridData; - - SceneRendererOptions Options; + Ref FinalPass; }; - struct SceneRendererStats - { - float ShadowPass = 0.0f; - float GeometryPass = 0.0f; - float CompositePass = 0.0f; - float AutoExposurePass = 0.0f; - - Timer ShadowPassTimer; - Timer GeometryPassTimer; - Timer CompositePassTimer; - Timer AutoExposurePassTimer; - }; - - static SceneRendererData s_Data; - static SceneRendererStats s_Stats; - + SceneRendererData s_Data; void SceneRenderer::Init() { - FramebufferSpecification geoFramebufferSpec; - geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::Depth }; - geoFramebufferSpec.Samples = 8; - geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + FramebufferSpecification finalFramebufferSpec; + finalFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::Depth}; + finalFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; - RenderPassSpecification geoRenderPassSpec; - geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec); - s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec); - - FramebufferSpecification compFramebufferSpec; - compFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 , 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 , FramebufferTextureFormat::RGBA8}; - 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"); - - // Luminance pass: used to compute average scene luminance (for auto exposure) - FramebufferSpecification luminanceFramebufferSpec; - luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; - luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; - - RenderPassSpecification luminanceRenderPassSpec; - luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec); - s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec); - - // Grid - // const auto gridShader = Shader::Create("assets/shaders/Grid.glsl"); - const auto gridShader = Shader::Create("assets/shaders/InfiniteGrid.glsl"); - s_Data.GridData.GridMaterial = MaterialInstance::Create(Material::Create(gridShader)); - // constexpr float gridScale = 16.025f; - // constexpr float gridSize = 0.025f; - // s_Data.GridMaterial->Set("u_Scale", gridScale); - // s_Data.GridMaterial->Set("u_Res", gridSize); - s_Data.GridData.GridMaterial->SetFlag(MaterialFlag::TwoSided, true); - - // outline - const auto outlineShader = Shader::Create("assets/shaders/Outline.glsl"); - s_Data.OutlineMaterial = MaterialInstance::Create(Material::Create(outlineShader)); - s_Data.OutlineMaterial->SetFlag(MaterialFlag::DepthTest, false); - - // Collider - 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; - - RenderPassSpecification shadowMapRenderPassSpec; - shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); - s_Data.ShadowMapRenderPass = 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); - }); + RenderPassSpecification finalRenderPassSpec; + finalRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(finalFramebufferSpec); + s_Data.FinalPass = RenderPass::Create(finalRenderPassSpec); + Renderer2D::Init(); + Renderer3D::Init(); } - void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height) + void SceneRenderer::SetViewportSize(const uint32_t width, const uint32_t height) { - s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height); - s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height); - if (s_Data.AutoExposureData.LuminancePass) - s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->Resize(width, height); + s_Data.FinalPass->GetSpecification().TargetFramebuffer->Resize(width, height); + + Renderer3D::SetViewportSize(width, height); } - void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera) + void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera, const bool enable2DDepthTest) { - PM_CORE_ASSERT(!s_Data.ActiveScene); - - s_Data.ActiveScene = scene; - - 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; + Renderer2D::BeginScene(camera.Camera.GetProjectionMatrix() * camera.ViewMatrix, enable2DDepthTest); + Renderer3D::BeginScene(scene, camera); } void SceneRenderer::EndScene() { - PM_CORE_ASSERT(s_Data.ActiveScene); - - s_Data.ActiveScene = nullptr; - - FlushDrawList(); + Renderer3D::EndScene(s_Data.FinalPass); + Renderer2D::EndScene(s_Data.FinalPass); } + //////////////////// 2D API //////////////////// + + void SceneRenderer::SubmitQuad(const glm::mat4& transform, const glm::vec4& color) + { + Renderer2D::DrawQuad(transform, color); + } + + void SceneRenderer::SubmitQuad(const glm::mat4& transform, const Ref& texture, float tilingFactor, const glm::vec4& tintColor) + { + Renderer2D::DrawQuad(transform, texture, tilingFactor, tintColor); + } + + void SceneRenderer::SubmitBillBoardQuad(const glm::vec3& position, const Ref& texture, const glm::vec3& cameraPos, const glm::vec3& upDirection, float tilingFactor, const glm::vec4& tintColor) + { + Renderer2D::DrawBillBoardQuad(position, texture, cameraPos, upDirection, tilingFactor, tintColor); + } + + void SceneRenderer::SubmitQuad(const glm::vec2& position, const glm::vec2& size, const glm::vec4& color) + { + Renderer2D::DrawQuad(position, size, color); + } + + void SceneRenderer::SubmitQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color) + { + Renderer2D::DrawQuad(position, size, color); + } + + void SceneRenderer::SubmitQuad(const glm::vec2& position, const glm::vec2& size, const Ref& texture, const float tilingFactor, const glm::vec4& tintColor) + { + Renderer2D::DrawQuad(position, size, texture, tilingFactor, tintColor); + } + + void SceneRenderer::SubmitQuad(const glm::vec3& position, const glm::vec2& size, const Ref& texture, + const float tilingFactor, const glm::vec4& tintColor) + { + Renderer2D::DrawQuad(position, size, texture, tilingFactor, tintColor); + } + + void SceneRenderer::SubmitRotatedQuad(const glm::vec2& position, const glm::vec2& size, const float rotation, const glm::vec4& color) + { + Renderer2D::DrawRotatedQuad(position, size, rotation, color); + } + + void SceneRenderer::SubmitRotatedQuad(const glm::vec3& position, const glm::vec2& size, const float rotation, const glm::vec4& color) + { + Renderer2D::DrawRotatedQuad(position, size, rotation, color); + } + + void SceneRenderer::SubmitRotatedQuad(const glm::vec2& position, const glm::vec2& size, const float rotation, const Ref& texture, const float tilingFactor, const glm::vec4& tintColor) + { + Renderer2D::DrawRotatedQuad(position, size, rotation, texture, tilingFactor, tintColor); + } + + void SceneRenderer::SubmitRotatedQuad(const glm::vec3& position, const glm::vec2& size, const float rotation, const Ref& texture, const float tilingFactor, const glm::vec4& tintColor) + { + Renderer2D::DrawRotatedQuad(position, size, rotation, texture, tilingFactor, tintColor); + } + + void SceneRenderer::SubmitRotatedRect(const glm::vec2& position, const glm::vec2& size, const float rotation, const glm::vec4& color) + { + Renderer2D::DrawRotatedRect(position, size, rotation, color); + } + + void SceneRenderer::SubmitRotatedRect(const glm::vec3& position, const glm::vec2& size, const float rotation, const glm::vec4& color) + { + Renderer2D::DrawRotatedRect(position, size, rotation, color); + } + + void SceneRenderer::SubmitCircle(const glm::vec2& position, const float radius, const glm::vec4& color, const float thickness) + { + Renderer2D::DrawCircle(position, radius, color, thickness); + } + + void SceneRenderer::SubmitCircle(const glm::vec3& position, const float radius, const glm::vec4& color, const float thickness) + { + Renderer2D::DrawCircle(position, radius, color, thickness); + } + + void SceneRenderer::SubmitLine(const glm::vec3& p0, const glm::vec3& p1, const glm::vec4& color) + { + Renderer2D::DrawLine(p0, p1, color); + } + + //////////////////// 3D API //////////////////// + void SceneRenderer::SubmitMesh(const Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial) { - // TODO: Culling, sorting, etc. - - s_Data.DrawList.push_back({ mesh, overrideMaterial, transform }); - s_Data.ShadowPassDrawList.push_back({ mesh, overrideMaterial, transform }); + Renderer3D::SubmitMesh(mesh, transform, overrideMaterial); } void SceneRenderer::SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform) { - s_Data.SelectedMeshDrawList.push_back({ mesh, nullptr, transform }); - s_Data.ShadowPassDrawList.push_back({ mesh, nullptr, transform }); + Renderer3D::SubmitSelectedMesh(mesh, transform); } void SceneRenderer::SubmitColliderMesh(const BoxColliderComponent& component, const glm::mat4& parentTransform) { - s_Data.ColliderDrawList.push_back({ component.DebugMesh, nullptr, glm::translate(parentTransform, component.Offset)}); + Renderer3D::SubmitColliderMesh(component, parentTransform); } void SceneRenderer::SubmitColliderMesh(const SphereColliderComponent& component, const glm::mat4& parentTransform) { - s_Data.ColliderDrawList.push_back({ component.DebugMesh, nullptr, parentTransform }); + Renderer3D::SubmitColliderMesh(component, parentTransform); } void SceneRenderer::SubmitColliderMesh(const CapsuleColliderComponent& component, const glm::mat4& parentTransform) { - s_Data.ColliderDrawList.push_back({ component.DebugMesh, nullptr, parentTransform }); + Renderer3D::SubmitColliderMesh(component, parentTransform); } void SceneRenderer::SubmitColliderMesh(const MeshColliderComponent& component, const glm::mat4& parentTransform) { - for (const auto debugMesh : component.ProcessedMeshes) - s_Data.ColliderDrawList.push_back({ debugMesh, nullptr, parentTransform }); + Renderer3D::SubmitColliderMesh(component, parentTransform); } - - static Ref equirectangularConversionShader, envFilteringShader, envIrradianceShader; - std::pair, Ref> SceneRenderer::CreateEnvironmentMap(const std::string& filepath) { - constexpr uint32_t cubemapSize = 2048; - constexpr uint32_t irradianceMapSize = 32; - - Ref envUnfiltered = TextureCube::Create(TextureFormat::Float16, cubemapSize, cubemapSize); - if (!equirectangularConversionShader) - equirectangularConversionShader = Shader::Create("assets/shaders/EquirectangularToCubeMap.glsl"); - Ref envEquirect = Texture2D::Create(filepath); - if (envEquirect->GetFormat() != TextureFormat::Float16) - PM_CORE_WARN("Texture is not HDR!"); - - equirectangularConversionShader->Bind(); - envEquirect->Bind(); - Renderer::Submit([envUnfiltered, cubemapSize, envEquirect]() - { - glBindImageTexture(0, envUnfiltered->GetRendererID(), 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); - glDispatchCompute(cubemapSize / 32, cubemapSize / 32, 6); - glGenerateTextureMipmap(envUnfiltered->GetRendererID()); - }); - - - if (!envFilteringShader) - envFilteringShader = Shader::Create("assets/shaders/EnvironmentMipFilter.glsl"); - - Ref envFiltered = TextureCube::Create(TextureFormat::Float16, cubemapSize, cubemapSize); - - Renderer::Submit([envUnfiltered, envFiltered]() - { - glCopyImageSubData(envUnfiltered->GetRendererID(), GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0, - envFiltered->GetRendererID(), GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0, - envFiltered->GetWidth(), envFiltered->GetHeight(), 6); - }); - - envFilteringShader->Bind(); - envUnfiltered->Bind(); - - Renderer::Submit([envUnfiltered, envFiltered, cubemapSize]() { - const float deltaRoughness = 1.0f / glm::max((float)(envFiltered->GetMipLevelCount() - 1.0f), 1.0f); - for (int level = 1, size = cubemapSize / 2; level < (int)envFiltered->GetMipLevelCount(); level++, size /= 2) // <= ? - { - const GLuint numGroups = glm::max(1, size / 32); - glBindImageTexture(0, envFiltered->GetRendererID(), level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); - glProgramUniform1f(envFilteringShader->GetRendererID(), 0, level * deltaRoughness); - glDispatchCompute(numGroups, numGroups, 6); - } - }); - - if (!envIrradianceShader) - envIrradianceShader = Shader::Create("assets/shaders/EnvironmentIrradiance.glsl"); - - Ref irradianceMap = TextureCube::Create(TextureFormat::Float16, irradianceMapSize, irradianceMapSize); - envIrradianceShader->Bind(); - envFiltered->Bind(); - Renderer::Submit([irradianceMap]() - { - glBindImageTexture(0, irradianceMap->GetRendererID(), 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA16F); - glDispatchCompute(irradianceMap->GetWidth() / 32, irradianceMap->GetHeight() / 32, 6); - glGenerateTextureMipmap(irradianceMap->GetRendererID()); - }); - - return { envFiltered, irradianceMap }; + return Renderer3D::CreateEnvironmentMap(filepath); } + // TODO: this maybe storage in SceneRenderer Ref SceneRenderer::GetFinalRenderPass() { - return s_Data.CompositePass; + return s_Data.FinalPass; + } + + Ref SceneRenderer::GetGeoPass() + { + return Renderer3D::GetGeoPass(); } Ref SceneRenderer::GetFinalColorBuffer() { - PM_CORE_ASSERT(false, "Not implemented"); - return nullptr; + return Renderer3D::GetFinalColorBuffer(); } void SceneRenderer::SetFocusPoint(const glm::vec2& point) { - s_Data.FocusPoint = point; + Renderer3D::SetFocusPoint(point); } uint32_t SceneRenderer::GetFinalColorBufferRendererID() { - return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(); + return s_Data.FinalPass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(); } + // TODO: this maybe storage in SceneRenderer, think about Renderer2D option SceneRendererOptions& SceneRenderer::GetOptions() { - return s_Data.Options; - } - - - void SceneRenderer::FlushDrawList() - { - PM_CORE_ASSERT(!s_Data.ActiveScene); - - 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(); }); - } - - // Compute average luminance and update exposure (GPU-copy + mipmap -> read 1 texel) - { - Renderer::Submit([]() { s_Stats.AutoExposurePassTimer.Reset(); }); - AutoExposurePass(); - Renderer::Submit([] { s_Stats.AutoExposurePass = s_Stats.AutoExposurePassTimer.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 = {}; - } - - void SceneRenderer::GeometryPass() - { - const bool outline = !s_Data.SelectedMeshDrawList.empty(); - const bool collider = !s_Data.ColliderDrawList.empty(); - - if (outline) - { - Renderer::Submit([]() - { - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - }); - } - - Renderer::BeginRenderPass(s_Data.GeoPass); - - if (outline) - { - Renderer::Submit([]() - { - glStencilMask(0); - }); - } - - const auto& sceneCamera = s_Data.SceneData.SceneCamera; - - const auto cameraProjection = sceneCamera.Camera.GetProjectionMatrix(); - const auto cameraView = sceneCamera.ViewMatrix; - const auto cameraViewProjection = cameraProjection * cameraView; - 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(cameraViewProjection)); - 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", cameraViewProjection); - 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); - baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment->IrradianceMap); - baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT); - - // Set lights (TODO: move to light environment and don't do per mesh) - - baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]); - // baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight); - - // shadow - baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices); - baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowEnabled); - baseMaterial->Set("u_ShadowBias", s_Data.ShadowBias); - baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowIntensity); - - auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap"); - if (rd) - { - auto reg = rd->GetRegister(); - auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); - - Renderer::Submit([reg, tex]() mutable - { - glBindTextureUnit(reg, tex); - glBindSampler(reg++, s_Data.ShadowMapSampler); - }); - } - - /* - 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", cameraViewProjection); - 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]); - // baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight); - - - // shadow - baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices); - baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowEnabled); - baseMaterial->Set("u_ShadowBias", s_Data.ShadowBias); - baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowIntensity); - - - auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap"); - if (rd) - { - auto reg = rd->GetRegister(); - auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); - - Renderer::Submit([reg, tex]() mutable - { - glBindTextureUnit(reg, tex); - glBindSampler(reg, s_Data.ShadowMapSampler); - }); - } - /* - 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_NOTEQUAL, 1, 0xff); - glStencilMask(0); - - glLineWidth(10); - glEnable(GL_LINE_SMOOTH); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glDisable(GL_DEPTH_TEST); - }); - - // Draw outline here - s_Data.OutlineMaterial->Set("u_ViewProjection", cameraViewProjection); - s_Data.OutlineAnimMaterial->Set("u_ViewProjection", cameraViewProjection); - for (auto& dc : s_Data.SelectedMeshDrawList) - { - // 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([]() - { - glPointSize(10); - glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); - }); - for (auto& dc : s_Data.SelectedMeshDrawList) - { - // Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial); - Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.mesh->IsAnimated() ? s_Data.OutlineAnimMaterial : s_Data.OutlineMaterial); - } - - Renderer::Submit([]() - { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glStencilMask(0xff); - glStencilFunc(GL_ALWAYS, 1, 0xff); - glEnable(GL_DEPTH_TEST); - }); - } - - if (collider) - { - Renderer::Submit([]() - { - // glStencilFunc(GL_NOTEQUAL, 1, 0xff); - // glStencilMask(0); - - glLineWidth(1); - glEnable(GL_LINE_SMOOTH); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glDisable(GL_DEPTH_TEST); - }); - - s_Data.ColliderMaterial->Set("u_ViewProjection", cameraViewProjection); - for (auto& dc : s_Data.ColliderDrawList) - { - if (dc.mesh) - Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.ColliderMaterial); - } - - Renderer::Submit([]() - { - glPointSize(1); - glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); - }); - - for (auto& dc : s_Data.ColliderDrawList) - { - if (dc.mesh) - Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.ColliderMaterial); - } - - Renderer::Submit([]() - { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - // glStencilMask(0xff); - // glStencilFunc(GL_ALWAYS, 1, 0xff); - glEnable(GL_DEPTH_TEST); - }); - } - - - // Grid - const auto option = GetOptions(); - if (option.ShowGrid) - { - s_Data.GridData.GridMaterial->Set("u_View", sceneCamera.ViewMatrix); - s_Data.GridData.GridMaterial->Set("u_Projection", cameraProjection); - s_Data.GridData.GridMaterial->Set("u_CameraPosition", cameraPosition); - - // grid config - s_Data.GridData.GridMaterial->Set("u_GridPlane", s_Data.GridData.GridPlane); - s_Data.GridData.GridMaterial->Set("u_GridScale", s_Data.GridData.GridScale); - s_Data.GridData.GridMaterial->Set("u_GridColorThin", s_Data.GridData.GridColorThin); - s_Data.GridData.GridMaterial->Set("u_GridColorThick", s_Data.GridData.GridColorThick); - s_Data.GridData.GridMaterial->Set("u_AxisColorX", s_Data.GridData.AxisColorX); - s_Data.GridData.GridMaterial->Set("u_AxisColorZ", s_Data.GridData.AxisColorZ); - s_Data.GridData.GridMaterial->Set("u_FadeDistance", s_Data.GridData.FadeDistance); - - Renderer::SubmitFullscreenQuad(s_Data.GridData.GridMaterial); - } - - if (option.ShowBoundingBoxes) - { - Renderer2D::BeginScene(cameraViewProjection); - for (auto& dc : s_Data.DrawList) - Renderer::DrawAABB(dc.mesh, dc.Transform); - Renderer2D::EndScene(); - } - - Renderer::EndRenderPass(); - } - - void SceneRenderer::AutoExposurePass() - { - if (!s_Data.AutoExposureData.EnableAutoExposure) - return; - - auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; - auto dstFB = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer; - if (!srcFB || !dstFB) - return; - - const uint32_t dstID = dstFB->GetColorAttachmentRendererID(); - const uint32_t width = dstFB->GetWidth(); - const uint32_t height = dstFB->GetHeight(); - - Renderer::Submit([dstID, width, height, srcFB]() mutable - { - // Use framebuffer blit to resolve multisampled source into non-multisampled luminance target. - const GLuint srcFBO = srcFB->GetRendererID(); - const GLuint dstFBO = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID(); - - // Bind read/draw FBOs and blit color attachment 0 - glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glDrawBuffer(GL_COLOR_ATTACHMENT0); - - // Source size — try to use srcFB dimensions if available - const int srcWidth = srcFB->GetWidth(); - const int srcHeight = srcFB->GetHeight(); - - glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); - - // Unbind - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - - // Generate mipmaps so the smallest mip is the average color - glGenerateTextureMipmap(dstID); - - // Determine highest mip level - int maxLevel = (int)std::floor(std::log2((float)std::max(width, height))); - if (maxLevel < 0) maxLevel = 0; - - - GLint levelWidth = 0, levelHeight = 0; - glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_WIDTH, &levelWidth); - glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_HEIGHT, &levelHeight); - - if (levelWidth == 0 || levelHeight == 0) return; - - const int bufSize = levelWidth * levelHeight * 4 * sizeof(float); - std::vector pixelData(bufSize / sizeof(float)); - - glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, bufSize, pixelData.data()); - - // Sanitize pixel values (handle NaN/Inf or negative values coming from GPU) - for (int i = 0; i < 3; ++i) - { - if (!std::isfinite(pixelData[i]) || pixelData[i] < 0.0f) - pixelData[i] = 0.0f; - } - - // Convert to luminance - float lum = 0.2126f * pixelData[0] + 0.7152f * pixelData[1] + 0.0722f * pixelData[2]; - if (!std::isfinite(lum) || lum <= 0.0f) - lum = 1e-6f; // fallback minimum luminance - - // Compute desired exposure (simple key/avg approach) - const float key = s_Data.AutoExposureData.Key; - constexpr float minLum = 1e-6f; - float desiredExposure = key / std::max(lum, minLum); - desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure); - - - // Adapt exposure over time (exponential) - const float dt = s_Data.AutoExposureData.ExposureTimer.ElapsedMillis() / 1000.0f; - s_Data.AutoExposureData.ExposureTimer.Reset(); - const float tau = s_Data.AutoExposureData.AdaptationSpeed; - const float adaptFactor = 1.0f - std::exp(-tau * dt); - s_Data.AutoExposureData.CurrentExposure = s_Data.AutoExposureData.CurrentExposure + (desiredExposure - s_Data.AutoExposureData.CurrentExposure) * adaptFactor; - s_Data.AutoExposureData.CurrentExposure = std::clamp(s_Data.AutoExposureData.CurrentExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure); - - // Write exposure directly into composite shader program uniform so the subsequent composite pass uses it - /* - if (const GLuint prog = s_Data.CompositeShader->GetRendererID()) - { - const GLint loc = glGetUniformLocation(prog, "u_Exposure"); - if (loc >= 0) - glProgramUniform1f(prog, loc, s_Data.CurrentExposure); - } - */ - }); - - } - - 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.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : 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.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom); - - 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(); - auto & adad =s_Data; - // s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); - s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure()); - s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom); - - s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0); - s_Data.BloomBlurPass[index]->GetSpecification().TargetFramebuffer->BindTexture(1); - - Renderer::SubmitFullscreenQuad(nullptr); - Renderer::EndRenderPass(); - } - } - - struct FrustumBounds - { - float r, l, b, t, f, n; - }; - - struct CascadeData - { - glm::mat4 ViewProj; - glm::mat4 View; - float SplitDepth; - }; - - - static void CalculateCascades(CascadeData* cascades, const glm::vec3& lightDirection) - { - // FrustumBounds frustumBounds[3]; - - auto& sceneCamera = s_Data.SceneData.SceneCamera; - auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix; - - constexpr int SHADOW_MAP_CASCADE_COUNT = 4; - float cascadeSplits[SHADOW_MAP_CASCADE_COUNT]; - - // TODO: less hard-coding! - float nearClip = 0.1f; - float farClip = 1000.0f; - float clipRange = farClip - nearClip; - - float minZ = nearClip; - float maxZ = nearClip + clipRange; - - float range = maxZ - minZ; - float ratio = maxZ / minZ; - - // Calculate split depths based on view camera frustum - // Based on method presented in https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html - for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++) - { - float p = (i + 1) / static_cast(SHADOW_MAP_CASCADE_COUNT); - float log = minZ * std::pow(ratio, p); - float uniform = minZ + range * p; - float d = s_Data.CascadeSplitLambda * (log - uniform) + uniform; - cascadeSplits[i] = (d - nearClip) / clipRange; - } - - // cascadeSplits[3] = 0.3f; - - // Manually set cascades here - // cascadeSplits[0] = 0.05f; - // cascadeSplits[1] = 0.15f; - // cascadeSplits[2] = 0.3f; - // cascadeSplits[3] = 1.0f; - - // Calculate orthographic projection matrix for each cascade - float lastSplitDist = 1.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); - - // Offset to texel space to avoid shimmering (from https://stackoverflow.com/questions/33499053/cascaded-shadow-map-shimmering) - glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix; - constexpr float ShadowMapResolution = 4096.0f; - glm::vec4 shadowOrigin = (shadowMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) * ShadowMapResolution / 2.0f; - glm::vec4 roundedOrigin = glm::round(shadowOrigin); - glm::vec4 roundOffset = roundedOrigin - shadowOrigin; - roundOffset = roundOffset * 2.0f / ShadowMapResolution; - roundOffset.z = 0.0f; - roundOffset.w = 0.0f; - - lightOrthoMatrix[3] += roundOffset; - - // 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); - Renderer::EndRenderPass(); - } - return; - } - - glm::vec3 lightDir = glm::normalize(directionalLights[0].Direction); // 光线方向(从光源指向场景) - glm::vec3 lightPos = lightDir * 10.0f; - - glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - - float orthoSize = 100.0f; - float nearPlane = 0.01f, farPlane = 1000.0f; - glm::mat4 lightProjection = glm::ortho(-orthoSize, orthoSize, -orthoSize, orthoSize, nearPlane, farPlane); - glm::mat4 lightSpaceMatrix = lightProjection * lightView; - - Renderer::Submit([]() - { - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - }); - - { - Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass); - - s_Data.LightMatrices = lightSpaceMatrix; - - - // Render entities - for (auto& dc : s_Data.ShadowPassDrawList) - { - Ref shader = dc.mesh->IsAnimated() ? s_Data.ShadowMapAnimShader : s_Data.ShadowMapShader; - shader->SetMat4("u_ViewProjection", lightSpaceMatrix); - Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader); - } - - Renderer::EndRenderPass(); - } - } - - void SceneRenderer::OnImGuiRender() - { - ImGui::Begin("Scene Renderer"); - - UI::Property("Composite Pass time", s_Stats.CompositePass); - UI::Property("Geometry Pass time", s_Stats.GeometryPass); - UI::Property("Shadow Pass time", s_Stats.ShadowPass); - UI::Property("AutoExposure Pass time", s_Stats.AutoExposurePass); - - if (UI::BeginTreeNode("Grid Config", false)) - { - // Grid plane: 0 = XZ (Y up), 1 = XY (Z forward), 2 = YZ (X right) - const char* projTypeStrings[] = { "XZ (Y up)", "XY (Z forward)", "YZ (X right)" }; - const char* currentMode = projTypeStrings[s_Data.GridData.GridPlane]; - if (ImGui::BeginCombo("Grid Plane Mode", currentMode)) - { - for (int type = 0; type < 3; type++) - { - const bool is_selected = (currentMode == projTypeStrings[type]); - if (ImGui::Selectable(projTypeStrings[type], is_selected)) - { - currentMode = projTypeStrings[type]; - s_Data.GridData.GridPlane = type; - } - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - - UI::BeginPropertyGrid(); - UI::Property("GridScale", s_Data.GridData.GridScale); - UI::PropertyColor("GridColorThin", s_Data.GridData.GridColorThin); - UI::PropertyColor("GridColorThick", s_Data.GridData.GridColorThick); - UI::PropertyColor("AxisColorX", s_Data.GridData.AxisColorX); - UI::PropertyColor("AxisColorZ", s_Data.GridData.AxisColorZ); - UI::Property("FadeDistance ", s_Data.GridData.FadeDistance); - UI::EndPropertyGrid(); - UI::EndTreeNode(); - } - - if (UI::BeginTreeNode("Shadows", false)) - { - if (UI::BeginTreeNode("Shadow Map", false)) - { - - UI::BeginPropertyGrid(); - UI::Property("EnableMap", s_Data.ShadowEnabled); - UI::Property("ShadowBias", s_Data.ShadowBias, 0.01f); - UI::Property("ShadowSoftness", s_Data.ShadowSoftness); - UI::Property("ShadowIntensity", s_Data.ShadowIntensity, 0.01f); - UI::EndPropertyGrid(); - - auto fb = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer; - auto id = fb->GetDepthAttachmentRendererID(); - - float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f - ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 }); - UI::EndTreeNode(); - } - - UI::EndTreeNode(); - } - - if (UI::BeginTreeNode("Bloom", false)) - { - 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; - const auto id = fb->GetColorAttachmentRendererID(); - - const 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(); - } - - if (UI::BeginTreeNode("Auto Exposure", false)) - { - UI::BeginPropertyGrid(); - UI::Property("Enable Auto Exposure", s_Data.AutoExposureData.EnableAutoExposure); - UI::Property("Key (middle gray)", s_Data.AutoExposureData.Key, 0.001f, 0.001f, 2.5f); - UI::Property("Adaptation Speed", s_Data.AutoExposureData.AdaptationSpeed, 0.01f, 0.001f, 5.0f); - UI::Property("Current Exposure", s_Data.AutoExposureData.CurrentExposure, 0.01f, 0.0f, 0.0f, true); - UI::EndPropertyGrid(); - - UI::EndTreeNode(); - } - - - ImGui::End(); + return Renderer3D::GetOptions(); } } diff --git a/Prism/src/Prism/Renderer/SceneRenderer.h b/Prism/src/Prism/Renderer/SceneRenderer.h index ec41c7b..78d3484 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.h +++ b/Prism/src/Prism/Renderer/SceneRenderer.h @@ -35,9 +35,44 @@ namespace Prism static void SetViewportSize(uint32_t width, uint32_t height); - static void BeginScene(const Scene* scene, const SceneRendererCamera& camera); + static void BeginScene(const Scene* scene, const SceneRendererCamera& camera, bool enable2DDepthTest = true); static void EndScene(); + // Renderer2D + static void SubmitQuad(const glm::mat4& transform, const glm::vec4& color); + static void SubmitQuad(const glm::mat4& transform, const Ref& texture, float tilingFactor = 1.0f, const glm::vec4& tintColor = glm::vec4(1.0f)); + + /** + * draw a Billboard's texture + * @param position entity position + * @param texture entity texture + * @param cameraPos editcamera or camera postion + * @param upDirection default (0.0f, 1.0f, 0.0f) + * @param tilingFactor default 0 + * @param tintColor default (1.0f, 1.0f, 1.0f) + */ + static void SubmitBillBoardQuad(const glm::vec3& position, const Ref& texture, const glm::vec3& cameraPos, const glm::vec3& upDirection = glm::vec3(0.0f, 1.0f, 0.0f), float tilingFactor = 1.0f, const glm::vec4& tintColor = glm::vec4(1.0f)); + + static void SubmitQuad(const glm::vec2& position, const glm::vec2& size, const glm::vec4& color); + static void SubmitQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color); + static void SubmitQuad(const glm::vec2& position, const glm::vec2& size, const Ref& texture, float tilingFactor = 1.0f, const glm::vec4& tintColor = glm::vec4(1.0f)); + static void SubmitQuad(const glm::vec3& position, const glm::vec2& size, const Ref& texture, float tilingFactor = 1.0f, const glm::vec4& tintColor = glm::vec4(1.0f)); + + static void SubmitRotatedQuad(const glm::vec2& position, const glm::vec2& size, float rotation, const glm::vec4& color); + static void SubmitRotatedQuad(const glm::vec3& position, const glm::vec2& size, float rotation, const glm::vec4& color); + static void SubmitRotatedQuad(const glm::vec2& position, const glm::vec2& size, float rotation, const Ref& texture, float tilingFactor = 1.0f, const glm::vec4& tintColor = glm::vec4(1.0f)); + static void SubmitRotatedQuad(const glm::vec3& position, const glm::vec2& size, float rotation, const Ref& texture, float tilingFactor = 1.0f, const glm::vec4& tintColor = glm::vec4(1.0f)); + + static void SubmitRotatedRect(const glm::vec2& position, const glm::vec2& size, float rotation, const glm::vec4& color); + static void SubmitRotatedRect(const glm::vec3& position, const glm::vec2& size, float rotation, const glm::vec4& color); + + static void SubmitCircle(const glm::vec2& position, float radius, const glm::vec4& color, float thickness = 0.05f); + static void SubmitCircle(const glm::vec3& position, float radius, const glm::vec4& color, float thickness = 0.05f); + + static void SubmitLine(const glm::vec3& p0, const glm::vec3& p1, const glm::vec4& color = glm::vec4(1.0f)); + + + // Renderer3D static void SubmitMesh(const Ref& mesh, const glm::mat4& transform = glm::mat4(1.0f), const Ref& overrideMaterial = nullptr); static void SubmitSelectedMesh(const Ref& mesh, const glm::mat4& transform = glm::mat4(1.0f)); @@ -49,24 +84,16 @@ namespace Prism static std::pair, Ref> CreateEnvironmentMap(const std::string& filepath); static Ref GetFinalRenderPass(); + static Ref GetGeoPass(); static Ref GetFinalColorBuffer(); static void SetFocusPoint(const glm::vec2& point); // TODO: Temp static uint32_t GetFinalColorBufferRendererID(); - static SceneRendererOptions& GetOptions(); - static void OnImGuiRender(); private: - static void FlushDrawList(); - static void AutoExposurePass(); - static void GeometryPass(); - static void CompositePass(); - static void BloomBlurPass(); - - static void ShadowMapPass(); }; } diff --git a/Prism/src/Prism/Scene/Components.h b/Prism/src/Prism/Scene/Components.h index ec10f9d..2edbea4 100644 --- a/Prism/src/Prism/Scene/Components.h +++ b/Prism/src/Prism/Scene/Components.h @@ -19,6 +19,7 @@ #include #include +#include "Prism/Animation/Skeleton.h" #include "Prism/Core/Math/Math.h" #include "Prism/Renderer/SceneEnvironment.h" @@ -99,6 +100,16 @@ namespace Prism operator Ref () { return Mesh; } }; + struct AnimationComponent + { + Ref SkeletonAsset; + Ref AnimationController; + + AnimationComponent() = default; + AnimationComponent(const AnimationComponent&) = default; + }; + + struct ScriptComponent { diff --git a/Prism/src/Prism/Scene/Entity.h b/Prism/src/Prism/Scene/Entity.h index 5735529..fdf7617 100644 --- a/Prism/src/Prism/Scene/Entity.h +++ b/Prism/src/Prism/Scene/Entity.h @@ -36,6 +36,20 @@ namespace Prism return m_Scene->m_Registry.get(m_EntityHandle); } + template + T& GetComponent() const + { + PM_CORE_ASSERT(HasComponent(), "Entity doesn't have component!"); + return m_Scene->m_Registry.get(m_EntityHandle); + } + + template + const T* TryGetComponent() const + { + return m_Scene->m_Registry.try_get(m_EntityHandle); + } + + template bool HasComponent() const { @@ -77,6 +91,7 @@ namespace Prism UUID GetUUID() { return GetComponent().ID; } + UUID GetUUID() const { return GetComponent().ID; } UUID GetSceneUUID() const { return m_Scene->GetUUID(); } private: diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp index c5729fd..8f2d43b 100644 --- a/Prism/src/Prism/Scene/Scene.cpp +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -23,7 +23,6 @@ #include "Prism/Core/Input.h" #include "Prism/Core/Math/Math.h" #include "Prism/Renderer/Renderer.h" -#include "Prism/Renderer/Renderer2D.h" namespace Prism { @@ -233,18 +232,21 @@ namespace Prism void Scene::OnRenderRuntime(const TimeStep ts) { - ///////////////////////////////////////////////////////////////////// - // RENDER 3D SCENE // - ///////////////////////////////////////////////////////////////////// Entity cameraEntity = GetMainCameraEntity(); if (!cameraEntity) return; - const glm::mat4 cameraViewMatrix = glm::inverse(GetTransformRelativeToParent(cameraEntity)); + const glm::mat4 cameraViewMatrix = glm::inverse(GetTransformRelativeToParent(cameraEntity)); PM_CORE_ASSERT(cameraEntity, "Scene does not contain any cameras!"); SceneCamera& camera = cameraEntity.GetComponent(); camera.SetViewportSize(m_ViewportWidth, m_ViewportHeight); + + + ///////////////////////////////////////////////////////////////////// + // RENDER 3D SCENE // + ///////////////////////////////////////////////////////////////////// + // Process lights { m_LightEnvironment = LightEnvironment(); @@ -281,49 +283,45 @@ namespace Prism m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); - auto group = m_Registry.group(entt::get); SceneRenderer::BeginScene(this, { static_cast(camera), cameraViewMatrix }); - for (auto entity : group) { - const auto& [transformComponent, meshComponent] = group.get(entity); - if (meshComponent.Mesh) + const auto group = m_Registry.group(entt::get); + for (const auto entity : group) { - meshComponent.Mesh->OnUpdate(ts); + const auto& [transformComponent, meshComponent] = group.get(entity); + if (meshComponent.Mesh) + { + meshComponent.Mesh->OnUpdate(ts); - glm::mat4 transform = GetTransformRelativeToParent(Entity(entity, this)); + glm::mat4 transform = GetTransformRelativeToParent(Entity(entity, this)); - // TODO: Should we render (logically) - SceneRenderer::SubmitMesh(meshComponent, transform); + // TODO: Should we render (logically) + SceneRenderer::SubmitMesh(meshComponent, transform); + } } + ///////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////// + // RENDER 2D SCENE // + ///////////////////////////////////////////////////////////////////// + + + // render sprite + { + const auto entitiesGroup = m_Registry.group(entt::get); + for (const auto entity : entitiesGroup) + { + auto [transformComponent, spriteRendererComponent] = entitiesGroup.get(entity); + if (spriteRendererComponent.Texture) + SceneRenderer::SubmitQuad(transformComponent.GetTransform(), spriteRendererComponent.Texture, spriteRendererComponent.TilingFactor, spriteRendererComponent.Color); + else + SceneRenderer::SubmitQuad(transformComponent.GetTransform(), spriteRendererComponent.Color); + } + } + + ///////////////////////////////////////////////////////////////////// } SceneRenderer::EndScene(); - ///////////////////////////////////////////////////////////////////// - - - ///////////////////////////////////////////////////////////////////// - // RENDER 2D SCENE // - ///////////////////////////////////////////////////////////////////// - - Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); - const glm::mat4 cameraViewProjection = cameraViewMatrix * camera.GetProjectionMatrix(); - Renderer2D::BeginScene(cameraViewProjection, false); - - // render sprite - { - const auto entitiesGroup = m_Registry.group(entt::get); - for (const auto entity : entitiesGroup) - { - auto [transformComponent, spriteRendererComponent] = entitiesGroup.get(entity); - if (spriteRendererComponent.Texture) - Renderer2D::DrawQuad(transformComponent.GetTransform(), spriteRendererComponent.Texture, spriteRendererComponent.TilingFactor, spriteRendererComponent.Color); - else - Renderer2D::DrawQuad(transformComponent.GetTransform(), spriteRendererComponent.Color); - } - } - - - Renderer2D::EndScene(); - Renderer::EndRenderPass(); } void Scene::OnRenderEditor(const TimeStep ts, const EditorCamera& editorCamera) @@ -335,12 +333,12 @@ namespace Prism // Process lights { m_LightEnvironment = LightEnvironment(); - auto lights = m_Registry.group(entt::get); + const auto lights = m_Registry.group(entt::get); uint32_t directionalLightIndex = 0; - for (auto entity : lights) + for (const auto entity : lights) { auto [transformComponent, lightComponent] = lights.get(entity); - glm::vec3 direction = -glm::normalize(glm::mat3(transformComponent.GetTransform()) * glm::vec3(1.0f)); + const glm::vec3 direction = -glm::normalize(glm::mat3(transformComponent.GetTransform()) * glm::vec3(1.0f)); m_LightEnvironment.DirectionalLights[directionalLightIndex++] = { direction, @@ -353,148 +351,105 @@ namespace Prism // TODO: only one sky light at the moment! { - auto lights = m_Registry.group(entt::get); + const auto lights = m_Registry.group(entt::get); if (!lights.empty()) { - for (auto entity : lights) + for (const auto entity : lights) { auto [transformComponent, skyLightComponent] = lights.get(entity); m_Environment = skyLightComponent.SceneEnvironment; m_EnvironmentIntensity = skyLightComponent.Intensity; SetSkybox(m_Environment->RadianceMap); } - }else - { - // TODO: } } m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); - auto group = m_Registry.group(entt::get); - SceneRenderer::BeginScene(this, { static_cast(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f }); // TODO: real values + // TODO: this value should be storage and can modify + + SceneRenderer::BeginScene(this, { static_cast(editorCamera), editorCamera.GetViewMatrix(), 0.1f, 1000.0f, 45.0f}); { - for (const auto entity : group) + const auto group = m_Registry.group(entt::get); { - const auto& [transformComponent, meshComponent] = group.get(entity); - if (meshComponent.Mesh) + for (const auto [entity, meshComponent, transformComponent]: group.each()) { - meshComponent.Mesh->OnUpdate(ts); + Entity e = {entity, this}; - glm::mat4 transform = GetTransformRelativeToParent(Entity{ entity, this }); + if (meshComponent.Mesh) + { + meshComponent.Mesh->OnUpdate(ts); - // TODO: Should we render (logically) + glm::mat4 transform = GetTransformRelativeToParent(e); + + // TODO: Should we render (logically) + if (m_SelectedEntity == entity) + SceneRenderer::SubmitSelectedMesh(meshComponent, transform); + else + SceneRenderer::SubmitMesh(meshComponent, transform); + } + + // Renderer Collider if (m_SelectedEntity == entity) - SceneRenderer::SubmitSelectedMesh(meshComponent, transform); + { + glm::mat4 transform = GetTransformRelativeToParent(e); + + if (const auto collider = e.TryGetComponent()) + SceneRenderer::SubmitColliderMesh(*collider, transform); + if (const auto collider = e.TryGetComponent()) + SceneRenderer::SubmitColliderMesh(*collider, transform); + if (const auto collider = e.TryGetComponent()) + SceneRenderer::SubmitColliderMesh(*collider, transform); + if (const auto collider = e.TryGetComponent()) + SceneRenderer::SubmitColliderMesh(*collider, transform); + } + } + } + ///////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////// + // RENDER 2D SCENE // + ///////////////////////////////////////////////////////////////////// + + // render sprite + { + const auto entitiesGroup = m_Registry.group(entt::get); + for (const auto entity : entitiesGroup) + { + auto [transformComponent, spriteRendererComponent] = entitiesGroup.get(entity); + if (spriteRendererComponent.Texture) + SceneRenderer::SubmitQuad(transformComponent.GetTransform(), spriteRendererComponent.Texture, spriteRendererComponent.TilingFactor, spriteRendererComponent.Color); else - SceneRenderer::SubmitMesh(meshComponent, transform); + SceneRenderer::SubmitQuad(transformComponent.GetTransform(), spriteRendererComponent.Color); } } + // render camera icon { - const auto view = m_Registry.view(); - for (const auto entity : view) + const auto cameras = m_Registry.view(); + if (!cameras.empty()) { - Entity e = { entity, this }; - auto& collider = e.GetComponent(); - glm::mat4 transform = GetTransformRelativeToParent(e); + for (auto& entity : cameras) + { + Entity e = { entity, this }; + SceneRenderer::SubmitBillBoardQuad(e.Transform().Translation, m_CameraIcon, editorCamera.GetPosition(), editorCamera.GetUpDirection()); + } + } - if (m_SelectedEntity == entity) - SceneRenderer::SubmitColliderMesh(collider, transform); - } - } - - { - const auto view = m_Registry.view(); - for (const auto entity : view) - { - Entity e = { entity, this }; - auto& collider = e.GetComponent(); - glm::mat4 transform = GetTransformRelativeToParent(e); - - if (m_SelectedEntity == entity) - SceneRenderer::SubmitColliderMesh(collider, transform); - } - } - - { - const auto view = m_Registry.view(); - for (const auto entity : view) - { - Entity e = { entity, this }; - auto& collider = e.GetComponent(); - glm::mat4 transform = GetTransformRelativeToParent(e); - - if (m_SelectedEntity == entity) - SceneRenderer::SubmitColliderMesh(collider, transform); - } - } - - { - const auto view = m_Registry.view(); - for (const auto entity : view) - { - Entity e = { entity, this }; - auto& collider = e.GetComponent(); - glm::mat4 transform = GetTransformRelativeToParent(e); - - if (m_SelectedEntity == entity) - SceneRenderer::SubmitColliderMesh(collider, transform); + const auto lights = m_Registry.view(); + if (!lights.empty()) + { + for (auto& entity : lights) + { + Entity e = { entity, this }; + SceneRenderer::SubmitBillBoardQuad(e.Transform().Translation, m_LightIcon, editorCamera.GetPosition(), editorCamera.GetUpDirection()); + } } } + ///////////////////////////////////////////////////////////////////// } - SceneRenderer::EndScene(); - ///////////////////////////////////////////////////////////////////// - - - ///////////////////////////////////////////////////////////////////// - // RENDER 2D SCENE // - ///////////////////////////////////////////////////////////////////// - - Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false); - const auto viewProj = editorCamera.GetViewProjection(); - Renderer2D::BeginScene(viewProj, false); - - // render sprite - { - const auto entitiesGroup = m_Registry.group(entt::get); - for (const auto entity : entitiesGroup) - { - auto [transformComponent, spriteRendererComponent] = entitiesGroup.get(entity); - if (spriteRendererComponent.Texture) - Renderer2D::DrawQuad(transformComponent.GetTransform(), spriteRendererComponent.Texture, spriteRendererComponent.TilingFactor, spriteRendererComponent.Color); - else - Renderer2D::DrawQuad(transformComponent.GetTransform(), spriteRendererComponent.Color); - } - } - - - // render camera icon - { - const auto cameras = m_Registry.view(); - if (!cameras.empty()) - { - for (auto& entity : cameras) - { - Entity e = { entity, this }; - Renderer2D::DrawBillBoardQuad(e.Transform().Translation, m_CameraIcon, editorCamera.GetPosition(), editorCamera.GetUpDirection()); - } - } - - const auto lights = m_Registry.view(); - if (!lights.empty()) - { - for (auto& entity : lights) - { - Entity e = { entity, this }; - Renderer2D::DrawBillBoardQuad(e.Transform().Translation, m_LightIcon, editorCamera.GetPosition(), editorCamera.GetUpDirection()); - } - } - } - - Renderer2D::EndScene(); - Renderer::EndRenderPass(); } void Scene::OnEvent(Event& e) diff --git a/Prism/src/Prism/Scene/Scene.h b/Prism/src/Prism/Scene/Scene.h index 2926c54..c088766 100644 --- a/Prism/src/Prism/Scene/Scene.h +++ b/Prism/src/Prism/Scene/Scene.h @@ -105,7 +105,7 @@ namespace Prism void SetPhysics2DGravity(float gravity); // Editor-specific - void SetSelectedEntity(entt::entity entity) { m_SelectedEntity = entity; } + void SetSelectedEntity(const entt::entity entity) { m_SelectedEntity = entity; } private: UUID m_SceneID; @@ -143,6 +143,7 @@ namespace Prism private: friend class Entity; + friend class Renderer3D; friend class SceneRenderer; friend class SceneSerializer; friend class SceneHierarchyPanel;