Compare commits

...

1 Commits

Author SHA1 Message Date
Petru Conduraru c85bf90558 Correct OBJ texture-coordinate indices (#8457)
The OBJ serializer wrote the normal index into the texture-coordinate slot
of a v/vt/vn face (emitting v/vn/vn), so exported UVs were wrong. Track a
separate texcoord counter and emit the correct vt index.

Per review, the material lookup is left relying on ApplyDefaultMaterials
(default on, and IfcConvert exposes no toggle to disable it) so an unstyled
element is given the default style before triangulation and material_ids
never contain the -1 no-style sentinel, matching how the Collada serializer
already indexes its materials without an ad hoc guard. The earlier
material_id >= 0 guard is therefore removed.

Verified on OCC 7.9.2: a styled box exports its material, an unstyled box
exports the injected DefaultMaterial (valid index, no out-of-bounds) on
both the default and --generate-uvs paths, and the UV path now emits
correct v/vt/vn indices.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:34:22 +03:00
2 changed files with 10 additions and 4 deletions
+9 -3
View File
@@ -33,6 +33,7 @@ WaveFrontOBJSerializer::WaveFrontOBJSerializer(const stream_or_filename& obj_fil
, mtl_stream(mtl_filename)
, vcount_total(1)
, ncount_total(1)
, uvcount_total(1)
{
obj_stream.stream << std::setprecision(settings.get<ifcopenshell::geometry::settings::FloatingPointDigits>().get());
mtl_stream.stream << std::setprecision(settings.get<ifcopenshell::geometry::settings::FloatingPointDigits>().get());
@@ -96,6 +97,7 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* o)
size_t vcount = mesh.verts().size() / 3;
size_t ncount = mesh.normals().size() / 3;
size_t uvcount = mesh.uvs().size() / 2;
for (auto it = mesh.verts().begin(); it != mesh.verts().end();) {
const double x = *(it++);
@@ -151,9 +153,12 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* o)
const int n3 = v3 - vcount_total + ncount_total;
if (has_normals && has_uvs) {
obj_stream.stream << "f " << v1 << "/" << n1 << "/" << n1 << " "
<< v2 << "/" << n2 << "/" << n2 << " "
<< v3 << "/" << n3 << "/" << n3 << "\n";
const int t1 = v1 - vcount_total + uvcount_total;
const int t2 = v2 - vcount_total + uvcount_total;
const int t3 = v3 - vcount_total + uvcount_total;
obj_stream.stream << "f " << v1 << "/" << t1 << "/" << n1 << " "
<< v2 << "/" << t2 << "/" << n2 << " "
<< v3 << "/" << t3 << "/" << n3 << "\n";
} else if (has_normals) {
obj_stream.stream << "f " << v1 << "//" << n1 << " "
<< v2 << "//" << n2 << " "
@@ -196,4 +201,5 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* o)
vcount_total += vcount;
ncount_total += ncount;
uvcount_total += uvcount;
}
+1 -1
View File
@@ -32,7 +32,7 @@ class SERIALIZERS_API WaveFrontOBJSerializer : public WriteOnlyGeometrySerialize
private:
stream_or_filename obj_stream;
stream_or_filename mtl_stream;
size_t vcount_total, ncount_total;
size_t vcount_total, ncount_total, uvcount_total;
std::set<std::string> materials;
public:
WaveFrontOBJSerializer(const stream_or_filename& obj_filename, const stream_or_filename& mtl_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger = Logger::Root());