]> OCCT Git - occt.git/commitdiff
0032530: Data Exchange, RWGltf_CafWriter - add option merging Faces within the Part
authorkgv <kgv@opencascade.com>
Wed, 11 Aug 2021 15:48:23 +0000 (18:48 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 12 Aug 2021 16:12:23 +0000 (19:12 +0300)
Added RWGltf_CafWriter::ToMergeFaces() property disabled by default.
RWMesh_MaterialMap - fixed creation of texture folder within working dir ".".
XCAFDoc_VisMaterial::FillMaterialAspect() - added clamping of too small shininess values.

Added options -mergefaces and -splitindices16 to WriteGltf for new feature.
Added -systemCoordSys option to WriteGltf for consistency with WriteObj.

17 files changed:
src/RWGltf/RWGltf_CafWriter.cxx
src/RWGltf/RWGltf_CafWriter.hxx
src/RWGltf/RWGltf_GltfFace.hxx
src/RWMesh/RWMesh_FaceIterator.cxx
src/RWMesh/RWMesh_FaceIterator.hxx
src/RWMesh/RWMesh_MaterialMap.cxx
src/TopExp/FILES
src/TopExp/TopExp_Explorer.cxx
src/TopExp/TopExp_Explorer.hxx
src/TopExp/TopExp_Explorer.lxx [deleted file]
src/TopoDS/FILES
src/TopoDS/TopoDS_Iterator.cxx
src/TopoDS/TopoDS_Iterator.hxx
src/TopoDS/TopoDS_Iterator.lxx [deleted file]
src/XCAFDoc/XCAFDoc_VisMaterial.cxx
src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx
tests/de_mesh/gltf_write/as1 [new file with mode: 0644]

index f06f8034af64e5a23d44671478208b20030659f7..b8a50cd59d3646b58c0051ab640b72cbb9bb5b19 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <RWGltf_CafWriter.hxx>
 
+#include <BRep_Builder.hxx>
 #include <gp_Quaternion.hxx>
 #include <Message.hxx>
 #include <Message_Messenger.hxx>
@@ -23,6 +24,7 @@
 #include <OSD_Path.hxx>
 #include <Poly_Triangulation.hxx>
 #include <RWGltf_GltfAccessorLayout.hxx>
+#include <RWGltf_GltfArrayType.hxx>
 #include <RWGltf_GltfMaterialMap.hxx>
 #include <RWGltf_GltfPrimitiveMode.hxx>
 #include <RWGltf_GltfRootElement.hxx>
@@ -32,6 +34,7 @@
 #include <TDataStd_Name.hxx>
 #include <TDF_Tool.hxx>
 #include <TDocStd_Document.hxx>
+#include <TopoDS_Compound.hxx>
 #include <XCAFDoc_DocumentTool.hxx>
 #include <XCAFDoc_ShapeTool.hxx>
 #include <XCAFPrs_DocumentExplorer.hxx>
@@ -95,6 +98,8 @@ RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
   myIsBinary      (theIsBinary),
   myIsForcedUVExport (false),
   myToEmbedTexturesInGlb (true),
+  myToMergeFaces (false),
+  myToSplitIndices16 (false),
   myBinDataLen64  (0)
 {
   myCSTrsf.SetOutputLengthUnit (1.0); // meters
@@ -146,11 +151,19 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
                                   const RWMesh_FaceIterator& theFaceIter,
                                   Standard_Integer& theAccessorNb) const
 {
-  theGltfFace.NodePos.Id            = theAccessorNb++;
-  theGltfFace.NodePos.Count         = theFaceIter.NbNodes();
-  theGltfFace.NodePos.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewPos.ByteOffset;
-  theGltfFace.NodePos.Type          = RWGltf_GltfAccessorLayout_Vec3;
-  theGltfFace.NodePos.ComponentType = RWGltf_GltfAccessorCompType_Float32;
+  if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
+  {
+    theGltfFace.NodePos.Id            = theAccessorNb++;
+    theGltfFace.NodePos.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewPos.ByteOffset;
+    theGltfFace.NodePos.Type          = RWGltf_GltfAccessorLayout_Vec3;
+    theGltfFace.NodePos.ComponentType = RWGltf_GltfAccessorCompType_Float32;
+  }
+  else
+  {
+    const int64_t aPos = theGltfFace.NodePos.ByteOffset + myBuffViewPos.ByteOffset + theGltfFace.NodePos.Count * sizeof(Graphic3d_Vec3);
+    Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
+  }
+  theGltfFace.NodePos.Count += theFaceIter.NbNodes();
 
   const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
   for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
@@ -176,11 +189,19 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
     return;
   }
 
-  theGltfFace.NodeNorm.Id            = theAccessorNb++;
-  theGltfFace.NodeNorm.Count         = theFaceIter.NbNodes();
-  theGltfFace.NodeNorm.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewNorm.ByteOffset;
-  theGltfFace.NodeNorm.Type          = RWGltf_GltfAccessorLayout_Vec3;
-  theGltfFace.NodeNorm.ComponentType = RWGltf_GltfAccessorCompType_Float32;
+  if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
+  {
+    theGltfFace.NodeNorm.Id            = theAccessorNb++;
+    theGltfFace.NodeNorm.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewNorm.ByteOffset;
+    theGltfFace.NodeNorm.Type          = RWGltf_GltfAccessorLayout_Vec3;
+    theGltfFace.NodeNorm.ComponentType = RWGltf_GltfAccessorCompType_Float32;
+  }
+  else
+  {
+    const int64_t aPos = theGltfFace.NodeNorm.ByteOffset + myBuffViewNorm.ByteOffset + theGltfFace.NodeNorm.Count * sizeof(Graphic3d_Vec3);
+    Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
+  }
+  theGltfFace.NodeNorm.Count += theFaceIter.NbNodes();
 
   const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
   for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
@@ -222,11 +243,20 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
     }
   }
 
