SceneRenderer (WIP).
This commit is contained in:
parent
122190e44b
commit
c44f87ad00
@ -3,10 +3,9 @@
|
|||||||
|
|
||||||
#include <glm/mat4x4.hpp>
|
#include <glm/mat4x4.hpp>
|
||||||
#include <glm/vec2.hpp>
|
#include <glm/vec2.hpp>
|
||||||
#include <glm/vec3.hpp>
|
|
||||||
#include <glm/vec4.hpp>
|
#include <glm/vec4.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
|
#include "../scene/mesh.hpp"
|
||||||
#include "../sdlpp/keyboard.hpp"
|
#include "../sdlpp/keyboard.hpp"
|
||||||
#include "../util/bitmap.hpp"
|
#include "../util/bitmap.hpp"
|
||||||
#include "../util/mesh.hpp"
|
#include "../util/mesh.hpp"
|
||||||
@ -18,12 +17,6 @@ namespace
|
|||||||
inline constexpr float Y_POS_MIN = -10.f;
|
inline constexpr float Y_POS_MIN = -10.f;
|
||||||
inline constexpr float Y_POS_MAX = 10.f;
|
inline constexpr float Y_POS_MAX = 10.f;
|
||||||
inline constexpr Uint16 AXIS_DEADZONE = 5000;
|
inline constexpr Uint16 AXIS_DEADZONE = 5000;
|
||||||
|
|
||||||
struct VertexShaderParameters
|
|
||||||
{
|
|
||||||
glm::mat4 worldToView;
|
|
||||||
glm::mat4 viewToClip;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreeDSceneApp::init(const AppInitArgs& args)
|
void ThreeDSceneApp::init(const AppInitArgs& args)
|
||||||
@ -38,30 +31,11 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
|
|||||||
.height = 720
|
.height = 720
|
||||||
});
|
});
|
||||||
|
|
||||||
// create graphics pipeline
|
// init UI renderer
|
||||||
createMeshPipeline();
|
|
||||||
|
|
||||||
// create UI pipeline
|
|
||||||
mUIRenderer.init(*this);
|
mUIRenderer.init(*this);
|
||||||
|
|
||||||
// load the mesh
|
// init scene renderer
|
||||||
const Mesh mesh = loadMesh(mFileSystem.getPath("meshes/cube.obj"));
|
mSceneRenderer.init(*this);
|
||||||
mNumVertices = static_cast<Uint32>(mesh.vertices.size());
|
|
||||||
|
|
||||||
// create vertex buffer
|
|
||||||
mVertexBuffer.create(mDevice, {
|
|
||||||
.usage = {.vertex = true},
|
|
||||||
.size = static_cast<Uint32>(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, {});
|
|
||||||
|
|
||||||
// open gamepad
|
// open gamepad
|
||||||
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
|
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
|
||||||
@ -84,6 +58,11 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
|
|||||||
.posX = 100,
|
.posX = 100,
|
||||||
.posY = 500
|
.posY = 500
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// init the scene
|
||||||
|
mScene.init({.renderer = &mSceneRenderer});
|
||||||
|
mScene.getRootNode().emplaceChild<MeshNode>({.mesh = "meshes/cube.obj", .texture = "bitmaps/cube.png"});
|
||||||
|
|
||||||
mButton->clicked.connect([&]()
|
mButton->clicked.connect([&]()
|
||||||
{
|
{
|
||||||
mButton->setText("Thanks!");
|
mButton->setText("Thanks!");
|
||||||
@ -116,39 +95,14 @@ void ThreeDSceneApp::update(const AppUpdateArgs& args)
|
|||||||
mLastSwapchainHeight = swapchainHeight;
|
mLastSwapchainHeight = swapchainHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// render the 3D "scene"
|
// render the scene
|
||||||
const VertexShaderParameters vertexShaderParameters = {
|
mSceneRenderer.render({
|
||||||
.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))
|
.cmdBuffer = &cmdBuffer,
|
||||||
* glm::rotate(glm::mat4(1.f), glm::radians(mRotation), glm::vec3(0.f, 1.f, 0.f)),
|
.targetTexture = &swapchainTexture,
|
||||||
.viewToClip = glm::perspectiveFov(
|
.depthTexture = &mDepthBuffer,
|
||||||
/* fov = */ glm::radians(90.f),
|
.targetTextureWidth = swapchainWidth,
|
||||||
/* width = */ static_cast<float>(swapchainWidth),
|
.targetTextureHeight = swapchainHeight
|
||||||
/* height = */ static_cast<float>(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
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
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"
|
// render the "UI"
|
||||||
mUIRenderer.render({
|
mUIRenderer.render({
|
||||||
@ -192,72 +146,6 @@ void ThreeDSceneApp::handleMouseButtonEvent(const sdlpp::MouseButtonEvent& event
|
|||||||
mWidgetTree.notifyMouseButton(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)
|
void ThreeDSceneApp::processInput(const AppUpdateArgs& args)
|
||||||
{
|
{
|
||||||
const std::span<const SDL_bool> keystates = sdlpp::getKeyboardState();
|
const std::span<const SDL_bool> keystates = sdlpp::getKeyboardState();
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include "../gui/label.hpp"
|
#include "../gui/label.hpp"
|
||||||
#include "../gui/widget.hpp"
|
#include "../gui/widget.hpp"
|
||||||
#include "../gui/ui_renderer.hpp"
|
#include "../gui/ui_renderer.hpp"
|
||||||
|
#include "../scene/scene.hpp"
|
||||||
|
#include "../scene/scene_renderer.hpp"
|
||||||
#include "../sdlpp/gamepad.hpp"
|
#include "../sdlpp/gamepad.hpp"
|
||||||
|
|
||||||
namespace sdl_gpu_test
|
namespace sdl_gpu_test
|
||||||
@ -16,20 +18,18 @@ namespace sdl_gpu_test
|
|||||||
class ThreeDSceneApp : public Application
|
class ThreeDSceneApp : public Application
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
sdlpp::GPUBuffer mVertexBuffer;
|
|
||||||
sdlpp::GPUTexture mDepthBuffer;
|
sdlpp::GPUTexture mDepthBuffer;
|
||||||
sdlpp::GPUGraphicsPipeline mMeshPipeline;
|
|
||||||
sdlpp::GPUTexture mTexture;
|
|
||||||
sdlpp::GPUSampler mSampler;
|
|
||||||
|
|
||||||
UIRenderer mUIRenderer;
|
UIRenderer mUIRenderer;
|
||||||
WidgetTree mWidgetTree;
|
WidgetTree mWidgetTree;
|
||||||
|
|
||||||
Label* mLabel;
|
SceneRenderer mSceneRenderer;
|
||||||
Button* mButton;
|
Scene mScene;
|
||||||
|
|
||||||
|
Label* mLabel = nullptr;
|
||||||
|
Button* mButton = nullptr;
|
||||||
|
|
||||||
sdlpp::Gamepad mGamepad;
|
sdlpp::Gamepad mGamepad;
|
||||||
Uint32 mNumVertices = 0;
|
|
||||||
Uint32 mLastSwapchainWidth = 1280;
|
Uint32 mLastSwapchainWidth = 1280;
|
||||||
Uint32 mLastSwapchainHeight = 720;
|
Uint32 mLastSwapchainHeight = 720;
|
||||||
float mRotation = 0.f;
|
float mRotation = 0.f;
|
||||||
@ -41,7 +41,6 @@ public:
|
|||||||
void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override;
|
void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override;
|
||||||
void handleMouseButtonEvent(const sdlpp::MouseButtonEvent&) override;
|
void handleMouseButtonEvent(const sdlpp::MouseButtonEvent&) override;
|
||||||
private:
|
private:
|
||||||
void createMeshPipeline();
|
|
||||||
void processInput(const AppUpdateArgs& args);
|
void processInput(const AppUpdateArgs& args);
|
||||||
};
|
};
|
||||||
} // namespace sdl_gpu_test
|
} // namespace sdl_gpu_test
|
||||||
|
@ -9,6 +9,9 @@ src_files = Split("""
|
|||||||
gui/label.cpp
|
gui/label.cpp
|
||||||
gui/ui_renderer.cpp
|
gui/ui_renderer.cpp
|
||||||
gui/widget.cpp
|
gui/widget.cpp
|
||||||
|
scene/mesh.cpp
|
||||||
|
scene/scene.cpp
|
||||||
|
scene/scene_renderer.cpp
|
||||||
util/bitmap.cpp
|
util/bitmap.cpp
|
||||||
util/font_map.cpp
|
util/font_map.cpp
|
||||||
util/mesh.cpp
|
util/mesh.cpp
|
||||||
|
62
private/sdl_gpu_test/scene/mesh.cpp
Normal file
62
private/sdl_gpu_test/scene/mesh.cpp
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
private/sdl_gpu_test/scene/mesh.hpp
Normal file
41
private/sdl_gpu_test/scene/mesh.hpp
Normal file
@ -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<SceneMesh> mMesh;
|
||||||
|
std::shared_ptr<SceneTexture> 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)
|
42
private/sdl_gpu_test/scene/scene.cpp
Normal file
42
private/sdl_gpu_test/scene/scene.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
71
private/sdl_gpu_test/scene/scene.hpp
Normal file
71
private/sdl_gpu_test/scene/scene.hpp
Normal file
@ -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 <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "./scene_renderer.hpp"
|
||||||
|
#include "./transform3d.hpp"
|
||||||
|
|
||||||
|
namespace sdl_gpu_test
|
||||||
|
{
|
||||||
|
using scene_node_ptr_t = std::unique_ptr<class SceneNode>;
|
||||||
|
|
||||||
|
struct SceneNodeCreateArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class SceneNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using create_args_t = SceneNodeCreateArgs;
|
||||||
|
protected:
|
||||||
|
std::vector<scene_node_ptr_t> 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<typename TNode>
|
||||||
|
TNode* emplaceChild(typename TNode::create_args_t args)
|
||||||
|
{
|
||||||
|
return static_cast<TNode*>(addChild(std::make_unique<TNode>(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)
|
184
private/sdl_gpu_test/scene/scene_renderer.cpp
Normal file
184
private/sdl_gpu_test/scene/scene_renderer.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
|
||||||
|
#include "./scene_renderer.hpp"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#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<SceneMesh> SceneRenderer::getMesh(const std::string& resourcePath)
|
||||||
|
{
|
||||||
|
auto it = mCachedMeshes.find(resourcePath);
|
||||||
|
if (it != mCachedMeshes.end())
|
||||||
|
{
|
||||||
|
if (std::shared_ptr<SceneMesh> result = it->second.lock(); result != nullptr)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
mCachedMeshes.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Mesh mesh = loadMesh(mApplication->getFileSystem().getPath(resourcePath));
|
||||||
|
std::shared_ptr<SceneMesh> result = std::make_shared<SceneMesh>();
|
||||||
|
result->numVertices = mesh.vertices.size();
|
||||||
|
result->vertexBuffer.create(mApplication->getDevice(), {
|
||||||
|
.usage = {.vertex = true},
|
||||||
|
.size = static_cast<Uint32>(result->numVertices * sizeof(Vertex))
|
||||||
|
});
|
||||||
|
mApplication->uploadVertexData(result->vertexBuffer, std::span(mesh.vertices));
|
||||||
|
mCachedMeshes.emplace(resourcePath, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SceneTexture> SceneRenderer::getTexture(const std::string& resourcePath)
|
||||||
|
{
|
||||||
|
auto it = mCachedTextures.find(resourcePath);
|
||||||
|
if (it != mCachedTextures.end())
|
||||||
|
{
|
||||||
|
if (std::shared_ptr<SceneTexture> result = it->second.lock(); result != nullptr)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
mCachedTextures.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Bitmap bitmap = loadBitmap(mApplication->getFileSystem().getPath(resourcePath));
|
||||||
|
std::shared_ptr<SceneTexture> result = std::make_shared<SceneTexture>();
|
||||||
|
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<float>(args.targetTextureWidth),
|
||||||
|
/* height = */ static_cast<float>(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<const VertexShaderParameters>(&vertexShaderParameters, 1));
|
||||||
|
renderPass.bindFragmentSampler({.texture = entry.texture->texture, .sampler = mSampler});
|
||||||
|
renderPass.bindVertexBuffer({.buffer = entry.mesh->vertexBuffer});
|
||||||
|
renderPass.drawPrimitives({.numVertices = static_cast<Uint32>(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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
72
private/sdl_gpu_test/scene/scene_renderer.hpp
Normal file
72
private/sdl_gpu_test/scene/scene_renderer.hpp
Normal file
@ -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 <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<SceneMesh> mesh;
|
||||||
|
std::shared_ptr<SceneTexture> texture;
|
||||||
|
Transform3D transform;
|
||||||
|
mesh_idx_t meshIdx;
|
||||||
|
};
|
||||||
|
|
||||||
|
Application* mApplication = nullptr;
|
||||||
|
std::unordered_map<std::string, std::weak_ptr<SceneMesh>> mCachedMeshes;
|
||||||
|
std::unordered_map<std::string, std::weak_ptr<SceneTexture>> mCachedTextures;
|
||||||
|
std::vector<RenderListEntry> mRenderList;
|
||||||
|
|
||||||
|
sdlpp::GPUGraphicsPipeline mPipeline;
|
||||||
|
sdlpp::GPUSampler mSampler;
|
||||||
|
public:
|
||||||
|
void init(Application& application);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::shared_ptr<SceneMesh> getMesh(const std::string& resourcePath);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::shared_ptr<SceneTexture> 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)
|
82
private/sdl_gpu_test/scene/transform3d.hpp
Normal file
82
private/sdl_gpu_test/scene/transform3d.hpp
Normal file
@ -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 <glm/mat4x4.hpp>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <glm/gtx/euler_angles.hpp>
|
||||||
|
#include <glm/gtx/matrix_decompose.hpp>
|
||||||
|
#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<typename T>
|
||||||
|
[[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)
|
Loading…
x
Reference in New Issue
Block a user