diff --git a/src/Mod/Mesh/App/CMakeLists.txt b/src/Mod/Mesh/App/CMakeLists.txt index 7f93f2ce04..25e5627d5c 100644 --- a/src/Mod/Mesh/App/CMakeLists.txt +++ b/src/Mod/Mesh/App/CMakeLists.txt @@ -104,6 +104,8 @@ SET(Core_SRCS Core/IO/ReaderOBJ.h Core/IO/Writer3MF.cpp Core/IO/Writer3MF.h + Core/IO/WriterInventor.cpp + Core/IO/WriterInventor.h Core/IO/WriterOBJ.cpp Core/IO/WriterOBJ.h ) diff --git a/src/Mod/Mesh/App/Core/IO/WriterInventor.cpp b/src/Mod/Mesh/App/Core/IO/WriterInventor.cpp new file mode 100644 index 0000000000..92ae078ba5 --- /dev/null +++ b/src/Mod/Mesh/App/Core/IO/WriterInventor.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (c) 2022 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include +#include +#include "Core/Iterator.h" + +#include "WriterInventor.h" + + +using namespace MeshCore; + +class WriterInventorImp +{ + Base::InventorBuilder& builder; + +public: + static bool isStreamInvalid(std::ostream& out) + { + return !out || out.bad(); + } + + WriterInventorImp(Base::InventorBuilder& builder) + : builder(builder) + { + } + + void setupStream(std::ostream& out) + { + out.precision(6); + out.setf(std::ios::fixed | std::ios::showpoint); + } + + void addInfoNode() + { + Base::InfoItem info{"Created by FreeCAD "}; + builder.addNode(info); + } + + void addLabel(const MeshCore::MeshKernel& kernel) + { + std::stringstream str; + str << "Triangle mesh contains " + << kernel.CountPoints() + << " vertices and " + << kernel.CountFacets() + << " faces"; + Base::LabelItem label{str.str().c_str()}; + builder.addNode(label); + } + + void addTransformNode(const Base::Matrix4D& mat, bool append) + { + if (!append) + return; + + Base::Placement placement; + placement.fromMatrix(mat); + + Base::TransformItem item{placement}; + builder.addNode(item); + } + + void addNormalNode(const MeshCore::MeshKernel& kernel) + { + MeshFacetIterator clIter(kernel), clEnd(kernel); + const MeshGeomFacet* geomFacet = nullptr; + + // write out the normals of the facets + std::vector normals; + normals.reserve(kernel.CountFacets()); + + clIter.Begin(); + clEnd.End(); + + while (clIter < clEnd) { + geomFacet = &(*clIter); + normals.push_back(geomFacet->GetNormal()); + ++clIter; + } + + builder.addNode(Base::NormalItem{normals}); + + Base::NormalBindingItem binding; + binding.setValue(Base::BindingElement::Binding::PerFace); + builder.addNode(binding); + } + + void addCoordinateNode(const MeshCore::MeshKernel& kernel) + { + const MeshPointArray& points = kernel.GetPoints(); + + // coordinates of the vertices + std::vector coords; + coords.reserve(points.size()); + coords.insert(coords.begin(), points.begin(), points.end()); + + builder.addNode(Base::Coordinate3Item{coords}); + } + + void addMaterialNode(const Material* material) + { + if (!material) + return; + + auto transformColors = [](const std::vector& input) { + std::vector output; + output.reserve(input.size()); + std::transform(input.cbegin(), input.cend(), std::back_inserter(output), [](const App::Color& col) { + return Base::ColorRGB{col.r, col.g, col.b}; + }); + + return output; + }; + + Base::MaterialItem mat; + mat.setAmbientColor(transformColors(material->ambientColor)); + mat.setDiffuseColor(transformColors(material->diffuseColor)); + mat.setSpecularColor(transformColors(material->specularColor)); + mat.setEmissiveColor(transformColors(material->emissiveColor)); + mat.setShininess(material->shininess); + mat.setTransparency(material->transparency); + builder.addNode(mat); + } + + void addMaterialBindingNode(const Material* material) + { + if (!material) + return; + + Base::MaterialBindingItem binding; + switch (material->binding) { + case MeshIO::PER_FACE: + binding.setValue(Base::BindingElement::Binding::PerFace); + break; + case MeshIO::PER_VERTEX: + binding.setValue(Base::BindingElement::Binding::PerVertex); + break; + default: + binding.setValue(Base::BindingElement::Binding::Overall); + break; + } + + builder.addNode(binding); + } + + void addIndexedFaceSetNode(const MeshCore::MeshKernel& kernel) + { + // and finally the facets with their point indices + const MeshFacetArray& faces = kernel.GetFacets(); + std::vector indices; + indices.reserve(4 * faces.size()); + for (const auto& it : faces) { + indices.push_back(static_cast(it._aulPoints[0])); + indices.push_back(static_cast(it._aulPoints[1])); + indices.push_back(static_cast(it._aulPoints[2])); + indices.push_back(-1); + } + + builder.addNode(Base::IndexedFaceSetItem{indices}); + } +}; + +WriterInventor::WriterInventor(const MeshKernel& kernel, const Material* material) + : _kernel(kernel) + , _material(material) + , apply_transform(false) +{ +} + +void WriterInventor::SetTransform(const Base::Matrix4D& mat) +{ + _transform = mat; + if (mat != Base::Matrix4D()) + apply_transform = true; +} + +bool WriterInventor::Save(std::ostream& out) +{ + if (WriterInventorImp::isStreamInvalid(out)) + return false; + + Base::InventorBuilder builder(out); + builder.beginSeparator(); + + WriterInventorImp writer{builder}; + writer.setupStream(out); + writer.addInfoNode(); + writer.addLabel(_kernel); + writer.addTransformNode(_transform, apply_transform); + writer.addNormalNode(_kernel); + writer.addCoordinateNode(_kernel); + writer.addMaterialNode(_material); + writer.addMaterialBindingNode(_material); + writer.addIndexedFaceSetNode(_kernel); + + builder.endSeparator(); + + return true; +} diff --git a/src/Mod/Mesh/App/Core/IO/WriterInventor.h b/src/Mod/Mesh/App/Core/IO/WriterInventor.h new file mode 100644 index 0000000000..66cb3c63df --- /dev/null +++ b/src/Mod/Mesh/App/Core/IO/WriterInventor.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (c) 2022 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef MESH_IO_WRITER_IV_H +#define MESH_IO_WRITER_IV_H + +#include +#include + +namespace MeshCore +{ + +/** Saves the mesh object into OpenInventor v2.1 format. */ +class MeshExport WriterInventor +{ +public: + /*! + * \brief WriterInventor + */ + explicit WriterInventor(const MeshKernel& kernel, const Material*); + /*! + * \brief Apply a transformation for the exported mesh. + */ + void SetTransform(const Base::Matrix4D&); + /*! + * \brief Save the mesh to an OpenInventor file. + * \return true if the data could be written successfully, false otherwise. + */ + bool Save(std::ostream&); + +private: + const MeshKernel& _kernel; + const Material* _material; + Base::Matrix4D _transform; + bool apply_transform; +}; + +} // namespace MeshCore + + +#endif // MESH_IO_WRITER_IV_H diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index 510fdccfbd..297cf352c1 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -49,6 +49,7 @@ #include "IO/Reader3MF.h" #include "IO/ReaderOBJ.h" #include "IO/Writer3MF.h" +#include "IO/WriterInventor.h" #include "IO/WriterOBJ.h" #include #include @@ -2671,83 +2672,9 @@ triplot t xt yt zt 'b' /** Writes an OpenInventor file. */ bool MeshOutput::SaveInventor (std::ostream &rstrOut) const { - if (!rstrOut || rstrOut.bad() || (_rclMesh.CountFacets() == 0)) - return false; - - MeshFacetIterator clIter(_rclMesh), clEnd(_rclMesh); - clIter.Transform(this->_transform); - MeshPointIterator clPtIter(_rclMesh), clPtEnd(_rclMesh); - clPtIter.Transform(this->_transform); - const MeshGeomFacet* pclFacet; - - Base::SequencerLauncher seq("Saving...", _rclMesh.CountFacets() + 1); - rstrOut.precision(6); - rstrOut.setf(std::ios::fixed | std::ios::showpoint); - - // Header info - Base::InventorBuilder builder(rstrOut); - builder.beginSeparator(); - Base::InfoItem info{"Created by FreeCAD "}; - builder.addNode(info); - std::stringstream str; - str << "Triangle mesh contains " - << _rclMesh.CountPoints() - << " vertices and " - << _rclMesh.CountFacets() - << " faces"; - Base::LabelItem label{str.str().c_str()}; - builder.addNode(label); - - // write out the normals of the facets - std::vector normals; - normals.reserve(_rclMesh.CountFacets()); - - clIter.Begin(); - clEnd.End(); - - while (clIter < clEnd) { - pclFacet = &(*clIter); - normals.push_back(pclFacet->GetNormal()); - ++clIter; - - seq.next(true); // allow to cancel - } - - builder.addNode(Base::NormalItem{normals}); - - Base::NormalBindingItem binding; - binding.setValue(Base::BindingElement::Binding::PerFace); - builder.addNode(binding); - - // coordinates of the vertices - std::vector coords; - coords.reserve(_rclMesh.CountPoints()); - - clPtIter.Begin(); - clPtEnd.End(); - - while (clPtIter < clPtEnd) { - coords.push_back(*clPtIter); - ++clPtIter; - seq.next(true); // allow to cancel - } - - builder.addNode(Base::Coordinate3Item{coords}); - - // and finally the facets with their point indices - const MeshFacetArray& faces = _rclMesh.GetFacets(); - std::vector indices; - indices.reserve(4 * faces.size()); - for (MeshFacetArray::_TConstIterator it = faces.begin(); it != faces.end(); ++it) { - indices.push_back(static_cast(it->_aulPoints[0])); - indices.push_back(static_cast(it->_aulPoints[1])); - indices.push_back(static_cast(it->_aulPoints[2])); - indices.push_back(-1); - } - builder.addNode(Base::IndexedFaceSetItem{indices}); - builder.endSeparator(); - - return true; + WriterInventor writer(_rclMesh, _material); + writer.SetTransform(_transform); + return writer.Save(rstrOut); } /** Writes an X3D file. */