-  theGltfFace.NodeUV.Id            = theAccessorNb++;
-  theGltfFace.NodeUV.Count         = theFaceIter.NbNodes();
-  theGltfFace.NodeUV.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
-  theGltfFace.NodeUV.Type          = RWGltf_GltfAccessorLayout_Vec2;
-  theGltfFace.NodeUV.ComponentType = RWGltf_GltfAccessorCompType_Float32;
+  if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
+  {
+    theGltfFace.NodeUV.Id            = theAccessorNb++;
+    theGltfFace.NodeUV.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
+    theGltfFace.NodeUV.Type          = RWGltf_GltfAccessorLayout_Vec2;
+    theGltfFace.NodeUV.ComponentType = RWGltf_GltfAccessorCompType_Float32;
+  }
+  else
+  {
+    const int64_t aPos = theGltfFace.NodeUV.ByteOffset + myBuffViewTextCoord.ByteOffset + theGltfFace.NodeUV.Count * sizeof(Graphic3d_Vec2);
+    Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
+  }
+  theGltfFace.NodeUV.Count += theFaceIter.NbNodes();
+
   const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
   for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
   {
@@ -245,22 +275,36 @@ void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
                                     const RWMesh_FaceIterator& theFaceIter,
                                     Standard_Integer& theAccessorNb)
 {
-  theGltfFace.Indices.Id            = theAccessorNb++;
-  theGltfFace.Indices.Count         = theFaceIter.NbTriangles() * 3;
-  theGltfFace.Indices.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewInd.ByteOffset;
-  theGltfFace.Indices.Type          = RWGltf_GltfAccessorLayout_Scalar;
-  theGltfFace.Indices.ComponentType = theGltfFace.NodePos.Count > std::numeric_limits<uint16_t>::max()
-                                    ? RWGltf_GltfAccessorCompType_UInt32
-                                    : RWGltf_GltfAccessorCompType_UInt16;
+  if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
+  {
+    theGltfFace.Indices.Id            = theAccessorNb++;
+    theGltfFace.Indices.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewInd.ByteOffset;
+    theGltfFace.Indices.Type          = RWGltf_GltfAccessorLayout_Scalar;
+    theGltfFace.Indices.ComponentType = theGltfFace.NodePos.Count > std::numeric_limits<uint16_t>::max()
+                                      ? RWGltf_GltfAccessorCompType_UInt32
+                                      : RWGltf_GltfAccessorCompType_UInt16;
+  }
+  else
+  {
+    const int64_t aRefPos = (int64_t )theBinFile.tellp();
+    const int64_t aPos = theGltfFace.Indices.ByteOffset
+                       + myBuffViewInd.ByteOffset
+                       + theGltfFace.Indices.Count * (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt32 ? sizeof(uint32_t) : sizeof(uint16_t));
+    Standard_ASSERT_RAISE (aPos == aRefPos, "wrong offset");
+  }
+
+  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)
   {
     Poly_Triangle aTri = theFaceIter.TriangleOriented (anElemIter);
-    aTri(1) -= anElemLower;
-    aTri(2) -= anElemLower;
-    aTri(3) -= anElemLower;
+    aTri(1) += aNodeFirst;
+    aTri(2) += aNodeFirst;
+    aTri(3) += aNodeFirst;
     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)));
@@ -270,16 +314,6 @@ void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
       writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
     }
   }
-  if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
-  {
-    // alignment by 4 bytes
-    int64_t aContentLen64 = (int64_t)theBinFile.tellp();
-    while (aContentLen64 % 4 != 0)
-    {
-      theBinFile.write (" ", 1);
-      ++aContentLen64;
-    }
-  }
 }
 
 // =======================================================================
@@ -369,11 +403,16 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
   }
 
   Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4);
+  const RWGltf_GltfArrayType anArrTypes[4] =
+  {
+    RWGltf_GltfArrayType_Position,
+    RWGltf_GltfArrayType_Normal,
+    RWGltf_GltfArrayType_TCoord0,
+    RWGltf_GltfArrayType_Indices
+  };
 
-  Standard_Integer aNbAccessors = 0;
-
-  // write positions
-  myBuffViewPos.ByteOffset = aBinFile->tellp();
+  // dispatch faces
+  NCollection_DataMap<XCAFPrs_Style, Handle(RWGltf_GltfFace), XCAFPrs_Style> aMergedFaces;
   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
        aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
   {
@@ -385,149 +424,165 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
     }
 
     // transformation will be stored at scene nodes
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
+    aMergedFaces.Clear (false);
+
+    RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
+    if (myToMergeFaces)
     {
-      if (myBinDataMap.IsBound (aFaceIter.Face())
-       || toSkipFaceMesh (aFaceIter))
+      if (myBinDataMap.Contains (aFaceIter.ExploredShape()))
       {
         continue;
       }
 
-      RWGltf_GltfFace aGltfFace;
-      saveNodes (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
-
-      if (!aBinFile->good())
+      Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList();
+      myBinDataMap.Add (aFaceIter.ExploredShape(), aGltfFaceList);
+      for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
       {
-        Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
-        return false;
-      }
-
-      myBinDataMap.Bind (aFaceIter.Face(), aGltfFace);
-    }
-  }
-  myBuffViewPos.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewPos.ByteOffset;
-  if (!aPSentryBin.More())
-  {
-    return false;
-  }
-  aPSentryBin.Next();
+        if (toSkipFaceMesh (aFaceIter))
+        {
+          continue;
+        }
 
-  // write normals
-  myBuffViewNorm.ByteOffset = aBinFile->tellp();
-  for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
-       aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
-  {
-    const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
-    if (theLabelFilter != NULL
-    && !theLabelFilter->Contains (aDocNode.Id))
-    {
-      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();
+        }
+      }
     }
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
+    else
     {
-      if (toSkipFaceMesh (aFaceIter))
+      for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
       {
-        continue;
-      }
-
-      RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
-      if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
-      {
-        continue;
-      }
-
-      saveNormals (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+        if (toSkipFaceMesh (aFaceIter)
+         || myBinDataMap.Contains (aFaceIter.Face()))
+        {
+          continue;
+        }
 
-      if (!aBinFile->good())
-      {
-        Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
-        return false;
+        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 (aFaceIter.Face(), aGltfFaceList);
       }
     }
   }
-  myBuffViewNorm.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewNorm.ByteOffset;
-  if (!aPSentryBin.More())
-  {
-    return false;
-  }
-  aPSentryBin.Next();
 
