]> OCCT Git - occt.git/commitdiff
0032867: Data Exchange - Implement Draco compression for writing glTF
authorichesnok <ichesnok@opencascade.com>
Tue, 29 Mar 2022 14:02:02 +0000 (17:02 +0300)
committersmoskvin <smoskvin@opencascade.com>
Wed, 6 Jul 2022 16:15:56 +0000 (19:15 +0300)
Draco compression added in RWGltf_CafWriter class.

14 files changed:
adm/cmake/draco.cmake
src/RWGltf/FILES
src/RWGltf/RWGltf_CafWriter.cxx
src/RWGltf/RWGltf_CafWriter.hxx
src/RWGltf/RWGltf_DracoParameters.hxx [new file with mode: 0644]
src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx
tests/de_mesh/gltf_write/010 [new file with mode: 0644]
tests/de_mesh/gltf_write/Diamond [new file with mode: 0644]
tests/de_mesh/gltf_write/as1draco [new file with mode: 0644]
tests/de_mesh/gltf_write/bearing [new file with mode: 0644]
tests/de_mesh/gltf_write/bull [new file with mode: 0644]
tests/de_mesh/gltf_write/screw [new file with mode: 0644]
tests/de_mesh/gltf_write/soapbox [new file with mode: 0644]
tests/de_mesh/gltf_write/test [new file with mode: 0644]

index 53cf90a954d9a020785bcd3074381cbbd5232b1d..755391fa529e41ae01492bfc4a7900e8d478dece 100644 (file)
@@ -1,4 +1,96 @@
 # Draco - a library for a lossy vertex data compression, used as extension to glTF format.
 # https://github.com/google/draco
 
-THIRDPARTY_PRODUCT("DRACO" "draco/compression/decode.h" "CSF_Draco" "")
+OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/occt_macros")
+
+if (NOT DEFINED INSTALL_DRACO)
+  set (INSTALL_DRACO OFF CACHE BOOL "${INSTALL_DRACO_DESCR}")
+endif()
+
+if (NOT DEFINED 3RDPARTY_DRACO_DIR)
+  set (3RDPARTY_DRACO_DIR "" CACHE PATH "The directory containing Draco")
+endif()
+
+if (NOT DEFINED 3RDPARTY_DRACO_INCLUDE_DIR)
+  set (3RDPARTY_DRACO_INCLUDE_DIR  "" CACHE PATH "The directory containing headers of the Draco")
+endif()
+
+if (NOT DEFINED 3RDPARTY_DRACO_LIBRARY OR NOT 3RDPARTY_DRACO_LIBRARY_DIR OR NOT EXISTS "${3RDPARTY_DRACO_LIBRARY_DIR}")
+  set (3RDPARTY_DRACO_LIBRARY "" CACHE FILEPATH "Draco library"  FORCE)
+endif()
+
+if (NOT DEFINED 3RDPARTY_DRACO_LIBRARY_DIR)
+  set (3RDPARTY_DRACO_LIBRARY_DIR "" CACHE PATH "The directory containing Draco library")
+endif()
+
+if (3RDPARTY_DIR AND EXISTS "${3RDPARTY_DIR}")
+  if (NOT 3RDPARTY_DRACO_DIR OR NOT EXISTS "${3RDPARTY_DRACO_DIR}")
+    FIND_PRODUCT_DIR("${3RDPARTY_DIR}" draco DRACO_DIR_NAME)
+    if (DRACO_DIR_NAME)
+      set (3RDPARTY_DRACO_DIR "${3RDPARTY_DIR}/${DRACO_DIR_NAME}" CACHE PATH "The directory containing Draco" FORCE)
+    endif()
+  endif()
+endif()
+
+if (3RDPARTY_DRACO_DIR AND EXISTS "${3RDPARTY_DRACO_DIR}")
+  set (DRACO_INCLUDE_PATH "${3RDPARTY_DRACO_DIR}/include")
+  set (DRACO_LIBRARY_PATH "${3RDPARTY_DRACO_DIR}/lib")
+endif()
+
+if (NOT 3RDPARTY_DRACO_INCLUDE_DIR)
+  if (DRACO_INCLUDE_PATH AND EXISTS "${DRACO_INCLUDE_PATH}")
+    set (3RDPARTY_DRACO_INCLUDE_DIR "${DRACO_INCLUDE_PATH}" CACHE FILEPATH "The directory containing headers of DRACO" FORCE)
+  endif()
+endif()
+
+if (NOT 3RDPARTY_DRACO_LIBRARY_DIR)
+  if (DRACO_LIBRARY_PATH AND EXISTS "${DRACO_LIBRARY_PATH}")
+    set (3RDPARTY_DRACO_LIBRARY_DIR "${DRACO_LIBRARY_PATH}" CACHE FILEPATH "The directory containing DRACO library" FORCE)
+  endif()
+endif()
+
+if (3RDPARTY_DRACO_INCLUDE_DIR AND EXISTS "${3RDPARTY_DRACO_INCLUDE_DIR}")
+  list (APPEND 3RDPARTY_INCLUDE_DIRS "${3RDPARTY_DRACO_INCLUDE_DIR}")
+else()
+  list (APPEND 3RDPARTY_NOT_INCLUDED 3RDPARTY_DRACO_INCLUDE_DIR)
+endif()
+
+if (3RDPARTY_DRACO_DIR AND EXISTS "${3RDPARTY_DRACO_DIR}")
+  if (NOT 3RDPARTY_DRACO_LIBRARY OR NOT EXISTS "${3RDPARTY_DRACO_LIBRARY}")
+    set (CMAKE_FIND_LIBRARY_SUFFIXES .lib .so .dylib .a)
+    set (3RDPARTY_DRACO_LIBRARY "3RDPARTY_DRACO_LIBRARY-NOTFOUND" CACHE FILEPATH "The path to Draco library" FORCE)
+
+    find_library (3RDPARTY_DRACO_LIBRARY NAMES ${CSF_Draco}
+                                         PATHS "${3RDPARTY_DRACO_LIBRARY_DIR}"
+                                         PATH_SUFFIXES lib
+                                         CMAKE_FIND_ROOT_PATH_BOTH
+                                         NO_DEFAULT_PATH)
+    if (3RDPARTY_DRACO_LIBRARY AND EXISTS "${3RDPARTY_DRACO_LIBRARY}")
+      get_filename_component (3RDPARTY_DRACO_LIBRARY_DIR "${3RDPARTY_DRACO_LIBRARY}" PATH)
+      set (3RDPARTY_DRACO_LIBRARY_DIR "${3RDPARTY_DRACO_LIBRARY_DIR}" CACHE FILEPATH "The directory containing Draco library" FORCE)
+    endif()
+  endif()
+endif()
+
+if (3RDPARTY_DRACO_LIBRARY_DIR AND EXISTS "${3RDPARTY_DRACO_LIBRARY_DIR}")
+  list (APPEND 3RDPARTY_LIBRARY_DIRS "${3RDPARTY_DRACO_LIBRARY_DIR}")
+else()
+  list (APPEND 3RDPARTY_NO_LIBS 3RDPARTY_DRACO_LIBRARY_DIR)
+endif()
+
+if (INSTALL_DRACO)
+  get_filename_component(3RDPARTY_DRACO_LIBRARY_REALPATH ${3RDPARTY_DRACO_LIBRARY} REALPATH)
+  if (SINGLE_GENERATOR)
+    install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH} DESTINATION "${INSTALL_DIR_LIB}")
+  else()
+    install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH}
+             CONFIGURATIONS Release
+             DESTINATION "${INSTALL_DIR_LIB}")
+    install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH}
+             CONFIGURATIONS RelWithDebInfo
+             DESTINATION "${INSTALL_DIR_LIB}i")
+    install (FILES ${3RDPARTY_DRACO_LIBRARY_REALPATH}
+             CONFIGURATIONS Debug
+             DESTINATION "${INSTALL_DIR_LIB}d")
+  endif()
+endif()
index 5d580a2052121b540e71d22db2c442eda012f4b4..fc24654adf99909b43572faee7e0b865b123673f 100644 (file)
@@ -4,6 +4,7 @@ RWGltf_CafWriter.cxx
 RWGltf_CafWriter.hxx
 RWGltf_ConfigurationNode.cxx
 RWGltf_ConfigurationNode.hxx
