]> OCCT Git - occt.git/commitdiff
GLTF Export - Edge and Vertex support #243
authormzernova <mzernova@opencascade.com>
Sun, 5 Jan 2025 22:43:23 +0000 (22:43 +0000)
committermzernova <mzernova@opencascade.com>
Sun, 2 Feb 2025 21:25:53 +0000 (21:25 +0000)
Added functionality to export TopoDS_Vertex and TopoDS_Edge to GLTF format

12 files changed:
src/RWGltf/RWGltf_CafWriter.cxx
src/RWGltf/RWGltf_CafWriter.hxx
src/RWMesh/FILES
src/RWMesh/RWMesh_EdgeIterator.cxx [new file with mode: 0644]
src/RWMesh/RWMesh_EdgeIterator.hxx [new file with mode: 0644]
src/RWMesh/RWMesh_FaceIterator.cxx
src/RWMesh/RWMesh_FaceIterator.hxx
src/RWMesh/RWMesh_ShapeIterator.cxx [new file with mode: 0644]
src/RWMesh/RWMesh_ShapeIterator.hxx [new file with mode: 0644]
src/RWMesh/RWMesh_VertexIterator.cxx [new file with mode: 0644]
src/RWMesh/RWMesh_VertexIterator.hxx [new file with mode: 0644]
tests/de_mesh/gltf_write/draco_mesh [new file with mode: 0644]

index c9f21f6941f5a853dc81f106d1e39fffa823411d..c2d14ac52fc6b67d066e905357d20df22df1636f 100644 (file)
 #include <gp_Quaternion.hxx>
 #include <Message.hxx>
 #include <Message_Messenger.hxx>
-#include <Message_ProgressScope.hxx>
-#include <NCollection_DataMap.hxx>
 #include <OSD_FileSystem.hxx>
 #include <OSD_File.hxx>
 #include <OSD_Parallel.hxx>
 #include <OSD_Path.hxx>
 #include <OSD_Timer.hxx>
 #include <RWGltf_GltfAccessorLayout.hxx>
-#include <RWGltf_GltfArrayType.hxx>
 #include <RWGltf_GltfMaterialMap.hxx>
 #include <RWGltf_GltfPrimitiveMode.hxx>
 #include <RWGltf_GltfRootElement.hxx>
 #include <RWGltf_GltfSceneNodeMap.hxx>
 #include <RWMesh.hxx>
+#include <RWMesh_EdgeIterator.hxx>
+#include <RWMesh_ShapeIterator.hxx>
 #include <RWMesh_FaceIterator.hxx>
+#include <RWMesh_VertexIterator.hxx>
 #include <Standard_Version.hxx>
 #include <TDataStd_Name.hxx>
 #include <TDF_Tool.hxx>
@@ -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 <typename VecType>
+static void writeTriangle(std::ostream& theStream, const VecType& theTri)
 {
-  theStream.write((const char*)theTri.GetData(), sizeof(theTri));
+  theStream.write(reinterpret_cast<const char*>(theTri.GetData()), sizeof(theTri));
 }
 
-//! Write triangle indices.
-static void writeTriangle16(std::ostream& theStream, const NCollection_Vec3<uint16_t>& theTri)
+//! General function to write vertex index.
+template <typename T>
+static void writeVertex(std::ostream& theStream, const T& theVertex)
 {
-  theStream.write((const char*)theTri.GetData(), sizeof(theTri));
+  theStream.write(reinterpret_cast<const char*>(&theVertex), sizeof(T));
 }
 
 #ifdef HAVE_DRACO
