From 7d60a8a9ece368e365b5c857600004298cb89526 Mon Sep 17 00:00:00 2001 From: skal Date: Fri, 6 Feb 2026 08:46:20 +0100 Subject: fix: Correct mesh normal transformation and floor shadow rendering --- src/3d/object.h | 6 +++++- src/3d/renderer.cc | 6 ++++-- src/tests/test_mesh.cc | 33 ++++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/3d/object.h b/src/3d/object.h index a7ce0b8..3d4ff35 100644 --- a/src/3d/object.h +++ b/src/3d/object.h @@ -40,12 +40,14 @@ class Object3D { 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), mesh_asset_id((AssetId)0), user_data(nullptr) { + is_static(false), mesh_asset_id((AssetId)0), local_extent(1, 1, 1), + user_data(nullptr) { } mat4 get_model_matrix() const { @@ -61,6 +63,8 @@ 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}}; } diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index a9beffe..eea3ff0 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -174,7 +174,7 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, type_id = 4.0f; else if (obj.type == ObjectType::MESH) type_id = 5.0f; - data.params = vec4(type_id, 0, 0, 0); + 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; @@ -303,9 +303,11 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene, #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 diff --git a/src/tests/test_mesh.cc b/src/tests/test_mesh.cc index 0294d9b..f29ec27 100644 --- a/src/tests/test_mesh.cc +++ b/src/tests/test_mesh.cc @@ -169,7 +169,15 @@ bool load_obj_and_create_buffers(const char* path, Object3D& out_obj) { std::string parts[3] = {s1, s2, s3}; RawFace face = {}; for (int i = 0; i < 3; ++i) { - sscanf(parts[i].c_str(), "%d/%d/%d", &face.v[i], &face.vt[i], &face.vn[i]); + // Handle v//vn format + if (parts[i].find("//") != std::string::npos) { + sscanf(parts[i].c_str(), "%d//%d", &face.v[i], &face.vn[i]); + face.vt[i] = 0; + } else { + int res = sscanf(parts[i].c_str(), "%d/%d/%d", &face.v[i], &face.vt[i], &face.vn[i]); + if (res == 2) face.vn[i] = 0; + else if (res == 1) { face.vt[i] = 0; face.vn[i] = 0; } + } } raw_faces.push_back(face); } @@ -213,6 +221,21 @@ bool load_obj_and_create_buffers(const char* path, Object3D& out_obj) { } if (final_vertices.empty()) return false; + + // Calculate AABB and center the mesh + float min_x = 1e10f, min_y = 1e10f, min_z = 1e10f; + float max_x = -1e10f, max_y = -1e10f, max_z = -1e10f; + for (const auto& v : final_vertices) { + min_x = std::min(min_x, v.p[0]); min_y = std::min(min_y, v.p[1]); min_z = std::min(min_z, v.p[2]); + max_x = std::max(max_x, v.p[0]); max_y = std::max(max_y, v.p[1]); max_z = std::max(max_z, v.p[2]); + } + float cx = (min_x + max_x) * 0.5f; + float cy = (min_y + max_y) * 0.5f; + float cz = (min_z + max_z) * 0.5f; + for (auto& v : final_vertices) { + v.p[0] -= cx; v.p[1] -= cy; v.p[2] -= cz; + } + out_obj.local_extent = vec3((max_x - min_x) * 0.5f, (max_y - min_y) * 0.5f, (max_z - min_z) * 0.5f); g_mesh_gpu_data.num_indices = final_indices.size(); g_mesh_gpu_data.vertex_buffer = gpu_create_buffer(g_device, final_vertices.size() * sizeof(MeshVertex), WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst, final_vertices.data()).buffer; @@ -261,7 +284,7 @@ int main(int argc, char** argv) { // --- Create Scene --- Object3D floor(ObjectType::PLANE); - floor.scale = vec3(20.0f, 1.0f, 20.0f); + floor.scale = vec3(20.0f, 0.01f, 20.0f); // Very thin floor proxy floor.color = vec4(0.5f, 0.5f, 0.5f, 1.0f); g_scene.add_object(floor); @@ -271,11 +294,11 @@ int main(int argc, char** argv) { return 1; } mesh_obj.color = vec4(1.0f, 0.7f, 0.2f, 1.0f); - mesh_obj.position = {0, 1, 0}; + mesh_obj.position = {0, 1.5, 0}; // Elevate a bit more g_scene.add_object(mesh_obj); g_camera.position = vec3(0, 3, 5); - g_camera.target = vec3(0, 1, 0); + g_camera.target = vec3(0, 1.5, 0); while (!platform_should_close(&platform_state)) { platform_poll(&platform_state); @@ -287,7 +310,7 @@ int main(int argc, char** argv) { if (debug_mode) { auto* vertices = (std::vector*)g_scene.objects[1].user_data; - g_renderer.GetVisualDebug().add_mesh_normals(g_scene.objects[1].get_model_matrix(), vertices->size(), vertices->data()); + g_renderer.GetVisualDebug().add_mesh_normals(g_scene.objects[1].get_model_matrix(), (uint32_t)vertices->size(), vertices->data()); } WGPUSurfaceTexture surface_tex; -- cgit v1.2.3