From 85cf2fdfc8cc44da2cad5acb554370e81c0262f7 Mon Sep 17 00:00:00 2001 From: jyxiong Date: Sat, 13 Dec 2025 17:56:37 +0800 Subject: [PATCH 1/4] feat: use frame graph --- example/damaged_helmet/color_pass.cpp | 239 ++++++++++ example/damaged_helmet/color_pass.h | 53 +++ example/damaged_helmet/final_pass.cpp | 85 ++++ example/damaged_helmet/final_pass.h | 34 ++ example/damaged_helmet/main.cpp | 475 ++------------------ example/damaged_helmet/mesh_data.h | 17 + example/damaged_helmet/renderer.cpp | 230 ++++++++++ example/damaged_helmet/renderer.h | 112 +++++ example/damaged_helmet/screen_quad.cpp | 46 -- example/damaged_helmet/screen_quad.h | 23 - example/frame_graph/main.cpp | 8 +- source/paimon/core/fg/frame_graph_texture.h | 3 + source/paimon/core/fg/resource_entry.h | 6 +- 13 files changed, 811 insertions(+), 520 deletions(-) create mode 100644 example/damaged_helmet/color_pass.cpp create mode 100644 example/damaged_helmet/color_pass.h create mode 100644 example/damaged_helmet/final_pass.cpp create mode 100644 example/damaged_helmet/final_pass.h create mode 100644 example/damaged_helmet/mesh_data.h create mode 100644 example/damaged_helmet/renderer.cpp create mode 100644 example/damaged_helmet/renderer.h delete mode 100644 example/damaged_helmet/screen_quad.cpp delete mode 100644 example/damaged_helmet/screen_quad.h diff --git a/example/damaged_helmet/color_pass.cpp b/example/damaged_helmet/color_pass.cpp new file mode 100644 index 0000000..4c67ca9 --- /dev/null +++ b/example/damaged_helmet/color_pass.cpp @@ -0,0 +1,239 @@ +#include "color_pass.h" + +#include +#include + +#include "paimon/core/fg/frame_graph_texture.h" +#include "paimon/core/log_system.h" +#include "paimon/rendering/shader_manager.h" + +namespace paimon { + +// Forward declarations of UBO structures +struct TransformUBO { + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 projection; +}; + +struct MaterialUBO { + alignas(16) glm::vec4 baseColorFactor; + alignas(16) glm::vec3 emissiveFactor; + alignas(4) float metallicFactor; + alignas(4) float roughnessFactor; + alignas(4) float _padding[3]; +}; + +struct LightingUBO { + alignas(16) glm::vec3 lightPos; + alignas(4) float _padding1; + alignas(16) glm::vec3 viewPos; + alignas(4) float _padding2; +}; + +ColorPass::ColorPass(RenderContext &renderContext, const std::filesystem::path &assetPath) + : m_renderContext(renderContext), m_assetPath(assetPath) { + + auto shaderPath = m_assetPath / "shader"; + auto &shaderManager = ShaderManager::getInstance(); + + // Get shader programs + auto vertexProgram = shaderManager.getShaderProgram( + "damaged_helmet.vert", GL_VERTEX_SHADER); + auto fragmentProgram = shaderManager.getShaderProgram( + "damaged_helmet.frag", GL_FRAGMENT_SHADER); + + if (!vertexProgram || !fragmentProgram) { + LOG_ERROR("Failed to load shader programs"); + return; + } + + // Create graphics pipeline + GraphicsPipelineCreateInfo pipelineInfo; + pipelineInfo.shaderStages = { + {GL_VERTEX_SHADER_BIT, vertexProgram.get()}, + {GL_FRAGMENT_SHADER_BIT, fragmentProgram.get()}, + }; + + // Configure depth testing + pipelineInfo.state.depthStencil.depthTestEnable = true; + pipelineInfo.state.depthStencil.depthWriteEnable = true; + pipelineInfo.state.depthStencil.depthCompareOp = GL_LESS; + + // Disable face culling for debugging + pipelineInfo.state.rasterization.cullMode = GL_BACK; + + // VertexInputState + pipelineInfo.state.vertexInput.bindings = { + {.binding = 0, .stride = sizeof(glm::vec3)}, // Position + {.binding = 1, .stride = sizeof(glm::vec3)}, // Normal + {.binding = 2, .stride = sizeof(glm::vec2)}, // TexCoord + }; + pipelineInfo.state.vertexInput.attributes = { + {.location = 0, .binding = 0, .format = GL_FLOAT, .size = 3, .offset = 0}, + {.location = 1, .binding = 1, .format = GL_FLOAT, .size = 3, .offset = 0}, + {.location = 2, .binding = 2, .format = GL_FLOAT, .size = 2, .offset = 0}, + }; + + m_pipeline = std::make_unique(pipelineInfo); + if (!m_pipeline->validate()) { + LOG_ERROR("Failed to validate graphics pipeline"); + return; + } + + // Create sampler + m_sampler = std::make_unique(); + m_sampler->set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + m_sampler->set(GL_TEXTURE_MAG_FILTER, GL_LINEAR); + m_sampler->set(GL_TEXTURE_WRAP_S, GL_REPEAT); + m_sampler->set(GL_TEXTURE_WRAP_T, GL_REPEAT); +} + +NodeId ColorPass::registerPass( + FrameGraph &fg, + const glm::ivec2 &size, + const std::vector &meshDataList, + const std::map, Texture*> &textureMap, + Buffer *transformUBO, + Buffer *materialUBO, + Buffer *lightingUBO) { + + NodeId colorOutput; + NodeId depthOutput; + + auto &passData = fg.create_pass( + "ColorPass", + [&](FrameGraph::Builder &builder, Data &data) { + // Create color target + FrameGraphTexture::Descriptor colorDesc; + colorDesc.target = GL_TEXTURE_2D; + colorDesc.width = size.x; + colorDesc.height = size.y; + colorDesc.format = GL_RGBA8; + data.colorOutput = builder.create("ColorTarget", colorDesc); + + // Create depth target + FrameGraphTexture::Descriptor depthDesc; + depthDesc.target = GL_TEXTURE_2D; + depthDesc.width = size.x; + depthDesc.height = size.y; + depthDesc.format = GL_DEPTH_COMPONENT32; + data.depthOutput = builder.create("DepthTarget", depthDesc); + + // Mark as write targets + builder.write(data.colorOutput); + builder.write(data.depthOutput); + + // Capture NodeIds for execute phase + colorOutput = data.colorOutput; + depthOutput = data.depthOutput; + }, + [&](FrameGraphResources &resources, void *context) { + // Get textures from frame graph + auto &colorTexture = fg.get(colorOutput); + auto &depthTexture = fg.get(depthOutput); + + // Setup rendering info for FBO + RenderingInfo renderingInfo; + renderingInfo.renderAreaOffset = {0, 0}; + renderingInfo.renderAreaExtent = {size.x, size.y}; + + // Setup color attachment + renderingInfo.colorAttachments.emplace_back( + *colorTexture.getTexture(), + AttachmentLoadOp::Clear, + AttachmentStoreOp::Store, + ClearValue::Color(0.1f, 0.1f, 0.1f, 1.0f)); + + // Setup depth attachment + renderingInfo.depthAttachment.emplace( + *depthTexture.getTexture(), + AttachmentLoadOp::Clear, + AttachmentStoreOp::Store, + ClearValue::DepthStencil(1.0f, 0)); + + // Begin rendering to FBO + m_renderContext.beginRendering(renderingInfo); + + // Bind pipeline + m_renderContext.bindPipeline(*m_pipeline); + + // Set viewport + m_renderContext.setViewport(0, 0, size.x, size.y); + + // Render each mesh + for (const auto &meshData : meshDataList) { + m_renderContext.bindVertexBuffer(0, meshData.position_buffer, 0, sizeof(glm::vec3)); + m_renderContext.bindVertexBuffer(1, meshData.normal_buffer, 0, sizeof(glm::vec3)); + m_renderContext.bindVertexBuffer(2, meshData.texcoord_buffer, 0, sizeof(glm::vec2)); + m_renderContext.bindIndexBuffer(meshData.index_buffer, GL_UNSIGNED_INT); + + // Update material UBO and bind textures + if (meshData.material) { + const auto &mat = meshData.material; + const auto &pbr = mat->pbr_metallic_roughness; + + // Prepare material data + MaterialUBO materialData; + materialData.baseColorFactor = pbr.base_color_factor; + materialData.emissiveFactor = mat->emissive_factor; + materialData.metallicFactor = pbr.metallic_factor; + materialData.roughnessFactor = pbr.roughness_factor; + + // Update material UBO + if (materialUBO) { + materialUBO->set_sub_data(0, sizeof(MaterialUBO), &materialData); + } + + // Bind textures with sampler + // Base color (unit 0) + Texture *tex = nullptr; + if (pbr.base_color_texture && textureMap.count(pbr.base_color_texture)) { + tex = textureMap.at(pbr.base_color_texture); + m_renderContext.bindTexture(0, *tex, *m_sampler); + } + + // Metallic roughness (unit 1) + tex = nullptr; + if (pbr.metallic_roughness_texture && textureMap.count(pbr.metallic_roughness_texture)) { + tex = textureMap.at(pbr.metallic_roughness_texture); + m_renderContext.bindTexture(1, *tex, *m_sampler); + } + + // Normal (unit 2) + tex = nullptr; + if (mat->normal_texture && textureMap.count(mat->normal_texture)) { + tex = textureMap.at(mat->normal_texture); + m_renderContext.bindTexture(2, *tex, *m_sampler); + } + + // Emissive (unit 3) + tex = nullptr; + if (mat->emissive_texture && textureMap.count(mat->emissive_texture)) { + tex = textureMap.at(mat->emissive_texture); + m_renderContext.bindTexture(3, *tex, *m_sampler); + } + + // Occlusion (unit 4) + tex = nullptr; + if (mat->occlusion_texture && textureMap.count(mat->occlusion_texture)) { + tex = textureMap.at(mat->occlusion_texture); + m_renderContext.bindTexture(4, *tex, *m_sampler); + } + } + + // Draw indexed + if (meshData.index_count > 0) { + m_renderContext.drawElements(meshData.index_count, nullptr); + } + } + + // End rendering to FBO + m_renderContext.endRendering(); + }); + + // Return the color output NodeId + return passData.colorOutput; +} + +} // namespace paimon diff --git a/example/damaged_helmet/color_pass.h b/example/damaged_helmet/color_pass.h new file mode 100644 index 0000000..01c4ff3 --- /dev/null +++ b/example/damaged_helmet/color_pass.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include + +#include "paimon/core/fg/frame_graph.h" +#include "paimon/opengl/buffer.h" +#include "paimon/opengl/sampler.h" +#include "paimon/opengl/texture.h" +#include "paimon/rendering/graphics_pipeline.h" +#include "paimon/rendering/render_context.h" + +#include "mesh_data.h" + +namespace paimon { + +// UBO structures (shared from renderer) +struct TransformUBO; +struct MaterialUBO; +struct LightingUBO; + +class ColorPass { +public: + struct Data { + NodeId colorOutput; + NodeId depthOutput; + }; + + ColorPass(RenderContext &renderContext, const std::filesystem::path &assetPath); + + // Register ColorPass to the frame graph + // Returns the color output NodeId + NodeId registerPass( + FrameGraph &fg, + const glm::ivec2 &size, + const std::vector &meshDataList, + const std::map, Texture*> &textureMap, + Buffer *transformUBO, + Buffer *materialUBO, + Buffer *lightingUBO); + +private: + RenderContext &m_renderContext; + std::filesystem::path m_assetPath; + + // Graphics pipeline + std::unique_ptr m_pipeline; + std::unique_ptr m_sampler; +}; + +} // namespace paimon diff --git a/example/damaged_helmet/final_pass.cpp b/example/damaged_helmet/final_pass.cpp new file mode 100644 index 0000000..29c41d3 --- /dev/null +++ b/example/damaged_helmet/final_pass.cpp @@ -0,0 +1,85 @@ +#include "final_pass.h" + +#include + +#include "paimon/core/fg/frame_graph_texture.h" +#include "paimon/core/log_system.h" +#include "paimon/rendering/rendering_info.h" +#include "paimon/rendering/shader_manager.h" + +namespace paimon { + +FinalPass::FinalPass(RenderContext &renderContext, const std::filesystem::path &assetPath) + : m_renderContext(renderContext), m_assetPath(assetPath) { + + auto shaderPath = m_assetPath / "shader"; + auto &shaderManager = ShaderManager::getInstance(); + + // Get shader programs + auto screenVertProgram = shaderManager.getShaderProgram( + "screen_quad.vert", GL_VERTEX_SHADER); + auto screenFragProgram = shaderManager.getShaderProgram( + "screen_quad.frag", GL_FRAGMENT_SHADER); + + if (!screenVertProgram || !screenFragProgram) { + LOG_ERROR("Failed to load screen quad shader programs"); + return; + } + + // Create screen quad pipeline + GraphicsPipelineCreateInfo screenPipelineInfo; + screenPipelineInfo.shaderStages = { + {GL_VERTEX_SHADER_BIT, screenVertProgram.get()}, + {GL_FRAGMENT_SHADER_BIT, screenFragProgram.get()}, + }; + screenPipelineInfo.state.depthStencil.depthTestEnable = false; + screenPipelineInfo.state.depthStencil.depthWriteEnable = false; + m_pipeline = std::make_unique(screenPipelineInfo); + + // Create sampler + m_sampler = std::make_unique(); + m_sampler->set(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_sampler->set(GL_TEXTURE_MAG_FILTER, GL_LINEAR); + m_sampler->set(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + m_sampler->set(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void FinalPass::registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 &size) { + + fg.create_pass( + "FinalPass", + [colorInput](FrameGraph::Builder &builder, Data &data) { + // Read color input from previous pass + data.colorInput = colorInput; + builder.read(data.colorInput); + }, + [&](FrameGraphResources &resources, void *context) { + // Get source texture from frame graph + auto &texture = fg.get(colorInput); + + // Setup rendering to default framebuffer + SwapchainRenderingInfo renderingInfo; + renderingInfo.renderAreaOffset = {0, 0}; + renderingInfo.renderAreaExtent = {size.x, size.y}; + + // Begin rendering to default framebuffer + m_renderContext.beginSwapchainRendering(renderingInfo); + + // Bind screen quad pipeline + m_renderContext.bindPipeline(*m_pipeline); + + // Bind texture and sampler + m_renderContext.bindTexture(0, *texture.getTexture(), *m_sampler); + + // Set viewport + m_renderContext.setViewport(0, 0, size.x, size.y); + + // Draw fullscreen quad (without vertex buffers, generated in vertex shader) + m_renderContext.drawArrays(0, 3); + + // End rendering + m_renderContext.endSwapchainRendering(); + }); +} + +} // namespace paimon diff --git a/example/damaged_helmet/final_pass.h b/example/damaged_helmet/final_pass.h new file mode 100644 index 0000000..357f0a8 --- /dev/null +++ b/example/damaged_helmet/final_pass.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +#include "paimon/core/fg/frame_graph.h" +#include "paimon/opengl/sampler.h" +#include "paimon/rendering/graphics_pipeline.h" +#include "paimon/rendering/render_context.h" + +namespace paimon { + +class FinalPass { +public: + struct Data { + NodeId colorInput; + }; + + FinalPass(RenderContext &renderContext, const std::filesystem::path &assetPath); + + // Register FinalPass to the frame graph + void registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 &size); + +private: + RenderContext &m_renderContext; + std::filesystem::path m_assetPath; + + // Screen quad pipeline + std::unique_ptr m_pipeline; + std::unique_ptr m_sampler; +}; + +} // namespace paimon diff --git a/example/damaged_helmet/main.cpp b/example/damaged_helmet/main.cpp index a89e487..645b50b 100644 --- a/example/damaged_helmet/main.cpp +++ b/example/damaged_helmet/main.cpp @@ -1,101 +1,16 @@ #include -#include #include -#include #include -#include -#include #include "paimon/app/window.h" -#include "paimon/core/io/gltf.h" #include "paimon/core/log_system.h" -#include "paimon/opengl/buffer.h" -#include "paimon/opengl/sampler.h" -#include "paimon/opengl/texture.h" -#include "paimon/rendering/graphics_pipeline.h" -#include "paimon/rendering/render_context.h" -#include "paimon/rendering/rendering_info.h" #include "paimon/rendering/shader_manager.h" -#include "screen_quad.h" +#include "renderer.h" using namespace paimon; -namespace { - -// UBO structures matching shader layout -struct TransformUBO { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 projection; -}; - -struct MaterialUBO { - alignas(16) glm::vec4 baseColorFactor; - alignas(16) glm::vec3 emissiveFactor; - alignas(4) float metallicFactor; - alignas(4) float roughnessFactor; - alignas(4) float _padding[3]; // alignment -}; - -struct LightingUBO { - alignas(16) glm::vec3 lightPos; - alignas(4) float _padding1; - alignas(16) glm::vec3 viewPos; - alignas(4) float _padding2; -}; - -// Camera state -struct Camera { - glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f); - glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f); - glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); - float yaw = -90.0f; - float pitch = 0.0f; - float fov = 45.0f; -}; - -Camera g_camera; -glm::ivec2 g_size = {1280, 720}; - -// Helper function to create OpenGL texture from sg::Image -Texture createTextureFromImage(const std::shared_ptr &image) { - Texture texture(GL_TEXTURE_2D); - - if (!image || image->data.empty()) { - // Create a default 1x1 white texture - std::vector white = {255, 255, 255, 255}; - texture.set_storage_2d(1, GL_RGBA8, 1, 1); - texture.set_sub_image_2d(0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, - white.data()); - return texture; - } - - GLenum internalFormat = GL_RGBA8; - GLenum format = GL_RGBA; - - if (image->components == 1) { - internalFormat = GL_R8; - format = GL_RED; - } else if (image->components == 2) { - internalFormat = GL_RG8; - format = GL_RG; - } else if (image->components == 3) { - internalFormat = GL_RGB8; - format = GL_RGB; - } - - texture.set_storage_2d(1, internalFormat, image->width, image->height); - texture.set_sub_image_2d(0, 0, 0, image->width, image->height, format, - GL_UNSIGNED_BYTE, image->data.data()); - texture.generate_mipmap(); - - return texture; -} - -} // namespace - int main() { LogSystem::init(); LOG_INFO("=== DamagedHelmet PBR Example ==="); @@ -105,6 +20,9 @@ int main() { LOG_INFO(" - Use GraphicsPipeline + RenderContext for rendering"); LOG_INFO(""); + // Window dimensions + glm::ivec2 windowSize = {1280, 720}; + // Create window auto window = Window::create(WindowConfig{ .title = "Paimon - DamagedHelmet PBR", @@ -115,215 +33,41 @@ int main() { .profile = ContextProfile::Core, .debug = false, }, - .width = static_cast(g_size.x), - .height = static_cast(g_size.y), + .width = static_cast(windowSize.x), + .height = static_cast(windowSize.y), .resizable = true, .visible = true, .vsync = true, }); - // Setup shader manager - auto assetPath = std::filesystem::current_path().parent_path().parent_path()/ "asset"; - auto shaderPath = assetPath / "shader"; - - auto& shaderManager = ShaderManager::getInstance(); - shaderManager.load(shaderPath); - - // Get shader programs for main rendering (separable programs for pipeline) - auto vertex_program_ptr = shaderManager.getShaderProgram( - "damaged_helmet.vert", GL_VERTEX_SHADER); - auto fragment_program_ptr = shaderManager.getShaderProgram( - "damaged_helmet.frag", GL_FRAGMENT_SHADER); - - if (!vertex_program_ptr || !fragment_program_ptr) { - LOG_ERROR("Failed to load main shader programs"); - return -1; - } - // Load glTF model - GltfLoader loader; - sg::Scene scene; + // Get asset path + auto assetPath = std::filesystem::current_path().parent_path().parent_path() / "asset"; - auto assetPModelath = assetPath / "model"; - - std::string model_path = - (assetPModelath / "DamagedHelmet/glTF/DamagedHelmet.gltf").string(); - if (!loader.LoadFromFile(model_path, scene)) { - LOG_ERROR("Failed to load glTF model: {}", loader.GetError()); + // Create and initialize renderer + Renderer renderer; + if (!renderer.initialize(windowSize, assetPath)) { + LOG_ERROR("Failed to initialize renderer"); return -1; } - if (!loader.GetWarning().empty()) { - LOG_WARN("glTF warnings: {}", loader.GetWarning()); - } - - // Create graphics pipeline - GraphicsPipelineCreateInfo pipelineInfo; - pipelineInfo.shaderStages = { - {GL_VERTEX_SHADER_BIT, vertex_program_ptr.get()}, - {GL_FRAGMENT_SHADER_BIT, fragment_program_ptr.get()}, - }; - - // Configure depth testing - pipelineInfo.state.depthStencil.depthTestEnable = true; - pipelineInfo.state.depthStencil.depthWriteEnable = true; - pipelineInfo.state.depthStencil.depthCompareOp = GL_LESS; - - // Disable face culling for debugging - pipelineInfo.state.rasterization.cullMode = GL_BACK; - - // VertexInputState - configure vertex attribute layout - // We use separate bindings for each attribute to allow independent buffer binding - pipelineInfo.state.vertexInput.bindings = { - {.binding = 0, .stride = sizeof(glm::vec3)}, // Position - {.binding = 1, .stride = sizeof(glm::vec3)}, // Normal - {.binding = 2, .stride = sizeof(glm::vec2)}, // TexCoord - }; - pipelineInfo.state.vertexInput.attributes = { - { - .location = 0, - .binding = 0, - .format = GL_FLOAT, - .size = 3, // vec3 - .offset = 0, - }, - { - .location = 1, - .binding = 1, - .format = GL_FLOAT, - .size = 3, // vec3 - .offset = 0, - }, - { - .location = 2, - .binding = 2, - .format = GL_FLOAT, - .size = 2, // vec2 - .offset = 0, - }, - }; - - auto pipeline = GraphicsPipeline(pipelineInfo); - if (!pipeline.validate()) { - LOG_ERROR("Failed to validate graphics pipeline"); + // Load glTF model + auto modelPath = assetPath / "model" / "DamagedHelmet/glTF/DamagedHelmet.gltf"; + if (!renderer.loadModel(modelPath.string())) { + LOG_ERROR("Failed to load model"); return -1; } - // Process all meshes in the scene - struct MeshData { - // VertexArray vao; - Buffer position_buffer; - Buffer normal_buffer; - Buffer texcoord_buffer; - Buffer index_buffer; - size_t index_count; - std::shared_ptr material; - }; - - std::vector mesh_data_list; - - for (const auto &mesh : scene.meshes) { - for (const auto &primitive : mesh->primitives) { - MeshData mesh_data; - - // Setup buffers - if (primitive.attributes.HasPositions()) { - mesh_data.position_buffer.set_storage( - sizeof(glm::vec3) * primitive.attributes.positions.size(), - primitive.attributes.positions.data(), GL_DYNAMIC_STORAGE_BIT); - } - - if (primitive.attributes.HasNormals()) { - mesh_data.normal_buffer.set_storage( - sizeof(glm::vec3) * primitive.attributes.normals.size(), - primitive.attributes.normals.data(), GL_DYNAMIC_STORAGE_BIT); - } - - if (primitive.attributes.HasTexCoords0()) { - mesh_data.texcoord_buffer.set_storage( - sizeof(glm::vec2) * primitive.attributes.texcoords_0.size(), - primitive.attributes.texcoords_0.data(), GL_DYNAMIC_STORAGE_BIT); - } - - if (primitive.HasIndices()) { - mesh_data.index_buffer.set_storage( - sizeof(uint32_t) * primitive.indices.size(), - primitive.indices.data(), GL_DYNAMIC_STORAGE_BIT); - mesh_data.index_count = primitive.indices.size(); - } - - mesh_data.material = primitive.material; - mesh_data_list.push_back(std::move(mesh_data)); - } - } - - // Create textures from materials - std::map, std::unique_ptr> texture_map; - - for (const auto &tex_pair : scene.textures) { - if (tex_pair && tex_pair->image) { - texture_map[tex_pair] = - std::make_unique(createTextureFromImage(tex_pair->image)); - } - } - - // Create default textures - Texture default_white = createTextureFromImage(nullptr); - std::vector normal_data = {128, 128, 255, - 255}; // default normal (0, 0, 1) - Texture default_normal(GL_TEXTURE_2D); - default_normal.set_storage_2d(1, GL_RGBA8, 1, 1); - default_normal.set_sub_image_2d(0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, - normal_data.data()); - - std::vector mr_data = { - 0, 255, 0, 255}; // default: no AO, full roughness, no metallic - Texture default_metallic_roughness(GL_TEXTURE_2D); - default_metallic_roughness.set_storage_2d(1, GL_RGBA8, 1, 1); - default_metallic_roughness.set_sub_image_2d(0, 0, 0, 1, 1, GL_RGBA, - GL_UNSIGNED_BYTE, mr_data.data()); - - std::vector black_data = {0, 0, 0, 255}; - Texture default_black(GL_TEXTURE_2D); - default_black.set_storage_2d(1, GL_RGBA8, 1, 1); - default_black.set_sub_image_2d(0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, - black_data.data()); - - // Create sampler - Sampler sampler; - sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - sampler.set(GL_TEXTURE_WRAP_S, GL_REPEAT); - sampler.set(GL_TEXTURE_WRAP_T, GL_REPEAT); - - // Create UBOs - Buffer transform_ubo; - transform_ubo.set_storage(sizeof(TransformUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); - transform_ubo.bind_base(GL_UNIFORM_BUFFER, 0); - - Buffer material_ubo; - material_ubo.set_storage(sizeof(MaterialUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); - material_ubo.bind_base(GL_UNIFORM_BUFFER, 1); - - Buffer lighting_ubo; - lighting_ubo.set_storage(sizeof(LightingUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); - lighting_ubo.bind_base(GL_UNIFORM_BUFFER, 2); - - // Create FBO textures for color and depth attachments - Texture fbo_color_texture(GL_TEXTURE_2D); - fbo_color_texture.set_storage_2d(1, GL_RGBA8, g_size.x, g_size.y); - - Texture fbo_depth_texture(GL_TEXTURE_2D); - fbo_depth_texture.set_storage_2d(1, GL_DEPTH_COMPONENT32, g_size.x, g_size.y); - - // Create screen quad (it will load shaders internally from singleton) - ScreenQuad screen_quad; + // Setup camera + Camera camera; + camera.position = glm::vec3(0.0f, 0.0f, 3.0f); + camera.fov = 45.0f; + renderer.setCamera(camera); - // Create render context - RenderContext ctx; + // Setup lighting + renderer.setLightPosition(glm::vec3(5.0f, 5.0f, 5.0f)); LOG_INFO("Setup complete, entering render loop"); - float rotation = 0.0f; float lastFrame = 0.0f; // Main render loop @@ -336,180 +80,17 @@ int main() { // Input window->pollEvents(); - // Auto-rotate the model - rotation += deltaTime * 30.0f; // 30 degrees per second - - // Setup transformation matrices - TransformUBO transformData; - transformData.model = glm::mat4(1.0f); - transformData.model = glm::rotate(transformData.model, - glm::radians(rotation), - glm::vec3(0.0f, 1.0f, 0.0f)); - transformData.view = glm::lookAt(g_camera.position, - glm::vec3(0.0f, 0.0f, 0.0f), - g_camera.up); - transformData.projection = glm::perspective(glm::radians(g_camera.fov), - static_cast(g_size.x) / g_size.y, - 0.1f, 100.0f); - - // Update transform UBO - transform_ubo.set_sub_data(0, sizeof(TransformUBO), &transformData); - - // Setup lighting - LightingUBO lightingData; - lightingData.lightPos = glm::vec3(5.0f, 5.0f, 5.0f); - lightingData.viewPos = g_camera.position; - - // Update lighting UBO - lighting_ubo.set_sub_data(0, sizeof(LightingUBO), &lightingData); - - // ===== First Pass: Render to FBO ===== - - { - // Setup rendering info for FBO - RenderingInfo renderingInfo; - renderingInfo.renderAreaOffset = {0, 0}; - renderingInfo.renderAreaExtent = {g_size.x, g_size.y}; - - // Setup color attachment - renderingInfo.colorAttachments.emplace_back( - fbo_color_texture, - AttachmentLoadOp::Clear, - AttachmentStoreOp::Store, - ClearValue::Color(0.1f, 0.1f, 0.1f, 1.0f) - ); - - // Setup depth attachment - renderingInfo.depthAttachment.emplace( - fbo_depth_texture, - AttachmentLoadOp::Clear, - AttachmentStoreOp::Store, - ClearValue::DepthStencil(1.0f, 0) - ); - - // Begin rendering to FBO - ctx.beginRendering(renderingInfo); - - // Bind pipeline (this applies depth test and other states) - ctx.bindPipeline(pipeline); - - // Set viewport - ctx.setViewport(0, 0, g_size.x, g_size.y); - - // Render each mesh - int meshCount = 0; - for (const auto &mesh_data : mesh_data_list) { - - ctx.bindVertexBuffer(0, mesh_data.position_buffer, 0, sizeof(glm::vec3)); - ctx.bindVertexBuffer(1, mesh_data.normal_buffer, 0, sizeof(glm::vec3)); - ctx.bindVertexBuffer(2, mesh_data.texcoord_buffer, 0, sizeof(glm::vec2)); - ctx.bindIndexBuffer(mesh_data.index_buffer, GL_UNSIGNED_INT); - - // Update material UBO and bind textures - if (mesh_data.material) { - const auto &mat = mesh_data.material; - const auto &pbr = mat->pbr_metallic_roughness; - - // Prepare material data - MaterialUBO materialData; - materialData.baseColorFactor = pbr.base_color_factor; - materialData.emissiveFactor = mat->emissive_factor; - materialData.metallicFactor = pbr.metallic_factor; - materialData.roughnessFactor = pbr.roughness_factor; - - // Update material UBO - material_ubo.set_sub_data(0, sizeof(MaterialUBO), &materialData); - - // Bind textures with sampler - // Base color (unit 0) - if (pbr.base_color_texture && - texture_map.count(pbr.base_color_texture)) { - texture_map.at(pbr.base_color_texture)->bind(0); - } else { - default_white.bind(0); - } - sampler.bind(0); - - // Metallic roughness (unit 1) - if (pbr.metallic_roughness_texture && - texture_map.count(pbr.metallic_roughness_texture)) { - texture_map.at(pbr.metallic_roughness_texture)->bind(1); - } else { - default_metallic_roughness.bind(1); - } - sampler.bind(1); - - // Normal (unit 2) - if (mat->normal_texture && texture_map.count(mat->normal_texture)) { - texture_map.at(mat->normal_texture)->bind(2); - } else { - default_normal.bind(2); - } - sampler.bind(2); - - // Emissive (unit 3) - if (mat->emissive_texture && texture_map.count(mat->emissive_texture)) { - texture_map.at(mat->emissive_texture)->bind(3); - } else { - default_black.bind(3); - } - sampler.bind(3); - - // Occlusion (unit 4) - if (mat->occlusion_texture && - texture_map.count(mat->occlusion_texture)) { - texture_map.at(mat->occlusion_texture)->bind(4); - } else { - default_white.bind(4); - } - sampler.bind(4); - } - - // Draw indexed - if (mesh_data.index_count > 0) { - ctx.drawElements(mesh_data.index_count, nullptr); - meshCount++; - } - } - - // End rendering to FBO - ctx.endRendering(); - - } - - // ===== Second Pass: Render FBO texture to screen ===== - { - // Setup swapchain rendering info - SwapchainRenderingInfo swapchainInfo; - swapchainInfo.renderAreaOffset = {0, 0}; - swapchainInfo.renderAreaExtent = {g_size.x, g_size.y}; - swapchainInfo.clearColor = ClearValue::Color(0.0f, 0.0f, 0.0f, 1.0f); - swapchainInfo.clearDepth = 1.0f; - swapchainInfo.clearStencil = 0; - - // Begin swapchain rendering - ctx.beginSwapchainRendering(swapchainInfo); - - // Disable depth test for screen quad - glDisable(GL_DEPTH_TEST); - - // Use screen quad to render FBO texture - screen_quad.draw(ctx, fbo_color_texture); - - // End swapchain rendering - ctx.endSwapchainRendering(); - } - - + // Render frame + renderer.render(deltaTime); // Swap buffers window->swapBuffers(); } LOG_INFO("Shutting down"); - + // Clear shader resources before OpenGL context is destroyed - shaderManager.clear(); - + ShaderManager::getInstance().clear(); + return 0; } diff --git a/example/damaged_helmet/mesh_data.h b/example/damaged_helmet/mesh_data.h new file mode 100644 index 0000000..25d3255 --- /dev/null +++ b/example/damaged_helmet/mesh_data.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "paimon/core/sg/material.h" +#include "paimon/opengl/buffer.h" + +namespace paimon { +struct MeshData { + Buffer position_buffer; + Buffer normal_buffer; + Buffer texcoord_buffer; + Buffer index_buffer; + size_t index_count; + std::shared_ptr material; +}; +} // namespace paimon \ No newline at end of file diff --git a/example/damaged_helmet/renderer.cpp b/example/damaged_helmet/renderer.cpp new file mode 100644 index 0000000..72a5c0b --- /dev/null +++ b/example/damaged_helmet/renderer.cpp @@ -0,0 +1,230 @@ +#include "renderer.h" + +#include +#include +#include + +#include "paimon/core/fg/frame_graph.h" +#include "paimon/core/fg/transient_resources.h" +#include "paimon/core/log_system.h" +#include "paimon/core/io/gltf.h" +#include "paimon/rendering/shader_manager.h" + +#include "color_pass.h" +#include "final_pass.h" + +namespace paimon { + +Renderer::Renderer() : m_camera{}, m_size{1280, 720}, m_rotation(0.0f) { + m_renderContext = std::make_unique(); +} + +bool Renderer::initialize(const glm::ivec2 &size, const std::filesystem::path &assetPath) { + m_size = size; + m_assetPath = assetPath; + + auto shaderPath = m_assetPath / "shader"; + auto &shaderManager = ShaderManager::getInstance(); + shaderManager.load(shaderPath); + + // Create UBOs + m_transformUBO = std::make_unique(); + m_transformUBO->set_storage(sizeof(TransformUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); + m_transformUBO->bind_base(GL_UNIFORM_BUFFER, 0); + + m_materialUBO = std::make_unique(); + m_materialUBO->set_storage(sizeof(MaterialUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); + m_materialUBO->bind_base(GL_UNIFORM_BUFFER, 1); + + m_lightingUBO = std::make_unique(); + m_lightingUBO->set_storage(sizeof(LightingUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); + m_lightingUBO->bind_base(GL_UNIFORM_BUFFER, 2); + + // Create passes + m_colorPass = std::make_unique(*m_renderContext, m_assetPath); + m_finalPass = std::make_unique(*m_renderContext, m_assetPath); + + LOG_INFO("Renderer initialized successfully"); + return true; +} + +bool Renderer::loadModel(const std::string &modelPath) { + GltfLoader loader; + + if (!loader.LoadFromFile(modelPath, m_scene)) { + LOG_ERROR("Failed to load glTF model: {}", loader.GetError()); + return false; + } + + if (!loader.GetWarning().empty()) { + LOG_WARN("glTF warnings: {}", loader.GetWarning()); + } + + setupModel(); + LOG_INFO("Model loaded successfully: {}", modelPath); + return true; +} + +void Renderer::setupModel() { + m_meshDataList.clear(); + m_textureMap.clear(); + + // Process all meshes in the scene + std::vector colorPassMeshData; + for (const auto &mesh : m_scene.meshes) { + for (const auto &primitive : mesh->primitives) { + MeshData meshData; + + // Setup buffers + if (primitive.attributes.HasPositions()) { + meshData.position_buffer.set_storage( + sizeof(glm::vec3) * primitive.attributes.positions.size(), + primitive.attributes.positions.data(), GL_DYNAMIC_STORAGE_BIT); + } + + if (primitive.attributes.HasNormals()) { + meshData.normal_buffer.set_storage( + sizeof(glm::vec3) * primitive.attributes.normals.size(), + primitive.attributes.normals.data(), GL_DYNAMIC_STORAGE_BIT); + } + + if (primitive.attributes.HasTexCoords0()) { + meshData.texcoord_buffer.set_storage( + sizeof(glm::vec2) * primitive.attributes.texcoords_0.size(), + primitive.attributes.texcoords_0.data(), GL_DYNAMIC_STORAGE_BIT); + } + + if (primitive.HasIndices()) { + meshData.index_buffer.set_storage( + sizeof(uint32_t) * primitive.indices.size(), + primitive.indices.data(), GL_DYNAMIC_STORAGE_BIT); + meshData.index_count = primitive.indices.size(); + } + + meshData.material = primitive.material; + colorPassMeshData.push_back(std::move(meshData)); + } + } + + // Save mesh data for frame graph registration + m_meshDataList = std::move(colorPassMeshData); + + // Create textures from materials + m_textureMap.clear(); + for (const auto &texPair : m_scene.textures) { + if (texPair && texPair->image) { + m_textureMap[texPair] = std::make_unique(createTextureFromImage(texPair->image)); + } + } +} + +void Renderer::setCamera(const Camera &camera) { + m_camera = camera; +} + +void Renderer::setLightPosition(const glm::vec3 &lightPos) { + m_lightPos = lightPos; +} + +void Renderer::render(float deltaTime) { + FrameGraph fg; + TransientResources tr(*m_renderContext); + + // Auto-rotate the model + m_rotation += deltaTime * 30.0f; // 30 degrees per second + + // Setup transformation matrices + TransformUBO transformData; + transformData.model = glm::mat4(1.0f); + transformData.model = glm::rotate(transformData.model, + glm::radians(m_rotation), + glm::vec3(0.0f, 1.0f, 0.0f)); + transformData.view = glm::lookAt(m_camera.position, + glm::vec3(0.0f, 0.0f, 0.0f), + m_camera.up); + transformData.projection = glm::perspective( + glm::radians(m_camera.fov), + static_cast(m_size.x) / m_size.y, + 0.1f, 100.0f); + + // Update transform UBO + m_transformUBO->set_sub_data(0, sizeof(TransformUBO), &transformData); + + // Setup lighting + LightingUBO lightingData; + lightingData.lightPos = m_lightPos; + lightingData.viewPos = m_camera.position; + + // Update lighting UBO + m_lightingUBO->set_sub_data(0, sizeof(LightingUBO), &lightingData); + + // Prepare texture map with raw pointers + std::map, Texture*> texturePtrMap; + for (const auto &[key, value] : m_textureMap) { + texturePtrMap[key] = value.get(); + } + + // Register ColorPass and get its output + NodeId colorOutput = m_colorPass->registerPass( + fg, + m_size, + m_meshDataList, + texturePtrMap, + m_transformUBO.get(), + m_materialUBO.get(), + m_lightingUBO.get()); + + // Register FinalPass using ColorPass output + m_finalPass->registerPass(fg, colorOutput, m_size); + + // Compile the frame graph + fg.compile(); + + // Execute frame graph + fg.execute(m_renderContext.get(), &tr); +} + +void Renderer::resize(const glm::ivec2 &newSize) { + if (newSize == m_size) { + return; + } + + m_size = newSize; + LOG_INFO("Renderer resized to {}x{}", m_size.x, m_size.y); +} + +Texture Renderer::createTextureFromImage(const std::shared_ptr &image) { + Texture texture(GL_TEXTURE_2D); + + if (!image || image->data.empty()) { + // Create a default 1x1 white texture + std::vector white = {255, 255, 255, 255}; + texture.set_storage_2d(1, GL_RGBA8, 1, 1); + texture.set_sub_image_2d(0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + white.data()); + return texture; + } + + GLenum internalFormat = GL_RGBA8; + GLenum format = GL_RGBA; + + if (image->components == 1) { + internalFormat = GL_R8; + format = GL_RED; + } else if (image->components == 2) { + internalFormat = GL_RG8; + format = GL_RG; + } else if (image->components == 3) { + internalFormat = GL_RGB8; + format = GL_RGB; + } + + texture.set_storage_2d(1, internalFormat, image->width, image->height); + texture.set_sub_image_2d(0, 0, 0, image->width, image->height, format, + GL_UNSIGNED_BYTE, image->data.data()); + texture.generate_mipmap(); + + return texture; +} + +} // namespace paimon diff --git a/example/damaged_helmet/renderer.h b/example/damaged_helmet/renderer.h new file mode 100644 index 0000000..9ab870a --- /dev/null +++ b/example/damaged_helmet/renderer.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include + +#include + +#include "paimon/core/fg/frame_graph.h" +#include "paimon/core/fg/transient_resources.h" +#include "paimon/core/sg/material.h" +#include "paimon/core/sg/scene.h" +#include "paimon/opengl/buffer.h" +#include "paimon/opengl/sampler.h" +#include "paimon/opengl/texture.h" +#include "paimon/rendering/graphics_pipeline.h" +#include "paimon/rendering/render_context.h" + +#include "color_pass.h" +#include "final_pass.h" + +namespace paimon { + +// UBO structures matching shader layout +struct TransformUBO { + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 projection; +}; + +struct MaterialUBO { + alignas(16) glm::vec4 baseColorFactor; + alignas(16) glm::vec3 emissiveFactor; + alignas(4) float metallicFactor; + alignas(4) float roughnessFactor; + alignas(4) float _padding[3]; // alignment +}; + +struct LightingUBO { + alignas(16) glm::vec3 lightPos; + alignas(4) float _padding1; + alignas(16) glm::vec3 viewPos; + alignas(4) float _padding2; +}; + +// Camera state +struct Camera { + glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f); + glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f); + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + float yaw = -90.0f; + float pitch = 0.0f; + float fov = 45.0f; +}; + +class Renderer { +public: + Renderer(); + ~Renderer() = default; + + // Initialize renderer with window dimensions and asset path + bool initialize(const glm::ivec2 &size, const std::filesystem::path &assetPath); + + // Load glTF model + bool loadModel(const std::string &modelPath); + + // Set camera parameters + void setCamera(const Camera &camera); + Camera &getCamera() { return m_camera; } + + // Set lighting parameters + void setLightPosition(const glm::vec3 &lightPos); + + // Render a frame + void render(float deltaTime); + + // Resize framebuffer + void resize(const glm::ivec2 &newSize); + +private: + // Helper function to create OpenGL texture from sg::Image + Texture createTextureFromImage(const std::shared_ptr &image); + + // Setup buffers and textures for loaded model + void setupModel(); + + // Scene and model data + sg::Scene m_scene; + std::vector m_meshDataList; + std::map, std::unique_ptr> m_textureMap; + + // UBOs + std::unique_ptr m_transformUBO; + std::unique_ptr m_materialUBO; + std::unique_ptr m_lightingUBO; + + // Render context + std::unique_ptr m_renderContext; + + // Frame graph passes + std::unique_ptr m_colorPass; + std::unique_ptr m_finalPass; + + // State + Camera m_camera; + glm::ivec2 m_size; + glm::vec3 m_lightPos = glm::vec3(5.0f, 5.0f, 5.0f); + float m_rotation = 0.0f; + std::filesystem::path m_assetPath; +}; + +} // namespace paimon diff --git a/example/damaged_helmet/screen_quad.cpp b/example/damaged_helmet/screen_quad.cpp deleted file mode 100644 index c3c6233..0000000 --- a/example/damaged_helmet/screen_quad.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "screen_quad.h" - -#include - -#include "paimon/core/log_system.h" -#include "paimon/rendering/shader_manager.h" - -ScreenQuad::ScreenQuad() { - // Create a minimal VAO (no vertex data needed, vertices are in shader) - // m_vao = std::make_unique(); - - // Setup sampler for texture filtering - m_sampler = std::make_unique(); - m_sampler->set(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - m_sampler->set(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - m_sampler->set(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - m_sampler->set(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Load shaders from ShaderManager singleton - auto& shaderManager = ShaderManager::getInstance(); - auto vert_program = shaderManager.getShaderProgram("screen_quad.vert", GL_VERTEX_SHADER); - auto frag_program = shaderManager.getShaderProgram("screen_quad.frag", GL_FRAGMENT_SHADER); - - if (!vert_program || !frag_program) { - LOG_ERROR("Failed to load screen quad shaders"); - return; - } - - // Create graphics pipeline - GraphicsPipelineCreateInfo pipelineInfo; - pipelineInfo.shaderStages = { - {GL_VERTEX_SHADER_BIT, vert_program.get()}, - {GL_FRAGMENT_SHADER_BIT, frag_program.get()}, - }; - pipelineInfo.state.depthStencil.depthTestEnable = false; - - m_pipeline = std::make_unique(pipelineInfo); -} - -void ScreenQuad::draw(RenderContext& ctx, const Texture &texture) { - ctx.bindPipeline(*m_pipeline); - texture.bind(6); - m_sampler->bind(6); - // Draw full-screen quad with 6 vertices (2 triangles) - ctx.drawArrays(0, 6); -} diff --git a/example/damaged_helmet/screen_quad.h b/example/damaged_helmet/screen_quad.h deleted file mode 100644 index 9aacb2e..0000000 --- a/example/damaged_helmet/screen_quad.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -#include "paimon/opengl/sampler.h" -#include "paimon/opengl/texture.h" -#include "paimon/opengl/vertex_array.h" -#include "paimon/rendering/graphics_pipeline.h" -#include "paimon/rendering/render_context.h" - -using namespace paimon; - -class ScreenQuad { -public: - ScreenQuad(); - - void draw(RenderContext& ctx, const Texture &texture); - -private: - // std::unique_ptr m_vao; - std::unique_ptr m_sampler; - std::unique_ptr m_pipeline; -}; diff --git a/example/frame_graph/main.cpp b/example/frame_graph/main.cpp index 67df2f3..778de06 100644 --- a/example/frame_graph/main.cpp +++ b/example/frame_graph/main.cpp @@ -6,6 +6,7 @@ #include #include "paimon/app/window.h" +#include "paimon/core/log_system.h" #include "paimon/core/fg/frame_graph.h" #include "paimon/core/fg/frame_graph_texture.h" #include "paimon/core/fg/transient_resources.h" @@ -388,6 +389,8 @@ void renderScene(RenderData &rd, Program &program, bool isShadowPass) { } int main() { + LogSystem::init(); + auto window = Window::create(WindowConfig{ .title = "Frame Graph Shadow Mapping Example", .format = @@ -404,6 +407,9 @@ int main() { .vsync = true, }); + RenderContext rc; + TransientResources allocator(rc); + RenderData renderData; // Setup geometry and shaders @@ -548,8 +554,6 @@ int main() { while (!window->shouldClose()) { window->pollEvents(); - RenderContext rc; - TransientResources allocator(rc); fg.execute(&rc, &allocator); window->swapBuffers(); diff --git a/source/paimon/core/fg/frame_graph_texture.h b/source/paimon/core/fg/frame_graph_texture.h index e8e1d5e..ed78d43 100644 --- a/source/paimon/core/fg/frame_graph_texture.h +++ b/source/paimon/core/fg/frame_graph_texture.h @@ -26,6 +26,9 @@ class FrameGraphTexture { void preWrite(void *context, const Descriptor &desc, uint32_t flags = 0); + Texture *getTexture() { return m_texture; } + const Texture *getTexture() const { return m_texture; } + private: Texture *m_texture = nullptr; }; diff --git a/source/paimon/core/fg/resource_entry.h b/source/paimon/core/fg/resource_entry.h index a9b2058..04c497c 100644 --- a/source/paimon/core/fg/resource_entry.h +++ b/source/paimon/core/fg/resource_entry.h @@ -40,12 +40,14 @@ class ResourceEntry { template TResource &get() { - return dynamic_cast>(*m_concept).get(); + auto &resource = static_cast&>(*m_concept); + return resource.get(); } template typename TResource::Descriptor &get_desc() { - return dynamic_cast>(*m_concept).get_desc(); + auto &resource = static_cast&>(*m_concept); + return resource.get_desc(); } private: From d049d061a53048625f652ecb6cdaf8f9d19af653 Mon Sep 17 00:00:00 2001 From: jyxiong Date: Sat, 13 Dec 2025 17:58:09 +0800 Subject: [PATCH 2/4] fix --- example/damaged_helmet/renderer.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/example/damaged_helmet/renderer.h b/example/damaged_helmet/renderer.h index 9ab870a..d7b12de 100644 --- a/example/damaged_helmet/renderer.h +++ b/example/damaged_helmet/renderer.h @@ -6,14 +6,11 @@ #include -#include "paimon/core/fg/frame_graph.h" #include "paimon/core/fg/transient_resources.h" #include "paimon/core/sg/material.h" #include "paimon/core/sg/scene.h" #include "paimon/opengl/buffer.h" -#include "paimon/opengl/sampler.h" #include "paimon/opengl/texture.h" -#include "paimon/rendering/graphics_pipeline.h" #include "paimon/rendering/render_context.h" #include "color_pass.h" From b2396d4c65678847acf2434fdf2a91dab8442369 Mon Sep 17 00:00:00 2001 From: jyxiong Date: Sun, 14 Dec 2025 14:56:28 +0800 Subject: [PATCH 3/4] fix --- example/damaged_helmet/color_pass.cpp | 41 +++++++++++-------- example/damaged_helmet/color_pass.h | 5 +++ example/damaged_helmet/final_pass.cpp | 13 +++--- example/damaged_helmet/final_pass.h | 3 +- example/damaged_helmet/renderer.cpp | 6 +-- example/frame_graph/main.cpp | 4 +- source/paimon/core/fg/frame_graph.h | 2 +- source/paimon/core/fg/frame_graph_texture.cpp | 1 - source/paimon/core/fg/pass.h | 2 +- source/paimon/rendering/render_context.cpp | 4 +- source/paimon/rendering/rendering_info.h | 14 +++++++ 11 files changed, 59 insertions(+), 36 deletions(-) diff --git a/example/damaged_helmet/color_pass.cpp b/example/damaged_helmet/color_pass.cpp index 4c67ca9..d7dbfff 100644 --- a/example/damaged_helmet/color_pass.cpp +++ b/example/damaged_helmet/color_pass.cpp @@ -61,7 +61,7 @@ ColorPass::ColorPass(RenderContext &renderContext, const std::filesystem::path & pipelineInfo.state.depthStencil.depthCompareOp = GL_LESS; // Disable face culling for debugging - pipelineInfo.state.rasterization.cullMode = GL_BACK; + pipelineInfo.state.rasterization.cullMode = GL_NONE; // VertexInputState pipelineInfo.state.vertexInput.bindings = { @@ -83,10 +83,13 @@ ColorPass::ColorPass(RenderContext &renderContext, const std::filesystem::path & // Create sampler m_sampler = std::make_unique(); - m_sampler->set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + m_sampler->set(GL_TEXTURE_MIN_FILTER, GL_LINEAR); m_sampler->set(GL_TEXTURE_MAG_FILTER, GL_LINEAR); m_sampler->set(GL_TEXTURE_WRAP_S, GL_REPEAT); m_sampler->set(GL_TEXTURE_WRAP_T, GL_REPEAT); + + m_colorTexture = std::make_unique(GL_TEXTURE_2D); + m_depthTexture = std::make_unique(GL_TEXTURE_2D); } NodeId ColorPass::registerPass( @@ -97,10 +100,10 @@ NodeId ColorPass::registerPass( Buffer *transformUBO, Buffer *materialUBO, Buffer *lightingUBO) { - - NodeId colorOutput; - NodeId depthOutput; - + + m_colorTexture->set_storage_2d(1, GL_RGBA8, size.x, size.y); + m_depthTexture->set_storage_2d(1, GL_DEPTH_COMPONENT32, size.x, size.y); + auto &passData = fg.create_pass( "ColorPass", [&](FrameGraph::Builder &builder, Data &data) { @@ -123,16 +126,12 @@ NodeId ColorPass::registerPass( // Mark as write targets builder.write(data.colorOutput); builder.write(data.depthOutput); - - // Capture NodeIds for execute phase - colorOutput = data.colorOutput; - depthOutput = data.depthOutput; }, - [&](FrameGraphResources &resources, void *context) { + [&](const Data &data, FrameGraphResources &resources, void *context) { // Get textures from frame graph - auto &colorTexture = fg.get(colorOutput); - auto &depthTexture = fg.get(depthOutput); - + auto *colorTexture = fg.get(data.colorOutput).getTexture(); + auto *depthTexture = fg.get(data.depthOutput).getTexture(); + // Setup rendering info for FBO RenderingInfo renderingInfo; renderingInfo.renderAreaOffset = {0, 0}; @@ -140,17 +139,17 @@ NodeId ColorPass::registerPass( // Setup color attachment renderingInfo.colorAttachments.emplace_back( - *colorTexture.getTexture(), + *m_colorTexture, AttachmentLoadOp::Clear, AttachmentStoreOp::Store, ClearValue::Color(0.1f, 0.1f, 0.1f, 1.0f)); // Setup depth attachment renderingInfo.depthAttachment.emplace( - *depthTexture.getTexture(), + *m_depthTexture, AttachmentLoadOp::Clear, AttachmentStoreOp::Store, - ClearValue::DepthStencil(1.0f, 0)); + ClearValue::Depth(1.0f)); // Begin rendering to FBO m_renderContext.beginRendering(renderingInfo); @@ -161,6 +160,14 @@ NodeId ColorPass::registerPass( // Set viewport m_renderContext.setViewport(0, 0, size.x, size.y); + // Bind UBOs + if (transformUBO) { + m_renderContext.bindUniformBuffer(0, *transformUBO, 0, sizeof(TransformUBO)); + } + if (lightingUBO) { + m_renderContext.bindUniformBuffer(2, *lightingUBO, 0, sizeof(LightingUBO)); + } + // Render each mesh for (const auto &meshData : meshDataList) { m_renderContext.bindVertexBuffer(0, meshData.position_buffer, 0, sizeof(glm::vec3)); diff --git a/example/damaged_helmet/color_pass.h b/example/damaged_helmet/color_pass.h index 01c4ff3..d6ae396 100644 --- a/example/damaged_helmet/color_pass.h +++ b/example/damaged_helmet/color_pass.h @@ -41,6 +41,8 @@ class ColorPass { Buffer *materialUBO, Buffer *lightingUBO); + Texture* getColorTexture() const { return m_colorTexture.get(); } + private: RenderContext &m_renderContext; std::filesystem::path m_assetPath; @@ -48,6 +50,9 @@ class ColorPass { // Graphics pipeline std::unique_ptr m_pipeline; std::unique_ptr m_sampler; + + std::unique_ptr m_colorTexture; + std::unique_ptr m_depthTexture; }; } // namespace paimon diff --git a/example/damaged_helmet/final_pass.cpp b/example/damaged_helmet/final_pass.cpp index 29c41d3..4646035 100644 --- a/example/damaged_helmet/final_pass.cpp +++ b/example/damaged_helmet/final_pass.cpp @@ -44,24 +44,25 @@ FinalPass::FinalPass(RenderContext &renderContext, const std::filesystem::path & m_sampler->set(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } -void FinalPass::registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 &size) { +void FinalPass::registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 &size, Texture* outputTexture) { fg.create_pass( "FinalPass", - [colorInput](FrameGraph::Builder &builder, Data &data) { + [&](FrameGraph::Builder &builder, Data &data) { // Read color input from previous pass data.colorInput = colorInput; builder.read(data.colorInput); }, - [&](FrameGraphResources &resources, void *context) { + [&](const Data &data, FrameGraphResources &resources, void *context) { // Get source texture from frame graph - auto &texture = fg.get(colorInput); + auto *texture = fg.get(data.colorInput).getTexture(); // Setup rendering to default framebuffer SwapchainRenderingInfo renderingInfo; renderingInfo.renderAreaOffset = {0, 0}; renderingInfo.renderAreaExtent = {size.x, size.y}; - + renderingInfo.clearColor = ClearValue::Color(1.0f, 0.0f, 0.0f, 1.0f); + // Begin rendering to default framebuffer m_renderContext.beginSwapchainRendering(renderingInfo); @@ -69,7 +70,7 @@ void FinalPass::registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 m_renderContext.bindPipeline(*m_pipeline); // Bind texture and sampler - m_renderContext.bindTexture(0, *texture.getTexture(), *m_sampler); + m_renderContext.bindTexture(6, *outputTexture, *m_sampler); // Set viewport m_renderContext.setViewport(0, 0, size.x, size.y); diff --git a/example/damaged_helmet/final_pass.h b/example/damaged_helmet/final_pass.h index 357f0a8..549a808 100644 --- a/example/damaged_helmet/final_pass.h +++ b/example/damaged_helmet/final_pass.h @@ -6,6 +6,7 @@ #include "paimon/core/fg/frame_graph.h" #include "paimon/opengl/sampler.h" +#include "paimon/opengl/texture.h" #include "paimon/rendering/graphics_pipeline.h" #include "paimon/rendering/render_context.h" @@ -20,7 +21,7 @@ class FinalPass { FinalPass(RenderContext &renderContext, const std::filesystem::path &assetPath); // Register FinalPass to the frame graph - void registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 &size); + void registerPass(FrameGraph &fg, NodeId colorInput, const glm::ivec2 &size, Texture* outputTexture); private: RenderContext &m_renderContext; diff --git a/example/damaged_helmet/renderer.cpp b/example/damaged_helmet/renderer.cpp index 72a5c0b..54e1423 100644 --- a/example/damaged_helmet/renderer.cpp +++ b/example/damaged_helmet/renderer.cpp @@ -30,15 +30,12 @@ bool Renderer::initialize(const glm::ivec2 &size, const std::filesystem::path &a // Create UBOs m_transformUBO = std::make_unique(); m_transformUBO->set_storage(sizeof(TransformUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); - m_transformUBO->bind_base(GL_UNIFORM_BUFFER, 0); m_materialUBO = std::make_unique(); m_materialUBO->set_storage(sizeof(MaterialUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); - m_materialUBO->bind_base(GL_UNIFORM_BUFFER, 1); m_lightingUBO = std::make_unique(); m_lightingUBO->set_storage(sizeof(LightingUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); - m_lightingUBO->bind_base(GL_UNIFORM_BUFFER, 2); // Create passes m_colorPass = std::make_unique(*m_renderContext, m_assetPath); @@ -175,7 +172,7 @@ void Renderer::render(float deltaTime) { m_lightingUBO.get()); // Register FinalPass using ColorPass output - m_finalPass->registerPass(fg, colorOutput, m_size); + m_finalPass->registerPass(fg, colorOutput, m_size, m_colorPass->getColorTexture()); // Compile the frame graph fg.compile(); @@ -222,7 +219,6 @@ Texture Renderer::createTextureFromImage(const std::shared_ptr &image texture.set_storage_2d(1, internalFormat, image->width, image->height); texture.set_sub_image_2d(0, 0, 0, image->width, image->height, format, GL_UNSIGNED_BYTE, image->data.data()); - texture.generate_mipmap(); return texture; } diff --git a/example/frame_graph/main.cpp b/example/frame_graph/main.cpp index 778de06..9d46474 100644 --- a/example/frame_graph/main.cpp +++ b/example/frame_graph/main.cpp @@ -469,7 +469,7 @@ int main() { }); data.shadow_map = builder.write(data.shadow_map); }, - [&renderData](FrameGraphResources &resources, void *context) { + [&renderData](const ShadowPassData &data, FrameGraphResources &resources, void *context) { std::cout << "Executing Shadow Pass\n"; // Bind shadow framebuffer @@ -499,7 +499,7 @@ int main() { [&](FrameGraph::Builder &builder, ScenePassData &data) { data.shadow_input = builder.read(shadow_pass.shadow_map); }, - [&renderData](FrameGraphResources &resources, void *context) { + [&renderData](const ScenePassData &data, FrameGraphResources &resources, void *context) { std::cout << "Executing Scene Pass\n"; // Render to default framebuffer diff --git a/source/paimon/core/fg/frame_graph.h b/source/paimon/core/fg/frame_graph.h index 882e8d7..73a8f71 100644 --- a/source/paimon/core/fg/frame_graph.h +++ b/source/paimon/core/fg/frame_graph.h @@ -51,7 +51,7 @@ class FrameGraph { template requires std::invocable && - std::invocable + std::invocable const TData &create_pass(std::string name, TSetup &&setup, TExecutor &&executor) { auto id = m_pass_nodes.size(); diff --git a/source/paimon/core/fg/frame_graph_texture.cpp b/source/paimon/core/fg/frame_graph_texture.cpp index d73eaad..a3e5547 100644 --- a/source/paimon/core/fg/frame_graph_texture.cpp +++ b/source/paimon/core/fg/frame_graph_texture.cpp @@ -12,7 +12,6 @@ void FrameGraphTexture::create(void *allocator, const Descriptor &desc) { void FrameGraphTexture::destroy(void *allocator, const Descriptor &desc) { auto *transientResources = static_cast(allocator); transientResources->releaseTexture(desc, m_texture); - m_texture = nullptr; } void FrameGraphTexture::preRead(void *context, const Descriptor &desc, diff --git a/source/paimon/core/fg/pass.h b/source/paimon/core/fg/pass.h index c6a121c..1168b8e 100644 --- a/source/paimon/core/fg/pass.h +++ b/source/paimon/core/fg/pass.h @@ -27,7 +27,7 @@ class Pass : public PassConcept { ~Pass() override = default; void execute(FrameGraphResources &resources, void *context) const override { - std::invoke(m_executor, resources, context); + std::invoke(m_executor, m_data, resources, context); } TData &get_data() { return m_data; } diff --git a/source/paimon/rendering/render_context.cpp b/source/paimon/rendering/render_context.cpp index 06a82b7..f229524 100644 --- a/source/paimon/rendering/render_context.cpp +++ b/source/paimon/rendering/render_context.cpp @@ -37,7 +37,7 @@ void RenderContext::beginRendering(const RenderingInfo& info) { if (info.depthAttachment.has_value()) { const auto& attachment = info.depthAttachment.value(); if (attachment.loadOp == AttachmentLoadOp::Clear) { - float depth = attachment.clearValue.depthStencil.depth; + float depth = attachment.clearValue.depth; m_currentFbo->clear(GL_DEPTH, 0, &depth); } } @@ -46,7 +46,7 @@ void RenderContext::beginRendering(const RenderingInfo& info) { if (info.stencilAttachment.has_value()) { const auto& attachment = info.stencilAttachment.value(); if (attachment.loadOp == AttachmentLoadOp::Clear) { - GLint stencilValue = static_cast(attachment.clearValue.depthStencil.stencil); + GLint stencilValue = static_cast(attachment.clearValue.stencil); m_currentFbo->clear(GL_STENCIL, 0, &stencilValue); } } diff --git a/source/paimon/rendering/rendering_info.h b/source/paimon/rendering/rendering_info.h index 4c02bd9..218d550 100644 --- a/source/paimon/rendering/rendering_info.h +++ b/source/paimon/rendering/rendering_info.h @@ -29,6 +29,8 @@ struct ClearValue { struct { float r, g, b, a; } color; + float depth; + uint32_t stencil; struct { float depth; uint32_t stencil; @@ -46,6 +48,18 @@ struct ClearValue { return value; } + static ClearValue Depth(float depth) { + ClearValue value; + value.depth = depth; + return value; + } + + static ClearValue Stencil(uint32_t stencil) { + ClearValue value; + value.stencil = stencil; + return value; + } + static ClearValue DepthStencil(float depth, uint32_t stencil = 0) { ClearValue value; value.depthStencil.depth = depth; From b34aaaa192f4bb22094ae10207fdbb1793c1abab Mon Sep 17 00:00:00 2001 From: jyxiong Date: Wed, 17 Dec 2025 11:14:41 +0800 Subject: [PATCH 4/4] dev --- example/damaged_helmet/main.cpp | 70 +++---------------------- example/damaged_helmet/renderer.cpp | 41 +++++++++++++++ example/damaged_helmet/renderer.h | 51 ++++++++++++++++--- source/paimon/core/ecs/entity.cpp | 0 source/paimon/core/ecs/entity.h | 79 +++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 69 deletions(-) create mode 100644 source/paimon/core/ecs/entity.cpp create mode 100644 source/paimon/core/ecs/entity.h diff --git a/example/damaged_helmet/main.cpp b/example/damaged_helmet/main.cpp index f4bd175..ff5ca74 100644 --- a/example/damaged_helmet/main.cpp +++ b/example/damaged_helmet/main.cpp @@ -15,8 +15,7 @@ #include "paimon/rendering/render_context.h" #include "paimon/rendering/shader_manager.h" -#include "color_pass.h" -#include "final_pass.h" +#include "renderer.h" #include "mesh_data.h" @@ -24,16 +23,7 @@ using namespace paimon; namespace { -// Camera state -struct Camera { - glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f); - glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f); - glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); - float yaw = -90.0f; - float pitch = 0.0f; - float fov = 45.0f; -}; - +// Camera state is now provided by Renderer::Camera Camera g_camera; glm::ivec2 g_size = {1280, 720}; @@ -177,32 +167,11 @@ int main() { texturePtrMap[key] = value.get(); } - // Create UBOs - Buffer transform_ubo; - transform_ubo.set_storage(sizeof(TransformUBO), nullptr, - GL_DYNAMIC_STORAGE_BIT); - - Buffer material_ubo; - material_ubo.set_storage(sizeof(MaterialUBO), nullptr, - GL_DYNAMIC_STORAGE_BIT); - - Buffer lighting_ubo; - lighting_ubo.set_storage(sizeof(LightingUBO), nullptr, - GL_DYNAMIC_STORAGE_BIT); - - // Create FBO textures for color and depth attachments - Texture fbo_color_texture(GL_TEXTURE_2D); - fbo_color_texture.set_storage_2d(1, GL_RGBA8, g_size.x, g_size.y); - - Texture fbo_depth_texture(GL_TEXTURE_2D); - fbo_depth_texture.set_storage_2d(1, GL_DEPTH_COMPONENT32, g_size.x, g_size.y); - // Create render context RenderContext ctx; - // Create screen quad (it will load shaders internally from singleton) - ColorPass color_pass(ctx); - FinalPass final_pass(ctx); + // Create Renderer (it will own per-pass resources) + Renderer renderer(ctx, texturePtrMap); LOG_INFO("Setup complete, entering render loop"); @@ -229,36 +198,13 @@ int main() { g_camera.position.z = radius * cos(glm::radians(rotation)); g_camera.position.y = 0.0f; - // Setup transformation matrices - TransformUBO transformData; - transformData.model = glm::mat4(1.0f); // Keep model stationary - transformData.view = glm::lookAt(g_camera.position, - glm::vec3(0.0f, 0.0f, 0.0f), g_camera.up); - transformData.projection = - glm::perspective(glm::radians(g_camera.fov), - static_cast(g_size.x) / g_size.y, 0.1f, 100.0f); - transform_ubo.set_sub_data(0, sizeof(TransformUBO), &transformData); - // Setup lighting LightingUBO lightingData; lightingData.lightPos = glm::vec3(5.0f, 5.0f, 5.0f); - lightingData.viewPos = g_camera.position; - lighting_ubo.set_sub_data(0, sizeof(LightingUBO), &lightingData); - - // ===== First Pass: Render to FBO ===== - color_pass.draw( - ctx, g_size, - mesh_data_list, - texturePtrMap, - transform_ubo, - material_ubo, - lighting_ubo); - - // ===== Second Pass: Render FBO texture to screen ===== - { - // Use screen quad to render FBO texture - final_pass.draw(ctx, *color_pass.getColorTexture(), g_size); - } + // Renderer will copy viewPos from camera + + // Render (Renderer handles UBOs and multi-pass internals) + renderer.draw(g_size, g_camera, lightingData, mesh_data_list); // Swap buffers window->swapBuffers(); diff --git a/example/damaged_helmet/renderer.cpp b/example/damaged_helmet/renderer.cpp index e69de29..43a4545 100644 --- a/example/damaged_helmet/renderer.cpp +++ b/example/damaged_helmet/renderer.cpp @@ -0,0 +1,41 @@ +#include "renderer.h" + +using namespace paimon; + +Renderer::Renderer(RenderContext &renderContext, + const std::map, Texture *> &texturePtrMap) + : m_renderContext(renderContext), + m_colorPass(renderContext), + m_finalPass(renderContext), + m_transformUbo(), + m_materialUbo(), + m_lightingUbo(), + m_texturePtrMap(texturePtrMap) { + m_transformUbo.set_storage(sizeof(TransformUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); + m_materialUbo.set_storage(sizeof(MaterialUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); + m_lightingUbo.set_storage(sizeof(LightingUBO), nullptr, GL_DYNAMIC_STORAGE_BIT); +} + +void Renderer::draw(const glm::ivec2 &resolution, const Camera &camera, + const LightingUBO &lighting, + const std::vector &meshes) { + // Prepare transform UBO + TransformUBO transformData; + transformData.model = glm::mat4(1.0f); + transformData.view = glm::lookAt(camera.position, glm::vec3(0.0f, 0.0f, 0.0f), camera.up); + transformData.projection = glm::perspective(glm::radians(camera.fov), + static_cast(resolution.x) / resolution.y, + 0.1f, 100.0f); + m_transformUbo.set_sub_data(0, sizeof(TransformUBO), &transformData); + + // Update lighting UBO (copy viewPos from camera) + LightingUBO lightingData = lighting; + lightingData.viewPos = camera.position; + m_lightingUbo.set_sub_data(0, sizeof(LightingUBO), &lightingData); + + // First pass: render scene to color texture + m_colorPass.draw(m_renderContext, resolution, meshes, m_texturePtrMap, m_transformUbo, m_materialUbo, m_lightingUbo); + + // Second pass: blit color texture to default framebuffer + m_finalPass.draw(m_renderContext, *m_colorPass.getColorTexture(), resolution); +} \ No newline at end of file diff --git a/example/damaged_helmet/renderer.h b/example/damaged_helmet/renderer.h index 9155f74..1e5b095 100644 --- a/example/damaged_helmet/renderer.h +++ b/example/damaged_helmet/renderer.h @@ -1,21 +1,58 @@ #pragma once +#include +#include +#include + +#include +#include + #include "paimon/rendering/render_context.h" -#include "paimon/core/fg/transient_resources.h" +#include "paimon/opengl/buffer.h" +#include "paimon/opengl/texture.h" + +#include "color_pass.h" +#include "final_pass.h" +#include "mesh_data.h" namespace paimon { +// Camera struct used by Renderer (matches previous main.cpp fields) +struct Camera { + glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f); + glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f); + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + float yaw = -90.0f; + float pitch = 0.0f; + float fov = 45.0f; +}; + class Renderer { public: - Renderer(RenderContext &renderContext); + Renderer(RenderContext &renderContext, + const std::map, Texture *> &texturePtrMap); + + // resolution: framebuffer size in pixels + // camera: camera state (position, fov...) + // lighting: LightingUBO structure (lightPos, viewPos) + // meshes: list of MeshData to render + void draw(const glm::ivec2 &resolution, const Camera &camera, + const LightingUBO &lighting, + const std::vector &meshes); - void render(); + // expose color texture for tests or external use if needed + const Texture *getColorTexture() const { return m_colorPass.getColorTexture(); } private: RenderContext &m_renderContext; - TransientResources m_transientResources; + ColorPass m_colorPass; + FinalPass m_finalPass; - - float m_rotation = 0.0f; + Buffer m_transformUbo; + Buffer m_materialUbo; + Buffer m_lightingUbo; + + const std::map, Texture *> &m_texturePtrMap; }; -} + +} // namespace paimon diff --git a/source/paimon/core/ecs/entity.cpp b/source/paimon/core/ecs/entity.cpp new file mode 100644 index 0000000..e69de29 diff --git a/source/paimon/core/ecs/entity.h b/source/paimon/core/ecs/entity.h new file mode 100644 index 0000000..6642817 --- /dev/null +++ b/source/paimon/core/ecs/entity.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +namespace paimon::ecs +{ + class Scene; + + /// @brief An entity represents any object in a scene + /// @note This class is ment to be passed by value since its just an id + class Entity + { + public: + Entity() = default; + Entity(entt::entity entityHandle, Scene* scene); + Entity(const Entity&) = default; + Entity(Entity&&) = default; + + auto operator=(const Entity&) -> Entity& = default; + auto operator=(Entity&&) -> Entity& = default; + + bool operator==(const Entity& other) const; + bool operator!=(const Entity& other) const; + + operator bool() const { return m_id != entt::null; } + operator entt::entity() const { return m_id; } + operator uint32_t() const { return static_cast(m_id); } + + [[nodiscard]] auto registry() const -> entt::registry&; + + /// @brief Checks if the entity has all components of type T... + template + auto has() const -> bool + { + return registry().all_of(m_id); + } + + /// @brief Acces to the component of type T + template + auto get() const -> T& + { + return registry().get(m_id); + } + + /// @brief Adds a component of type T to the entity + /// @return A refrence to the new component + template + auto add(Args&&... args) -> T& + { + return registry().emplace(m_id, std::forward(args)...); + } + + template + auto getOrAdd() -> T& + { + return registry().get_or_emplace(m_id); + } + + /// @brief Removes a component of type T from the entity + /// @note Entity MUST have the component and it MUST be an optional component + template + requires OptionalComponent + void remove() + { + registry().remove(m_id); + } + + void setParent(Entity parent); + void removeParent(); + void addChild(Entity child); + void removeChild(Entity child); + void removeChildren(); + + private: + + entt::entity m_id{ entt::null }; + Scene* m_scene{ nullptr }; + }; +}