/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #include "WavefrontObjSerializer.h" #include "../ifcgeom/IfcGeomRenderStyles.h" #include "../ifcparse/utils.h" #include #include WaveFrontOBJSerializer::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) : WriteOnlyGeometrySerializer(geometry_settings, settings, logger) , obj_stream(obj_filename) , mtl_stream(mtl_filename) , vcount_total(1) , ncount_total(1) { obj_stream.stream << std::setprecision(settings.get().get()); mtl_stream.stream << std::setprecision(settings.get().get()); } bool WaveFrontOBJSerializer::ready() { return obj_stream.is_ready() && mtl_stream.is_ready(); } void WaveFrontOBJSerializer::writeHeader() { obj_stream.stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n"; #ifdef WIN32 const char dir_separator = '\\'; #else const char dir_separator = '/'; #endif if (mtl_stream.filename()) { std::string mtl_basename = *mtl_stream.filename(); std::string::size_type slash = mtl_basename.find_last_of(dir_separator); if (slash != std::string::npos) { mtl_basename = mtl_basename.substr(slash + 1); } obj_stream.stream << "mtllib " << mtl_basename << "\n"; } mtl_stream.stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n"; } void WaveFrontOBJSerializer::writeMaterial(const ifcopenshell::geometry::taxonomy::style::ptr styleptr) { auto& style = *styleptr; std::string material_name = style.name; IfcUtil::sanitate_material_name(material_name); mtl_stream.stream << "newmtl " << material_name << "\n"; { auto& diffuse = style.get_color().ccomponents(); mtl_stream.stream << "Kd " << diffuse(0) << " " << diffuse(1) << " " << diffuse(2) << "\n"; } if (style.specular) { auto& specular = style.specular.ccomponents(); mtl_stream.stream << "Ks " << specular(0) << " " << specular(1) << " " << specular(2) << "\n"; } if (style.specularity == style.specularity) { mtl_stream.stream << "Ns " << style.specularity << "\n"; } if (style.transparency == style.transparency) { const double transparency = 1.0 - style.transparency; if (transparency < 1) { mtl_stream.stream << "d " << transparency << "\n"; } } } void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* o) { obj_stream.stream << "g " << object_id(o) << "\n"; obj_stream.stream << "s 1" << "\n"; const bool isyup = settings().get().get(); const IfcGeom::Representation::Triangulation& mesh = o->geometry(); size_t vcount = mesh.verts().size() / 3; size_t ncount = mesh.normals().size() / 3; for (auto it = mesh.verts().begin(); it != mesh.verts().end();) { const double x = *(it++); const double y = *(it++); const double z = *(it++); if (isyup) { obj_stream.stream << "v " << x << " " << z << " " << -y << "\n"; } else { obj_stream.stream << "v " << x << " " << y << " " << z << "\n"; } } for (auto it = mesh.normals().begin(); it != mesh.normals().end();) { const double x = *(it++); const double y = *(it++); const double z = *(it++); obj_stream.stream << "vn " << x << " " << y << " " << z << "\n"; } for (auto it = mesh.uvs().begin(); it != mesh.uvs().end();) { const double u = *it++; const double v = *it++; obj_stream.stream << "vt " << u << " " << v << "\n"; } int previous_material_id = -2; std::vector::const_iterator material_it = mesh.material_ids().begin(); const bool has_uvs = !mesh.uvs().empty(); const bool has_normals = !mesh.normals().empty(); for ( std::vector::const_iterator it = mesh.faces().begin(); it != mesh.faces().end(); ) { const int material_id = *(material_it++); if (material_id != previous_material_id) { const ifcopenshell::geometry::taxonomy::style::ptr material = mesh.materials()[material_id]; std::string material_name = material->name; IfcUtil::sanitate_material_name(material_name); obj_stream.stream << "usemtl " << material_name << "\n"; if (materials.find(material_name) == materials.end()) { writeMaterial(material); materials.insert(material_name); } previous_material_id = material_id; } const int v1 = *(it++) + vcount_total; const int v2 = *(it++) + vcount_total; const int v3 = *(it++) + vcount_total; const int n1 = v1 - vcount_total + ncount_total; const int n2 = v2 - vcount_total + ncount_total; 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"; } else if (has_normals) { obj_stream.stream << "f " << v1 << "//" << n1 << " " << v2 << "//" << n2 << " " << v3 << "//" << n3 << "\n"; } else { obj_stream.stream << "f " << v1 << " " << v2 << " " << v3 << "\n"; } } std::set faces_set (mesh.faces().begin(), mesh.faces().end()); const std::vector& edges = mesh.edges(); for ( std::vector::const_iterator it = edges.begin(); it != edges.end(); ) { const int i1 = *(it++); const int i2 = *(it++); if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) { continue; } const int material_id = *(material_it++); if (material_id != previous_material_id) { const ifcopenshell::geometry::taxonomy::style::ptr material = mesh.materials()[material_id]; std::string material_name = material->name; IfcUtil::sanitate_material_name(material_name); obj_stream.stream << "usemtl " << material_name << "\n"; if (materials.find(material_name) == materials.end()) { writeMaterial(material); materials.insert(material_name); } previous_material_id = material_id; } const int v1 = i1 + vcount_total; const int v2 = i2 + vcount_total; obj_stream.stream << "l " << v1 << " " << v2 << "\n"; } vcount_total += vcount; ncount_total += ncount; }