-  // write texture coordinates
-  myBuffViewTextCoord.ByteOffset = aBinFile->tellp();
-  for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
-       aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
+  Standard_Integer aNbAccessors = 0;
+  NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
+  for (Standard_Integer aTypeIter = 0; aTypeIter < 4; ++aTypeIter)
   {
-    const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
-    if (theLabelFilter != NULL
-    && !theLabelFilter->Contains (aDocNode.Id))
+    const RWGltf_GltfArrayType anArrType = (RWGltf_GltfArrayType )anArrTypes[aTypeIter];
+    RWGltf_GltfBufferView* aBuffView = NULL;
+    switch (anArrType)
     {
-      continue;
+      case RWGltf_GltfArrayType_Position: aBuffView = &myBuffViewPos;  break;
+      case RWGltf_GltfArrayType_Normal:   aBuffView = &myBuffViewNorm; break;
+      case RWGltf_GltfArrayType_TCoord0:  aBuffView = &myBuffViewTextCoord; break;
+      case RWGltf_GltfArrayType_Indices:  aBuffView = &myBuffViewInd; break;
+      default: break;
     }
-
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
+    aBuffView->ByteOffset = aBinFile->tellp();
+    aWrittenFaces.Clear (false);
+    for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More() && aPSentryBin.More(); aBinDataIter.Next())
     {
-      if (toSkipFaceMesh (aFaceIter))
+      const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
+      if (!aWrittenFaces.Add (aGltfFaceList)) // skip repeating faces
       {
         continue;
       }
 
-      RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
-      if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
+      for (RWGltf_GltfFaceList::Iterator aGltfFaceIter (*aGltfFaceList); aGltfFaceIter.More() && aPSentryBin.More(); aGltfFaceIter.Next())
       {
-        continue;
-      }
+        const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceIter.Value();
+        for (RWMesh_FaceIterator aFaceIter (aGltfFace->Shape, aGltfFace->Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
+        {
+          switch (anArrType)
+          {
+            case RWGltf_GltfArrayType_Position:
+            {
+              aGltfFace->NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step
+              saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              break;
+            }
+            case RWGltf_GltfArrayType_Normal:
+            {
+              saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              break;
+            }
+            case RWGltf_GltfArrayType_TCoord0:
+            {
+              saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              break;
+            }
+            case RWGltf_GltfArrayType_Indices:
+            {
+              saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              break;
+            }
+            default:
+            {
+              break;
+            }
+          }
 
-      saveTextCoords (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+          if (!aBinFile->good())
+          {
+            Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be written");
+            return false;
+          }
+        }
 
-      if (!aBinFile->good())
-      {
-        Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
-        return false;
+        // add alignment by 4 bytes (might happen on RWGltf_GltfAccessorCompType_UInt16 indices)
+        int64_t aContentLen64 = (int64_t)aBinFile->tellp();
+        while (aContentLen64 % 4 != 0)
+        {
+          aBinFile->write (" ", 1);
+          ++aContentLen64;
+        }
       }
     }
-  }
-  myBuffViewTextCoord.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewTextCoord.ByteOffset;
-  if (!aPSentryBin.More())
-  {
-    return false;
-  }
-  aPSentryBin.Next();
 
-  // write indices
-  myBuffViewInd.ByteOffset = aBinFile->tellp();
-  for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
-       aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
-  {
-    const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
-    if (theLabelFilter != NULL
-    && !theLabelFilter->Contains (aDocNode.Id))
+    aBuffView->ByteLength = (int64_t )aBinFile->tellp() - aBuffView->ByteOffset;
+    if (!aPSentryBin.More())
     {
-      continue;
+      return false;
     }
 
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
-    {
-      if (toSkipFaceMesh (aFaceIter))
-      {
-        continue;
-      }
-
-      RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
-      if (aGltfFace.Indices.Id != RWGltf_GltfAccessor::INVALID_ID)
-      {
-        continue;
-      }
-
-      saveIndices (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
-
-      if (!aBinFile->good())
-      {
-        Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
-        return false;
-      }
-    }
+    aPSentryBin.Next();
   }
-  myBuffViewInd.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewInd.ByteOffset;
 
   if (myIsBinary
    && myToEmbedTexturesInGlb)
@@ -579,7 +634,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
   aBinFile->flush();
   if (!aBinFile->good())
   {
-    Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
+    Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be written");
     return false;
   }
   aBinFile.reset();
@@ -788,7 +843,7 @@ bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)&  theDocument,
 // function : writeAccessors
 // purpose  :
 // =======================================================================
-void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
+void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& )
 {
 #ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
@@ -796,74 +851,61 @@ void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNo
   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
   myWriter->StartArray();
 
-  NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aWrittenFaces;
-  for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
-  {
-    const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
-    {
-      if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
-        || toSkipFaceMesh (aFaceIter))
-      {
-        continue;
-      }
-
-      const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
-      writePositions (aGltfFace);
-    }
-  }
-  aWrittenFaces.Clear();
-  for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
+  const RWGltf_GltfArrayType anArrTypes[4] =
   {
-    const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
-    {
-      if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
-        || toSkipFaceMesh (aFaceIter))
-      {
-        continue;
-      }
-
-      const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
-      writeNormals (aGltfFace);
-    }
-  }
-  aWrittenFaces.Clear();
-  for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
+    RWGltf_GltfArrayType_Position,
+    RWGltf_GltfArrayType_Normal,
+    RWGltf_GltfArrayType_TCoord0,
+    RWGltf_GltfArrayType_Indices
+  };
+  NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
+  for (Standard_Integer aTypeIter = 0; aTypeIter < 4; ++aTypeIter)
   {
-    const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
+    const RWGltf_GltfArrayType anArrType = (RWGltf_GltfArrayType )anArrTypes[aTypeIter];
+    aWrittenFaces.Clear (false);
+    for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More(); aBinDataIter.Next())
     {
-      if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
-        || toSkipFaceMesh (aFaceIter))
+      const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
+      if (!aWrittenFaces.Add (aGltfFaceList)) // skip repeating faces
       {
         continue;
       }
 
-      const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
-      writeTextCoords (aGltfFace);
-    }
-  }
-  aWrittenFaces.Clear();
-  for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
-  {
-    const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
-    {
-      if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
-        || toSkipFaceMesh (aFaceIter))
+      for (RWGltf_GltfFaceList::Iterator aFaceIter (*aGltfFaceList); aFaceIter.More(); aFaceIter.Next())
       {
-        continue;
+        const Handle(RWGltf_GltfFace)& aGltfFace = aFaceIter.Value();
+        switch (anArrType)
+        {
+          case RWGltf_GltfArrayType_Position:
+          {
+            writePositions (*aGltfFace);
+            break;
+          }
+          case RWGltf_GltfArrayType_Normal:
+          {
+            writeNormals (*aGltfFace);
+            break;
+          }
+          case RWGltf_GltfArrayType_TCoord0:
+          {
+            writeTextCoords (*aGltfFace);
+            break;
+          }
+          case RWGltf_GltfArrayType_Indices:
+          {
+            writeIndices (*aGltfFace);
+            break;
+          }
+          default:
+          {
+            break;
+          }
+        }
       }
-
-      const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
-      writeIndices (aGltfFace);
     }
   }
 
   myWriter->EndArray();
-#else
-  (void )theSceneNodeMap;
 #endif
 }
 
@@ -1280,6 +1322,67 @@ void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNo
 #endif
 }
 
