diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index ec77056..7a19922 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -25,8 +25,10 @@ set(SRC_FILES "src/Graphics/Resources/GPUImage.cpp" "src/Graphics/Resources/NBuffer.cpp" + "src/Graphics/Resources/Cubemap.cpp" "src/Graphics/Pipelines/MeshPipeline.cpp" + "src/Graphics/Pipelines/SkyboxPipeline.cpp" "src/Input/InputManager.cpp" diff --git a/destrum/assets_src/shaders/cubemap.frag b/destrum/assets_src/shaders/cubemap.frag new file mode 100644 index 0000000..8386f54 --- /dev/null +++ b/destrum/assets_src/shaders/cubemap.frag @@ -0,0 +1,38 @@ +#version 450 +#extension GL_GOOGLE_include_directive : require +#include "bindless.glsl" + + +layout(location = 0) in vec3 localPos; + +// Input equirectangular HDR texture + +layout(location = 0) out vec4 outColor; + +const float PI = 3.14159265359; + +layout(push_constant) uniform PushConstants { + mat4 view; + mat4 proj; + uint skyboxId; +} pcs; + +vec2 sampleSphericalMap(vec3 v) { + // Convert direction to spherical coordinates + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv /= vec2(2.0 * PI, PI); + uv += 0.5; + return uv; +} + +void main() { + // Normalize direction vector + vec3 dir = normalize(localPos); + + // Sample from equirectangular texture + vec2 uv = sampleSphericalMap(dir); + vec4 color = sampleTexture2DNearest(pcs.skyboxId, uv); + + + outColor = color; +} \ No newline at end of file diff --git a/destrum/assets_src/shaders/cubemap.vert b/destrum/assets_src/shaders/cubemap.vert new file mode 100644 index 0000000..0dbc0d8 --- /dev/null +++ b/destrum/assets_src/shaders/cubemap.vert @@ -0,0 +1,42 @@ +#version 450 + +// Hardcoded cube vertices (36 vertices for 12 triangles) +const vec3 positions[36] = vec3[]( +// Front face +vec3(-1.0, -1.0, 1.0), vec3( 1.0, -1.0, 1.0), vec3( 1.0, 1.0, 1.0), +vec3( 1.0, 1.0, 1.0), vec3(-1.0, 1.0, 1.0), vec3(-1.0, -1.0, 1.0), +// Back face +vec3(-1.0, -1.0, -1.0), vec3(-1.0, 1.0, -1.0), vec3( 1.0, 1.0, -1.0), +vec3( 1.0, 1.0, -1.0), vec3( 1.0, -1.0, -1.0), vec3(-1.0, -1.0, -1.0), +// Top face +vec3(-1.0, 1.0, -1.0), vec3(-1.0, 1.0, 1.0), vec3( 1.0, 1.0, 1.0), +vec3( 1.0, 1.0, 1.0), vec3( 1.0, 1.0, -1.0), vec3(-1.0, 1.0, -1.0), +// Bottom face +vec3(-1.0, -1.0, -1.0), vec3( 1.0, -1.0, -1.0), vec3( 1.0, -1.0, 1.0), +vec3( 1.0, -1.0, 1.0), vec3(-1.0, -1.0, 1.0), vec3(-1.0, -1.0, -1.0), +// Right face +vec3( 1.0, -1.0, -1.0), vec3( 1.0, 1.0, -1.0), vec3( 1.0, 1.0, 1.0), +vec3( 1.0, 1.0, 1.0), vec3( 1.0, -1.0, 1.0), vec3( 1.0, -1.0, -1.0), +// Left face +vec3(-1.0, -1.0, -1.0), vec3(-1.0, -1.0, 1.0), vec3(-1.0, 1.0, 1.0), +vec3(-1.0, 1.0, 1.0), vec3(-1.0, 1.0, -1.0), vec3(-1.0, -1.0, -1.0) +); + +// Hardcoded UVs aren't needed for cube map rendering +layout(location = 0) out vec3 localPos; + +// Push constants for view and projection matrices +layout(push_constant) uniform PushConstants { + mat4 view; + mat4 proj; + uint skyboxId; +} pc; + +void main() { + // Get hardcoded vertex position + vec3 pos = positions[gl_VertexIndex]; + localPos = pos; // Pass to fragment shader + + // Apply view and projection matrices + gl_Position = pc.proj * pc.view * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/destrum/assets_src/shaders/fullscreen_triangle.vert b/destrum/assets_src/shaders/fullscreen_triangle.vert new file mode 100644 index 0000000..c956fa3 --- /dev/null +++ b/destrum/assets_src/shaders/fullscreen_triangle.vert @@ -0,0 +1,10 @@ +#version 460 + +layout (location = 0) out vec2 outUV; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); +// gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); + gl_Position = vec4(outUV * 2.0 - 1.0, 1.0, 1.0); +} diff --git a/destrum/assets_src/shaders/mesh.frag b/destrum/assets_src/shaders/mesh.frag index 83e219e..b7f93ff 100644 --- a/destrum/assets_src/shaders/mesh.frag +++ b/destrum/assets_src/shaders/mesh.frag @@ -18,7 +18,7 @@ void main() { MaterialData material = pcs.sceneData.materials.data[pcs.materialID]; - vec4 diffuse = sampleTexture2DLinear(material.diffuseTex, inUV); + vec4 diffuse = sampleTexture2DLinear(material.diffuseTex, inUV) * material.baseColor; outFragColor = diffuse; diff --git a/destrum/assets_src/shaders/skybox.frag b/destrum/assets_src/shaders/skybox.frag new file mode 100644 index 0000000..5f0c40a --- /dev/null +++ b/destrum/assets_src/shaders/skybox.frag @@ -0,0 +1,35 @@ +#version 450 +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_nonuniform_qualifier : enable + +#include "bindless.glsl" + +layout(location = 0) in vec2 uv; // from fullscreen triangle: 0..2 range +layout(location = 0) out vec4 outColor; + +layout(push_constant) uniform SkyboxPC { + mat4 invViewProj; // inverse(Proj * View) (your current setup) + vec4 cameraPos; // xyz = camera world position + uint skyboxTextureId; // index into textureCubes[] +} pcs; + +void main() +{ + // Fullscreen-triangle trick gives uv in [0..2]. Convert to [0..1]. + vec2 uv01 = uv * 0.5; + + // Build an NDC point on the far plane. + // Vulkan NDC is x,y in [-1..1], z in [0..1]. Using z=1 means "far". + vec4 ndc = vec4(uv01 * 2.0 - 1.0, 1.0, 1.0); + + // Unproject to world space + vec4 world = pcs.invViewProj * ndc; + vec3 worldPos = world.xyz / world.w; + + // Direction from camera through this pixel + vec3 dir = normalize(worldPos - pcs.cameraPos.xyz); + + // Sample cubemap directly + outColor = sampleTextureCubeLinear(pcs.skyboxTextureId, dir); + // outColor = sampleTextureCubeNearest(pcs.skyboxTextureId, dir); +} diff --git a/destrum/assets_src/textures/skybox.jpg b/destrum/assets_src/textures/skybox.jpg new file mode 100644 index 0000000..fd035a8 Binary files /dev/null and b/destrum/assets_src/textures/skybox.jpg differ diff --git a/destrum/assets_src/textures/test-skybox.png b/destrum/assets_src/textures/test-skybox.png new file mode 100644 index 0000000..f239305 Binary files /dev/null and b/destrum/assets_src/textures/test-skybox.png differ diff --git a/destrum/include/destrum/Components/OrbitAndSpin.h b/destrum/include/destrum/Components/OrbitAndSpin.h index 08221d2..e1a0f8b 100644 --- a/destrum/include/destrum/Components/OrbitAndSpin.h +++ b/destrum/include/destrum/Components/OrbitAndSpin.h @@ -21,6 +21,7 @@ public: void Randomize(uint32_t seed); void Update() override; + void Start() override; // optional setters void SetRadius(float r) { m_Radius = r; } @@ -37,12 +38,18 @@ private: float m_OrbitSpeed = 1.0f; // rad/sec float m_OrbitAngle = 0.0f; // current angle float m_OrbitPhase = 0.0f; // starting offset + float m_GrowPhase = 0.0f; glm::vec3 m_U{1,0,0}; // orbit basis axis 1 glm::vec3 m_V{0,0,1}; // orbit basis axis 2 + glm::vec3 m_BaseScale{1.0f}; + float m_GrowSpeed = 1.0f; // rad/sec + // self spin glm::vec3 m_SpinAxis{0,1,0}; float m_SpinSpeed = 2.0f; // rad/sec + + MaterialID m_MaterialID{0}; }; #endif //ORBITANDSPIN_H diff --git a/destrum/include/destrum/Graphics/GfxDevice.h b/destrum/include/destrum/Graphics/GfxDevice.h index 538ac9c..ca9c307 100644 --- a/destrum/include/destrum/Graphics/GfxDevice.h +++ b/destrum/include/destrum/Graphics/GfxDevice.h @@ -46,6 +46,7 @@ public: VkCommandBuffer beginFrame(); [[nodiscard]] bool needsSwapchainRecreate() const { return swapchain.isDirty(); } + VulkanImmediateExecutor& GetImmediateExecuter(); struct EndFrameProps { @@ -126,6 +127,19 @@ private: ImageCache imageCache; + static uint32_t BytesPerTexel(VkFormat fmt) { + switch (fmt) { + case VK_FORMAT_R8_UNORM: return 1; + case VK_FORMAT_R8G8B8A8_UNORM: return 4; + case VK_FORMAT_B8G8R8A8_SRGB: return 4; + case VK_FORMAT_R16G16B16A16_SFLOAT: return 8; + case VK_FORMAT_R32G32B32A32_SFLOAT: return 16; + case VK_FORMAT_R8G8B8A8_SRGB: return 4; + default: + throw std::runtime_error("BytesPerTexel: unsupported format"); + } + } + }; diff --git a/destrum/include/destrum/Graphics/ImageCache.h b/destrum/include/destrum/Graphics/ImageCache.h index 3157a88..363b913 100644 --- a/destrum/include/destrum/Graphics/ImageCache.h +++ b/destrum/include/destrum/Graphics/ImageCache.h @@ -39,6 +39,16 @@ public: void setErrorImageId(ImageID id) { errorImageId = id; } + static uint32_t BytesPerTexel(VkFormat fmt) { + switch (fmt) { + case VK_FORMAT_R32G32B32A32_SFLOAT: return 16; + case VK_FORMAT_R16G16B16A16_SFLOAT: return 8; + case VK_FORMAT_R8G8B8A8_UNORM: return 4; + // add formats you use + default: throw std::runtime_error("BytesPerTexel: unsupported format"); + } + } + private: std::vector images; GfxDevice& gfxDevice; diff --git a/destrum/include/destrum/Graphics/MaterialCache.h b/destrum/include/destrum/Graphics/MaterialCache.h index 464c2e8..6ca08f1 100644 --- a/destrum/include/destrum/Graphics/MaterialCache.h +++ b/destrum/include/destrum/Graphics/MaterialCache.h @@ -26,6 +26,10 @@ public: const GPUBuffer& getMaterialDataBuffer() const { return materialDataBuffer; } VkDeviceAddress getMaterialDataBufferAddress() const { return materialDataBuffer.address; } + + Material& getMaterialMutable(MaterialID id); + void updateMaterialGPU(GfxDevice& gfxDevice, MaterialID id); + private: std::vector materials; diff --git a/destrum/include/destrum/Graphics/Pipelines/SkyboxPipeline.h b/destrum/include/destrum/Graphics/Pipelines/SkyboxPipeline.h new file mode 100644 index 0000000..4e45918 --- /dev/null +++ b/destrum/include/destrum/Graphics/Pipelines/SkyboxPipeline.h @@ -0,0 +1,43 @@ +#ifndef SKYBOXPIPELINE_H +#define SKYBOXPIPELINE_H + +#include +#include +#include +#include +#include +#include +#include + +class SkyboxPipeline final { +public: + + SkyboxPipeline(); + ~SkyboxPipeline(); + + void init( + GfxDevice& gfxDevice, + VkFormat drawImageFormat, + VkFormat depthImageFormat + ); + void cleanup(VkDevice device); + + void draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera); + + void setSkyboxImage(const ImageID skyboxId); + +private: + + VkPipelineLayout pipelineLayout; + std::unique_ptr pipeline; + ImageID skyboxTextureId{NULL_IMAGE_ID}; + + + struct SkyboxPushConstants { + glm::mat4 invViewProj; + glm::vec4 cameraPos; + std::uint32_t skyboxTextureId; + }; +}; + +#endif //SKYBOXPIPELINE_H diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index f23c288..3c61d38 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -13,6 +13,8 @@ #include +#include "Pipelines/SkyboxPipeline.h" + class GameRenderer { public: struct SceneData { @@ -39,6 +41,12 @@ public: createDrawImage(gfxDevice, newSize, false); } + Material& getMaterialMutable(MaterialID id); + void updateMaterialGPU(MaterialID id); + + void setSkyboxTexture(ImageID skyboxImageId); + + private: void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate); @@ -79,6 +87,7 @@ private: std::unique_ptr meshPipeline; + std::unique_ptr skyboxPipeline; }; #endif //RENDERER_H diff --git a/destrum/include/destrum/Graphics/Resources/Cubemap.h b/destrum/include/destrum/Graphics/Resources/Cubemap.h new file mode 100644 index 0000000..ba34f70 --- /dev/null +++ b/destrum/include/destrum/Graphics/Resources/Cubemap.h @@ -0,0 +1,66 @@ +#ifndef CUBEMAP_H +#define CUBEMAP_H + +#include +#include +#include +#include +#include + +#include + +#include "destrum/Graphics/Pipeline.h" + + +class CubeMap { +public: + explicit CubeMap(); + ~CubeMap(); + + void LoadCubeMap(const std::filesystem::path &directoryPath); + void RenderToCubemap(ImageID inputImage, VkImage outputImage, std::array faceViews, uint32_t size); + + void InitCubemapPipeline(const std::string& vertPath, const std::string& fragPath); + void CreateCubeMap(); + ImageID GetCubeMapImageID(); + +private: + const std::array viewMatrices = { + // POSITIVE_X + glm::lookAt(glm::vec3(0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + // NEGATIVE_X + glm::lookAt(glm::vec3(0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + // POSITIVE_Y + glm::lookAt(glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + // NEGATIVE_Y + glm::lookAt(glm::vec3(0.0f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)), + // POSITIVE_Z + glm::lookAt(glm::vec3(0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + // NEGATIVE_Z + glm::lookAt(glm::vec3(0.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)) + }; + + struct alignas(16) PC { + glm::mat4 viewMtx; // 64 + glm::mat4 projMtx; // 64 + std::uint32_t inputImageId; // 4 + }; + + glm::mat4 m_projection{}; + + ImageID m_hdrImage{}; + + uint32_t m_cubeMapSize = 1024; // Default size for cube map + VkImageView m_skyboxView; + + VkPipelineLayout m_cubemapPipelineLayout = VK_NULL_HANDLE; + std::unique_ptr m_cubemapPipeline; + + std::string m_cubemapVert; + std::string m_cubemapFrag; + + + ImageID m_cubemapImageID; +}; + +#endif //CUBEMAP_H diff --git a/destrum/include/destrum/Util/GameState.h b/destrum/include/destrum/Util/GameState.h new file mode 100644 index 0000000..7ae6574 --- /dev/null +++ b/destrum/include/destrum/Util/GameState.h @@ -0,0 +1,40 @@ +#ifndef GAMESTATE_H +#define GAMESTATE_H + +#include +#include + + +class GfxDevice; +class GameRenderer; + +class GameState final: public Singleton { +public: + friend class Singleton; + + void SetGfxDevice(GfxDevice* device) { m_gfxDevice = device; } + GfxDevice& Gfx() { + assert(m_gfxDevice && "GfxDevice not registered yet!"); + return *m_gfxDevice; + } + const GfxDevice& Gfx() const { + assert(m_gfxDevice && "GfxDevice not registered yet!"); + return *m_gfxDevice; + } + + void SetRenderer(GameRenderer* renderer) { m_renderer = renderer; } + GameRenderer& Renderer() { + assert(m_renderer && "Renderer not registered yet!"); + return *m_renderer; + } + const GameRenderer& Renderer() const { + assert(m_renderer && "Renderer not registered yet!"); + return *m_renderer; + } + +private: + GfxDevice* m_gfxDevice{nullptr}; + GameRenderer* m_renderer{nullptr}; +}; + +#endif //GAMESTATE_H diff --git a/destrum/src/Components/OrbitAndSpin.cpp b/destrum/src/Components/OrbitAndSpin.cpp index 404e580..41616cf 100644 --- a/destrum/src/Components/OrbitAndSpin.cpp +++ b/destrum/src/Components/OrbitAndSpin.cpp @@ -4,7 +4,10 @@ #include #include +#include "destrum/ObjectModel/GameObject.h" #include "destrum/ObjectModel/Transform.h" +#include "destrum/Components/MeshRendererComponent.h" +#include "destrum/Util/GameState.h" static glm::vec3 RandomUnitVector(std::mt19937& rng) { @@ -48,11 +51,6 @@ void OrbitAndSpin::BuildOrbitBasis() void OrbitAndSpin::Update() { - // If your engine provides dt via a global/time service, use that instead. - // Since your Spinner takes dt indirectly, I'm assuming Component::Update() - // is called once per frame and you can access dt somewhere globally. - // - // If you CAN pass dt into Update, change signature to Update(float dt). float dt = 1.0f / 60.0f; // orbit @@ -60,10 +58,37 @@ void OrbitAndSpin::Update() float a = m_OrbitAngle + m_OrbitPhase; glm::vec3 offset = (m_U * std::cos(a) + m_V * std::sin(a)) * m_Radius; + + // IMPORTANT: if SetWorldPosition resets TRS in your engine, this will wipe scale unless you set it again after. GetTransform().SetWorldPosition(m_Center + offset); - // self spin (local rotation) + // spin glm::quat dq = glm::angleAxis(m_SpinSpeed * dt, glm::normalize(m_SpinAxis)); - auto current = GetTransform().GetLocalRotation(); // adapt to your API + auto current = GetTransform().GetLocalRotation(); GetTransform().SetLocalRotation(glm::normalize(dq * current)); + + // grow (always positive) + m_GrowPhase += m_GrowSpeed * dt; + + float t = 0.5f * (std::sin(m_GrowPhase) + 1.0f); // 0..1 + float s = 1.50 + t * 0.70f; // 0.05..0.15 (pick what you want) + + GetTransform().SetLocalScale(glm::vec3(std::sin(m_GrowPhase))); + + // material color +// auto& mat = GameState::GetInstance().Renderer().getMaterialMutable(m_MaterialID); +// mat.baseColor = glm::vec3( +// 0.5f + 0.5f * std::sin(m_OrbitAngle * 2.0f), +// 0.5f + 0.5f * std::sin(m_OrbitAngle * 3.0f + 2.0f), +// 0.5f + 0.5f * std::sin(m_OrbitAngle * 4.0f + 4.0f) +// ); +// GameState::GetInstance().Renderer().updateMaterialGPU(m_MaterialID); +} + +void OrbitAndSpin::Start() { + auto meshComp = this->GetGameObject()->GetComponent(); + m_MaterialID = meshComp->GetMaterialID(); + + m_BaseScale = GetTransform().GetLocalScale(); // <-- important + } diff --git a/destrum/src/Graphics/Camera.cpp b/destrum/src/Graphics/Camera.cpp index 17a3169..5911c98 100644 --- a/destrum/src/Graphics/Camera.cpp +++ b/destrum/src/Graphics/Camera.cpp @@ -70,8 +70,8 @@ void Camera::Update(float deltaTime) { if (input.IsKeyDown(SDL_SCANCODE_S)) move -= m_forward; if (input.IsKeyDown(SDL_SCANCODE_D)) move += m_right; if (input.IsKeyDown(SDL_SCANCODE_A)) move -= m_right; - if (input.IsKeyDown(SDL_SCANCODE_Q)) move += glm::vec3(0, 1, 0); // Absolute Up - if (input.IsKeyDown(SDL_SCANCODE_E)) move -= glm::vec3(0, 1, 0); // Absolute Down + if (input.IsKeyDown(SDL_SCANCODE_E)) move += glm::vec3(0, 1, 0); // Absolute Up + if (input.IsKeyDown(SDL_SCANCODE_Q)) move -= glm::vec3(0, 1, 0); // Absolute Down if (glm::length2(move) > 0.0f) { m_position += glm::normalize(move) * (moveSpeed * deltaTime); diff --git a/destrum/src/Graphics/GfxDevice.cpp b/destrum/src/Graphics/GfxDevice.cpp index 82b4e29..9189379 100644 --- a/destrum/src/Graphics/GfxDevice.cpp +++ b/destrum/src/Graphics/GfxDevice.cpp @@ -15,6 +15,7 @@ #include #include "destrum/Graphics/imageLoader.h" +#include "destrum/Util/GameState.h" #include "spdlog/spdlog.h" GfxDevice::GfxDevice(): imageCache(*this) { @@ -141,7 +142,7 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) // "white texture", // &pixel); // } - + GameState::GetInstance().SetGfxDevice(this); } void GfxDevice::recreateSwapchain(int width, int height) { @@ -164,6 +165,10 @@ VkCommandBuffer GfxDevice::beginFrame() { return cmd; } +VulkanImmediateExecutor& GfxDevice::GetImmediateExecuter() { + return executor; +} + void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) { // get swapchain image const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex()); @@ -426,7 +431,7 @@ GPUImage GfxDevice::createImageRaw( if (createInfo.isCubemap) { assert(createInfo.numLayers % 6 == 0); - assert(!createInfo.mipMap); + // assert(!createInfo.mipMap); assert((createInfo.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0); } @@ -503,16 +508,14 @@ GPUImage GfxDevice::createImageRaw( void GfxDevice::uploadImageData(const GPUImage& image, void* pixelData, std::uint32_t layer) const { - int numChannels = 4; - if (image.format == VK_FORMAT_R8_UNORM) { - // FIXME: support more types - numChannels = 1; - } - const auto dataSize = - image.extent.depth * image.extent.width * image.extent.height * numChannels; + VkDeviceSize dataSize = + VkDeviceSize(image.extent.depth) * + image.extent.width * + image.extent.height * + BytesPerTexel(image.format); - const auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - memcpy(uploadBuffer.info.pMappedData, pixelData, dataSize); + auto uploadBuffer = createBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + memcpy(uploadBuffer.info.pMappedData, pixelData, size_t(dataSize)); executor.immediateSubmit([&](VkCommandBuffer cmd) { assert( diff --git a/destrum/src/Graphics/MaterialCache.cpp b/destrum/src/Graphics/MaterialCache.cpp index 7684107..98a09fa 100644 --- a/destrum/src/Graphics/MaterialCache.cpp +++ b/destrum/src/Graphics/MaterialCache.cpp @@ -75,3 +75,33 @@ MaterialID MaterialCache::getPlaceholderMaterialId() const assert(placeholderMaterialId != NULL_MATERIAL_ID && "MaterialCache::init not called"); return placeholderMaterialId; } + +Material& MaterialCache::getMaterialMutable(MaterialID id) { + assert(id < materials.size()); + return materials.at(id); +} + +void MaterialCache::updateMaterialGPU(GfxDevice& gfxDevice, MaterialID id) +{ + assert(id < materials.size()); + assert(materialDataBuffer.info.pMappedData && "materialDataBuffer must be mapped"); + + const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder) { + return imageId != NULL_IMAGE_ID ? imageId : placeholder; + }; + + Material& material = materials[id]; + + MaterialData* data = reinterpret_cast(materialDataBuffer.info.pMappedData); + + const ImageID whiteTextureID = gfxDevice.getWhiteTextureID(); + + data[id] = MaterialData{ + .baseColor = glm::vec4(material.baseColor, 1.0f), + .metalRoughnessEmissive = glm::vec4(material.metallicFactor, material.roughnessFactor, material.emissiveFactor, 0.f), + .diffuseTex = getTextureOrElse(material.diffuseTexture, whiteTextureID), + .normalTex = whiteTextureID, // if you have this field + .metallicRoughnessTex = whiteTextureID, + .emissiveTex = whiteTextureID, + }; +} diff --git a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp index a9b2614..293e5af 100644 --- a/destrum/src/Graphics/Pipelines/MeshPipeline.cpp +++ b/destrum/src/Graphics/Pipelines/MeshPipeline.cpp @@ -1,7 +1,7 @@ #include #include -MeshPipeline::MeshPipeline() { +MeshPipeline::MeshPipeline(): m_pipelineLayout{nullptr} { } MeshPipeline::~MeshPipeline() { diff --git a/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp b/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp new file mode 100644 index 0000000..1386cc3 --- /dev/null +++ b/destrum/src/Graphics/Pipelines/SkyboxPipeline.cpp @@ -0,0 +1,90 @@ +#include + +#include + +#include "spdlog/spdlog.h" + +SkyboxPipeline::SkyboxPipeline(): pipelineLayout{nullptr} { +} + +SkyboxPipeline::~SkyboxPipeline() { +} + +void SkyboxPipeline::init(GfxDevice& gfxDevice, VkFormat drawImageFormat, VkFormat depthImageFormat) { + + const auto vertexShader = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/fullscreen_triangle.vert"); + const auto fragShader = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/skybox.frag"); + + constexpr auto bufferRange = VkPushConstantRange{ + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = sizeof(SkyboxPushConstants), + }; + constexpr auto pushConstantRanges = std::array{bufferRange}; + const auto layouts = std::array{gfxDevice.getBindlessDescSetLayout()}; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = static_cast(layouts.size()); + pipelineLayoutInfo.pSetLayouts = layouts.data(); + + pipelineLayoutInfo.pushConstantRangeCount = static_cast(pushConstantRanges.size()); + pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data(); + + if (vkCreatePipelineLayout(gfxDevice.getDevice().device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Could not make pipleine layout"); + } + + PipelineConfigInfo pipelineConfig{}; + Pipeline::DefaultPipelineConfigInfo(pipelineConfig); + pipelineConfig.name = "skybox pipeline"; + pipelineConfig.pipelineLayout = pipelineLayout; + + pipelineConfig.vertexAttributeDescriptions = {}; + pipelineConfig.vertexBindingDescriptions = {}; + + pipelineConfig.colorAttachments = { drawImageFormat }; + pipelineConfig.depthAttachment = depthImageFormat; + + pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE; + + pipelineConfig.depthStencilInfo.depthTestEnable = VK_TRUE; + pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE; + pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + + pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE; + + pipeline = std::make_unique( + gfxDevice, + vertexShader.string(), + fragShader.string(), + pipelineConfig + ); +} + +void SkyboxPipeline::cleanup(VkDevice device) { + pipeline.reset(); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); +} + +void SkyboxPipeline::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera) { + if (skyboxTextureId == NULL_IMAGE_ID) { + return; + } + + pipeline->bind(cmd); + gfxDevice.bindBindlessDescSet(cmd, pipelineLayout); + + const auto pcs = SkyboxPushConstants{ + .invViewProj = glm::inverse(camera.GetViewProjectionMatrix()), + .cameraPos = glm::vec4{camera.GetPosition(), 1.f}, + .skyboxTextureId = static_cast(skyboxTextureId), + }; + vkCmdPushConstants(cmd, pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(SkyboxPushConstants), &pcs); + + vkCmdDraw(cmd, 3, 1, 0, 0); +} + +void SkyboxPipeline::setSkyboxImage(const ImageID skyboxId) { + skyboxTextureId = skyboxId; +} diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp index bd03015..b1a683c 100644 --- a/destrum/src/Graphics/Renderer.cpp +++ b/destrum/src/Graphics/Renderer.cpp @@ -2,6 +2,7 @@ #include +#include "destrum/Util/GameState.h" #include "spdlog/spdlog.h" GameRenderer::GameRenderer(MeshCache& meshCache, MaterialCache& matCache): meshCache{meshCache}, materialCache{matCache} { @@ -19,6 +20,11 @@ void GameRenderer::init(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize) { meshPipeline = std::make_unique(); meshPipeline->init(gfxDevice, drawImageFormat, depthImageFormat); + skyboxPipeline = std::make_unique(); + skyboxPipeline->init(gfxDevice, drawImageFormat, depthImageFormat); + + + GameState::GetInstance().SetRenderer(this); } void GameRenderer::beginDrawing(GfxDevice& gfxDevice) { @@ -80,6 +86,9 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& meshDrawCommands, sortedMeshDrawCommands); + skyboxPipeline->draw(cmd, gfxDevice, camera); + + vkCmdEndRendering(cmd); // vkutil::cmdEndLabel(cmd); @@ -106,6 +115,21 @@ const GPUImage& GameRenderer::getDrawImage(const GfxDevice& gfx_device) const { return gfx_device.getImage(drawImageId); } +Material& GameRenderer::getMaterialMutable(MaterialID id) { + assert(id != NULL_MATERIAL_ID); + return materialCache.getMaterialMutable(id); +} + +void GameRenderer::updateMaterialGPU(MaterialID id) { + assert(id != NULL_MATERIAL_ID); + materialCache.updateMaterialGPU(GameState::GetInstance().Gfx(), id); +} + +void GameRenderer::setSkyboxTexture(ImageID skyboxImageId) { + spdlog::debug("Set skybox texture to image id {}", skyboxImageId); + skyboxPipeline->setSkyboxImage(skyboxImageId); +} + void GameRenderer::createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate) diff --git a/destrum/src/Graphics/Resources/Cubemap.cpp b/destrum/src/Graphics/Resources/Cubemap.cpp new file mode 100644 index 0000000..40146f4 --- /dev/null +++ b/destrum/src/Graphics/Resources/Cubemap.cpp @@ -0,0 +1,286 @@ +#include + +#include "destrum/FS/AssetFS.h" +#include "destrum/Graphics/GfxDevice.h" +#include "destrum/Graphics/Pipeline.h" +#include "destrum/Util/GameState.h" +#include + +#include "glm/ext/matrix_clip_space.hpp" +#include "spdlog/spdlog.h" + + +CubeMap::CubeMap() { + m_projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);; +} + +CubeMap::~CubeMap() { + auto& gfx = GameState::GetInstance().Gfx(); + VkDevice device = gfx.getDevice(); + + if (m_skyboxView) { + vkDestroyImageView(device, m_skyboxView, nullptr); + m_skyboxView = VK_NULL_HANDLE; + } + + if (m_cubemapPipelineLayout) { + vkDestroyPipelineLayout(device, m_cubemapPipelineLayout, nullptr); + m_cubemapPipelineLayout = VK_NULL_HANDLE; + } +} + +void CubeMap::LoadCubeMap(const std::filesystem::path& directoryPath) { + m_hdrImage = GameState::GetInstance().Gfx().loadImageFromFile(directoryPath, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, false); +} + +void CubeMap::RenderToCubemap(ImageID inputImage, + VkImage outputImage, + std::array faceViews, + uint32_t size) +{ + // Ensure pipeline exists + if (!m_cubemapPipeline || m_cubemapPipelineLayout == VK_NULL_HANDLE) { + throw std::runtime_error("Cubemap pipeline not initialized. Call InitCubemapPipeline first."); + } + + auto& gfx = GameState::GetInstance().Gfx(); + + gfx.GetImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) { + + VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = outputImage; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 6; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + vkCmdPipelineBarrier( + cmd, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(size); + viewport.height = static_cast(size); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = {size, size}; + + for (uint32_t face = 0; face < 6; ++face) { + PC pc{}; + pc.viewMtx = viewMatrices[face]; + pc.projMtx = m_projection; + pc.inputImageId = m_hdrImage; + + VkRenderingAttachmentInfoKHR colorAttachment{ VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR }; + colorAttachment.imageView = faceViews[face]; + colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.clearValue.color = {0.f, 0.f, 0.f, 1.f}; + + VkRenderingInfoKHR renderingInfo{ VK_STRUCTURE_TYPE_RENDERING_INFO_KHR }; + renderingInfo.renderArea.offset = {0, 0}; + renderingInfo.renderArea.extent = {size, size}; + renderingInfo.layerCount = 1; + renderingInfo.colorAttachmentCount = 1; + renderingInfo.pColorAttachments = &colorAttachment; + + vkCmdBeginRendering(cmd, &renderingInfo); + + vkCmdSetViewport(cmd, 0, 1, &viewport); + vkCmdSetScissor(cmd, 0, 1, &scissor); + + m_cubemapPipeline->bind(cmd); + gfx.bindBindlessDescSet(cmd, m_cubemapPipelineLayout); + + vkCmdPushConstants( + cmd, + m_cubemapPipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, + sizeof(PC), + &pc + ); + + vkCmdDraw(cmd, 36, 1, 0, 0); + + vkCmdEndRendering(cmd); + } + + // Transition to shader read + barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier( + cmd, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + }); +} + +void CubeMap::CreateCubeMap() { + const uint32_t mipLevels = static_cast(std::floor(std::log2(m_cubeMapSize))) + 1; + + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.height = m_cubeMapSize; + imageInfo.extent.width = m_cubeMapSize; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = mipLevels; + imageInfo.arrayLayers = 6; // 6 faces for cubemap + imageInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; // Create a cubemap + + VmaAllocationCreateInfo allocInfo{}; + allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + auto& device = GameState::GetInstance().Gfx(); + GPUImage cubeMapID = device.createImageRaw({ + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + .flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, + .extent = + VkExtent3D{ + .width = m_cubeMapSize, + .height = m_cubeMapSize, + .depth = 1, + }, + .numLayers = 6, + .mipMap = true, + .isCubemap = true + }); + + // if (vmaCreateImage(device.getAllocator(), &imageInfo, &allocInfo, &cubeMapID.image, &cubeMapID.allocation, nullptr) != VK_SUCCESS) { + // throw std::runtime_error("Failed to create image with VMA!"); + // } + + + std::array faceViews{}; + for (uint32_t face = 0; face < 6; ++face) { + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = cubeMapID.image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = face; + viewInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device.getDevice(), &viewInfo, nullptr, &faceViews[face]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create image view!"); + } + } + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = cubeMapID.image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 6; + + + if (vkCreateImageView(device.getDevice(), &viewInfo, nullptr, &m_skyboxView) != VK_SUCCESS) { + throw std::runtime_error("Failed to create image view!"); + } + const auto vertPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert"); + const auto fragPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag"); + spdlog::info("hdriImage id = {}", m_hdrImage); + RenderToCubemap(m_hdrImage, cubeMapID.image, faceViews, m_cubeMapSize); + + m_cubemapImageID = GameState::GetInstance().Gfx().addImageToCache(cubeMapID); + + for (auto v : faceViews) { + vkDestroyImageView(device.getDevice(), v, nullptr); + } +} + +ImageID CubeMap::GetCubeMapImageID() { + return m_cubemapImageID; +} + +void CubeMap::InitCubemapPipeline(const std::string& vertPath, const std::string& fragPath) +{ + auto& gfx = GameState::GetInstance().Gfx(); + VkDevice device = gfx.getDevice(); + + if (m_cubemapPipeline) return; // already created + + // Save paths if you want + m_cubemapVert = vertPath; + m_cubemapFrag = fragPath; + + + + VkPushConstantRange pushConstantRange{}; + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + pushConstantRange.offset = 0; + pushConstantRange.size = sizeof(PC); + + const auto layouts = std::array{ gfx.getBindlessDescSetLayout() }; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = static_cast(layouts.size()); + pipelineLayoutInfo.pSetLayouts = layouts.data(); + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; + + if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &m_cubemapPipelineLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create cubemap pipeline layout!"); + } + + PipelineConfigInfo pipelineConfig{}; + Pipeline::DefaultPipelineConfigInfo(pipelineConfig); + + pipelineConfig.vertexAttributeDescriptions = {}; + pipelineConfig.vertexBindingDescriptions = {}; + pipelineConfig.pipelineLayout = m_cubemapPipelineLayout; + pipelineConfig.colorAttachments = { VK_FORMAT_R32G32B32A32_SFLOAT }; // must match cubemap image view format + pipelineConfig.depthAttachment = VK_FORMAT_UNDEFINED; + pipelineConfig.depthStencilInfo.depthTestEnable = VK_FALSE; + pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE; + pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE; + + m_cubemapPipeline = std::make_unique( + gfx, + vertPath, + fragPath, + pipelineConfig + ); +} diff --git a/lightkeeper/CMakeLists.txt b/lightkeeper/CMakeLists.txt index a58b311..3d73dfe 100644 --- a/lightkeeper/CMakeLists.txt +++ b/lightkeeper/CMakeLists.txt @@ -47,3 +47,5 @@ add_custom_target(_internal_cook_game_assets ALL DEPENDS TheChef ) + + diff --git a/lightkeeper/include/Lightkeeper.h b/lightkeeper/include/Lightkeeper.h index b29d1a8..f03eb4b 100644 --- a/lightkeeper/include/Lightkeeper.h +++ b/lightkeeper/include/Lightkeeper.h @@ -4,6 +4,8 @@ #include #include +#include "destrum/Graphics/Resources/Cubemap.h" + class LightKeeper final : public App { public: LightKeeper(); @@ -24,6 +26,8 @@ private: CPUMesh testMesh{}; MeshID testMeshID; MaterialID testMaterialID; + + std::unique_ptr skyboxCubemap; }; #endif //LIGHTKEEPER_H diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index b095ade..81b54a2 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -46,6 +46,8 @@ void LightKeeper::customInit() { }); spdlog::info("Test material created with id: {}", testMaterialID); + renderer.setSkyboxTexture(testimgID); + camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f))); auto& scene = SceneManager::GetInstance().CreateScene("Main"); @@ -63,31 +65,46 @@ void LightKeeper::customInit() { globeRoot->AddComponent(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec scene.Add(globeRoot); - const int count = 100; - const float radius = 5.0f; - - const float orbitRadius = 5.0f; - - for (int i = 0; i < count; ++i) { - auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); - - auto childMeshComp = childCube->AddComponent(); - childMeshComp->SetMeshID(testMeshID); - childMeshComp->SetMaterialID(testMaterialID); - - childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); - - // Add orbit + self spin - auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); - orbit->Randomize(1337u + (uint32_t)i); // stable random per index - - scene.Add(childCube); - } + // const int count = 100; + // const float radius = 5.0f; + // + // const float orbitRadius = 5.0f; + // + // for (int i = 0; i < count; ++i) { + // auto childCube = std::make_shared(fmt::format("ChildCube{}", i)); + // + // auto childMeshComp = childCube->AddComponent(); + // childMeshComp->SetMeshID(testMeshID); + // childMeshComp->SetMaterialID(testMaterialID); + // + // childCube->GetTransform().SetWorldScale(glm::vec3(0.1f)); + // + // // Add orbit + self spin + // auto orbit = childCube->AddComponent(orbitRadius, glm::vec3(0.0f)); + // orbit->Randomize(1337u + (uint32_t)i); // stable random per index + // + // scene.Add(childCube); + // } // testCube->AddComponent(10, 5); scene.Add(testCube); + + // const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg"); + const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png"); + auto skyboxIDs = gfxDevice.loadImageFromFile(testimgpath); + + const auto vertShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert"); + const auto fragShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag"); + + skyboxCubemap = std::make_unique(); + skyboxCubemap->LoadCubeMap(skyboxID.generic_string()); + skyboxCubemap->InitCubemapPipeline(vertShaderPath.generic_string(), fragShaderPath.generic_string()); + skyboxCubemap->CreateCubeMap(); + + renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID()); + // skyboxCubemap->CreateCubeMap(); } void LightKeeper::customUpdate(float dt) {