diff options
Diffstat (limited to 'src/3d')
| -rw-r--r-- | src/3d/object.h | 15 | ||||
| -rw-r--r-- | src/3d/renderer.cc | 286 | ||||
| -rw-r--r-- | src/3d/renderer.h | 23 | ||||
| -rw-r--r-- | src/3d/renderer_mesh.cc | 202 | ||||
| -rw-r--r-- | src/3d/renderer_sdf.cc | 158 | ||||
| -rw-r--r-- | src/3d/renderer_skybox.cc | 94 | ||||
| -rw-r--r-- | src/3d/visual_debug.cc | 29 | ||||
| -rw-r--r-- | src/3d/visual_debug.h | 4 |
8 files changed, 563 insertions, 248 deletions
diff --git a/src/3d/object.h b/src/3d/object.h index 6d21393..c2d9ba1 100644 --- a/src/3d/object.h +++ b/src/3d/object.h @@ -5,6 +5,7 @@ #pragma once #include "util/mini_math.h" +#include "util/asset_manager_dcl.h" enum class ObjectType { CUBE, @@ -12,7 +13,8 @@ enum class ObjectType { PLANE, TORUS, BOX, - SKYBOX + SKYBOX, + MESH // Add more SDF types here }; @@ -37,10 +39,15 @@ class Object3D { float restitution; bool is_static; + AssetId mesh_asset_id; + vec3 local_extent; // Half-extents for AABB (used by meshes for shadows) + void* user_data; // For tool-specific data, not for general use + Object3D(ObjectType t = ObjectType::CUBE) : position(0, 0, 0), rotation(0, 0, 0, 1), scale(1, 1, 1), type(t), color(1, 1, 1, 1), velocity(0, 0, 0), mass(1.0f), restitution(0.5f), - is_static(false) { + is_static(false), mesh_asset_id((AssetId)0), local_extent(1, 1, 1), + user_data(nullptr) { } mat4 get_model_matrix() const { @@ -56,7 +63,9 @@ class Object3D { BoundingVolume get_local_bounds() const { if (type == ObjectType::TORUS) return {{-1.5f, -0.5f, -1.5f}, {1.5f, 0.5f, 1.5f}}; + if (type == ObjectType::MESH) + return {-local_extent, local_extent}; // Simple defaults for unit primitives return {{-1, -1, -1}, {1, 1, 1}}; } -}; +};
\ No newline at end of file diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index 9dfc1f8..eea3ff0 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,157 +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 -} - -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); - - // DEBUG: Print shader source to check for include errors - std::cout << "Shader Source (BVH=" << use_bvh << "):" << std::endl << shader_source << std::endl; - -#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; -} - void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, float time) { GlobalUniforms globals; @@ -399,7 +172,9 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, type_id = 3.0f; else if (obj.type == ObjectType::PLANE) type_id = 4.0f; - data.params = vec4(type_id, 0, 0, 0); + else if (obj.type == ObjectType::MESH) + type_id = 5.0f; + data.params = vec4(type_id, obj.local_extent.x, obj.local_extent.y, obj.local_extent.z); obj_data.push_back(data); if (obj_data.size() >= kMaxObjects) break; @@ -497,15 +272,42 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene, (uint32_t)std::min((size_t)kMaxObjects, scene.objects.size()); if (instance_count > 0) { + wgpuRenderPassEncoderSetPipeline(pass, current_pipeline); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); wgpuRenderPassEncoderDraw(pass, 36, instance_count, 0, 0); + + // Mesh pass + if (mesh_pipeline_) { + wgpuRenderPassEncoderSetPipeline(pass, mesh_pipeline_); + // Bind group is the same layout + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); + + for (uint32_t i = 0; i < instance_count; ++i) { + const auto& obj = scene.objects[i]; + if (obj.type == ObjectType::MESH) { + const MeshGpuData* mesh = temp_mesh_override_ ? temp_mesh_override_ : get_or_create_mesh(obj.mesh_asset_id); + if (mesh) { + wgpuRenderPassEncoderSetVertexBuffer(pass, 0, mesh->vertex_buffer, 0, + WGPU_WHOLE_SIZE); + wgpuRenderPassEncoderSetIndexBuffer( + pass, mesh->index_buffer, WGPUIndexFormat_Uint32, 0, + WGPU_WHOLE_SIZE); + wgpuRenderPassEncoderDrawIndexed(pass, mesh->num_indices, 1, 0, 0, + i); + } + } + } + } } #if !defined(STRIP_ALL) if (s_debug_enabled_) { for (const auto& obj : scene.objects) { - vec3 extent(1.0f, 1.0f, 1.0f); + vec3 extent = obj.local_extent; if (obj.type == ObjectType::TORUS) { extent = vec3(1.5f, 0.5f, 1.5f); + } else if (obj.type != ObjectType::MESH) { + extent = vec3(1.0f, 1.0f, 1.0f); } visual_debug_.add_box(obj.get_model_matrix(), extent, vec3(1.0f, 1.0f, 0.0f)); // Yellow boxes @@ -620,4 +422,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.h b/src/3d/renderer.h index db86b72..5c9fd38 100644 --- a/src/3d/renderer.h +++ b/src/3d/renderer.h @@ -8,6 +8,7 @@ #include "3d/scene.h" #include "3d/bvh.h" #include "gpu/gpu.h" +#include <map> #include <vector> #if !defined(STRIP_ALL) @@ -64,12 +65,30 @@ class Renderer3D { // Set whether to use BVH acceleration void SetBvhEnabled(bool enabled) { bvh_enabled_ = enabled; } + struct MeshGpuData { + WGPUBuffer vertex_buffer; + WGPUBuffer index_buffer; + uint32_t num_indices; + }; + + // HACK for test_mesh tool + void override_mesh_buffers(const MeshGpuData* data) { + temp_mesh_override_ = data; + } + +#if !defined(STRIP_ALL) + VisualDebug& GetVisualDebug() { return visual_debug_; } +#endif + private: + void create_pipeline(); WGPURenderPipeline create_pipeline_impl(bool use_bvh); + void create_mesh_pipeline(); void create_skybox_pipeline(); void create_default_resources(); void update_uniforms(const Scene& scene, const Camera& camera, float time); + const MeshGpuData* get_or_create_mesh(AssetId asset_id); WGPUDevice device_ = nullptr; WGPUQueue queue_ = nullptr; @@ -78,6 +97,7 @@ class Renderer3D { WGPURenderPipeline pipeline_ = nullptr; // BVH enabled WGPURenderPipeline pipeline_no_bvh_ = nullptr; // BVH disabled WGPUBindGroup bind_group_ = nullptr; + WGPURenderPipeline mesh_pipeline_ = nullptr; WGPURenderPipeline skybox_pipeline_ = nullptr; WGPUBindGroup skybox_bind_group_ = nullptr; WGPUBuffer global_uniform_buffer_ = nullptr; @@ -87,6 +107,9 @@ class Renderer3D { BVH cpu_bvh_; // Keep a CPU-side copy for building/uploading bool bvh_enabled_ = true; + std::map<AssetId, MeshGpuData> mesh_cache_; + const MeshGpuData* temp_mesh_override_ = nullptr; // HACK for test_mesh tool + WGPUTextureView noise_texture_view_ = nullptr; WGPUTextureView sky_texture_view_ = nullptr; WGPUSampler default_sampler_ = nullptr; diff --git a/src/3d/renderer_mesh.cc b/src/3d/renderer_mesh.cc new file mode 100644 index 0000000..0a098ef --- /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_utils.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); +} diff --git a/src/3d/visual_debug.cc b/src/3d/visual_debug.cc index 86f12b4..9667d87 100644 --- a/src/3d/visual_debug.cc +++ b/src/3d/visual_debug.cc @@ -6,7 +6,7 @@ #if !defined(STRIP_ALL) #include "generated/assets.h" -#include "util/asset_manager.h" +#include "util/asset_manager_utils.h" #include <cstdio> #include <cstring> @@ -107,7 +107,7 @@ void VisualDebug::create_pipeline(WGPUTextureFormat format) { #if defined(DEMO_CROSS_COMPILE_WIN32) pipeline_desc.vertex.entryPoint = "vs_main"; #else - pipeline_desc.vertex.entryPoint = {"vs_main", 7}; + pipeline_desc.vertex.entryPoint = str_view("vs_main"); #endif pipeline_desc.vertex.bufferCount = 1; pipeline_desc.vertex.buffers = &vertex_layout; @@ -117,7 +117,7 @@ void VisualDebug::create_pipeline(WGPUTextureFormat format) { #if defined(DEMO_CROSS_COMPILE_WIN32) fragment_state.entryPoint = "fs_main"; #else - fragment_state.entryPoint = {"fs_main", 7}; + fragment_state.entryPoint = str_view("fs_main"); #endif fragment_state.targetCount = 1; @@ -203,6 +203,29 @@ void VisualDebug::add_aabb(const vec3& min, const vec3& max, } } +void VisualDebug::add_mesh_normals(const mat4& transform, uint32_t num_vertices, + const MeshVertex* vertices) { + if (!vertices || num_vertices == 0) + return; + + mat4 normal_matrix = mat4::transpose(transform.inverse()); + + for (uint32_t i = 0; i < num_vertices; ++i) { + const auto& v = vertices[i]; + + vec4 p_world = transform * vec4(v.p[0], v.p[1], v.p[2], 1.0f); + vec3 n_object = vec3(v.n[0], v.n[1], v.n[2]); + vec4 n_world_h = normal_matrix * vec4(n_object.x, n_object.y, n_object.z, 0.0f); + vec3 n_world = n_world_h.xyz().normalize(); + + lines_.push_back( + {p_world.xyz(), p_world.xyz() + n_world * 0.1f, // 0.1 is the length + {0.0f, 1.0f, 1.0f} // Cyan color + }); + } +} + + void VisualDebug::update_buffers(const mat4& view_proj) { // Update Uniforms wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), uniform_buffer_, 0, diff --git a/src/3d/visual_debug.h b/src/3d/visual_debug.h index 6173fc4..505a799 100644 --- a/src/3d/visual_debug.h +++ b/src/3d/visual_debug.h @@ -8,6 +8,8 @@ #include "gpu/gpu.h" #include "util/mini_math.h" +#include "util/asset_manager_utils.h" +#include "3d/object.h" #include <vector> struct DebugLine { @@ -27,6 +29,8 @@ class VisualDebug { void add_aabb(const vec3& min, const vec3& max, const vec3& color); + void add_mesh_normals(const mat4& transform, uint32_t num_vertices, const MeshVertex* vertices); + // Render all queued primitives and clear the queue void render(WGPURenderPassEncoder pass, const mat4& view_proj); |
