Implemented scene loading. (Still problems with depth testing, WIP).
This commit is contained in:
parent
e92f414e02
commit
c904353baf
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
7
assets/scenes/test_scene.yml
Normal file
7
assets/scenes/test_scene.yml
Normal file
@ -0,0 +1,7 @@
|
||||
nodes:
|
||||
- type: mesh
|
||||
translation: [0, 0, 0]
|
||||
mesh: meshes/cube.obj
|
||||
- type: mesh
|
||||
translation: [1, 0, -10]
|
||||
mesh: meshes/cube.obj
|
29
assets/shaders/glsl/scene_renderer.vert
Normal file
29
assets/shaders/glsl/scene_renderer.vert
Normal file
@ -0,0 +1,29 @@
|
||||
#version 460
|
||||
|
||||
layout(set = 1, binding = 0)
|
||||
uniform PerSceneParameters
|
||||
{
|
||||
mat4 u_worldToView;
|
||||
mat4 u_viewToClip;
|
||||
};
|
||||
|
||||
layout(set = 1, binding = 1)
|
||||
uniform PerModelParameters
|
||||
{
|
||||
mat4 u_modelToWorld;
|
||||
};
|
||||
|
||||
layout(location = 0)
|
||||
in vec3 i_position;
|
||||
|
||||
layout(location = 1)
|
||||
in vec2 i_texCoord;
|
||||
|
||||
layout(location = 0)
|
||||
out vec2 o_texCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = u_viewToClip * u_worldToView * u_modelToWorld * vec4(i_position, 1.0);
|
||||
o_texCoord = i_texCoord;
|
||||
}
|
@ -114,7 +114,7 @@ void TexturedCubeApp::init(const AppInitArgs& args)
|
||||
.format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB,
|
||||
.usage = {.sampler = true}
|
||||
};
|
||||
mTexture = loadTexture("bitmaps/cube.png", textureArgs);
|
||||
mTexture = loadTexture("meshes/cube.png", textureArgs);
|
||||
mSampler.create(mDevice, {});
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ void InputApp::init(const AppInitArgs& args)
|
||||
.format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB,
|
||||
.usage = {.sampler = true}
|
||||
};
|
||||
mTexture = loadTexture("bitmaps/cube.png", textureArgs);
|
||||
mTexture = loadTexture("meshes/cube.png", textureArgs);
|
||||
mSampler.create(mDevice, {});
|
||||
|
||||
// open gamepad
|
||||
|
@ -60,7 +60,7 @@ void UIApp::init(const AppInitArgs& args)
|
||||
.format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB,
|
||||
.usage = {.sampler = true}
|
||||
};
|
||||
mTexture = loadTexture("bitmaps/cube.png", textureArgs);
|
||||
mTexture = loadTexture("meshes/cube.png", textureArgs);
|
||||
mSampler.create(mDevice, {});
|
||||
|
||||
// open gamepad
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "../scene/mesh.hpp"
|
||||
#include "../scene/loader.hpp"
|
||||
#include "../sdlpp/keyboard.hpp"
|
||||
#include "../util/bitmap.hpp"
|
||||
#include "../util/mesh.hpp"
|
||||
@ -64,7 +65,8 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
|
||||
|
||||
// init the scene
|
||||
mScene.init({.renderer = &mSceneRenderer});
|
||||
mScene.getRootNode().emplaceChild<MeshNode>({.mesh = "meshes/cube.obj", .texture = "bitmaps/cube.png"});
|
||||
loadScene("scenes/test_scene.yml", mScene);
|
||||
// mScene.getRootNode().emplaceChild<MeshNode>({.mesh = "meshes/cube.obj", .texture = "meshes/cube.png"});
|
||||
|
||||
mButton->clicked.connect([&]()
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ src_files = Split("""
|
||||
gui/label.cpp
|
||||
gui/ui_renderer.cpp
|
||||
gui/widget.cpp
|
||||
scene/loader.cpp
|
||||
scene/mesh.cpp
|
||||
scene/scene.cpp
|
||||
scene/scene_renderer.cpp
|
||||
@ -45,7 +46,8 @@ prog_app = env.UnityProgram(
|
||||
},
|
||||
'spdlog': {},
|
||||
'stb': {},
|
||||
'tinyobjloader': {}
|
||||
'tinyobjloader': {},
|
||||
'yaml-cpp': {}
|
||||
}
|
||||
)
|
||||
env.Default(prog_app)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <mijin/util/variant.hpp>
|
||||
#include <mijin/virtual_filesystem/relative.hpp>
|
||||
|
||||
#include "./scene/loader.hpp"
|
||||
#include "./util/bitmap.hpp"
|
||||
#include "./util/spdlog_wrapper.hpp"
|
||||
|
||||
@ -125,6 +126,11 @@ sdlpp::GPUTexture Application::loadTexture(const fs::path& path, sdlpp::GPUTextu
|
||||
return texture;
|
||||
}
|
||||
|
||||
void Application::loadScene(const fs::path& path, Scene& outScene)
|
||||
{
|
||||
sdl_gpu_test::loadScene(mFileSystem.getPath(path), outScene);
|
||||
}
|
||||
|
||||
void Application::uploadTextureData(const sdlpp::GPUTexture& texture, const Bitmap& bitmap) const
|
||||
{
|
||||
sdlpp::GPUTransferBuffer transferBuffer;
|
||||
|
@ -77,6 +77,8 @@ public:
|
||||
[[nodiscard]]
|
||||
sdlpp::GPUTexture loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args);
|
||||
|
||||
void loadScene(const fs::path& path, class Scene& outScene);
|
||||
|
||||
void uploadTextureData(const sdlpp::GPUTexture& texture, const struct Bitmap& bitmap) const;
|
||||
|
||||
template<typename TVertex>
|
||||
|
106
private/sdl_gpu_test/scene/loader.cpp
Normal file
106
private/sdl_gpu_test/scene/loader.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
#include "./loader.hpp"
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include "./mesh.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void loadCommonNodeProperties(const YAML::Node& yaml, SceneNode& node)
|
||||
{
|
||||
const YAML::Node& translationNode = yaml["translation"];
|
||||
glm::vec3 translation = {0.f, 0.f, 0.f};
|
||||
if (translationNode.IsDefined())
|
||||
{
|
||||
mijin::ensure(translationNode.IsSequence() && translationNode.size() == 3
|
||||
&& translationNode[0].IsScalar() && translationNode[1].IsScalar() && translationNode[2].IsScalar(),
|
||||
"Invalid scene YAML: node translation must be sequence of three scalars.");
|
||||
translation.x = translationNode[0].as<float>();
|
||||
translation.y = translationNode[1].as<float>();
|
||||
translation.z = translationNode[2].as<float>();
|
||||
}
|
||||
|
||||
node.setTransform(Transform3D::make(translation));
|
||||
}
|
||||
|
||||
std::unique_ptr<MeshNode> loadMeshNode(const YAML::Node& yaml)
|
||||
{
|
||||
MIJIN_ASSERT(yaml.IsMap(), "yaml must be a map");
|
||||
|
||||
const YAML::Node& meshNode = yaml["mesh"];
|
||||
mijin::ensure(meshNode.IsScalar(), "Invalid scene YAML: invalid or missing mesh property for mesh node.");
|
||||
|
||||
std::string meshPath = meshNode.as<std::string>();
|
||||
std::string texturePath;
|
||||
|
||||
const YAML::Node& textureNode = yaml["texture"];
|
||||
if (!textureNode.IsDefined())
|
||||
{
|
||||
const std::string::size_type dotPos = meshPath.rfind('.');
|
||||
texturePath = std::string_view(meshPath).substr(0, dotPos);
|
||||
texturePath.append(".png");
|
||||
}
|
||||
else
|
||||
{
|
||||
mijin::ensure(textureNode.IsScalar(), "Invalid scene YAML: invalid texture property for mesh node.");
|
||||
texturePath = textureNode.as<std::string>();
|
||||
}
|
||||
|
||||
return std::make_unique<MeshNode>(MeshNodeCreateArgs{
|
||||
.mesh = std::move(meshPath), .texture = std::move(texturePath)
|
||||
});
|
||||
}
|
||||
|
||||
void loadSceneNodes(const YAML::Node& nodesNode, SceneNode& parentNode)
|
||||
{
|
||||
MIJIN_ASSERT(nodesNode.IsSequence(), "nodesNode must be a sequence");
|
||||
|
||||
for (const YAML::Node& nodeNode : nodesNode)
|
||||
{
|
||||
mijin::ensure(nodeNode.IsMap(), "Invalid scene YAML: scene node is not a sequence.");
|
||||
|
||||
std::unique_ptr<SceneNode> newNode;
|
||||
const YAML::Node& typeNode = nodeNode["type"];
|
||||
if (!typeNode.IsDefined())
|
||||
{
|
||||
newNode = std::make_unique<SceneNode>();
|
||||
}
|
||||
else
|
||||
{
|
||||
mijin::ensure(typeNode.IsScalar(), "Invalid scene YAML: scene node type not a scalar.");
|
||||
|
||||
const std::string type = typeNode.as<std::string>();
|
||||
if (type == "mesh")
|
||||
{
|
||||
newNode = loadMeshNode(nodeNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::format("Invalid scene YAML: invalid scene node type '{}'.", type));
|
||||
}
|
||||
}
|
||||
loadCommonNodeProperties(nodeNode, *newNode);
|
||||
parentNode.addChild(std::move(newNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadScene(const mijin::PathReference& path, Scene& outScene)
|
||||
{
|
||||
std::unique_ptr<mijin::Stream> stream;
|
||||
mijin::throwOnError(path.open(mijin::FileOpenMode::READ, stream));
|
||||
|
||||
std::string content;
|
||||
mijin::throwOnError(stream->readAsString(content));
|
||||
|
||||
const YAML::Node root = YAML::Load(content);
|
||||
mijin::ensure(root.IsMap(), "Invalid scene YAML: root is not a map.");
|
||||
|
||||
const YAML::Node& nodesNode = root["nodes"];
|
||||
mijin::ensure(nodesNode.IsSequence(), "Invalid scene YAML: nodes missing or not a sequence.");
|
||||
|
||||
loadSceneNodes(nodesNode, outScene.getRootNode());
|
||||
}
|
||||
}
|
16
private/sdl_gpu_test/scene/loader.hpp
Normal file
16
private/sdl_gpu_test/scene/loader.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_LOADER_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_LOADER_HPP_INCLUDED 1
|
||||
|
||||
#include <mijin/virtual_filesystem/filesystem.hpp>
|
||||
|
||||
#include "./scene.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
void loadScene(const mijin::PathReference& path, Scene& outScene);
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SCENE_LOADER_HPP_INCLUDED)
|
@ -8,6 +8,18 @@ MeshNode::MeshNode(MeshNodeCreateArgs args) : mMeshPath(std::move(args.mesh)), m
|
||||
|
||||
}
|
||||
|
||||
Transform3D MeshNode::getCombinedTransform() const noexcept
|
||||
{
|
||||
Transform3D result = mTransform;
|
||||
|
||||
for (SceneNode* parent = mParent; parent != nullptr; parent = parent->getParent())
|
||||
{
|
||||
result = parent->getTransform().apply(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MeshNode::setMesh(std::string resourcePath)
|
||||
{
|
||||
mMeshPath = std::move(resourcePath);
|
||||
@ -68,10 +80,10 @@ void MeshNode::updateSceneMesh()
|
||||
if (mMeshId == SceneRenderer::UNSET_MESH_ID || !mScene->getRenderer().updateRenderListEntry(mMeshId, {
|
||||
.mesh = mMesh,
|
||||
.texture = mTexture,
|
||||
.transform = mTransform
|
||||
.transform = getCombinedTransform()
|
||||
}))
|
||||
{
|
||||
mScene->getRenderer().addMeshToRenderList(mMesh, mTexture, mTransform, mMeshId);
|
||||
mScene->getRenderer().addMeshToRenderList(mMesh, mTexture, getCombinedTransform(), mMeshId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ private:
|
||||
public:
|
||||
MeshNode(MeshNodeCreateArgs args);
|
||||
|
||||
[[nodiscard]]
|
||||
Transform3D getCombinedTransform() const noexcept;
|
||||
|
||||
void setMesh(std::string resourcePath);
|
||||
void setTexture(std::string resourcePath);
|
||||
|
||||
|
@ -31,6 +31,14 @@ protected:
|
||||
public:
|
||||
virtual ~SceneNode() noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
SceneNode* getParent() const noexcept { return mParent; }
|
||||
|
||||
[[nodiscard]]
|
||||
const Transform3D& getTransform() const noexcept { return mTransform; }
|
||||
|
||||
void setTransform(const Transform3D& transform) noexcept { mTransform = transform; }
|
||||
|
||||
virtual void handleEnteredScene();
|
||||
|
||||
SceneNode* addChild(scene_node_ptr_t&& node);
|
||||
|
@ -13,11 +13,16 @@ namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct VertexShaderParameters
|
||||
{
|
||||
struct PerSceneParameters
|
||||
{
|
||||
glm::mat4 worldToView;
|
||||
glm::mat4 viewToClip;
|
||||
};
|
||||
};
|
||||
|
||||
struct PerModelParameters
|
||||
{
|
||||
glm::mat4 modelToWorld;
|
||||
};
|
||||
}
|
||||
void SceneRenderer::init(Application& application)
|
||||
{
|
||||
@ -76,7 +81,7 @@ std::shared_ptr<SceneTexture> SceneRenderer::getTexture(const std::string& resou
|
||||
|
||||
void SceneRenderer::render(const SceneRendererRenderArgs& args)
|
||||
{
|
||||
VertexShaderParameters vertexShaderParameters = {
|
||||
PerSceneParameters perSceneParameters = {
|
||||
// note that we are transforming the world, but are passed the camera transform, so invert it
|
||||
.worldToView = glm::translate(
|
||||
glm::transpose(glm::yawPitchRoll(args.camera.yaw, args.camera.pitch, args.camera.roll)),
|
||||
@ -107,10 +112,14 @@ void SceneRenderer::render(const SceneRendererRenderArgs& args)
|
||||
|
||||
static const glm::vec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
args.cmdBuffer->pushVertexUniformData(0, std::span<const PerSceneParameters>(&perSceneParameters, 1));
|
||||
for (const RenderListEntry& entry : mRenderList)
|
||||
{
|
||||
PerModelParameters perModelParameters = {
|
||||
.modelToWorld = entry.transform.matrix
|
||||
};
|
||||
args.cmdBuffer->pushFragmentUniformData(0, std::span(&WHITE, 1));
|
||||
args.cmdBuffer->pushVertexUniformData(0, std::span<const VertexShaderParameters>(&vertexShaderParameters, 1));
|
||||
args.cmdBuffer->pushVertexUniformData(1, std::span<const PerModelParameters>(&perModelParameters, 1));
|
||||
renderPass.bindFragmentSampler({.texture = entry.texture->texture, .sampler = mSampler});
|
||||
renderPass.bindVertexBuffer({.buffer = entry.mesh->vertexBuffer});
|
||||
renderPass.drawPrimitives({.numVertices = static_cast<Uint32>(entry.mesh->numVertices)});
|
||||
@ -187,10 +196,10 @@ bool SceneRenderer::updateRenderListEntry(mesh_id_t meshId, const RenderListUpda
|
||||
void SceneRenderer::createPipeline()
|
||||
{
|
||||
// create shaders
|
||||
const sdlpp::GPUShader vertexShader = mApplication->loadShader("shaders/glsl/textured_3dtriangles_from_buffer.vert.spv", {
|
||||
const sdlpp::GPUShader vertexShader = mApplication->loadShader("shaders/glsl/scene_renderer.vert.spv", {
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
.stage = sdlpp::GPUShaderStage::VERTEX,
|
||||
.numUniformBuffers = 1
|
||||
.numUniformBuffers = 2
|
||||
});
|
||||
const sdlpp::GPUShader fragmentShader = mApplication->loadShader("shaders/glsl/color_from_texture.frag.spv", {
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
@ -242,6 +251,10 @@ void SceneRenderer::createPipeline()
|
||||
.rasterizerState = {
|
||||
.cullMode = sdlpp::GPUCullMode::BACK
|
||||
},
|
||||
.depthStencilState = {
|
||||
.enableDepthTest = true,
|
||||
.enableDepthWrite = true
|
||||
},
|
||||
.targetInfo = {
|
||||
.colorTargetDescriptions = colorTargetsDescs,
|
||||
.depthStencilFormat = sdlpp::GPUTextureFormat::D16_UNORM
|
||||
|
@ -464,12 +464,12 @@ static_assert(sizeof(GPUColorTargetInfo) == sizeof(SDL_GPUColorTargetInfo)
|
||||
struct GPUDepthStencilTargetInfo
|
||||
{
|
||||
SDL_GPUTexture* texture = nullptr;
|
||||
float clearDepth = 0.f;
|
||||
float clearDepth = 1.f;
|
||||
GPULoadOp loadOp = GPULoadOp::LOAD;
|
||||
GPUStoreOp storeOp = GPUStoreOp::STORE;
|
||||
GPULoadOp stencilLoadOp = GPULoadOp::LOAD;
|
||||
GPUStoreOp stencilStoreOp = GPUStoreOp::STORE;
|
||||
bool cycle = false;
|
||||
bool cycle = true;
|
||||
Uint8 clearStencil;
|
||||
};
|
||||
static_assert(sizeof(GPUDepthStencilTargetInfo) == sizeof(SDL_GPUDepthStencilTargetInfo)
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
#include "./mesh.hpp"
|
||||
|
||||
#include <mijin/io/stlstream.hpp>
|
||||
#include <tiny_obj_loader.h>
|
||||
|
||||
namespace sdl_gpu_test
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <mijin/io/stlstream.hpp>
|
||||
#include <mijin/virtual_filesystem/filesystem.hpp>
|
||||
|
||||
namespace sdl_gpu_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user