Compare commits

..

2 Commits

Author SHA1 Message Date
c44f87ad00 SceneRenderer (WIP). 2024-09-20 17:56:33 +02:00
122190e44b Removed CLion project from the repo. 2024-09-20 17:56:10 +02:00
19 changed files with 583 additions and 338 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# Project
.idea/
# Executables
/bin
/bin_*

8
.idea/.gitignore generated vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Better" />
</state>
</component>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CLionExternalBuildManager">
<target id="cab1ed29-9bf0-48fc-9e34-104cc93d7c34" name="Scons Debug" defaultType="TOOL">
<configuration id="4a9d6d63-54eb-4f76-b81f-b3afffdb4626" name="Scons Debug">
<build type="TOOL">
<tool actionId="Tool_External Tools_Scons Debug" />
</build>
<clean type="TOOL">
<tool actionId="Tool_External Tools_Scons Debug Clean" />
</clean>
</configuration>
</target>
</component>
</project>

16
.idea/editor.xml generated
View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BackendCodeEditorSettings">
<option name="/Default/Housekeeping/GlobalSettingsUpgraded/IsUpgraded/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Astring_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Futil_003Fstring_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Astream_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Fio_003Fstream_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Aassert_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Fdebug_003Fassert_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Awinundef_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Futil_003Fwinundef_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Asocket_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Fnet_003Fsocket_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWinSock2_002Eh_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FProgram_0020Files_0020_0028x86_0029_003FWindows_0020Kits_003F10_003Finclude_003F10_002E0_002E22621_002E0_003Fum_003FWinSock2_002Eh/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Aws2tcpip_002Eh_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FProgram_0020Files_0020_0028x86_0029_003FWindows_0020Kits_003F10_003Finclude_003F10_002E0_002E22621_002E0_003Fum_003Fws2tcpip_002Eh/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Aip_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Fnet_003Fip_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=8BC9CEB8_002D8B4A_002D11D0_002D8D11_002D00A0C91BC942_002Ff_003Aip_002Ecpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Fnet_003Fip_002Ecpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
<option name="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Anet_005Fcommon_002Ehpp_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FLocal_003Fspp_005Fcache_003Fcloned_003Fgit_003Fmijin_003F4249f9388d95_003Fsource_003Fmijin_003Fnet_003Fdetail_003Fnet_005Fcommon_002Ehpp/@EntryIndexedValue" value="ExplicitlyExcluded" type="string" />
</component>
</project>

17
.idea/misc.xml generated
View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompDBSettings">
<option name="linkedExternalProjectsSettings">
<CompDBProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</CompDBProjectSettings>
</option>
</component>
<component name="CompDBWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="ExternalStorageConfigurationManager" enabled="true" />
</project>

View File

@ -1,16 +0,0 @@
<toolSet name="External Tools">
<tool name="Scons Debug" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
<exec>
<option name="COMMAND" value="scons" />
<option name="PARAMETERS" value="-j14 --build_type=debug --unity=disable" />
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
</exec>
</tool>
<tool name="Scons Debug Clean" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
<exec>
<option name="COMMAND" value="scons" />
<option name="PARAMETERS" value="-j14 --build_type=debug --unity=disable -c" />
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
</exec>
</tool>
</toolSet>

7
.idea/vcs.xml generated
View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/external/scons-plus-plus" vcs="Git" />
</component>
</project>

118
.idea/workspace.xml generated
View File

@ -1,118 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="CMakeRunConfigurationManager">
<generated />
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="47989f6f-f370-4d63-b72a-bbc574e0808a" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/SConstruct" beforeDir="false" afterPath="$PROJECT_DIR$/SConstruct" afterDir="false" />
<change beforePath="$PROJECT_DIR$/private/spp_template/SModule" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/private/spp_template/main.cpp" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="CompDBLocalSettings">
<option name="availableProjects">
<map>
<entry>
<key>
<ExternalProjectPojo>
<option name="name" value="sdl_gpu_test" />
<option name="path" value="$PROJECT_DIR$" />
</ExternalProjectPojo>
</key>
<value>
<list>
<ExternalProjectPojo>
<option name="name" value="sdl_gpu_test" />
<option name="path" value="$PROJECT_DIR$" />
</ExternalProjectPojo>
</list>
</value>
</entry>
</map>
</option>
<option name="projectSyncType">
<map>
<entry key="$PROJECT_DIR$" value="RE_IMPORT" />
</map>
</option>
</component>
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
</projectState>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 5
}</component>
<component name="ProjectId" id="2i9uuaw4bEGvLlTjsdQ0TGqrg69" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Custom Build Application.Debug.executor&quot;: &quot;Debug&quot;,
&quot;RunOnceActivity.RadMigrateCodeStyle&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;RunOnceActivity.readMode.enableVisualFormatting&quot;: &quot;true&quot;,
&quot;cf.advertisement.text.has.clang-format&quot;: &quot;true&quot;,
&quot;cf.first.check.clang-format&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/mewin/Documents/projects/C++/sdl_gpu_test/compile_commands.json&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;CLionExternalConfigurable&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="RunManager">
<configuration name="Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sdl_gpu_test" TARGET_NAME="Scons Debug" CONFIG_NAME="Scons Debug" RUN_PATH="$PROJECT_DIR$/bin/sdl_gpu_test">
<method v="2">
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
</method>
</configuration>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="47989f6f-f370-4d63-b72a-bbc574e0808a" name="Changes" comment="" />
<created>1718915337529</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1718915337529</updated>
<workItem from="1718915338619" duration="23000" />
<workItem from="1718915550483" duration="442000" />
<workItem from="1718916001754" duration="8000" />
<workItem from="1725911967308" duration="1000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>

View File

@ -3,10 +3,9 @@
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#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<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, {});
// init scene renderer
mSceneRenderer.init(*this);
// open gamepad
const std::vector<SDL_JoystickID> 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<MeshNode>({.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<float>(swapchainWidth),
/* 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
}
// 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<const SDL_bool> keystates = sdlpp::getKeyboardState();

View File

@ -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

View File

@ -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

View 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);
}
}
}

View 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)

View 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;
}
}

View 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)

View 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
}
});
}
}

View 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)

View 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)