+// =======================================================================
+// function : writePrimArray
+// purpose  :
+// =======================================================================
+void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace,
+                                       const TCollection_AsciiString& theName,
+                                       bool& theToStartPrims)
+{
+#ifdef HAVE_RAPIDJSON
+  if (theToStartPrims)
+  {
+    theToStartPrims = false;
+    myWriter->StartObject();
+    if (!theName.IsEmpty())
+    {
+      myWriter->Key ("name");
+      myWriter->String (theName.ToCString());
+    }
+    myWriter->Key ("primitives");
+    myWriter->StartArray();
+  }
+
+  const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (theGltfFace.Style);
+  myWriter->StartObject();
+  {
+    myWriter->Key ("attributes");
+    myWriter->StartObject();
+    {
+      if (theGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
+      {
+        myWriter->Key ("NORMAL");
+        myWriter->Int (theGltfFace.NodeNorm.Id);
+      }
+      myWriter->Key ("POSITION");
+      myWriter->Int (theGltfFace.NodePos.Id);
+      if (theGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
+      {
+        myWriter->Key ("TEXCOORD_0");
+        myWriter->Int (theGltfFace.NodeUV.Id);
+      }
+    }
+    myWriter->EndObject();
+
+    myWriter->Key ("indices");
+    myWriter->Int (theGltfFace.Indices.Id);
+    if (!aMatId.IsEmpty())
+    {
+      myWriter->Key ("material");
+      myWriter->Int (aMatId.IntegerValue());
+    }
+    myWriter->Key ("mode");
+    myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
+  }
+  myWriter->EndObject();
+#else
+  (void )theGltfFace;
+  (void )theName;
+  (void )theToStartPrims;
+#endif
+}
+
 // =======================================================================
 // function : writeMeshes
 // purpose  :
@@ -1292,6 +1395,7 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
   myWriter->StartArray();
 
+  NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
   {
     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
@@ -1299,59 +1403,48 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
 
     bool toStartPrims = true;
     Standard_Integer aNbFacesInNode = 0;
-    for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
+    aWrittenFaces.Clear (false);
+    if (myToMergeFaces)
     {
-      if (toSkipFaceMesh (aFaceIter))
+      TopoDS_Shape aShape;
+      if (!XCAFDoc_ShapeTool::GetShape (aDocNode.RefLabel, aShape)
+      ||  aShape.IsNull())
       {
         continue;
       }
 
-      if (toStartPrims)
+      Handle(RWGltf_GltfFaceList) aGltfFaceList;
+      aShape.Location (TopLoc_Location());
+      myBinDataMap.FindFromKey (aShape, aGltfFaceList);
+      if (!aWrittenFaces.Add (aGltfFaceList))
       {
-        toStartPrims = false;
-        myWriter->StartObject();
-        if (!aNodeName.IsEmpty())
-        {
-          myWriter->Key ("name");
-          myWriter->String (aNodeName.ToCString());
-        }
-        myWriter->Key ("primitives");
-        myWriter->StartArray();
+        continue;
       }
 
-      const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
-      const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (aFaceIter.FaceStyle());
-      myWriter->StartObject();
+      for (RWGltf_GltfFaceList::Iterator aFaceGroupIter (*aGltfFaceList); aFaceGroupIter.More(); aFaceGroupIter.Next())
+      {
+        const Handle(RWGltf_GltfFace)& aGltfFace = aFaceGroupIter.Value();
+        writePrimArray (*aGltfFace, aNodeName, toStartPrims);
+      }
+    }
+    else
+    {
+      for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
       {
-        myWriter->Key ("attributes");
-        myWriter->StartObject();
+        if (toSkipFaceMesh (aFaceIter))
         {
-          if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
-          {
-            myWriter->Key ("NORMAL");
-            myWriter->Int (aGltfFace.NodeNorm.Id);
-          }
-          myWriter->Key ("POSITION");
-          myWriter->Int (aGltfFace.NodePos.Id);
-          if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
-          {
-            myWriter->Key ("TEXCOORD_0");
-            myWriter->Int (aGltfFace.NodeUV.Id);
-          }
+          continue;
         }
-        myWriter->EndObject();
 
-        myWriter->Key ("indices");
-        myWriter->Int (aGltfFace.Indices.Id);
-        if (!aMatId.IsEmpty())
+        const Handle(RWGltf_GltfFaceList)& aGltfFaceList = myBinDataMap.FindFromKey (aFaceIter.Face());
+        if (!aWrittenFaces.Add (aGltfFaceList))
         {
-          myWriter->Key ("material");
-          myWriter->Int (aMatId.IntegerValue());
+          continue;
         }
-        myWriter->Key ("mode");
-        myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
+
+        const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceList->First();
+        writePrimArray (*aGltfFace, aNodeName, toStartPrims);
       }
-      myWriter->EndObject();
     }
 
     if (!toStartPrims)
index 5aad88a3265389b9f298ecadff8d3ab5ba3d4e2e..bfc51191778c277e8df0c919ee697e85c2d98c17 100644 (file)
@@ -99,6 +99,21 @@ public:
   //! Set flag to write image textures into GLB file (binary gltf export).
   void SetToEmbedTexturesInGlb (Standard_Boolean theToEmbedTexturesInGlb) { myToEmbedTexturesInGlb = theToEmbedTexturesInGlb; }
 
+  //! Return flag to merge faces within a single part; FALSE by default.
+  bool ToMergeFaces() const { return myToMergeFaces; }
+
+  //! Set flag to merge faces within a single part.
+  //! May reduce JSON size thanks to smaller number of primitive arrays.
+  void SetMergeFaces (bool theToMerge) { myToMergeFaces = theToMerge; }
+
+  //! Return flag to prefer keeping 16-bit indexes while merging face; FALSE by default.
+  bool ToSplitIndices16() const { return myToSplitIndices16; }
+
+  //! Set flag to prefer keeping 16-bit indexes while merging face.
+  //! Has effect only with ToMergeFaces() option turned ON.
+  //! May reduce binary data size thanks to smaller triangle indexes.
+  void SetSplitIndices16 (bool theToSplit) { myToSplitIndices16 = theToSplit; }
+
   //! Write glTF file and associated binary file.
   //! Triangulation data should be precomputed within shapes!
   //! @param theDocument    [in] input document
@@ -262,6 +277,14 @@ protected:
   //! @param theMaterialMap  [in] map of materials
   Standard_EXPORT virtual void writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
 
+  //! Write a primitive array to RWGltf_GltfRootElement_Meshes section.
+  //! @param[in]     theGltfFace     face to write
+  //! @param[in]     theName         primitive array name
+  //! @param[in,out] theToStartPrims flag indicating that primitive array has been started
+  Standard_EXPORT virtual void writePrimArray (const RWGltf_GltfFace& theGltfFace,
+                                               const TCollection_AsciiString& theName,
+                                               bool& theToStartPrims);
+
   //! Write RWGltf_GltfRootElement_Nodes section.
   //! @param theDocument     [in] input document
   //! @param theRootLabels   [in] list of root shapes to export
@@ -293,6 +316,10 @@ protected:
   //! @param theMaterialMap [out] map of materials, filled with textures
   Standard_EXPORT virtual void writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
 
+protected:
+
+  typedef NCollection_IndexedDataMap<TopoDS_Shape, Handle(RWGltf_GltfFaceList), TopTools_ShapeMapHasher> ShapeToGltfFaceMap;
+
 protected:
 
   TCollection_AsciiString                       myFile;              //!< output glTF file
@@ -304,6 +331,8 @@ protected:
   Standard_Boolean                              myIsBinary;          //!< flag to write into binary glTF format (.glb)
   Standard_Boolean                              myIsForcedUVExport;  //!< export UV coordinates even if there are no mapped texture
   Standard_Boolean                              myToEmbedTexturesInGlb; //!< flag to write image textures into GLB file
+  Standard_Boolean                              myToMergeFaces;      //!< flag to merge faces within a single part
+  Standard_Boolean                              myToSplitIndices16;  //!< flag to prefer keeping 16-bit indexes while merging face
   RWMesh_CoordinateSystemConverter              myCSTrsf;            //!< transformation from OCCT to glTF coordinate system
   XCAFPrs_Style                                 myDefaultStyle;      //!< default material definition to be used for nodes with only color defined
 
@@ -314,8 +343,7 @@ protected:
   RWGltf_GltfBufferView                         myBuffViewNorm;      //!< current buffer view with nodes normals
   RWGltf_GltfBufferView                         myBuffViewTextCoord; //!< current buffer view with nodes UV coordinates
   RWGltf_GltfBufferView                         myBuffViewInd;       //!< current buffer view with triangulation indexes
-  NCollection_DataMap<TopoDS_Shape, RWGltf_GltfFace,
-                      TopTools_ShapeMapHasher>  myBinDataMap;        //!< map for TopoDS_Face to glTF face (merging duplicates)
+  ShapeToGltfFaceMap                            myBinDataMap;        //!< map for TopoDS_Face to glTF face (merging duplicates)
   int64_t                                       myBinDataLen64;      //!< length of binary file
 
 };
index c90a9c1478ca4aed9ba295e637451d3e63afcf46..0c4298c2ff4552325208b4782621a9f277e1010b 100644 (file)
 #ifndef _RWGltf_GltfFace_HeaderFile
 #define _RWGltf_GltfFace_HeaderFile
 
+#include <NCollection_List.hxx>
+#include <NCollection_Shared.hxx>
 #include <RWGltf_GltfAccessor.hxx>
+#include <TopoDS_Shape.hxx>
+#include <XCAFPrs_Style.hxx>
 
 //! Low-level glTF data structure holding single Face (one primitive array) definition.
-struct RWGltf_GltfFace
+class RWGltf_GltfFace : public Standard_Transient
 {
+public:
   RWGltf_GltfAccessor NodePos;  //!< accessor for nodal positions
   RWGltf_GltfAccessor NodeNorm; //!< accessor for nodal normals
   RWGltf_GltfAccessor NodeUV;   //!< accessor for nodal UV texture coordinates
   RWGltf_GltfAccessor Indices;  //!< accessor for indexes
+  TopoDS_Shape        Shape;    //!< original Face or face list
+  XCAFPrs_Style       Style;    //!< face style
+  Standard_Integer    NbIndexedNodes; //!< transient variable for merging several faces into one while writing Indices
+
+  RWGltf_GltfFace() : NbIndexedNodes (0) {}
 };
 
+typedef NCollection_Shared<NCollection_List<Handle(RWGltf_GltfFace)>> RWGltf_GltfFaceList;
+
 #endif // _RWGltf_GltfFace_HeaderFile
index cdcd5241d776a6b47d7e7aa1266042e87fd2c828..a19ee169203ae36fefb4ba1431711c90073108bc 100644 (file)
@@ -53,6 +53,28 @@ RWMesh_FaceIterator::RWMesh_FaceIterator (const TDF_Label&       theLabel,
   Next();
 }
 
+// =======================================================================
+// function : RWMesh_FaceIterator
+// purpose  :
+// =======================================================================
+RWMesh_FaceIterator::RWMesh_FaceIterator (const TopoDS_Shape&  theShape,
+                                          const XCAFPrs_Style& theStyle)
+: myDefStyle (theStyle),
+  myToMapColors (true),
+  mySLTool  (1, 1e-12),
+  myHasNormals (false),
+  myIsMirrored (false),
+  myHasFaceColor (false)
+{
+  if (theShape.IsNull())
+  {
+    return;
+  }
+
+  myFaceIter.Init (theShape, TopAbs_FACE);
+  Next();
+}
+
 // =======================================================================
 // function : dispatchStyles
 // purpose  :
index f70b3786107bf4635b2f4c980f9f954b30413015..3a74040af65265e7d2631429db9ac51f2cebee60 100644 (file)
@@ -39,6 +39,13 @@ public:
                                        const Standard_Boolean theToMapColors = false,
                                        const XCAFPrs_Style&   theStyle = XCAFPrs_Style());
 
+  //! Auxiliary constructor.
+  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(); }
 
