/******************************************************************************** * * * 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_schema_agnostic/IfcGeomRenderStyles.h" #include "../ifcparse/utils.h" #include #include WaveFrontOBJSerializer::WaveFrontOBJSerializer(const stream_or_filename& obj_filename, const stream_or_filename& mtl_filename, const SerializerSettings& settings) : WriteOnlyGeometrySerializer(settings) , obj_stream(obj_filename) , mtl_stream(mtl_filename) , vcount_total(1) { obj_stream.stream << std::setprecision(settings.precision); mtl_stream.stream << std::setprecision(settings.precision); } 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 IfcGeom::Material& style) { std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES) ? style.original_name() : style.name()); IfcUtil::sanitate_material_name(material_name); mtl_stream.stream << "newmtl " << material_name << "\n"; if (style.hasDiffuse()) { const double* diffuse = style.diffuse(); mtl_stream.stream << "Kd " << diffuse[0] << " " << diffuse[1] << " " << diffuse[2] << "\n"; } if (style.hasSpecular()) { const double* specular = style.specular(); mtl_stream.stream << "Ks " << specular[0] << " " << specular[1] << " " << specular[2] << "\n"; } if (style.hasSpecularity()) { mtl_stream.stream << "Ns " << style.specularity() << "\n"; } if (style.hasTransparency()) { 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(SerializerSettings::USE_Y_UP); const IfcGeom::Representation::Triangulation& mesh = o->geometry(); const int vcount = (int)mesh.verts().size() / 3; for ( std::vector::const_iterator 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 ( std::vector::const_iterator 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 (std::vector::const_iterator 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 IfcGeom::Material& material = mesh.materials()[material_id]; std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES) ? material.original_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; if (has_normals && has_uvs) { obj_stream.stream << "f " << v1 << "/" << v1 << "/" << v1 << " " << v2 << "/" << v2 << "/" << v2 << " " << v3 << "/" << v3 << "/" << v3 << "\n"; } else if (has_normals) { obj_stream.stream << "f " << v1 << "//" << v1 << " " << v2 << "//" << v2 << " " << v3 << "//" << v3 << "\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 IfcGeom::Material& material = mesh.materials()[material_id]; std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES) ? material.original_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; }