From c44f87ad0046614bea59b4a32fb672a73ef7110b Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Fri, 20 Sep 2024 17:56:33 +0200 Subject: [PATCH] SceneRenderer (WIP). --- private/sdl_gpu_test/7_3d_scene/app.cpp | 144 ++------------ private/sdl_gpu_test/7_3d_scene/app.hpp | 15 +- private/sdl_gpu_test/SModule | 3 + private/sdl_gpu_test/scene/mesh.cpp | 62 ++++++ private/sdl_gpu_test/scene/mesh.hpp | 41 ++++ private/sdl_gpu_test/scene/scene.cpp | 42 ++++ private/sdl_gpu_test/scene/scene.hpp | 71 +++++++ private/sdl_gpu_test/scene/scene_renderer.cpp | 184 ++++++++++++++++++ private/sdl_gpu_test/scene/scene_renderer.hpp | 72 +++++++ private/sdl_gpu_test/scene/transform3d.hpp | 82 ++++++++ 10 files changed, 580 insertions(+), 136 deletions(-) create mode 100644 private/sdl_gpu_test/scene/mesh.cpp create mode 100644 private/sdl_gpu_test/scene/mesh.hpp create mode 100644 private/sdl_gpu_test/scene/scene.cpp create mode 100644 private/sdl_gpu_test/scene/scene.hpp create mode 100644 private/sdl_gpu_test/scene/scene_renderer.cpp create mode 100644 private/sdl_gpu_test/scene/scene_renderer.hpp create mode 100644 private/sdl_gpu_test/scene/transform3d.hpp diff --git a/private/sdl_gpu_test/7_3d_scene/app.cpp b/private/sdl_gpu_test/7_3d_scene/app.cpp index 3200c98..8b0cfd0 100644 --- a/private/sdl_gpu_test/7_3d_scene/app.cpp +++ b/private/sdl_gpu_test/7_3d_scene/app.cpp @@ -3,10 +3,9 @@ #include #include -#include #include -#include +#include "../scene/mesh.hpp" #include "../sdlpp/keyboard.hpp" #include "../util/bitmap.hpp" #include "../util/mesh.hpp" @@ -18,12 +17,6 @@ namespace inline constexpr float Y_POS_MIN = -10.f; inline constexpr float Y_POS_MAX = 10.f; inline constexpr Uint16 AXIS_DEADZONE = 5000; - -struct VertexShaderParameters -{ - glm::mat4 worldToView; - glm::mat4 viewToClip; -}; } void ThreeDSceneApp::init(const AppInitArgs& args) @@ -38,30 +31,11 @@ void ThreeDSceneApp::init(const AppInitArgs& args) .height = 720 }); - // create graphics pipeline - createMeshPipeline(); - - // create UI pipeline + // init UI renderer mUIRenderer.init(*this); - // load the mesh - const Mesh mesh = loadMesh(mFileSystem.getPath("meshes/cube.obj")); - mNumVertices = static_cast(mesh.vertices.size()); - - // create vertex buffer - mVertexBuffer.create(mDevice, { - .usage = {.vertex = true}, - .size = static_cast(mesh.vertices.size() * sizeof(Vertex)) - }); - uploadVertexData(mVertexBuffer, std::span(mesh.vertices)); - - // create texture and sampler - sdlpp::GPUTextureCreateArgs textureArgs = { - .format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB, - .usage = {.sampler = true} - }; - mTexture = loadTexture("bitmaps/cube.png", textureArgs); - mSampler.create(mDevice, {}); + // init scene renderer + mSceneRenderer.init(*this); // open gamepad const std::vector gamepads = sdlpp::getGamepads(); @@ -84,6 +58,11 @@ void ThreeDSceneApp::init(const AppInitArgs& args) .posX = 100, .posY = 500 }); + + // init the scene + mScene.init({.renderer = &mSceneRenderer}); + mScene.getRootNode().emplaceChild({.mesh = "meshes/cube.obj", .texture = "bitmaps/cube.png"}); + mButton->clicked.connect([&]() { mButton->setText("Thanks!"); @@ -116,39 +95,14 @@ void ThreeDSceneApp::update(const AppUpdateArgs& args) mLastSwapchainHeight = swapchainHeight; } - // render the 3D "scene" - const VertexShaderParameters vertexShaderParameters = { - .worldToView = glm::lookAt(glm::vec3(2.f, mYPos, 2.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)) - * glm::rotate(glm::mat4(1.f), glm::radians(mRotation), glm::vec3(0.f, 1.f, 0.f)), - .viewToClip = glm::perspectiveFov( - /* fov = */ glm::radians(90.f), - /* width = */ static_cast(swapchainWidth), - /* height = */ static_cast(swapchainHeight), - /* zNear = */ 0.1f, - /* zFar = */ 100.f - ) - }; - - std::array colorTargets = {sdlpp::GPUColorTargetInfo{ - .texture = swapchainTexture, - .clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f}, - .loadOp = sdlpp::GPULoadOp::CLEAR, - }}; - sdlpp::GPURenderPass renderPass = cmdBuffer.beginRenderPass({ - .colorTargetInfos = colorTargets, - .depthStencilTargetInfo = sdlpp::GPUDepthStencilTargetInfo{ - .texture = mDepthBuffer, - .loadOp = sdlpp::GPULoadOp::CLEAR - } + // render the scene + mSceneRenderer.render({ + .cmdBuffer = &cmdBuffer, + .targetTexture = &swapchainTexture, + .depthTexture = &mDepthBuffer, + .targetTextureWidth = swapchainWidth, + .targetTextureHeight = swapchainHeight }); - static const glm::vec4 WHITE(1.f, 1.f, 1.f, 1.f); - cmdBuffer.pushFragmentUniformData(0, std::span(&WHITE, 1)); - cmdBuffer.pushVertexUniformData(0, std::span(&vertexShaderParameters, 1)); - renderPass.bindFragmentSampler({.texture = mTexture, .sampler = mSampler}); - renderPass.bindGraphicsPipeline(mMeshPipeline); - renderPass.bindVertexBuffer({.buffer = mVertexBuffer}); - renderPass.drawPrimitives({.numVertices = mNumVertices}); - renderPass.end(); // render the "UI" mUIRenderer.render({ @@ -192,72 +146,6 @@ void ThreeDSceneApp::handleMouseButtonEvent(const sdlpp::MouseButtonEvent& event mWidgetTree.notifyMouseButton(event); } - -void ThreeDSceneApp::createMeshPipeline() -{ - // create shaders - const sdlpp::GPUShader vertexShader = loadShader("shaders/glsl/textured_3dtriangles_from_buffer.vert.spv", { - .format = sdlpp::GPUShaderFormat::SPIRV, - .stage = sdlpp::GPUShaderStage::VERTEX, - .numUniformBuffers = 1 - }); - const sdlpp::GPUShader fragmentShader = loadShader("shaders/glsl/color_from_texture.frag.spv", { - .format = sdlpp::GPUShaderFormat::SPIRV, - .stage = sdlpp::GPUShaderStage::FRAGMENT, - .numSamplers = 1, - .numUniformBuffers = 1 - }); - std::array colorTargetsDescs = { - sdlpp::GPUColorTargetDescription{ - .format = mDevice.getSwapchainTextureFormat(mWindow), - .blendState = { - .enableBlend = true, - .srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA, - .dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA, - .colorBlendOp = sdlpp::GPUBlendOp::ADD, - .srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE, - .dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO, - .alphaBlendOp = sdlpp::GPUBlendOp::ADD - } - } - }; - std::array vertexBindings = { - sdlpp::GPUVertexBinding{ - .index = 0, - .pitch = sizeof(Vertex) - } - }; - std::array vertexAttributes = { - sdlpp::GPUVertexAttribute{ - .location = 0, - .bindingIndex = 0, - .format = sdlpp::GPUVertexElementFormat::FLOAT3, - .offset = offsetof(Vertex, pos) - }, - sdlpp::GPUVertexAttribute{ - .location = 1, - .bindingIndex = 0, - .format = sdlpp::GPUVertexElementFormat::FLOAT2, - .offset = offsetof(Vertex, texcoord) - } - }; - mMeshPipeline.create(mDevice, { - .vertexShader = vertexShader, - .fragmentShader = fragmentShader, - .vertexInputState = { - .vertexBindings = vertexBindings, - .vertexAttributes = vertexAttributes - }, - .rasterizerState = { - .cullMode = sdlpp::GPUCullMode::BACK - }, - .targetInfo = { - .colorTargetDescriptions = colorTargetsDescs, - .depthStencilFormat = sdlpp::GPUTextureFormat::D16_UNORM - } - }); -} - void ThreeDSceneApp::processInput(const AppUpdateArgs& args) { const std::span keystates = sdlpp::getKeyboardState(); diff --git a/private/sdl_gpu_test/7_3d_scene/app.hpp b/private/sdl_gpu_test/7_3d_scene/app.hpp index 774f671..20393d8 100644 --- a/private/sdl_gpu_test/7_3d_scene/app.hpp +++ b/private/sdl_gpu_test/7_3d_scene/app.hpp @@ -9,6 +9,8 @@ #include "../gui/label.hpp" #include "../gui/widget.hpp" #include "../gui/ui_renderer.hpp" +#include "../scene/scene.hpp" +#include "../scene/scene_renderer.hpp" #include "../sdlpp/gamepad.hpp" namespace sdl_gpu_test @@ -16,20 +18,18 @@ namespace sdl_gpu_test class ThreeDSceneApp : public Application { private: - sdlpp::GPUBuffer mVertexBuffer; sdlpp::GPUTexture mDepthBuffer; - sdlpp::GPUGraphicsPipeline mMeshPipeline; - sdlpp::GPUTexture mTexture; - sdlpp::GPUSampler mSampler; UIRenderer mUIRenderer; WidgetTree mWidgetTree; - Label* mLabel; - Button* mButton; + SceneRenderer mSceneRenderer; + Scene mScene; + + Label* mLabel = nullptr; + Button* mButton = nullptr; sdlpp::Gamepad mGamepad; - Uint32 mNumVertices = 0; Uint32 mLastSwapchainWidth = 1280; Uint32 mLastSwapchainHeight = 720; float mRotation = 0.f; @@ -41,7 +41,6 @@ public: void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override; void handleMouseButtonEvent(const sdlpp::MouseButtonEvent&) override; private: - void createMeshPipeline(); void processInput(const AppUpdateArgs& args); }; } // namespace sdl_gpu_test diff --git a/private/sdl_gpu_test/SModule b/private/sdl_gpu_test/SModule index 214b061..d3d2430 100644 --- a/private/sdl_gpu_test/SModule +++ b/private/sdl_gpu_test/SModule @@ -9,6 +9,9 @@ src_files = Split(""" gui/label.cpp gui/ui_renderer.cpp gui/widget.cpp + scene/mesh.cpp + scene/scene.cpp + scene/scene_renderer.cpp util/bitmap.cpp util/font_map.cpp util/mesh.cpp diff --git a/private/sdl_gpu_test/scene/mesh.cpp b/private/sdl_gpu_test/scene/mesh.cpp new file mode 100644 index 0000000..18ae6eb --- /dev/null +++ b/private/sdl_gpu_test/scene/mesh.cpp @@ -0,0 +1,62 @@ + +#include "./mesh.hpp" + +namespace sdl_gpu_test +{ +MeshNode::MeshNode(MeshNodeCreateArgs args) : mMeshPath(std::move(args.mesh)), mTexturePath(std::move(args.texture)) +{ + +} + +void MeshNode::setMesh(std::string resourcePath) +{ + mMeshPath = std::move(resourcePath); + + if (mScene != nullptr) + { + updateMesh(); + } +} + +void MeshNode::setTexture(std::string resourcePath) +{ + mTexturePath = std::move(resourcePath); + + if (mScene != nullptr) + { + updateTexture(); + } +} + +void MeshNode::handleEnteredScene() +{ + SceneNode::handleEnteredScene(); + + updateMesh(); + updateTexture(); +} + +void MeshNode::updateMesh() +{ + if (mMeshPath.empty()) + { + mMesh = nullptr; + } + else + { + mMesh = mScene->getRenderer().getMesh(mMeshPath); + } +} + +void MeshNode::updateTexture() +{ + if (mTexturePath.empty()) + { + mTexture = nullptr; + } + else + { + mTexture = mScene->getRenderer().getTexture(mTexturePath); + } +} +} diff --git a/private/sdl_gpu_test/scene/mesh.hpp b/private/sdl_gpu_test/scene/mesh.hpp new file mode 100644 index 0000000..f4a94ac --- /dev/null +++ b/private/sdl_gpu_test/scene/mesh.hpp @@ -0,0 +1,41 @@ + +#pragma once + +#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_MESH_HPP_INCLUDED) +#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_MESH_HPP_INCLUDED 1 + +#include "./scene.hpp" + +#include "../util/mesh.hpp" + +namespace sdl_gpu_test +{ +struct MeshNodeCreateArgs +{ + std::string mesh; + std::string texture; +}; + +class MeshNode : public SceneNode +{ +public: + using create_args_t = MeshNodeCreateArgs; +private: + std::shared_ptr mMesh; + std::shared_ptr mTexture; + std::string mMeshPath; + std::string mTexturePath; +public: + MeshNode(MeshNodeCreateArgs args); + + void setMesh(std::string resourcePath); + void setTexture(std::string resourcePath); + + void handleEnteredScene() override; +private: + void updateMesh(); + void updateTexture(); +}; +} // namespace sdl_gpu_test + +#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_MESH_HPP_INCLUDED) diff --git a/private/sdl_gpu_test/scene/scene.cpp b/private/sdl_gpu_test/scene/scene.cpp new file mode 100644 index 0000000..28fd78f --- /dev/null +++ b/private/sdl_gpu_test/scene/scene.cpp @@ -0,0 +1,42 @@ + +#include "./scene.hpp" + +namespace sdl_gpu_test +{ +void SceneNode::handleEnteredScene() +{ + for (scene_node_ptr_t& child : mChildren) + { + child->enterScene(mScene, this); + } +} + +SceneNode* SceneNode::addChild(scene_node_ptr_t&& node) +{ + mChildren.push_back(std::move(node)); + + if (mScene != nullptr) + { + mChildren.back()->enterScene(mScene, this); + } + + return mChildren.back().get(); +} + + +void SceneNode::enterScene(Scene* scene, SceneNode* parent) +{ + MIJIN_ASSERT(mScene == nullptr && mParent == nullptr, "SceneNode is already in a scene."); + MIJIN_ASSERT_FATAL(scene != nullptr, "Missing parameter: scene."); + + mScene = scene; + mParent = parent; + handleEnteredScene(); +} + +void Scene::init(const SceneInitArgs& args) +{ + MIJIN_ASSERT_FATAL(args.renderer != nullptr, "Missing parameter: renderer."); + mRenderer = args.renderer; +} +} diff --git a/private/sdl_gpu_test/scene/scene.hpp b/private/sdl_gpu_test/scene/scene.hpp new file mode 100644 index 0000000..3b5075a --- /dev/null +++ b/private/sdl_gpu_test/scene/scene.hpp @@ -0,0 +1,71 @@ + +#pragma once + +#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_SCENE_HPP_INCLUDED) +#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_SCENE_HPP_INCLUDED 1 + +#include +#include + +#include "./scene_renderer.hpp" +#include "./transform3d.hpp" + +namespace sdl_gpu_test +{ +using scene_node_ptr_t = std::unique_ptr; + +struct SceneNodeCreateArgs +{ + +}; + +class SceneNode +{ +public: + using create_args_t = SceneNodeCreateArgs; +protected: + std::vector mChildren; + SceneNode* mParent = nullptr; + class Scene* mScene = nullptr; + Transform3D mTransform; +public: + virtual ~SceneNode() noexcept = default; + + virtual void handleEnteredScene(); + + SceneNode* addChild(scene_node_ptr_t&& node); + + template + TNode* emplaceChild(typename TNode::create_args_t args) + { + return static_cast(addChild(std::make_unique(args))); + } +private: + void enterScene(Scene* scene, SceneNode* parent); +}; + +struct SceneInitArgs +{ + SceneRenderer* renderer; +}; + +class Scene +{ +private: + SceneNode mRootNode; + SceneRenderer* mRenderer = nullptr; +public: + void init(const SceneInitArgs& args); + + [[nodiscard]] + SceneNode& getRootNode() noexcept { return mRootNode; } + + [[nodiscard]] + const SceneNode& getRootNode() const noexcept { return mRootNode; } + + [[nodiscard]] + SceneRenderer& getRenderer() const noexcept { return *mRenderer; } +}; +} // namespace sdl_gpu_test + +#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_SCENE_HPP_INCLUDED) diff --git a/private/sdl_gpu_test/scene/scene_renderer.cpp b/private/sdl_gpu_test/scene/scene_renderer.cpp new file mode 100644 index 0000000..3cd3d79 --- /dev/null +++ b/private/sdl_gpu_test/scene/scene_renderer.cpp @@ -0,0 +1,184 @@ + +#include "./scene_renderer.hpp" + +#include +#include + +#include "./mesh.hpp" +#include "../util/bitmap.hpp" +#include "../util/mesh.hpp" + +namespace sdl_gpu_test +{ +namespace +{ + struct VertexShaderParameters + { + glm::mat4 worldToView; + glm::mat4 viewToClip; + }; +} +void SceneRenderer::init(Application& application) +{ + mApplication = &application; + + mSampler.create(mApplication->getDevice(), {}); +} + +std::shared_ptr SceneRenderer::getMesh(const std::string& resourcePath) +{ + auto it = mCachedMeshes.find(resourcePath); + if (it != mCachedMeshes.end()) + { + if (std::shared_ptr result = it->second.lock(); result != nullptr) + { + return result; + } + mCachedMeshes.erase(it); + } + + const Mesh mesh = loadMesh(mApplication->getFileSystem().getPath(resourcePath)); + std::shared_ptr result = std::make_shared(); + result->numVertices = mesh.vertices.size(); + result->vertexBuffer.create(mApplication->getDevice(), { + .usage = {.vertex = true}, + .size = static_cast(result->numVertices * sizeof(Vertex)) + }); + mApplication->uploadVertexData(result->vertexBuffer, std::span(mesh.vertices)); + mCachedMeshes.emplace(resourcePath, result); + return result; +} + +std::shared_ptr SceneRenderer::getTexture(const std::string& resourcePath) +{ + auto it = mCachedTextures.find(resourcePath); + if (it != mCachedTextures.end()) + { + if (std::shared_ptr result = it->second.lock(); result != nullptr) + { + return result; + } + mCachedTextures.erase(it); + } + + const Bitmap bitmap = loadBitmap(mApplication->getFileSystem().getPath(resourcePath)); + std::shared_ptr result = std::make_shared(); + result->texture.create(mApplication->getDevice(), { + .format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB, + .usage = {.sampler = true} + }); + mApplication->uploadTextureData(result->texture, bitmap); + mCachedTextures.emplace(resourcePath, result); + return result; +} + +void SceneRenderer::render(const SceneRendererRenderArgs& args) +{ + // just temporary + const float mYPos = 1.5f; + const float mRotation = 0.f; + + VertexShaderParameters vertexShaderParameters = { + .worldToView = glm::lookAt(glm::vec3(2.f, mYPos, 2.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)) + * glm::rotate(glm::mat4(1.f), glm::radians(mRotation), glm::vec3(0.f, 1.f, 0.f)), + .viewToClip = glm::perspectiveFov( + /* fov = */ glm::radians(90.f), + /* width = */ static_cast(args.targetTextureWidth), + /* height = */ static_cast(args.targetTextureHeight), + /* zNear = */ 0.1f, + /* zFar = */ 100.f + ) + }; + + std::array colorTargets = {sdlpp::GPUColorTargetInfo{ + .texture = *args.targetTexture, + .clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f}, + .loadOp = sdlpp::GPULoadOp::CLEAR, + }}; + sdlpp::GPURenderPass renderPass = args.cmdBuffer->beginRenderPass({ + .colorTargetInfos = colorTargets, + .depthStencilTargetInfo = sdlpp::GPUDepthStencilTargetInfo{ + .texture = *args.depthTexture, + .loadOp = sdlpp::GPULoadOp::CLEAR + } + }); + renderPass.bindGraphicsPipeline(mPipeline); + + static const glm::vec4 WHITE(1.f, 1.f, 1.f, 1.f); + + for (const RenderListEntry& entry : mRenderList) + { + args.cmdBuffer->pushFragmentUniformData(0, std::span(&WHITE, 1)); + args.cmdBuffer->pushVertexUniformData(0, std::span(&vertexShaderParameters, 1)); + renderPass.bindFragmentSampler({.texture = entry.texture->texture, .sampler = mSampler}); + renderPass.bindVertexBuffer({.buffer = entry.mesh->vertexBuffer}); + renderPass.drawPrimitives({.numVertices = static_cast(entry.mesh->numVertices)}); + } + renderPass.end(); +} + +void SceneRenderer::createPipeline() +{ + // create shaders + const sdlpp::GPUShader vertexShader = mApplication->loadShader("shaders/glsl/textured_3dtriangles_from_buffer.vert.spv", { + .format = sdlpp::GPUShaderFormat::SPIRV, + .stage = sdlpp::GPUShaderStage::VERTEX, + .numUniformBuffers = 1 + }); + const sdlpp::GPUShader fragmentShader = mApplication->loadShader("shaders/glsl/color_from_texture.frag.spv", { + .format = sdlpp::GPUShaderFormat::SPIRV, + .stage = sdlpp::GPUShaderStage::FRAGMENT, + .numSamplers = 1, + .numUniformBuffers = 1 + }); + std::array colorTargetsDescs = { + sdlpp::GPUColorTargetDescription{ + .format = mApplication->getDevice().getSwapchainTextureFormat(mApplication->getWindow()), + .blendState = { + .enableBlend = true, + .srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA, + .dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA, + .colorBlendOp = sdlpp::GPUBlendOp::ADD, + .srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE, + .dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO, + .alphaBlendOp = sdlpp::GPUBlendOp::ADD + } + } + }; + std::array vertexBindings = { + sdlpp::GPUVertexBinding{ + .index = 0, + .pitch = sizeof(Vertex) + } + }; + std::array vertexAttributes = { + sdlpp::GPUVertexAttribute{ + .location = 0, + .bindingIndex = 0, + .format = sdlpp::GPUVertexElementFormat::FLOAT3, + .offset = offsetof(Vertex, pos) + }, + sdlpp::GPUVertexAttribute{ + .location = 1, + .bindingIndex = 0, + .format = sdlpp::GPUVertexElementFormat::FLOAT2, + .offset = offsetof(Vertex, texcoord) + } + }; + mPipeline.create(mApplication->getDevice(), { + .vertexShader = vertexShader, + .fragmentShader = fragmentShader, + .vertexInputState = { + .vertexBindings = vertexBindings, + .vertexAttributes = vertexAttributes + }, + .rasterizerState = { + .cullMode = sdlpp::GPUCullMode::BACK + }, + .targetInfo = { + .colorTargetDescriptions = colorTargetsDescs, + .depthStencilFormat = sdlpp::GPUTextureFormat::D16_UNORM + } + }); +} +} diff --git a/private/sdl_gpu_test/scene/scene_renderer.hpp b/private/sdl_gpu_test/scene/scene_renderer.hpp new file mode 100644 index 0000000..4ce11c8 --- /dev/null +++ b/private/sdl_gpu_test/scene/scene_renderer.hpp @@ -0,0 +1,72 @@ + +#pragma once + +#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_SCENE_RENDERER_HPP_INCLUDED) +#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_SCENE_RENDERER_HPP_INCLUDED 1 + +#include +#include +#include + +#include "./transform3d.hpp" +#include "../application.hpp" +#include "../sdlpp/gpu.hpp" + +namespace sdl_gpu_test +{ +struct SceneMesh +{ + sdlpp::GPUBuffer vertexBuffer; + std::size_t numVertices; +}; + +struct SceneTexture +{ + sdlpp::GPUTexture texture; +}; + +struct SceneRendererRenderArgs +{ + const sdlpp::GPUCommandBuffer* cmdBuffer; + const sdlpp::GPUTexture* targetTexture; + const sdlpp::GPUTexture* depthTexture; + unsigned targetTextureWidth; + unsigned targetTextureHeight; +}; + +class SceneRenderer +{ +public: + using mesh_idx_t = std::uint64_t; +private: + struct RenderListEntry + { + std::shared_ptr mesh; + std::shared_ptr texture; + Transform3D transform; + mesh_idx_t meshIdx; + }; + + Application* mApplication = nullptr; + std::unordered_map> mCachedMeshes; + std::unordered_map> mCachedTextures; + std::vector mRenderList; + + sdlpp::GPUGraphicsPipeline mPipeline; + sdlpp::GPUSampler mSampler; +public: + void init(Application& application); + + [[nodiscard]] + std::shared_ptr getMesh(const std::string& resourcePath); + + [[nodiscard]] + std::shared_ptr getTexture(const std::string& resourcePath); + + void render(const SceneRendererRenderArgs& args); +private: + void createPipeline(); +}; +} // namespace sdl_gpu_test + +#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_SCENE_RENDERER_HPP_INCLUDED) diff --git a/private/sdl_gpu_test/scene/transform3d.hpp b/private/sdl_gpu_test/scene/transform3d.hpp new file mode 100644 index 0000000..18fc6b3 --- /dev/null +++ b/private/sdl_gpu_test/scene/transform3d.hpp @@ -0,0 +1,82 @@ + +#pragma once + +#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_TRANSFORM3D_HPP_INCLUDED) +#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_TRANSFORM3D_HPP_INCLUDED 1 + +#define GLM_ENABLE_EXPERIMENTAL 1 +#include +#include +#include +#include +#include +#undef GLM_ENABLE_EXPERIMENTAL + +namespace sdl_gpu_test +{ +struct Transform3D +{ + glm::mat4 matrix = glm::mat4(1); + + [[nodiscard]] Transform3D inverse() const + { + return Transform3D { + .matrix = glm::inverse(matrix) + }; + } + + [[nodiscard]] Transform3D apply(const Transform3D& other) const + { + return Transform3D{ + .matrix = matrix * other.matrix + }; + } + + template + [[nodiscard]] T apply(const T& value) const + { + return matrix * value; + } + + [[nodiscard]] glm::vec3 apply(const glm::vec3 value) const + { + return glm::vec3(matrix * glm::vec4(value, 1.f)); + } + + [[nodiscard]] glm::vec3 applyRotation(const glm::vec3 vec) const + { + // TODO: maybe find a better way to extract the rotation? this is really expensive + glm::vec3 scale; + glm::quat orientation; + glm::vec3 translation; + glm::vec3 skew; + glm::vec4 perspective; + glm::decompose(matrix, scale, orientation, translation, skew, perspective); + return orientation * vec; + } + + [[nodiscard]] glm::vec3 getTranslation() const { return glm::vec3(matrix[3]); } + + inline void setTranslation(const glm::vec3& translation); + void setLookAt(const glm::vec3& eye, const glm::vec3& target, const glm::vec3& up = glm::vec3(0.f, 1.f, 0.f)) { + matrix = glm::inverse(glm::lookAt(eye, target, up)); // glm lookAt() is for camera matrices, therefore basically inverted + } + + [[nodiscard]] static inline Transform3D make(const glm::vec3& translation, const glm::quat& rotation = {}, const glm::vec3& scale = glm::vec3(1.f)); + [[nodiscard]] static Transform3D makeLookAt(const glm::vec3& eye, const glm::vec3& target, const glm::vec3& up = glm::vec3(0.f, 1.f, 0.f)) { + return Transform3D{glm::inverse(glm::lookAt(eye, target, up))}; // glm lookAt() is for camera matrices, therefore basically inverted + } +}; + +inline Transform3D Transform3D::make(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale) +{ + return Transform3D{glm::translate(glm::mat4_cast(rotation) * glm::scale(glm::mat4(1), scale), translation)}; +} + +void Transform3D::setTranslation(const glm::vec3& translation) +{ + matrix[3] = glm::vec4(translation, 1.f); +} +} // namespace sdl_gpu_test + +#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_TRANSFORM3D_HPP_INCLUDED)