diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/3d/object.h | 10 | ||||
| -rw-r--r-- | src/3d/renderer.cc | 224 | ||||
| -rw-r--r-- | src/3d/renderer.h | 12 | ||||
| -rw-r--r-- | src/gpu/effects/shaders.cc | 1 | ||||
| -rw-r--r-- | src/tests/test_3d_render.cc | 9 | ||||
| -rw-r--r-- | src/util/asset_manager.cc | 19 | ||||
| -rw-r--r-- | src/util/asset_manager.h | 16 |
7 files changed, 285 insertions, 6 deletions
diff --git a/src/3d/object.h b/src/3d/object.h index 6d21393..0c8edd8 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.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,12 @@ class Object3D { float restitution; bool is_static; + AssetId mesh_asset_id; + 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) { } mat4 get_model_matrix() const { @@ -59,4 +63,4 @@ class Object3D { // 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..c3083fc 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -217,6 +217,170 @@ void Renderer3D::add_debug_aabb(const vec3& min, const vec3& max, 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) { @@ -304,9 +468,6 @@ WGPURenderPipeline Renderer3D::create_pipeline_impl(bool use_bvh) { 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 = {}; @@ -365,6 +526,36 @@ WGPURenderPipeline Renderer3D::create_pipeline_impl(bool use_bvh) { 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; @@ -399,6 +590,8 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, type_id = 3.0f; else if (obj.type == ObjectType::PLANE) type_id = 4.0f; + else if (obj.type == ObjectType::MESH) + type_id = 5.0f; data.params = vec4(type_id, 0, 0, 0); obj_data.push_back(data); if (obj_data.size() >= kMaxObjects) @@ -497,7 +690,32 @@ 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 = 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) diff --git a/src/3d/renderer.h b/src/3d/renderer.h index db86b72..5caf19b 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) @@ -65,11 +66,19 @@ class Renderer3D { void SetBvhEnabled(bool enabled) { bvh_enabled_ = enabled; } private: + struct MeshGpuData { + WGPUBuffer vertex_buffer; + WGPUBuffer index_buffer; + uint32_t num_indices; + }; + 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 +87,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 +97,8 @@ class Renderer3D { BVH cpu_bvh_; // Keep a CPU-side copy for building/uploading bool bvh_enabled_ = true; + std::map<AssetId, MeshGpuData> mesh_cache_; + WGPUTextureView noise_texture_view_ = nullptr; WGPUTextureView sky_texture_view_ = nullptr; WGPUSampler default_sampler_ = nullptr; diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc index 4fc8108..0646f92 100644 --- a/src/gpu/effects/shaders.cc +++ b/src/gpu/effects/shaders.cc @@ -40,6 +40,7 @@ void InitShaderComposer() { AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_LINEAR); register_if_exists("render/lighting_utils", AssetId::ASSET_SHADER_RENDER_LIGHTING_UTILS); + register_if_exists("render/mesh", AssetId::ASSET_SHADER_MESH); register_if_exists("sdf_primitives", AssetId::ASSET_SHADER_SDF_PRIMITIVES); diff --git a/src/tests/test_3d_render.cc b/src/tests/test_3d_render.cc index f8bbaa7..ae00819 100644 --- a/src/tests/test_3d_render.cc +++ b/src/tests/test_3d_render.cc @@ -5,6 +5,7 @@ #include "3d/object.h" #include "3d/renderer.h" #include "3d/scene.h" +#include "generated/assets.h" #include "gpu/effects/shaders.h" #include "gpu/texture_manager.h" #include "platform.h" @@ -141,6 +142,14 @@ void setup_scene() { sphere.color = vec4(0.2, 1, 0.2, 1); g_scene.add_object(sphere); + // Mesh Object (Rasterized) + Object3D mesh_obj(ObjectType::MESH); + mesh_obj.position = vec3(-4.0f, 2.0f, 0); + mesh_obj.scale = vec3(2.0f, 2.0f, 2.0f); + mesh_obj.color = vec4(0.2, 0.2, 1, 1); + mesh_obj.mesh_asset_id = AssetId::ASSET_MESH_CUBE; + g_scene.add_object(mesh_obj); + // Random objects for (int i = 0; i < 30; ++i) { ObjectType type = ObjectType::SPHERE; diff --git a/src/util/asset_manager.cc b/src/util/asset_manager.cc index d4dd5c7..c12331c 100644 --- a/src/util/asset_manager.cc +++ b/src/util/asset_manager.cc @@ -153,6 +153,25 @@ TextureAsset GetTextureAsset(AssetId asset_id) { return {(int)header[0], (int)header[1], data + 8}; } +MeshAsset GetMeshAsset(AssetId asset_id) { + size_t size = 0; + const uint8_t* data = GetAsset(asset_id, &size); + if (!data || size < 8) { + return {0, nullptr, 0, nullptr}; + } + + const uint8_t* ptr = data; + uint32_t num_vertices = *reinterpret_cast<const uint32_t*>(ptr); + ptr += sizeof(uint32_t); + const MeshVertex* vertices = reinterpret_cast<const MeshVertex*>(ptr); + ptr += num_vertices * sizeof(MeshVertex); + uint32_t num_indices = *reinterpret_cast<const uint32_t*>(ptr); + ptr += sizeof(uint32_t); + const uint32_t* indices = reinterpret_cast<const uint32_t*>(ptr); + + return {num_vertices, vertices, num_indices, indices}; +} + void DropAsset(AssetId asset_id, const uint8_t* asset) { uint16_t index = (uint16_t)asset_id; if (index >= (uint16_t)AssetId::ASSET_LAST_ID) { diff --git a/src/util/asset_manager.h b/src/util/asset_manager.h index a78447d..0c2cc63 100644 --- a/src/util/asset_manager.h +++ b/src/util/asset_manager.h @@ -39,6 +39,22 @@ struct TextureAsset { const uint8_t* pixels; }; +struct MeshVertex { + float p[3]; + float n[3]; + float u[2]; +}; + +struct MeshAsset { + uint32_t num_vertices; + const MeshVertex* vertices; + uint32_t num_indices; + const uint32_t* indices; +}; + // Helper to retrieve and parse a simple texture asset (from packer's // [w][h][pixels] format) TextureAsset GetTextureAsset(AssetId asset_id); + +// Helper to retrieve and parse a mesh asset (from packer's binary format) +MeshAsset GetMeshAsset(AssetId asset_id); |