index 1a8f2e93f534b1804d8a55a6a4e563bacbf11683..457d9622c79eba532457ca56aee846dfcf5b6c12 100644 (file)
@@ -38,6 +38,10 @@ RWMesh_MaterialMap::RWMesh_MaterialMap (const TCollection_AsciiString& theFile)
   TCollection_AsciiString aFileName, aFileExt;
   OSD_Path::FolderAndFileFromPath (theFile, myFolder, aFileName);
   OSD_Path::FileNameAndExtension (aFileName, myShortFileNameBase, aFileExt);
+  if (myFolder.IsEmpty())
+  {
+    myFolder = ".";
+  }
 }
 
 // =======================================================================
@@ -216,6 +220,7 @@ bool RWMesh_MaterialMap::CreateTextureFolder()
   OSD_Directory aResDir (aResFolderPath);
   if (!aResDir.Exists())
   {
+    Message::SendFail() << "Failed to create textures folder '" << myFolder << "'";
     return false;
   }
   const OSD_Protection aParentProt = aResDir.Protection();
@@ -233,6 +238,7 @@ bool RWMesh_MaterialMap::CreateTextureFolder()
   if (aTexDir.Failed())
   {
     // fallback to the same folder as output model file
+    Message::SendFail() << "Failed to create textures folder '" << myTexFolder << "'";
     myTexFolder = myFolder;
     myTexFolderShort.Clear();
     return true;
index 7d56a6d55f675720bc460f01ffef52444b1a2fd7..cace9690df91c7488d1ef7e5cde1ab58c5c73df2 100644 (file)
@@ -2,5 +2,4 @@ TopExp.cxx
 TopExp.hxx
 TopExp_Explorer.cxx
 TopExp_Explorer.hxx
-TopExp_Explorer.lxx
 TopExp_Stack.hxx
index f698f928cca2376f1d58da98625e1e7751e6870e..569f5f21b1ccd0915b94d6cb7ffcc6da4953ac6b 100644 (file)
 #define No_Standard_NoMoreObject
 #define No_Standard_NoSuchObject
 
+#include <TopExp_Explorer.hxx>
 
-#include <Standard.hxx>
 #include <Standard_NoMoreObject.hxx>
 #include <Standard_NoSuchObject.hxx>
 #include <TopAbs.hxx>
-#include <TopExp_Explorer.hxx>
-#include <TopoDS_Iterator.hxx>
-#include <TopoDS_Shape.hxx>
 
 // macro to compare two types of shapes
 // always True if the first one is SHAPE
index 33daa37ff647e0cf19d0db6b3178b17c4322787e..a49d3f15e7d8eb6953104a68d90cf9e5fc596587 100644 (file)
 #ifndef _TopExp_Explorer_HeaderFile
 #define _TopExp_Explorer_HeaderFile
 
-#include <Standard.hxx>
-#include <Standard_DefineAlloc.hxx>
-#include <Standard_Handle.hxx>
-
 #include <TopExp_Stack.hxx>
-#include <Standard_Integer.hxx>
 #include <TopoDS_Shape.hxx>
-#include <Standard_Boolean.hxx>
-#include <TopAbs_ShapeEnum.hxx>
-class Standard_NoMoreObject;
-class Standard_NoSuchObject;
-class TopoDS_Shape;
-
 
 //! An Explorer is a Tool to visit  a Topological Data
 //! Structure form the TopoDS package.
@@ -117,10 +106,9 @@ public:
   //! ToFind it has no effect on the search.
   Standard_EXPORT void Init (const TopoDS_Shape& S, const TopAbs_ShapeEnum ToFind, const TopAbs_ShapeEnum ToAvoid = TopAbs_SHAPE);
   
-  //! Returns  True if  there are   more  shapes in  the
-  //! exploration.
-    Standard_Boolean More() const;
-  
+  //! Returns True if there are more shapes in the exploration.
+  Standard_Boolean More() const { return hasMore; }
+
   //! Moves to the next Shape in the exploration.
   //! Exceptions
   //! Standard_NoMoreObject if there are no more shapes to explore.
@@ -135,15 +123,17 @@ public:
   //! Exceptions
   //! Standard_NoSuchObject if this explorer has no more shapes to explore.
   Standard_EXPORT const TopoDS_Shape& Current() const;
-  
-  //! Reinitialize  the    exploration with the original
-  //! arguments.
+
+  //! Reinitialize the exploration with the original arguments.
   Standard_EXPORT void ReInit();
-  
+
+  //! Return explored shape.
+  const TopoDS_Shape& ExploredShape() const { return myShape; }
+
   //! Returns the current depth of the exploration. 0 is
   //! the shape to explore itself.
-    Standard_Integer Depth() const;
-  
+  Standard_Integer Depth() const { return myTop; }
+
   //! Clears the content of the explorer. It will return
   //! False on More().
     void Clear();
@@ -154,19 +144,8 @@ public:
   Destroy();
 }
 