+RWGltf_DracoParameters.hxx
 RWGltf_GltfAccessor.hxx
 RWGltf_GltfAccessorCompType.hxx
 RWGltf_GltfAccessorLayout.hxx
index 5e0c8cf0ba33cdf2d3e67fddbd0fb2c8ec28ebde..836d15b86e5f5c52ac862fbb09670c1962b95016 100644 (file)
@@ -22,7 +22,7 @@
 #include <OSD_FileSystem.hxx>
 #include <OSD_File.hxx>
 #include <OSD_Path.hxx>
-#include <Poly_Triangulation.hxx>
+#include <OSD_Timer.hxx>
 #include <RWGltf_GltfAccessorLayout.hxx>
 #include <RWGltf_GltfArrayType.hxx>
 #include <RWGltf_GltfMaterialMap.hxx>
   #include <RWGltf_GltfOStreamWriter.hxx>
 #endif
 
+#ifdef HAVE_DRACO
+  #include <draco/compression/encode.h>
+#endif
+
 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
 
 namespace
@@ -84,6 +88,84 @@ namespace
   {
     theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
   }
+
+#ifdef HAVE_DRACO
+  //! Write nodes to Draco mesh
+  static void writeNodesToDracoMesh (draco::Mesh& theMesh,
+                                     const std::vector<Graphic3d_Vec3>& theNodes)
+  {
+    if (theNodes.empty())
+    {
+      return;
+    }
+
+    draco::PointAttribute anAttr;
+    anAttr.Init (draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32, false, sizeof(float) * 3);
+    const int anId = theMesh.AddAttribute (anAttr, true, uint32_t(theNodes.size()));
+    draco::PointAttribute* aPtr = theMesh.attribute (anId);
+    draco::PointIndex anIndex(0);
+    for (size_t aNodeInd = 0; aNodeInd < theNodes.size(); ++aNodeInd, ++anIndex)
+    {
+      aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theNodes[aNodeInd].GetData());
+    }
+  }
+
+  //! Write normals to Draco mesh
+  static void writeNormalsToDracoMesh (draco::Mesh& theMesh,
+                                       const std::vector<Graphic3d_Vec3>& theNormals)
+  {
+    if (theNormals.empty())
+    {
+      return;
+    }
+
+    draco::PointAttribute anAttr;
+    anAttr.Init (draco::GeometryAttribute::NORMAL, 3, draco::DT_FLOAT32, false, sizeof(float) * 3);
+    const int anId = theMesh.AddAttribute (anAttr, true, uint32_t(theNormals.size()));
+    draco::PointAttribute* aPtr = theMesh.attribute (anId);
+    draco::PointIndex anIndex(0);
+    for (size_t aNormInd = 0; aNormInd < theNormals.size(); ++aNormInd, ++anIndex)
+    {
+      aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theNormals[aNormInd].GetData());
+    }
+  }
+
+  //! Write texture UV coordinates to Draco mesh
+  static void writeTexCoordsToDracoMesh (draco::Mesh& theMesh,
+                                         const std::vector<Graphic3d_Vec2>& theTexCoord)
+  {
+    if (theTexCoord.empty())
+    {
+      return;
+    }
+
+    draco::PointAttribute anAttr;
+    anAttr.Init (draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32, false, sizeof(float) * 2);
+    const int anId = theMesh.AddAttribute (anAttr, true, uint32_t(theTexCoord.size()));
+    draco::PointAttribute* aPtr = theMesh.attribute (anId);
+    draco::PointIndex anIndex(0);
+    for (size_t aTexInd = 0; aTexInd < theTexCoord.size(); ++aTexInd, ++anIndex)
+    {
+      aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theTexCoord[aTexInd].GetData());
+    }
+  }
+
+  //! Write indices to Draco mesh
+  static void writeIndicesToDracoMesh (draco::Mesh& theMesh,
+                                       const std::vector<Poly_Triangle>& theIndices)
+  {
+    draco::Mesh::Face aFace;
+    int anIndex = 0;
+    for (size_t anInd = 0; anInd < theIndices.size(); ++anInd, ++anIndex)
+    {
+      const Poly_Triangle& anElem = theIndices[anInd];
+      aFace[0] = anElem.Value(1);
+      aFace[1] = anElem.Value(2);
+      aFace[2] = anElem.Value(3);
+      theMesh.SetFace (draco::FaceIndex (anIndex), aFace);
+    }
+  }
+#endif
 }
 
 //================================================================
