From b68c8d8cbe9274e42a89888186152d4ded1a2962 Mon Sep 17 00:00:00 2001 From: skal Date: Fri, 6 Feb 2026 06:51:16 +0100 Subject: feat(3d): Implement basic OBJ mesh asset pipeline Added support for loading and rendering OBJ meshes. - Updated asset_packer to parse .obj files into a binary format. - Added MeshAsset and GetMeshAsset helper to asset_manager. - Extended Object3D with mesh_asset_id and ObjectType::MESH. - Implemented mesh rasterization pipeline in Renderer3D. - Added a sample cube mesh and verified in test_3d_render. --- assets/final/demo_assets.txt | 4 +- assets/final/shaders/mesh_render.wgsl | 59 +++++++++ assets/final/shaders/renderer_3d.wgsl | 6 + assets/final/test_assets_list.txt | 14 +++ assets/final/test_mesh.obj | 30 +++++ src/3d/object.h | 10 +- src/3d/renderer.cc | 224 +++++++++++++++++++++++++++++++++- src/3d/renderer.h | 12 ++ src/gpu/effects/shaders.cc | 1 + src/tests/test_3d_render.cc | 9 ++ src/util/asset_manager.cc | 19 +++ src/util/asset_manager.h | 16 +++ 12 files changed, 397 insertions(+), 7 deletions(-) create mode 100644 assets/final/shaders/mesh_render.wgsl create mode 100644 assets/final/test_mesh.obj diff --git a/assets/final/demo_assets.txt b/assets/final/demo_assets.txt index c38da51..0febd75 100644 --- a/assets/final/demo_assets.txt +++ b/assets/final/demo_assets.txt @@ -46,4 +46,6 @@ SHADER_MATH_SDF_UTILS, NONE, shaders/math/sdf_utils.wgsl, "SDF Utils Snippet" SHADER_RENDER_SHADOWS, NONE, shaders/render/shadows.wgsl, "Shadows Snippet" SHADER_RENDER_SCENE_QUERY_BVH, NONE, shaders/render/scene_query_bvh.wgsl, "Scene Query Snippet (BVH)" SHADER_RENDER_SCENE_QUERY_LINEAR, NONE, shaders/render/scene_query_linear.wgsl, "Scene Query Snippet (Linear)" -SHADER_RENDER_LIGHTING_UTILS, NONE, shaders/render/lighting_utils.wgsl, "Lighting Utils Snippet" \ No newline at end of file +SHADER_RENDER_LIGHTING_UTILS, NONE, shaders/render/lighting_utils.wgsl, "Lighting Utils Snippet" +SHADER_MESH, NONE, shaders/mesh_render.wgsl, "Mesh Rasterization Shader" +MESH_CUBE, NONE, test_mesh.obj, "A simple cube mesh" \ No newline at end of file diff --git a/assets/final/shaders/mesh_render.wgsl b/assets/final/shaders/mesh_render.wgsl new file mode 100644 index 0000000..3759747 --- /dev/null +++ b/assets/final/shaders/mesh_render.wgsl @@ -0,0 +1,59 @@ +#include "common_uniforms" + +@group(0) @binding(0) var globals: GlobalUniforms; +@group(0) @binding(1) var object_data: ObjectsBuffer; + +// Binding 2 is reserved for BVH (not used here but matches layout for simplicity) + +@group(0) @binding(3) var noise_tex: texture_2d; +@group(0) @binding(4) var noise_sampler: sampler; +@group(0) @binding(5) var sky_tex: texture_2d; + +struct VertexInput { + @location(0) position: vec3, + @location(1) normal: vec3, + @location(2) uv: vec2, +}; + +struct VertexOutput { + @builtin(position) clip_pos: vec4, + @location(0) world_pos: vec3, + @location(1) normal: vec3, + @location(2) uv: vec2, + @location(3) color: vec4, + @location(4) @interpolate(flat) instance_index: u32, +}; + +@vertex +fn vs_main(in: VertexInput, @builtin(instance_index) instance_index: u32) -> VertexOutput { + let obj = object_data.objects[instance_index]; + let world_pos = obj.model * vec4(in.position, 1.0); + + var out: VertexOutput; + out.clip_pos = globals.view_proj * world_pos; + out.world_pos = world_pos.xyz; + + // Normal transform (assuming uniform scale or using transpose(inverse(model))) + // For simplicity, we use the same mat3 logic as renderer_3d.wgsl + let normal_matrix = mat3x3(obj.model[0].xyz, obj.model[1].xyz, obj.model[2].xyz); + out.normal = normalize(normal_matrix * in.normal); + + out.uv = in.uv; + out.color = obj.color; + out.instance_index = instance_index; + return out; +} + +#include "render/scene_query_mode" +#include "render/shadows" +#include "render/lighting_utils" + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + let light_dir = normalize(vec3(1.0, 1.0, 1.0)); + + let shadow = calc_shadow(in.world_pos, light_dir, 0.05, 20.0, in.instance_index); + let lit_color = calculate_lighting(in.color.rgb, in.normal, in.world_pos, shadow); + + return vec4(lit_color, in.color.a); +} diff --git a/assets/final/shaders/renderer_3d.wgsl b/assets/final/shaders/renderer_3d.wgsl index f855052..e7cb810 100644 --- a/assets/final/shaders/renderer_3d.wgsl +++ b/assets/final/shaders/renderer_3d.wgsl @@ -40,6 +40,12 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, let obj = object_data.objects[instance_index]; let obj_type = obj.params.x; + if (obj_type == 5.0) { // MESH + var out: VertexOutput; + out.position = vec4(0.0, 0.0, 0.0, 0.0); + return out; + } + // Tight fit for Torus proxy hull (major radius 1.0, minor 0.4) if (obj_type == 3.0) { p.x = p.x * 1.5; diff --git a/assets/final/test_assets_list.txt b/assets/final/test_assets_list.txt index c9dd83b..7cded99 100644 --- a/assets/final/test_assets_list.txt +++ b/assets/final/test_assets_list.txt @@ -5,5 +5,19 @@ SHADER_SNIPPET_A, NONE, shaders/test_snippet_a.wgsl, "Test snippet A" SHADER_SNIPPET_B, NONE, shaders/test_snippet_b.wgsl, "Test snippet B" PROC_NOISE_256, PROC(gen_noise, 4321, 8), _, "Procedural noise for testing" TEST_IMAGE, NONE, test_image.tga, "A test TGA image" +TEST_MESH, NONE, test_mesh.obj, "A simple test cube mesh" PROC_UNKNOWN, PROC(gen_unknown_func, 0), _, "Unknown proc function" PROC_FAIL, PROC(gen_noise, -1337, 8), _, "Failing proc function" + +# --- Required Shaders for Renderer3D --- +SHADER_MESH, NONE, shaders/mesh_render.wgsl, "Mesh Rasterization Shader" +SHADER_RENDERER_3D, NONE, shaders/renderer_3d.wgsl, "Hybrid 3D Renderer Shader" +SHADER_SKYBOX, NONE, shaders/skybox.wgsl, "Skybox background shader" +SHADER_COMMON_UNIFORMS, NONE, shaders/common_uniforms.wgsl, "Common Uniforms Snippet" +SHADER_MATH_SDF_SHAPES, NONE, shaders/math/sdf_shapes.wgsl, "SDF Shapes Snippet" +SHADER_MATH_SDF_UTILS, NONE, shaders/math/sdf_utils.wgsl, "SDF Utils Snippet" +SHADER_RENDER_SHADOWS, NONE, shaders/render/shadows.wgsl, "Shadows Snippet" +SHADER_RENDER_SCENE_QUERY_BVH, NONE, shaders/render/scene_query_bvh.wgsl, "Scene Query Snippet (BVH)" +SHADER_RENDER_SCENE_QUERY_LINEAR, NONE, shaders/render/scene_query_linear.wgsl, "Scene Query Snippet (Linear)" +SHADER_RENDER_LIGHTING_UTILS, NONE, shaders/render/lighting_utils.wgsl, "Lighting Utils Snippet" +SHADER_RAY_BOX, NONE, shaders/ray_box.wgsl, "Ray-Box Intersection Snippet" \ No newline at end of file diff --git a/assets/final/test_mesh.obj b/assets/final/test_mesh.obj new file mode 100644 index 0000000..eb304d4 --- /dev/null +++ b/assets/final/test_mesh.obj @@ -0,0 +1,30 @@ +v -0.5 -0.5 0.5 +v 0.5 -0.5 0.5 +v 0.5 0.5 0.5 +v -0.5 0.5 0.5 +v -0.5 -0.5 -0.5 +v 0.5 -0.5 -0.5 +v 0.5 0.5 -0.5 +v -0.5 0.5 -0.5 +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 +vt 0.0 0.0 +vt 1.0 0.0 +vt 1.0 1.0 +vt 0.0 1.0 +f 1/1/1 2/2/1 3/3/1 +f 1/1/1 3/3/1 4/4/1 +f 5/1/2 8/4/2 7/3/2 +f 5/1/2 7/3/2 6/2/2 +f 1/1/6 4/4/6 8/3/6 +f 1/1/6 8/3/6 5/2/6 +f 2/1/5 6/4/5 7/3/5 +f 2/1/5 7/3/5 3/2/5 +f 4/1/3 3/4/3 7/3/3 +f 4/1/3 7/3/3 8/2/3 +f 1/1/4 5/4/4 6/3/4 +f 1/1/4 6/3/4 2/2/4 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 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 #include #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 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(ptr); + ptr += sizeof(uint32_t); + const MeshVertex* vertices = reinterpret_cast(ptr); + ptr += num_vertices * sizeof(MeshVertex); + uint32_t num_indices = *reinterpret_cast(ptr); + ptr += sizeof(uint32_t); + const uint32_t* indices = reinterpret_cast(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); -- cgit v1.2.3