-
-
-
-protected:
-
-
-
-
-
 private:
 
-
-
   TopExp_Stack myStack;
   Standard_Integer myTop;
   Standard_Integer mySizeOfStack;
@@ -175,14 +154,21 @@ private:
   TopAbs_ShapeEnum toFind;
   TopAbs_ShapeEnum toAvoid;
 
-
 };
 
+#include <TopoDS_Iterator.hxx>
 
-#include <TopExp_Explorer.lxx>
-
-
-
-
+inline void TopExp_Explorer::Clear()
+{
+  hasMore = Standard_False;
+  if (myTop > 0)
+  {
+    for (int i = 1; i <= myTop; i++)
+    {
+      myStack[i].~TopoDS_Iterator();
+    }
+  }
+  myTop = 0;
+}
 
 #endif // _TopExp_Explorer_HeaderFile
diff --git a/src/TopExp/TopExp_Explorer.lxx b/src/TopExp/TopExp_Explorer.lxx
deleted file mode 100644 (file)
index 3f26377..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-// Created on: 1993-01-18
-// Created by: Remi LEQUETTE
-// Copyright (c) 1993-1999 Matra Datavision
-// Copyright (c) 1999-2014 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 <TopoDS_Iterator.hxx>
-
-//=======================================================================
-//function : More
-//purpose  : 
-//=======================================================================
-
-inline Standard_Boolean  TopExp_Explorer::More()const 
-{
-  return hasMore;
-}
-
-
-//=======================================================================
-//function : Depth
-//purpose  : 
-//=======================================================================
-
-inline Standard_Integer  TopExp_Explorer::Depth()const 
-{
-  return myTop;
-}
-
-
-//=======================================================================
-//function : Clear
-//purpose  : 
-//=======================================================================
-
-inline void  TopExp_Explorer::Clear()
-{
-  hasMore = Standard_False;
-  if(myTop >0) {
-    for(int i=1;i<= myTop; i++)
-      myStack[i].~TopoDS_Iterator();
-  }
-  myTop = 0;
-}
index 27345a189fc39878a902c236d20ed90e5448ba2e..1fd91a9eea9c58eca8f95ba47c44a6ba9347c5c3 100644 (file)
@@ -19,7 +19,6 @@ TopoDS_HShape.hxx
 TopoDS_HShape.lxx
 TopoDS_Iterator.cxx
 TopoDS_Iterator.hxx
-TopoDS_Iterator.lxx
 TopoDS_ListIteratorOfListOfShape.hxx
 TopoDS_ListOfShape.hxx
 TopoDS_LockedShape.hxx
index eb5aab901065c27b8a1a5b50fa1cf5d3ce2564eb..ca51b61d0016f59dea2043068f106505eec833b0 100644 (file)
 
 #define No_Standard_NoSuchObject
 
+#include <TopoDS_Iterator.hxx>
 
 #include <Standard_NoMoreObject.hxx>
 #include <Standard_NoSuchObject.hxx>