@@ -150,7 +232,8 @@ Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& th
 void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
                                   std::ostream& theBinFile,
                                   const RWMesh_FaceIterator& theFaceIter,
-                                  Standard_Integer& theAccessorNb) const
+                                  Standard_Integer& theAccessorNb,
+                                  const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const
 {
   if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
   {
@@ -161,8 +244,11 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
   }
   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");
+    if (theMesh.get() == nullptr)
+    {
+      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();
 
@@ -172,7 +258,14 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
     gp_XYZ aNode = theFaceIter.NodeTransformed (aNodeIter).XYZ();
     myCSTrsf.TransformPosition (aNode);
     theGltfFace.NodePos.BndBox.Add (Graphic3d_Vec3d(aNode.X(), aNode.Y(), aNode.Z()));
-    writeVec3 (theBinFile, aNode);
+    if (theMesh.get() != nullptr)
+    {
+      theMesh->NodesVec.push_back(Graphic3d_Vec3(float(aNode.X()), float(aNode.Y()), float(aNode.Z())));
+    }
+    else
+    {
+      writeVec3(theBinFile, aNode);
+    }
   }
 }
 
@@ -183,7 +276,8 @@ void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
 void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
                                     std::ostream& theBinFile,
                                     RWMesh_FaceIterator& theFaceIter,
-                                    Standard_Integer& theAccessorNb) const
+                                    Standard_Integer& theAccessorNb,
+                                    const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const
 {
   if (!theFaceIter.HasNormals())
   {
@@ -199,8 +293,11 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
   }
   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");
+    if (theMesh.get() == nullptr)
+    {
+      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();
 
@@ -210,7 +307,14 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
     const gp_Dir aNormal = theFaceIter.NormalTransformed (aNodeIter);
     Graphic3d_Vec3 aVecNormal ((float )aNormal.X(), (float )aNormal.Y(), (float )aNormal.Z());
     myCSTrsf.TransformNormal (aVecNormal);
-    writeVec3 (theBinFile, aVecNormal);
+    if (theMesh.get() != nullptr)
+    {
+      theMesh->NormalsVec.push_back(aVecNormal);
+    }
+    else
+    {
+      writeVec3(theBinFile, aVecNormal);
+    }
   }
 }
 
@@ -221,7 +325,8 @@ void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
 void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
                                        std::ostream& theBinFile,
                                        const RWMesh_FaceIterator& theFaceIter,
-                                       Standard_Integer& theAccessorNb) const
+                                       Standard_Integer& theAccessorNb,
+                                       const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const
 {
   if (!theFaceIter.HasTexCoords())
   {
@@ -253,8 +358,11 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
   }
   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");
+    if (theMesh.get() == nullptr)
+    {
+      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();
 
@@ -263,7 +371,14 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
   {
     gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
     aTexCoord.SetY (1.0 - aTexCoord.Y());
-    writeVec2 (theBinFile, aTexCoord.XY());
+    if (theMesh.get() != nullptr)
+    {
+      theMesh->TexCoordsVec.push_back(Graphic3d_Vec2((float)aTexCoord.X(), (float)aTexCoord.Y()));
+    }
+    else
+    {
+      writeVec2(theBinFile, aTexCoord.XY());
+    }
   }
 }
 
@@ -274,7 +389,8 @@ void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
 void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
                                     std::ostream& theBinFile,
                                     const RWMesh_FaceIterator& theFaceIter,
-                                    Standard_Integer& theAccessorNb)
+                                    Standard_Integer& theAccessorNb,
+                                    const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh)
 {
   if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
   {
@@ -287,11 +403,14 @@ void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
   }
   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");
+    if (theMesh.get() == nullptr)
+    {
+      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();
@@ -306,13 +425,20 @@ void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
     aTri(1) += aNodeFirst;
     aTri(2) += aNodeFirst;
     aTri(3) += aNodeFirst;
-    if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
+    if (theMesh.get() != nullptr)
     {
-      writeTriangle16 (theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
+      theMesh->IndicesVec.push_back(aTri);
     }
     else
     {
-      writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
+      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)));
+      }
     }
   }
 }
@@ -373,6 +499,14 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
                                      const TColStd_MapOfAsciiString* theLabelFilter,
                                      const Message_ProgressRange& theProgress)
 {
+#ifndef HAVE_DRACO
+  if (myDracoParameters.DracoCompression)
+  {
+    Message::SendFail ("Error: cannot use Draco compression, Draco library missing.");
+    return false;
+  }
+#endif
+
   myBuffViewPos.Id               = RWGltf_GltfAccessor::INVALID_ID;
   myBuffViewPos.ByteOffset       = 0;
   myBuffViewPos.ByteLength       = 0;
@@ -396,6 +530,8 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
   myBuffViewInd.ByteLength       = 0;
   myBuffViewInd.Target           = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
 
+  myBuffViewsDraco.clear();
+
   myBinDataMap.Clear();
   myBinDataLen64 = 0;
 
@@ -508,6 +644,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
     }
   }
 
+  std::vector<std::shared_ptr<RWGltf_CafWriter::Mesh>> aMeshes;
   Standard_Integer aNbAccessors = 0;
   NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
   NCollection_DataMap<TopoDS_Shape, Handle(RWGltf_GltfFace), TopTools_ShapeMapHasher> aWrittenPrimData;
@@ -526,6 +663,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
     aBuffView->ByteOffset = aBinFile->tellp();
     aWrittenFaces.Clear (false);
     aWrittenPrimData.Clear (false);
