From 5bfd611c967c164b0e7c62a7645769a580f19f75 Mon Sep 17 00:00:00 2001 From: mzernova Date: Sun, 5 Jan 2025 22:43:23 +0000 Subject: [PATCH] GLTF Export - Edge and Vertex support #243 Added functionality to export TopoDS_Vertex and TopoDS_Edge to GLTF format --- src/RWGltf/RWGltf_CafWriter.cxx | 761 +++++++++++++++++---------- src/RWGltf/RWGltf_CafWriter.hxx | 122 ++++- src/RWMesh/FILES | 6 + src/RWMesh/RWMesh_EdgeIterator.cxx | 78 +++ src/RWMesh/RWMesh_EdgeIterator.hxx | 118 +++++ src/RWMesh/RWMesh_FaceIterator.cxx | 136 +---- src/RWMesh/RWMesh_FaceIterator.hxx | 98 ++-- src/RWMesh/RWMesh_ShapeIterator.cxx | 163 ++++++ src/RWMesh/RWMesh_ShapeIterator.hxx | 131 +++++ src/RWMesh/RWMesh_VertexIterator.cxx | 78 +++ src/RWMesh/RWMesh_VertexIterator.hxx | 108 ++++ tests/de_mesh/gltf_write/draco_mesh | 22 + 12 files changed, 1350 insertions(+), 471 deletions(-) create mode 100644 src/RWMesh/RWMesh_EdgeIterator.cxx create mode 100644 src/RWMesh/RWMesh_EdgeIterator.hxx create mode 100644 src/RWMesh/RWMesh_ShapeIterator.cxx create mode 100644 src/RWMesh/RWMesh_ShapeIterator.hxx create mode 100644 src/RWMesh/RWMesh_VertexIterator.cxx create mode 100644 src/RWMesh/RWMesh_VertexIterator.hxx create mode 100644 tests/de_mesh/gltf_write/draco_mesh diff --git a/src/RWGltf/RWGltf_CafWriter.cxx b/src/RWGltf/RWGltf_CafWriter.cxx index c9f21f6941..2b96935e01 100644 --- a/src/RWGltf/RWGltf_CafWriter.cxx +++ b/src/RWGltf/RWGltf_CafWriter.cxx @@ -17,21 +17,21 @@ #include #include #include -#include -#include #include #include #include #include #include #include -#include #include #include #include #include #include +#include +#include #include +#include #include #include #include @@ -75,16 +75,18 @@ static void writeVec2(std::ostream& theStream, const gp_XY& theVec2) theStream.write((const char*)aVec2.GetData(), sizeof(aVec2)); } -//! Write triangle indices. -static void writeTriangle32(std::ostream& theStream, const Graphic3d_Vec3i& theTri) +//! General function to write triangle indices. +template +static void writeTriangle(std::ostream& theStream, const VecType& theTri) { - theStream.write((const char*)theTri.GetData(), sizeof(theTri)); + theStream.write(reinterpret_cast(theTri.GetData()), sizeof(theTri)); } -//! Write triangle indices. -static void writeTriangle16(std::ostream& theStream, const NCollection_Vec3& theTri) +//! General function to write vertex index. +template +static void writeVertex(std::ostream& theStream, const T& theVertex) { - theStream.write((const char*)theTri.GetData(), sizeof(theTri)); + theStream.write(reinterpret_cast(&theVertex), sizeof(T)); } #ifdef HAVE_DRACO @@ -96,24 +98,19 @@ static void writeNodesToDracoMesh(draco::Mesh& theMesh, const std::vector + static void writeTriangle (std::ostream& theStream, const VecType& theTri) { - aPtr->SetAttributeValue(aPtr->mapped_index(anIndex), theNodes[aNodeInd].GetData()); + theStream.write (reinterpret_cast(theTri.GetData()), sizeof(theTri)); } } -//! Write normals to Draco mesh -static void writeNormalsToDracoMesh(draco::Mesh& theMesh, - const std::vector& theNormals) -{ - if (theNormals.empty()) + //! General function to write vertex index. + template + static void writeVertex (std::ostream& theStream, const T& theVertex) { - return; + theStream.write (reinterpret_cast(&theVertex), sizeof(T)); } draco::PointAttribute anAttr; @@ -276,9 +273,29 @@ TCollection_AsciiString RWGltf_CafWriter::formatName(RWMesh_NameFormat theFormat //================================================================================================= -Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh(const RWMesh_FaceIterator& theFaceIter) +Standard_Boolean RWGltf_CafWriter::toSkipShape (const RWMesh_ShapeIterator& theShapeIter) const { - return theFaceIter.IsEmptyMesh(); + return theShapeIter.IsEmpty(); +} + +//================================================================================================= + +Standard_Boolean RWGltf_CafWriter::hasTriangulation (const RWGltf_GltfFace& theGltfFace) const +{ + switch (theGltfFace.Shape.ShapeType()) + { + case TopAbs_COMPOUND: + case TopAbs_COMPSOLID: + case TopAbs_SOLID: + case TopAbs_SHELL: + case TopAbs_FACE: + return true; + case TopAbs_WIRE: + case TopAbs_EDGE: + case TopAbs_VERTEX: + default: + return false; + } } // ======================================================================= @@ -287,7 +304,7 @@ Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh(const RWMesh_FaceIterator& the // ======================================================================= void RWGltf_CafWriter::saveNodes(RWGltf_GltfFace& theGltfFace, std::ostream& theBinFile, - const RWMesh_FaceIterator& theFaceIter, + const RWMesh_ShapeIterator& theShapeIter, Standard_Integer& theAccessorNb, const std::shared_ptr& theMesh) const { @@ -307,15 +324,16 @@ void RWGltf_CafWriter::saveNodes(RWGltf_GltfFace& Standard_ASSERT_RAISE(aPos == (int64_t)theBinFile.tellp(), "wrong offset"); } } - theGltfFace.NodePos.Count += theFaceIter.NbNodes(); + theGltfFace.NodePos.Count += theShapeIter.NbNodes(); - const Standard_Integer aNodeUpper = theFaceIter.NodeUpper(); - for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter) + const Standard_Integer aNodeUpper = theShapeIter.NodeUpper(); + for (Standard_Integer aNodeIter = theShapeIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter) { - gp_XYZ aNode = theFaceIter.NodeTransformed(aNodeIter).XYZ(); + gp_XYZ aNode = theShapeIter.NodeTransformed(aNodeIter).XYZ(); myCSTrsf.TransformPosition(aNode); theGltfFace.NodePos.BndBox.Add(Graphic3d_Vec3d(aNode.X(), aNode.Y(), aNode.Z())); - if (theMesh.get() != nullptr) + if (theMesh.get() != nullptr + && hasTriangulation (theGltfFace)) { theMesh->NodesVec.push_back( Graphic3d_Vec3(float(aNode.X()), float(aNode.Y()), float(aNode.Z()))); @@ -333,7 +351,7 @@ void RWGltf_CafWriter::saveNodes(RWGltf_GltfFace& // ======================================================================= void RWGltf_CafWriter::saveNormals(RWGltf_GltfFace& theGltfFace, std::ostream& theBinFile, - RWMesh_FaceIterator& theFaceIter, + const RWMesh_FaceIterator& theFaceIter, Standard_Integer& theAccessorNb, const std::shared_ptr& theMesh) const { @@ -393,16 +411,16 @@ void RWGltf_CafWriter::saveTextCoords(RWGltf_GltfFace& } if (!myIsForcedUVExport) { - if (theFaceIter.FaceStyle().Material().IsNull()) + if (theFaceIter.Style().Material().IsNull()) { return; } - if (RWGltf_GltfMaterialMap::baseColorTexture(theFaceIter.FaceStyle().Material()).IsNull() - && theFaceIter.FaceStyle().Material()->PbrMaterial().MetallicRoughnessTexture.IsNull() - && theFaceIter.FaceStyle().Material()->PbrMaterial().EmissiveTexture.IsNull() - && theFaceIter.FaceStyle().Material()->PbrMaterial().OcclusionTexture.IsNull() - && theFaceIter.FaceStyle().Material()->PbrMaterial().NormalTexture.IsNull()) + if (RWGltf_GltfMaterialMap::baseColorTexture(theFaceIter.Style().Material()).IsNull() + && theFaceIter.Style().Material()->PbrMaterial().MetallicRoughnessTexture.IsNull() + && theFaceIter.Style().Material()->PbrMaterial().EmissiveTexture.IsNull() + && theFaceIter.Style().Material()->PbrMaterial().OcclusionTexture.IsNull() + && theFaceIter.Style().Material()->PbrMaterial().NormalTexture.IsNull()) { return; } @@ -442,13 +460,97 @@ void RWGltf_CafWriter::saveTextCoords(RWGltf_GltfFace& } } +// ======================================================================= +// function : saveTriangleIndices +// purpose : +// ======================================================================= +void RWGltf_CafWriter::saveTriangleIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_FaceIterator& theFaceIter, + const std::shared_ptr& theMesh) +{ + const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower(); + theGltfFace.NbIndexedNodes += theFaceIter.NbNodes(); + theGltfFace.Indices.Count += theFaceIter.NbTriangles() * 3; + for (Standard_Integer anElemIter = theFaceIter.ElemLower(); anElemIter <= theFaceIter.ElemUpper(); ++anElemIter) + { + Poly_Triangle aTri = theFaceIter.TriangleOriented(anElemIter); + aTri(1) += aNodeFirst; + aTri(2) += aNodeFirst; + aTri(3) += aNodeFirst; + if (theMesh.get() != nullptr) + { + theMesh->IndicesVec.push_back(aTri); + } + else + { + if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16) + { + writeTriangle (theBinFile, NCollection_Vec3((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3))); + } + else + { + writeTriangle (theBinFile, Graphic3d_Vec3i(aTri(1), aTri(2), aTri(3))); + } + } + } +} + +// ======================================================================= +// function : saveEdgeIndices +// purpose : +// ======================================================================= +void RWGltf_CafWriter::saveEdgeIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_EdgeIterator& theFaceIter) +{ + const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower(); + theGltfFace.NbIndexedNodes += theFaceIter.NbNodes(); + theGltfFace.Indices.Count += theFaceIter.NbNodes(); + for (Standard_Integer anElemIter = theFaceIter.ElemLower(); anElemIter <= theFaceIter.ElemUpper(); ++anElemIter) + { + if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16) + { + writeVertex (theBinFile, (uint16_t)(anElemIter + aNodeFirst)); + } + else + { + writeVertex (theBinFile, anElemIter + aNodeFirst); + } + } +} + +// ======================================================================= +// function : saveVertexIndices +// purpose : +// ======================================================================= +void RWGltf_CafWriter::saveVertexIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_VertexIterator& theFaceIter) +{ + const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower(); + theGltfFace.NbIndexedNodes += theFaceIter.NbNodes(); + theGltfFace.Indices.Count += theFaceIter.NbNodes(); + for (Standard_Integer anElemIter = theFaceIter.ElemLower(); anElemIter <= theFaceIter.ElemUpper(); ++anElemIter) + { + if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16) + { + writeVertex (theBinFile, (uint16_t)(anElemIter + aNodeFirst)); + } + else + { + writeVertex (theBinFile, anElemIter + aNodeFirst); + } + } +} + // ======================================================================= // function : saveIndices // purpose : // ======================================================================= void RWGltf_CafWriter::saveIndices(RWGltf_GltfFace& theGltfFace, std::ostream& theBinFile, - const RWMesh_FaceIterator& theFaceIter, + const RWMesh_ShapeIterator& theFaceIter, Standard_Integer& theAccessorNb, const std::shared_ptr& theMesh) { @@ -477,35 +579,17 @@ void RWGltf_CafWriter::saveIndices(RWGltf_GltfFace& } } - const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower(); - theGltfFace.NbIndexedNodes += theFaceIter.NbNodes(); - theGltfFace.Indices.Count += theFaceIter.NbTriangles() * 3; - - const Standard_Integer anElemLower = theFaceIter.ElemLower(); - const Standard_Integer anElemUpper = theFaceIter.ElemUpper(); - for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper; ++anElemIter) + if (const RWMesh_FaceIterator* aFaceIter = dynamic_cast(&theFaceIter)) { - Poly_Triangle aTri = theFaceIter.TriangleOriented(anElemIter); - aTri(1) += aNodeFirst; - aTri(2) += aNodeFirst; - aTri(3) += aNodeFirst; - if (theMesh.get() != nullptr) - { - theMesh->IndicesVec.push_back(aTri); - } - else - { - if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16) - { - writeTriangle16( - theBinFile, - NCollection_Vec3((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3))); - } - else - { - writeTriangle32(theBinFile, Graphic3d_Vec3i(aTri(1), aTri(2), aTri(3))); - } - } + saveTriangleIndices (theGltfFace, theBinFile, *aFaceIter, theMesh); + } + else if (const RWMesh_EdgeIterator* anEdgeIter = dynamic_cast(&theFaceIter)) + { + saveEdgeIndices (theGltfFace, theBinFile, *anEdgeIter); + } + else if (const RWMesh_VertexIterator* aVertexIter = dynamic_cast(&theFaceIter)) + { + saveVertexIndices (theGltfFace, theBinFile, *aVertexIter); } } @@ -556,6 +640,149 @@ bool RWGltf_CafWriter::Perform(const Handle(TDocStd_Document)& theDo return writeJson(theDocument, theRootLabels, theLabelFilter, theFileInfo, aPSentry.Next()); } +// ======================================================================= +// function : dispatchShapes +// purpose : +// ======================================================================= +void RWGltf_CafWriter::dispatchShapes (const XCAFPrs_DocumentNode& theDocNode, + const Message_ProgressScope& thePSentryBin, + NCollection_DataMap& theMergedFaces, + RWMesh_ShapeIterator& theShapeIter) +{ + if (myToMergeFaces && theShapeIter.More()) + { + RWGltf_StyledShape aStyledShape (theShapeIter.ExploredShape(), theDocNode.Style); + if (myBinDataMap.Contains (aStyledShape)) + { + return; + } + + Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList(); + myBinDataMap.Add (aStyledShape, aGltfFaceList); + for (; theShapeIter.More() && thePSentryBin.More(); theShapeIter.Next()) + { + if (toSkipShape (theShapeIter)) + { + continue; + } + + Handle(RWGltf_GltfFace) aGltfFace; + if (!theMergedFaces.Find (theShapeIter.Style(), aGltfFace)) + { + aGltfFace = new RWGltf_GltfFace(); + aGltfFaceList->Append (aGltfFace); + aGltfFace->Shape = theShapeIter.Shape(); + aGltfFace->Style = theShapeIter.Style(); + aGltfFace->NbIndexedNodes = theShapeIter.NbNodes(); + theMergedFaces.Bind (theShapeIter.Style(), aGltfFace); + } + else if (myToSplitIndices16 + && aGltfFace->NbIndexedNodes < std::numeric_limits::max() + && (aGltfFace->NbIndexedNodes + theShapeIter.NbNodes()) >= std::numeric_limits::max()) + { + theMergedFaces.UnBind (theShapeIter.Style()); + aGltfFace = new RWGltf_GltfFace(); + aGltfFaceList->Append(aGltfFace); + aGltfFace->Shape = theShapeIter.Shape(); + aGltfFace->Style = theShapeIter.Style(); + aGltfFace->NbIndexedNodes = theShapeIter.NbNodes(); + theMergedFaces.Bind (theShapeIter.Style(), aGltfFace); + } + else + { + if (aGltfFace->Shape.ShapeType() != TopAbs_COMPOUND) + { + TopoDS_Shape anOldShape = aGltfFace->Shape; + TopoDS_Compound aComp; + BRep_Builder().MakeCompound (aComp); + BRep_Builder().Add (aComp, anOldShape); + aGltfFace->Shape = aComp; + } + BRep_Builder().Add (aGltfFace->Shape, theShapeIter.Shape()); + aGltfFace->NbIndexedNodes += theShapeIter.NbNodes(); + } + } + } + else + { + for (; theShapeIter.More() && thePSentryBin.More(); theShapeIter.Next()) + { + RWGltf_StyledShape aStyledShape (theShapeIter.Shape(), theShapeIter.Style()); + if (toSkipShape (theShapeIter) + || myBinDataMap.Contains (aStyledShape)) + { + continue; + } + + Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList(); + Handle(RWGltf_GltfFace) aGltfFace = new RWGltf_GltfFace(); + aGltfFace->Shape = theShapeIter.Shape(); + aGltfFace->Style = theShapeIter.Style(); + aGltfFaceList->Append (aGltfFace); + myBinDataMap.Add (aStyledShape, aGltfFaceList); + } + } +} + +// ======================================================================= +// function : saveShapes +// purpose : +// ======================================================================= +Standard_Boolean RWGltf_CafWriter::writeShapesToBin( + RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + RWMesh_ShapeIterator& theShapeIter, + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh, + const RWGltf_GltfArrayType theArrType, + const Message_ProgressScope& thePSentryBin) +{ + for (; theShapeIter.More() && thePSentryBin.More(); theShapeIter.Next()) + { + switch (theArrType) + { + case RWGltf_GltfArrayType_Position: { + // clang-format off + theGltfFace.NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step + // clang-format on + saveNodes(theGltfFace, theBinFile, theShapeIter, theAccessorNb, theMesh); + break; + } + case RWGltf_GltfArrayType_Normal: { + if (const RWMesh_FaceIterator* aFaceIter = + dynamic_cast(&theShapeIter)) + { + saveNormals(theGltfFace, theBinFile, *aFaceIter, theAccessorNb, theMesh); + } + break; + } + case RWGltf_GltfArrayType_TCoord0: { + if (const RWMesh_FaceIterator* aFaceIter = + dynamic_cast(&theShapeIter)) + { + saveTextCoords(theGltfFace, theBinFile, *aFaceIter, theAccessorNb, theMesh); + } + break; + } + case RWGltf_GltfArrayType_Indices: { + saveIndices(theGltfFace, theBinFile, theShapeIter, theAccessorNb, theMesh); + break; + } + default: { + break; + } + } + + if (!theBinFile.good()) + { + Message::SendFail(TCollection_AsciiString("File '") + myBinFileNameFull + + "' cannot be written"); + return false; + } + } + return true; +} + // ======================================================================= // function : writeBinData // purpose : @@ -621,7 +848,7 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, RWGltf_GltfArrayType_TCoord0, RWGltf_GltfArrayType_Indices}; - // dispatch faces + // dispatch shapes NCollection_DataMap aMergedFaces; for (XCAFPrs_DocumentExplorer aDocExplorer(theDocument, theRootLabels, @@ -638,79 +865,20 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, // transformation will be stored at scene nodes aMergedFaces.Clear(false); - RWMesh_FaceIterator aFaceIter(aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); - if (myToMergeFaces) + Standard_Integer aBinDataSize = myBinDataMap.Size(); { - RWGltf_StyledShape aStyledShape(aFaceIter.ExploredShape(), aDocNode.Style); - if (myBinDataMap.Contains(aStyledShape)) - { - continue; - } - - Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList(); - myBinDataMap.Add(aStyledShape, aGltfFaceList); - for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next()) - { - if (toSkipFaceMesh(aFaceIter)) - { - continue; - } - - Handle(RWGltf_GltfFace) aGltfFace; - if (!aMergedFaces.Find(aFaceIter.FaceStyle(), aGltfFace)) - { - aGltfFace = new RWGltf_GltfFace(); - aGltfFaceList->Append(aGltfFace); - aGltfFace->Shape = aFaceIter.Face(); - aGltfFace->Style = aFaceIter.FaceStyle(); - aGltfFace->NbIndexedNodes = aFaceIter.NbNodes(); - aMergedFaces.Bind(aFaceIter.FaceStyle(), aGltfFace); - } - else if (myToSplitIndices16 - && aGltfFace->NbIndexedNodes < std::numeric_limits::max() - && (aGltfFace->NbIndexedNodes + aFaceIter.NbNodes()) - >= std::numeric_limits::max()) - { - aMergedFaces.UnBind(aFaceIter.FaceStyle()); - aGltfFace = new RWGltf_GltfFace(); - aGltfFaceList->Append(aGltfFace); - aGltfFace->Shape = aFaceIter.Face(); - aGltfFace->Style = aFaceIter.FaceStyle(); - aGltfFace->NbIndexedNodes = aFaceIter.NbNodes(); - aMergedFaces.Bind(aFaceIter.FaceStyle(), aGltfFace); - } - else - { - if (aGltfFace->Shape.ShapeType() != TopAbs_COMPOUND) - { - TopoDS_Shape anOldShape = aGltfFace->Shape; - TopoDS_Compound aComp; - BRep_Builder().MakeCompound(aComp); - BRep_Builder().Add(aComp, anOldShape); - aGltfFace->Shape = aComp; - } - BRep_Builder().Add(aGltfFace->Shape, aFaceIter.Face()); - aGltfFace->NbIndexedNodes += aFaceIter.NbNodes(); - } - } + RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + dispatchShapes (aDocNode, aPSentryBin, aMergedFaces, aFaceIter); } - else + if (aBinDataSize == myBinDataMap.Size()) { - for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next()) - { - RWGltf_StyledShape aStyledShape(aFaceIter.Face(), aFaceIter.FaceStyle()); - if (toSkipFaceMesh(aFaceIter) || myBinDataMap.Contains(aStyledShape)) - { - continue; - } - - Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList(); - Handle(RWGltf_GltfFace) aGltfFace = new RWGltf_GltfFace(); - aGltfFace->Shape = aFaceIter.Face(); - aGltfFace->Style = aFaceIter.FaceStyle(); - aGltfFaceList->Append(aGltfFace); - myBinDataMap.Add(aStyledShape, aGltfFaceList); - } + RWMesh_EdgeIterator anEdgeIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + dispatchShapes (aDocNode, aPSentryBin, aMergedFaces, anEdgeIter); + } + if (aBinDataSize == myBinDataMap.Size()) + { + RWMesh_VertexIterator aVertexIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + dispatchShapes (aDocNode, aPSentryBin, aMergedFaces, aVertexIter); } } @@ -746,9 +914,9 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, #ifdef HAVE_DRACO size_t aMeshIndex = 0; #endif - for (ShapeToGltfFaceMap::Iterator aBinDataIter(myBinDataMap); - aBinDataIter.More() && aPSentryBin.More(); - aBinDataIter.Next()) + + Standard_Boolean isFacesOnly = Standard_True; + for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More() && aPSentryBin.More(); aBinDataIter.Next()) { const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value(); if (!aWrittenFaces.Add(aGltfFaceList)) // skip repeating faces @@ -808,47 +976,44 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, } aWrittenPrimData.Bind(aGltfFace->Shape, aGltfFace); - for (RWMesh_FaceIterator aFaceIter(aGltfFace->Shape, aGltfFace->Style); - aFaceIter.More() && aPSentryBin.More(); - aFaceIter.Next()) + Standard_Boolean wasWrittenNonFace = Standard_False; + switch (aGltfFace->Shape.ShapeType()) { - switch (anArrType) + case TopAbs_EDGE: { - case RWGltf_GltfArrayType_Position: { - // clang-format off - aGltfFace->NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step - // clang-format on - saveNodes(*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr); - break; - } - case RWGltf_GltfArrayType_Normal: { - saveNormals(*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr); - break; - } - case RWGltf_GltfArrayType_TCoord0: { - saveTextCoords(*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr); - break; - } - case RWGltf_GltfArrayType_Indices: { - saveIndices(*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr); - break; + RWMesh_EdgeIterator anIter(aGltfFace->Shape, aGltfFace->Style); + if (!writeShapesToBin (*aGltfFace, *aBinFile, anIter, aNbAccessors, aMeshPtr, anArrType, aPSentryBin)) + { + return false; } - default: { - break; + wasWrittenNonFace = Standard_True; + break; + } + case TopAbs_VERTEX: + { + RWMesh_VertexIterator anIter(aGltfFace->Shape, aGltfFace->Style); + if (!writeShapesToBin (*aGltfFace, *aBinFile, anIter, aNbAccessors, aMeshPtr, anArrType, aPSentryBin)) + { + return false; } + wasWrittenNonFace = Standard_True; + break; } - - if (!aBinFile->good()) + default: { - Message::SendFail(TCollection_AsciiString("File '") + myBinFileNameFull - + "' cannot be written"); - return false; + RWMesh_FaceIterator anIter(aGltfFace->Shape, aGltfFace->Style); + if (!writeShapesToBin (*aGltfFace, *aBinFile, anIter, aNbAccessors, aMeshPtr, anArrType, aPSentryBin)) + { + return false; + } + break; } } // add alignment by 4 bytes (might happen on RWGltf_GltfAccessorCompType_UInt16 indices) - if (!myDracoParameters.DracoCompression) + if (!myDracoParameters.DracoCompression || wasWrittenNonFace) { + isFacesOnly = Standard_False; int64_t aContentLen64 = (int64_t)aBinFile->tellp(); while (aContentLen64 % 4 != 0) { @@ -859,7 +1024,7 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, } } - if (!myDracoParameters.DracoCompression) + if (!myDracoParameters.DracoCompression || !isFacesOnly) { aBuffView->ByteLength = (int64_t)aBinFile->tellp() - aBuffView->ByteOffset; } @@ -871,6 +1036,24 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, aPSentryBin.Next(); } + int aBuffViewId = 0; + if (myBuffViewPos.ByteLength > 0) + { + myBuffViewPos.Id = aBuffViewId++; + } + if (myBuffViewNorm.ByteLength > 0) + { + myBuffViewNorm.Id = aBuffViewId++; + } + if (myBuffViewTextCoord.ByteLength > 0) + { + myBuffViewTextCoord.Id = aBuffViewId++; + } + if (myBuffViewInd.ByteLength > 0) + { + myBuffViewInd.Id = aBuffViewId++; + } + if (myDracoParameters.DracoCompression) { #ifdef HAVE_DRACO @@ -894,16 +1077,24 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, DracoEncodingFunctor aFunctor(aScope.Next(), aDracoEncoder, aMeshes, anEncoderBuffers); OSD_Parallel::For(0, int(aMeshes.size()), aFunctor, !myToParallel); + int aNbSkippedBuffers = 0; for (size_t aBuffInd = 0; aBuffInd != anEncoderBuffers.size(); ++aBuffInd) { if (anEncoderBuffers.at(aBuffInd).get() == nullptr) { - Message::SendFail(TCollection_AsciiString("Error: mesh not encoded in draco buffer.")); - return false; + if (aBuffViewId == 0) + { + Message::SendFail() << "Error: mesh not encoded in draco buffer."; + return false; + } + Message::SendWarning() + << "Warning: mesh is not encoded as a Draco buffer and has been loaded into a regular buffer."; + aNbSkippedBuffers++; + continue; } RWGltf_GltfBufferView aBuffViewDraco; - aBuffViewDraco.Id = (int)aBuffInd; - aBuffViewDraco.ByteOffset = aBinFile->tellp(); + aBuffViewDraco.Id = (int)aBuffInd + aBuffViewId - aNbSkippedBuffers; + aBuffViewDraco.ByteOffset = aBinFile->tellp(); const draco::EncoderBuffer& anEncoderBuff = *anEncoderBuffers.at(aBuffInd); aBinFile->write(anEncoderBuff.data(), std::streamsize(anEncoderBuff.size())); if (!aBinFile->good()) @@ -951,33 +1142,15 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument, aFaceIter.More(); aFaceIter.Next()) { - if (toSkipFaceMesh(aFaceIter)) + if (toSkipShape (aFaceIter)) { continue; } - myMaterialMap->AddGlbImages(*aBinFile, aFaceIter.FaceStyle()); + myMaterialMap->AddGlbImages (*aBinFile, aFaceIter.Style()); } } } - - int aBuffViewId = 0; - if (myBuffViewPos.ByteLength > 0) - { - myBuffViewPos.Id = aBuffViewId++; - } - if (myBuffViewNorm.ByteLength > 0) - { - myBuffViewNorm.Id = aBuffViewId++; - } - if (myBuffViewTextCoord.ByteLength > 0) - { - myBuffViewTextCoord.Id = aBuffViewId++; - } - if (myBuffViewInd.ByteLength > 0) - { - myBuffViewInd.Id = aBuffViewId++; - } // myMaterialMap->FlushGlbBufferViews() will put image bufferView's IDs at the end of list myBinDataLen64 = aBinFile->tellp(); @@ -1048,34 +1221,45 @@ bool RWGltf_CafWriter::writeJson(const Handle(TDocStd_Document)& the continue; } - bool hasMeshData = false; + bool hasShapeData = false; if (!aDocNode.IsAssembly) { - for (RWMesh_FaceIterator aFaceIter(aDocNode.RefLabel, - TopLoc_Location(), - true, - aDocNode.Style); - aFaceIter.More(); - aFaceIter.Next()) + auto checkShapeData = [&](RWMesh_ShapeIterator& anIter) { - if (!toSkipFaceMesh(aFaceIter)) + for (; anIter.More(); anIter.Next()) { - hasMeshData = true; - break; + if (!toSkipShape (anIter)) + { + return true; + } } + return false; + }; + { + RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + hasShapeData = checkShapeData (aFaceIter); + } + if (!hasShapeData) + { + RWMesh_EdgeIterator anEdgeIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + hasShapeData = checkShapeData (anEdgeIter); + } + if (!hasShapeData) + { + RWMesh_VertexIterator aVertIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + hasShapeData = checkShapeData (aVertIter); } } - if (hasMeshData) + + if (hasShapeData) { aSceneNodeMap.Add(aDocNode); } else { - // glTF disallows empty meshes / primitive arrays - const TCollection_AsciiString aNodeName = - formatName(RWMesh_NameFormat_ProductOrInstance, aDocNode.Label, aDocNode.RefLabel); - Message::SendWarning(TCollection_AsciiString("RWGltf_CafWriter skipped node '") + aNodeName - + "' without triangulation data"); + // glTF disallows empty shapes / primitive arrays + const TCollection_AsciiString aNodeName = formatName (RWMesh_NameFormat_ProductOrInstance, aDocNode.Label, aDocNode.RefLabel); + Message::SendWarning (TCollection_AsciiString("RWGltf_CafWriter skipped node '") + aNodeName + "' without geometry data"); } } @@ -1327,7 +1511,8 @@ void RWGltf_CafWriter::writePositions(const RWGltf_GltfFace& theGltfFace) } myWriter->StartObject(); - if (!myDracoParameters.DracoCompression) + if (!myDracoParameters.DracoCompression + || !hasTriangulation (theGltfFace)) { myWriter->Key("bufferView"); myWriter->Int(myBuffViewPos.Id); @@ -1483,12 +1668,13 @@ void RWGltf_CafWriter::writeIndices(const RWGltf_GltfFace& theGltfFace) } myWriter->StartObject(); - if (!myDracoParameters.DracoCompression) + if (!myDracoParameters.DracoCompression + || !hasTriangulation (theGltfFace)) { - myWriter->Key("bufferView"); - myWriter->Int(myBuffViewInd.Id); - myWriter->Key("byteOffset"); - myWriter->Int64(theGltfFace.Indices.ByteOffset); + myWriter->Key ("bufferView"); + myWriter->Int (myBuffViewInd.Id); + myWriter->Key ("byteOffset"); + myWriter->Int64 (theGltfFace.Indices.ByteOffset); } myWriter->Key("componentType"); myWriter->Int(theGltfFace.Indices.ComponentType); @@ -1752,7 +1938,7 @@ void RWGltf_CafWriter::writeImages(const RWGltf_GltfSceneNodeMap& theSceneNodeMa aFaceIter.More(); aFaceIter.Next()) { - myMaterialMap->AddImages(myWriter.get(), aFaceIter.FaceStyle(), anIsStarted); + myMaterialMap->AddImages (myWriter.get(), aFaceIter.Style(), anIsStarted); } } if (anIsStarted) @@ -1785,7 +1971,7 @@ void RWGltf_CafWriter::writeMaterials(const RWGltf_GltfSceneNodeMap& theSceneNod aFaceIter.More(); aFaceIter.Next()) { - myMaterialMap->AddMaterial(myWriter.get(), aFaceIter.FaceStyle(), anIsStarted); + myMaterialMap->AddMaterial (myWriter.get(), aFaceIter.Style(), anIsStarted); } } if (anIsStarted) @@ -1848,10 +2034,23 @@ void RWGltf_CafWriter::writePrimArray(const RWGltf_GltfFace& theGltfFace myWriter->Key("material"); myWriter->Int(aMatId.IntegerValue()); } - myWriter->Key("mode"); - myWriter->Int(RWGltf_GltfPrimitiveMode_Triangles); - if (myDracoParameters.DracoCompression) + myWriter->Key ("mode"); + switch (theGltfFace.Shape.ShapeType()) + { + case TopAbs_EDGE: + myWriter->Int (RWGltf_GltfPrimitiveMode_Lines); + break; + case TopAbs_VERTEX: + myWriter->Int (RWGltf_GltfPrimitiveMode_Points); + break; + default: + myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles); + break; + } + + if (myDracoParameters.DracoCompression + && hasTriangulation (theGltfFace)) { myWriter->Key("extensions"); myWriter->StartObject(); @@ -1895,6 +2094,51 @@ void RWGltf_CafWriter::writePrimArray(const RWGltf_GltfFace& theGltfFace #endif } +// ======================================================================= +// function : writeShapes +// purpose : +// ======================================================================= +void RWGltf_CafWriter::writeShapes (RWMesh_ShapeIterator& theShapeIter, + Standard_Integer& theNbFacesInNode, + Standard_Integer& theDracoBufInd, + Standard_Boolean& theToStartPrims, + const TCollection_AsciiString& theNodeName, + NCollection_Map& theWrittenShapes, + NCollection_IndexedDataMap& theDracoBufIndMap) +{ + for ( ; theShapeIter.More(); theShapeIter.Next(), ++theNbFacesInNode) + { + if (toSkipShape (theShapeIter)) + { + continue; + } + + RWGltf_StyledShape aStyledShape (theShapeIter.Shape(), theShapeIter.Style()); + const Handle(RWGltf_GltfFaceList)& aGltfShapeList = myBinDataMap.FindFromKey(aStyledShape); + if (!theWrittenShapes.Add (aGltfShapeList)) + { + continue; + } + + const Handle(RWGltf_GltfFace)& aGltfShape = aGltfShapeList->First(); + int aCurrentDracoBufInd = 0; + + if (myDracoParameters.DracoCompression && hasTriangulation(*aGltfShape)) + { + // Check if we've seen this NodePos.Id before + if (!theDracoBufIndMap.FindFromKey(aGltfShape->NodePos.Id, aCurrentDracoBufInd)) + { + // New Draco buffer entry needed + aCurrentDracoBufInd = theDracoBufInd; + theDracoBufIndMap.Add(aGltfShape->NodePos.Id, aCurrentDracoBufInd); + ++theDracoBufInd; + } + } + + writePrimArray(*aGltfShape, theNodeName, aCurrentDracoBufInd, theToStartPrims); + } +} + // ======================================================================= // function : writeMeshes // purpose : @@ -1908,9 +2152,9 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_Meshes)); myWriter->StartArray(); - int aDracoBufInd = 0; - NCollection_IndexedDataMap aDracoBufIndMap; - NCollection_Map aWrittenFaces; + int aDracoBufInd = 0; + NCollection_IndexedDataMap aDracoBufMap; + NCollection_Map aWrittenShapes; for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next()) { @@ -1919,8 +2163,8 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa formatName(myMeshNameFormat, aDocNode.Label, aDocNode.RefLabel); bool toStartPrims = true; - Standard_Integer aNbFacesInNode = 0; - aWrittenFaces.Clear(false); + Standard_Integer aNbShapes = 0; + aWrittenShapes.Clear (false); if (myToMergeFaces) { TopoDS_Shape aShape; @@ -1930,10 +2174,10 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa } Handle(RWGltf_GltfFaceList) aGltfFaceList; - aShape.Location(TopLoc_Location()); - RWGltf_StyledShape aStyledShape(aShape, aDocNode.Style); - myBinDataMap.FindFromKey(aStyledShape, aGltfFaceList); - if (!aWrittenFaces.Add(aGltfFaceList)) + aShape.Location (TopLoc_Location()); + RWGltf_StyledShape aStyledShape (aShape, aDocNode.Style); + myBinDataMap.FindFromKey (aStyledShape, aGltfFaceList); + if (!aWrittenShapes.Add (aGltfFaceList)) { continue; } @@ -1944,58 +2188,35 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa const Handle(RWGltf_GltfFace)& aGltfFace = aFaceGroupIter.Value(); int aCurrentDracoBufInd = 0; - if (myDracoParameters.DracoCompression) + if (myDracoParameters.DracoCompression && hasTriangulation(*aGltfFace)) { // Check if we've seen this NodePos.Id before - if (!aDracoBufIndMap.FindFromKey(aGltfFace->NodePos.Id, aCurrentDracoBufInd)) + if (!aDracoBufMap.FindFromKey(aGltfFace->NodePos.Id, aCurrentDracoBufInd)) { // New Draco buffer entry needed aCurrentDracoBufInd = aDracoBufInd; - aDracoBufIndMap.Add(aGltfFace->NodePos.Id, aCurrentDracoBufInd); + aDracoBufMap.Add(aGltfFace->NodePos.Id, aCurrentDracoBufInd); ++aDracoBufInd; } } - writePrimArray(*aGltfFace, aNodeName, aCurrentDracoBufInd, toStartPrims); } } else { - for (RWMesh_FaceIterator aFaceIter(aDocNode.RefLabel, - TopLoc_Location(), - true, - aDocNode.Style); - aFaceIter.More(); - aFaceIter.Next(), ++aNbFacesInNode) { - if (toSkipFaceMesh(aFaceIter)) - { - continue; - } - - RWGltf_StyledShape aStyledShape(aFaceIter.Face(), aFaceIter.FaceStyle()); - const Handle(RWGltf_GltfFaceList)& aGltfFaceList = myBinDataMap.FindFromKey(aStyledShape); - if (!aWrittenFaces.Add(aGltfFaceList)) - { - continue; - } - - const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceList->First(); - int aCurrentDracoBufInd = 0; - - if (myDracoParameters.DracoCompression) - { - // Check if we've seen this NodePos.Id before - if (!aDracoBufIndMap.FindFromKey(aGltfFace->NodePos.Id, aCurrentDracoBufInd)) - { - // New Draco buffer entry needed - aCurrentDracoBufInd = aDracoBufInd; - aDracoBufIndMap.Add(aGltfFace->NodePos.Id, aCurrentDracoBufInd); - ++aDracoBufInd; - } - } - - writePrimArray(*aGltfFace, aNodeName, aCurrentDracoBufInd, toStartPrims); + RWMesh_FaceIterator anIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + writeShapes (anIter, aNbShapes, aDracoBufInd, toStartPrims, aNodeName, aWrittenShapes, aDracoBufMap); + } + if (aNbShapes == 0) + { + RWMesh_EdgeIterator anIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + writeShapes (anIter, aNbShapes, aDracoBufInd, toStartPrims, aNodeName, aWrittenShapes, aDracoBufMap); + } + if (aNbShapes == 0) + { + RWMesh_VertexIterator anIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); + writeShapes (anIter, aNbShapes, aDracoBufInd, toStartPrims, aNodeName, aWrittenShapes, aDracoBufMap); } } @@ -2424,7 +2645,7 @@ void RWGltf_CafWriter::writeTextures(const RWGltf_GltfSceneNodeMap& theSceneNode aFaceIter.More(); aFaceIter.Next()) { - myMaterialMap->AddTextures(myWriter.get(), aFaceIter.FaceStyle(), anIsStarted); + myMaterialMap->AddTextures (myWriter.get(), aFaceIter.Style(), anIsStarted); } } if (anIsStarted) diff --git a/src/RWGltf/RWGltf_CafWriter.hxx b/src/RWGltf/RWGltf_CafWriter.hxx index dde01930c5..ef523f647d 100644 --- a/src/RWGltf/RWGltf_CafWriter.hxx +++ b/src/RWGltf/RWGltf_CafWriter.hxx @@ -14,23 +14,30 @@ #ifndef _RWGltf_CafWriter_HeaderFiler #define _RWGltf_CafWriter_HeaderFiler +#include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include class Message_ProgressRange; +class RWMesh_ShapeIterator; class RWMesh_FaceIterator; +class RWMesh_EdgeIterator; +class RWMesh_VertexIterator; class RWGltf_GltfOStreamWriter; class RWGltf_GltfMaterialMap; class RWGltf_GltfSceneNodeMap; @@ -202,8 +209,11 @@ protected: const Message_ProgressRange& theProgress); protected: - //! Return TRUE if face mesh should be skipped (e.g. because it is invalid or empty). - Standard_EXPORT virtual Standard_Boolean toSkipFaceMesh(const RWMesh_FaceIterator& theFaceIter); + //! Return TRUE if face shape should be skipped (e.g. because it is invalid or empty). + Standard_EXPORT virtual Standard_Boolean toSkipShape (const RWMesh_ShapeIterator& theShapeIter) const; + + //! Return TRUE if shape has triangulation (not vertex or edge). + Standard_EXPORT virtual Standard_Boolean hasTriangulation (const RWGltf_GltfFace& theShape) const; //! Generate name for specified labels. //! @param[in] theFormat name format to apply @@ -216,15 +226,14 @@ protected: //! Write mesh nodes into binary file. //! @param[out] theGltfFace glTF face definition //! @param[out] theBinFile output file to write into - //! @param[in] theFaceIter current face to write + //! @param[in] theShapeIter current shape to write //! @param[in][out] theAccessorNb last accessor index //! @param[in][out] theMesh mesh - Standard_EXPORT virtual void saveNodes( - RWGltf_GltfFace& theGltfFace, - std::ostream& theBinFile, - const RWMesh_FaceIterator& theFaceIter, - Standard_Integer& theAccessorNb, - const std::shared_ptr& theMesh) const; + Standard_EXPORT virtual void saveNodes (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_ShapeIterator& theShapeIter, + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh) const; //! Write mesh normals into binary file. //! @param[out] theGltfFace glTF face definition @@ -232,12 +241,11 @@ protected: //! @param[in] theFaceIter current face to write //! @param[in][out] theAccessorNb last accessor index //! @param[in][out] theMesh mesh - Standard_EXPORT virtual void saveNormals( - RWGltf_GltfFace& theGltfFace, - std::ostream& theBinFile, - RWMesh_FaceIterator& theFaceIter, - Standard_Integer& theAccessorNb, - const std::shared_ptr& theMesh) const; + Standard_EXPORT virtual void saveNormals (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_FaceIterator& theFaceIter, + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh) const; //! Write mesh texture UV coordinates into binary file. //! @param[out] theGltfFace glTF face definition @@ -255,14 +263,40 @@ protected: //! Write mesh indexes into binary file. //! @param[out] theGltfFace glTF face definition //! @param[out] theBinFile output file to write into - //! @param[in] theFaceIter current face to write + //! @param[in] theShapeIter current shape to write //! @param[in][out] theAccessorNb last accessor index //! @param[in][out] theMesh mesh - Standard_EXPORT virtual void saveIndices(RWGltf_GltfFace& theGltfFace, - std::ostream& theBinFile, - const RWMesh_FaceIterator& theFaceIter, - Standard_Integer& theAccessorNb, - const std::shared_ptr& theMesh); + Standard_EXPORT virtual void saveIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_ShapeIterator& theShapeIter, + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh); + + //! Write triangle indexes into binary file. + //! @param[out] theGltfFace glTF face definition + //! @param[out] theBinFile output file to write into + //! @param[in] theFaceIter current face to write + //! @param[in][out] theMesh mesh + Standard_EXPORT virtual void saveTriangleIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_FaceIterator& theFaceIter, + const std::shared_ptr& theMesh); + + //! Write edge indexes into binary file. + //! @param[out] theGltfFace glTF face definition + //! @param[out] theBinFile output file to write into + //! @param[in] theEdgeIter current edge to write + Standard_EXPORT virtual void saveEdgeIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_EdgeIterator& theEdgeIter); + + //! Write vertex indexes into binary file. + //! @param[out] theGltfFace glTF face definition + //! @param[out] theBinFile output file to write into + //! @param[in] theVertexIter current vertex to write + Standard_EXPORT virtual void saveVertexIndices (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + const RWMesh_VertexIterator& theVertexIter); protected: //! Write bufferView for vertex positions within RWGltf_GltfRootElement_Accessors section @@ -366,8 +400,50 @@ protected: //! Write nodes.extras section with key-value attributes. //! @param[in] theNamedData attributes map to process. - Standard_EXPORT virtual void writeExtrasAttributes( - const Handle(TDataStd_NamedData)& theNamedData); + Standard_EXPORT virtual void writeExtrasAttributes (const Handle(TDataStd_NamedData)& theNamedData); + + //! Dispatch shapes + //! @param[in] theDocNode Document node containing shape data + //! @param[in] thePSentryBin Progress scope for the operation + //! @param[in,out] theMergedFaces Data map to store merged faces + //! @param[in,out] theShapeIter Shape iterator to traverse shapes + Standard_EXPORT virtual void dispatchShapes (const XCAFPrs_DocumentNode& theDocNode, + const Message_ProgressScope& thePSentryBin, + NCollection_DataMap& theMergedFaces, + RWMesh_ShapeIterator& theShapeIter); + + //! Write shape into binary file + //! @param[out] theGltfFace glTF face definition + //! @param[out] theBinFile Output file to write into + //! @param[in] theShapeIter Current shape iterator + //! @param[in,out] theAccessorNb Last accessor index + //! @param[in,out] theMesh Mesh data + //! @param[in] theArrType Array type for glTF + //! @param[in] thePSentryBin Progress scope for the operation + //! @return True if shapes were successfully written to the binary file, false otherwise + Standard_EXPORT bool writeShapesToBin (RWGltf_GltfFace& theGltfFace, + std::ostream& theBinFile, + RWMesh_ShapeIterator& theShapeIter, + Standard_Integer& theAccessorNb, + const std::shared_ptr& theMesh, + const RWGltf_GltfArrayType theArrType, + const Message_ProgressScope& thePSentryBin); + + //! Write shapes to RWGltf_GltfRootElement_Meshes section + //! @param[in] theShapeIter Shape iterator to traverse shapes + //! @param[in,out] theNbFacesInNode Number of faces in the current node + //! @param[in,out] theDracoBufInd Draco buffer index + //! @param[in,out] theToStartPrims Flag to indicate if primitives should be started + //! @param[in] theNodeName Name of the current node + //! @param[in,out] theWrittenShapes Map to store written shapes + //! @param[in,out] theDracoBufIndMap Map to store Draco buffer indices + Standard_EXPORT virtual void writeShapes (RWMesh_ShapeIterator& theShapeIter, + Standard_Integer& theNbFacesInNode, + Standard_Integer& theDracoBufInd, + Standard_Boolean& theToStartPrims, + const TCollection_AsciiString& theNodeName, + NCollection_Map& theWrittenShapes, + NCollection_IndexedDataMap& theDracoBufIndMap); protected: //! Shape + Style pair. diff --git a/src/RWMesh/FILES b/src/RWMesh/FILES index 63bb91ee07..cbe8a51c98 100644 --- a/src/RWMesh/FILES +++ b/src/RWMesh/FILES @@ -5,13 +5,19 @@ RWMesh_CafReader.hxx RWMesh_CoordinateSystem.hxx RWMesh_CoordinateSystemConverter.cxx RWMesh_CoordinateSystemConverter.hxx +RWMesh_EdgeIterator.cxx +RWMesh_EdgeIterator.hxx RWMesh_FaceIterator.cxx RWMesh_FaceIterator.hxx RWMesh_MaterialMap.cxx RWMesh_MaterialMap.hxx RWMesh_NameFormat.hxx RWMesh_NodeAttributes.hxx +RWMesh_ShapeIterator.cxx +RWMesh_ShapeIterator.hxx RWMesh_TriangulationReader.cxx RWMesh_TriangulationReader.hxx RWMesh_TriangulationSource.cxx RWMesh_TriangulationSource.hxx +RWMesh_VertexIterator.cxx +RWMesh_VertexIterator.hxx diff --git a/src/RWMesh/RWMesh_EdgeIterator.cxx b/src/RWMesh/RWMesh_EdgeIterator.cxx new file mode 100644 index 0000000000..bdcbe8ce81 --- /dev/null +++ b/src/RWMesh/RWMesh_EdgeIterator.cxx @@ -0,0 +1,78 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include + +// ======================================================================= +// function : RWMesh_EdgeIterator +// purpose : +// ======================================================================= +RWMesh_EdgeIterator::RWMesh_EdgeIterator(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const Standard_Boolean theToMapColors, + const XCAFPrs_Style& theStyle) + : RWMesh_ShapeIterator(theLabel, theLocation, TopAbs_EDGE, theToMapColors, theStyle) +{ + Next(); +} + +// ======================================================================= +// function : RWMesh_EdgeIterator +// purpose : +// ======================================================================= +RWMesh_EdgeIterator::RWMesh_EdgeIterator(const TopoDS_Shape& theShape, + const XCAFPrs_Style& theStyle) + : RWMesh_ShapeIterator(theShape, TopAbs_EDGE, theStyle) +{ + Next(); +} + +// ======================================================================= +// function : Next +// purpose : +// ======================================================================= +void RWMesh_EdgeIterator::Next() +{ + for (; myIter.More(); myIter.Next()) + { + myEdge = TopoDS::Edge(myIter.Current()); + myPolygon3D = BRep_Tool::Polygon3D(myEdge, myLocation); + myTrsf = myLocation.Transformation(); + if (myPolygon3D.IsNull() || myPolygon3D->NbNodes() == 0) + { + resetEdge(); + continue; + } + + initEdge(); + myIter.Next(); + return; + } + + resetEdge(); +} + +// ======================================================================= +// function : initEdge +// purpose : +// ======================================================================= +void RWMesh_EdgeIterator::initEdge() +{ + initShape(); +} diff --git a/src/RWMesh/RWMesh_EdgeIterator.hxx b/src/RWMesh/RWMesh_EdgeIterator.hxx new file mode 100644 index 0000000000..b71c012866 --- /dev/null +++ b/src/RWMesh/RWMesh_EdgeIterator.hxx @@ -0,0 +1,118 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _RWMesh_EdgeIterator_HeaderFile +#define _RWMesh_EdgeIterator_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class TDF_Label; + +//! Auxiliary class to iterate through edges. +//! Provides functionality to iterate through the edges of a shape. +//! It inherits from `RWMesh_ShapeIterator` and implements +//! methods to access and manipulate edge data. +class RWMesh_EdgeIterator : public RWMesh_ShapeIterator +{ +public: + //! Main constructor. + //! @param[in] theLabel The label of the shape. + //! @param[in] theLocation The location of the shape. + //! @param[in] theToMapColors Flag to indicate if colors should be mapped. + //! @param[in] theStyle The style of the shape. + Standard_EXPORT RWMesh_EdgeIterator(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const Standard_Boolean theToMapColors = false, + const XCAFPrs_Style& theStyle = XCAFPrs_Style()); + + //! Auxiliary constructor. + //! @param[in] theShape The shape to iterate. + //! @param[in] theStyle The style of the shape. + Standard_EXPORT RWMesh_EdgeIterator(const TopoDS_Shape& theShape, + const XCAFPrs_Style& theStyle = XCAFPrs_Style()); + + //! Return true if iterator points to the valid triangulation. + bool More() const Standard_OVERRIDE { return !myPolygon3D.IsNull(); } + + //! Find next value. + Standard_EXPORT void Next() Standard_OVERRIDE; + + //! Return current edge. + const TopoDS_Edge& Edge() const { return myEdge; } + + //! Return current edge. + const TopoDS_Shape& Shape() const Standard_OVERRIDE { return myEdge; } + + //! Return current edge data. + const Handle(Poly_Polygon3D)& Polygon3D() const { return myPolygon3D; } + + //! Return true if geometry data is defined. + bool IsEmpty() const Standard_OVERRIDE + { + return myPolygon3D.IsNull() || myPolygon3D->NbNodes() < 1; + } + +public: + //! Lower element index in current triangulation. + Standard_Integer ElemLower() const Standard_OVERRIDE { return 1; } + + //! Upper element index in current triangulation. + Standard_Integer ElemUpper() const Standard_OVERRIDE { return myPolygon3D->NbNodes(); } + +public: + //! Return number of nodes for the current edge. + Standard_Integer NbNodes() const Standard_OVERRIDE + { + return !myPolygon3D.IsNull() ? myPolygon3D->NbNodes() : 0; + } + + //! Lower node index in current triangulation. + Standard_Integer NodeLower() const Standard_OVERRIDE { return 1; } + + //! Upper node index in current triangulation. + Standard_Integer NodeUpper() const Standard_OVERRIDE { return myPolygon3D->NbNodes(); } + +public: + //! Return the node with specified index with applied transformation. + gp_Pnt node(const Standard_Integer theNode) const Standard_OVERRIDE + { + return myPolygon3D->Nodes().Value(theNode); + } + +private: + //! Reset information for current edge. + void resetEdge() + { + myPolygon3D.Nullify(); + myEdge.Nullify(); + resetShape(); + } + + //! Initialize edge properties. + void initEdge(); + +private: + TopoDS_Edge myEdge; //!< current edge + Handle(Poly_Polygon3D) myPolygon3D; //!< geometry of current edge +}; + +#endif // _RWMesh_EdgeIterator_HeaderFile diff --git a/src/RWMesh/RWMesh_FaceIterator.cxx b/src/RWMesh/RWMesh_FaceIterator.cxx index 1f1a5ab010..2cdc3c923d 100644 --- a/src/RWMesh/RWMesh_FaceIterator.cxx +++ b/src/RWMesh/RWMesh_FaceIterator.cxx @@ -28,28 +28,11 @@ RWMesh_FaceIterator::RWMesh_FaceIterator(const TDF_Label& theLabel, const TopLoc_Location& theLocation, const Standard_Boolean theToMapColors, const XCAFPrs_Style& theStyle) - : myDefStyle(theStyle), - myToMapColors(theToMapColors), + : RWMesh_ShapeIterator(theLabel, theLocation, TopAbs_FACE, theToMapColors, theStyle), mySLTool(1, 1e-12), myHasNormals(false), - myIsMirrored(false), - myHasFaceColor(false) + myIsMirrored(false) { - TopoDS_Shape aShape; - if (!XCAFDoc_ShapeTool::GetShape(theLabel, aShape) || aShape.IsNull()) - { - return; - } - - aShape.Location(theLocation, false); - myFaceIter.Init(aShape, TopAbs_FACE); - - if (theToMapColors) - { - dispatchStyles(theLabel, theLocation, theStyle); - myStyles.Bind(aShape, theStyle); - } - Next(); } @@ -59,90 +42,14 @@ RWMesh_FaceIterator::RWMesh_FaceIterator(const TDF_Label& theLabel, // ======================================================================= RWMesh_FaceIterator::RWMesh_FaceIterator(const TopoDS_Shape& theShape, const XCAFPrs_Style& theStyle) - : myDefStyle(theStyle), - myToMapColors(true), + : RWMesh_ShapeIterator(theShape, TopAbs_FACE, theStyle), mySLTool(1, 1e-12), myHasNormals(false), - myIsMirrored(false), - myHasFaceColor(false) + myIsMirrored(false) { - if (theShape.IsNull()) - { - return; - } - - myFaceIter.Init(theShape, TopAbs_FACE); Next(); } -// ======================================================================= -// function : dispatchStyles -// purpose : -// ======================================================================= -void RWMesh_FaceIterator::dispatchStyles(const TDF_Label& theLabel, - const TopLoc_Location& theLocation, - const XCAFPrs_Style& theStyle) -{ - TopLoc_Location aDummyLoc; - XCAFPrs_IndexedDataMapOfShapeStyle aStyles; - XCAFPrs::CollectStyleSettings(theLabel, aDummyLoc, aStyles); - - Standard_Integer aNbTypes[TopAbs_SHAPE] = {}; - for (Standard_Integer aTypeIter = TopAbs_FACE; aTypeIter >= TopAbs_COMPOUND; --aTypeIter) - { - if (aTypeIter != TopAbs_FACE && aNbTypes[aTypeIter] == 0) - { - continue; - } - - for (XCAFPrs_IndexedDataMapOfShapeStyle::Iterator aStyleIter(aStyles); aStyleIter.More(); - aStyleIter.Next()) - { - const TopoDS_Shape& aKeyShape = aStyleIter.Key(); - const TopAbs_ShapeEnum aKeyShapeType = aKeyShape.ShapeType(); - if (aTypeIter == TopAbs_FACE) - { - ++aNbTypes[aKeyShapeType]; - } - if (aTypeIter != aKeyShapeType) - { - continue; - } - - XCAFPrs_Style aCafStyle = aStyleIter.Value(); - if (!aCafStyle.IsSetColorCurv() && theStyle.IsSetColorCurv()) - { - aCafStyle.SetColorCurv(theStyle.GetColorCurv()); - } - if (!aCafStyle.IsSetColorSurf() && theStyle.IsSetColorSurf()) - { - aCafStyle.SetColorSurf(theStyle.GetColorSurfRGBA()); - } - if (aCafStyle.Material().IsNull() && !theStyle.Material().IsNull()) - { - aCafStyle.SetMaterial(theStyle.Material()); - } - - TopoDS_Shape aKeyShapeLocated = aKeyShape.Located(theLocation); - if (aKeyShapeType == TopAbs_FACE) - { - myStyles.Bind(aKeyShapeLocated, aCafStyle); - } - else - { - for (TopExp_Explorer aFaceIter(aKeyShapeLocated, TopAbs_FACE); aFaceIter.More(); - aFaceIter.Next()) - { - if (!myStyles.IsBound(aFaceIter.Current())) - { - myStyles.Bind(aFaceIter.Current(), aCafStyle); - } - } - } - } - } -} - // ======================================================================= // function : normal // purpose : @@ -177,11 +84,11 @@ gp_Dir RWMesh_FaceIterator::normal(Standard_Integer theNode) const // ======================================================================= void RWMesh_FaceIterator::Next() { - for (; myFaceIter.More(); myFaceIter.Next()) + for (; myIter.More(); myIter.Next()) { - myFace = TopoDS::Face(myFaceIter.Current()); - myPolyTriang = BRep_Tool::Triangulation(myFace, myFaceLocation); - myTrsf = myFaceLocation.Transformation(); + myFace = TopoDS::Face(myIter.Current()); + myPolyTriang = BRep_Tool::Triangulation(myFace, myLocation); + myTrsf = myLocation.Transformation(); if (myPolyTriang.IsNull() || myPolyTriang->NbTriangles() == 0) { resetFace(); @@ -189,7 +96,7 @@ void RWMesh_FaceIterator::Next() } initFace(); - myFaceIter.Next(); + myIter.Next(); return; } @@ -202,9 +109,8 @@ void RWMesh_FaceIterator::Next() // ======================================================================= void RWMesh_FaceIterator::initFace() { - myHasNormals = false; - myHasFaceColor = false; - myIsMirrored = myTrsf.VectorialPart().Determinant() < 0.0; + myHasNormals = false; + myIsMirrored = myTrsf.VectorialPart().Determinant() < 0.0; if (myPolyTriang->HasNormals()) { myHasNormals = true; @@ -221,24 +127,6 @@ void RWMesh_FaceIterator::initFace() myHasNormals = true; } } - if (!myToMapColors) - { - return; - } - - if (!myStyles.Find(myFace, myFaceStyle)) - { - myFaceStyle = myDefStyle; - } - if (!myFaceStyle.Material().IsNull()) - { - myHasFaceColor = true; - myFaceColor = myFaceStyle.Material()->BaseColor(); - } - else if (myFaceStyle.IsSetColorSurf()) - { - myHasFaceColor = true; - myFaceColor = myFaceStyle.GetColorSurfRGBA(); - } + initShape(); } diff --git a/src/RWMesh/RWMesh_FaceIterator.hxx b/src/RWMesh/RWMesh_FaceIterator.hxx index 4fdb8e89f8..7073999228 100644 --- a/src/RWMesh/RWMesh_FaceIterator.hxx +++ b/src/RWMesh/RWMesh_FaceIterator.hxx @@ -14,50 +14,60 @@ #ifndef _RWMesh_FaceIterator_HeaderFile #define _RWMesh_FaceIterator_HeaderFile +#include + #include -#include -#include #include -#include #include -#include -#include #include class TDF_Label; //! Auxiliary class to iterate through triangulated faces. -class RWMesh_FaceIterator +//! Class is designed to provide an interface for iterating over the faces +//! of a shape, specifically focusing on triangulated faces. +//! It inherits from the `RWMesh_ShapeIterator` base class and +//! extends its functionality to handle faces with triangulation data. +class RWMesh_FaceIterator : public RWMesh_ShapeIterator { public: //! Main constructor. + //! @param[in] theLabel Label containing the face data + //! @param[in] theLocation Location of the face + //! @param[in] theToMapColors Flag to indicate if colors should be mapped + //! @param[in] theStyle Style information for the face Standard_EXPORT RWMesh_FaceIterator(const TDF_Label& theLabel, const TopLoc_Location& theLocation, const Standard_Boolean theToMapColors = false, const XCAFPrs_Style& theStyle = XCAFPrs_Style()); //! Auxiliary constructor. + //! @param[in] theShape Shape containing the face data + //! @param[in] theStyle Style information for the face Standard_EXPORT RWMesh_FaceIterator(const TopoDS_Shape& theShape, const XCAFPrs_Style& theStyle = XCAFPrs_Style()); - //! Return explored shape. - const TopoDS_Shape& ExploredShape() const { return myFaceIter.ExploredShape(); } - //! Return true if iterator points to the valid triangulation. - bool More() const { return !myPolyTriang.IsNull(); } + bool More() const Standard_OVERRIDE { return !myPolyTriang.IsNull(); } //! Find next value. - Standard_EXPORT void Next(); + Standard_EXPORT void Next() Standard_OVERRIDE; //! Return current face. const TopoDS_Face& Face() const { return myFace; } + //! Return current face. + const TopoDS_Shape& Shape() const Standard_OVERRIDE { return myFace; } + //! Return current face triangulation. const Handle(Poly_Triangulation)& Triangulation() const { return myPolyTriang; } //! Return true if mesh data is defined. - bool IsEmptyMesh() const + bool IsEmptyMesh() const { return IsEmpty(); } + + //! Return true if mesh data is defined. + bool IsEmpty() const Standard_OVERRIDE { return myPolyTriang.IsNull() || (myPolyTriang->NbNodes() < 1 && myPolyTriang->NbTriangles() < 1); @@ -65,23 +75,23 @@ public: public: //! Return face material. - const XCAFPrs_Style& FaceStyle() const { return myFaceStyle; } + const XCAFPrs_Style& FaceStyle() const { return myStyle; } //! Return TRUE if face color is set. - bool HasFaceColor() const { return myHasFaceColor; } + bool HasFaceColor() const { return myHasColor; } //! Return face color. - const Quantity_ColorRGBA& FaceColor() const { return myFaceColor; } + const Quantity_ColorRGBA& FaceColor() const { return myColor; } public: //! Return number of elements of specific type for the current face. Standard_Integer NbTriangles() const { return myPolyTriang->NbTriangles(); } //! Lower element index in current triangulation. - Standard_Integer ElemLower() const { return 1; } + Standard_Integer ElemLower() const Standard_OVERRIDE { return 1; } //! Upper element index in current triangulation. - Standard_Integer ElemUpper() const { return myPolyTriang->NbTriangles(); } + Standard_Integer ElemUpper() const Standard_OVERRIDE { return myPolyTriang->NbTriangles(); } //! Return triangle with specified index with applied Face orientation. Poly_Triangle TriangleOriented(Standard_Integer theElemIndex) const @@ -118,21 +128,16 @@ public: } //! Return number of nodes for the current face. - Standard_Integer NbNodes() const { return !myPolyTriang.IsNull() ? myPolyTriang->NbNodes() : 0; } + Standard_Integer NbNodes() const Standard_OVERRIDE + { + return !myPolyTriang.IsNull() ? myPolyTriang->NbNodes() : 0; + } //! Lower node index in current triangulation. - Standard_Integer NodeLower() const { return 1; } + Standard_Integer NodeLower() const Standard_OVERRIDE { return 1; } //! Upper node index in current triangulation. - Standard_Integer NodeUpper() const { return myPolyTriang->NbNodes(); } - - //! Return the node with specified index with applied transformation. - gp_Pnt NodeTransformed(const Standard_Integer theNode) const - { - gp_Pnt aNode = node(theNode); - aNode.Transform(myTrsf); - return aNode; - } + Standard_Integer NodeUpper() const Standard_OVERRIDE { return myPolyTriang->NbNodes(); } //! Return texture coordinates for the node. gp_Pnt2d NodeTexCoord(const Standard_Integer theNode) const @@ -142,7 +147,10 @@ public: public: //! Return the node with specified index with applied transformation. - gp_Pnt node(const Standard_Integer theNode) const { return myPolyTriang->Node(theNode); } + gp_Pnt node(const Standard_Integer theNode) const Standard_OVERRIDE + { + return myPolyTriang->Node(theNode); + } //! Return normal at specified node index without face transformation applied. Standard_EXPORT gp_Dir normal(Standard_Integer theNode) const; @@ -154,44 +162,26 @@ public: } private: - //! Dispatch face styles. - void dispatchStyles(const TDF_Label& theLabel, - const TopLoc_Location& theLocation, - const XCAFPrs_Style& theStyle); - //! Reset information for current face. void resetFace() { myPolyTriang.Nullify(); myFace.Nullify(); myHasNormals = false; - myHasFaceColor = false; - myFaceColor = Quantity_ColorRGBA(); - myFaceStyle = XCAFPrs_Style(); + resetShape(); } //! Initialize face properties. void initFace(); private: - NCollection_DataMap - myStyles; //!< Face -> Style map // clang-format off - XCAFPrs_Style myDefStyle; //!< default style for faces without dedicated style - Standard_Boolean myToMapColors; //!< flag to dispatch styles - - TopExp_Explorer myFaceIter; //!< face explorer - TopoDS_Face myFace; //!< current face - Handle(Poly_Triangulation) myPolyTriang; //!< triangulation of current face - TopLoc_Location myFaceLocation; //!< current face location - mutable BRepLProp_SLProps mySLTool; //!< auxiliary tool for fetching normals from surface - BRepAdaptor_Surface myFaceAdaptor; //!< surface adaptor for fetching normals from surface - Standard_Boolean myHasNormals; //!< flag indicating that current face has normals - gp_Trsf myTrsf; //!< current face transformation - Standard_Boolean myIsMirrored; //!< flag indicating that face triangles should be mirrored - XCAFPrs_Style myFaceStyle; //!< current face style - Quantity_ColorRGBA myFaceColor; //!< current face color - Standard_Boolean myHasFaceColor; //!< flag indicating that current face has assigned color + TopoDS_Face myFace; //!< current face + Handle(Poly_Triangulation) myPolyTriang; //!< triangulation of current face + mutable BRepLProp_SLProps mySLTool; //!< auxiliary tool for fetching normals from surface + BRepAdaptor_Surface myFaceAdaptor; //!< surface adaptor for fetching normals from surface + Standard_Boolean myHasNormals; //!< flag indicating that current face has normals + Standard_Boolean myIsMirrored; //!< flag indicating that face triangles should be mirrored // clang-format on }; diff --git a/src/RWMesh/RWMesh_ShapeIterator.cxx b/src/RWMesh/RWMesh_ShapeIterator.cxx new file mode 100644 index 0000000000..63c3fccaf3 --- /dev/null +++ b/src/RWMesh/RWMesh_ShapeIterator.cxx @@ -0,0 +1,163 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include + +// ======================================================================= +// function : RWMesh_ShapeIterator +// purpose : +// ======================================================================= +RWMesh_ShapeIterator::RWMesh_ShapeIterator(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const TopAbs_ShapeEnum theShapeType, + const Standard_Boolean theToMapColors, + const XCAFPrs_Style& theStyle) + : myDefStyle(theStyle), + myToMapColors(theToMapColors), + myShapeType(theShapeType), + myHasColor(false) +{ + TopoDS_Shape aShape; + if (!XCAFDoc_ShapeTool::GetShape(theLabel, aShape) || aShape.IsNull()) + { + return; + } + + aShape.Location(theLocation, false); + myIter.Init(aShape, myShapeType); + + if (theToMapColors) + { + dispatchStyles(theLabel, theLocation, theStyle); + myStyles.Bind(aShape, theStyle); + } +} + +// ======================================================================= +// function : RWMesh_ShapeIterator +// purpose : +// ======================================================================= +RWMesh_ShapeIterator::RWMesh_ShapeIterator(const TopoDS_Shape& theShape, + const TopAbs_ShapeEnum theShapeType, + const XCAFPrs_Style& theStyle) + : myDefStyle(theStyle), + myToMapColors(true), + myShapeType(theShapeType), + myHasColor(false) +{ + if (theShape.IsNull()) + { + return; + } + myIter.Init(theShape, myShapeType); +} + +// ======================================================================= +// function : dispatchStyles +// purpose : +// ======================================================================= +void RWMesh_ShapeIterator::dispatchStyles(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const XCAFPrs_Style& theStyle) +{ + TopLoc_Location aDummyLoc; + XCAFPrs_IndexedDataMapOfShapeStyle aStyles; + XCAFPrs::CollectStyleSettings(theLabel, aDummyLoc, aStyles); + + Standard_Integer aNbTypes[TopAbs_SHAPE] = {}; + for (Standard_Integer aTypeIter = myShapeType; aTypeIter >= TopAbs_COMPOUND; --aTypeIter) + { + if (aTypeIter != myShapeType && aNbTypes[aTypeIter] == 0) + { + continue; + } + + for (XCAFPrs_IndexedDataMapOfShapeStyle::Iterator aStyleIter(aStyles); aStyleIter.More(); + aStyleIter.Next()) + { + const TopoDS_Shape& aKeyShape = aStyleIter.Key(); + const TopAbs_ShapeEnum aKeyShapeType = aKeyShape.ShapeType(); + if (aTypeIter == myShapeType) + { + ++aNbTypes[aKeyShapeType]; + } + if (aTypeIter != aKeyShapeType) + { + continue; + } + + XCAFPrs_Style aCafStyle = aStyleIter.Value(); + if (!aCafStyle.IsSetColorCurv() && theStyle.IsSetColorCurv()) + { + aCafStyle.SetColorCurv(theStyle.GetColorCurv()); + } + if (!aCafStyle.IsSetColorSurf() && theStyle.IsSetColorSurf()) + { + aCafStyle.SetColorSurf(theStyle.GetColorSurfRGBA()); + } + if (aCafStyle.Material().IsNull() && !theStyle.Material().IsNull()) + { + aCafStyle.SetMaterial(theStyle.Material()); + } + + TopoDS_Shape aKeyShapeLocated = aKeyShape.Located(theLocation); + if (aKeyShapeType == myShapeType) + { + myStyles.Bind(aKeyShapeLocated, aCafStyle); + } + else + { + for (TopExp_Explorer aShapeIter(aKeyShapeLocated, myShapeType); aShapeIter.More(); + aShapeIter.Next()) + { + if (!myStyles.IsBound(aShapeIter.Current())) + { + myStyles.Bind(aShapeIter.Current(), aCafStyle); + } + } + } + } + } +} + +// ======================================================================= +// function : initEdge +// purpose : +// ======================================================================= +void RWMesh_ShapeIterator::initShape() +{ + myHasColor = false; + if (!myToMapColors) + { + return; + } + if (!myStyles.Find(Shape(), myStyle)) + { + myStyle = myDefStyle; + } + if (!myStyle.Material().IsNull()) + { + myHasColor = true; + myColor = myStyle.Material()->BaseColor(); + } + else if (myStyle.IsSetColorSurf()) + { + myHasColor = true; + myColor = myStyle.GetColorSurfRGBA(); + } +} \ No newline at end of file diff --git a/src/RWMesh/RWMesh_ShapeIterator.hxx b/src/RWMesh/RWMesh_ShapeIterator.hxx new file mode 100644 index 0000000000..6efdfa915c --- /dev/null +++ b/src/RWMesh/RWMesh_ShapeIterator.hxx @@ -0,0 +1,131 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _RWMesh_ShapeIterator_HeaderFile +#define _RWMesh_ShapeIterator_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class TDF_Label; + +//! This is a virtual base class for other shape iterators. +//! Provides an abstract interface for iterating over the elements of a shape. +//! It defines a set of pure virtual methods that must be implemented by +//! derived classes to handle specific types of shapes and their elements. +class RWMesh_ShapeIterator +{ +public: + //! Return explored shape. + const TopoDS_Shape& ExploredShape() const { return myIter.ExploredShape(); } + + //! Return shape. + Standard_EXPORT virtual const TopoDS_Shape& Shape() const = 0; + + //! Return true if iterator points to the valid triangulation. + Standard_EXPORT virtual bool More() const = 0; + + //! Find next value. + Standard_EXPORT virtual void Next() = 0; + + //! Return true if mesh data is defined. + Standard_EXPORT virtual bool IsEmpty() const = 0; + + //! Return shape material. + const XCAFPrs_Style& Style() const { return myStyle; } + + //! Return TRUE if shape color is set. + bool HasColor() const { return myHasColor; } + + //! Return shape color. + const Quantity_ColorRGBA& Color() const { return myColor; } + + //! Lower element index in current triangulation. + Standard_EXPORT virtual Standard_Integer ElemLower() const = 0; + + //! Upper element index in current triangulation. + Standard_EXPORT virtual Standard_Integer ElemUpper() const = 0; + + //! Return number of nodes for the current shape. + Standard_EXPORT virtual Standard_Integer NbNodes() const = 0; + + //! Lower node index in current shape. + Standard_EXPORT virtual Standard_Integer NodeLower() const = 0; + + //! Upper node index in current shape. + Standard_EXPORT virtual Standard_Integer NodeUpper() const = 0; + + //! Return the node with specified index with applied transformation. + gp_Pnt NodeTransformed(const Standard_Integer theNode) const + { + gp_Pnt aNode = node(theNode); + aNode.Transform(myTrsf); + return aNode; + } + +protected: + //! Return the node with specified index with applied transformation. + virtual gp_Pnt node(const Standard_Integer theNode) const = 0; + + //! Main constructor. + RWMesh_ShapeIterator(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const TopAbs_ShapeEnum theShapeType, + const Standard_Boolean theToMapColors = false, + const XCAFPrs_Style& theStyle = XCAFPrs_Style()); + + //! Auxiliary constructor. + RWMesh_ShapeIterator(const TopoDS_Shape& theShape, + const TopAbs_ShapeEnum theShapeType, + const XCAFPrs_Style& theStyle = XCAFPrs_Style()); + + //! Dispatch shape styles. + void dispatchStyles(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const XCAFPrs_Style& theStyle); + + //! Reset information for current shape. + void resetShape() + { + myHasColor = false; + myColor = Quantity_ColorRGBA(); + myStyle = XCAFPrs_Style(); + } + + //! Initialize shape properties. + void initShape(); + +protected: + NCollection_DataMap + myStyles; //!< Shape -> Style map + XCAFPrs_Style myDefStyle; //!< default style for shapes without dedicated style + Standard_Boolean myToMapColors; //!< flag to dispatch styles + + TopExp_Explorer myIter; //!< shape explorer + TopLoc_Location myLocation; //!< current shape location + gp_Trsf myTrsf; //!< current shape transformation + XCAFPrs_Style myStyle; //!< current shape style + Quantity_ColorRGBA myColor; //!< current shape color + TopAbs_ShapeEnum myShapeType; //!< type of shape + Standard_Boolean myHasColor; //!< flag indicating that current shape has assigned color +}; + +#endif // _RWMesh_ShapeIterator_HeaderFile diff --git a/src/RWMesh/RWMesh_VertexIterator.cxx b/src/RWMesh/RWMesh_VertexIterator.cxx new file mode 100644 index 0000000000..ce9d3deb47 --- /dev/null +++ b/src/RWMesh/RWMesh_VertexIterator.cxx @@ -0,0 +1,78 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include +#include + +// ======================================================================= +// function : RWMesh_VertexIterator +// purpose : +// ======================================================================= +RWMesh_VertexIterator::RWMesh_VertexIterator(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const Standard_Boolean theToMapColors, + const XCAFPrs_Style& theStyle) + : RWMesh_ShapeIterator(theLabel, theLocation, TopAbs_VERTEX, theToMapColors, theStyle) +{ + Next(); +} + +// ======================================================================= +// function : RWMesh_VertexIterator +// purpose : +// ======================================================================= +RWMesh_VertexIterator::RWMesh_VertexIterator(const TopoDS_Shape& theShape, + const XCAFPrs_Style& theStyle) + : RWMesh_ShapeIterator(theShape, TopAbs_VERTEX, theStyle) +{ + Next(); +} + +// ======================================================================= +// function : Next +// purpose : +// ======================================================================= +void RWMesh_VertexIterator::Next() +{ + for (; myIter.More(); myIter.Next()) + { + myVertex = TopoDS::Vertex(myIter.Current()); + myPoint = BRep_Tool::Pnt(myVertex); + myTrsf = myLocation.Transformation(); + if (myVertex.IsNull()) + { + resetVertex(); + continue; + } + + initVertex(); + myIter.Next(); + return; + } + + resetVertex(); +} + +// ======================================================================= +// function : initEdge +// purpose : +// ======================================================================= +void RWMesh_VertexIterator::initVertex() +{ + initShape(); +} diff --git a/src/RWMesh/RWMesh_VertexIterator.hxx b/src/RWMesh/RWMesh_VertexIterator.hxx new file mode 100644 index 0000000000..c57945a300 --- /dev/null +++ b/src/RWMesh/RWMesh_VertexIterator.hxx @@ -0,0 +1,108 @@ +// Copyright (c) 2025 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _RWMesh_VertexIterator_HeaderFile +#define _RWMesh_VertexIterator_HeaderFile + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class TDF_Label; + +//! Auxiliary class to iterate through vertices. +//! Provides functionality to iterate through the vertices of a shape. +//! It inherits from `RWMesh_ShapeIterator` and implements +//! methods to access and manipulate vertex data. +class RWMesh_VertexIterator : public RWMesh_ShapeIterator +{ +public: + //! Main constructor. + //! @param[in] theLabel The label of the shape. + //! @param[in] theLocation The location of the shape. + //! @param[in] theToMapColors Flag to indicate if colors should be mapped. + //! @param[in] theStyle The style of the shape. + Standard_EXPORT RWMesh_VertexIterator(const TDF_Label& theLabel, + const TopLoc_Location& theLocation, + const Standard_Boolean theToMapColors = false, + const XCAFPrs_Style& theStyle = XCAFPrs_Style()); + + //! Auxiliary constructor. + //! @param[in] theShape The shape to iterate. + //! @param[in] theStyle The style of the shape. + Standard_EXPORT RWMesh_VertexIterator(const TopoDS_Shape& theShape, + const XCAFPrs_Style& theStyle = XCAFPrs_Style()); + + //! Return true if iterator points to the valid triangulation. + bool More() const Standard_OVERRIDE { return !myVertex.IsNull(); } + + //! Find next value. + Standard_EXPORT void Next() Standard_OVERRIDE; + + //! Return current edge. + const TopoDS_Vertex& Vertex() const { return myVertex; } + + //! Return current vertex. + const TopoDS_Shape& Shape() const Standard_OVERRIDE { return myVertex; } + + //! Return current vertex data. + const gp_Pnt& Point() const { return myPoint; } + + //! Return true if geometry data is defined. + bool IsEmpty() const Standard_OVERRIDE { return myVertex.IsNull(); } + +public: + //! Lower element index in current triangulation. + Standard_Integer ElemLower() const Standard_OVERRIDE { return 1; } + + //! Upper element index in current triangulation. + Standard_Integer ElemUpper() const Standard_OVERRIDE { return 1; } + +public: + //! Return number of nodes for the current edge. + Standard_Integer NbNodes() const Standard_OVERRIDE { return 1; } + + //! Lower node index in current triangulation. + Standard_Integer NodeLower() const Standard_OVERRIDE { return 1; } + + //! Upper node index in current triangulation. + Standard_Integer NodeUpper() const Standard_OVERRIDE { return 1; } + +public: + //! Return the node with specified index with applied transformation. + gp_Pnt node(const Standard_Integer /*theNode*/) const Standard_OVERRIDE { return myPoint; } + +private: + //! Reset information for current vertex. + void resetVertex() + { + myVertex.Nullify(); + resetShape(); + } + + //! Initialize vertex properties. + void initVertex(); + +private: + TopoDS_Vertex myVertex; //!< current vertex + gp_Pnt myPoint; //!< geometry of current vertex +}; + +#endif // _RWMesh_VertexIterator_HeaderFile diff --git a/tests/de_mesh/gltf_write/draco_mesh b/tests/de_mesh/gltf_write/draco_mesh new file mode 100644 index 0000000000..d64eba2899 --- /dev/null +++ b/tests/de_mesh/gltf_write/draco_mesh @@ -0,0 +1,22 @@ +puts "========" +puts "278: Data Exchange, Fail to export to GLTF with draco buffer index out of bounds" +puts "========" + +vclear +vclose ALL +Close * + +set aTmpGltf "${imagedir}/${casename}_tmp.glb" + +ReadStep D [locate_data_file "bug31670_russian.stp"] +XGetOneShape s D +incmesh s 0.1 + +XNewDoc D +XAddShape D s + +WriteGltf D "$aTmpGltf" -draco -mergefaces + +ReadGltf D1 "$aTmpGltf" +XGetOneShape s1 D1 +checknbshapes s1 -face 11 -compound 10 -shape 21