summaryrefslogtreecommitdiff
path: root/src/3d
diff options
context:
space:
mode:
Diffstat (limited to 'src/3d')
-rw-r--r--src/3d/renderer.cc444
-rw-r--r--src/3d/renderer_mesh.cc202
-rw-r--r--src/3d/renderer_sdf.cc158
-rw-r--r--src/3d/renderer_skybox.cc94
4 files changed, 467 insertions, 431 deletions
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc
index c3083fc..513b6f6 100644
--- a/src/3d/renderer.cc
+++ b/src/3d/renderer.cc
@@ -1,5 +1,5 @@
// This file is part of the 64k demo project.
-// It implements the Renderer3D class.
+// It implements the core Renderer3D class logic.
#include "3d/renderer.h"
#include "generated/assets.h"
@@ -37,93 +37,6 @@ void Renderer3D::init(WGPUDevice device, WGPUQueue queue,
#endif
}
-void Renderer3D::create_skybox_pipeline() {
- WGPUBindGroupLayoutEntry entries[3] = {};
- entries[0].binding = 0;
- entries[0].visibility = WGPUShaderStage_Fragment;
- entries[0].texture.sampleType = WGPUTextureSampleType_Float;
- entries[0].texture.viewDimension = WGPUTextureViewDimension_2D;
-
- entries[1].binding = 1;
- entries[1].visibility = WGPUShaderStage_Fragment;
- entries[1].sampler.type = WGPUSamplerBindingType_Filtering;
-
- entries[2].binding = 2;
- entries[2].visibility = WGPUShaderStage_Fragment;
- entries[2].buffer.type = WGPUBufferBindingType_Uniform;
- entries[2].buffer.minBindingSize = sizeof(GlobalUniforms);
-
- WGPUBindGroupLayoutDescriptor bgl_desc = {};
- bgl_desc.entryCount = 3;
- bgl_desc.entries = entries;
- WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
-
- WGPUPipelineLayoutDescriptor pl_desc = {};
- pl_desc.bindGroupLayoutCount = 1;
- pl_desc.bindGroupLayouts = &bgl;
- WGPUPipelineLayout pipeline_layout =
- wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
-
- const uint8_t* shader_code_asset =
- GetAsset(AssetId::ASSET_SHADER_SKYBOX, nullptr);
- std::string shader_source =
- ShaderComposer::Get().Compose({}, (const char*)shader_code_asset);
-
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
- wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
- wgsl_desc.code = shader_source.c_str();
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
-#else
- WGPUShaderSourceWGSL wgsl_desc = {};
- wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_desc.code = str_view(shader_source.c_str());
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
-#endif
- WGPUShaderModule shader_module =
- wgpuDeviceCreateShaderModule(device_, &shader_desc);
-
- WGPURenderPipelineDescriptor desc = {};
- desc.layout = pipeline_layout;
- desc.vertex.module = shader_module;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- desc.vertex.entryPoint = "vs_main";
-#else
- desc.vertex.entryPoint = {"vs_main", 7};
-#endif
- WGPUColorTargetState color_target = {};
- color_target.format = format_;
- color_target.writeMask = WGPUColorWriteMask_All;
- WGPUFragmentState fragment = {};
- fragment.module = shader_module;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- fragment.entryPoint = "fs_main";
-#else
- fragment.entryPoint = {"fs_main", 7};
-#endif
- fragment.targetCount = 1;
- fragment.targets = &color_target;
- desc.fragment = &fragment;
- desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
- desc.primitive.cullMode = WGPUCullMode_Back;
- desc.primitive.frontFace = WGPUFrontFace_CCW;
- desc.multisample.count = 1;
- desc.multisample.mask = 0xFFFFFFFF;
-
- WGPUDepthStencilState depth_stencil = {};
- depth_stencil.format = WGPUTextureFormat_Depth24Plus;
- depth_stencil.depthWriteEnabled = WGPUOptionalBool_False;
- depth_stencil.depthCompare = WGPUCompareFunction_Always;
- desc.depthStencil = &depth_stencil;
-
- skybox_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &desc);
- wgpuBindGroupLayoutRelease(bgl);
- wgpuPipelineLayoutRelease(pipeline_layout);
- wgpuShaderModuleRelease(shader_module);
-}
-
void Renderer3D::shutdown() {
#if !defined(STRIP_ALL)
visual_debug_.shutdown();
@@ -137,6 +50,8 @@ void Renderer3D::shutdown() {
wgpuRenderPipelineRelease(pipeline_no_bvh_);
if (bind_group_)
wgpuBindGroupRelease(bind_group_);
+ if (mesh_pipeline_)
+ wgpuRenderPipelineRelease(mesh_pipeline_);
if (skybox_pipeline_)
wgpuRenderPipelineRelease(skybox_pipeline_);
if (skybox_bind_group_)
@@ -151,6 +66,15 @@ void Renderer3D::shutdown() {
wgpuTextureViewRelease(depth_view_);
if (depth_texture_)
wgpuTextureRelease(depth_texture_);
+
+ // Clear mesh cache
+ for (auto& pair : mesh_cache_) {
+ if (pair.second.vertex_buffer)
+ wgpuBufferRelease(pair.second.vertex_buffer);
+ if (pair.second.index_buffer)
+ wgpuBufferRelease(pair.second.index_buffer);
+ }
+ mesh_cache_.clear();
}
void Renderer3D::resize(int width, int height) {
@@ -214,348 +138,6 @@ void Renderer3D::add_debug_aabb(const vec3& min, const vec3& max,
#endif
}
-void Renderer3D::create_pipeline() {
- pipeline_ = create_pipeline_impl(true); // BVH enabled
- pipeline_no_bvh_ = create_pipeline_impl(false); // BVH disabled
- create_mesh_pipeline();
-}
-
-void Renderer3D::create_mesh_pipeline() {
- std::vector<WGPUBindGroupLayoutEntry> entries;
-
- // Binding 0: Global Uniforms
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 0;
- e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
- e.buffer.type = WGPUBufferBindingType_Uniform;
- e.buffer.minBindingSize = sizeof(GlobalUniforms);
- entries.push_back(e);
- }
-
- // Binding 1: Object Data
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 1;
- e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
- e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
- e.buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects;
- entries.push_back(e);
- }
-
- // Binding 2: BVH Nodes (Optional)
- if (bvh_enabled_) {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 2;
- e.visibility = WGPUShaderStage_Fragment;
- e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
- e.buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2;
- entries.push_back(e);
- }
-
- // Binding 3: Noise Texture
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 3;
- e.visibility = WGPUShaderStage_Fragment;
- e.texture.sampleType = WGPUTextureSampleType_Float;
- e.texture.viewDimension = WGPUTextureViewDimension_2D;
- entries.push_back(e);
- }
-
- // Binding 4: Default Sampler
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 4;
- e.visibility = WGPUShaderStage_Fragment;
- e.sampler.type = WGPUSamplerBindingType_Filtering;
- entries.push_back(e);
- }
-
- // Binding 5: Sky Texture
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 5;
- e.visibility = WGPUShaderStage_Fragment;
- e.texture.sampleType = WGPUTextureSampleType_Float;
- e.texture.viewDimension = WGPUTextureViewDimension_2D;
- entries.push_back(e);
- }
-
- WGPUBindGroupLayoutDescriptor bgl_desc = {};
- bgl_desc.entryCount = (uint32_t)entries.size();
- bgl_desc.entries = entries.data();
- WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
-
- WGPUPipelineLayoutDescriptor pl_desc = {};
- pl_desc.bindGroupLayoutCount = 1;
- pl_desc.bindGroupLayouts = &bgl;
- WGPUPipelineLayout pipeline_layout =
- wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
-
- const char* shader_code_asset = (const char*)GetAsset(AssetId::ASSET_SHADER_MESH);
-
- ShaderComposer::CompositionMap composition_map;
- if (bvh_enabled_) {
- composition_map["render/scene_query_mode"] = "render/scene_query_bvh";
- } else {
- composition_map["render/scene_query_mode"] = "render/scene_query_linear";
- }
- std::string shader_source = ShaderComposer::Get().Compose({}, shader_code_asset, composition_map);
-
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
- wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
- wgsl_desc.code = shader_source.c_str();
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
-#else
- WGPUShaderSourceWGSL wgsl_desc = {};
- wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_desc.code = str_view(shader_source.c_str());
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
-#endif
- WGPUShaderModule shader_module =
- wgpuDeviceCreateShaderModule(device_, &shader_desc);
-
- WGPUVertexAttribute vert_attrs[3] = {};
- // pos
- vert_attrs[0].format = WGPUVertexFormat_Float32x3;
- vert_attrs[0].offset = offsetof(MeshVertex, p);
- vert_attrs[0].shaderLocation = 0;
- // norm
- vert_attrs[1].format = WGPUVertexFormat_Float32x3;
- vert_attrs[1].offset = offsetof(MeshVertex, n);
- vert_attrs[1].shaderLocation = 1;
- // uv
- vert_attrs[2].format = WGPUVertexFormat_Float32x2;
- vert_attrs[2].offset = offsetof(MeshVertex, u);
- vert_attrs[2].shaderLocation = 2;
-
- WGPUVertexBufferLayout vert_layout = {};
- vert_layout.arrayStride = sizeof(MeshVertex);
- vert_layout.stepMode = WGPUVertexStepMode_Vertex;
- vert_layout.attributeCount = 3;
- vert_layout.attributes = vert_attrs;
-
- WGPURenderPipelineDescriptor desc = {};
- desc.layout = pipeline_layout;
- desc.vertex.module = shader_module;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- desc.vertex.entryPoint = "vs_main";
-#else
- desc.vertex.entryPoint = {"vs_main", 7};
-#endif
- desc.vertex.bufferCount = 1;
- desc.vertex.buffers = &vert_layout;
-
- WGPUColorTargetState color_target = {};
- color_target.format = format_;
- color_target.writeMask = WGPUColorWriteMask_All;
- WGPUFragmentState fragment = {};
- fragment.module = shader_module;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- fragment.entryPoint = "fs_main";
-#else
- fragment.entryPoint = {"fs_main", 7};
-#endif
- fragment.targetCount = 1;
- fragment.targets = &color_target;
- desc.fragment = &fragment;
-
- desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
- desc.primitive.cullMode = WGPUCullMode_Back;
- desc.primitive.frontFace = WGPUFrontFace_CCW;
-
- WGPUDepthStencilState depth_stencil = {};
- depth_stencil.format = WGPUTextureFormat_Depth24Plus;
- depth_stencil.depthWriteEnabled = WGPUOptionalBool_True;
- depth_stencil.depthCompare = WGPUCompareFunction_Less;
- desc.depthStencil = &depth_stencil;
-
- desc.multisample.count = 1;
- desc.multisample.mask = 0xFFFFFFFF;
-
- mesh_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &desc);
- wgpuBindGroupLayoutRelease(bgl);
- wgpuPipelineLayoutRelease(pipeline_layout);
- wgpuShaderModuleRelease(shader_module);
-}
-
-WGPURenderPipeline Renderer3D::create_pipeline_impl(bool use_bvh) {
- std::vector<WGPUBindGroupLayoutEntry> entries;
-
- // Binding 0: Global Uniforms
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 0;
- e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
- e.buffer.type = WGPUBufferBindingType_Uniform;
- e.buffer.minBindingSize = sizeof(GlobalUniforms);
- entries.push_back(e);
- }
-
- // Binding 1: Object Data
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 1;
- e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
- e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
- e.buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects;
- entries.push_back(e);
- }
-
- // Binding 2: BVH Nodes (only if BVH is used)
- if (use_bvh) {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 2;
- e.visibility = WGPUShaderStage_Fragment;
- e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
- e.buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2;
- entries.push_back(e);
- }
-
- // Binding 3: Noise Texture
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 3;
- e.visibility = WGPUShaderStage_Fragment;
- e.texture.sampleType = WGPUTextureSampleType_Float;
- e.texture.viewDimension = WGPUTextureViewDimension_2D;
- entries.push_back(e);
- }
-
- // Binding 4: Default Sampler
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 4;
- e.visibility = WGPUShaderStage_Fragment;
- e.sampler.type = WGPUSamplerBindingType_Filtering;
- entries.push_back(e);
- }
-
- // Binding 5: Sky Texture
- {
- WGPUBindGroupLayoutEntry e = {};
- e.binding = 5;
- e.visibility = WGPUShaderStage_Fragment;
- e.texture.sampleType = WGPUTextureSampleType_Float;
- e.texture.viewDimension = WGPUTextureViewDimension_2D;
- entries.push_back(e);
- }
-
- WGPUBindGroupLayoutDescriptor bgl_desc = {};
- bgl_desc.entryCount = (uint32_t)entries.size();
- bgl_desc.entries = entries.data();
- WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
-
- WGPUPipelineLayoutDescriptor pl_desc = {};
- pl_desc.bindGroupLayoutCount = 1;
- pl_desc.bindGroupLayouts = &bgl;
- WGPUPipelineLayout pipeline_layout =
- wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
-
- const char* asset_data =
- (const char*)GetAsset(AssetId::ASSET_SHADER_RENDERER_3D);
- std::string main_code = asset_data;
-
- // Use ShaderComposer to dynamically include the correct scene_query snippet
- ShaderComposer::CompositionMap composition_map;
- if (use_bvh) {
- composition_map["render/scene_query_mode"] = "render/scene_query_bvh";
- } else {
- composition_map["render/scene_query_mode"] = "render/scene_query_linear";
- }
- std::string shader_source = ShaderComposer::Get().Compose({}, main_code, composition_map);
-
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
- wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
- wgsl_desc.code = shader_source.c_str();
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
-#else
- WGPUShaderSourceWGSL wgsl_desc = {};
- wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_desc.code = str_view(shader_source.c_str());
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
-#endif
- WGPUShaderModule shader_module =
- wgpuDeviceCreateShaderModule(device_, &shader_desc);
-
- WGPUDepthStencilState depth_stencil = {};
- depth_stencil.format = WGPUTextureFormat_Depth24Plus;
- depth_stencil.depthWriteEnabled = WGPUOptionalBool_True;
- depth_stencil.depthCompare = WGPUCompareFunction_Less;
-
- WGPURenderPipelineDescriptor desc = {};
- desc.layout = pipeline_layout;
- desc.vertex.module = shader_module;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- desc.vertex.entryPoint = "vs_main";
-#else
- desc.vertex.entryPoint = {"vs_main", 7};
-#endif
- WGPUColorTargetState color_target = {};
- color_target.format = format_;
- color_target.writeMask = WGPUColorWriteMask_All;
- WGPUFragmentState fragment = {};
- fragment.module = shader_module;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- fragment.entryPoint = "fs_main";
-#else
- fragment.entryPoint = {"fs_main", 7};
-#endif
- fragment.targetCount = 1;
- fragment.targets = &color_target;
- desc.fragment = &fragment;
- desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
- desc.primitive.cullMode = WGPUCullMode_Back;
- desc.primitive.frontFace = WGPUFrontFace_CCW;
- desc.depthStencil = &depth_stencil;
- desc.multisample.count = 1;
- desc.multisample.mask = 0xFFFFFFFF;
-
- WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device_, &desc);
- wgpuBindGroupLayoutRelease(bgl);
- wgpuPipelineLayoutRelease(pipeline_layout);
- wgpuShaderModuleRelease(shader_module);
-
- return pipeline;
-}
-
-const Renderer3D::MeshGpuData*
-Renderer3D::get_or_create_mesh(AssetId asset_id) {
- auto it = mesh_cache_.find(asset_id);
- if (it != mesh_cache_.end()) {
- return &it->second;
- }
-
- MeshAsset asset = GetMeshAsset(asset_id);
- if (!asset.vertices || asset.num_vertices == 0) {
- return nullptr;
- }
-
- MeshGpuData data;
- data.num_indices = asset.num_indices;
-
- data.vertex_buffer =
- gpu_create_buffer(device_, asset.num_vertices * sizeof(MeshVertex),
- WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst,
- asset.vertices)
- .buffer;
- data.index_buffer =
- gpu_create_buffer(device_, asset.num_indices * sizeof(uint32_t),
- WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst,
- asset.indices)
- .buffer;
-
- mesh_cache_[asset_id] = data;
- return &mesh_cache_[asset_id];
-}
-
void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera,
float time) {
GlobalUniforms globals;
@@ -838,4 +420,4 @@ void Renderer3D::render(const Scene& scene, const Camera& camera, float time,
wgpuCommandBufferRelease(commands);
wgpuCommandEncoderRelease(encoder);
-}
+} \ No newline at end of file
diff --git a/src/3d/renderer_mesh.cc b/src/3d/renderer_mesh.cc
new file mode 100644
index 0000000..97b91d3
--- /dev/null
+++ b/src/3d/renderer_mesh.cc
@@ -0,0 +1,202 @@
+// This file is part of the 64k demo project.
+// It implements mesh-related logic for Renderer3D.
+
+#include "3d/renderer.h"
+#include "generated/assets.h"
+#include "gpu/effects/shader_composer.h"
+#include "util/asset_manager.h"
+#include <cstring>
+#include <vector>
+
+void Renderer3D::create_mesh_pipeline() {
+ std::vector<WGPUBindGroupLayoutEntry> entries;
+
+ // Binding 0: Global Uniforms
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 0;
+ e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
+ e.buffer.type = WGPUBufferBindingType_Uniform;
+ e.buffer.minBindingSize = sizeof(GlobalUniforms);
+ entries.push_back(e);
+ }
+
+ // Binding 1: Object Data
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 1;
+ e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
+ e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
+ e.buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects;
+ entries.push_back(e);
+ }
+
+ // Binding 2: BVH Nodes (Optional)
+ if (bvh_enabled_) {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 2;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
+ e.buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2;
+ entries.push_back(e);
+ }
+
+ // Binding 3: Noise Texture
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 3;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.texture.sampleType = WGPUTextureSampleType_Float;
+ e.texture.viewDimension = WGPUTextureViewDimension_2D;
+ entries.push_back(e);
+ }
+
+ // Binding 4: Default Sampler
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 4;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.sampler.type = WGPUSamplerBindingType_Filtering;
+ entries.push_back(e);
+ }
+
+ // Binding 5: Sky Texture
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 5;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.texture.sampleType = WGPUTextureSampleType_Float;
+ e.texture.viewDimension = WGPUTextureViewDimension_2D;
+ entries.push_back(e);
+ }
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = (uint32_t)entries.size();
+ bgl_desc.entries = entries.data();
+ WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bgl;
+ WGPUPipelineLayout pipeline_layout =
+ wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ const char* shader_code_asset = (const char*)GetAsset(AssetId::ASSET_SHADER_MESH);
+
+ ShaderComposer::CompositionMap composition_map;
+ if (bvh_enabled_) {
+ composition_map["render/scene_query_mode"] = "render/scene_query_bvh";
+ } else {
+ composition_map["render/scene_query_mode"] = "render/scene_query_linear";
+ }
+ std::string shader_source = ShaderComposer::Get().Compose({}, shader_code_asset, composition_map);
+
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
+ wgsl_desc.code = shader_source.c_str();
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#else
+ WGPUShaderSourceWGSL wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_desc.code = str_view(shader_source.c_str());
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#endif
+ WGPUShaderModule shader_module =
+ wgpuDeviceCreateShaderModule(device_, &shader_desc);
+
+ WGPUVertexAttribute vert_attrs[3] = {};
+ // pos
+ vert_attrs[0].format = WGPUVertexFormat_Float32x3;
+ vert_attrs[0].offset = offsetof(MeshVertex, p);
+ vert_attrs[0].shaderLocation = 0;
+ // norm
+ vert_attrs[1].format = WGPUVertexFormat_Float32x3;
+ vert_attrs[1].offset = offsetof(MeshVertex, n);
+ vert_attrs[1].shaderLocation = 1;
+ // uv
+ vert_attrs[2].format = WGPUVertexFormat_Float32x2;
+ vert_attrs[2].offset = offsetof(MeshVertex, u);
+ vert_attrs[2].shaderLocation = 2;
+
+ WGPUVertexBufferLayout vert_layout = {};
+ vert_layout.arrayStride = sizeof(MeshVertex);
+ vert_layout.stepMode = WGPUVertexStepMode_Vertex;
+ vert_layout.attributeCount = 3;
+ vert_layout.attributes = vert_attrs;
+
+ WGPURenderPipelineDescriptor desc = {};
+ desc.layout = pipeline_layout;
+ desc.vertex.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ desc.vertex.entryPoint = "vs_main";
+#else
+ desc.vertex.entryPoint = {"vs_main", 7};
+#endif
+ desc.vertex.bufferCount = 1;
+ desc.vertex.buffers = &vert_layout;
+
+ WGPUColorTargetState color_target = {};
+ color_target.format = format_;
+ color_target.writeMask = WGPUColorWriteMask_All;
+ WGPUFragmentState fragment = {};
+ fragment.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ fragment.entryPoint = "fs_main";
+#else
+ fragment.entryPoint = {"fs_main", 7};
+#endif
+ fragment.targetCount = 1;
+ fragment.targets = &color_target;
+ desc.fragment = &fragment;
+
+ desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ desc.primitive.cullMode = WGPUCullMode_Back;
+ desc.primitive.frontFace = WGPUFrontFace_CCW;
+
+ WGPUDepthStencilState depth_stencil = {};
+ depth_stencil.format = WGPUTextureFormat_Depth24Plus;
+ depth_stencil.depthWriteEnabled = WGPUOptionalBool_True;
+ depth_stencil.depthCompare = WGPUCompareFunction_Less;
+ desc.depthStencil = &depth_stencil;
+
+ desc.multisample.count = 1;
+ desc.multisample.mask = 0xFFFFFFFF;
+
+ mesh_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &desc);
+ wgpuBindGroupLayoutRelease(bgl);
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuShaderModuleRelease(shader_module);
+}
+
+const Renderer3D::MeshGpuData*
+Renderer3D::get_or_create_mesh(AssetId asset_id) {
+ auto it = mesh_cache_.find(asset_id);
+ if (it != mesh_cache_.end()) {
+ return &it->second;
+ }
+
+ MeshAsset asset = GetMeshAsset(asset_id);
+ if (!asset.vertices || asset.num_vertices == 0) {
+ return nullptr;
+ }
+
+ MeshGpuData data;
+ data.num_indices = asset.num_indices;
+
+ data.vertex_buffer =
+ gpu_create_buffer(device_, asset.num_vertices * sizeof(MeshVertex),
+ WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst,
+ asset.vertices)
+ .buffer;
+ data.index_buffer =
+ gpu_create_buffer(device_, asset.num_indices * sizeof(uint32_t),
+ WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst,
+ asset.indices)
+ .buffer;
+
+ mesh_cache_[asset_id] = data;
+ return &mesh_cache_[asset_id];
+}
diff --git a/src/3d/renderer_sdf.cc b/src/3d/renderer_sdf.cc
new file mode 100644
index 0000000..d94df11
--- /dev/null
+++ b/src/3d/renderer_sdf.cc
@@ -0,0 +1,158 @@
+// This file is part of the 64k demo project.
+// It implements SDF-related pipeline logic for Renderer3D.
+
+#include "3d/renderer.h"
+#include "generated/assets.h"
+#include "gpu/effects/shader_composer.h"
+#include "util/asset_manager.h"
+#include <vector>
+
+void Renderer3D::create_pipeline() {
+ pipeline_ = create_pipeline_impl(true); // BVH enabled
+ pipeline_no_bvh_ = create_pipeline_impl(false); // BVH disabled
+ create_mesh_pipeline();
+}
+
+WGPURenderPipeline Renderer3D::create_pipeline_impl(bool use_bvh) {
+ std::vector<WGPUBindGroupLayoutEntry> entries;
+
+ // Binding 0: Global Uniforms
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 0;
+ e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
+ e.buffer.type = WGPUBufferBindingType_Uniform;
+ e.buffer.minBindingSize = sizeof(GlobalUniforms);
+ entries.push_back(e);
+ }
+
+ // Binding 1: Object Data
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 1;
+ e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
+ e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
+ e.buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects;
+ entries.push_back(e);
+ }
+
+ // Binding 2: BVH Nodes (only if BVH is used)
+ if (use_bvh) {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 2;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
+ e.buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2;
+ entries.push_back(e);
+ }
+
+ // Binding 3: Noise Texture
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 3;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.texture.sampleType = WGPUTextureSampleType_Float;
+ e.texture.viewDimension = WGPUTextureViewDimension_2D;
+ entries.push_back(e);
+ }
+
+ // Binding 4: Default Sampler
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 4;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.sampler.type = WGPUSamplerBindingType_Filtering;
+ entries.push_back(e);
+ }
+
+ // Binding 5: Sky Texture
+ {
+ WGPUBindGroupLayoutEntry e = {};
+ e.binding = 5;
+ e.visibility = WGPUShaderStage_Fragment;
+ e.texture.sampleType = WGPUTextureSampleType_Float;
+ e.texture.viewDimension = WGPUTextureViewDimension_2D;
+ entries.push_back(e);
+ }
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = (uint32_t)entries.size();
+ bgl_desc.entries = entries.data();
+ WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bgl;
+ WGPUPipelineLayout pipeline_layout =
+ wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ const char* asset_data =
+ (const char*)GetAsset(AssetId::ASSET_SHADER_RENDERER_3D);
+ std::string main_code = asset_data;
+
+ // Use ShaderComposer to dynamically include the correct scene_query snippet
+ ShaderComposer::CompositionMap composition_map;
+ if (use_bvh) {
+ composition_map["render/scene_query_mode"] = "render/scene_query_bvh";
+ } else {
+ composition_map["render/scene_query_mode"] = "render/scene_query_linear";
+ }
+ std::string shader_source =
+ ShaderComposer::Get().Compose({}, main_code, composition_map);
+
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
+ wgsl_desc.code = shader_source.c_str();
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#else
+ WGPUShaderSourceWGSL wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_desc.code = str_view(shader_source.c_str());
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#endif
+ WGPUShaderModule shader_module =
+ wgpuDeviceCreateShaderModule(device_, &shader_desc);
+
+ WGPUDepthStencilState depth_stencil = {};
+ depth_stencil.format = WGPUTextureFormat_Depth24Plus;
+ depth_stencil.depthWriteEnabled = WGPUOptionalBool_True;
+ depth_stencil.depthCompare = WGPUCompareFunction_Less;
+
+ WGPURenderPipelineDescriptor desc = {};
+ desc.layout = pipeline_layout;
+ desc.vertex.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ desc.vertex.entryPoint = "vs_main";
+#else
+ desc.vertex.entryPoint = {"vs_main", 7};
+#endif
+ WGPUColorTargetState color_target = {};
+ color_target.format = format_;
+ color_target.writeMask = WGPUColorWriteMask_All;
+ WGPUFragmentState fragment = {};
+ fragment.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ fragment.entryPoint = "fs_main";
+#else
+ fragment.entryPoint = {"fs_main", 7};
+#endif
+ fragment.targetCount = 1;
+ fragment.targets = &color_target;
+ desc.fragment = &fragment;
+ desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ desc.primitive.cullMode = WGPUCullMode_Back;
+ desc.primitive.frontFace = WGPUFrontFace_CCW;
+ desc.depthStencil = &depth_stencil;
+ desc.multisample.count = 1;
+ desc.multisample.mask = 0xFFFFFFFF;
+
+ WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device_, &desc);
+ wgpuBindGroupLayoutRelease(bgl);
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuShaderModuleRelease(shader_module);
+
+ return pipeline;
+}
diff --git a/src/3d/renderer_skybox.cc b/src/3d/renderer_skybox.cc
new file mode 100644
index 0000000..203cc07
--- /dev/null
+++ b/src/3d/renderer_skybox.cc
@@ -0,0 +1,94 @@
+// This file is part of the 64k demo project.
+// It implements the skybox pipeline for Renderer3D.
+
+#include "3d/renderer.h"
+#include "generated/assets.h"
+#include "gpu/effects/shader_composer.h"
+#include "util/asset_manager.h"
+
+void Renderer3D::create_skybox_pipeline() {
+ WGPUBindGroupLayoutEntry entries[3] = {};
+ entries[0].binding = 0;
+ entries[0].visibility = WGPUShaderStage_Fragment;
+ entries[0].texture.sampleType = WGPUTextureSampleType_Float;
+ entries[0].texture.viewDimension = WGPUTextureViewDimension_2D;
+
+ entries[1].binding = 1;
+ entries[1].visibility = WGPUShaderStage_Fragment;
+ entries[1].sampler.type = WGPUSamplerBindingType_Filtering;
+
+ entries[2].binding = 2;
+ entries[2].visibility = WGPUShaderStage_Fragment;
+ entries[2].buffer.type = WGPUBufferBindingType_Uniform;
+ entries[2].buffer.minBindingSize = sizeof(GlobalUniforms);
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = 3;
+ bgl_desc.entries = entries;
+ WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bgl;
+ WGPUPipelineLayout pipeline_layout =
+ wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ const uint8_t* shader_code_asset =
+ GetAsset(AssetId::ASSET_SHADER_SKYBOX, nullptr);
+ std::string shader_source =
+ ShaderComposer::Get().Compose({}, (const char*)shader_code_asset);
+
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
+ wgsl_desc.code = shader_source.c_str();
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#else
+ WGPUShaderSourceWGSL wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_desc.code = str_view(shader_source.c_str());
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#endif
+ WGPUShaderModule shader_module =
+ wgpuDeviceCreateShaderModule(device_, &shader_desc);
+
+ WGPURenderPipelineDescriptor desc = {};
+ desc.layout = pipeline_layout;
+ desc.vertex.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ desc.vertex.entryPoint = "vs_main";
+#else
+ desc.vertex.entryPoint = {"vs_main", 7};
+#endif
+ WGPUColorTargetState color_target = {};
+ color_target.format = format_;
+ color_target.writeMask = WGPUColorWriteMask_All;
+ WGPUFragmentState fragment = {};
+ fragment.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ fragment.entryPoint = "fs_main";
+#else
+ fragment.entryPoint = {"fs_main", 7};
+#endif
+ fragment.targetCount = 1;
+ fragment.targets = &color_target;
+ desc.fragment = &fragment;
+ desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ desc.primitive.cullMode = WGPUCullMode_Back;
+ desc.primitive.frontFace = WGPUFrontFace_CCW;
+ desc.multisample.count = 1;
+ desc.multisample.mask = 0xFFFFFFFF;
+
+ WGPUDepthStencilState depth_stencil = {};
+ depth_stencil.format = WGPUTextureFormat_Depth24Plus;
+ depth_stencil.depthWriteEnabled = WGPUOptionalBool_False;
+ depth_stencil.depthCompare = WGPUCompareFunction_Always;
+ desc.depthStencil = &depth_stencil;
+
+ skybox_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &desc);
+ wgpuBindGroupLayoutRelease(bgl);
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuShaderModuleRelease(shader_module);
+}