+    size_t aMeshIndex = 0;
     for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More() && aPSentryBin.More(); aBinDataIter.Next())
     {
       const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
@@ -533,6 +671,23 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
       {
         continue;
       }
+      
+      std::shared_ptr<RWGltf_CafWriter::Mesh> aMeshPtr;
+      ++aMeshIndex;
+    #ifdef HAVE_DRACO
+      if (myDracoParameters.DracoCompression)
+      {
+        if (aMeshIndex <= aMeshes.size())
+        {
+          aMeshPtr = aMeshes.at(aMeshIndex - 1);
+        }
+        else
+        {
+          aMeshes.push_back(std::make_shared<RWGltf_CafWriter::Mesh>(RWGltf_CafWriter::Mesh()));
+          aMeshPtr = aMeshes.back();
+        }
+      }
+    #endif
 
       for (RWGltf_GltfFaceList::Iterator aGltfFaceIter (*aGltfFaceList); aGltfFaceIter.More() && aPSentryBin.More(); aGltfFaceIter.Next())
       {
@@ -579,22 +734,22 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
             case RWGltf_GltfArrayType_Position:
             {
               aGltfFace->NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step
-              saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
               break;
             }
             case RWGltf_GltfArrayType_Normal:
             {
-              saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
               break;
             }
             case RWGltf_GltfArrayType_TCoord0:
             {
-              saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
               break;
             }
             case RWGltf_GltfArrayType_Indices:
             {
-              saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
+              saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
               break;
             }
             default:
@@ -611,16 +766,22 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
         }
 
         // add alignment by 4 bytes (might happen on RWGltf_GltfAccessorCompType_UInt16 indices)
-        int64_t aContentLen64 = (int64_t)aBinFile->tellp();
-        while (aContentLen64 % 4 != 0)
+        if (!myDracoParameters.DracoCompression)
         {
-          aBinFile->write (" ", 1);
-          ++aContentLen64;
+          int64_t aContentLen64 = (int64_t)aBinFile->tellp();
+          while (aContentLen64 % 4 != 0)
+          {
+            aBinFile->write(" ", 1);
+            ++aContentLen64;
+          }
         }
       }
     }
 
-    aBuffView->ByteLength = (int64_t )aBinFile->tellp() - aBuffView->ByteOffset;
+    if (!myDracoParameters.DracoCompression)
+    {
+      aBuffView->ByteLength = (int64_t)aBinFile->tellp() - aBuffView->ByteOffset;
+    }
     if (!aPSentryBin.More())
     {
       return false;
@@ -629,6 +790,72 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
     aPSentryBin.Next();
   }
 
+  if (myDracoParameters.DracoCompression)
+  {
+#ifdef HAVE_DRACO
+    OSD_Timer aDracoTimer;
+    aDracoTimer.Start();
+    int aBuffId = 0;
+    draco::Encoder aDracoEncoder;
+    aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::POSITION,  myDracoParameters.QuantizePositionBits);
+    aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::NORMAL,    myDracoParameters.QuantizeNormalBits);
+    aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::TEX_COORD, myDracoParameters.QuantizeTexcoordBits);
+    aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::COLOR,     myDracoParameters.QuantizeColorBits);
+    aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::GENERIC,   myDracoParameters.QuantizeGenericBits);
+    aDracoEncoder.SetSpeedOptions (myDracoParameters.CompressionLevel, myDracoParameters.CompressionLevel);
+    for (size_t aMeshInd = 0; aMeshInd != aMeshes.size(); ++aMeshInd)
+    {
+      const std::shared_ptr<RWGltf_CafWriter::Mesh>& aCurrentMesh = aMeshes[aMeshInd];
+      if (aCurrentMesh->NodesVec.empty())
+      {
+        continue;
+      }
+
+      draco::Mesh aDracoMesh;
+      writeNodesToDracoMesh (aDracoMesh, aCurrentMesh->NodesVec);
+      if (!aCurrentMesh->NormalsVec.empty())
+      {
+        writeNormalsToDracoMesh (aDracoMesh, aCurrentMesh->NormalsVec);
+      }
+      if (!aCurrentMesh->TexCoordsVec.empty())
+      {
+        writeTexCoordsToDracoMesh (aDracoMesh, aCurrentMesh->TexCoordsVec);
+      }
+      writeIndicesToDracoMesh (aDracoMesh, aCurrentMesh->IndicesVec);
+
+      draco::EncoderBuffer anEncoderBuff;
+      draco::Status aStatus = aDracoEncoder.EncodeMeshToBuffer (aDracoMesh, &anEncoderBuff);
+      if (!aStatus.ok())
+      {
+        Message::SendFail (TCollection_AsciiString("Error: mesh cannot be encoded in draco buffer."));
+        return false;
+      }
+
+      RWGltf_GltfBufferView aBuffViewDraco;
+      aBuffViewDraco.Id = aBuffId++;
+      aBuffViewDraco.ByteOffset = aBinFile->tellp();
+      aBinFile->write (anEncoderBuff.data(), std::streamsize(anEncoderBuff.size()));
+      if (!aBinFile->good())
+      {
+        Message::SendFail (TCollection_AsciiString("File '") + myBinFileNameFull + "' cannot be written");
+        return false;
+      }
+
+      int64_t aLength = (int64_t)aBinFile->tellp();
+      while (aLength % 4 != 0)
+      {
+        aBinFile->write(" ", 1);
+        ++aLength;
+      }
+
+      aBuffViewDraco.ByteLength = aLength - aBuffViewDraco.ByteOffset;
+      myBuffViewsDraco.push_back (aBuffViewDraco);
+    }
+    aDracoTimer.Stop();
+    Message::SendInfo (TCollection_AsciiString("Draco compression time: ") + aDracoTimer.ElapsedTime() + " s");
+#endif
+  }
+
   if (myIsBinary
    && myToEmbedTexturesInGlb)
   {
@@ -1020,10 +1247,13 @@ void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
   }
 
   myWriter->StartObject();
-  myWriter->Key    ("bufferView");
-  myWriter->Int    (myBuffViewPos.Id);
-  myWriter->Key    ("byteOffset");
-  myWriter->Int64  (theGltfFace.NodePos.ByteOffset);
+  if (!myDracoParameters.DracoCompression)
+  {
+    myWriter->Key    ("bufferView");
+    myWriter->Int    (myBuffViewPos.Id);
+    myWriter->Key    ("byteOffset");
+    myWriter->Int64  (theGltfFace.NodePos.ByteOffset);
+  }
   myWriter->Key    ("componentType");
   myWriter->Int    (theGltfFace.NodePos.ComponentType);
   myWriter->Key    ("count");
@@ -1068,10 +1298,13 @@ void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
   }
 
   myWriter->StartObject();
