diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index e98b730..c03c560 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -3,9 +3,7 @@ // #include "EditorLayer.h" - #include "ImGuizmo.h" -#include "Prism/Core/KeyCodes.h" namespace Prism { @@ -19,7 +17,6 @@ namespace Prism ImGui::Text(name.c_str()); ImGui::NextColumn(); ImGui::PushItemWidth(-1); - std::string id = "##" + name; ImGui::Checkbox(id.c_str(), &value); @@ -88,6 +85,25 @@ namespace Prism } + void Property(const std::string& name, glm::vec2& value, float min, float max, PropertyFlag flags) + { + ImGui::Text(name.c_str()); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + std::string id = "##" + name; + ImGui::SliderFloat2(id.c_str(), glm::value_ptr(value), min, max); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + + void Property(const std::string& name, glm::vec2& value, PropertyFlag flags) + { + Property(name, value, -1.0f, 1.0f, flags); + } + + static void ImGuiShowHelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); @@ -102,8 +118,7 @@ namespace Prism } EditorLayer::EditorLayer() - : m_ClearColor{0.1f, 0.1f, 0.1f, 1.0f}, m_Scene(Scene::Model), - m_Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)) + : m_SceneType(SceneType::Model) { } @@ -113,114 +128,83 @@ namespace Prism void EditorLayer::OnAttach() { - // RenderPass - FramebufferSpecification geoFramebufferSpec; - geoFramebufferSpec.Width = 1280; - geoFramebufferSpec.Height = 720; - geoFramebufferSpec.Format = FramebufferFormat::RGBA16F; - geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + auto environment = Environment::Load("assets/env/birchwood_4k.hdr"); - RenderPassSpecification geoRenderPassSpec; - geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec); - m_GeoPass = RenderPass::Create(geoRenderPassSpec); + // Model Scene + { + m_Scene = CreateRef("Model Scene"); + m_Scene->SetCamera(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))); + m_Scene->SetEnvironment(environment); - FramebufferSpecification compFramebufferSpec; - compFramebufferSpec.Width = 1280; - compFramebufferSpec.Height = 720; - compFramebufferSpec.Format = FramebufferFormat::RGBA8; - compFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + m_MeshEntity = m_Scene->CreateEntity(); - RenderPassSpecification compRenderPassSpec; - compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec); - m_CompositePass = RenderPass::Create(compRenderPassSpec); + const auto mesh = CreateRef("assets/models/m1911/m1911.fbx"); + //auto mesh = CreateRef("assets/meshes/cerberus/CerberusMaterials.fbx"); + // auto mesh = CreateRef("assets/models/m1911/M1911Materials.fbx"); + m_MeshEntity->SetMesh(mesh); - /* - m_Framebuffer.reset(FrameBuffer::Create(1280, 720, FramebufferFormat::RGBA16F)); - m_FinalPresentBuffer.reset(FrameBuffer::Create(1280, 720, FramebufferFormat::RGBA8)); - */ + m_MeshMaterial = mesh->GetMaterial(); + } - m_QuadShader = Shader::Create("assets/shaders/quad.glsl"); - m_HDRShader = Shader::Create("assets/shaders/hdr.glsl"); + // Sphere Scene + { + m_SphereScene = CreateRef("PBR Sphere Scene"); + m_SphereScene->SetCamera(Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))); - m_Mesh = CreateRef("assets/models/m1911/m1911.fbx"); - m_MeshMaterial = CreateRef(m_Mesh->GetMaterial()); + m_SphereScene->SetEnvironment(environment); - m_SphereMesh = CreateRef("assets/models/Sphere1m.fbx"); - m_PlaneMesh = CreateRef("assets/models/Plane1m.fbx"); + auto sphereMesh = CreateRef("assets/models/Sphere1m.fbx"); + m_SphereBaseMaterial = sphereMesh->GetMaterial(); - m_GridShader = Shader::Create("assets/shaders/Grid.glsl"); - m_GridMaterial = MaterialInstance::Create(Material::Create(m_GridShader)); - m_GridMaterial->Set("u_Scale", m_GridScale); - m_GridMaterial->Set("u_Res", m_GridSize); + float x = -4.0f; + float roughness = 0.0f; + for (int i = 0; i < 8; i++) + { + auto sphereEntity = m_SphereScene->CreateEntity(); + + Ref mi = CreateRef(m_SphereBaseMaterial); + mi->Set("u_Metalness", 1.0f); + mi->Set("u_Roughness", roughness); + x += 1.1f; + roughness += 0.15f; + m_MetalSphereMaterialInstances.push_back(mi); + + sphereEntity->SetMesh(sphereMesh); + sphereEntity->SetMaterial(mi); + sphereEntity->Transform() = glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f)); + } + + x = -4.0f; + roughness = 0.0f; + for (int i = 0; i < 8; i++) + { + auto sphereEntity = m_SphereScene->CreateEntity(); + + Ref mi(new MaterialInstance(m_SphereBaseMaterial)); + mi->Set("u_Metalness", 0.0f); + mi->Set("u_Roughness", roughness); + x += 1.1f; + roughness += 0.15f; + m_DielectricSphereMaterialInstances.push_back(mi); + + sphereEntity->SetMesh(sphereMesh); + sphereEntity->SetMaterial(mi); + sphereEntity->Transform() = glm::translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f)); + } + } + + m_ActiveScene = m_Scene; + m_SceneHierarchyPanel = CreateScope(m_ActiveScene); + + m_PlaneMesh.reset(new Mesh("assets/models/Plane1m.obj")); // Editor - m_CheckerboardTex.reset(Texture2D::Create("assets/editor/Checkerboard.tga")); - - // Environment - m_EnvironmentCubeMap.reset( - TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Radiance.tga")); - m_EnvironmentIrradiance.reset( - TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Irradiance.tga")); - m_BRDFLUT.reset(Texture2D::Create("assets/textures/BRDF_LUT.tga")); - - - float x = -4.0f; - float roughness = 0.0f; - for (int i = 0; i < 8; i++) - { - Ref mi = CreateRef( - m_SphereMesh->GetMaterial()); - mi->Set("u_Metalness", 1.0f); - mi->Set("u_Roughness", roughness); - mi->Set("u_ModelMatrix", glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f))); - x += 1.1f; - roughness += 0.125f; - m_MetalSphereMaterialInstances.push_back(mi); - } - - x = -4.0f; - roughness = 0.0f; - for (int i = 0; i < 8; i++) - { - Ref mi = CreateRef( - m_SphereMesh->GetMaterial()); - mi->Set("u_Metalness", 0.0f); - mi->Set("u_Roughness", roughness); - mi->Set("u_ModelMatrix", translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f))); - x += 1.1f; - roughness += 0.125f; - m_DielectricSphereMaterialInstances.push_back(mi); - } - - // Create Quad - static float QuadVertex[] = { - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f - }; - static uint32_t QuadIndices[] = { - 0, 1, 2, 2, 3, 0 - }; - - m_FullscreenQuadVertexArray = VertexArray::Create(); - auto quadVB = VertexBuffer::Create(QuadVertex, sizeof(float) * sizeof(QuadVertex)); - quadVB->SetLayout({ - { ShaderDataType::Float3, "a_Position" }, - { ShaderDataType::Float2, "a_TexCoord" } - }); - - auto quadIB = IndexBuffer::Create(QuadIndices, sizeof(QuadIndices) * sizeof(uint32_t)); - m_FullscreenQuadVertexArray->AddVertexBuffer(quadVB); - m_FullscreenQuadVertexArray->SetIndexBuffer(quadIB); - + m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga"); + // lights m_Light.Direction = {-0.5f, -0.5f, 1.0f}; m_Light.Radiance = {1.0f, 1.0f, 1.0f}; - - - m_Transform = glm::scale(glm::mat4(1.0f), glm::vec3(m_MeshScale)); } void EditorLayer::OnDetach() @@ -232,62 +216,28 @@ namespace Prism { // THINGS TO LOOK AT: // - BRDF LUT - // - Cubemap mips and filtering // - Tonemapping and proper HDR pipeline using namespace Prism; using namespace glm; - m_Camera.Update(deltaTime); - auto viewProjection = m_Camera.GetProjectionMatrix() * m_Camera.GetViewMatrix(); - - // m_Framebuffer->Bind(); - Renderer::BeginRenderPass(m_GeoPass); - Renderer::Clear(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]); - - m_QuadShader->Bind(); - m_QuadShader->SetMat4("u_InverseVP", inverse(viewProjection)); - m_EnvironmentCubeMap->Bind(0); - /* - m_VertexBuffer->Bind(); - m_IndexBuffer->Bind(); - */ - m_FullscreenQuadVertexArray->Bind(); - Renderer::DrawIndexed(m_FullscreenQuadVertexArray->GetIndexBuffer()->GetCount(), false); - - m_MeshMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color); m_MeshMaterial->Set("u_Metalness", m_MetalnessInput.Value); m_MeshMaterial->Set("u_Roughness", m_RoughnessInput.Value); - m_MeshMaterial->Set("u_ViewProjectionMatrix", viewProjection); - m_MeshMaterial->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale))); m_MeshMaterial->Set("lights", m_Light); - m_MeshMaterial->Set("u_CameraPosition", m_Camera.GetPosition()); - m_MeshMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f); m_MeshMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f); m_MeshMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f); m_MeshMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f); m_MeshMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f); m_MeshMaterial->Set("u_EnvMapRotation", m_EnvMapRotation); - m_MeshMaterial->Set("u_EnvRadianceTex", m_EnvironmentCubeMap); - m_MeshMaterial->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance); - m_MeshMaterial->Set("u_BRDFLUTTexture", m_BRDFLUT); - m_SphereMesh->GetMaterial()->Set("u_AlbedoColor", m_AlbedoInput.Color); - m_SphereMesh->GetMaterial()->Set("u_Metalness", m_MetalnessInput.Value); - m_SphereMesh->GetMaterial()->Set("u_Roughness", m_RoughnessInput.Value); - m_SphereMesh->GetMaterial()->Set("u_ViewProjectionMatrix", viewProjection); - m_SphereMesh->GetMaterial()->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale))); - m_SphereMesh->GetMaterial()->Set("lights", m_Light); - m_SphereMesh->GetMaterial()->Set("u_CameraPosition", m_Camera.GetPosition()); - m_SphereMesh->GetMaterial()->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f); - m_SphereMesh->GetMaterial()->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f); - m_SphereMesh->GetMaterial()->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f); - m_SphereMesh->GetMaterial()->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f); - m_SphereMesh->GetMaterial()->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f); - m_SphereMesh->GetMaterial()->Set("u_EnvMapRotation", m_EnvMapRotation); - m_SphereMesh->GetMaterial()->Set("u_EnvRadianceTex", m_EnvironmentCubeMap); - m_SphereMesh->GetMaterial()->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance); - m_SphereMesh->GetMaterial()->Set("u_BRDFLUTTexture", m_BRDFLUT); + m_SphereBaseMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color); + m_SphereBaseMaterial->Set("lights", m_Light); + m_SphereBaseMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f); + m_SphereBaseMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f); + m_SphereBaseMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f); + m_SphereBaseMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f); + m_SphereBaseMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f); + m_SphereBaseMaterial->Set("u_EnvMapRotation", m_EnvMapRotation); if (m_AlbedoInput.TextureMap) @@ -299,56 +249,7 @@ namespace Prism if (m_RoughnessInput.TextureMap) m_MeshMaterial->Set("u_RoughnessTexture", m_RoughnessInput.TextureMap); - if (m_Scene == Scene::Spheres) - { - // Metals - for (int i = 0; i < 8; i++) - { - m_SphereMesh->Render(deltaTime, glm::mat4(1.0f), m_MetalSphereMaterialInstances[i]); - /* - m_MetalSphereMaterialInstances[i]->Bind(); - m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get()); - */ - } - - // Dielectrics - for (int i = 0; i < 8; i++) - { - m_SphereMesh->Render(deltaTime, glm::mat4(1.0f), m_DielectricSphereMaterialInstances[i]); - /* - m_DielectricSphereMaterialInstances[i]->Bind(); - m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get()); - */ - } - } - else if (m_Scene == Scene::Model) - { - if (m_Mesh) - { - m_Mesh->Render(deltaTime, m_Transform, m_MeshMaterial); - } - } - - m_GridMaterial->Set("u_MVP", viewProjection * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f))); - m_PlaneMesh->Render(deltaTime, m_GridMaterial); - - // m_Framebuffer->Unbind(); - Renderer::EndRenderPass(); - - Renderer::BeginRenderPass(m_CompositePass); - // m_FinalPresentBuffer->Bind(); - m_HDRShader->Bind(); - m_HDRShader->SetFloat("u_Exposure", m_Exposure); - // m_Framebuffer->BindTexture(); - m_GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); - /* - m_VertexBuffer->Bind(); - m_IndexBuffer->Bind(); - */ - m_FullscreenQuadVertexArray->Bind(); - Renderer::DrawIndexed(m_FullscreenQuadVertexArray->GetIndexBuffer()->GetCount(), false); - // m_FinalPresentBuffer->Unbind(); - Renderer::EndRenderPass(); + m_ActiveScene->OnUpdate(deltaTime); } } @@ -493,25 +394,39 @@ namespace Prism ImGui::EndMenuBar(); } + m_SceneHierarchyPanel->OnImGuiRender(); ImGui::End(); #endif // Editor Panel ------------------------------------------------------------------------------ ImGui::Begin("Model"); - ImGui::RadioButton("Spheres", (int*)&m_Scene, (int)Scene::Spheres); + + if (ImGui::RadioButton("Spheres", (int*)&m_SceneType, (int)SceneType::Spheres)) + m_ActiveScene = m_SphereScene; ImGui::SameLine(); - ImGui::RadioButton("Model", (int*)&m_Scene, (int)Scene::Model); + if (ImGui::RadioButton("Model", (int*)&m_SceneType, (int)SceneType::Model)) + m_ActiveScene = m_Scene; ImGui::Begin("Environment"); + if (ImGui::Button("Load Environment Map")) + { + std::string filename = Application::Get().OpenFile("*.hdr"); + if (!filename.empty()) + m_ActiveScene->SetEnvironment(Environment::Load(filename)); + } + + ImGui::SliderFloat("Skybox LOD", &m_Scene->GetSkyboxLod(), 0.0f, 11.0f); + + ImGui::Columns(2); ImGui::AlignTextToFramePadding(); Property("Light Direction", m_Light.Direction); Property("Light Radiance", m_Light.Radiance, PropertyFlag::ColorProperty); Property("Light Multiplier", m_LightMultiplier, 0.0f, 5.0f); - Property("Exposure", m_Exposure, 0.0f, 5.0f); + Property("Exposure", m_ActiveScene->GetCamera().GetExposure(), 0.0f, 5.0f); Property("Radiance Prefiltering", m_RadiancePrefilter); Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f); @@ -523,16 +438,22 @@ namespace Prism ImGui::Separator(); { ImGui::Text("Mesh"); - std::string fullpath = m_Mesh ? m_Mesh->GetFilePath() : "None"; - size_t found = fullpath.find_last_of("/\\"); - std::string path = found != std::string::npos ? fullpath.substr(found + 1) : fullpath; + auto mesh = m_MeshEntity->GetMesh(); + std::string fullpath = mesh ? mesh->GetFilePath() : "None"; + size_t found = fullpath.find_last_of("/\\"); + std::string path = found != std::string::npos ? fullpath.substr(found + 1) : fullpath; ImGui::Text(path.c_str()); ImGui::SameLine(); if (ImGui::Button("...##Mesh")) { std::string filename = Application::Get().OpenFile(""); - if (filename != "") - m_Mesh.reset(new Mesh(filename)); + if (!filename.empty()) + { + auto newMesh = CreateRef(filename); + // m_MeshMaterial.reset(new MaterialInstance(newMesh->GetMaterial())); + // m_MeshEntity->SetMaterial(m_MeshMaterial); + m_MeshEntity->SetMesh(newMesh); + } } } ImGui::Separator(); @@ -561,8 +482,8 @@ namespace Prism if (ImGui::IsItemClicked()) { std::string filename = Application::Get().OpenFile(""); - if (filename != "") - m_AlbedoInput.TextureMap.reset(Texture2D::Create(filename, m_AlbedoInput.SRGB)); + if (!filename.empty()) + m_AlbedoInput.TextureMap = Texture2D::Create(filename, m_AlbedoInput.SRGB); } } ImGui::SameLine(); @@ -571,8 +492,7 @@ namespace Prism if (ImGui::Checkbox("sRGB##AlbedoMap", &m_AlbedoInput.SRGB)) { if (m_AlbedoInput.TextureMap) - m_AlbedoInput.TextureMap.reset( - Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB)); + m_AlbedoInput.TextureMap = Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB); } ImGui::EndGroup(); ImGui::SameLine(); @@ -602,8 +522,8 @@ namespace Prism if (ImGui::IsItemClicked()) { std::string filename = Application::Get().OpenFile(""); - if (filename != "") - m_NormalInput.TextureMap.reset(Texture2D::Create(filename)); + if (!filename.empty()) + m_NormalInput.TextureMap = Texture2D::Create(filename); } } ImGui::SameLine(); @@ -633,8 +553,8 @@ namespace Prism if (ImGui::IsItemClicked()) { std::string filename = Application::Get().OpenFile(""); - if (filename != "") - m_MetalnessInput.TextureMap.reset(Texture2D::Create(filename)); + if (!filename.empty()) + m_MetalnessInput.TextureMap = Texture2D::Create(filename); } } ImGui::SameLine(); @@ -666,8 +586,8 @@ namespace Prism if (ImGui::IsItemClicked()) { std::string filename = Application::Get().OpenFile(""); - if (filename != "") - m_RoughnessInput.TextureMap.reset(Texture2D::Create(filename)); + if (!filename.empty()) + m_RoughnessInput.TextureMap = Texture2D::Create(filename); } } ImGui::SameLine(); @@ -701,42 +621,25 @@ namespace Prism ImGui::Begin("Viewport"); auto viewportSize = ImGui::GetContentRegionAvail(); - m_GeoPass->GetSpecification().TargetFramebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_CompositePass->GetSpecification().TargetFramebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - - // m_Framebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - // m_FinalPresentBuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_Camera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); - // ImGui::Image((ImTextureRef)m_FinalPresentBuffer->GetColorAttachmentRendererID(), viewportSize, {0, 1}, {1, 0}); - ImGui::Image((ImTextureRef)m_CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); + SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + m_ActiveScene->GetCamera().SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); + m_ActiveScene->GetCamera().SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); // ImGuizmo if (m_GizmoType != -1) { - const float rw = (float)ImGui::GetWindowWidth(); - const float rh = (float)ImGui::GetWindowHeight(); + const auto rw = (float)ImGui::GetWindowWidth(); + const auto rh = (float)ImGui::GetWindowHeight(); ImGuizmo::SetOrthographic(false); ImGuizmo::SetDrawlist(); ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y, rw, rh); - ImGuizmo::Manipulate(glm::value_ptr(m_Camera.GetViewMatrix()), glm::value_ptr(m_Camera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, ImGuizmo::LOCAL, glm::value_ptr(m_Transform)); + const auto& camera = m_ActiveScene->GetCamera(); + ImGuizmo::Manipulate(glm::value_ptr(camera.GetViewMatrix()), glm::value_ptr(camera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, ImGuizmo::LOCAL, glm::value_ptr(m_MeshEntity->Transform())); } ImGui::End(); ImGui::PopStyleVar(); - - ImGui::Begin("Log"); - const auto& cameraPos = m_Camera.GetPosition(); - ImGui::Text("cameraPos: (%.2f, %.2f, %.2f)", cameraPos.x, cameraPos.y, cameraPos.z); - - const auto& Direct = m_Camera.GetForwardDirection(); - ImGui::Text("forward Vec: (%.2f, %.2f, %.2f)", Direct.x, Direct.y, Direct.z); - - const auto& distance = m_Camera.GetDistance(); - ImGui::Text("distance: %.3f", distance); - ImGui::End(); - - if (m_Mesh) - m_Mesh->OnImGuiRender(); } diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index d906c10..14db0e4 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -6,120 +6,110 @@ #define EDITORLAYER_H #include "Prism.h" +#include "Prism/Editor/SceneHierachyPanel.h" namespace Prism { -class EditorLayer : public Layer -{ -public: - EditorLayer(); - virtual ~EditorLayer(); - - virtual void OnAttach() override; - virtual void OnDetach() override; - virtual void OnUpdate(TimeStep deltaTime) override; - virtual void OnImGuiRender() override; - virtual void OnEvent(Event& e) override; - -private: - bool OnKeyPressedEvent(KeyPressedEvent& e); - -private: - float m_ClearColor[4]; - - // Ref m_Framebuffer, m_FinalPresentBuffer; - Ref m_GeoPass, m_CompositePass; - - /* - Ref m_VertexBuffer; - Ref m_IndexBuffer; - */ - Ref m_FullscreenQuadVertexArray; - Ref m_EnvironmentCubeMap, m_EnvironmentIrradiance; - - Camera m_Camera; - - Ref m_QuadShader; - Ref m_HDRShader; - Ref m_GridShader; - Ref m_Mesh; - Ref m_SphereMesh, m_PlaneMesh; - Ref m_BRDFLUT; - - Ref m_MeshMaterial; - Ref m_GridMaterial; - - std::vector> m_MetalSphereMaterialInstances; - std::vector> m_DielectricSphereMaterialInstances; - - float m_GridScale = 16.025f, m_GridSize = 0.025f; - float m_MeshScale = 1.0f; - - Ref m_Shader; - Ref m_PBRShader; - - // Imguizmo - int m_GizmoType = -1; // -1 = no gizmo - glm::mat4 m_Transform; - - - struct AlbedoInput + class EditorLayer : public Layer { - glm::vec3 Color = { 0.972f, 0.96f, 0.915f }; // Silver, from https://docs.unrealengine.com/en-us/Engine/Rendering/Materials/PhysicallyBased - Ref TextureMap; - bool SRGB = true; - bool UseTexture = false; + public: + EditorLayer(); + virtual ~EditorLayer(); + + virtual void OnAttach() override; + virtual void OnDetach() override; + virtual void OnUpdate(TimeStep deltaTime) override; + virtual void OnImGuiRender() override; + virtual void OnEvent(Event& e) override; + + private: + bool OnKeyPressedEvent(KeyPressedEvent& e); + + private: + Scope m_SceneHierarchyPanel; + + Ref m_Scene; + Ref m_SphereScene; + Ref m_ActiveScene; + + Entity* m_MeshEntity = nullptr; + + Ref m_BrushShader; + Ref m_PlaneMesh; + Ref m_SphereBaseMaterial; + + Ref m_MeshMaterial; + + Ref m_GeoPass, m_CompositePass; + + Ref m_GridMaterial; + + std::vector> m_MetalSphereMaterialInstances; + std::vector> m_DielectricSphereMaterialInstances; + + float m_GridScale = 16.025f, m_GridSize = 0.025f; + float m_MeshScale = 1.0f; + + + // Imguizmo + int m_GizmoType = -1; // -1 = no gizmo + + + struct AlbedoInput + { + glm::vec3 Color = { 0.972f, 0.96f, 0.915f }; // Silver, from https://docs.unrealengine.com/en-us/Engine/Rendering/Materials/PhysicallyBased + Ref TextureMap; + bool SRGB = true; + bool UseTexture = false; + }; + AlbedoInput m_AlbedoInput; + + struct NormalInput + { + Ref TextureMap; + bool UseTexture = false; + }; + NormalInput m_NormalInput; + + struct MetalnessInput + { + float Value = 1.0f; + Ref TextureMap; + bool UseTexture = false; + }; + MetalnessInput m_MetalnessInput; + + struct RoughnessInput + { + float Value = 0.5f; + Ref TextureMap; + bool UseTexture = false; + }; + RoughnessInput m_RoughnessInput; + + + struct Light + { + glm::vec3 Direction; + glm::vec3 Radiance; + }; + Light m_Light; + float m_LightMultiplier = 0.3f; + + // PBR params + bool m_RadiancePrefilter = false; + + float m_EnvMapRotation = 0.0f; + + enum class SceneType : uint32_t + { + Spheres = 0, Model = 1 + }; + SceneType m_SceneType; + + // Editor resources + Ref m_CheckerboardTex; }; - AlbedoInput m_AlbedoInput; - - struct NormalInput - { - Ref TextureMap; - bool UseTexture = false; - }; - NormalInput m_NormalInput; - - struct MetalnessInput - { - float Value = 1.0f; - Ref TextureMap; - bool UseTexture = false; - }; - MetalnessInput m_MetalnessInput; - - struct RoughnessInput - { - float Value = 0.5f; - Ref TextureMap; - bool UseTexture = false; - }; - RoughnessInput m_RoughnessInput; - - - struct Light - { - glm::vec3 Direction; - glm::vec3 Radiance; - }; - Light m_Light; - float m_LightMultiplier = 0.3f; - - // PBR params - float m_Exposure = 1.0f; - - bool m_RadiancePrefilter = false; - - float m_EnvMapRotation = 0.0f; - - enum class Scene : uint32_t - { - Spheres = 0, Model = 1 - }; - Scene m_Scene; - - // Editor resources - Ref m_CheckerboardTex; -}; } diff --git a/Editor/assets/env/birchwood_4k.hdr b/Editor/assets/env/birchwood_4k.hdr new file mode 100644 index 0000000..595ac80 Binary files /dev/null and b/Editor/assets/env/birchwood_4k.hdr differ diff --git a/Editor/assets/meshes/Sphere1m.fbx b/Editor/assets/meshes/Sphere1m.fbx new file mode 100644 index 0000000..cfe36ea Binary files /dev/null and b/Editor/assets/meshes/Sphere1m.fbx differ diff --git a/Editor/assets/meshes/cerberus/Cerberus.blend b/Editor/assets/meshes/cerberus/Cerberus.blend new file mode 100644 index 0000000..321a2f0 Binary files /dev/null and b/Editor/assets/meshes/cerberus/Cerberus.blend differ diff --git a/Editor/assets/meshes/cerberus/Cerberus.blend1 b/Editor/assets/meshes/cerberus/Cerberus.blend1 new file mode 100644 index 0000000..9f762bf Binary files /dev/null and b/Editor/assets/meshes/cerberus/Cerberus.blend1 differ diff --git a/Editor/assets/meshes/cerberus/CerberusMaterials.fbx b/Editor/assets/meshes/cerberus/CerberusMaterials.fbx new file mode 100644 index 0000000..bbd38dd Binary files /dev/null and b/Editor/assets/meshes/cerberus/CerberusMaterials.fbx differ diff --git a/Editor/assets/meshes/cerberus/Textures/Cerberus_A.png b/Editor/assets/meshes/cerberus/Textures/Cerberus_A.png new file mode 100644 index 0000000..0d104b5 Binary files /dev/null and b/Editor/assets/meshes/cerberus/Textures/Cerberus_A.png differ diff --git a/Editor/assets/meshes/cerberus/Textures/Cerberus_M.png b/Editor/assets/meshes/cerberus/Textures/Cerberus_M.png new file mode 100644 index 0000000..640a4a2 Binary files /dev/null and b/Editor/assets/meshes/cerberus/Textures/Cerberus_M.png differ diff --git a/Editor/assets/meshes/cerberus/Textures/Cerberus_N.png b/Editor/assets/meshes/cerberus/Textures/Cerberus_N.png new file mode 100644 index 0000000..ebfec05 Binary files /dev/null and b/Editor/assets/meshes/cerberus/Textures/Cerberus_N.png differ diff --git a/Editor/assets/meshes/cerberus/Textures/Cerberus_R.png b/Editor/assets/meshes/cerberus/Textures/Cerberus_R.png new file mode 100644 index 0000000..d517509 Binary files /dev/null and b/Editor/assets/meshes/cerberus/Textures/Cerberus_R.png differ diff --git a/Editor/assets/meshes/cerberus/cerberus.fbx b/Editor/assets/meshes/cerberus/cerberus.fbx new file mode 100644 index 0000000..619d61e Binary files /dev/null and b/Editor/assets/meshes/cerberus/cerberus.fbx differ diff --git a/Editor/assets/models/m1911/M1911.blend b/Editor/assets/models/m1911/M1911.blend new file mode 100644 index 0000000..c800949 Binary files /dev/null and b/Editor/assets/models/m1911/M1911.blend differ diff --git a/Editor/assets/models/m1911/M1911.blend1 b/Editor/assets/models/m1911/M1911.blend1 new file mode 100644 index 0000000..e67a15e Binary files /dev/null and b/Editor/assets/models/m1911/M1911.blend1 differ diff --git a/Editor/assets/models/m1911/M1911Materials.fbx b/Editor/assets/models/m1911/M1911Materials.fbx new file mode 100644 index 0000000..2c23887 Binary files /dev/null and b/Editor/assets/models/m1911/M1911Materials.fbx differ diff --git a/Editor/assets/models/m1911/imgui.ini b/Editor/assets/models/m1911/imgui.ini new file mode 100644 index 0000000..2a11d43 --- /dev/null +++ b/Editor/assets/models/m1911/imgui.ini @@ -0,0 +1,71 @@ +[Window][DockSpace Demo] +Pos=0,0 +Size=2560,1387 +Collapsed=0 + +[Window][Debug##Default] +Pos=55,90 +Size=400,400 +Collapsed=0 + +[Window][Renderer] +Pos=2047,1141 +Size=285,134 +Collapsed=0 + +[Window][Model] +Pos=1772,24 +Size=788,577 +Collapsed=0 +DockId=0x00000001,0 + +[Window][Environment] +Pos=1772,603 +Size=788,784 +Collapsed=0 +DockId=0x00000002,0 + +[Window][Viewport] +Pos=421,24 +Size=1349,1363 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Mesh Debug] +Pos=2055,471 +Size=505,916 +Collapsed=0 +DockId=0x00000002,1 + +[Window][ImGui Demo] +ViewportPos=276,66 +ViewportId=0x080FC883 +Size=1923,1168 +Collapsed=0 + +[Window][Mesh Hierarchy] +Pos=0,24 +Size=419,1363 +Collapsed=0 +DockId=0x00000005,1 + +[Window][Scene Hierarchy] +Pos=0,24 +Size=419,1363 +Collapsed=0 +DockId=0x00000005,0 + +[Window][Properties] +Pos=107,1086 +Size=188,127 +Collapsed=0 + +[Docking][Data] +DockSpace ID=0xFA06BC56 Window=0x4647B76E Pos=0,47 Size=2560,1363 Split=X + DockNode ID=0x00000005 Parent=0xFA06BC56 SizeRef=419,1363 Selected=0x9A68760C + DockNode ID=0x00000006 Parent=0xFA06BC56 SizeRef=2139,1363 Split=X + DockNode ID=0x00000003 Parent=0x00000006 SizeRef=1349,876 CentralNode=1 HiddenTabBar=1 Selected=0x995B0CF8 + DockNode ID=0x00000004 Parent=0x00000006 SizeRef=788,876 Split=Y Selected=0x16545DDD + DockNode ID=0x00000001 Parent=0x00000004 SizeRef=705,577 Selected=0x16545DDD + DockNode ID=0x00000002 Parent=0x00000004 SizeRef=705,784 Selected=0xC0BA51F5 + diff --git a/Editor/assets/shaders/EnvironmentIrradiance.glsl b/Editor/assets/shaders/EnvironmentIrradiance.glsl new file mode 100644 index 0000000..6b5c085 --- /dev/null +++ b/Editor/assets/shaders/EnvironmentIrradiance.glsl @@ -0,0 +1,103 @@ +#type compute +#version 450 core +// Physically Based Rendering +// Copyright (c) 2017-2018 Michał Siejak + +// Computes diffuse irradiance cubemap convolution for image-based lighting. +// Uses quasi Monte Carlo sampling with Hammersley sequence. + +const float PI = 3.141592; +const float TwoPI = 2 * PI; +const float Epsilon = 0.00001; + +const uint NumSamples = 64 * 1024; +const float InvNumSamples = 1.0 / float(NumSamples); + +layout(binding=0) uniform samplerCube inputTexture; +layout(binding=0, rgba16f) restrict writeonly uniform imageCube outputTexture; + +// Compute Van der Corput radical inverse +// See: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html +float radicalInverse_VdC(uint bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +// Sample i-th point from Hammersley point set of NumSamples points total. +vec2 sampleHammersley(uint i) +{ + return vec2(i * InvNumSamples, radicalInverse_VdC(i)); +} + +// Uniformly sample point on a hemisphere. +// Cosine-weighted sampling would be a better fit for Lambertian BRDF but since this +// compute shader runs only once as a pre-processing step performance is not *that* important. +// See: "Physically Based Rendering" 2nd ed., section 13.6.1. +vec3 sampleHemisphere(float u1, float u2) +{ + const float u1p = sqrt(max(0.0, 1.0 - u1*u1)); + return vec3(cos(TwoPI*u2) * u1p, sin(TwoPI*u2) * u1p, u1); +} + +vec3 GetCubeMapTexCoord() +{ + vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(outputTexture)); + vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0); + + vec3 ret; + if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x); + else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x); + else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y); + else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y); + else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0); + else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0); + return normalize(ret); +} + +// Compute orthonormal basis for converting from tanget/shading space to world space. +void computeBasisVectors(const vec3 N, out vec3 S, out vec3 T) +{ + // Branchless select non-degenerate T. + T = cross(N, vec3(0.0, 1.0, 0.0)); + T = mix(cross(N, vec3(1.0, 0.0, 0.0)), T, step(Epsilon, dot(T, T))); + + T = normalize(T); + S = normalize(cross(N, T)); +} + +// Convert point from tangent/shading space to world space. +vec3 tangentToWorld(const vec3 v, const vec3 N, const vec3 S, const vec3 T) +{ + return S * v.x + T * v.y + N * v.z; +} + +layout(local_size_x=32, local_size_y=32, local_size_z=1) in; +void main(void) +{ + vec3 N = GetCubeMapTexCoord(); + + vec3 S, T; + computeBasisVectors(N, S, T); + + // Monte Carlo integration of hemispherical irradiance. + // As a small optimization this also includes Lambertian BRDF assuming perfectly white surface (albedo of 1.0) + // so we don't need to normalize in PBR fragment shader (so technically it encodes exitant radiance rather than irradiance). + vec3 irradiance = vec3(0); + for(uint i = 0; i < NumSamples; i++) + { + vec2 u = sampleHammersley(i); + vec3 Li = tangentToWorld(sampleHemisphere(u.x, u.y), N, S, T); + float cosTheta = max(0.0, dot(Li, N)); + + // PIs here cancel out because of division by pdf. + irradiance += 2.0 * textureLod(inputTexture, Li, 0).rgb * cosTheta; + } + irradiance /= vec3(NumSamples); + + imageStore(outputTexture, ivec3(gl_GlobalInvocationID), vec4(irradiance, 1.0)); +} diff --git a/Editor/assets/shaders/EnvironmentMipFilter.glsl b/Editor/assets/shaders/EnvironmentMipFilter.glsl new file mode 100644 index 0000000..26d4d5b --- /dev/null +++ b/Editor/assets/shaders/EnvironmentMipFilter.glsl @@ -0,0 +1,159 @@ +#type compute +#version 450 core +// Physically Based Rendering +// Copyright (c) 2017-2018 Michał Siejak + +// Pre-filters environment cube map using GGX NDF importance sampling. +// Part of specular IBL split-sum approximation. + +const float PI = 3.141592; +const float TwoPI = 2 * PI; +const float Epsilon = 0.00001; + +const uint NumSamples = 1024; +const float InvNumSamples = 1.0 / float(NumSamples); + +const int NumMipLevels = 1; +layout(binding = 0) uniform samplerCube inputTexture; +layout(binding = 0, rgba16f) restrict writeonly uniform imageCube outputTexture[NumMipLevels]; + +// Roughness value to pre-filter for. +layout(location=0) uniform float roughness; + +#define PARAM_LEVEL 0 +#define PARAM_ROUGHNESS roughness + +// Compute Van der Corput radical inverse +// See: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html +float radicalInverse_VdC(uint bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +// Sample i-th point from Hammersley point set of NumSamples points total. +vec2 sampleHammersley(uint i) +{ + return vec2(i * InvNumSamples, radicalInverse_VdC(i)); +} + +// Importance sample GGX normal distribution function for a fixed roughness value. +// This returns normalized half-vector between Li & Lo. +// For derivation see: http://blog.tobias-franke.eu/2014/03/30/notes_on_importance_sampling.html +vec3 sampleGGX(float u1, float u2, float roughness) +{ + float alpha = roughness * roughness; + + float cosTheta = sqrt((1.0 - u2) / (1.0 + (alpha*alpha - 1.0) * u2)); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); // Trig. identity + float phi = TwoPI * u1; + + // Convert to Cartesian upon return. + return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); +} + +// GGX/Towbridge-Reitz normal distribution function. +// Uses Disney's reparametrization of alpha = roughness^2. +float ndfGGX(float cosLh, float roughness) +{ + float alpha = roughness * roughness; + float alphaSq = alpha * alpha; + + float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0; + return alphaSq / (PI * denom * denom); +} + +vec3 GetCubeMapTexCoord() +{ + vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(outputTexture[PARAM_LEVEL])); + vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0); + + vec3 ret; + if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x); + else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x); + else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y); + else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y); + else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0); + else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0); + return normalize(ret); +} + +// Compute orthonormal basis for converting from tanget/shading space to world space. +void computeBasisVectors(const vec3 N, out vec3 S, out vec3 T) +{ + // Branchless select non-degenerate T. + T = cross(N, vec3(0.0, 1.0, 0.0)); + T = mix(cross(N, vec3(1.0, 0.0, 0.0)), T, step(Epsilon, dot(T, T))); + + T = normalize(T); + S = normalize(cross(N, T)); +} + +// Convert point from tangent/shading space to world space. +vec3 tangentToWorld(const vec3 v, const vec3 N, const vec3 S, const vec3 T) +{ + return S * v.x + T * v.y + N * v.z; +} + +layout(local_size_x=32, local_size_y=32, local_size_z=1) in; +void main(void) +{ + // Make sure we won't write past output when computing higher mipmap levels. + ivec2 outputSize = imageSize(outputTexture[PARAM_LEVEL]); + if(gl_GlobalInvocationID.x >= outputSize.x || gl_GlobalInvocationID.y >= outputSize.y) { + return; + } + + // Solid angle associated with a single cubemap texel at zero mipmap level. + // This will come in handy for importance sampling below. + vec2 inputSize = vec2(textureSize(inputTexture, 0)); + float wt = 4.0 * PI / (6 * inputSize.x * inputSize.y); + + // Approximation: Assume zero viewing angle (isotropic reflections). + vec3 N = GetCubeMapTexCoord(); + vec3 Lo = N; + + vec3 S, T; + computeBasisVectors(N, S, T); + + vec3 color = vec3(0); + float weight = 0; + + // Convolve environment map using GGX NDF importance sampling. + // Weight by cosine term since Epic claims it generally improves quality. + for(uint i = 0; i < NumSamples; i++) { + vec2 u = sampleHammersley(i); + vec3 Lh = tangentToWorld(sampleGGX(u.x, u.y, PARAM_ROUGHNESS), N, S, T); + + // Compute incident direction (Li) by reflecting viewing direction (Lo) around half-vector (Lh). + vec3 Li = 2.0 * dot(Lo, Lh) * Lh - Lo; + + float cosLi = dot(N, Li); + if(cosLi > 0.0) { + // Use Mipmap Filtered Importance Sampling to improve convergence. + // See: https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html, section 20.4 + + float cosLh = max(dot(N, Lh), 0.0); + + // GGX normal distribution function (D term) probability density function. + // Scaling by 1/4 is due to change of density in terms of Lh to Li (and since N=V, rest of the scaling factor cancels out). + float pdf = ndfGGX(cosLh, PARAM_ROUGHNESS) * 0.25; + + // Solid angle associated with this sample. + float ws = 1.0 / (NumSamples * pdf); + + // Mip level to sample from. + float mipLevel = max(0.5 * log2(ws / wt) + 1.0, 0.0); + + color += textureLod(inputTexture, Li, mipLevel).rgb * cosLi; + weight += cosLi; + } + } + color /= weight; + + imageStore(outputTexture[PARAM_LEVEL], ivec3(gl_GlobalInvocationID), vec4(color, 1.0)); +} diff --git a/Editor/assets/shaders/EquirectangularToCubeMap.glsl b/Editor/assets/shaders/EquirectangularToCubeMap.glsl new file mode 100644 index 0000000..3ccef95 --- /dev/null +++ b/Editor/assets/shaders/EquirectangularToCubeMap.glsl @@ -0,0 +1,39 @@ +#type compute +#version 450 core + +// Converts equirectangular (lat-long) projection texture into a cubemap + +const float PI = 3.141592; + +layout(binding = 0) uniform sampler2D u_EquirectangularTex; +layout(binding = 0, rgba16f) restrict writeonly uniform imageCube o_CubeMap; + +vec3 GetCubeMapTexCoord() +{ + vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(o_CubeMap)); + vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0); + + vec3 ret; + if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x); + else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x); + else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y); + else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y); + else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0); + else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0); + return normalize(ret); +} + +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; +void main() +{ + vec3 cubeTC = GetCubeMapTexCoord(); + + // Calculate sampling coords for equirectangular texture + // https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates + float phi = atan(cubeTC.z, cubeTC.x); + float theta = acos(cubeTC.y); + vec2 uv = vec2(phi / (2.0 * PI) + 0.5, theta / PI); + + vec4 color = texture(u_EquirectangularTex, uv); + imageStore(o_CubeMap, ivec3(gl_GlobalInvocationID), color); +} diff --git a/Editor/assets/shaders/Grid.glsl b/Editor/assets/shaders/Grid.glsl index c6885bb..adf6b1d 100644 --- a/Editor/assets/shaders/Grid.glsl +++ b/Editor/assets/shaders/Grid.glsl @@ -1,18 +1,19 @@ -// Simple Texture Shader +// Grid Shader #type vertex #version 430 layout(location = 0) in vec3 a_Position; -layout(location = 4) in vec2 a_TexCoord; +layout(location = 1) in vec2 a_TexCoord; -uniform mat4 u_MVP; +uniform mat4 u_ViewProjection; +uniform mat4 u_Transform; out vec2 v_TexCoord; void main() { - vec4 position = u_MVP * vec4(a_Position, 1.0); + vec4 position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0); gl_Position = position; v_TexCoord = a_TexCoord; @@ -28,11 +29,6 @@ uniform float u_Res; in vec2 v_TexCoord; -/*void main() -{ - color = texture(u_Texture, v_TexCoord * 8.0); -}*/ - float grid(vec2 st, float res) { vec2 grid = fract(st); diff --git a/Editor/assets/shaders/simplepbr_Anim.glsl b/Editor/assets/shaders/PBRShader_Anim.glsl similarity index 89% rename from Editor/assets/shaders/simplepbr_Anim.glsl rename to Editor/assets/shaders/PBRShader_Anim.glsl index b4add84..2cbcf39 100644 --- a/Editor/assets/shaders/simplepbr_Anim.glsl +++ b/Editor/assets/shaders/PBRShader_Anim.glsl @@ -22,7 +22,7 @@ layout(location = 5) in ivec4 a_BoneIndices; layout(location = 6) in vec4 a_BoneWeights; uniform mat4 u_ViewProjectionMatrix; -uniform mat4 u_ModelMatrix; +uniform mat4 u_Transform; const int MAX_BONES = 100; uniform mat4 u_BoneTransforms[100]; @@ -38,21 +38,20 @@ out VertexOutput void main() { - mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0]; - boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1]; - boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2]; - boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3]; + mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0]; + boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1]; + boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2]; + boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3]; vec4 localPosition = boneTransform * vec4(a_Position, 1.0); - vs_Output.WorldPosition = vec3(u_ModelMatrix * boneTransform * vec4(a_Position, 1.0)); - vs_Output.Normal = mat3(boneTransform) * a_Normal; + vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0)); + vs_Output.Normal = mat3(boneTransform) * a_Normal; vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); - vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal); + vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal); vs_Output.Binormal = mat3(boneTransform) * a_Binormal; - //gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0); - gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * localPosition; + gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition; } #type fragment @@ -63,7 +62,6 @@ const float Epsilon = 0.00001; const int LightCount = 1; - // Constant normal incidence Fresnel factor for all dielectrics. const vec3 Fdielectric = vec3(0.04); @@ -290,12 +288,7 @@ vec3 IBL(vec3 F0, vec3 Lr) int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex); float NoV = clamp(m_Params.NdotV, 0.0, 1.0); vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View; - vec3 specularIrradiance = vec3(0.0); - - if (u_RadiancePrefilter > 0.5) - specularIrradiance = PrefilterEnvMap(m_Params.Roughness * m_Params.Roughness, R) * u_RadiancePrefilter; - else - specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), sqrt(m_Params.Roughness) * u_EnvRadianceTexLevels).rgb * (1.0 - u_RadiancePrefilter); + vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness * m_Params.Roughness) * u_EnvRadianceTexLevels).rgb; // Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg; @@ -329,7 +322,7 @@ void main() // Fresnel reflectance, metals use albedo vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); - vec3 lightContribution = Lighting(F0); + vec3 lightContribution = vec3(0.0);//Lighting(F0); vec3 iblContribution = IBL(F0, Lr); color = vec4(lightContribution + iblContribution, 1.0); diff --git a/Editor/assets/shaders/PBRShader_Static.glsl b/Editor/assets/shaders/PBRShader_Static.glsl new file mode 100644 index 0000000..65b1544 --- /dev/null +++ b/Editor/assets/shaders/PBRShader_Static.glsl @@ -0,0 +1,319 @@ +// ----------------------------- +// -- Hazel Engine PBR shader -- +// ----------------------------- +// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in. +// Currently heavily updated. +// +// References upon which this is based: +// - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf) +// - Frostbite's SIGGRAPH 2014 paper (https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/) +// - Michał Siejak's PBR project (https://github.com/Nadrin) +// - My implementation from years ago in the Sparky engine (https://github.com/TheCherno/Sparky) +#type vertex +#version 430 core + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec3 a_Normal; +layout(location = 2) in vec3 a_Tangent; +layout(location = 3) in vec3 a_Binormal; +layout(location = 4) in vec2 a_TexCoord; + +uniform mat4 u_ViewProjectionMatrix; +uniform mat4 u_Transform; + +out VertexOutput +{ + vec3 WorldPosition; + vec3 Normal; + vec2 TexCoord; + mat3 WorldNormals; + mat3 WorldTransform; + vec3 Binormal; +} vs_Output; + +void main() +{ + vs_Output.WorldPosition = vec3(u_Transform * vec4(a_Position, 1.0)); + vs_Output.Normal = a_Normal; + vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); + vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal); + vs_Output.WorldTransform = mat3(u_Transform); + vs_Output.Binormal = a_Binormal; + + gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0); +} + +#type fragment +#version 430 core + +const float PI = 3.141592; +const float Epsilon = 0.00001; + +const int LightCount = 1; + +// Constant normal incidence Fresnel factor for all dielectrics. +const vec3 Fdielectric = vec3(0.04); + +struct Light { + vec3 Direction; + vec3 Radiance; +}; + +in VertexOutput +{ + vec3 WorldPosition; + vec3 Normal; + vec2 TexCoord; + mat3 WorldNormals; + mat3 WorldTransform; + vec3 Binormal; +} vs_Input; + +layout(location = 0) out vec4 color; + +uniform Light lights; +uniform vec3 u_CameraPosition; + +// PBR texture inputs +uniform sampler2D u_AlbedoTexture; +uniform sampler2D u_NormalTexture; +uniform sampler2D u_MetalnessTexture; +uniform sampler2D u_RoughnessTexture; + +// Environment maps +uniform samplerCube u_EnvRadianceTex; +uniform samplerCube u_EnvIrradianceTex; + +// BRDF LUT +uniform sampler2D u_BRDFLUTTexture; + +uniform vec3 u_AlbedoColor; +uniform float u_Metalness; +uniform float u_Roughness; + +uniform float u_EnvMapRotation; + +// Toggles +uniform float u_RadiancePrefilter; +uniform float u_AlbedoTexToggle; +uniform float u_NormalTexToggle; +uniform float u_MetalnessTexToggle; +uniform float u_RoughnessTexToggle; + +struct PBRParameters +{ + vec3 Albedo; + float Roughness; + float Metalness; + + vec3 Normal; + vec3 View; + float NdotV; +}; + +PBRParameters m_Params; + +// GGX/Towbridge-Reitz normal distribution function. +// Uses Disney's reparametrization of alpha = roughness^2 +float ndfGGX(float cosLh, float roughness) +{ + float alpha = roughness * roughness; + float alphaSq = alpha * alpha; + + float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0; + return alphaSq / (PI * denom * denom); +} + +// Single term for separable Schlick-GGX below. +float gaSchlickG1(float cosTheta, float k) +{ + return cosTheta / (cosTheta * (1.0 - k) + k); +} + +// Schlick-GGX approximation of geometric attenuation function using Smith's method. +float gaSchlickGGX(float cosLi, float NdotV, float roughness) +{ + float r = roughness + 1.0; + float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights. + return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k); +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +// Shlick's approximation of the Fresnel factor. +vec3 fresnelSchlick(vec3 F0, float cosTheta) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +// --------------------------------------------------------------------------------------------------- +// The following code (from Unreal Engine 4's paper) shows how to filter the environment map +// for different roughnesses. This is mean to be computed offline and stored in cube map mips, +// so turning this on online will cause poor performance +float RadicalInverse_VdC(uint bits) +{ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +vec2 Hammersley(uint i, uint N) +{ + return vec2(float(i)/float(N), RadicalInverse_VdC(i)); +} + +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) +{ + float a = Roughness * Roughness; + float Phi = 2 * PI * Xi.x; + float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) ); + float SinTheta = sqrt( 1 - CosTheta * CosTheta ); + vec3 H; + H.x = SinTheta * cos( Phi ); + H.y = SinTheta * sin( Phi ); + H.z = CosTheta; + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0); + vec3 TangentX = normalize( cross( UpVector, N ) ); + vec3 TangentY = cross( N, TangentX ); + // Tangent to world space + return TangentX * H.x + TangentY * H.y + N * H.z; +} + +float TotalWeight = 0.0; + +vec3 PrefilterEnvMap(float Roughness, vec3 R) +{ + vec3 N = R; + vec3 V = R; + vec3 PrefilteredColor = vec3(0.0); + int NumSamples = 1024; + for(int i = 0; i < NumSamples; i++) + { + vec2 Xi = Hammersley(i, NumSamples); + vec3 H = ImportanceSampleGGX(Xi, Roughness, N); + vec3 L = 2 * dot(V, H) * H - V; + float NoL = clamp(dot(N, L), 0.0, 1.0); + if (NoL > 0) + { + PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL; + TotalWeight += NoL; + } + } + return PrefilteredColor / TotalWeight; +} + +// --------------------------------------------------------------------------------------------------- + +vec3 RotateVectorAboutY(float angle, vec3 vec) +{ + angle = radians(angle); + mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), + vec3(0.0,1.0,0.0), + vec3(-sin(angle),0.0,cos(angle))}; + return rotationMatrix * vec; +} + +vec3 Lighting(vec3 F0) +{ + vec3 result = vec3(0.0); + for(int i = 0; i < LightCount; i++) + { + vec3 Li = -lights.Direction; + vec3 Lradiance = lights.Radiance; + vec3 Lh = normalize(Li + m_Params.View); + + // Calculate angles between surface normal and various light vectors. + float cosLi = max(0.0, dot(m_Params.Normal, Li)); + float cosLh = max(0.0, dot(m_Params.Normal, Lh)); + + vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View))); + float D = ndfGGX(cosLh, m_Params.Roughness); + float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness); + + vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); + vec3 diffuseBRDF = kd * m_Params.Albedo; + + // Cook-Torrance + vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV); + + result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi; + } + return result; +} + +vec3 IBL(vec3 F0, vec3 Lr) +{ + vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb; + vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness); + vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); + vec3 diffuseIBL = m_Params.Albedo * irradiance; + + int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex); + float NoV = clamp(m_Params.NdotV, 0.0, 1.0); + vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View; + vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness * m_Params.Roughness) * u_EnvRadianceTexLevels).rgb; + + // Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model + vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg; + vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y); + + return kd * diffuseIBL + specularIBL; +} + +void main() +{ + // Standard PBR inputs + m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; + m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness; + m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness; + m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight + + // Normals (either from vertex or map) + m_Params.Normal = normalize(vs_Input.Normal); + if (u_NormalTexToggle > 0.5) + { + m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0); + m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal); + } + + m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition); + m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0); + + // Specular reflection vector + vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View; + + // Fresnel reflectance, metals use albedo + vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); + + vec3 lightContribution = Lighting(F0); + vec3 iblContribution = IBL(F0, Lr); + + color = vec4(lightContribution + iblContribution, 1.0); +} diff --git a/Editor/assets/shaders/Skybox.glsl b/Editor/assets/shaders/Skybox.glsl new file mode 100644 index 0000000..9cad508 --- /dev/null +++ b/Editor/assets/shaders/Skybox.glsl @@ -0,0 +1,33 @@ +// Skybox shader + +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; + +uniform mat4 u_InverseVP; + +out vec3 v_Position; + +void main() +{ + vec4 position = vec4(a_Position.xy, 1.0, 1.0); + gl_Position = position; + + v_Position = (u_InverseVP * position).xyz; +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 finalColor; + +uniform samplerCube u_Texture; +uniform float u_TextureLod; + +in vec3 v_Position; + +void main() +{ + finalColor = textureLod(u_Texture, v_Position, u_TextureLod); +} \ No newline at end of file diff --git a/Editor/assets/shaders/hdr.glsl b/Editor/assets/shaders/hdr.glsl index 2549d63..cf91719 100644 --- a/Editor/assets/shaders/hdr.glsl +++ b/Editor/assets/shaders/hdr.glsl @@ -8,7 +8,7 @@ out vec2 v_TexCoord; void main() { - vec4 position = vec4(a_Position.xy, 1.0, 1.0); + vec4 position = vec4(a_Position.xy, 0.0, 1.0); v_TexCoord = a_TexCoord; gl_Position = position; } diff --git a/Editor/assets/shaders/shader.glsl b/Editor/assets/shaders/shader.glsl index aa3771d..a941791 100644 --- a/Editor/assets/shaders/shader.glsl +++ b/Editor/assets/shaders/shader.glsl @@ -22,11 +22,14 @@ void main() layout(location = 0) out vec4 finalColor; -//uniform vec4 u_Color; +uniform vec4 u_Color; in vec3 v_Normal; void main() { + finalColor = vec4(0.8, 0.0, 0.8, 1.0); + + finalColor = vec4((v_Normal * 0.5 + 0.5), 1.0);// * u_Color.xyz, 1.0); } \ No newline at end of file diff --git a/Editor/assets/shaders/simplepbr_Static.glsl b/Editor/assets/shaders/simplepbr_Static.glsl deleted file mode 100644 index 85709e0..0000000 --- a/Editor/assets/shaders/simplepbr_Static.glsl +++ /dev/null @@ -1,321 +0,0 @@ -// ----------------------------- -// -- Hazel Engine PBR shader -- -// ----------------------------- -// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in. -// Currently heavily updated. -// -// References upon which this is based: -// - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf) -// - Frostbite's SIGGRAPH 2014 paper (https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/) -// - Michał Siejak's PBR project (https://github.com/Nadrin) -// - My implementation from years ago in the Sparky engine (https://github.com/TheCherno/Sparky) -#type vertex -#version 430 core - -layout(location = 0) in vec3 a_Position; -layout(location = 1) in vec3 a_Normal; -layout(location = 2) in vec3 a_Tangent; -layout(location = 3) in vec3 a_Binormal; -layout(location = 4) in vec2 a_TexCoord; - -uniform mat4 u_ViewProjectionMatrix; -uniform mat4 u_ModelMatrix; - -out VertexOutput -{ - vec3 WorldPosition; - vec3 Normal; - vec2 TexCoord; - mat3 WorldNormals; - vec3 Binormal; -} vs_Output; - -void main() -{ - vs_Output.WorldPosition = vec3(u_ModelMatrix * vec4(a_Position, 1.0)); - vs_Output.Normal = a_Normal; - vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); - vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal); - vs_Output.Binormal = a_Binormal; - - gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0); -} - -#type fragment -#version 430 core - -const float PI = 3.141592; -const float Epsilon = 0.00001; - -const int LightCount = 1; - -// Constant normal incidence Fresnel factor for all dielectrics. -const vec3 Fdielectric = vec3(0.04); - -struct Light { - vec3 Direction; - vec3 Radiance; -}; - -in VertexOutput -{ - vec3 WorldPosition; - vec3 Normal; - vec2 TexCoord; - mat3 WorldNormals; - vec3 Binormal; -} vs_Input; - -layout(location = 0) out vec4 color; - -uniform Light lights; -uniform vec3 u_CameraPosition; - -// PBR texture inputs -uniform sampler2D u_AlbedoTexture; -uniform sampler2D u_NormalTexture; -uniform sampler2D u_MetalnessTexture; -uniform sampler2D u_RoughnessTexture; - -// Environment maps -uniform samplerCube u_EnvRadianceTex; -uniform samplerCube u_EnvIrradianceTex; - -// BRDF LUT -uniform sampler2D u_BRDFLUTTexture; - -uniform vec3 u_AlbedoColor; -uniform float u_Metalness; -uniform float u_Roughness; - -uniform float u_EnvMapRotation; - -// Toggles -uniform float u_RadiancePrefilter; -uniform float u_AlbedoTexToggle; -uniform float u_NormalTexToggle; -uniform float u_MetalnessTexToggle; -uniform float u_RoughnessTexToggle; - -struct PBRParameters -{ - vec3 Albedo; - float Roughness; - float Metalness; - - vec3 Normal; - vec3 View; - float NdotV; -}; - -PBRParameters m_Params; - -// GGX/Towbridge-Reitz normal distribution function. -// Uses Disney's reparametrization of alpha = roughness^2 -float ndfGGX(float cosLh, float roughness) -{ - float alpha = roughness * roughness; - float alphaSq = alpha * alpha; - - float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0; - return alphaSq / (PI * denom * denom); -} - -// Single term for separable Schlick-GGX below. -float gaSchlickG1(float cosTheta, float k) -{ - return cosTheta / (cosTheta * (1.0 - k) + k); -} - -// Schlick-GGX approximation of geometric attenuation function using Smith's method. -float gaSchlickGGX(float cosLi, float NdotV, float roughness) -{ - float r = roughness + 1.0; - float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights. - return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k); -} - -float GeometrySchlickGGX(float NdotV, float roughness) -{ - float r = (roughness + 1.0); - float k = (r*r) / 8.0; - - float nom = NdotV; - float denom = NdotV * (1.0 - k) + k; - - return nom / denom; -} - -float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) -{ - float NdotV = max(dot(N, V), 0.0); - float NdotL = max(dot(N, L), 0.0); - float ggx2 = GeometrySchlickGGX(NdotV, roughness); - float ggx1 = GeometrySchlickGGX(NdotL, roughness); - - return ggx1 * ggx2; -} - -// Shlick's approximation of the Fresnel factor. -vec3 fresnelSchlick(vec3 F0, float cosTheta) -{ - return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); -} - -vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) -{ - return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); -} - -// --------------------------------------------------------------------------------------------------- -// The following code (from Unreal Engine 4's paper) shows how to filter the environment map -// for different roughnesses. This is mean to be computed offline and stored in cube map mips, -// so turning this on online will cause poor performance -float RadicalInverse_VdC(uint bits) -{ - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 -} - -vec2 Hammersley(uint i, uint N) -{ - return vec2(float(i)/float(N), RadicalInverse_VdC(i)); -} - -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) -{ - float a = Roughness * Roughness; - float Phi = 2 * PI * Xi.x; - float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) ); - float SinTheta = sqrt( 1 - CosTheta * CosTheta ); - vec3 H; - H.x = SinTheta * cos( Phi ); - H.y = SinTheta * sin( Phi ); - H.z = CosTheta; - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0); - vec3 TangentX = normalize( cross( UpVector, N ) ); - vec3 TangentY = cross( N, TangentX ); - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; -} - -float TotalWeight = 0.0; - -vec3 PrefilterEnvMap(float Roughness, vec3 R) -{ - vec3 N = R; - vec3 V = R; - vec3 PrefilteredColor = vec3(0.0); - int NumSamples = 1024; - for(int i = 0; i < NumSamples; i++) - { - vec2 Xi = Hammersley(i, NumSamples); - vec3 H = ImportanceSampleGGX(Xi, Roughness, N); - vec3 L = 2 * dot(V, H) * H - V; - float NoL = clamp(dot(N, L), 0.0, 1.0); - if (NoL > 0) - { - PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL; - TotalWeight += NoL; - } - } - return PrefilteredColor / TotalWeight; -} - -// --------------------------------------------------------------------------------------------------- - -vec3 RotateVectorAboutY(float angle, vec3 vec) -{ - angle = radians(angle); - mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)), - vec3(0.0,1.0,0.0), - vec3(-sin(angle),0.0,cos(angle))}; - return rotationMatrix * vec; -} - -vec3 Lighting(vec3 F0) -{ - vec3 result = vec3(0.0); - for(int i = 0; i < LightCount; i++) - { - vec3 Li = -lights.Direction; - vec3 Lradiance = lights.Radiance; - vec3 Lh = normalize(Li + m_Params.View); - - // Calculate angles between surface normal and various light vectors. - float cosLi = max(0.0, dot(m_Params.Normal, Li)); - float cosLh = max(0.0, dot(m_Params.Normal, Lh)); - - vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View))); - float D = ndfGGX(cosLh, m_Params.Roughness); - float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness); - - vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); - vec3 diffuseBRDF = kd * m_Params.Albedo; - - // Cook-Torrance - vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV); - - result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi; - } - return result; -} - -vec3 IBL(vec3 F0, vec3 Lr) -{ - vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb; - vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness); - vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness); - vec3 diffuseIBL = m_Params.Albedo * irradiance; - - int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex); - float NoV = clamp(m_Params.NdotV, 0.0, 1.0); - vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View; - vec3 specularIrradiance = vec3(0.0); - - if (u_RadiancePrefilter > 0.5) - specularIrradiance = PrefilterEnvMap(m_Params.Roughness * m_Params.Roughness, R) * u_RadiancePrefilter; - else - specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), sqrt(m_Params.Roughness) * u_EnvRadianceTexLevels).rgb * (1.0 - u_RadiancePrefilter); - - // Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model - vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg; - vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y); - - return kd * diffuseIBL + specularIBL; -} - -void main() -{ - // Standard PBR inputs - m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor; - m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness; - m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness; - m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight - - // Normals (either from vertex or map) - m_Params.Normal = normalize(vs_Input.Normal); - if (u_NormalTexToggle > 0.5) - { - m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0); - m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal); - } - - m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition); - m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0); - - // Specular reflection vector - vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View; - - // Fresnel reflectance, metals use albedo - vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness); - - vec3 lightContribution = Lighting(F0); - vec3 iblContribution = IBL(F0, Lr); - - color = vec4(lightContribution + iblContribution, 1.0); -} diff --git a/Editor/assets/textures/debug/DebugCubeMap.tga b/Editor/assets/textures/debug/DebugCubeMap.tga new file mode 100644 index 0000000..06cd95d Binary files /dev/null and b/Editor/assets/textures/debug/DebugCubeMap.tga differ diff --git a/Prism/CMakeLists.txt b/Prism/CMakeLists.txt index 28108bf..b0f525e 100644 --- a/Prism/CMakeLists.txt +++ b/Prism/CMakeLists.txt @@ -43,7 +43,6 @@ list(APPEND SRC_SOURCE ${IMGUIZMO_SOURCE}) # ------------- link libraries ------------- set(LINK_LIBRARIES_PRIVATE - spdlog glfw glad assimp @@ -52,6 +51,7 @@ set(LINK_LIBRARIES_PRIVATE ) set(LINK_LIBRARIES_PUBLIC + spdlog glm ) diff --git a/Prism/src/Prism.h b/Prism/src/Prism.h index 8c966ea..ca349ec 100644 --- a/Prism/src/Prism.h +++ b/Prism/src/Prism.h @@ -8,6 +8,7 @@ #include "Prism/Core/Application.h" #include "Prism/Core/TimeStep.h" +#include "Prism/Core/KeyCodes.h" #include "Prism/Core/Events/Event.h" #include "Prism/Core/Events/ApplicationEvent.h" #include "Prism/Core/Events/KeyEvent.h" @@ -18,6 +19,7 @@ #include "Prism/Renderer/Buffer.h" #include "Prism/Renderer/Renderer.h" +#include "Prism/Renderer/SceneRenderer.h" #include "Prism/Renderer/RenderPass.h" #include "Prism/Renderer/Shader.h" #include "Prism/Renderer/Texture.h" @@ -27,4 +29,8 @@ #include "Prism/Renderer/Camera.h" #include "Prism/Renderer/Material.h" +// Scene +#include "Prism/Scene/Entity.h" +#include "Prism/Scene/Scene.h" + #endif //PRISM_H diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index 281eac6..322d8f7 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -36,7 +36,7 @@ namespace Prism PushOverlay(m_ImGuiLayer); Renderer::Init(); - Renderer::Get().WaitAndRender(); + Renderer::WaitAndRender(); } Application::~Application() @@ -56,7 +56,7 @@ namespace Prism Renderer::Submit([this](){ this->RenderImGui(); }); - Renderer::Get().WaitAndRender(); + Renderer::WaitAndRender(); } @@ -87,13 +87,15 @@ namespace Prism { m_ImGuiLayer->Begin(); + /* ImGui::Begin("Renderer"); - auto& caps = RendererAPI::GetCapabilities(); + const auto& caps = RendererAPI::GetCapabilities(); ImGui::Text("Vendor: %s", caps.Vendor.c_str()); ImGui::Text("Renderer: %s", caps.Renderer.c_str()); ImGui::Text("Version: %s", caps.Version.c_str()); ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds()); ImGui::End(); + */ for (Layer* layer : m_LayerStack) layer->OnImGuiRender(); diff --git a/Prism/src/Prism/Core/Core.h b/Prism/src/Prism/Core/Core.h index ae6d232..2542149 100644 --- a/Prism/src/Prism/Core/Core.h +++ b/Prism/src/Prism/Core/Core.h @@ -61,6 +61,13 @@ namespace Prism template using Scope = std::unique_ptr; + template + constexpr Scope CreateScope(Args&& ... args) + { + return std::make_unique(std::forward(args)...); + } + + template using Ref = std::shared_ptr; diff --git a/Prism/src/Prism/Core/Input.cpp b/Prism/src/Prism/Core/Input.cpp index ce64150..c59e5bf 100644 --- a/Prism/src/Prism/Core/Input.cpp +++ b/Prism/src/Prism/Core/Input.cpp @@ -5,23 +5,4 @@ namespace Prism { - bool Input::IsKeyPressed(const int keycode) - { - return s_Instance->IsKeyPressedImpl(keycode); - } - - bool Input::IsMouseButtonPressed(const int button) - { - return s_Instance->IsMouseButtonPressedImpl(button); - } - - float Input::GetMouseX() - { - return s_Instance->GetMouseXImpl(); - } - - float Input::GetMouseY() - { - return s_Instance->GetMouseYImpl(); - } } diff --git a/Prism/src/Prism/Core/Input.h b/Prism/src/Prism/Core/Input.h index f2e3bdc..74366df 100644 --- a/Prism/src/Prism/Core/Input.h +++ b/Prism/src/Prism/Core/Input.h @@ -10,19 +10,11 @@ namespace Prism class PRISM_API Input { public: - static bool IsKeyPressed(const int keycode); + static bool IsKeyPressed(int keycode); - static bool IsMouseButtonPressed(const int button); + static bool IsMouseButtonPressed(int button); static float GetMouseX(); static float GetMouseY(); - - protected: - virtual bool IsKeyPressedImpl(int keycode) = 0; - virtual bool IsMouseButtonPressedImpl(int button) = 0; - virtual float GetMouseXImpl() = 0; - virtual float GetMouseYImpl() = 0; - private: - static Input* s_Instance; }; } diff --git a/Prism/src/Prism/Core/Log.cpp b/Prism/src/Prism/Core/Log.cpp index 853faa5..30f0f32 100644 --- a/Prism/src/Prism/Core/Log.cpp +++ b/Prism/src/Prism/Core/Log.cpp @@ -27,4 +27,14 @@ namespace Prism s_ClientLogger = spdlog::stdout_color_mt("APP"); s_ClientLogger->set_level(LOG_LEVEL); } + + std::shared_ptr& Log::GetCoreLogger() + { + return s_CoreLogger; + } + + std::shared_ptr& Log::GetClientLogger() + { + return s_ClientLogger; + } } diff --git a/Prism/src/Prism/Core/Log.h b/Prism/src/Prism/Core/Log.h index 701080a..6612c26 100644 --- a/Prism/src/Prism/Core/Log.h +++ b/Prism/src/Prism/Core/Log.h @@ -5,7 +5,7 @@ #ifndef LOG_H #define LOG_H -#include "spdlog/spdlog.h" +#include namespace Prism @@ -15,8 +15,9 @@ namespace Prism public: static void Init(); - inline static std::shared_ptr& GetCoreLogger() { return s_CoreLogger; } - inline static std::shared_ptr& GetClientLogger() { return s_ClientLogger; } + PRISM_API static std::shared_ptr& GetCoreLogger(); + PRISM_API static std::shared_ptr& GetClientLogger(); + private: static std::shared_ptr s_CoreLogger; static std::shared_ptr s_ClientLogger; diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.cpp b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp new file mode 100644 index 0000000..465e370 --- /dev/null +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.cpp @@ -0,0 +1,141 @@ +// +// Created by sfd on 25-11-30. +// + +#include "SceneHierachyPanel.h" + +#include "imgui.h" +#include "assimp/matrix4x4.h" +#define GLM_ENABLE_EXPERIMENTAL +#include "assimp/scene.h" +#include "glm/gtx/matrix_decompose.hpp" + +namespace Prism +{ + + glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix); + + SceneHierarchyPanel::SceneHierarchyPanel(const Ref& context) + : m_Context(context) + { + } + + void SceneHierarchyPanel::SetContext(const Ref& scene) + { + m_Context = scene; + } + + void SceneHierarchyPanel::OnImGuiRender() + { + ImGui::Begin("Scene Hierarchy"); + + auto& sceneEntities = m_Context->m_Entities; + for (Entity* entity : sceneEntities) + { + auto mesh = entity->GetMesh(); + auto material = entity->GetMaterial(); + const auto& transform = entity->GetTransform(); + + if (mesh) + { + DrawMeshNode(mesh); + } + + } + + ImGui::End(); + + ImGui::Begin("Properties"); + + if (m_SelectionContext) + { + /*auto mesh = m_SelectionContext; + + { + auto [translation, rotation, scale] = GetTransformDecomposition(transform); + ImGui::Text("World Transform"); + ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z); + ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z); + } + { + auto [translation, rotation, scale] = GetTransformDecomposition(localTransform); + ImGui::Text("Local Transform"); + ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z); + ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z); + }*/ + } + + 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 + + } + + static std::tuple GetTransformDecomposition(const glm::mat4& transform) + { + glm::vec3 scale, translation, skew; + glm::vec4 perspective; + glm::quat orientation; + glm::decompose(transform, scale, orientation, translation, skew, perspective); + + return { translation, orientation, scale }; + } + + void SceneHierarchyPanel::DrawMeshNode(const Ref& mesh) + { + auto rootNode = mesh->m_Scene->mRootNode; + MeshNodeHierarchy(mesh, rootNode); + } + + + void SceneHierarchyPanel::MeshNodeHierarchy(const Ref& mesh, aiNode* node, const glm::mat4& parentTransform, uint32_t level) + { + glm::mat4 localTransform = Mat4FromAssimpMat4(node->mTransformation); + glm::mat4 transform = parentTransform * localTransform; + for (uint32_t i = 0; i < node->mNumMeshes; i++) + { + uint32_t meshIndex = node->mMeshes[i]; + mesh->m_Submeshes[meshIndex].Transform = transform; + } + + if (ImGui::TreeNode(node->mName.C_Str())) + { + { + auto [translation, rotation, scale] = GetTransformDecomposition(transform); + ImGui::Text("World Transform"); + ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z); + ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z); + } + { + auto [translation, rotation, scale] = GetTransformDecomposition(localTransform); + ImGui::Text("Local Transform"); + ImGui::Text(" Translation: %.2f, %.2f, %.2f", translation.x, translation.y, translation.z); + ImGui::Text(" Scale: %.2f, %.2f, %.2f", scale.x, scale.y, scale.z); + } + + for (uint32_t i = 0; i < node->mNumChildren; i++) + MeshNodeHierarchy(mesh, node->mChildren[i], transform, level + 1); + + ImGui::TreePop(); + } + + } +} diff --git a/Prism/src/Prism/Editor/SceneHierachyPanel.h b/Prism/src/Prism/Editor/SceneHierachyPanel.h new file mode 100644 index 0000000..9d83756 --- /dev/null +++ b/Prism/src/Prism/Editor/SceneHierachyPanel.h @@ -0,0 +1,30 @@ +// +// Created by sfd on 25-11-30. +// + +#ifndef SCENEHIERACHYPANEL_H +#define SCENEHIERACHYPANEL_H +#include "Prism/Scene/Scene.h" + + +namespace Prism +{ + class PRISM_API SceneHierarchyPanel + { + public: + SceneHierarchyPanel(const Ref& context); + + void SetContext(const Ref& scene); + + void OnImGuiRender(); + private: + void DrawMeshNode(const Ref& mesh); + void MeshNodeHierarchy(const Ref& mesh, aiNode* node, const glm::mat4& parentTransform = glm::mat4(1.0f), uint32_t level = 0); + private: + Ref m_Context; + Ref m_SelectionContext; + }; +} + + +#endif //SCENEHIERACHYPANEL_H diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp index 49f0369..b0a7b96 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp @@ -19,7 +19,7 @@ namespace Prism OpenGLFrameBuffer::~OpenGLFrameBuffer() { - Renderer::Submit([this](){ + Renderer::Submit([=](){ glDeleteFramebuffers(1, &m_RendererID); }); } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index dac31ac..a8755f8 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -37,7 +37,8 @@ namespace Prism void OpenGLShader::Load(const std::string& source) { m_ShaderSource = PreProcess(source); - Parse(); + if (!m_IsCompute) + Parse(); Renderer::Submit([this](){ auto &a = m_RendererID; @@ -45,8 +46,11 @@ namespace Prism glDeleteProgram(m_RendererID); CompileAndUploadShader(); - ResolveUniforms(); - ValidateUniforms(); + if (!m_IsCompute) + { + ResolveUniforms(); + ValidateUniforms(); + } if (m_Loaded) { @@ -60,7 +64,7 @@ namespace Prism void OpenGLShader::Bind() { - Renderer::Submit([this](){ + Renderer::Submit([=](){ glUseProgram(m_RendererID); }); } @@ -179,7 +183,7 @@ namespace Prism } else { - PM_CORE_WARN("Could not read shader file {0}", filepath); + PM_CORE_ASSERT(false, "Could not load shader file"); } return result; @@ -206,11 +210,20 @@ namespace Prism PM_CORE_ASSERT(eol != std::string::npos, "Syntax error"); size_t begin = pos + typeTokenLength + 1; std::string type = source.substr(begin, eol - begin); - PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel", "Invalid shader type"); + PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel" || type == "compute", "Invalid shader type"); size_t nextLinePos = source.find_first_not_of("\r\n", eol); pos = source.find(typeToken, nextLinePos); - shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? m_ShaderSource.size() - 1 : nextLinePos)); + auto shaderType = ShaderTypeFromString(type); + shaderSources[shaderType] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? m_ShaderSource.size() - 1 : nextLinePos)); + + + // Compute shaders cannot contain other types + if (shaderType == GL_COMPUTE_SHADER) + { + m_IsCompute = true; + break; + } } return shaderSources; @@ -678,6 +691,8 @@ namespace Prism return GL_VERTEX_SHADER; if (type == "fragment" || type == "pixel") return GL_FRAGMENT_SHADER; + if (type == "compute") + return GL_COMPUTE_SHADER; return GL_NONE; } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h index 75ab48f..41b4f6a 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h @@ -19,6 +19,8 @@ namespace Prism OpenGLShader(const std::string& filepath); static Ref CreateFromString(const std::string& source); + virtual RendererID GetRendererID() const override { return m_RendererID; } + virtual void Reload(); virtual void Bind() override; @@ -81,15 +83,18 @@ namespace Prism void UploadUniformStruct(const OpenGLShaderUniformDeclaration* uniform, byte* buffer, uint32_t offset); void UploadUniformIntArray(const std::string& name, const int32_t* values, int32_t count) const; - inline const ShaderUniformBufferList& GetVSRendererUniforms() const override { return m_VSRendererUniformBuffers; } - inline const ShaderUniformBufferList& GetPSRendererUniforms() const override { return m_PSRendererUniformBuffers; } - inline const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const override { return *m_VSMaterialUniformBuffer; } - inline const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const override { return *m_PSMaterialUniformBuffer; } - inline const ShaderResourceList& GetResources() const override { return m_Resources; } + virtual const ShaderUniformBufferList& GetVSRendererUniforms() const override { return m_VSRendererUniformBuffers; } + virtual const ShaderUniformBufferList& GetPSRendererUniforms() const override { return m_PSRendererUniformBuffers; } + virtual bool HasVSMaterialUniformBuffer() const override { return (bool)m_VSMaterialUniformBuffer; } + virtual bool HasPSMaterialUniformBuffer() const override { return (bool)m_PSMaterialUniformBuffer; } + virtual const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const override { return *m_VSMaterialUniformBuffer; } + virtual const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const override { return *m_PSMaterialUniformBuffer; } + virtual const ShaderResourceList& GetResources() const override { return m_Resources; } private: RendererID m_RendererID = 0; bool m_Loaded = false; + bool m_IsCompute = false; std::string m_Name, m_AssetPath; std::unordered_map m_ShaderSource; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp index 844f93c..477b550 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp @@ -11,29 +11,23 @@ namespace Prism { - static GLenum PrismToOpenGLTextureFormat(TextureFormat format) + static GLint PrismToOpenGLTextureFormat(const TextureFormat format) { switch (format) { case TextureFormat::RGB: return GL_RGB; case TextureFormat::RGBA: return GL_RGBA; + case TextureFormat::Float16: return GL_RGBA16F; } return 0; } - static int CalculateMipMapCount(const int width, const int height) - { - int levels = 1; - while ((width | height) >> levels) { - levels++; - } - return levels; - } + // ****************************************** // Texture2D // ****************************************** - OpenGLTexture2D::OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap) + OpenGLTexture2D::OpenGLTexture2D(TextureFormat format, uint32_t width, uint32_t height, TextureWrap wrap) : m_Format(format), m_Width(width), m_Height(height), m_Wrap(wrap) { Renderer::Submit([this]() { @@ -42,12 +36,12 @@ namespace Prism glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - GLenum wrap = m_Wrap == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT; + const GLint wrap = m_Wrap == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); glTextureParameterf(m_RendererID, GL_TEXTURE_MAX_ANISOTROPY, RendererAPI::GetCapabilities().MaxAnisotropy); - glTexImage2D(GL_TEXTURE_2D, 0, Prism::PrismToOpenGLTextureFormat(m_Format), m_Width, m_Height, 0, Prism::PrismToOpenGLTextureFormat(m_Format), GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(m_Format), (GLint)m_Width, (GLint)m_Height, 0, Prism::PrismToOpenGLTextureFormat(m_Format), GL_UNSIGNED_BYTE, nullptr); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); @@ -60,25 +54,39 @@ namespace Prism : m_FilePath(path) { int width, height, channels; - PM_CORE_INFO("Loading texture {0}, srgb={1}", path, srgb); - m_ImageData.Data = stbi_load(path.c_str(), &width, &height, &channels, srgb ? STBI_rgb : STBI_rgb_alpha); + if (stbi_is_hdr(path.c_str())) + { + PM_CORE_INFO("Loading HDR texture {0}, srgb={1}", path, srgb); + m_ImageData.Data = (byte*)stbi_loadf(path.c_str(), &width, &height, &channels, 0); + m_IsHDR = true; + m_Format = TextureFormat::Float16; + } + else + { + PM_CORE_INFO("Loading texture {0}, srgb={1}", path, srgb); + m_ImageData.Data = stbi_load(path.c_str(), &width, &height, &channels, srgb ? STBI_rgb : STBI_rgb_alpha); + PM_CORE_ASSERT(m_ImageData.Data, "Could not read image!"); + m_Format = TextureFormat::RGBA; + } - m_Width = width; + if (!m_ImageData.Data) + return; + m_Loaded = true; + + m_Width = width; m_Height = height; - m_Format = TextureFormat::RGBA; - Renderer::Submit([this, srgb]() { + Renderer::Submit([=]() { // TODO: Consolidate properly if (srgb) { glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); - int levels = CalculateMipMapCount(m_Width, m_Height); - PM_CORE_INFO("Creating srgb texture width {0} mips", levels); - glTextureStorage2D(m_RendererID, levels, GL_SRGB8, m_Width, m_Height); + const auto levels = (GLint)CalculateMipMapCount(m_Width, m_Height); + glTextureStorage2D(m_RendererID, levels, GL_SRGB8, (GLint)m_Width, (GLint)m_Height); glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, levels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, GL_RGB, GL_UNSIGNED_BYTE, m_ImageData.Data); + glTextureSubImage2D(m_RendererID, 0, 0, 0, (GLint)m_Width, (GLint)m_Height, GL_RGB, GL_UNSIGNED_BYTE, m_ImageData.Data); glGenerateTextureMipmap(m_RendererID); } else @@ -90,8 +98,13 @@ namespace Prism glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + GLenum internalFormat = PrismToOpenGLTextureFormat(m_Format); + GLenum format = srgb ? GL_SRGB8 : (m_IsHDR ? GL_RGB : PrismToOpenGLTextureFormat(m_Format)); // HDR = GL_RGB for now + GLenum type = internalFormat == GL_RGBA16F ? GL_FLOAT : GL_UNSIGNED_BYTE; + glTexImage2D(GL_TEXTURE_2D, 0, (GLint)internalFormat, (GLint)m_Width, (GLint)m_Height, 0, format, type, m_ImageData.Data); - glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(m_Format), m_Width, m_Height, 0, srgb ? GL_SRGB8 : PrismToOpenGLTextureFormat(m_Format), GL_UNSIGNED_BYTE, m_ImageData.Data); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); @@ -114,6 +127,11 @@ namespace Prism }); } + uint32_t OpenGLTexture2D::GetMipLevelCount() const + { + return CalculateMipMapCount(m_Width, m_Height); + } + void OpenGLTexture2D::Lock() { m_Locked = true; @@ -132,9 +150,6 @@ namespace Prism PM_CORE_ASSERT(m_Locked, "Texture must be locked!"); m_ImageData.Allocate(width * height * Texture::GetBPP(m_Format)); -#if PM_DEBUG - m_ImageData.ZeroInitialize(); -#endif } Buffer OpenGLTexture2D::GetWriteableBuffer() @@ -148,6 +163,26 @@ namespace Prism // TextureCube // ****************************************** + OpenGLTextureCube::OpenGLTextureCube(TextureFormat format, uint32_t width, uint32_t height) + { + m_Width = width; + m_Height = height; + m_Format = format; + + const GLint levels = CalculateMipMapCount(width, height); + + Renderer::Submit([=]() { + glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, levels, PrismToOpenGLTextureFormat(m_Format), width, height); + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, levels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + }); + } + OpenGLTextureCube::OpenGLTextureCube(const std::string& path) : m_FilePath(path) { @@ -159,8 +194,8 @@ namespace Prism m_Height = height; m_Format = TextureFormat::RGB; - unsigned int faceWidth = m_Width / 4; - unsigned int faceHeight = m_Height / 3; + const uint32_t faceWidth = m_Width / 4; + const uint32_t faceHeight = m_Height / 3; PM_CORE_ASSERT(faceWidth == faceHeight, "Non-square faces!"); std::array faces; @@ -212,6 +247,8 @@ namespace Prism glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTextureParameterf(m_RendererID, GL_TEXTURE_MAX_ANISOTROPY, RendererAPI::GetCapabilities().MaxAnisotropy); auto format = PrismToOpenGLTextureFormat(m_Format); @@ -249,4 +286,8 @@ namespace Prism }); } + uint32_t OpenGLTextureCube::GetMipLevelCount() const + { + return CalculateMipMapCount(m_Width, m_Height); + } } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h index 039062b..9f83686 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h @@ -13,7 +13,7 @@ namespace Prism class OpenGLTexture2D : public Texture2D { public: - OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap); + OpenGLTexture2D(TextureFormat format, uint32_t width, uint32_t height, TextureWrap wrap); OpenGLTexture2D(const std::string& path, bool srgb); virtual ~OpenGLTexture2D(); @@ -23,12 +23,17 @@ namespace Prism virtual uint32_t GetWidth() const override { return m_Width; } virtual uint32_t GetHeight() const override { return m_Height; } + // This function currently returns the expected number of mips based on image size, + // not present mips in data + virtual uint32_t GetMipLevelCount() const override; + virtual void Lock() override; virtual void Unlock() override; virtual void Resize(uint32_t width, uint32_t height) override; virtual Buffer GetWriteableBuffer() override; + virtual bool Loaded() const override { return m_Loaded; } virtual const std::string& GetPath() const override { return m_FilePath; } @@ -39,6 +44,8 @@ namespace Prism unsigned int m_Width, m_Height; TextureWrap m_Wrap = TextureWrap::Clamp; + bool m_IsHDR = false; + bool m_Loaded = false; bool m_Locked = false; Buffer m_ImageData; @@ -49,14 +56,18 @@ namespace Prism class OpenGLTextureCube : public TextureCube { public: + OpenGLTextureCube(TextureFormat format, uint32_t width, uint32_t height); OpenGLTextureCube(const std::string& path); virtual ~OpenGLTextureCube(); virtual void Bind(unsigned int slot = 0) const override; virtual TextureFormat GetFormat() const override { return m_Format; } - virtual unsigned int GetWidth() const override { return m_Width; } - virtual unsigned int GetHeight() const override { return m_Height; } + virtual uint32_t GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + // This function currently returns the expected number of mips based on image size, + // not present mips in data + virtual uint32_t GetMipLevelCount() const override; virtual const std::string& GetPath() const override { return m_FilePath; } @@ -65,9 +76,9 @@ namespace Prism private: RendererID m_RendererID; TextureFormat m_Format; - unsigned int m_Width, m_Height; + uint32_t m_Width, m_Height; - unsigned char* m_ImageData; + uint8_t* m_ImageData; std::string m_FilePath; }; diff --git a/Prism/src/Prism/Platform/Windows/WindowsInput.cpp b/Prism/src/Prism/Platform/Windows/WindowsInput.cpp index eac569e..dc6edeb 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsInput.cpp +++ b/Prism/src/Prism/Platform/Windows/WindowsInput.cpp @@ -2,50 +2,47 @@ // Created by sfd on 25-11-20. // -#include "WindowsInput.h" - #include "WindowsWindow.h" #include "Prism/Core/Application.h" #include "GLFW/glfw3.h" +#include "Prism/Core/Input.h" namespace Prism { - Input* Input::s_Instance = new WindowsInput; - - bool WindowsInput::IsKeyPressedImpl(const int keycode) + bool Input::IsKeyPressed(int keycode) { - auto& window = static_cast(Application::Get().GetWindow()); - auto state = glfwGetKey((GLFWwindow*)window.GetNativeWindow(), keycode); + const auto& window = dynamic_cast(Application::Get().GetWindow()); + const auto state = glfwGetKey(static_cast(window.GetNativeWindow()), keycode); return state == GLFW_PRESS || state == GLFW_REPEAT; } - - bool WindowsInput::IsMouseButtonPressedImpl(const int button) + bool Input::IsMouseButtonPressed(int button) { - auto& window = static_cast(Application::Get().GetWindow()); + const auto& window = dynamic_cast(Application::Get().GetWindow()); - auto state = glfwGetMouseButton((GLFWwindow*)window.GetNativeWindow(), button); + const auto state = glfwGetMouseButton(static_cast(window.GetNativeWindow()), button); return state == GLFW_PRESS; } - float WindowsInput::GetMouseXImpl() + float Input::GetMouseX() { - auto& window = static_cast(Application::Get().GetWindow()); + const auto& window = dynamic_cast(Application::Get().GetWindow()); double xpos, ypos; - glfwGetCursorPos((GLFWwindow*)window.GetNativeWindow(), &xpos, &ypos); + glfwGetCursorPos(static_cast(window.GetNativeWindow()), &xpos, &ypos); - return (float)xpos; + return static_cast(xpos); } - float WindowsInput::GetMouseYImpl() + float Input::GetMouseY() { - auto& window = static_cast(Application::Get().GetWindow()); + const auto& window = dynamic_cast(Application::Get().GetWindow()); double xpos, ypos; - glfwGetCursorPos((GLFWwindow*)window.GetNativeWindow(), &xpos, &ypos); + glfwGetCursorPos(static_cast(window.GetNativeWindow()), &xpos, &ypos); - return (float)ypos; + return static_cast(ypos); } + } diff --git a/Prism/src/Prism/Platform/Windows/WindowsInput.h b/Prism/src/Prism/Platform/Windows/WindowsInput.h deleted file mode 100644 index 3a075fd..0000000 --- a/Prism/src/Prism/Platform/Windows/WindowsInput.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by sfd on 25-11-20. -// - -#ifndef WINDOWSINPUT_H -#define WINDOWSINPUT_H -#include "Prism/Core/Input.h" - - -namespace Prism -{ - class WindowsInput : public Input - { - protected: - virtual bool IsKeyPressedImpl(int keycode); - - virtual bool IsMouseButtonPressedImpl(int button); - virtual float GetMouseXImpl(); - virtual float GetMouseYImpl(); - }; -} - - - - -#endif //WINDOWSINPUT_H diff --git a/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp b/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp index 1168b9a..134f7e4 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp +++ b/Prism/src/Prism/Platform/Windows/WindowsWindow.cpp @@ -87,6 +87,7 @@ namespace Prism m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr); glfwMakeContextCurrent(m_Window); + // glfwMaximizeWindow(m_Window); // init glad int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); diff --git a/Prism/src/Prism/Renderer/Camera.h b/Prism/src/Prism/Renderer/Camera.h index 5cbce2f..fd76140 100644 --- a/Prism/src/Prism/Renderer/Camera.h +++ b/Prism/src/Prism/Renderer/Camera.h @@ -15,6 +15,7 @@ namespace Prism class PRISM_API Camera { public: + Camera() = default; Camera(const glm::mat4& projectionMatrix); void Focus(); @@ -32,6 +33,10 @@ namespace Prism glm::vec3 GetRightDirection(); glm::vec3 GetForwardDirection(); const glm::vec3& GetPosition() const { return m_Position; } + + float GetExposure() const { return m_Exposure; } + float& GetExposure() { return m_Exposure; } + public: inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; } private: @@ -57,6 +62,8 @@ namespace Prism float m_Distance; + float m_Exposure = 0.8f; + float m_Pitch, m_Yaw; }; } diff --git a/Prism/src/Prism/Renderer/Material.cpp b/Prism/src/Prism/Renderer/Material.cpp index eaaddbd..5b33315 100644 --- a/Prism/src/Prism/Renderer/Material.cpp +++ b/Prism/src/Prism/Renderer/Material.cpp @@ -11,6 +11,9 @@ namespace Prism { m_Shader->AddShaderReloadedCallback(std::bind(&Material::OnShaderReloaded, this)); AllocateStorage(); + + m_MaterialFlags |= (uint32_t)MaterialFlag::DepthTest; + m_MaterialFlags |= (uint32_t)MaterialFlag::Blend; } Material::~Material() @@ -25,13 +28,19 @@ namespace Prism void Material::AllocateStorage() { - const auto& vsBuffer = m_Shader->GetVSMaterialUniformBuffer(); - m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize()); - m_VSUniformStorageBuffer.ZeroInitialize(); + if (m_Shader->HasVSMaterialUniformBuffer()) + { + const auto& vsBuffer = m_Shader->GetVSMaterialUniformBuffer(); + m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize()); + m_VSUniformStorageBuffer.ZeroInitialize(); + } - const auto& psBuffer = m_Shader->GetPSMaterialUniformBuffer(); - m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize()); - m_PSUniformStorageBuffer.ZeroInitialize(); + if (m_Shader->HasPSMaterialUniformBuffer()) + { + const auto& psBuffer = m_Shader->GetPSMaterialUniformBuffer(); + m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize()); + m_PSUniformStorageBuffer.ZeroInitialize(); + } } void Material::OnShaderReloaded() @@ -135,15 +144,29 @@ namespace Prism m_OverriddenValues.clear(); } + void MaterialInstance::SetFlag(MaterialFlag flag, bool value) + { + if (value) + m_Material->m_MaterialFlags |= (uint32_t)flag; + else + m_Material->m_MaterialFlags &= ~(uint32_t)flag; + } + void MaterialInstance::AllocateStorage() { - const auto& vsBuffer = m_Material->m_Shader->GetVSMaterialUniformBuffer(); - m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize()); - memcpy(m_VSUniformStorageBuffer.Data, m_Material->m_VSUniformStorageBuffer.Data, vsBuffer.GetSize()); + if (m_Material->m_Shader->HasVSMaterialUniformBuffer()) + { + const auto& vsBuffer = m_Material->m_Shader->GetVSMaterialUniformBuffer(); + m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize()); + memcpy(m_VSUniformStorageBuffer.Data, m_Material->m_VSUniformStorageBuffer.Data, vsBuffer.GetSize()); + } - const auto& psBuffer = m_Material->m_Shader->GetPSMaterialUniformBuffer(); - m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize()); - memcpy(m_PSUniformStorageBuffer.Data, m_Material->m_PSUniformStorageBuffer.Data, psBuffer.GetSize()); + if (m_Material->m_Shader->HasPSMaterialUniformBuffer()) + { + const auto& psBuffer = m_Material->m_Shader->GetPSMaterialUniformBuffer(); + m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize()); + memcpy(m_PSUniformStorageBuffer.Data, m_Material->m_PSUniformStorageBuffer.Data, psBuffer.GetSize()); + } } void MaterialInstance::OnMaterialValueUpdated(ShaderUniformDeclaration* decl) diff --git a/Prism/src/Prism/Renderer/Material.h b/Prism/src/Prism/Renderer/Material.h index 6ab5e9e..c3c0125 100644 --- a/Prism/src/Prism/Renderer/Material.h +++ b/Prism/src/Prism/Renderer/Material.h @@ -8,6 +8,7 @@ #include "Shader.h" #include "Texture.h" +#include "Prism/Core/Log.h" namespace Prism { @@ -16,6 +17,13 @@ namespace Prism class MaterialInstance; #endif + enum class MaterialFlag + { + None = BIT(0), + DepthTest = BIT(1), + Blend = BIT(2) + }; + class PRISM_API Material { friend class MaterialInstance; @@ -27,6 +35,9 @@ namespace Prism void Bind() const; + uint32_t GetFlags() const { return m_MaterialFlags; } + void SetFlag(MaterialFlag flag) { m_MaterialFlags |= (uint32_t)flag; } + template void Set(const std::string& name, const T& value); @@ -60,14 +71,14 @@ namespace Prism Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration); private: - Ref m_Shader; + Ref m_Shader; std::unordered_set m_MaterialInstances; Buffer m_VSUniformStorageBuffer; Buffer m_PSUniformStorageBuffer; std::vector> m_Textures; - int32_t m_RenderFlags = 0; + int32_t m_MaterialFlags = 0; }; @@ -84,9 +95,8 @@ namespace Prism template void Set(const std::string& name, const T& value) { - auto decl = m_Material->FindUniformDeclaration(name); - // HZ_CORE_ASSERT(decl, "Could not find uniform with name '{0}'", name); - PM_CORE_ASSERT(decl, "Could not find uniform with name 'x'"); + const auto decl = m_Material->FindUniformDeclaration(name); + if (!decl) PM_CORE_WARN("Could not find uniform with name {0}", name); auto& buffer = GetUniformBufferTarget(decl); buffer.Write((byte*)& value, decl->GetSize(), decl->GetOffset()); @@ -113,6 +123,12 @@ namespace Prism } void Bind() const; + + uint32_t GetFlags() const { return m_Material->m_MaterialFlags; } + bool GetFlag(MaterialFlag flag) const { return (uint32_t)flag & m_Material->m_MaterialFlags; } + void SetFlag(MaterialFlag flag, bool value = true); + + RefGetShader() { return m_Material->m_Shader; } private: void AllocateStorage(); void OnShaderReloaded(); diff --git a/Prism/src/Prism/Renderer/Mesh.cpp b/Prism/src/Prism/Renderer/Mesh.cpp index 825fddd..30020be 100644 --- a/Prism/src/Prism/Renderer/Mesh.cpp +++ b/Prism/src/Prism/Renderer/Mesh.cpp @@ -13,6 +13,8 @@ #include "glad/glad.h" #define GLM_ENABLE_EXPERIMENTAL +#include + #include "glm/gtx/quaternion.hpp" #include "Prism/Core/Log.h" @@ -45,15 +47,15 @@ namespace Prism } }; - static glm::mat4 aiMatrix4x4ToGlm(const aiMatrix4x4 &from) + glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix) { - glm::mat4 to; + glm::mat4 result; //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column - to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; - to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; - to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; - to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; - return to; + result[0][0] = matrix.a1; result[1][0] = matrix.a2; result[2][0] = matrix.a3; result[3][0] = matrix.a4; + result[0][1] = matrix.b1; result[1][1] = matrix.b2; result[2][1] = matrix.b3; result[3][1] = matrix.b4; + result[0][2] = matrix.c1; result[1][2] = matrix.c2; result[2][2] = matrix.c3; result[3][2] = matrix.c4; + result[0][3] = matrix.d1; result[1][3] = matrix.d2; result[2][3] = matrix.d3; result[3][3] = matrix.d4; + return result; } @@ -102,11 +104,14 @@ namespace Prism if (!scene || !scene->HasMeshes()) PM_CORE_ERROR("Failed to load mesh file: {0}", filename); + //double factor; + //scene->mMetaData->Get("UnitScaleFactor", factor); + //PM_CORE_INFO("FBX Scene Scale: {0}", factor); m_IsAnimated = scene->mAnimations != nullptr; - m_MeshShader = m_IsAnimated ? Renderer::GetShaderLibrary()->Get("simplepbr_Anim") : Renderer::GetShaderLibrary()->Get("simplepbr_Static"); - m_Material.reset(new Prism::Material(m_MeshShader)); - m_InverseTransform = glm::inverse(aiMatrix4x4ToGlm(scene->mRootNode->mTransformation)); + m_MeshShader = m_IsAnimated ? Renderer::GetShaderLibrary()->Get("PBRShader_Anim") : Renderer::GetShaderLibrary()->Get("PBRShader_Static"); + m_BaseMaterial = CreateRef(m_MeshShader); + m_InverseTransform = glm::inverse(Mat4FromAssimpMat4(scene->mRootNode->mTransformation)); uint32_t vertexCount = 0; uint32_t indexCount = 0; @@ -200,7 +205,7 @@ namespace Prism m_BoneCount++; BoneInfo bi; m_BoneInfo.push_back(bi); - m_BoneInfo[boneIndex].BoneOffset = aiMatrix4x4ToGlm(bone->mOffsetMatrix); + m_BoneInfo[boneIndex].BoneOffset = Mat4FromAssimpMat4(bone->mOffsetMatrix); m_BoneMapping[boneName] = boneIndex; } else @@ -219,6 +224,213 @@ namespace Prism } } + // Material + if (scene->HasMaterials()) + { + m_Textures.resize(scene->mNumMaterials); + m_Materials.resize(scene->mNumMaterials); + for (uint32_t i = 0; i < scene->mNumMaterials; i++) + { + auto aiMaterial = scene->mMaterials[i]; + auto aiMaterialName = aiMaterial->GetName(); + + auto mi = CreateRef(m_BaseMaterial); + m_Materials[i] = mi; + + PM_CORE_INFO("Material Name = {0}; Index = {1}", aiMaterialName.data, i); + aiString aiTexPath; + uint32_t textureCount = aiMaterial->GetTextureCount(aiTextureType_DIFFUSE); + PM_CORE_TRACE(" TextureCount = {0}", textureCount); + + aiColor3D aiColor; + aiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, aiColor); + PM_CORE_TRACE("COLOR = {0}, {1}, {2}", aiColor.r, aiColor.g, aiColor.b); + + if (aiMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &aiTexPath) == AI_SUCCESS) + { + // TODO: Temp - this should be handled by Hazel's filesystem + std::filesystem::path path = filename; + auto parentPath = path.parent_path(); + parentPath /= std::string(aiTexPath.data); + std::string texturePath = parentPath.string(); + + auto texture = Texture2D::Create(texturePath, true); + if (texture->Loaded()) + { + m_Textures[i] = texture; + PM_CORE_TRACE(" Texture Path = {0}", texturePath); + mi->Set("u_AlbedoTexture", m_Textures[i]); + mi->Set("u_AlbedoTexToggle", 1.0f); + } + else + { + PM_CORE_ERROR("Could not load texture: {0}", texturePath); + //mi->Set("u_AlbedoTexToggle", 0.0f); + mi->Set("u_AlbedoColor", glm::vec3{ aiColor.r, aiColor.g, aiColor.b }); + } + } + else + { + mi->Set("u_AlbedoTexToggle", 0.0f); + mi->Set("u_AlbedoColor", glm::vec3 { aiColor.r, aiColor.g, aiColor.b }); + } + + for (uint32_t i = 0; i < aiMaterial->mNumProperties; i++) + { + auto prop = aiMaterial->mProperties[i]; + PM_CORE_TRACE("Material Property:"); + PM_CORE_TRACE(" Name = {0}", prop->mKey.data); + + switch (prop->mSemantic) + { + case aiTextureType_NONE: + PM_CORE_TRACE(" Semantic = aiTextureType_NONE"); + break; + case aiTextureType_DIFFUSE: + PM_CORE_TRACE(" Semantic = aiTextureType_DIFFUSE"); + break; + case aiTextureType_SPECULAR: + PM_CORE_TRACE(" Semantic = aiTextureType_SPECULAR"); + break; + case aiTextureType_AMBIENT: + PM_CORE_TRACE(" Semantic = aiTextureType_AMBIENT"); + break; + case aiTextureType_EMISSIVE: + PM_CORE_TRACE(" Semantic = aiTextureType_EMISSIVE"); + break; + case aiTextureType_HEIGHT: + PM_CORE_TRACE(" Semantic = aiTextureType_HEIGHT"); + break; + case aiTextureType_NORMALS: + PM_CORE_TRACE(" Semantic = aiTextureType_NORMALS"); + break; + case aiTextureType_SHININESS: + PM_CORE_TRACE(" Semantic = aiTextureType_SHININESS"); + break; + case aiTextureType_OPACITY: + PM_CORE_TRACE(" Semantic = aiTextureType_OPACITY"); + break; + case aiTextureType_DISPLACEMENT: + PM_CORE_TRACE(" Semantic = aiTextureType_DISPLACEMENT"); + break; + case aiTextureType_LIGHTMAP: + PM_CORE_TRACE(" Semantic = aiTextureType_LIGHTMAP"); + break; + case aiTextureType_REFLECTION: + PM_CORE_TRACE(" Semantic = aiTextureType_REFLECTION"); + break; + case aiTextureType_UNKNOWN: + PM_CORE_TRACE(" Semantic = aiTextureType_UNKNOWN"); + break; + } + + if (prop->mType == aiPTI_String) + { + uint32_t strLength = *(uint32_t*)prop->mData; + std::string str(prop->mData + 4, strLength); + PM_CORE_TRACE(" Value = {0}", str); + + std::string key = prop->mKey.data; + if (key == "$raw.ReflectionFactor|file") + { + // TODO: Temp - this should be handled by Hazel's filesystem + std::filesystem::path path = filename; + auto parentPath = path.parent_path(); + parentPath /= str; + std::string texturePath = parentPath.string(); + + auto texture = Texture2D::Create(texturePath); + if (texture->Loaded()) + { + PM_CORE_TRACE(" Metalness map path = {0}", texturePath); + mi->Set("u_MetalnessTexture", texture); + mi->Set("u_MetalnessTexToggle", 1.0f); + } + else + { + PM_CORE_ERROR("Could not load texture: {0}", texturePath); + mi->Set("u_Metalness", 0.5f); + mi->Set("u_MetalnessTexToggle", 1.0f); + } + } + } + } + + + // Normal maps + if (aiMaterial->GetTexture(aiTextureType_NORMALS, 0, &aiTexPath) == AI_SUCCESS) + { + // TODO: Temp - this should be handled by Hazel's filesystem + std::filesystem::path path = filename; + auto parentPath = path.parent_path(); + parentPath /= std::string(aiTexPath.data); + std::string texturePath = parentPath.string(); + + auto texture = Texture2D::Create(texturePath); + if (texture->Loaded()) + { + PM_CORE_TRACE(" Normal map path = {0}", texturePath); + mi->Set("u_NormalTexture", texture); + mi->Set("u_NormalTexToggle", 1.0f); + } + else + { + PM_CORE_ERROR("Could not load texture: {0}", texturePath); + //mi->Set("u_AlbedoTexToggle", 0.0f); + // mi->Set("u_AlbedoColor", glm::vec3{ color.r, color.g, color.b }); + } + } + + // Roughness map + if (aiMaterial->GetTexture(aiTextureType_SHININESS, 0, &aiTexPath) == AI_SUCCESS) + { + // TODO: Temp - this should be handled by Hazel's filesystem + std::filesystem::path path = filename; + auto parentPath = path.parent_path(); + parentPath /= std::string(aiTexPath.data); + std::string texturePath = parentPath.string(); + + auto texture = Texture2D::Create(texturePath); + if (texture->Loaded()) + { + PM_CORE_TRACE(" Roughness map path = {0}", texturePath); + mi->Set("u_RoughnessTexture", texture); + mi->Set("u_RoughnessTexToggle", 1.0f); + } + else + { + PM_CORE_ERROR("Could not load texture: {0}", texturePath); + mi->Set("u_RoughnessTexToggle", 1.0f); + mi->Set("u_Roughness", 0.5f); + } + } + + // Metalness map + if (aiMaterial->Get("$raw.ReflectionFactor|file", aiPTI_String, 0, aiTexPath) == AI_SUCCESS) + { + // TODO: Temp - this should be handled by Hazel's filesystem + std::filesystem::path path = filename; + auto parentPath = path.parent_path(); + parentPath /= std::string(aiTexPath.data); + std::string texturePath = parentPath.string(); + + auto texture = Texture2D::Create(texturePath); + if (texture->Loaded()) + { + PM_CORE_TRACE(" Metalness map path = {0}", texturePath); + mi->Set("u_MetalnessTexture", texture); + mi->Set("u_MetalnessTexToggle", 1.0f); + } + else + { + PM_CORE_ERROR("Could not load texture: {0}", texturePath); + mi->Set("u_Metalness", 0.5f); + mi->Set("u_MetalnessTexToggle", 1.0f); + } + } + } + } + m_VertexArray = VertexArray::Create(); if (m_IsAnimated) { @@ -247,17 +459,6 @@ namespace Prism } - /* - m_VertexBuffer.reset(VertexBuffer::Create()); - if (m_IsAnimated) - m_VertexBuffer->SetData(m_AnimatedVertices.data(), m_AnimatedVertices.size() * sizeof(AnimatedVertex)); - else - m_VertexBuffer->SetData(m_StaticVertices.data(), m_StaticVertices.size() * sizeof(Vertex)); - - m_IndexBuffer.reset(IndexBuffer::Create()); - m_IndexBuffer->SetData(m_Indices.data(), m_Indices.size() * sizeof(Index)); - */ - auto ib = IndexBuffer::Create(m_Indices.data(), (uint32_t)m_Indices.size() * sizeof(Index)); m_VertexArray->SetIndexBuffer(ib); m_Scene = scene; @@ -265,12 +466,7 @@ namespace Prism Mesh::~Mesh() = default; - void Mesh::Render(const TimeStep deltaTime, const Ref& materialInstance) - { - Render(deltaTime, glm::mat4(1.0f), materialInstance); - } - - void Mesh::Render(TimeStep deltaTime, const glm::mat4& transform, const Ref& materialInstance) + void Mesh::OnUpdate(TimeStep deltaTime) { if (m_IsAnimated) { @@ -278,66 +474,16 @@ namespace Prism { m_WorldTime += deltaTime; - float ticksPerSecond = (float)(m_Scene->mAnimations[0]->mTicksPerSecond != 0 ? m_Scene->mAnimations[0]->mTicksPerSecond : 25.0f) * m_TimeMultiplier; + const float ticksPerSecond = (float)(m_Scene->mAnimations[0]->mTicksPerSecond != 0 ? m_Scene->mAnimations[0]->mTicksPerSecond : 25.0f) * m_TimeMultiplier; m_AnimationTime += deltaTime * ticksPerSecond; m_AnimationTime = fmod(m_AnimationTime, (float)m_Scene->mAnimations[0]->mDuration); } - BoneTransform(m_AnimationTime); + // TODO: We only need to recalc bones if rendering has been requested at the current animation frame + BoneTransform(m_AnimationTime); } - - if (materialInstance) - materialInstance->Bind(); - - // TODO: Sort this out - /* - m_VertexBuffer->Bind(); - m_IndexBuffer->Bind(); - */ - m_VertexArray->Bind(); - - bool materialOverride = !!materialInstance; - - // TODO: replace with render API calls - Renderer::Submit([=](){ - for (const Submesh& submesh : m_Submeshes) - { - if (m_IsAnimated) - { - for (size_t i = 0; i < m_BoneTransforms.size(); i++) - { - std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); - m_MeshShader->SetMat4FromRenderThread(uniformName, m_BoneTransforms[i]); - } - } - - // if (!materialOverride) - // self->m_MeshShader->SetMat4FromRenderThread("u_ModelMatrix", transform * submesh.Transform); - glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex); - } - }); } - void Mesh::OnImGuiRender() - { - ImGui::Begin("Mesh Debug"); - if (ImGui::CollapsingHeader(m_FilePath.c_str())) - { - if (m_IsAnimated) - { - if (ImGui::CollapsingHeader("Animation")) - { - if (ImGui::Button(m_AnimationPlaying ? "Pause" : "Play")) - m_AnimationPlaying = !m_AnimationPlaying; - - ImGui::SliderFloat("##AnimationTime", &m_AnimationTime, 0.0f, (float)m_Scene->mAnimations[0]->mDuration); - ImGui::DragFloat("Time Scale", &m_TimeMultiplier, 0.05f, 0.0f, 10.0f); - } - } - } - - ImGui::End(); - } void Mesh::DumpVertexBuffer() { @@ -387,7 +533,7 @@ namespace Prism { const std::string name(pNode->mName.data); const aiAnimation* animation = m_Scene->mAnimations[0]; - glm::mat4 nodeTransform(aiMatrix4x4ToGlm(pNode->mTransformation)); + glm::mat4 nodeTransform(Mat4FromAssimpMat4(pNode->mTransformation)); const aiNodeAnim* nodeAnim = FindNodeAnim(animation, name); if (nodeAnim) @@ -416,23 +562,26 @@ namespace Prism ReadNodeHierarchy(AnimationTime, pNode->mChildren[i], transform); } - void Mesh::TraverseNodes(const aiNode* node) + static std::string LevelToSpaces(uint32_t level) { - /* - std::string levelText; - for (int i = 0; i < level; i++) - levelText += "-"; - PM_CORE_TRACE("{0}Node name: {1}", levelText, std::string(node->mName.data)); - */ + std::string result; + for (uint32_t i = 0; i < level; i++) + result += "--"; + return result; + } + + void Mesh::TraverseNodes(const aiNode* node, const glm::mat4& parentTransform, uint32_t level) + { + const glm::mat4 transform = parentTransform * Mat4FromAssimpMat4(node->mTransformation); for (uint32_t i = 0; i < node->mNumMeshes; i++) { - uint32_t mesh = node->mMeshes[i]; - m_Submeshes[mesh].Transform = aiMatrix4x4ToGlm(node->mTransformation); + const uint32_t mesh = node->mMeshes[i]; + m_Submeshes[mesh].Transform = transform; } for (uint32_t i = 0; i < node->mNumChildren; i++) { - TraverseNodes(node->mChildren[i]); + TraverseNodes(node->mChildren[i], transform, level + 1); } } diff --git a/Prism/src/Prism/Renderer/Mesh.h b/Prism/src/Prism/Renderer/Mesh.h index 6e4ebfe..f33a497 100644 --- a/Prism/src/Prism/Renderer/Mesh.h +++ b/Prism/src/Prism/Renderer/Mesh.h @@ -97,18 +97,18 @@ namespace Prism Mesh(const std::string& filename); ~Mesh(); - void Render(TimeStep deltaTime, const Ref& materialInstance = Ref()); - void Render(TimeStep deltaTime, const glm::mat4& transform = glm::mat4(1.0f), const Ref& materialInstance = Ref()); - void OnImGuiRender(); + void OnUpdate(TimeStep deltaTime); void DumpVertexBuffer(); inline Ref GetMeshShader() { return m_MeshShader; } - inline Ref GetMaterial() { return m_Material; } - inline const std::string& GetFilePath() const { return m_FilePath; } + inline Ref GetMaterial() { return m_BaseMaterial; } + std::vector> GetMaterials() { return m_Materials; } + const std::vector>& GetTextures() const { return m_Textures; } + const std::string& GetFilePath() const { return m_FilePath; } private: void BoneTransform(float time); void ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const glm::mat4& ParentTransform); - void TraverseNodes(const aiNode* node); + 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); @@ -130,11 +130,6 @@ namespace Prism Ref m_VertexArray; - /* - std::unique_ptr m_VertexBuffer; - std::unique_ptr m_IndexBuffer; - */ - std::vector m_StaticVertices; std::vector m_AnimatedVertices; @@ -145,7 +140,10 @@ namespace Prism // Material Ref m_MeshShader; - Ref m_Material; + Ref m_BaseMaterial; + std::vector> m_Textures; + std::vector> m_NormalMaps; + std::vector> m_Materials; // Animation bool m_IsAnimated = false; @@ -157,6 +155,7 @@ namespace Prism std::string m_FilePath; private: friend class Renderer; + friend class SceneHierarchyPanel; }; } diff --git a/Prism/src/Prism/Renderer/Renderer.cpp b/Prism/src/Prism/Renderer/Renderer.cpp index bb495dd..e2f3868 100644 --- a/Prism/src/Prism/Renderer/Renderer.cpp +++ b/Prism/src/Prism/Renderer/Renderer.cpp @@ -5,14 +5,58 @@ #include "Renderer.h" #include "RendererAPI.h" +#include "SceneRenderer.h" #include "glad/glad.h" namespace Prism { - Renderer* Renderer::s_Instance = new Renderer(); RendererAPIType RendererAPI::s_CurrentRendererAPI = RendererAPIType::OpenGL; + struct RendererData + { + Ref m_ActiveRenderPass; + RenderCommandQueue m_CommandQueue; + Scope m_ShaderLibrary; + Ref m_FullscreenQuadVertexArray; + }; + + static RendererData s_Data; + + void Renderer::Init() + { + s_Data.m_ShaderLibrary = std::make_unique(); + Submit([](){ RendererAPI::Init(); }); + + GetShaderLibrary()->Load("assets/shaders/PBRShader_Static.glsl"); + GetShaderLibrary()->Load("assets/shaders/PBRShader_Anim.glsl"); + + SceneRenderer::Init(); + + // FullScreen Quad + static float fullScreenQuadVertex[] = { + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + static uint32_t fullScreenQuadIndices[] = { + 0, 1, 2, 2, 3, 0 + }; + + s_Data.m_FullscreenQuadVertexArray = VertexArray::Create(); + const auto quadVB = VertexBuffer::Create(fullScreenQuadVertex, sizeof(float) * sizeof(fullScreenQuadVertex)); + quadVB->SetLayout({ + { ShaderDataType::Float3, "a_Position" }, + { ShaderDataType::Float2, "a_TexCoord" } + }); + + const auto quadIB = IndexBuffer::Create(fullScreenQuadIndices, sizeof(fullScreenQuadIndices) * sizeof(uint32_t)); + s_Data.m_FullscreenQuadVertexArray->AddVertexBuffer(quadVB); + s_Data.m_FullscreenQuadVertexArray->SetIndexBuffer(quadIB); + + } + void Renderer::Clear() { Submit([]() @@ -39,90 +83,107 @@ namespace Prism }); } - void Renderer::ClearMagenta() - { - Clear(1, 0, 1); - } - void Renderer::Init() - { - s_Instance->m_ShaderLibrary = std::make_unique(); - Submit([](){ RendererAPI::Init(); }); - GetShaderLibrary()->Load("assets/shaders/simplepbr_Static.glsl"); - GetShaderLibrary()->Load("assets/shaders/simplepbr_Anim.glsl"); + const Scope& Renderer::GetShaderLibrary() + { + return s_Data.m_ShaderLibrary; } void Renderer::WaitAndRender() { - s_Instance->m_CommandQueue.Execute(); + s_Data.m_CommandQueue.Execute(); } void Renderer::BeginRenderPass(const Ref& renderPass) { - s_Instance->IBeginRenderPass(renderPass); - } + PM_CORE_ASSERT(renderPass, "Render pass cannot be null!"); - void Renderer::EndRenderPass() - { - s_Instance->IEndRenderPass(); - } - - void Renderer::SubmitMesh(const Ref& mesh, const glm::mat4& transform, - const Ref& overrideMaterial) - { - s_Instance->SubmitMeshI(mesh, transform, overrideMaterial); - } - - void Renderer::IBeginRenderPass(const Ref& renderPass) - { // TODO: Convert all of this into a render command buffer - m_ActiveRenderPass = renderPass; + s_Data.m_ActiveRenderPass = renderPass; renderPass->GetSpecification().TargetFramebuffer->Bind(); - - const glm::vec4& clearColor = renderPass->GetSpecification().TargetFramebuffer->GetSpecification().ClearColor; - Submit([=]() { + const glm::vec4& clearColor = renderPass->GetSpecification().TargetFramebuffer->GetSpecification().ClearColor; + Renderer::Submit([=]() { RendererAPI::Clear(clearColor.r, clearColor.g, clearColor.b, clearColor.a); }); } - void Renderer::IEndRenderPass() + void Renderer::EndRenderPass() { - PM_CORE_ASSERT(m_ActiveRenderPass, "No active render pass! Have you called Renderer::EndRenderPass twice?"); - m_ActiveRenderPass->GetSpecification().TargetFramebuffer->Unbind(); - m_ActiveRenderPass = nullptr; + PM_CORE_ASSERT(s_Data.m_ActiveRenderPass, "No active render pass! Have you called Renderer::EndRenderPass twice?"); + s_Data.m_ActiveRenderPass->GetSpecification().TargetFramebuffer->Unbind(); + s_Data.m_ActiveRenderPass = nullptr; } - void Renderer::SubmitMeshI(const Ref& mesh, const glm::mat4& transform, - const Ref& overrideMaterial) + void Renderer::SubmitQuad(const Ref& material, const glm::mat4& transform) { - if (overrideMaterial) + bool depthTest = true; + if (material) { - overrideMaterial->Bind(); - }else - { - // bind mesh material here + material->Bind(); + depthTest = material->GetFlag(MaterialFlag::DepthTest); + + auto shader = material->GetShader(); + shader->SetMat4("u_Transform", transform); } + s_Data.m_FullscreenQuadVertexArray->Bind(); + Renderer::DrawIndexed(6, depthTest); + } + + void Renderer::SubmitFullscreenQuad(const Ref& material) + { + bool depthTest = true; + if (material) + { + material->Bind(); + depthTest = material->GetFlag(MaterialFlag::DepthTest); + } + + s_Data.m_FullscreenQuadVertexArray->Bind(); + Renderer::DrawIndexed(6, depthTest); + } + + void Renderer::SubmitMesh(const Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial) + { + // auto material = overrideMaterial ? overrideMaterial : mesh->GetMaterialInstance(); + // auto shader = material->GetShader(); + + // TODO: Sort this out mesh->m_VertexArray->Bind(); - // TODO: temp test , use RenderAPI replace this - Submit([=]() + const auto& materials = mesh->GetMaterials(); + for (Submesh& submesh : mesh->m_Submeshes) { - for (const Submesh& submesh : mesh->m_Submeshes) - { - if (mesh->m_IsAnimated) - { - for (size_t i = 0; i < mesh->m_BoneTransforms.size(); i++) - { - std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); - mesh->m_MeshShader->SetMat4FromRenderThread(uniformName, mesh->m_BoneTransforms[i]); - } - } + // Material + auto material = materials[submesh.MaterialIndex]; + auto shader = material->GetShader(); + material->Bind(); - glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex); + if (mesh->m_IsAnimated) + { + for (size_t i = 0; i < mesh->m_BoneTransforms.size(); i++) + { + std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); + mesh->m_MeshShader->SetMat4(uniformName, mesh->m_BoneTransforms[i]); + } } - }); + shader->SetMat4("u_Transform", transform * submesh.Transform); + + Renderer::Submit([submesh, material]() { + if (material->GetFlag(MaterialFlag::DepthTest)) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex); + }); + } + } + + RenderCommandQueue& Renderer::GetRenderCommandQueue() + { + return s_Data.m_CommandQueue; } } diff --git a/Prism/src/Prism/Renderer/Renderer.h b/Prism/src/Prism/Renderer/Renderer.h index 3376d98..f4f9f0c 100644 --- a/Prism/src/Prism/Renderer/Renderer.h +++ b/Prism/src/Prism/Renderer/Renderer.h @@ -17,18 +17,15 @@ namespace Prism { public: typedef void(*RenderCommandFn)(void*); + static void Init(); static void Clear(); static void Clear(float r, float g, float b, float a = 1.0f); static void SetClearColor(float r, float g, float b, float a); static void DrawIndexed(uint32_t count, bool depthTest = true); - // test - static void ClearMagenta(); - static void Init(); - - static const Scope& GetShaderLibrary() { return Get().m_ShaderLibrary; } + static const Scope& GetShaderLibrary(); template static void Submit(FuncT&& func) @@ -42,39 +39,23 @@ namespace Prism // static_assert(std::is_trivially_destructible_v, "FuncT must be trivially destructible"); pFunc->~FuncT(); }; - auto storageBuffer = s_Instance->m_CommandQueue.Allocate(renderCmd, sizeof(func)); + auto storageBuffer = GetRenderCommandQueue().Allocate(renderCmd, sizeof(func)); new (storageBuffer) FuncT(std::forward(func)); } - /* - static void* Submit(const RenderCommandFn func, const unsigned int size) - { - return s_Instance->m_CommandQueue.Allocate(func, size); - } - */ - void WaitAndRender(); - inline static Renderer& Get() { return *s_Instance; } + static void WaitAndRender(); public: static void BeginRenderPass(const Ref& renderPass); static void EndRenderPass(); + static void SubmitQuad(const Ref& material, const glm::mat4& transform = glm::mat4(1.0f)); + static void SubmitFullscreenQuad(const Ref& material); static void SubmitMesh(const Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial = nullptr); private: - void IBeginRenderPass(const Ref& renderPass); - void IEndRenderPass(); - - void SubmitMeshI(const Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial = nullptr); - - - private: - static Renderer* s_Instance; - private: - Ref m_ActiveRenderPass; - Scope m_ShaderLibrary; - RenderCommandQueue m_CommandQueue; + static RenderCommandQueue& GetRenderCommandQueue(); }; #if 0 diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp new file mode 100644 index 0000000..480362a --- /dev/null +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -0,0 +1,252 @@ +// +// Created by sfd on 25-11-30. +// + +#include "SceneRenderer.h" + +#include "Camera.h" +#include "FrameBuffer.h" +#include "Material.h" +#include "Mesh.h" +#include "Renderer.h" +#include "RenderPass.h" +#include "glad/glad.h" + +namespace Prism +{ + struct SceneRendererData + { + const Scene* ActiveScene = nullptr; + struct SceneInfo + { + Camera SceneCamera; + + // Resources + Ref SkyboxMaterial; + Environment SceneEnvironment; + } SceneData; + + Ref BRDFLUT; + Ref CompositeShader; + + Ref GeoPass; + Ref CompositePass; + + struct DrawCommand + { + Ref mesh; + Ref Material; + glm::mat4 Transform; + }; + std::vector DrawList; + + // Grid + Ref GridMaterial; + }; + + static SceneRendererData s_Data; + + void SceneRenderer::Init() + { + FramebufferSpecification geoFramebufferSpec; + geoFramebufferSpec.Width = 1280; + geoFramebufferSpec.Height = 720; + geoFramebufferSpec.Format = FramebufferFormat::RGBA16F; + 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.Width = 1280; + compFramebufferSpec.Height = 720; + compFramebufferSpec.Format = FramebufferFormat::RGBA8; + compFramebufferSpec.ClearColor = { 0.5f, 0.1f, 0.1f, 1.0f }; + + RenderPassSpecification compRenderPassSpec; + compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec); + s_Data.CompositePass = RenderPass::Create(compRenderPassSpec); + + s_Data.CompositeShader = Shader::Create("assets/shaders/hdr.glsl"); + s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga"); + + + // Grid + const auto gridShader = Shader::Create("assets/shaders/Grid.glsl"); + s_Data.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); + } + + void SceneRenderer::SetViewportSize(uint32_t width, uint32_t height) + { + s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height); + s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height); + } + + void SceneRenderer::BeginScene(const Scene* scene) + { + PM_CORE_ASSERT(!s_Data.ActiveScene); + + s_Data.ActiveScene = scene; + + s_Data.SceneData.SceneCamera = scene->m_Camera; + s_Data.SceneData.SkyboxMaterial = scene->m_SkyboxMaterial; + s_Data.SceneData.SceneEnvironment = scene->m_Environment; + } + + void SceneRenderer::EndScene() + { + PM_CORE_ASSERT(s_Data.ActiveScene); + + s_Data.ActiveScene = nullptr; + + FlushDrawList(); + } + + void SceneRenderer::SubmitEntity(Entity* entity) + { + // TODO: Culling, sorting, etc. + + auto mesh = entity->GetMesh(); + if (!mesh) + return; + + s_Data.DrawList.push_back({ mesh, entity->GetMaterial(), entity->GetTransform() }); + } + + + static Ref equirectangularConversionShader, envFilteringShader, envIrradianceShader; + + std::pair, Ref> SceneRenderer::CreateEnvironmentMap(const std::string& filepath) + { + const uint32_t cubemapSize = 2048; + const 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); + PM_CORE_ASSERT(envEquirect->GetFormat() == TextureFormat::Float16, "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); + }); + + return { envFiltered, irradianceMap }; + } + + Ref SceneRenderer::GetFinalColorBuffer() + { + PM_CORE_ASSERT(false, "Not implemented"); + return nullptr; + } + + uint32_t SceneRenderer::GetFinalColorBufferRendererID() + { + return s_Data.CompositePass->GetSpecification().TargetFramebuffer->GetColorAttachmentRendererID(); + } + + void SceneRenderer::FlushDrawList() + { + PM_CORE_ASSERT(!s_Data.ActiveScene); + + GeometryPass(); + CompositePass(); + + s_Data.DrawList.clear(); + s_Data.SceneData = {}; + } + + void SceneRenderer::GeometryPass() + { + Renderer::BeginRenderPass(s_Data.GeoPass); + + auto viewProjection = s_Data.SceneData.SceneCamera.GetProjectionMatrix() * s_Data.SceneData.SceneCamera.GetViewMatrix(); + + // Skybox + auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader(); + s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(viewProjection)); + // s_Data.SceneInfo.EnvironmentIrradianceMap->Bind(0); + Renderer::SubmitFullscreenQuad(s_Data.SceneData.SkyboxMaterial); + + // Render entities + for (auto& dc : s_Data.DrawList) + { + auto baseMaterial = dc.mesh->GetMaterial(); + baseMaterial->Set("u_ViewProjectionMatrix", viewProjection); + baseMaterial->Set("u_CameraPosition", s_Data.SceneData.SceneCamera.GetPosition()); + + // 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); + + auto overrideMaterial = nullptr; // dc.Material; + Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial); + } + + // Grid + s_Data.GridMaterial->Set("u_ViewProjection", viewProjection); + Renderer::SubmitQuad(s_Data.GridMaterial, glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)) * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f))); + + Renderer::EndRenderPass(); + } + + void SceneRenderer::CompositePass() + { + Renderer::BeginRenderPass(s_Data.CompositePass); + s_Data.CompositeShader->Bind(); + s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.GetExposure()); + s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } +} diff --git a/Prism/src/Prism/Renderer/SceneRenderer.h b/Prism/src/Prism/Renderer/SceneRenderer.h new file mode 100644 index 0000000..861d546 --- /dev/null +++ b/Prism/src/Prism/Renderer/SceneRenderer.h @@ -0,0 +1,40 @@ +// +// Created by sfd on 25-11-30. +// + +#ifndef SCENERENDERER_H +#define SCENERENDERER_H +#include "Texture.h" +#include "Prism/Scene/Scene.h" + + +namespace Prism +{ + class PRISM_API SceneRenderer + { + public: + static void Init(); + + static void SetViewportSize(uint32_t width, uint32_t height); + + static void BeginScene(const Scene* scene); + static void EndScene(); + + static void SubmitEntity(Entity* entity); + + static std::pair, Ref> CreateEnvironmentMap(const std::string& filepath); + + static Ref GetFinalColorBuffer(); + + // TODO: Temp + static uint32_t GetFinalColorBufferRendererID(); + + private: + static void FlushDrawList(); + static void GeometryPass(); + static void CompositePass(); + }; +} + + +#endif //SCENERENDERER_H diff --git a/Prism/src/Prism/Renderer/Shader.cpp b/Prism/src/Prism/Renderer/Shader.cpp index 0e15593..7456830 100644 --- a/Prism/src/Prism/Renderer/Shader.cpp +++ b/Prism/src/Prism/Renderer/Shader.cpp @@ -33,7 +33,7 @@ namespace Prism switch (RendererAPI::Current()) { case RendererAPIType::None: return nullptr; - case RendererAPIType::OpenGL: result = OpenGLShader::CreateFromSring(source); + case RendererAPIType::OpenGL: result = CreateFromSring(source); } s_AllShaders.push_back(result); diff --git a/Prism/src/Prism/Renderer/Shader.h b/Prism/src/Prism/Renderer/Shader.h index 73dadf1..f0620d8 100644 --- a/Prism/src/Prism/Renderer/Shader.h +++ b/Prism/src/Prism/Renderer/Shader.h @@ -7,6 +7,7 @@ #include +#include "RendererAPI.h" #include "glm/gtc/type_ptr.hpp" #include "Prism/Core/Buffer.h" #include "ShaderUniform.h" @@ -102,6 +103,8 @@ namespace Prism static Ref Create(const std::string& filepath); static Ref CreateFromSring(const std::string& source); + virtual RendererID GetRendererID() const = 0; + virtual void Reload() = 0; virtual void Bind() = 0; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0; @@ -117,6 +120,8 @@ namespace Prism virtual const ShaderUniformBufferList& GetVSRendererUniforms() const = 0; virtual const ShaderUniformBufferList& GetPSRendererUniforms() const = 0; + virtual bool HasVSMaterialUniformBuffer() const = 0; + virtual bool HasPSMaterialUniformBuffer() const = 0; virtual const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const = 0; virtual const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const = 0; diff --git a/Prism/src/Prism/Renderer/Texture.cpp b/Prism/src/Prism/Renderer/Texture.cpp index 90d3cf4..5feebbd 100644 --- a/Prism/src/Prism/Renderer/Texture.cpp +++ b/Prism/src/Prism/Renderer/Texture.cpp @@ -19,33 +19,52 @@ namespace Prism return 0; } - Texture2D* Texture2D::Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap) + uint32_t Texture::CalculateMipMapCount(const uint32_t width, const uint32_t height) + { + uint32_t levels = 1; + while ((width | height) >> levels) + levels++; + + return levels; + } + + Ref Texture2D::Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap) { switch (RendererAPI::Current()) { case RendererAPIType::None: return nullptr; - case RendererAPIType::OpenGL: return new OpenGLTexture2D(format, width, height, wrap); + case RendererAPIType::OpenGL: return CreateRef(format, width, height, wrap); } return nullptr; } - Texture2D* Texture2D::Create(const std::string& path, bool srgb) + Ref Texture2D::Create(const std::string& path, bool srgb) { switch (RendererAPI::Current()) { case RendererAPIType::None: return nullptr; - case RendererAPIType::OpenGL: return new OpenGLTexture2D(path, srgb); + case RendererAPIType::OpenGL: return CreateRef(path, srgb); } return nullptr; } - TextureCube* TextureCube::Create(const std::string& path) + Ref TextureCube::Create(TextureFormat format, uint32_t width, uint32_t height) { switch (RendererAPI::Current()) { case RendererAPIType::None: return nullptr; - case RendererAPIType::OpenGL: return new OpenGLTextureCube(path); + case RendererAPIType::OpenGL: return CreateRef(format, width, height); + } + return nullptr; + } + + Ref TextureCube::Create(const std::string& path) + { + switch (RendererAPI::Current()) + { + case RendererAPIType::None: return nullptr; + case RendererAPIType::OpenGL: return CreateRef(path); } return nullptr; } diff --git a/Prism/src/Prism/Renderer/Texture.h b/Prism/src/Prism/Renderer/Texture.h index 03e69e2..4d893e5 100644 --- a/Prism/src/Prism/Renderer/Texture.h +++ b/Prism/src/Prism/Renderer/Texture.h @@ -16,6 +16,7 @@ namespace Prism None = 0, RGB = 1, RGBA = 2, + Float16 = 3, }; enum class TextureWrap @@ -32,16 +33,23 @@ namespace Prism virtual void Bind(uint32_t slot = 0) const = 0; - virtual RendererID GetRendererID() const = 0; + virtual TextureFormat GetFormat() const = 0; + + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; + virtual uint32_t GetMipLevelCount() const = 0; + + virtual RendererID GetRendererID() const = 0; static uint32_t GetBPP(TextureFormat format); + static uint32_t CalculateMipMapCount(uint32_t width, uint32_t height); }; class PRISM_API Texture2D : public Texture { public: - static Texture2D* Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap = TextureWrap::Clamp); - static Texture2D* Create(const std::string& path, bool srgb = false); + static Ref Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap = TextureWrap::Clamp); + static Ref Create(const std::string& path, bool srgb = false); virtual TextureFormat GetFormat() const = 0; @@ -53,17 +61,15 @@ namespace Prism virtual void Resize(uint32_t width, uint32_t height) = 0; virtual Buffer GetWriteableBuffer() = 0; + virtual bool Loaded() const = 0; virtual const std::string& GetPath() const = 0; }; class PRISM_API TextureCube : public Texture { public: - static TextureCube* Create(const std::string& path); - - virtual TextureFormat GetFormat() const = 0; - virtual uint32_t GetWidth() const = 0; - virtual uint32_t GetHeight() const = 0; + static Ref Create(TextureFormat format, uint32_t width, uint32_t height); + static Ref Create(const std::string& path); virtual const std::string& GetPath() const = 0; }; diff --git a/Prism/src/Prism/Scene/Entity.cpp b/Prism/src/Prism/Scene/Entity.cpp new file mode 100644 index 0000000..976c50f --- /dev/null +++ b/Prism/src/Prism/Scene/Entity.cpp @@ -0,0 +1,17 @@ +// +// Created by sfd on 25-11-30. +// + +#include "Entity.h" + +namespace Prism +{ + Entity::Entity() + : m_Transform(glm::mat4(1.0f)) + { + } + + Entity::~Entity() + { + } +} diff --git a/Prism/src/Prism/Scene/Entity.h b/Prism/src/Prism/Scene/Entity.h new file mode 100644 index 0000000..a357cb6 --- /dev/null +++ b/Prism/src/Prism/Scene/Entity.h @@ -0,0 +1,38 @@ +// +// Created by sfd on 25-11-30. +// + +#ifndef ENTITY_H +#define ENTITY_H +#include "glm/glm.hpp" +#include "Prism/Renderer/Mesh.h" + + +namespace Prism +{ + class PRISM_API Entity + { + public: + Entity(); + ~Entity(); + + // TODO: Move to Component + void SetMesh(const Ref& mesh) { m_Mesh = mesh; } + Ref GetMesh() { return m_Mesh; } + + void SetMaterial(const Ref& material) { m_Material = material; } + Ref GetMaterial() { return m_Material; } + + const glm::mat4& GetTransform() const { return m_Transform; } + glm::mat4& Transform() { return m_Transform; } + private: + glm::mat4 m_Transform; + + // TODO: Temp + Ref m_Mesh; + Ref m_Material; + }; +} + + +#endif //ENTITY_H diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp new file mode 100644 index 0000000..bc020aa --- /dev/null +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -0,0 +1,90 @@ +// +// Created by sfd on 25-11-30. +// + +#include "Scene.h" + +#include "Prism/Renderer/SceneRenderer.h" + +namespace Prism +{ + Environment Environment::Load(const std::string& filepath) + { + auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(filepath); + return { radiance, irradiance }; + } + + Scene::Scene(const std::string& debugName) + : m_DebugName(debugName) + { + Init(); + } + + Scene::~Scene() + { + for (const Entity* entity : m_Entities) + delete entity; + } + + void Scene::Init() + { + auto skyboxShader = Shader::Create("assets/shaders/Skybox.glsl"); + m_SkyboxMaterial = MaterialInstance::Create(Material::Create(skyboxShader)); + m_SkyboxMaterial->SetFlag(MaterialFlag::DepthTest, false); + } + + void Scene::OnUpdate(TimeStep ts) + { + m_Camera.Update(ts); + + m_SkyboxMaterial->Set("u_TextureLod", m_SkyboxLod); + + // Update all entities + for (auto entity : m_Entities) + { + auto mesh = entity->GetMesh(); + if (mesh) + mesh->OnUpdate(ts); + } + + SceneRenderer::BeginScene(this); + + // Render entities + for (auto entity : m_Entities) + { + // TODO: Should we render (logically) + SceneRenderer::SubmitEntity(entity); + } + + SceneRenderer::EndScene(); + } + + void Scene::SetCamera(const Camera& camera) + { + m_Camera = camera; + } + + void Scene::SetEnvironment(const Environment& environment) + { + m_Environment = environment; + SetSkybox(environment.RadianceMap); + } + + void Scene::SetSkybox(const Ref& skybox) + { + m_SkyboxTexture = skybox; + m_SkyboxMaterial->Set("u_Texture", skybox); + } + + void Scene::AddEntity(Entity* entity) + { + m_Entities.push_back(entity); + } + + Entity* Scene::CreateEntity() + { + const auto entity = new Entity(); + AddEntity(entity); + return entity; + } +} diff --git a/Prism/src/Prism/Scene/Scene.h b/Prism/src/Prism/Scene/Scene.h new file mode 100644 index 0000000..bfe747b --- /dev/null +++ b/Prism/src/Prism/Scene/Scene.h @@ -0,0 +1,60 @@ +// +// Created by sfd on 25-11-30. +// + +#ifndef SCENE_H +#define SCENE_H +#include "Entity.h" +#include "Prism/Renderer/Camera.h" +#include "Prism/Renderer/Material.h" + + +namespace Prism +{ + + struct PRISM_API Environment + { + Ref RadianceMap; + Ref IrradianceMap; + + static Environment Load(const std::string& filepath); + }; + + class PRISM_API Scene{ + public: + Scene(const std::string& debugName = "Scene"); + ~Scene(); + + void Init(); + + void OnUpdate(TimeStep ts); + + void SetCamera(const Camera& camera); + Camera& GetCamera() { return m_Camera; } + + void SetEnvironment(const Environment& environment); + void SetSkybox(const Ref& skybox); + + float& GetSkyboxLod() { return m_SkyboxLod; } + + void AddEntity(Entity* entity); + Entity* CreateEntity(); + + private: + std::string m_DebugName; + std::vector m_Entities; + Camera m_Camera; + + Environment m_Environment; + Ref m_SkyboxTexture; + Ref m_SkyboxMaterial; + + float m_SkyboxLod = 1.0f; + + friend class SceneRenderer; + friend class SceneHierarchyPanel; + }; +} + + +#endif //SCENE_H