diff --git a/scripts/generate_uv_models.py b/scripts/generate_uv_models.py index f29e0f7..b0920ba 100644 --- a/scripts/generate_uv_models.py +++ b/scripts/generate_uv_models.py @@ -51,7 +51,7 @@ def write_obj(path, name, vertices, uvs, normals, faces, mtl_lib, mtl_name): def gen_plane(filepath, size=4.0, subdivs=5, mtl_name='textured'): verts, uvs, norms, faces = [], [], [], [] - N = normal = (0, 1, 0) + normal = (0, 1, 0) # Grid of (subdivs+1) x (subdivs+1) vertices def idx(ix, iz): @@ -63,7 +63,7 @@ def idx(ix, iz): z = -size / 2 + iz * size / subdivs verts.append((x, 0.0, z)) uvs.append((ix / subdivs, iz / subdivs)) - norms.append(N) + norms.append(normal) # Two triangles per cell for ix in range(subdivs): diff --git a/src/rayon/gpu_renderers/scene_builder_cuda.cu b/src/rayon/gpu_renderers/scene_builder_cuda.cu index 9a65652..e7f0cea 100644 --- a/src/rayon/gpu_renderers/scene_builder_cuda.cu +++ b/src/rayon/gpu_renderers/scene_builder_cuda.cu @@ -133,13 +133,22 @@ static CudaScene::Geometry convertGeometry(const GeometryDesc &desc) static_cast(desc.data.triangle.n2.y()), static_cast(desc.data.triangle.n2.z())); geom.data.triangle.has_normals = desc.data.triangle.has_normals; - // UV coordinates - geom.data.triangle.uv0 = f2(static_cast(desc.data.triangle.uv0.x()), - static_cast(desc.data.triangle.uv0.y())); - geom.data.triangle.uv1 = f2(static_cast(desc.data.triangle.uv1.x()), - static_cast(desc.data.triangle.uv1.y())); - geom.data.triangle.uv2 = f2(static_cast(desc.data.triangle.uv2.x()), - static_cast(desc.data.triangle.uv2.y())); + // UV coordinates — only copy valid data; zero-initialize when UVs are absent + if (desc.data.triangle.has_uvs) + { + geom.data.triangle.uv0 = f2(static_cast(desc.data.triangle.uv0.x()), + static_cast(desc.data.triangle.uv0.y())); + geom.data.triangle.uv1 = f2(static_cast(desc.data.triangle.uv1.x()), + static_cast(desc.data.triangle.uv1.y())); + geom.data.triangle.uv2 = f2(static_cast(desc.data.triangle.uv2.x()), + static_cast(desc.data.triangle.uv2.y())); + } + else + { + geom.data.triangle.uv0 = f2(0.0f, 0.0f); + geom.data.triangle.uv1 = f2(0.0f, 0.0f); + geom.data.triangle.uv2 = f2(0.0f, 0.0f); + } geom.data.triangle.has_uvs = desc.data.triangle.has_uvs; break; @@ -404,21 +413,21 @@ void CudaSceneBuilder::freeGPUScene(CudaScene::Scene *d_scene) if (host_scene.d_textures && host_scene.num_textures > 0) { std::vector host_tex(static_cast(host_scene.num_textures)); - cudaMemcpy(host_tex.data(), host_scene.d_textures, - host_scene.num_textures * sizeof(cudaTextureObject_t), - cudaMemcpyDeviceToHost); + CUDA_CHECK(cudaMemcpy(host_tex.data(), host_scene.d_textures, + host_scene.num_textures * sizeof(cudaTextureObject_t), + cudaMemcpyDeviceToHost)); for (int i = 0; i < host_scene.num_textures; ++i) { if (host_tex[i]) { cudaResourceDesc rd = {}; - cudaGetTextureObjectResourceDesc(&rd, host_tex[i]); - cudaDestroyTextureObject(host_tex[i]); + CUDA_CHECK(cudaGetTextureObjectResourceDesc(&rd, host_tex[i])); + CUDA_CHECK(cudaDestroyTextureObject(host_tex[i])); if (rd.resType == cudaResourceTypeArray && rd.res.array.array) - cudaFreeArray(rd.res.array.array); + CUDA_CHECK(cudaFreeArray(rd.res.array.array)); } } - cudaFree(host_scene.d_textures); + CUDA_CHECK(cudaFree(host_scene.d_textures)); } // Free the scene struct itself (now on device) diff --git a/src/rayon/scenes/obj_loader.hpp b/src/rayon/scenes/obj_loader.hpp index c9fb50e..192170f 100644 --- a/src/rayon/scenes/obj_loader.hpp +++ b/src/rayon/scenes/obj_loader.hpp @@ -34,7 +34,7 @@ class OBJLoader * The caller-supplied @p fallback_mat_id is used only for faces that * belong to a group with no usemtl directive (or when no .mtl is found). * Pass -1 for @p fallback_mat_id to require all materials to come from - * the .mtl file (faces without usemtl are then skipped with a warning). + * the .mtl file (faces without usemtl are then skipped with a one-time warning). * * @param filename Path to .obj file * @param scene Scene to add triangles to @@ -70,6 +70,7 @@ class OBJLoader std::map mtl_name_to_scene_id; int active_mat_id = fallback_mat_id; // Current material for faces + bool face_skip_warned = false; // One-time warning when faces are skipped without a material auto resolveMtlMaterial = [&](const std::string &name) -> int { auto cached = mtl_name_to_scene_id.find(name); @@ -167,7 +168,12 @@ class OBJLoader { if (active_mat_id < 0) { - // No material yet and no fallback — skip + // No material yet and no fallback — skip with a one-time warning per file + if (!face_skip_warned) + { + std::cerr << "OBJ Loader: face(s) skipped — no 'usemtl' encountered and no fallback material provided\n"; + face_skip_warned = true; + } continue; } diff --git a/src/rayon/scenes/scene_description.hpp b/src/rayon/scenes/scene_description.hpp index 27da9bb..5799644 100644 --- a/src/rayon/scenes/scene_description.hpp +++ b/src/rayon/scenes/scene_description.hpp @@ -607,6 +607,9 @@ class SceneDescription { geom.data.triangle.v2 = v2; geom.data.triangle.has_normals = false; geom.data.triangle.has_uvs = false; + geom.data.triangle.uv0 = Vec3(0, 0, 0); + geom.data.triangle.uv1 = Vec3(0, 0, 0); + geom.data.triangle.uv2 = Vec3(0, 0, 0); // Compute bounding box with epsilon padding to avoid zero-thickness AABBs constexpr double eps = 1e-4; @@ -637,6 +640,9 @@ class SceneDescription { geom.data.triangle.n2 = n2; geom.data.triangle.has_normals = true; geom.data.triangle.has_uvs = false; + geom.data.triangle.uv0 = Vec3(0, 0, 0); + geom.data.triangle.uv1 = Vec3(0, 0, 0); + geom.data.triangle.uv2 = Vec3(0, 0, 0); // Compute bounding box with epsilon padding to avoid zero-thickness AABBs constexpr double eps = 1e-4;