aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2025-08-22 12:24:05 +0200
committerhachem <im@hachem.wtf>2025-08-22 12:24:05 +0200
commit0d819f2b9a71e862bce8799c6b0c1304e62219b3 (patch)
treea3011c8f5b04c98de5a8a24bc2b4ded312de958e /src
parentc20a400e620154f0c334fe0a37d547625b6ee65b (diff)
[add]: add HDRI
Diffstat (limited to 'src')
-rw-r--r--src/Core/HDRIManager.cpp64
-rw-r--r--src/Core/HDRIManager.h46
-rw-r--r--src/Engine/Engine.cpp24
-rw-r--r--src/Engine/Engine.h28
-rw-r--r--src/Platform/OpenGL/OpenGLFramebuffer.cpp1
-rw-r--r--src/Platform/OpenGL/OpenGLShader.h1
-rw-r--r--src/Platform/OpenGL/OpenGLTexture.cpp181
-rw-r--r--src/Platform/OpenGL/OpenGLTexture.h31
-rw-r--r--src/Platform/Vulkan/VulkanShader.h1
-rw-r--r--src/Platform/Vulkan/VulkanTexture.cpp49
-rw-r--r--src/Platform/Vulkan/VulkanTexture.h27
-rw-r--r--src/Rendering/Shader.h1
-rw-r--r--src/Rendering/Texture.cpp30
-rw-r--r--src/Rendering/Texture.h8
-rw-r--r--src/States/ConfigState.h1
-rw-r--r--src/States/SimulationState.cpp39
-rw-r--r--src/States/WorldBuilderState.cpp173
-rw-r--r--src/States/WorldBuilderState.h10
18 files changed, 678 insertions, 37 deletions
diff --git a/src/Core/HDRIManager.cpp b/src/Core/HDRIManager.cpp
new file mode 100644
index 0000000..6e420a6
--- /dev/null
+++ b/src/Core/HDRIManager.cpp
@@ -0,0 +1,64 @@
+#include "HDRIManager.h"
+#include <filesystem>
+
+namespace Donut
+{
+ Ref<CubemapTexture> HDRIManager::LoadHDRI(const std::string& path)
+ {
+ auto it = m_HDRICache.find(path);
+ if (it != m_HDRICache.end())
+ {
+ DONUT_INFO("HDRI already cached: {}", path);
+ return it->second;
+ }
+
+ DONUT_INFO("Loading HDRI: {}", path);
+ auto hdri = CubemapTexture::CreateFromHDRI(path);
+
+ if (hdri)
+ {
+ m_HDRICache[path] = hdri;
+ DONUT_INFO("Successfully loaded and cached HDRI: {}", path);
+ }
+ else
+ DONUT_ERROR("Failed to load HDRI: {}", path);
+
+ return hdri;
+ }
+
+ void HDRIManager::SetCurrentHDRI(const std::string& path)
+ {
+ auto hdri = LoadHDRI(path);
+ if (hdri)
+ {
+ m_CurrentHDRI = hdri;
+ DONUT_INFO("Set current HDRI to: {}", path);
+ }
+ else
+ DONUT_ERROR("Failed to set current HDRI: {}", path);
+ }
+
+ std::string HDRIManager::GetHDRIName(const std::string& path) const
+ {
+ std::filesystem::path filePath(path);
+ std::string filename = filePath.stem().string();
+ std::string name = filename;
+
+ for (size_t i = 0; i < name.length(); ++i)
+ {
+ if (name[i] == '_')
+ name[i] = ' ';
+ else if (i == 0 || name[i-1] == ' ')
+ name[i] = std::toupper(name[i]);
+ }
+
+ return name;
+ }
+
+ void HDRIManager::ClearCache()
+ {
+ m_HDRICache.clear();
+ m_CurrentHDRI.reset();
+ DONUT_INFO("HDRI cache cleared");
+ }
+};
diff --git a/src/Core/HDRIManager.h b/src/Core/HDRIManager.h
new file mode 100644
index 0000000..ebefb3a
--- /dev/null
+++ b/src/Core/HDRIManager.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "Core/Log.h"
+#include "Rendering/Texture.h"
+
+#include <string>
+#include <unordered_map>
+#include <memory>
+
+namespace Donut
+{
+ class HDRIManager
+ {
+ public:
+ static HDRIManager& Get()
+ {
+ static HDRIManager instance;
+ return instance;
+ }
+
+ Ref<CubemapTexture> LoadHDRI(const std::string& path);
+ Ref<CubemapTexture> GetCurrentHDRI() const { return m_CurrentHDRI; }
+
+ void SetCurrentHDRI(const std::string& path);
+ const std::vector<std::string>& GetAvailableHDRI() const { return m_AvailableHDRI; }
+ std::string GetHDRIName(const std::string& path) const;
+ void ClearCache();
+ private:
+ HDRIManager() = default;
+ ~HDRIManager() = default;
+
+ HDRIManager(const HDRIManager&) = delete;
+ HDRIManager& operator=(const HDRIManager&) = delete;
+
+ std::unordered_map<std::string, Ref<CubemapTexture>> m_HDRICache;
+
+ Ref<CubemapTexture> m_CurrentHDRI;
+
+ std::vector<std::string> m_AvailableHDRI =
+ {
+ "Assets/HDRI/HDR_blue_nebulae-1.hdr",
+ "Assets/HDRI/HDR_subdued_blue_nebulae.hdr",
+ "Assets/HDRI/HDR_subdued_multi_nebulae.hdr"
+ };
+ };
+};
diff --git a/src/Engine/Engine.cpp b/src/Engine/Engine.cpp
index 20a295f..a947357 100644
--- a/src/Engine/Engine.cpp
+++ b/src/Engine/Engine.cpp
@@ -10,6 +10,7 @@
#include "Engine.h"
#include "Core/Log.h"
+#include "Core/HDRIManager.h"
#include "Rendering/VertexBuffer.h"
#include "Rendering/IndexBuffer.h"
@@ -36,15 +37,25 @@ namespace Donut
m_ComputeProgram = CreateComputeProgram("Assets/Shaders/Geodesic.glsl");
m_BlurShader = Ref<Shader>(Shader::Create("Assets/Shaders/Blur.glsl"));
+ auto& hdriManager = HDRIManager::Get();
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+ if (!m_HDRIEnvironment)
+ {
+ hdriManager.SetCurrentHDRI("Assets/HDRI/HDR_blue_nebulae-1.hdr");
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+ if (!m_HDRIEnvironment)
+ DONUT_WARN("Failed to load default HDRI, using fallback");
+ }
+
m_CameraUBO = UniformBuffer::Create(128, 1);
- m_DiskUBO = UniformBuffer::Create(sizeof(float) * 5, 2); // Added density parameter
+ m_DiskUBO = UniformBuffer::Create(sizeof(float) * 5, 2);
uint32_t objUBOSize = sizeof(int) + 3 * sizeof(float)
+ 16 * (sizeof(glm::vec4) + sizeof(glm::vec4))
+ 16 * sizeof(float);
m_ObjectsUBO = UniformBuffer::Create(objUBOSize, 3);
- m_SimulationUBO = UniformBuffer::Create(sizeof(int) * 2 + sizeof(float) * 2, 4); // time added to existing float
+ m_SimulationUBO = UniformBuffer::Create(sizeof(int) * 2 + sizeof(float) * 2, 4);
auto result = QuadVAO();
m_QuadVAO = result.first;
@@ -117,6 +128,9 @@ namespace Donut
void Engine::DispatchCompute(const Camera& cam)
{
+ auto& hdriManager = HDRIManager::Get();
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+
int cw = GetComputeWidth();
int ch = m_ComputeHeight;
@@ -128,7 +142,10 @@ namespace Donut
UploadObjectsUBO(m_Objects);
UploadSimulationUBO();
m_Texture->BindAsImage(0, false);
-
+
+ if (m_HDRIEnvironment)
+ m_HDRIEnvironment->Bind(5);
+
uint32_t groupsX = static_cast<uint32_t>(std::ceil(cw / 16.0f));
uint32_t groupsY = static_cast<uint32_t>(std::ceil(ch / 16.0f));
m_ComputeProgram->Dispatch(groupsX, groupsY, 1);
@@ -509,4 +526,5 @@ namespace Donut
RenderCommand::SetViewport(0, 0, originalWidth, originalHeight);
}
+
}
diff --git a/src/Engine/Engine.h b/src/Engine/Engine.h
index 83f0b19..c3cfdd0 100644
--- a/src/Engine/Engine.h
+++ b/src/Engine/Engine.h
@@ -101,30 +101,34 @@ namespace Donut
void SetMaxStepsStatic(int steps) { m_MaxStepsStatic = steps; }
void SetEarlyExitDistance(float distance) { m_EarlyExitDistance = distance; }
- float GetDiskThickness() const { return m_DiskThickness; }
+ float GetDiskThickness() const { return m_DiskThickness; }
void SetDiskThickness(float thickness) { m_DiskThickness = thickness; }
- float GetDiskDensity() const { return m_DiskDensity; }
- void SetDiskDensity(float density) { m_DiskDensity = density; }
+ float GetDiskDensity() const { return m_DiskDensity; }
+ void SetDiskDensity(float density) { m_DiskDensity = density; }
- float GetRotationSpeed() const { return m_RotationSpeed; }
- void SetRotationSpeed(float speed) { m_RotationSpeed = speed; }
+ float GetRotationSpeed() const { return m_RotationSpeed; }
+ void SetRotationSpeed(float speed) { m_RotationSpeed = speed; }
- float GetBlurStrength() const { return m_BlurStrength; }
- void SetBlurStrength(float strength) { m_BlurStrength = strength; }
+ float GetBlurStrength() const { return m_BlurStrength; }
+ void SetBlurStrength(float strength) { m_BlurStrength = strength; }
- float GetGlowIntensity() const { return m_GlowIntensity; }
+ float GetGlowIntensity() const { return m_GlowIntensity; }
void SetGlowIntensity(float intensity) { m_GlowIntensity = intensity; }
void LoadObjectsFromScene(const std::vector<Donut::Object>& objects);
void ExportHighResFrame(const std::string& filename, int width = 4096, int height = 3072);
void PrintObjectInfo() const;
+
+ void SetHDRIEnvironment(Ref<CubemapTexture> hdri) { m_HDRIEnvironment = hdri; }
+ Ref<CubemapTexture> GetHDRIEnvironment() const { return m_HDRIEnvironment; }
private:
Ref<Shader> CreateComputeProgram(const char* path);
std::pair<Ref<VertexArray>, Ref<Texture2D>> QuadVAO();
private:
Ref<VertexArray> m_QuadVAO;
Ref<Texture2D> m_Texture;
+ Ref<CubemapTexture> m_HDRIEnvironment;
Ref<Shader> m_ShaderProgram;
Ref<Shader> m_ComputeProgram;
Ref<Shader> m_BlurShader;
@@ -135,8 +139,8 @@ namespace Donut
int m_Width;
int m_Height;
- float m_Width_f = 100*10e10f;
- float m_Height_f = 75*10e10f;
+ float m_Width_f = 100.0f*1e10f;
+ float m_Height_f = 75.0f*1e10f;
int m_TargetFPS = 60;
float m_CurrentFPS = 60.0f;
@@ -150,9 +154,9 @@ namespace Donut
int m_MaxStepsMoving = 60000;
int m_MaxStepsStatic = 30000;
- float m_EarlyExitDistance = 5e11f;
+ float m_EarlyExitDistance = 5.0e11f;
- float m_DiskThickness = 0.1;
+ float m_DiskThickness = 0.1f;
float m_DiskDensity = 0.1f;
float m_RotationSpeed = 1.0f;
float m_BlurStrength = 2.0f;
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.cpp b/src/Platform/OpenGL/OpenGLFramebuffer.cpp
index 523d748..3bb62f6 100644
--- a/src/Platform/OpenGL/OpenGLFramebuffer.cpp
+++ b/src/Platform/OpenGL/OpenGLFramebuffer.cpp
@@ -123,7 +123,6 @@ namespace Donut
bool multisample = m_Specification.Samples > 1;
- // Attachments
if (m_ColorAttachmentSpecifications.size())
{
m_ColorAttachments.resize(m_ColorAttachmentSpecifications.size());
diff --git a/src/Platform/OpenGL/OpenGLShader.h b/src/Platform/OpenGL/OpenGLShader.h
index e9a7dc2..99a150b 100644
--- a/src/Platform/OpenGL/OpenGLShader.h
+++ b/src/Platform/OpenGL/OpenGLShader.h
@@ -32,6 +32,7 @@ namespace Donut
virtual void MemoryBarrier(uint32_t barriers) override;
virtual const std::string& GetName() const override { return m_Name; }
+ virtual uint32_t GetRendererID() const override { return m_RendererID; }
void UploadUniformInt( const std::string& name, int value);
void UploadUniformIntArray(const std::string& name, int* values, uint32_t count);
diff --git a/src/Platform/OpenGL/OpenGLTexture.cpp b/src/Platform/OpenGL/OpenGLTexture.cpp
index e12dea4..d9033dc 100644
--- a/src/Platform/OpenGL/OpenGLTexture.cpp
+++ b/src/Platform/OpenGL/OpenGLTexture.cpp
@@ -1,5 +1,13 @@
#include "OpenGLTexture.h"
+#include "Rendering/Shader.h"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
namespace Donut
{
OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height)
@@ -18,7 +26,6 @@ namespace Donut
glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
- // TODO(Hachem): Texture Loading
OpenGLTexture2D::OpenGLTexture2D(const std::string& path)
: m_Path(path)
{
@@ -69,4 +76,176 @@ namespace Donut
GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY;
glBindImageTexture(slot, m_RendererID, 0, GL_FALSE, 0, access, m_InternalFormat);
}
+
+ OpenGLCubemapTexture::OpenGLCubemapTexture(uint32_t width, uint32_t height)
+ : m_Width(width), m_Height(height)
+ {
+ m_InternalFormat = GL_RGB16F;
+ m_DataFormat = GL_RGB;
+
+ glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &m_RendererID);
+ glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height);
+
+ glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ }
+
+ OpenGLCubemapTexture::OpenGLCubemapTexture(const std::string& path)
+ : m_Path(path)
+ {
+ m_Width = 1024;
+ m_Height = 1024;
+ m_InternalFormat = GL_RGB16F;
+ m_DataFormat = GL_RGB;
+
+ glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &m_RendererID);
+ glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height);
+
+ glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+ LoadHDRI(path);
+ }
+
+ OpenGLCubemapTexture::~OpenGLCubemapTexture()
+ {
+ glDeleteTextures(1, &m_RendererID);
+ }
+
+ void OpenGLCubemapTexture::LoadHDRI(const std::string& path)
+ {
+ stbi_set_flip_vertically_on_load(true);
+ int width, height, channels;
+ float* hdrData = stbi_loadf(path.c_str(), &width, &height, &channels, 3);
+
+ if (!hdrData)
+ {
+ DONUT_ERROR("Failed to load HDRI: {}", path);
+ float defaultSky[6 * 4] =
+ {
+ 0.5f, 0.7f, 1.0f, 1.0f, // Right
+ 0.5f, 0.7f, 1.0f, 1.0f, // Left
+ 0.5f, 0.7f, 1.0f, 1.0f, // Top
+ 0.5f, 0.7f, 1.0f, 1.0f, // Bottom
+ 0.5f, 0.7f, 1.0f, 1.0f, // Front
+ 0.5f, 0.7f, 1.0f, 1.0f // Back
+ };
+
+ for (int i = 0; i < 6; ++i)
+ glTextureSubImage3D(m_RendererID, 0, 0, 0, i, 1, 1, 1, GL_RGBA, GL_FLOAT, &defaultSky[i * 4]);
+ return;
+ }
+
+ ConvertEquirectangularToCubemap(hdrData, width, height);
+ stbi_image_free(hdrData);
+
+ DONUT_INFO("Successfully loaded HDRI: {} ({}x{})", path, width, height);
+ }
+
+ void OpenGLCubemapTexture::ConvertEquirectangularToCubemap(float* hdrData, int width, int height)
+ {
+ uint32_t captureFBO, captureRBO;
+ glGenFramebuffers(1, &captureFBO);
+ glGenRenderbuffers(1, &captureRBO);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+ glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_Width, m_Height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);
+
+ uint32_t hdrTexture;
+ glGenTextures(1, &hdrTexture);
+ glBindTexture(GL_TEXTURE_2D, hdrTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdrData);
+ 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_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ auto equirectShader = Shader::Create("Assets/Shaders/EquirectToCubemap.glsl");
+ if (!equirectShader)
+ {
+ DONUT_ERROR("Failed to create equirectangular to cubemap shader");
+ return;
+ }
+
+ uint32_t shaderProgram = equirectShader->GetRendererID();
+
+ float vertices[] =
+ {
+ -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
+ 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
+ -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f
+ };
+
+ uint32_t cubeVAO, cubeVBO;
+ glGenVertexArrays(1, &cubeVAO);
+ glGenBuffers(1, &cubeVBO);
+ glBindVertexArray(cubeVAO);
+ glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
+
+ glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
+ glm::mat4 captureViews[] =
+ {
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
+ };
+
+ glUseProgram(shaderProgram);
+ glUniform1i(glGetUniformLocation(shaderProgram, "equirectangularMap"), 0);
+ glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, &captureProjection[0][0]);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, hdrTexture);
+
+ glViewport(0, 0, m_Width, m_Height);
+ glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+ for (unsigned int i = 0; i < 6; ++i)
+ {
+ glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, &captureViews[i][0][0]);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_RendererID, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glBindVertexArray(cubeVAO);
+ glDrawArrays(GL_TRIANGLES, 0, 36);
+ }
+ glBindVertexArray(0);
+
+ glDeleteVertexArrays(1, &cubeVAO);
+ glDeleteBuffers(1, &cubeVBO);
+ glDeleteProgram(shaderProgram);
+ glDeleteTextures(1, &hdrTexture);
+ glDeleteFramebuffers(1, &captureFBO);
+ glDeleteRenderbuffers(1, &captureRBO);
+ }
+
+ void OpenGLCubemapTexture::SetData(void* data, uint32_t size)
+ {
+ DONUT_WARN("SetData not implemented for cubemaps");
+ }
+
+ void OpenGLCubemapTexture::Bind(uint32_t slot) const
+ {
+ glBindTextureUnit(slot, m_RendererID);
+ }
+
+ void OpenGLCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const
+ {
+ GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY;
+ glBindImageTexture(slot, m_RendererID, 0, GL_TRUE, 0, access, m_InternalFormat);
+ }
}; \ No newline at end of file
diff --git a/src/Platform/OpenGL/OpenGLTexture.h b/src/Platform/OpenGL/OpenGLTexture.h
index 1c3cb05..220df09 100644
--- a/src/Platform/OpenGL/OpenGLTexture.h
+++ b/src/Platform/OpenGL/OpenGLTexture.h
@@ -34,4 +34,35 @@ namespace Donut
uint32_t m_RendererID;
GLenum m_InternalFormat, m_DataFormat;
};
+
+ class OpenGLCubemapTexture
+ : public CubemapTexture
+ {
+ public:
+ OpenGLCubemapTexture(uint32_t width, uint32_t height);
+ OpenGLCubemapTexture(const std::string& path);
+ virtual ~OpenGLCubemapTexture();
+
+ virtual uint32_t GetWidth() const override { return m_Width; }
+ virtual uint32_t GetHeight() const override { return m_Height; }
+ virtual uint32_t GetRendererID() const override { return m_RendererID; }
+
+ virtual void SetData(void* data, uint32_t size) override;
+ virtual void Bind(uint32_t slot = 0) const override;
+ virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
+
+ virtual bool operator==(const Texture& other) const override
+ {
+ return m_RendererID == other.GetRendererID();
+ }
+
+ private:
+ void LoadHDRI(const std::string& path);
+ void ConvertEquirectangularToCubemap(float* hdrData, int width, int height);
+
+ std::string m_Path;
+ uint32_t m_Width, m_Height;
+ uint32_t m_RendererID;
+ GLenum m_InternalFormat, m_DataFormat;
+ };
}
diff --git a/src/Platform/Vulkan/VulkanShader.h b/src/Platform/Vulkan/VulkanShader.h
index 2b75331..138a742 100644
--- a/src/Platform/Vulkan/VulkanShader.h
+++ b/src/Platform/Vulkan/VulkanShader.h
@@ -32,6 +32,7 @@ namespace Donut
virtual void MemoryBarrier(uint32_t barriers) override;
virtual const std::string& GetName() const override { return m_Name; }
+ virtual uint32_t GetRendererID() const override { return m_RendererID; }
void UploadUniformInt(const std::string& name, int value);
void UploadUniformIntArray(const std::string& name, int* values, uint32_t count);
diff --git a/src/Platform/Vulkan/VulkanTexture.cpp b/src/Platform/Vulkan/VulkanTexture.cpp
index 623e540..4145130 100644
--- a/src/Platform/Vulkan/VulkanTexture.cpp
+++ b/src/Platform/Vulkan/VulkanTexture.cpp
@@ -5,32 +5,69 @@ namespace Donut
VulkanTexture2D::VulkanTexture2D(uint32_t width, uint32_t height)
: m_Width(width), m_Height(height)
{
- // TODO(Hachem): Implement Vulkan texture creation with dimensions
+ m_InternalFormat = 0;
+ m_DataFormat = 0;
+ m_RendererID = 0;
}
VulkanTexture2D::VulkanTexture2D(const std::string& path)
: m_Path(path)
{
- // TODO(Hachem): Implement Vulkan texture creation from file
+ m_Width = 1;
+ m_Height = 1;
+ m_InternalFormat = 0;
+ m_DataFormat = 0;
+ m_RendererID = 0;
}
VulkanTexture2D::~VulkanTexture2D()
{
- // TODO(Hachem): Implement Vulkan texture cleanup
}
void VulkanTexture2D::SetData(void* data, uint32_t size)
{
- // TODO(Hachem): Implement Vulkan texture data setting
}
void VulkanTexture2D::Bind(uint32_t slot) const
{
- // TODO(Hachem): Implement Vulkan texture binding
}
void VulkanTexture2D::BindAsImage(uint32_t slot, bool readOnly) const
{
- // TODO(Hachem): Implement Vulkan texture image binding
+ }
+
+ // Vulkan Cubemap Implementation (Placeholder)
+ VulkanCubemapTexture::VulkanCubemapTexture(uint32_t width, uint32_t height)
+ : m_Width(width), m_Height(height)
+ {
+ m_InternalFormat = 0;
+ m_DataFormat = 0;
+ m_RendererID = 0;
+ }
+
+ VulkanCubemapTexture::VulkanCubemapTexture(const std::string& path)
+ : m_Path(path)
+ {
+ m_Width = 1024;
+ m_Height = 1024;
+ m_InternalFormat = 0;
+ m_DataFormat = 0;
+ m_RendererID = 0;
+ }
+
+ VulkanCubemapTexture::~VulkanCubemapTexture()
+ {
+ }
+
+ void VulkanCubemapTexture::SetData(void* data, uint32_t size)
+ {
+ }
+
+ void VulkanCubemapTexture::Bind(uint32_t slot) const
+ {
+ }
+
+ void VulkanCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const
+ {
}
};
diff --git a/src/Platform/Vulkan/VulkanTexture.h b/src/Platform/Vulkan/VulkanTexture.h
index ad711f9..dd9e1df 100644
--- a/src/Platform/Vulkan/VulkanTexture.h
+++ b/src/Platform/Vulkan/VulkanTexture.h
@@ -30,4 +30,31 @@ namespace Donut
uint32_t m_RendererID;
uint32_t m_InternalFormat, m_DataFormat;
};
+
+ class VulkanCubemapTexture
+ : public CubemapTexture
+ {
+ public:
+ VulkanCubemapTexture(uint32_t width, uint32_t height);
+ VulkanCubemapTexture(const std::string& path);
+ virtual ~VulkanCubemapTexture();
+
+ virtual uint32_t GetWidth() const override { return m_Width; }
+ virtual uint32_t GetHeight() const override { return m_Height; }
+ virtual uint32_t GetRendererID() const override { return m_RendererID; }
+
+ virtual void SetData(void* data, uint32_t size) override;
+ virtual void Bind(uint32_t slot = 0) const override;
+ virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
+
+ virtual bool operator==(const Texture& other) const override
+ {
+ return m_RendererID == ((VulkanCubemapTexture&)other).m_RendererID;
+ }
+ private:
+ std::string m_Path;
+ uint32_t m_Width, m_Height;
+ uint32_t m_RendererID;
+ uint32_t m_InternalFormat, m_DataFormat;
+ };
};
diff --git a/src/Rendering/Shader.h b/src/Rendering/Shader.h
index e2d8d2a..483102d 100644
--- a/src/Rendering/Shader.h
+++ b/src/Rendering/Shader.h
@@ -34,6 +34,7 @@ namespace Donut
virtual void MemoryBarrier(uint32_t barriers) = 0;
virtual const std::string& GetName() const = 0;
+ virtual uint32_t GetRendererID() const = 0;
static Shader* Create(const std::string& filepath);
static Shader* Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
diff --git a/src/Rendering/Texture.cpp b/src/Rendering/Texture.cpp
index a96b816..93b8a75 100644
--- a/src/Rendering/Texture.cpp
+++ b/src/Rendering/Texture.cpp
@@ -35,4 +35,34 @@ namespace Donut
return nullptr;
}
}
+
+ Ref<CubemapTexture> CubemapTexture::Create(uint32_t width, uint32_t height)
+ {
+ switch (Renderer::GetAPI())
+ {
+ case RendererAPI::API::OpenGL:
+ return CreateRef<OpenGLCubemapTexture>(width, height);
+ case RendererAPI::API::Vulkan:
+ return CreateRef<VulkanCubemapTexture>(width, height);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
+
+ Ref<CubemapTexture> CubemapTexture::CreateFromHDRI(const std::string& path)
+ {
+ switch (Renderer::GetAPI())
+ {
+ case RendererAPI::API::OpenGL:
+ return CreateRef<OpenGLCubemapTexture>(path);
+ case RendererAPI::API::Vulkan:
+ return CreateRef<VulkanCubemapTexture>(path);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
}; \ No newline at end of file
diff --git a/src/Rendering/Texture.h b/src/Rendering/Texture.h
index 21941ee..3e53b2d 100644
--- a/src/Rendering/Texture.h
+++ b/src/Rendering/Texture.h
@@ -31,4 +31,12 @@ namespace Donut
static Ref<Texture2D> Create(uint32_t width, uint32_t height);
static Ref<Texture2D> Create(const std::string& path);
};
+
+ class CubemapTexture
+ : public Texture
+ {
+ public:
+ static Ref<CubemapTexture> Create(uint32_t width, uint32_t height);
+ static Ref<CubemapTexture> CreateFromHDRI(const std::string& path);
+ };
}; \ No newline at end of file
diff --git a/src/States/ConfigState.h b/src/States/ConfigState.h
index baee8fa..ad7e238 100644
--- a/src/States/ConfigState.h
+++ b/src/States/ConfigState.h
@@ -42,7 +42,6 @@ namespace Donut
bool m_ShowDebugInfo = false;
bool m_EnableAntiAliasing = true;
- // Theme settings
int m_SelectedTheme = 0; // 0=Dark, 1=Light, 2=Blue
};
}; \ No newline at end of file
diff --git a/src/States/SimulationState.cpp b/src/States/SimulationState.cpp
index 4d388fd..12c9397 100644
--- a/src/States/SimulationState.cpp
+++ b/src/States/SimulationState.cpp
@@ -1,6 +1,7 @@
#include "SimulationState.h"
#include "Rendering/Renderer.h"
#include "Core/Application.h"
+#include "Core/HDRIManager.h"
#include "Core/Window.h"
#include "Core/Event.h"
#include "Core/SettingsManager.h"
@@ -10,6 +11,7 @@
#include <chrono>
#include <iomanip>
#include <sstream>
+#include <vector>
namespace Donut
{
@@ -27,7 +29,6 @@ namespace Donut
engine.SetEarlyExitDistance(settings.simulation.earlyExitDistance);
engine.GetGravity() = settings.simulation.gravityEnabled;
- // Load accretion disk settings
engine.SetDiskThickness(settings.simulation.diskThickness);
engine.SetDiskDensity(settings.simulation.diskDensity);
engine.SetRotationSpeed(settings.simulation.rotationSpeed);
@@ -166,9 +167,7 @@ namespace Donut
ImGui::Text("Objects: %zu", engine.GetObjects().size());
if (ImGui::Button("Print Object Info"))
- {
engine.PrintObjectInfo();
- }
int computeHeight = engine.GetComputeHeight();
if (ImGui::SliderInt("Compute Height", &computeHeight, 64, 2048))
@@ -282,6 +281,40 @@ namespace Donut
ImGui::Spacing();
+ ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "HDRI Environment");
+ ImGui::Separator();
+
+ static int selectedHDRI = 0;
+ ImGui::PushID("SimulationHDRI");
+ auto& hdriManager = HDRIManager::Get();
+ const auto& availableHDRI = hdriManager.GetAvailableHDRI();
+
+ static std::vector<std::string> hdriOptionNames;
+ static std::vector<const char*> hdriOptions;
+
+ if (hdriOptionNames.size() != availableHDRI.size())
+ {
+ hdriOptionNames.clear();
+ hdriOptions.clear();
+
+ for (const auto& path : availableHDRI)
+ {
+ hdriOptionNames.push_back(hdriManager.GetHDRIName(path));
+ hdriOptions.push_back(hdriOptionNames.back().c_str());
+ }
+ }
+
+ if (ImGui::Combo("HDRI Environment", &selectedHDRI, hdriOptions.data(), static_cast<int>(hdriOptions.size())))
+ {
+ auto& hdriManager = HDRIManager::Get();
+ hdriManager.SetCurrentHDRI(availableHDRI[selectedHDRI]);
+ engine.SetHDRIEnvironment(hdriManager.GetCurrentHDRI());
+ }
+ ImGui::TextDisabled("HDRI provides background and lighting for the simulation");
+ ImGui::PopID();
+
+ ImGui::Spacing();
+
ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Camera");
ImGui::Separator();
diff --git a/src/States/WorldBuilderState.cpp b/src/States/WorldBuilderState.cpp
index 70ef3d4..5a27ea1 100644
--- a/src/States/WorldBuilderState.cpp
+++ b/src/States/WorldBuilderState.cpp
@@ -2,6 +2,7 @@
#include "Core/Application.h"
#include "Core/Window.h"
+#include "Core/HDRIManager.h"
#include "Rendering/Renderer.h"
#include "Rendering/Shader.h"
@@ -23,6 +24,7 @@
#include <fstream>
#include <sstream>
#include <limits>
+#include <vector>
namespace Donut
{
@@ -42,13 +44,29 @@ namespace Donut
m_Camera.SetElevation(static_cast<float>(std::numbers::pi) / 3.0f);
m_Camera.UpdateOrbital();
- // Initialize rendering resources once
m_SphereShader = Ref<Shader>(Shader::Create("Assets/Shaders/Sphere.glsl"));
+ m_SkyboxShader = Ref<Shader>(Shader::Create("Assets/Shaders/Skybox.glsl"));
+
+ if (!m_SphereShader)
+ DONUT_ERROR("Failed to create sphere shader");
+ if (!m_SkyboxShader)
+ DONUT_ERROR("Failed to create skybox shader");
+
InitializeSphereGeometry();
+ InitializeSkyboxGeometry();
+
+ auto& hdriManager = HDRIManager::Get();
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+ if (!m_HDRIEnvironment)
+ {
+ hdriManager.SetCurrentHDRI("Assets/HDRI/HDR_blue_nebulae-1.hdr");
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+ if (!m_HDRIEnvironment)
+ DONUT_WARN("Failed to load default HDRI for WorldBuilder, using fallback");
+ }
- // Initialize the black hole at the center
- Material blackHoleMaterial(glm::vec3(0.0f, 0.0f, 0.0f), 0.0f, 0.0f); // Pure black, no specular, no emission
- m_BlackHole = Object(glm::vec3(0.0f, 0.0f, 0.0f), 2.0f, blackHoleMaterial); // Position at origin, radius 2
+ Material blackHoleMaterial(glm::vec3(0.0f, 0.0f, 0.0f), 0.0f, 0.0f);
+ m_BlackHole = Object(glm::vec3(0.0f, 0.0f, 0.0f), 2.0f, blackHoleMaterial);
m_BlackHoleInitialized = true;
m_Initialized = true;
@@ -90,8 +108,15 @@ namespace Donut
void WorldBuilderState::OnRender()
{
+ auto& hdriManager = HDRIManager::Get();
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+
RenderCommand::SetClearColor(glm::vec4(0.1f, 0.1f, 0.1f, 1.0f));
RenderCommand::Clear();
+
+ if (m_HDRIEnvironment)
+ RenderSkybox();
+
RenderScene();
}
@@ -347,7 +372,6 @@ namespace Donut
ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "New Sphere");
ImGui::Separator();
- // Show object count and limit
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "Objects: %zu/16", m_Scene.objs.size());
ImGui::Text("Position:");
@@ -367,7 +391,6 @@ namespace Donut
ImGui::Spacing();
- // Disable button if object limit reached
if (m_Scene.objs.size() >= 16)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
@@ -468,6 +491,44 @@ namespace Donut
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "White rim around sphere silhouette");
}
+
+ ImGui::Spacing();
+
+ if (ImGui::CollapsingHeader("HDRI Environment", nullptr))
+ {
+ ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "HDRI Settings:");
+
+ static int selectedHDRI = 0;
+ ImGui::PushID("WorldBuilderHDRI");
+ auto& hdriManager = HDRIManager::Get();
+ const auto& availableHDRI = hdriManager.GetAvailableHDRI();
+
+ static std::vector<std::string> hdriOptionNames;
+ static std::vector<const char*> hdriOptions;
+
+ if (hdriOptionNames.size() != availableHDRI.size())
+ {
+ hdriOptionNames.clear();
+ hdriOptions.clear();
+
+ for (const auto& path : availableHDRI)
+ {
+ hdriOptionNames.push_back(hdriManager.GetHDRIName(path));
+ hdriOptions.push_back(hdriOptionNames.back().c_str());
+ }
+ }
+
+ if (ImGui::Combo("HDRI Environment", &selectedHDRI, hdriOptions.data(), static_cast<int>(hdriOptions.size())))
+ {
+ auto& hdriManager = HDRIManager::Get();
+ hdriManager.SetCurrentHDRI(availableHDRI[selectedHDRI]);
+ m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+ }
+
+ ImGui::Spacing();
+ ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "HDRI provides background skybox and lighting for the scene");
+ ImGui::PopID();
+ }
ImGui::Spacing();
ImGui::Separator();
@@ -486,11 +547,8 @@ namespace Donut
if (ImGui::Button("Start Simulation", ImVec2(ImGui::GetWindowWidth() - 20, 30)))
{
- // Load objects from WorldBuilder scene into Engine
auto& engine = Application::Get().GetEngine();
engine.LoadObjectsFromScene(m_Scene.objs);
-
- // Switch to simulation state
Application::Get().GetStateManager().SwitchToState("Simulation");
}
@@ -551,7 +609,6 @@ namespace Donut
void WorldBuilderState::AddSphere()
{
- // Check if we've reached the object limit (16 objects)
if (m_Scene.objs.size() >= 16)
{
DONUT_WARN("Cannot add more objects. Maximum of 16 objects reached.");
@@ -768,6 +825,12 @@ namespace Donut
m_SphereShader->SetFloat3("u_OutlineColor", m_OutlineColor);
m_SphereShader->SetFloat("u_OutlineWidth", m_OutlineWidth);
+ if (m_HDRIEnvironment)
+ {
+ m_HDRIEnvironment->Bind(1);
+ m_SphereShader->SetInt("u_HDRIEnvironment", 1);
+ }
+
if (m_BlackHoleInitialized)
{
glm::mat4 blackHoleTransform = glm::translate(glm::mat4(1.0f), m_BlackHole.m_Centre);
@@ -818,4 +881,94 @@ namespace Donut
RenderCommand::DisableDepthTest();
}
+
+
+
+ void WorldBuilderState::InitializeSkyboxGeometry()
+ {
+ float skyboxVertices[] =
+ {
+ -1.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f,
+
+ -1.0f, -1.0f, 1.0f,
+ -1.0f, -1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, -1.0f, 1.0f,
+
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+
+ -1.0f, -1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, -1.0f, 1.0f,
+ -1.0f, -1.0f, 1.0f,
+
+ -1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, -1.0f,
+ 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f, 1.0f,
+ -1.0f, 1.0f, -1.0f,
+
+ -1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, 1.0f,
+ 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, 1.0f,
+ 1.0f, -1.0f, 1.0f
+ };
+
+ auto vertexBuffer = Ref<VertexBuffer>(VertexBuffer::Create(skyboxVertices, sizeof(skyboxVertices)));
+ VertexBufferLayout layout;
+ layout.Push<float>(3);
+ vertexBuffer->SetLayout(layout);
+
+ m_SkyboxVAO = Ref<VertexArray>(VertexArray::Create());
+ m_SkyboxVAO->AddVertexBuffer(vertexBuffer);
+ }
+
+ void WorldBuilderState::RenderSkybox()
+ {
+ if (!m_SkyboxShader || !m_SkyboxVAO || !m_HDRIEnvironment)
+ {
+ return;
+ }
+
+ GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
+ int width, height;
+ glfwGetFramebufferSize(window, &width, &height);
+
+ RenderCommand::SetViewport(0, 0, width, height);
+ RenderCommand::DisableDepthTest();
+
+ glm::mat4 view = m_Camera.GetViewMatrix();
+ glm::mat4 projection = m_Camera.GetProjectionMatrix();
+
+ view = glm::mat4(glm::mat3(view));
+
+ m_SkyboxShader->Bind();
+ m_SkyboxShader->SetMat4("u_View", view);
+ m_SkyboxShader->SetMat4("u_Projection", projection);
+
+ m_HDRIEnvironment->Bind(0);
+ m_SkyboxShader->SetInt("u_Skybox", 0);
+
+ m_SkyboxVAO->Bind();
+ RenderCommand::DrawArrays(36);
+
+ RenderCommand::EnableDepthTest();
+ }
};
diff --git a/src/States/WorldBuilderState.h b/src/States/WorldBuilderState.h
index cbe8b9e..29662db 100644
--- a/src/States/WorldBuilderState.h
+++ b/src/States/WorldBuilderState.h
@@ -47,6 +47,10 @@ namespace Donut
Ref<Shader> m_SphereShader;
Ref<VertexArray> m_SphereVAO;
+ Ref<CubemapTexture> m_HDRIEnvironment;
+
+ Ref<Shader> m_SkyboxShader;
+ Ref<VertexArray> m_SkyboxVAO;
glm::vec3 m_NewObjectPosition = glm::vec3(0.0f, 0.0f, 0.0f);
float m_NewObjectRadius = 1.0f;
@@ -72,5 +76,11 @@ namespace Donut
Object m_BlackHole;
bool m_BlackHoleInitialized = false;
+
+ void SetHDRIEnvironment(Ref<CubemapTexture> hdri) { m_HDRIEnvironment = hdri; }
+ Ref<CubemapTexture> GetHDRIEnvironment() const { return m_HDRIEnvironment; }
+
+ void InitializeSkyboxGeometry();
+ void RenderSkybox();
};
};