-#include <TopoDS_Iterator.hxx>
-#include <TopoDS_Shape.hxx>
 
 //=======================================================================
 //function : Initialize
index 473c1e193da05eb8bbb9bde00ad417d1d1b34f72..b1ab7c4929c62a49a791686a34b64034a7698c21 100644 (file)
 #ifndef _TopoDS_Iterator_HeaderFile
 #define _TopoDS_Iterator_HeaderFile
 
-#include <Standard.hxx>
-#include <Standard_DefineAlloc.hxx>
-#include <Standard_Handle.hxx>
-
+#include <Standard_NoSuchObject.hxx>
 #include <TopoDS_Shape.hxx>
 #include <TopoDS_ListIteratorOfListOfShape.hxx>
 #include <TopAbs_Orientation.hxx>
 #include <TopLoc_Location.hxx>
-#include <Standard_Boolean.hxx>
-class Standard_NoMoreObject;
-class Standard_NoSuchObject;
-class TopoDS_Shape;
-
 
 //! Iterates on the underlying shape underlying a given
 //! TopoDS_Shape object, providing access to its
@@ -42,10 +34,9 @@ public:
 
   DEFINE_STANDARD_ALLOC
 
-  
   //! Creates an empty Iterator.
-    TopoDS_Iterator();
-  
+  TopoDS_Iterator() {}
+
   //! Creates an Iterator on <S> sub-shapes.
   //! Note:
   //! - If cumOri is true, the function composes all
@@ -53,8 +44,13 @@ public:
   //! - If cumLoc is true, the function multiplies all
   //! sub-shapes by the location of S, i.e. it applies to
   //! each sub-shape the transformation that is associated with S.
-    TopoDS_Iterator(const TopoDS_Shape& S, const Standard_Boolean cumOri = Standard_True, const Standard_Boolean cumLoc = Standard_True);
-  
+  TopoDS_Iterator (const TopoDS_Shape& S,
+                   const Standard_Boolean cumOri = Standard_True,
+                   const Standard_Boolean cumLoc = Standard_True)
+  {
+    Initialize (S, cumOri,cumLoc);
+  }
+
   //! Initializes this iterator with shape S.
   //! Note:
   //! - If cumOri is true, the function composes all
@@ -66,8 +62,8 @@ public:
   
   //! Returns true if there is another sub-shape in the
   //! shape which this iterator is scanning.
-    Standard_Boolean More() const;
-  
+  Standard_Boolean More() const { return myShapes.More(); }
+
   //! Moves on to the next sub-shape in the shape which
   //! this iterator is scanning.
   //! Exceptions
@@ -78,34 +74,19 @@ public:
   //! this iterator is scanning.
   //! Exceptions
   //! Standard_NoSuchObject if there is no current sub-shape.
-    const TopoDS_Shape& Value() const;
-
-
-
-
-protected:
-
-
-
-
+  const TopoDS_Shape& Value() const
+  {
+    Standard_NoSuchObject_Raise_if(!More(),"TopoDS_Iterator::Value");  
+    return myShape;
+  }
 
 private:
 
-
-
   TopoDS_Shape myShape;
   TopoDS_ListIteratorOfListOfShape myShapes;
   TopAbs_Orientation myOrientation;
   TopLoc_Location myLocation;
 
-
 };
 
-
-#include <TopoDS_Iterator.lxx>
-
-
-
-
-
 #endif // _TopoDS_Iterator_HeaderFile
diff --git a/src/TopoDS/TopoDS_Iterator.lxx b/src/TopoDS/TopoDS_Iterator.lxx
deleted file mode 100644 (file)
index b005ad0..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-// Created on: 1993-01-21
-// Created by: Remi LEQUETTE
-// Copyright (c) 1993-1999 Matra Datavision
-// Copyright (c) 1999-2014 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 <Standard_NoSuchObject.hxx>
-
-//=======================================================================
-//function : TopoDS_Iterator
-//purpose  : 
-//=======================================================================
-
-inline TopoDS_Iterator::TopoDS_Iterator()
-{}
-
-//=======================================================================
-//function : TopoDS_Iterator
-//purpose  : 
-//=======================================================================
-
-inline TopoDS_Iterator::TopoDS_Iterator(const TopoDS_Shape& S,
-                                        const Standard_Boolean cumOri,
-                                        const Standard_Boolean cumLoc)
-{
-  Initialize(S,cumOri,cumLoc);
-}
-
-//=======================================================================
-//function : More
-//purpose  : 
-//=======================================================================
-
-inline Standard_Boolean TopoDS_Iterator::More() const
-{
-  return myShapes.More();
-}
-
-//=======================================================================
-//function : Value
-//purpose  : 
-//=======================================================================
-
-inline const TopoDS_Shape& TopoDS_Iterator::Value() const
-{
-  Standard_NoSuchObject_Raise_if(!More(),"TopoDS_Iterator::Value");  
-  return myShape;
-}
index da6bd33caebc733520155d910696b6e7cc687736..847289eb09fce5874cae660c6f6c5cc7c6e463a4 100644 (file)
@@ -240,6 +240,11 @@ void XCAFDoc_VisMaterial::FillMaterialAspect (Graphic3d_MaterialAspect& theAspec
       theAspect.SetAlpha        (myPbrMat.BaseColor.Alpha());
       theAspect.SetSpecularColor(Quantity_Color (Graphic3d_Vec3 (myPbrMat.Metallic)));
       theAspect.SetShininess    (1.0f - myPbrMat.Roughness);
+      if (theAspect.Shininess() < 0.01f)
+      {
+        // clamp too small shininess values causing visual artifacts on corner view angles
+        theAspect.SetShininess (0.01f);
+      }
       theAspect.SetEmissiveColor (Quantity_Color (myPbrMat.EmissiveFactor.cwiseMin (Graphic3d_Vec3 (1.0f))));
     }
 
index 6a847af543d11f83580f01cc4572889f6f08208c..20c6ee9389ab7328dbd5d039d245126f3680670c 100644 (file)
@@ -148,6 +148,27 @@ static bool parseNameFormat (const char* theArg,
   return true;
 }
 
+//! Parse RWMesh_CoordinateSystem enumeration.
+static bool parseCoordinateSystem (const char* theArg,
+                                   RWMesh_CoordinateSystem& theSystem)
+{
+  TCollection_AsciiString aCSStr (theArg);
+  aCSStr.LowerCase();
+  if (aCSStr == "zup")
+  {
+    theSystem = RWMesh_CoordinateSystem_Zup;
+  }
+  else if (aCSStr == "yup")
+  {
+    theSystem = RWMesh_CoordinateSystem_Yup;
+  }
+  else
+  {
+    return Standard_False;
+  }
+  return Standard_True;
+}
+
 //=============================================================================
 //function : ReadGltf
 //purpose  : Reads glTF file