@@ -276,9 +278,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 +309,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<RWGltf_CafWriter::Mesh>& theMesh) const
 {
@@ -307,15 +329,15 @@ 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 +355,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<RWGltf_CafWriter::Mesh>& theMesh) const
 {
@@ -393,16 +415,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 +464,102 @@ 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<RWGltf_CafWriter::Mesh>& 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>((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<RWGltf_CafWriter::Mesh>& theMesh)
 {
@@ -477,35 +588,19 @@ 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<const RWMesh_FaceIterator*>(&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>((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<const RWMesh_EdgeIterator*>(&theFaceIter))
+  {
+    saveEdgeIndices(theGltfFace, theBinFile, *anEdgeIter);
+  }
+  else if (const RWMesh_VertexIterator* aVertexIter =
+             dynamic_cast<const RWMesh_VertexIterator*>(&theFaceIter))
+  {
+    saveVertexIndices(theGltfFace, theBinFile, *aVertexIter);
   }
 }
 
@@ -556,6 +651,150 @@ 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<XCAFPrs_Style, Handle(RWGltf_GltfFace)>& 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<uint16_t>::max()
+               && (aGltfFace->NbIndexedNodes + theShapeIter.NbNodes())
+                    >= std::numeric_limits<uint16_t>::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<RWGltf_CafWriter::Mesh>& 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<const RWMesh_FaceIterator*>(&theShapeIter))
+        {
+          saveNormals(theGltfFace, theBinFile, *aFaceIter, theAccessorNb, theMesh);
+        }
+        break;
+      }
+      case RWGltf_GltfArrayType_TCoord0: {
+        if (const RWMesh_FaceIterator* aFaceIter =
+              dynamic_cast<const RWMesh_FaceIterator*>(&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 +860,7 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument,
                                               RWGltf_GltfArrayType_TCoord0,
                                               RWGltf_GltfArrayType_Indices};
 
-  // dispatch faces
+  // dispatch shapes
   NCollection_DataMap<XCAFPrs_Style, Handle(RWGltf_GltfFace)> aMergedFaces;
   for (XCAFPrs_DocumentExplorer aDocExplorer(theDocument,
                                              theRootLabels,
@@ -638,79 +877,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<uint16_t>::max()
-                 && (aGltfFace->NbIndexedNodes + aFaceIter.NbNodes())
-                      >= std::numeric_limits<uint16_t>::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,6 +926,8 @@ bool RWGltf_CafWriter::writeBinData(const Handle(TDocStd_Document)& theDocument,
 #ifdef HAVE_DRACO
     size_t aMeshIndex = 0;
 #endif
+
+    Standard_Boolean isFacesOnly = Standard_True;
     for (ShapeToGltfFaceMap::Iterator aBinDataIter(myBinDataMap);
          aBinDataIter.More() && aPSentryBin.More();
          aBinDataIter.Next())
@@ -808,47 +990,59 @@ 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 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;
+          case TopAbs_EDGE: {
+            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())
-          {
-            Message::SendFail(TCollection_AsciiString("File '") + myBinFileNameFull
-                              + "' cannot be written");
-            return false;
+          default: {
+            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 +1053,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 +1065,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,15 +1106,23 @@ 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.Id                         = (int)aBuffInd + aBuffViewId - aNbSkippedBuffers;
       aBuffViewDraco.ByteOffset                 = aBinFile->tellp();
       const draco::EncoderBuffer& anEncoderBuff = *anEncoderBuffers.at(aBuffInd);
       aBinFile->write(anEncoderBuff.data(), std::streamsize(anEncoderBuff.size()));
@@ -951,33 +1171,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 +1250,46 @@ 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())
-      {
-        if (!toSkipFaceMesh(aFaceIter))
+      auto checkShapeData = [&](RWMesh_ShapeIterator& anIter) {
+        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
+      // 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 triangulation data");
+                           + "' without geometry data");
     }
   }
 
@@ -1327,7 +1541,7 @@ 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,7 +1697,7 @@ 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);
@@ -1752,7 +1966,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 +1999,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 +2062,22 @@ void RWGltf_CafWriter::writePrimArray(const RWGltf_GltfFace&         theGltfFace
       myWriter->Key("material");
       myWriter->Int(aMatId.IntegerValue());
     }
+
     myWriter->Key("mode");
-    myWriter->Int(RWGltf_GltfPrimitiveMode_Triangles);
+    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)
+    if (myDracoParameters.DracoCompression && hasTriangulation(theGltfFace))
     {
       myWriter->Key("extensions");
       myWriter->StartObject();
@@ -1895,6 +2121,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<Handle(RWGltf_GltfFaceList)>& theWrittenShapes,
+                                   NCollection_IndexedDataMap<int, int>&         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  :
@@ -1909,8 +2180,8 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa
   myWriter->StartArray();
 
   int                                          aDracoBufInd = 0;
-  NCollection_IndexedDataMap<int, int>         aDracoBufIndMap;
-  NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
+  NCollection_IndexedDataMap<int, int>         aDracoBufMap;
+  NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenShapes;
   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More();
        aSceneNodeIter.Next())
   {
@@ -1918,9 +2189,9 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa
     const TCollection_AsciiString aNodeName =
       formatName(myMeshNameFormat, aDocNode.Label, aDocNode.RefLabel);
 
-    bool             toStartPrims   = true;
-    Standard_Integer aNbFacesInNode = 0;
-    aWrittenFaces.Clear(false);
+    bool             toStartPrims = true;
+    Standard_Integer aNbShapes    = 0;
+    aWrittenShapes.Clear(false);
     if (myToMergeFaces)
     {
       TopoDS_Shape aShape;
@@ -1933,7 +2204,7 @@ void RWGltf_CafWriter::writeMeshes(const RWGltf_GltfSceneNodeMap& theSceneNodeMa
       aShape.Location(TopLoc_Location());
       RWGltf_StyledShape aStyledShape(aShape, aDocNode.Style);
       myBinDataMap.FindFromKey(aStyledShape, aGltfFaceList);
-      if (!aWrittenFaces.Add(aGltfFaceList))
+      if (!aWrittenShapes.Add(aGltfFaceList))
       {
         continue;
       }
@@ -1944,58 +2215,53 @@ 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 +2690,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)
index dde01930c5ebb6c90fe2394408943883bd9ec383..49abf3b18347f3b1c02cba72cd7e99f36f4a86e6 100644 (file)
 #ifndef _RWGltf_CafWriter_HeaderFiler
 #define _RWGltf_CafWriter_HeaderFiler
 
+#include <Message_ProgressScope.hxx>
+#include <NCollection_DataMap.hxx>
 #include <TColStd_IndexedDataMapOfStringString.hxx>
 #include <TColStd_MapOfAsciiString.hxx>
 #include <TDF_LabelSequence.hxx>
 #include <TopTools_ShapeMapHasher.hxx>
 #include <RWGltf_DracoParameters.hxx>
+#include <RWGltf_GltfArrayType.hxx>
 #include <RWGltf_GltfBufferView.hxx>
 #include <RWGltf_GltfFace.hxx>
 #include <RWGltf_WriterTrsfFormat.hxx>
 #include <RWMesh_CoordinateSystemConverter.hxx>
 #include <RWMesh_NameFormat.hxx>
+#include <XCAFPrs_DocumentNode.hxx>
 #include <XCAFPrs_Style.hxx>
 #include <Poly_Triangle.hxx>
 
 #include <memory>
 
 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,12 @@ 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,13 +227,13 @@ 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,
+    const RWMesh_ShapeIterator&                    theShapeIter,
     Standard_Integer&                              theAccessorNb,
     const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
 
@@ -235,7 +246,7 @@ protected:
   Standard_EXPORT virtual void saveNormals(
     RWGltf_GltfFace&                               theGltfFace,
     std::ostream&                                  theBinFile,
-    RWMesh_FaceIterator&                           theFaceIter,
+    const RWMesh_FaceIterator&                     theFaceIter,
     Standard_Integer&                              theAccessorNb,
     const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
 
@@ -255,15 +266,42 @@ 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,
+  Standard_EXPORT virtual void saveIndices(RWGltf_GltfFace&            theGltfFace,
+                                           std::ostream&               theBinFile,
+                                           const RWMesh_ShapeIterator& theShapeIter,
+                                           Standard_Integer&           theAccessorNb,
                                            const std::shared_ptr<RWGltf_CafWriter::Mesh>& 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<RWGltf_CafWriter::Mesh>& 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
   //! @param[in] theGltfFace  face definition to write
@@ -369,6 +407,51 @@ protected:
   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<XCAFPrs_Style, Handle(RWGltf_GltfFace)>& 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<RWGltf_CafWriter::Mesh>& 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<Handle(RWGltf_GltfFaceList)>& theWrittenShapes,
+    NCollection_IndexedDataMap<int, int>&         theDracoBufIndMap);
+
 protected:
   //! Shape + Style pair.
   struct RWGltf_StyledShape
index 63bb91ee07633fb8fca086087805e566cef89475..cbe8a51c983840d3c46cb0462293581aa5275c14 100644 (file)
@@ -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 (file)
index 0000000..bdcbe8c
--- /dev/null
@@ -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 <RWMesh_EdgeIterator.hxx>
+
+#include <BRep_Tool.hxx>
+#include <TopExp.hxx>
+#include <TopoDS.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs.hxx>
+
+// =======================================================================
+// 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 (file)
index 0000000..b71c012
--- /dev/null
@@ -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 <NCollection_DataMap.hxx>
+#include <Poly_Polygon3D.hxx>
+#include <RWMesh_ShapeIterator.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+#include <TopoDS_Edge.hxx>
+#include <XCAFPrs_Style.hxx>
+#include <gp_Trsf.hxx>
+
+#include <algorithm>
+
+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
index 1f1a5ab0108aa648c5cbf76df60bfd685975fa9b..2cdc3c923d3a222e54e75ce213aeb0b2d783c86a 100644 (file)
@@ -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();
 }
index 4fdb8e89f8e62fa4dffae4845555fc367e2ef1c5..ebd98f728ecff029833ba79e78e41e6f8fe297ae 100644 (file)
 #ifndef _RWMesh_FaceIterator_HeaderFile
 #define _RWMesh_FaceIterator_HeaderFile
 
+#include <RWMesh_ShapeIterator.hxx>
+
 #include <BRepLProp_SLProps.hxx>
-#include <gp_Trsf.hxx>
-#include <NCollection_DataMap.hxx>
 #include <Poly_Triangulation.hxx>
-#include <TopExp_Explorer.hxx>
 #include <TopoDS_Face.hxx>
-#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
-#include <XCAFPrs_Style.hxx>
 
 #include <algorithm>
 
 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();
+    myHasNormals = false;
+    resetShape();
   }
 
   //! Initialize face properties.
   void initFace();
 
 private:
-  NCollection_DataMap<TopoDS_Shape, XCAFPrs_Style, TopTools_ShapeMapHasher>
-    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 (file)
index 0000000..63c3fcc
--- /dev/null
@@ -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 <RWMesh_ShapeIterator.hxx>
+
+#include <TopExp.hxx>
+#include <TopoDS.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs.hxx>
+
+// =======================================================================
+// 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 (file)
index 0000000..6efdfa9
--- /dev/null
@@ -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 <BRepLProp_SLProps.hxx>
+#include <NCollection_DataMap.hxx>
+#include <Poly_Triangulation.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+#include <TopoDS_Edge.hxx>
+#include <XCAFPrs_Style.hxx>
+#include <gp_Trsf.hxx>
+
+#include <algorithm>
+
+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<TopoDS_Shape, XCAFPrs_Style, TopTools_ShapeMapHasher>
+                   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 (file)
index 0000000..ce9d3de
--- /dev/null
@@ -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 <RWMesh_VertexIterator.hxx>
+
+#include <BRep_Tool.hxx>
+#include <TopExp.hxx>
+#include <TopoDS.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs.hxx>
+
+// =======================================================================
+// 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 (file)
index 0000000..c57945a
--- /dev/null
@@ -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 <NCollection_DataMap.hxx>
+#include <Poly_Polygon3D.hxx>
+#include <RWMesh_ShapeIterator.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+#include <TopoDS_Vertex.hxx>
+#include <XCAFPrs_Style.hxx>
+#include <gp_Trsf.hxx>
+
+#include <algorithm>
+
+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 (file)
index 0000000..d64eba2
--- /dev/null
@@ -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