-  myWriter->Key    ("bufferView");
-  myWriter->Int    (myBuffViewNorm.Id);
-  myWriter->Key    ("byteOffset");
-  myWriter->Int64  (theGltfFace.NodeNorm.ByteOffset);
+  if (!myDracoParameters.DracoCompression)
+  {
+    myWriter->Key    ("bufferView");
+    myWriter->Int    (myBuffViewNorm.Id);
+    myWriter->Key    ("byteOffset");
+    myWriter->Int64  (theGltfFace.NodeNorm.ByteOffset);
+  }
   myWriter->Key    ("componentType");
   myWriter->Int    (theGltfFace.NodeNorm.ComponentType);
   myWriter->Key    ("count");
@@ -1116,10 +1349,13 @@ void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
   }
 
   myWriter->StartObject();
-  myWriter->Key    ("bufferView");
-  myWriter->Int    (myBuffViewTextCoord.Id);
-  myWriter->Key    ("byteOffset");
-  myWriter->Int64  (theGltfFace.NodeUV.ByteOffset);
+  if (!myDracoParameters.DracoCompression)
+  {
+    myWriter->Key    ("bufferView");
+    myWriter->Int    (myBuffViewTextCoord.Id);
+    myWriter->Key    ("byteOffset");
+    myWriter->Int64  (theGltfFace.NodeUV.ByteOffset);
+  }
   myWriter->Key    ("componentType");
   myWriter->Int    (theGltfFace.NodeUV.ComponentType);
   myWriter->Key    ("count");
@@ -1164,10 +1400,13 @@ void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
   }
 
   myWriter->StartObject();
-  myWriter->Key    ("bufferView");
-  myWriter->Int    (myBuffViewInd.Id);
-  myWriter->Key    ("byteOffset");
-  myWriter->Int64  (theGltfFace.Indices.ByteOffset);
+  if (!myDracoParameters.DracoCompression)
+  {
+    myWriter->Key("bufferView");
+    myWriter->Int(myBuffViewInd.Id);
+    myWriter->Key("byteOffset");
+    myWriter->Int64(theGltfFace.Indices.ByteOffset);
+  }
   myWriter->Key    ("componentType");
   myWriter->Int    (theGltfFace.Indices.ComponentType);
   myWriter->Key    ("count");
@@ -1309,6 +1548,24 @@ void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBuffer
     myWriter->Int    (myBuffViewInd.Target);
     myWriter->EndObject();
   }
+  if (myDracoParameters.DracoCompression)
+  {
+    for (size_t aBufInd = 0; aBufInd != myBuffViewsDraco.size(); ++aBufInd)
+    {
+      if (myBuffViewsDraco[aBufInd].Id != RWGltf_GltfAccessor::INVALID_ID)
+      {
+        aBuffViewId++;
+        myWriter->StartObject();
+        myWriter->Key("buffer");
+        myWriter->Int(theBinDataBufferId);
+        myWriter->Key("byteLength");
+        myWriter->Int64(myBuffViewsDraco[aBufInd].ByteLength);
+        myWriter->Key("byteOffset");
+        myWriter->Int64(myBuffViewsDraco[aBufInd].ByteOffset);
+        myWriter->EndObject();
+      }
+    }
+  }
 
   myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
 
@@ -1352,7 +1609,28 @@ void RWGltf_CafWriter::writeBuffers()
 // =======================================================================
 void RWGltf_CafWriter::writeExtensions()
 {
+#ifdef HAVE_RAPIDJSON
   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
+
+  if (myDracoParameters.DracoCompression)
+  {
+    myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_ExtensionsUsed));
+
+    myWriter->StartArray();
+    {
+      myWriter->Key("KHR_draco_mesh_compression");
+    }
+    myWriter->EndArray();
+
+    myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_ExtensionsRequired));
+
+    myWriter->StartArray();
+    {
+      myWriter->Key("KHR_draco_mesh_compression");
+    }
+    myWriter->EndArray();
+  }
+#endif
 }
 
 // =======================================================================
@@ -1425,6 +1703,7 @@ void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNo
 // =======================================================================
 void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace,
                                        const TCollection_AsciiString& theName,
+                                       const int theDracoBufInd,
                                        bool& theToStartPrims)
 {
 #ifdef HAVE_RAPIDJSON
@@ -1471,12 +1750,48 @@ void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace,
     }
     myWriter->Key ("mode");
     myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
+
+    if (myDracoParameters.DracoCompression)
+    {
+      myWriter->Key("extensions");
+      myWriter->StartObject();
+      {
+        myWriter->Key("KHR_draco_mesh_compression");
+        myWriter->StartObject();
+        myWriter->Key("bufferView");
+        myWriter->Int(myBuffViewsDraco[theDracoBufInd].Id);
+        myWriter->Key("attributes");
+        myWriter->StartObject();
+        {
+          int anAttrInd = 0;
+          if (theGltfFace.NodePos.Id != RWGltf_GltfAccessor::INVALID_ID)
+          {
+            myWriter->Key("POSITION");
+            myWriter->Int(anAttrInd++);
+          }
+          if (theGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
+          {
+            myWriter->Key("NORMAL");
+            myWriter->Int(anAttrInd++);
+          }
+          if (theGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
+          {
+            myWriter->Key("TEXCOORD_0");
+            myWriter->Int(anAttrInd++);
+          }
+        }
+        myWriter->EndObject();
+        myWriter->EndObject();
+      }
+      myWriter->EndObject();
+    }
   }
   myWriter->EndObject();
 #else
   (void )theGltfFace;
   (void )theName;
   (void )theToStartPrims;
+  (void )theDracoBufInd;
 #endif
 }
 
@@ -1492,6 +1807,8 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
   myWriter->StartArray();
 