@@ -333,7 +354,9 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
   Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
   TColStd_IndexedDataMapOfStringString aFileInfo;
   RWGltf_WriterTrsfFormat aTrsfFormat = RWGltf_WriterTrsfFormat_Compact;
+  RWMesh_CoordinateSystem aSystemCoordSys = RWMesh_CoordinateSystem_Zup;
   bool toForceUVExport = false, toEmbedTexturesInGlb = true;
+  bool toMergeFaces = false, toSplitIndices16 = false;
   RWMesh_NameFormat aNodeNameFormat = RWMesh_NameFormat_InstanceOrProduct;
   RWMesh_NameFormat aMeshNameFormat = RWMesh_NameFormat_Product;
   for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
@@ -360,6 +383,40 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
         ++anArgIter;
       }
     }
+    else if (anArgCase == "-mergefaces")
+    {
+      toMergeFaces = true;
+      if (anArgIter + 1 < theNbArgs
+       && Draw::ParseOnOff (theArgVec[anArgIter + 1], toMergeFaces))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (anArgCase == "-splitindices16"
+          || anArgCase == "-splitindexes16"
+          || anArgCase == "-splitindices"
+          || anArgCase == "-splitindexes"
+          || anArgCase == "-splitind")
+    {
+      toSplitIndices16 = true;
+      if (anArgIter + 1 < theNbArgs
+       && Draw::ParseOnOff (theArgVec[anArgIter + 1], toSplitIndices16))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && (anArgCase == "-systemcoordinatesystem"
+           || anArgCase == "-systemcoordsystem"
+           || anArgCase == "-systemcoordsys"
+           || anArgCase == "-syscoordsys"))
+    {
+      if (!parseCoordinateSystem (theArgVec[++anArgIter], aSystemCoordSys))
+      {
+        Message::SendFail() << "Syntax error: unknown coordinate system '" << theArgVec[anArgIter] << "'";
+        return 1;
+      }
+    }
     else if (anArgCase == "-trsfformat"
           && anArgIter + 1 < theNbArgs)
     {
@@ -456,8 +513,10 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
   aWriter.SetMeshNameFormat (aMeshNameFormat);
   aWriter.SetForcedUVExport (toForceUVExport);
   aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb);
+  aWriter.SetMergeFaces (toMergeFaces);
+  aWriter.SetSplitIndices16 (toSplitIndices16);
   aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aSystemUnitFactor);
-  aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (RWMesh_CoordinateSystem_Zup);
+  aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (aSystemCoordSys);
   aWriter.Perform (aDoc, aFileInfo, aProgress->Start());
   return 0;
 }
@@ -550,27 +609,6 @@ static Standard_Integer readstl(Draw_Interpretor& theDI,
   return 0;
 }
 
-//! Parse RWMesh_CoordinateSystem enumeration.
-static Standard_Boolean parseCoordinateSystem (const char* theArg,
-                                               RWMesh_CoordinateSystem& theSystem)
-{
-  TCollection_AsciiString aCSStr (theArg);
-  aCSStr.LowerCase();
-  if (aCSStr == "zup")
-  {
-    theSystem = RWMesh_CoordinateSystem_Zup;
-  }
-  else if (aCSStr == "yup")
-  {
-    theSystem = RWMesh_CoordinateSystem_Yup;
-  }
-  else
-  {
-    return Standard_False;
-  }
-  return Standard_True;
-}
-
 //=============================================================================
 //function : ReadObj
 //purpose  : Reads OBJ file
@@ -1985,14 +2023,18 @@ void  XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
                    "\n\t\t: Same as ReadGltf but reads glTF file into a shape instead of a document.",
                    __FILE__, ReadGltf, g);
   theCommands.Add ("WriteGltf",
-                   "WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}=compact]"
+                   "WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}]=compact"
+           "\n\t\t:                    [-systemCoordSys {Zup|Yup}]=Zup"
            "\n\t\t:                    [-comments Text] [-author Name]"
-           "\n\t\t:                    [-forceUVExport] [-texturesSeparate]"
-           "\n\t\t:                    [-nodeNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}=instOrProd]"
-           "\n\t\t:                    [-meshNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}=product]"
+           "\n\t\t:                    [-forceUVExport]=0 [-texturesSeparate]=0 [-mergeFaces]=0 [-splitIndices16]=0"
+           "\n\t\t:                    [-nodeNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=instOrProd"
+           "\n\t\t:                    [-meshNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=product"
            "\n\t\t: Write XDE document into glTF file."
            "\n\t\t:   -trsfFormat preferred transformation format"
-           "\n\t\t:   -forceUVExport always export UV coordinates"
+           "\n\t\t:   -systemCoordSys system coordinate system; Zup when not specified"
+           "\n\t\t:   -mergeFaces     merge Faces within the same Mesh"
+           "\n\t\t:   -splitIndices16 split Faces to keep 16-bit indices when -mergeFaces is enabled"
+           "\n\t\t:   -forceUVExport  always export UV coordinates"
            "\n\t\t:   -texturesSeparate write textures to separate files"
            "\n\t\t:   -nodeNameFormat name format for Nodes"
            "\n\t\t:   -meshNameFormat name format for Meshes",
diff --git a/tests/de_mesh/gltf_write/as1 b/tests/de_mesh/gltf_write/as1
new file mode 100644 (file)
index 0000000..3efb8b1
--- /dev/null
@@ -0,0 +1,24 @@
+puts "========"
+puts "0032530: Data Exchange, RWGltf_CafWriter - add option merging Faces within the Part"
+puts "========"
+
+Close D0 -silent
+ReadStep D0 [locate_data_file as1-oc-214-mat.stp]
+XGetOneShape ss D0
+incmesh ss 1.0
+
+set aTmpGltf1 "${imagedir}/${casename}_tmp1.glb"
+set aTmpGltf2 "${imagedir}/${casename}_tmp2.glb"
+lappend occ_tmp_files $aTmpGltf1
+lappend occ_tmp_files $aTmpGltf2
+
+WriteGltf D0 "$aTmpGltf1"
+WriteGltf D0 "$aTmpGltf2" -mergeFaces
+
+ReadGltf D1 "$aTmpGltf1"
+XGetOneShape s1 D1
+checknbshapes s1 -face 160 -compound 28
+
+ReadGltf D "$aTmpGltf2"
+XGetOneShape s2 D
+checknbshapes s2 -face 18 -compound 10