+  int aDracoBufInd = 0;
+  NCollection_IndexedDataMap<int, int> aDracoBufIndMap;
   NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
   {
@@ -1522,7 +1839,23 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
       for (RWGltf_GltfFaceList::Iterator aFaceGroupIter (*aGltfFaceList); aFaceGroupIter.More(); aFaceGroupIter.Next())
       {
         const Handle(RWGltf_GltfFace)& aGltfFace = aFaceGroupIter.Value();
-        writePrimArray (*aGltfFace, aNodeName, toStartPrims);
+        const int aPrevSize = aDracoBufIndMap.Size();
+        const int aTempDracoBufInd = aDracoBufInd;
+        if (myDracoParameters.DracoCompression
+        && !aDracoBufIndMap.FindFromKey (aGltfFace->NodePos.Id, aDracoBufInd))
+        {
+          aDracoBufIndMap.Add (aGltfFace->NodePos.Id, aDracoBufInd);
+        }
+
+        writePrimArray (*aGltfFace, aNodeName, aDracoBufInd, toStartPrims);
+        if (aTempDracoBufInd != aDracoBufInd)
+        {
+          aDracoBufInd = aTempDracoBufInd;
+        }
+        if (!myDracoParameters.DracoCompression || aDracoBufIndMap.Size() > aPrevSize)
+        {
+          ++aDracoBufInd;
+        }
       }
     }
     else
@@ -1542,7 +1875,23 @@ void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeM
         }
 
         const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceList->First();
-        writePrimArray (*aGltfFace, aNodeName, toStartPrims);
+        const int aPrevSize = aDracoBufIndMap.Size();
+        const int aTempDracoBufInd = aDracoBufInd;
+        if (myDracoParameters.DracoCompression
+        && !aDracoBufIndMap.FindFromKey(aGltfFace->NodePos.Id, aDracoBufInd))
+        {
+          aDracoBufIndMap.Add(aGltfFace->NodePos.Id, aDracoBufInd);
+        }
+
+        writePrimArray (*aGltfFace, aNodeName, aDracoBufInd, toStartPrims);
+        if (aTempDracoBufInd != aDracoBufInd)
+        {
+          aDracoBufInd = aTempDracoBufInd;
+        }
+        if (!myDracoParameters.DracoCompression || aDracoBufIndMap.Size() > aPrevSize)
+        {
+          ++aDracoBufInd;
+        }
       }
     }
 
index 6e8b9767337f36483944eb28a8096bfc4e6d5be8..be1d8a344d34c8679bda7cd8a0e8c05de6f2ad25 100644 (file)
 #include <TColStd_MapOfAsciiString.hxx>
 #include <TDF_LabelSequence.hxx>
 #include <TopTools_ShapeMapHasher.hxx>
+#include <RWGltf_DracoParameters.hxx>
 #include <RWGltf_GltfBufferView.hxx>
 #include <RWGltf_GltfFace.hxx>
 #include <RWGltf_WriterTrsfFormat.hxx>
 #include <RWMesh_CoordinateSystemConverter.hxx>
 #include <RWMesh_NameFormat.hxx>
 #include <XCAFPrs_Style.hxx>
+#include <Poly_Triangle.hxx>
 
 #include <memory>
 
@@ -40,6 +42,15 @@ class RWGltf_CafWriter : public Standard_Transient
   DEFINE_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
 public:
 
+  //! Mesh
+  struct Mesh
+  {
+    std::vector<Graphic3d_Vec3> NodesVec;     //!< vector for mesh nodes
+    std::vector<Graphic3d_Vec3> NormalsVec;   //!< vector for mesh normals
+    std::vector<Graphic3d_Vec2> TexCoordsVec; //!< vector for mesh texture UV coordinates
+    std::vector<Poly_Triangle>  IndicesVec;   //!< vector for mesh indices
+  };
+
   //! Main constructor.
   //! @param theFile     [in] path to output glTF file
   //! @param theIsBinary [in] flag to write into binary glTF format (.glb)
@@ -114,6 +125,12 @@ public:
   //! May reduce binary data size thanks to smaller triangle indexes.
   void SetSplitIndices16 (bool theToSplit) { myToSplitIndices16 = theToSplit; }
 
+  //! Return Draco parameters
+  const RWGltf_DracoParameters& CompressionParameters() const { return myDracoParameters; }
+
+  //! Set Draco parameters
+  void SetCompressionParameters(const RWGltf_DracoParameters& theDracoParameters) { myDracoParameters = theDracoParameters; }
+
   //! Write glTF file and associated binary file.
   //! Triangulation data should be precomputed within shapes!
   //! @param theDocument    [in] input document
@@ -186,40 +203,48 @@ protected:
   //! @param theBinFile  [out] output file to write into
   //! @param theFaceIter [in]  current face to write
   //! @param theAccessorNb [in] [out] last accessor index
+  //! @param theMesh [in] [out] mesh
   Standard_EXPORT virtual void saveNodes (RWGltf_GltfFace& theGltfFace,
                                           std::ostream& theBinFile,
                                           const RWMesh_FaceIterator& theFaceIter,
-                                          Standard_Integer& theAccessorNb) const;
+                                          Standard_Integer& theAccessorNb,
+                                          const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
 
   //! Write mesh normals into binary file.
   //! @param theGltfFace [out] glTF face definition
   //! @param theBinFile  [out] output file to write into
   //! @param theFaceIter [in]  current face to write
   //! @param theAccessorNb [in] [out] last accessor index
+  //! @param theMesh [in] [out] mesh
   Standard_EXPORT virtual void saveNormals (RWGltf_GltfFace& theGltfFace,
                                             std::ostream& theBinFile,
                                             RWMesh_FaceIterator& theFaceIter,
-                                            Standard_Integer& theAccessorNb) const;
+                                            Standard_Integer& theAccessorNb,
+                                            const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
 
   //! Write mesh texture UV coordinates into binary file.
   //! @param theGltfFace [out] glTF face definition
   //! @param theBinFile  [out] output file to write into
   //! @param theFaceIter [in]  current face to write
   //! @param theAccessorNb [in] [out] last accessor index
+  //! @param theMesh [in] [out] mesh
   Standard_EXPORT virtual void saveTextCoords (RWGltf_GltfFace& theGltfFace,
                                                std::ostream& theBinFile,
                                                const RWMesh_FaceIterator& theFaceIter,
-                                               Standard_Integer& theAccessorNb) const;
+                                               Standard_Integer& theAccessorNb,
+                                               const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh) const;
 
   //! Write mesh indexes into binary file.
   //! @param theGltfFace [out] glTF face definition
   //! @param theBinFile  [out] output file to write into
   //! @param theFaceIter [in]  current face to write
   //! @param theAccessorNb [in] [out] last accessor index
+  //! @param theMesh [in] [out] mesh
   Standard_EXPORT virtual void saveIndices (RWGltf_GltfFace& theGltfFace,
                                             std::ostream& theBinFile,
                                             const RWMesh_FaceIterator& theFaceIter,
-                                            Standard_Integer& theAccessorNb);
+                                            Standard_Integer& theAccessorNb,
+                                            const std::shared_ptr<RWGltf_CafWriter::Mesh>& theMesh);
 
 protected:
 
@@ -280,9 +305,11 @@ protected:
   //! Write a primitive array to RWGltf_GltfRootElement_Meshes section.
   //! @param[in]     theGltfFace     face to write
   //! @param[in]     theName         primitive array name
+  //! @param[in]     theDracoBufInd  draco buffer index 
   //! @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,
+                                               const int theDracoBufInd,
                                                bool& theToStartPrims);
 
   //! Write RWGltf_GltfRootElement_Nodes section.
@@ -369,6 +396,8 @@ protected:
   ShapeToGltfFaceMap                            myBinDataMap;        //!< map for TopoDS_Face to glTF face (merging duplicates)
   int64_t                                       myBinDataLen64;      //!< length of binary file
 
+  std::vector<RWGltf_GltfBufferView>            myBuffViewsDraco;    //!< vector of buffers view with compression data
+  RWGltf_DracoParameters                        myDracoParameters;   //!< Draco parameters
 };
 
 #endif // _RWGltf_CafWriter_HeaderFiler
diff --git a/src/RWGltf/RWGltf_DracoParameters.hxx b/src/RWGltf/RWGltf_DracoParameters.hxx
new file mode 100644 (file)
index 0000000..7e3f483
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright (c) 2022 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 _RWGltf_DracoParameters_HeaderFile
+#define _RWGltf_DracoParameters_HeaderFile
+
+//! Draco compression parameters
+struct RWGltf_DracoParameters
+{
+  RWGltf_DracoParameters()
+  : DracoCompression (false),
+    CompressionLevel (7),
+    QuantizePositionBits (14),
+    QuantizeNormalBits   (10),
+    QuantizeTexcoordBits (12),
+    QuantizeColorBits    (8),
+    QuantizeGenericBits  (12),
+    UnifiedQuantization (false)
+  {}
+
+  bool DracoCompression;    //!< flag to use Draco compression (FALSE by default). If it is TRUE, compression is used
+  int CompressionLevel;     //!< Draco compression level [0-10] (7 by default)
+  int QuantizePositionBits; //!< quantization bits for position attribute (14 by default)
+  int QuantizeNormalBits;   //!< quantization bits for normal attribute (10 by default)
+  int QuantizeTexcoordBits; //!< quantization bits for texture coordinate attribute (12 by default)
+  int QuantizeColorBits;    //!< quantization bits for color attributes (8 by default)
+  int QuantizeGenericBits;  //!< quantization bits for skinning and custom attributes (12 by default)
+  bool UnifiedQuantization; //!< quantize positions of all primitives using the same quantization grid (FALSE by default)
+};
+
+#endif
index 5abe0d7b8763ec93b48a076423e7e77ab46d2a4d..7976a23ecb8aa0b9caf23dc1c0e11bc85ae95d03 100644 (file)
@@ -44,6 +44,7 @@
 #include <Quantity_Color.hxx>
 #include <Quantity_HArray1OfColor.hxx>
 #include <Quantity_NameOfColor.hxx>
+#include <RWGltf_DracoParameters.hxx>
 #include <RWGltf_CafReader.hxx>
 #include <RWGltf_CafWriter.hxx>
 #include <RWMesh_FaceIterator.hxx>
@@ -384,6 +385,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
   bool toMergeFaces = false, toSplitIndices16 = false;
   RWMesh_NameFormat aNodeNameFormat = RWMesh_NameFormat_InstanceOrProduct;
   RWMesh_NameFormat aMeshNameFormat = RWMesh_NameFormat_Product;
+  RWGltf_DracoParameters aDracoParameters;
   for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
   {
     TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
@@ -516,6 +518,44 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
     {
       toEmbedTexturesInGlb = false;
     }
+    else if (anArgCase == "-draco")
+    {
+      aDracoParameters.DracoCompression = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
+    }
+    else if (anArgCase == "-compressionlevel" && (anArgIter + 1) < theNbArgs
+             && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.CompressionLevel))
+    {
+      ++anArgIter;
+    }
+    else if (anArgCase == "-quantizepositionbits" && (anArgIter + 1) < theNbArgs
+             && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizePositionBits))
+    {
+      ++anArgIter;
+    }
+    else if (anArgCase == "-quantizenormalbits" && (anArgIter + 1) < theNbArgs
+             && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeNormalBits))
+    {
+      ++anArgIter;
+    }
+    else if (anArgCase == "-quantizetexcoordbits" && (anArgIter + 1) < theNbArgs
+             && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeTexcoordBits))
+    {
+      ++anArgIter;
+    }
+    else if (anArgCase == "-quantizecolorbits" && (anArgIter + 1) < theNbArgs
+             && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeColorBits))
+    {
+      ++anArgIter;
+    }
+    else if (anArgCase == "-quantizegenericbits" && (anArgIter + 1) < theNbArgs
+             && Draw::ParseInteger(theArgVec[anArgIter + 1], aDracoParameters.QuantizeGenericBits))
+    {
+      ++anArgIter;
+    }
+    else if (anArgCase == "-unifiedquantization")
+    {
+      aDracoParameters.UnifiedQuantization = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
+    }
     else
     {
       Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'";
@@ -547,6 +587,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
   aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb);
   aWriter.SetMergeFaces (toMergeFaces);
   aWriter.SetSplitIndices16 (toSplitIndices16);
+  aWriter.SetCompressionParameters(aDracoParameters);
   aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aScaleFactorM);
   aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (aSystemCoordSys);
   aWriter.Perform (aDoc, aFileInfo, aProgress->Start());
@@ -2361,20 +2402,32 @@ void  XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
                    __FILE__, ReadGltf, g);
   theCommands.Add ("WriteGltf",
                    "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]=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:   -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",
+                   "\n\t\t:            [-systemCoordSys {Zup|Yup}]=Zup"
+                   "\n\t\t:            [-comments Text] [-author Name]"
+                   "\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:            [-draco]=0 [-compressionLevel {0-10}]=7 [-quantizePositionBits Value]=14 [-quantizeNormalBits Value]=10"
+                   "\n\t\t:            [-quantizeTexcoordBits Value]=12 [-quantizeColorBits Value]=8 [-quantizeGenericBits Value]=12"
+                   "\n\t\t:            [-unifiedQuantization]=0"
+                   "\n\t\t: Write XDE document into glTF file."
+                   "\n\t\t:   -trsfFormat       preferred transformation format"
+                   "\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"
+                   "\n\t\t:   -draco use Draco  compression 3D geometric meshes"
+                   "\n\t\t:   -compressionLevel draco compression level [0-10] (by default 7), a value of 0 will apply sequential encoding and preserve face order"
+                   "\n\t\t:   -quantizePositionBits quantization bits for position attribute when using Draco compression (by default 14)"
+                   "\n\t\t:   -quantizeNormalBits   quantization bits for normal attribute when using Draco compression (by default 10)"
+                   "\n\t\t:   -quantizeTexcoordBits quantization bits for texture coordinate attribute when using Draco compression (by default 12)"
+                   "\n\t\t:   -quantizeColorBits    quantization bits for color attribute when using Draco compression (by default 8)"
+                   "\n\t\t:   -quantizeGenericBits  quantization bits for skinning attribute (joint indices and joint weights)"
+                   "\n                        and custom attributes when using Draco compression (by default 12)"
+                   "\n\t\t:   -unifiedQuantization  quantization is applied on each primitive separately if this option is false",
                    __FILE__, WriteGltf, g);
   theCommands.Add ("writegltf",
                    "writegltf shape file",
diff --git a/tests/de_mesh/gltf_write/010 b/tests/de_mesh/gltf_write/010
new file mode 100644 (file)
index 0000000..b9b5d1b
--- /dev/null
@@ -0,0 +1,16 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+Close D0 -silent
+ReadGltf D0 [locate_data_file bug32867_010.glb]
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on -mergefaces
+WriteGltf D0 "$aGltfFile2" -draco on
+
+ReadGltf D1 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
diff --git a/tests/de_mesh/gltf_write/Diamond b/tests/de_mesh/gltf_write/Diamond
new file mode 100644 (file)
index 0000000..f020c33
--- /dev/null
@@ -0,0 +1,16 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+Close D0 -silent
+ReadGltf D0 [locate_data_file bug32867_Diamond.glb]
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on
+WriteGltf D0 "$aGltfFile2" -draco on -mergefaces
+
+ReadGltf D1 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
diff --git a/tests/de_mesh/gltf_write/as1draco b/tests/de_mesh/gltf_write/as1draco
new file mode 100644 (file)
index 0000000..f07a25a
--- /dev/null
@@ -0,0 +1,18 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+Close D0 -silent
+ReadStep D0 [locate_data_file as1-oc-214-mat.stp]
+XGetOneShape ss D0
+incmesh ss 1.0
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on
+WriteGltf D0 "$aGltfFile2" -draco on -mergefaces
+
+ReadGltf D1 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
diff --git a/tests/de_mesh/gltf_write/bearing b/tests/de_mesh/gltf_write/bearing
new file mode 100644 (file)
index 0000000..46d8ae6
--- /dev/null
@@ -0,0 +1,16 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+restore [locate_data_file bearing.brep] b
+incmesh b 0.1
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf b "$aGltfFile1" -draco on
+WriteGltf b "$aGltfFile2" -draco on -mergefaces
+
+ReadGltf D0 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
diff --git a/tests/de_mesh/gltf_write/bull b/tests/de_mesh/gltf_write/bull
new file mode 100644 (file)
index 0000000..5d7de77
--- /dev/null
@@ -0,0 +1,16 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+Close D0 -silent
+ReadGltf D0 [locate_data_file bug32867_bull.glb]
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on
+WriteGltf D0 "$aGltfFile2" -draco on -mergefaces
+
+ReadGltf D1 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
diff --git a/tests/de_mesh/gltf_write/screw b/tests/de_mesh/gltf_write/screw
new file mode 100644 (file)
index 0000000..fa7bee5
--- /dev/null
@@ -0,0 +1,18 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+Close D0 -silent
+ReadStep D0 [locate_data_file screw.step]
+XGetOneShape ss D0
+incmesh ss 1.0
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on -mergefaces
+WriteGltf D0 "$aGltfFile2" -draco on
+
+ReadGltf D1 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
\ No newline at end of file
diff --git a/tests/de_mesh/gltf_write/soapbox b/tests/de_mesh/gltf_write/soapbox
new file mode 100644 (file)
index 0000000..2ec1901
--- /dev/null
@@ -0,0 +1,17 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+ReadStep D0 [locate_data_file ec_soapbox-A.stp]
+XGetOneShape ss D0
+incmesh ss 1.0
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on
+WriteGltf D0 "$aGltfFile2" -draco on -mergefaces
+
+ReadGltf D1 "$aGltfFile1"
+ReadGltf D "$aGltfFile2"
\ No newline at end of file
diff --git a/tests/de_mesh/gltf_write/test b/tests/de_mesh/gltf_write/test
new file mode 100644 (file)
index 0000000..708cfe0
--- /dev/null
@@ -0,0 +1,20 @@
+puts "========"
+puts "0032867: Data Exchange - Implement Draco compression for writing glTF"
+puts "Test case exporting model into glb (binary glTF) file."
+puts "========"
+
+Close D0 -silent
+ReadGltf D0 [locate_data_file bug32867_test.glb]
+
+set aGltfFile1 "${imagedir}/${casename}_tmp1.glb"
+set aGltfFile2 "${imagedir}/${casename}_tmp2.glb"
+
+WriteGltf D0 "$aGltfFile1" -draco on
+WriteGltf D0 "$aGltfFile2" -draco on -mergefaces
+
+ReadGltf D1 "$aGltfFile1"
+XGetOneShape s1 D1
+checktrinfo s1 -tri 9366
+ReadGltf D "$aGltfFile2"
+XGetOneShape s2 D
+checktrinfo s2 -tri 9366