0032525: Data Exchange, RWGltf_CafReader - support KHR_draco_mesh_compression
authorkgv <kgv@opencascade.com>
Tue, 10 Aug 2021 17:35:14 +0000 (20:35 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 12 Aug 2021 16:03:55 +0000 (19:03 +0300)
Added new optional dependency - Draco library.
RWGltf_GltfJsonParser now detects KHR_draco_mesh_compression extension,
marks accessor being compressed and redirects to compressed buffer view.
RWGltf_TriangulationReader now handles decoding of buffer view compressed using Draco.

env.bat template for genproj has been modified to allow specifying dedicated folders
with debug versions of libraries (CSF_OPT_LIB64D / CSF_OPT_BIN64D) within custom.bat.
Removed unused CSF_FREETYPE from TKOpenGl.

20 files changed:
CMakeLists.txt
adm/cmake/draco.cmake [new file with mode: 0644]
adm/cmake/occt_csf.cmake
adm/cmake/vardescr.cmake
adm/genconf.tcl
adm/genconfdeps.tcl
adm/genproj.tcl
adm/templates/env.bat
adm/templates/env.sh
dox/introduction/introduction.md
src/Draw/Draw_BasicCommands.cxx
src/RWGltf/RWGltf_GltfAccessor.hxx
src/RWGltf/RWGltf_GltfJsonParser.cxx
src/RWGltf/RWGltf_GltfJsonParser.hxx
src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx
src/RWGltf/RWGltf_GltfPrimArrayData.hxx
src/RWGltf/RWGltf_TriangulationReader.cxx
src/RWGltf/RWGltf_TriangulationReader.hxx
src/TKOpenGl/EXTERNLIB
src/TKRWMesh/EXTERNLIB

index 7eee96b..43440b6 100644 (file)
@@ -370,6 +370,7 @@ set (USE_FREEIMAGE OFF CACHE BOOL "${USE_FREEIMAGE_DESCR}")
 set (USE_FFMPEG    OFF CACHE BOOL "${USE_FFMPEG_DESCR}")
 set (USE_OPENVR    OFF CACHE BOOL "${USE_OPENVR_DESCR}")
 set (USE_RAPIDJSON OFF CACHE BOOL "${USE_RAPIDJSON_DESCR}")
+set (USE_DRACO     OFF CACHE BOOL "${USE_DRACO_DESCR}")
 set (USE_TBB       OFF CACHE BOOL "${USE_TBB_DESCR}")
 set (USE_EIGEN     OFF CACHE BOOL "${USE_EIGEN_DESCR}")
 
@@ -716,6 +717,24 @@ else()
   OCCT_CHECK_AND_UNSET ("INSTALL_RAPIDJSON")
 endif()
 
+# Draco library
+# search for CSF_Draco variable in EXTERNLIB of each being used toolkit
+OCCT_IS_PRODUCT_REQUIRED (CSF_Draco CAN_USE_DRACO)
+if (CAN_USE_DRACO)
+  if (USE_DRACO)
+    add_definitions (-DHAVE_DRACO)
+    OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/draco")
+  else()
+    OCCT_CHECK_AND_UNSET_GROUP ("3RDPARTY_DRACO")
+    OCCT_CHECK_AND_UNSET ("INSTALL_DRACO")
+  endif()
+else()
+  OCCT_CHECK_AND_UNSET ("USE_DRACO")
+
+  OCCT_CHECK_AND_UNSET_GROUP ("3RDPARTY_DRACO")
+  OCCT_CHECK_AND_UNSET ("INSTALL_DRACO")
+endif()
+
 # EIGEN
 if (CAN_USE_EIGEN)
   if (USE_EIGEN)
diff --git a/adm/cmake/draco.cmake b/adm/cmake/draco.cmake
new file mode 100644 (file)
index 0000000..53cf90a
--- /dev/null
@@ -0,0 +1,4 @@
+# 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" "")
index 6cf7231..bee8ba7 100644 (file)
@@ -75,6 +75,13 @@ if (USE_TK)
   endif()
 endif()
 
+# Draco
+if (USE_DRACO)
+  set (CSF_Draco "draco")
+else()
+  set (CSF_Draco)
+endif()
+
 if (WIN32)
   set (CSF_advapi32      "advapi32.lib")
   set (CSF_gdi32         "gdi32.lib")
index 9dffc9f..06dab5b 100644 (file)
@@ -176,6 +176,9 @@ set (USE_RAPIDJSON_DESCR
 "Indicates whether RapidJSON product should be used in OCCT DataExchange
 module for support of JSON-based formats like glTF")
 
+set (USE_DRACO_DESCR
+"Indicates whether Draco mesh decoding library should be used by glTF reader")
+
 set (USE_EGL_DESCR
 "Indicates whether EGL should be used in OCCT visualization
 module instead of conventional OpenGL context creation APIs")
index d7ab6c8..d866a74 100644 (file)
@@ -227,6 +227,10 @@ proc wokdep:gui:UpdateList {} {
   if { "$::HAVE_RAPIDJSON" == "true" } {
     wokdep:SearchRapidJson anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
   }
+  if { "$::HAVE_DRACO" == "true" } {
+    set aDummy {}
+    wokdep:SearchStandardLibrary  anIncErrs anLib32Errs anLib64Errs aDummy aDummy "draco" "draco/compression/decode.h" "draco" {"draco"}
+  }
 
   if {"$::BUILD_Inspector" == "true" } {
     set ::CHECK_QT "true"
@@ -495,6 +499,8 @@ ttk::label    .myFrame.myChecks.myFFmpegLbl     -text "Use FFmpeg"
 #ttk::label    .myFrame.myChecks.myOpenClLbl     -text "Use OpenCL"
 checkbutton   .myFrame.myChecks.myRapidJsonCheck -offvalue "false" -onvalue "true" -variable HAVE_RAPIDJSON -command wokdep:gui:UpdateList
 ttk::label    .myFrame.myChecks.myRapidJsonLbl   -text "Use RapidJSON"
+checkbutton   .myFrame.myChecks.myDracoCheck    -offvalue "false" -onvalue "true" -variable HAVE_DRACO     -command wokdep:gui:UpdateList
+ttk::label    .myFrame.myChecks.myDracoLbl      -text "Use Draco"
 
 checkbutton   .myFrame.myChecks.myXLibCheck     -offvalue "false" -onvalue "true" -variable HAVE_XLIB
 ttk::label    .myFrame.myChecks.myXLibLbl       -text "Use X11 for windows drawing"
@@ -627,8 +633,9 @@ grid .myFrame.myChecks.myQtLbl        -row $aCheckRowIter -column 13 -sticky w
 incr aCheckRowIter
 grid .myFrame.myChecks.myFImageCheck   -row $aCheckRowIter -column 0 -sticky e
 grid .myFrame.myChecks.myFImageLbl     -row $aCheckRowIter -column 1 -sticky w
-grid .myFrame.myChecks.myTbbCheck      -row $aCheckRowIter -column 2 -sticky e
-grid .myFrame.myChecks.myTbbLbl        -row $aCheckRowIter -column 3 -sticky w
+grid .myFrame.myChecks.myDracoCheck    -row $aCheckRowIter -column 2 -sticky e
+grid .myFrame.myChecks.myDracoLbl      -row $aCheckRowIter -column 3 -sticky w
+
 if { "$::tcl_platform(platform)" == "windows" } {
   grid .myFrame.myChecks.myD3dCheck    -row $aCheckRowIter -column 4 -sticky e
   grid .myFrame.myChecks.myD3dLbl      -row $aCheckRowIter -column 5 -sticky w
@@ -658,6 +665,11 @@ if { "$::tcl_platform(platform)" == "windows" } {
 
 incr aCheckRowIter
 
+grid .myFrame.myChecks.myTbbCheck      -row $aCheckRowIter -column 12 -sticky e
+grid .myFrame.myChecks.myTbbLbl        -row $aCheckRowIter -column 13 -sticky w
+
+incr aCheckRowIter
+
 # Additional headers search paths
 grid .myFrame.myIncLbl    -row $aRowIter -column 0 -columnspan 10 -sticky w
 incr aRowIter
index 644a189..10ef197 100644 (file)
@@ -68,7 +68,10 @@ if { [info exists ::env(SHORTCUT_HEADERS)] } {
 }
 
 # fetch environment variables (e.g. set by custom.sh or custom.bat) and set them as tcl variables with the same name
-set THE_ENV_VARIABLES {HAVE_TK HAVE_FREETYPE HAVE_FREEIMAGE HAVE_FFMPEG HAVE_TBB HAVE_GLES2 HAVE_D3D HAVE_VTK HAVE_ZLIB HAVE_LIBLZMA HAVE_E57 HAVE_RAPIDJSON HAVE_OPENVR HAVE_OPENCL CHECK_QT4 CHECK_JDK HAVE_XLIB HAVE_RelWithDebInfo BUILD_Inspector}
+set THE_ENV_VARIABLES { HAVE_TK HAVE_FREETYPE HAVE_FREEIMAGE HAVE_FFMPEG HAVE_TBB HAVE_GLES2 HAVE_D3D HAVE_VTK \
+  HAVE_ZLIB HAVE_LIBLZMA HAVE_E57 HAVE_RAPIDJSON HAVE_DRACO HAVE_OPENVR HAVE_OPENCL \
+  CHECK_QT4 CHECK_JDK HAVE_XLIB \
+  HAVE_RelWithDebInfo BUILD_Inspector }
 foreach anEnvIter $THE_ENV_VARIABLES { set ${anEnvIter} "false" }
 set HAVE_TK       "true"
 set HAVE_FREETYPE "true"
index 04f9c20..ce70ef7 100644 (file)
@@ -1447,6 +1447,9 @@ proc osutils:csfList { theOS theCsfLibsMap theCsfFrmsMap theRelease} {
   if { "$::HAVE_LIBLZMA" == "true" } {
     set aLibsMap(CSF_LIBLZMA) "liblzma"
   }
+  if { "$::HAVE_DRACO" == "true" } {
+    set aLibsMap(CSF_Draco) "draco"
+  }
   if { "$::HAVE_OPENVR" == "true" } {
     set aLibsMap(CSF_OpenVR) "openvr_api"
   }
index 78dfa0e..a02bea5 100644 (file)
@@ -27,6 +27,7 @@ set "HAVE_D3D=false"
 set "HAVE_ZLIB=false"
 set "HAVE_LIBLZMA=false"
 set "HAVE_RAPIDJSON=false"
+set "HAVE_DRACO=false"
 set "HAVE_OPENVR=false"
 set "HAVE_E57=false"
 set "CSF_OPT_INC="
@@ -34,6 +35,14 @@ set "CSF_OPT_LIB32="
 set "CSF_OPT_LIB64="
 set "CSF_OPT_BIN32="
 set "CSF_OPT_BIN64="
+set "CSF_OPT_LIB32D="
+set "CSF_OPT_LIB64D="
+set "CSF_OPT_BIN32D="
+set "CSF_OPT_BIN64D="
+set "CSF_OPT_LIB32I="
+set "CSF_OPT_LIB64I="
+set "CSF_OPT_BIN32I="
+set "CSF_OPT_BIN64I="
 set "CSF_DEFINES=%CSF_DEFINES_EXTRA%"
 
 if not ["%CASROOT%"] == [""] if exist "%SCRIPTROOT%\%CASROOT%" set "CASROOT=%SCRIPTROOT%\%CASROOT%"
@@ -170,14 +179,14 @@ if /I "%VCFMT%" == "vc9" (
   exit /B
 )
 
-set "CSF_OPT_LIB32D=%CSF_OPT_LIB32%"
-set "CSF_OPT_LIB64D=%CSF_OPT_LIB64%"
-set "CSF_OPT_BIN32D=%CSF_OPT_BIN32%"
-set "CSF_OPT_BIN64D=%CSF_OPT_BIN64%"
-set "CSF_OPT_LIB32I=%CSF_OPT_LIB32%"
-set "CSF_OPT_LIB64I=%CSF_OPT_LIB64%"
-set "CSF_OPT_BIN32I=%CSF_OPT_BIN32%"
-set "CSF_OPT_BIN64I=%CSF_OPT_BIN64%"
+if ["%CSF_OPT_LIB32D%"] == [""] set "CSF_OPT_LIB32D=%CSF_OPT_LIB32%"
+if ["%CSF_OPT_LIB64D%"] == [""] set "CSF_OPT_LIB64D=%CSF_OPT_LIB64%"
+if ["%CSF_OPT_BIN32D%"] == [""] set "CSF_OPT_BIN32D=%CSF_OPT_BIN32%"
+if ["%CSF_OPT_BIN64D%"] == [""] set "CSF_OPT_BIN64D=%CSF_OPT_BIN64%"
+if ["%CSF_OPT_LIB32I%"] == [""] set "CSF_OPT_LIB32I=%CSF_OPT_LIB32%"
+if ["%CSF_OPT_LIB64I%"] == [""] set "CSF_OPT_LIB64I=%CSF_OPT_LIB64%"
+if ["%CSF_OPT_BIN32I%"] == [""] set "CSF_OPT_BIN32I=%CSF_OPT_BIN32%"
+if ["%CSF_OPT_BIN64I%"] == [""] set "CSF_OPT_BIN64I=%CSF_OPT_BIN64%"
 
 rem ----- Optional 3rd-parties should be enabled by HAVE macros -----
 set "CSF_OPT_CMPL="
@@ -194,6 +203,7 @@ if ["%HAVE_D3D%"]       == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DH
 if ["%HAVE_ZLIB%"]      == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_ZLIB"      & set "CSF_DEFINES=HAVE_ZLIB;%CSF_DEFINES%"
 if ["%HAVE_LIBLZMA%"]   == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_LIBLZMA"   & set "CSF_DEFINES=HAVE_LIBLZMA;%CSF_DEFINES%"
 if ["%HAVE_RAPIDJSON%"] == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_RAPIDJSON" & set "CSF_DEFINES=HAVE_RAPIDJSON;%CSF_DEFINES%"
+if ["%HAVE_DRACO%"]     == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_DRACO"     & set "CSF_DEFINES=HAVE_DRACO;%CSF_DEFINES%"
 if ["%HAVE_OPENVR%"]    == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_OPENVR"    & set "CSF_DEFINES=HAVE_OPENVR;%CSF_DEFINES%"
 if ["%HAVE_E57%"]       == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_E57"       & set "CSF_DEFINES=HAVE_E57;%CSF_DEFINES%"
 
index b0f29c0..4ab363e 100644 (file)
@@ -20,6 +20,7 @@ export HAVE_GLES2="false";
 export HAVE_ZLIB="false";
 export HAVE_LIBLZMA="false";
 export HAVE_RAPIDJSON="false";
+export HAVE_DRACO="false";
 export HAVE_OPENVR="false";
 export HAVE_E57="false";
 export HAVE_XLIB="true";
@@ -115,6 +116,7 @@ if [ "$HAVE_VTK"       == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -D
 if [ "$HAVE_ZLIB"      == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_ZLIB"; fi
 if [ "$HAVE_LIBLZMA"   == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_LIBLZMA"; fi
 if [ "$HAVE_RAPIDJSON" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_RAPIDJSON"; fi
+if [ "$HAVE_DRACO"     == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_DRACO"; fi
 if [ "$HAVE_OPENVR"    == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_OPENVR"; fi
 if [ "$HAVE_E57"       == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_E57"; fi
 if [ "$HAVE_XLIB"      == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_XLIB"; fi
index c32ef17..ae4ce84 100644 (file)
@@ -370,6 +370,7 @@ https://www.opencascade.com/content/3rd-party-components
 | VTK 6.1+ | https://www.vtk.org/download/ | Visualization | Optional (VTK integration) |
 | Flex 2.6.4+ and Bison 3.7.1+ | https://sourceforge.net/projects/winflexbison/ | Data Exchange | Optional (update of STEP and ExprIntrp parsers) |
 | RapidJSON 1.1+ | https://rapidjson.org/ | Data Exchange | Optional (reading glTF files) |
+| Draco 1.4.1+ | https://github.com/google/draco | Data Exchange | Optional (reading compressed glTF files) |
 | Tcl/Tk 8.6.3+ <br> or ActiveTcl 8.6 | https://www.tcl.tk/software/tcltk/download.html <br> https://www.activestate.com/activetcl/downloads | DRAW Test Harness | Required |
 | Qt Desktop: Qt 4.8.6+ <br> Android: Qt 5.3.2+ | https://www.qt.io/download/ | Samples and demos | Optional (Qt samples) |
 | Doxygen 1.8.5+ | https://www.doxygen.nl/download.html | Documentation | Required |
@@ -642,6 +643,10 @@ on this tool.
 **RapidJSON** is an Open Source JSON parser and generator for C++.
 RapidJSON is optionally used by OCCT for reading glTF files (https://rapidjson.org/).
 
+**Draco** is an Open Source JSON parser and generator for C++.
+Draco is optionally used by OCCT for reading glTF files using KHR_draco_mesh_compression extension (https://github.com/google/draco).
+Draco is available under Apache 2.0 license.
+
 **DejaVu** fonts are a font family based on the Vera Fonts under a permissive license (MIT-like, https://dejavu-fonts.github.io/License.html).
 DejaVu Sans (basic Latin sub-set) is used by OCCT as fallback font when no system font is available.
 
index 9c2b6c1..0f8dd40 100644 (file)
@@ -389,6 +389,11 @@ static Standard_Integer dversion(Draw_Interpretor& di, Standard_Integer, const c
 #else
   di << "RapidJSON disabled\n";
 #endif
+#ifdef HAVE_DRACO
+  di << "Draco enabled (HAVE_DRACO)\n";
+#else
+  di << "Draco disabled\n";
+#endif
 #ifdef HAVE_VTK
   di << "VTK enabled (HAVE_VTK)\n";
 #else
index ced6790..43087a0 100644 (file)
@@ -33,6 +33,7 @@ public:
   RWGltf_GltfAccessorLayout   Type;          //!< layout type
   RWGltf_GltfAccessorCompType ComponentType; //!< component type
   Graphic3d_BndBox3d          BndBox;        //!< bounding box
+  bool                        IsCompressed;  //!< flag indicating KHR_draco_mesh_compression
 
   //! Empty constructor.
   RWGltf_GltfAccessor()
@@ -41,7 +42,8 @@ public:
     Count (0),
     ByteStride (0),
     Type (RWGltf_GltfAccessorLayout_UNKNOWN),
-    ComponentType (RWGltf_GltfAccessorCompType_UNKNOWN) {}
+    ComponentType (RWGltf_GltfAccessorCompType_UNKNOWN),
+    IsCompressed (false) {}
 
 };
 
index 895718d..c082c5c 100644 (file)
@@ -35,8 +35,9 @@
 namespace
 {
   //! Material extension.
-  const char THE_KHR_materials_common[] = "KHR_materials_common";
-  const char THE_KHR_binary_glTF[]      = "KHR_binary_glTF";
+  static const char THE_KHR_materials_common[] = "KHR_materials_common";
+  static const char THE_KHR_binary_glTF[]      = "KHR_binary_glTF";
+  static const char THE_KHR_draco_mesh_compression[] = "KHR_draco_mesh_compression";
 
   //! Data buffer referring to a portion of another buffer.
   class RWGltf_SubBuffer : public NCollection_Buffer
@@ -1404,6 +1405,14 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrim
   const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
   const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
   const RWGltf_JsonValue* aModeVal  = findObjectMember (thePrimArray, "mode");
+  const RWGltf_JsonValue* anExtVal  = findObjectMember (thePrimArray, "extensions");
+  const RWGltf_JsonValue* aDracoVal = anExtVal != NULL
+                                    ? findObjectMember (*anExtVal, THE_KHR_draco_mesh_compression)
+                                    : NULL;
+  const RWGltf_JsonValue* aDracoBuf = aDracoVal != NULL
+                                    ? findObjectMember (*aDracoVal, "bufferView")
+                                    : NULL;
+
   RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
   if (anAttribs == NULL
   || !anAttribs->IsObject())
@@ -1473,7 +1482,7 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrim
       reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
       return false;
     }
-    else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
+    else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType, aDracoBuf))
     {
       return false;
     }
@@ -1498,7 +1507,7 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrim
       reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
       return false;
     }
-    else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
+    else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices, aDracoBuf))
     {
       return false;
     }
@@ -1518,12 +1527,17 @@ bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrim
 bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
                                                const TCollection_AsciiString& theName,
                                                const RWGltf_JsonValue& theAccessor,
-                                               const RWGltf_GltfArrayType theType)
+                                               const RWGltf_GltfArrayType theType,
+                                               const RWGltf_JsonValue* theCompBuffView)
 {
   RWGltf_GltfAccessor aStruct;
   const RWGltf_JsonValue* aTypeStr        = findObjectMember (theAccessor, "type");
-  const RWGltf_JsonValue* aBufferViewName = findObjectMember (theAccessor, "bufferView");
-  const RWGltf_JsonValue* aByteOffset     = findObjectMember (theAccessor, "byteOffset");
+  const RWGltf_JsonValue* aBufferViewName = theCompBuffView == NULL
+                                          ? findObjectMember (theAccessor, "bufferView")
+                                          : theCompBuffView;
+  const RWGltf_JsonValue* aByteOffset     = theCompBuffView == NULL
+                                          ? findObjectMember (theAccessor, "byteOffset")
+                                          : 0;
   const RWGltf_JsonValue* aByteStride     = findObjectMember (theAccessor, "byteStride"); // byteStride was part of bufferView in glTF 1.0
   const RWGltf_JsonValue* aCompType       = findObjectMember (theAccessor, "componentType");
   const RWGltf_JsonValue* aCount          = findObjectMember (theAccessor, "count");
@@ -1534,6 +1548,7 @@ bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimi
     return false;
   }
   aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
+  aStruct.IsCompressed = theCompBuffView != NULL;
   if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
   {
     reportGltfError ("Accessor '" + theName + "' has invalid type.");
@@ -1767,6 +1782,7 @@ bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimiti
     aData.Accessor = theAccessor;
     aData.Accessor.ByteStride = aByteStride;
     aData.StreamOffset = anOffset;
+    aData.StreamLength = theView.ByteLength;
     aData.StreamUri = myFilePath;
     return true;
   }
@@ -1785,6 +1801,7 @@ bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimiti
     aData.Accessor = theAccessor;
     aData.Accessor.ByteStride = aByteStride;
     aData.StreamOffset = anOffset;
+    aData.StreamLength = 0;
     if (!myDecodedBuffers.Find (theName, aData.StreamData))
     {
       // it is better decoding in multiple threads
@@ -1819,6 +1836,7 @@ bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimiti
     aData.Accessor = theAccessor;
     aData.Accessor.ByteStride = aByteStride;
     aData.StreamOffset = anOffset;
+    aData.StreamLength = theView.ByteLength;
     aData.StreamUri = myFolder + anUri;
     if (myExternalFiles != NULL)
     {
index 8ba7ab5..b285932 100644 (file)
@@ -196,7 +196,8 @@ protected:
   Standard_EXPORT bool gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
                                           const TCollection_AsciiString& theName,
                                           const RWGltf_JsonValue& theAccessor,
-                                          const RWGltf_GltfArrayType theType);
+                                          const RWGltf_GltfArrayType theType,
+                                          const RWGltf_JsonValue* theCompBuffView);
 
   //! Parse buffer view.
   Standard_EXPORT bool gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
index 1c39f41..10e2d29 100644 (file)
@@ -78,7 +78,7 @@ public:
   //! Add primitive array data element.
   Standard_EXPORT RWGltf_GltfPrimArrayData& AddPrimArrayData (RWGltf_GltfArrayType theType);
 
-  //! Return TRUE if there is deferred storege and some triangulation data
+  //! Return TRUE if there is deferred storage and some triangulation data
   //! that can be loaded using LoadDeferredData().
   virtual Standard_Boolean HasDeferredData() const Standard_OVERRIDE
   {
index 752f320..18cbac7 100644 (file)
@@ -27,15 +27,16 @@ public:
   Handle(NCollection_Buffer) StreamData;
   TCollection_AsciiString    StreamUri;
   int64_t                    StreamOffset;
+  int64_t                    StreamLength;
 
   RWGltf_GltfAccessor        Accessor;
   RWGltf_GltfArrayType       Type;
 
   RWGltf_GltfPrimArrayData()
-  : StreamOffset (0), Type (RWGltf_GltfArrayType_UNKNOWN) {}
+  : StreamOffset (0), StreamLength (0), Type (RWGltf_GltfArrayType_UNKNOWN) {}
 
   RWGltf_GltfPrimArrayData (RWGltf_GltfArrayType theType)
-  : StreamOffset (0), Type (theType) {}
+  : StreamOffset (0), StreamLength (0), Type (theType) {}
 };
 
 #endif // _RWGltf_GltfPrimArrayData_HeaderFile
index 09fbf6d..d4dff57 100644 (file)
 #include <Standard_ArrayStreamBuffer.hxx>
 #include <Standard_ReadBuffer.hxx>
 
+#ifdef HAVE_DRACO
+  #include <draco/compression/decode.h>
+#endif
+
 namespace
 {
   static const Standard_Integer   THE_LOWER_TRI_INDEX  = 1;
   static const Standard_Integer   THE_LOWER_NODE_INDEX = 1;
   static const Standard_ShortReal THE_NORMAL_PREC2 = 0.001f;
+
+#ifdef HAVE_DRACO
+  //! Return array type from Draco attribute type.
+  static RWGltf_GltfArrayType arrayTypeFromDraco (draco::GeometryAttribute::Type theType)
+  {
+    switch (theType)
+    {
+      case draco::GeometryAttribute::POSITION:  return RWGltf_GltfArrayType_Position;
+      case draco::GeometryAttribute::NORMAL:    return RWGltf_GltfArrayType_Normal;
+      case draco::GeometryAttribute::COLOR:     return RWGltf_GltfArrayType_Color;
+      case draco::GeometryAttribute::TEX_COORD: return RWGltf_GltfArrayType_TCoord0;
+      default:                                  return RWGltf_GltfArrayType_UNKNOWN;
+    }
+  }
+
+  //! Return layout from Draco number of components.
+  static RWGltf_GltfAccessorLayout layoutFromDraco (int8_t theNbComps)
+  {
+    switch (theNbComps)
+    {
+      case 1: return RWGltf_GltfAccessorLayout_Scalar;
+      case 2: return RWGltf_GltfAccessorLayout_Vec2;
+      case 3: return RWGltf_GltfAccessorLayout_Vec3;
+      case 4: return RWGltf_GltfAccessorLayout_Vec4;
+    }
+    return RWGltf_GltfAccessorLayout_UNKNOWN;
+  }
+
+  //! Return component type from Draco data type.
+  static RWGltf_GltfAccessorCompType compTypeFromDraco (draco::DataType theType)
+  {
+    switch (theType)
+    {
+      case draco::DT_INT8:    return RWGltf_GltfAccessorCompType_Int8;
+      case draco::DT_UINT8:   return RWGltf_GltfAccessorCompType_UInt8;
+      case draco::DT_INT16:   return RWGltf_GltfAccessorCompType_Int16;
+      case draco::DT_UINT16:  return RWGltf_GltfAccessorCompType_UInt16;
+      case draco::DT_INT32:
+      case draco::DT_UINT32:  return RWGltf_GltfAccessorCompType_UInt32;
+      //case draco::DT_INT64:
+      //case draco::DT_UINT64:
+      case draco::DT_FLOAT32: return RWGltf_GltfAccessorCompType_Float32;
+      //case draco::DT_FLOAT64:
+      //case draco::DT_BOOL:
+      default:                return RWGltf_GltfAccessorCompType_UNKNOWN;
+    }
+  }
+#endif
 }
 
 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWMesh_TriangulationReader)
@@ -104,9 +156,10 @@ bool RWGltf_TriangulationReader::readFileData (const Handle(RWGltf_GltfLatePrimi
     (theGltfData.StreamUri, std::ios::in | std::ios::binary, theGltfData.StreamOffset);
   if (aSharedStream.get() == NULL)
   {
-    reportError (TCollection_AsciiString("Buffer '") + theSourceGltfMesh->Id() + "refers to invalid file '" + theGltfData.StreamUri + "'.");
+    reportError (TCollection_AsciiString("Buffer '") + theSourceGltfMesh->Id() + "refers to invalid file '" + theGltfData.StreamUri + "'.");
     return false;
   }
+
   if (!readBuffer (theSourceGltfMesh, theDestMesh, *aSharedStream.get(), theGltfData.Accessor, theGltfData.Type))
   {
     return false;
@@ -149,6 +202,218 @@ bool RWGltf_TriangulationReader::loadStreamData (const Handle(RWMesh_Triangulati
   return wasLoaded;
 }
 
+// =======================================================================
+// function : readDracoBuffer
+// purpose  :
+// =======================================================================
+bool RWGltf_TriangulationReader::readDracoBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceGltfMesh,
+                                                  const RWGltf_GltfPrimArrayData& theGltfData,
+                                                  const Handle(Poly_Triangulation)& theDestMesh,
+                                                  const Handle(OSD_FileSystem)& theFileSystem) const
+{
+  const TCollection_AsciiString& aName = theSourceGltfMesh->Id();
+  const Handle(OSD_FileSystem)& aFileSystem = !theFileSystem.IsNull() ? theFileSystem : OSD_FileSystem::DefaultFileSystem();
+  opencascade::std::shared_ptr<std::istream> aSharedStream = aFileSystem->OpenIStream (theGltfData.StreamUri, std::ios::in | std::ios::binary, theGltfData.StreamOffset);
+  if (aSharedStream.get() == NULL)
+  {
+    reportError (TCollection_AsciiString("Buffer '") + aName + "' refers to invalid file '" + theGltfData.StreamUri + "'.");
+    return false;
+  }
+
+#ifdef HAVE_DRACO
+  std::vector<char> aReadData;
+  aReadData.resize (theGltfData.StreamLength);
+  aSharedStream->read (aReadData.data(), (std::streamsize )theGltfData.StreamLength);
+  if (!aSharedStream->good())
+  {
+    reportError (TCollection_AsciiString("Buffer '") + aName + "' refers to file that cannot be read '" + theGltfData.StreamUri + "'.");
+    return false;
+  }
+
+  draco::DecoderBuffer aDracoBuf;
+  aDracoBuf.Init (aReadData.data(), aReadData.size());
+
+  draco::Decoder aDracoDecoder;
+  draco::StatusOr<std::unique_ptr<draco::Mesh>> aDracoStat = aDracoDecoder.DecodeMeshFromBuffer (&aDracoBuf);
+  if (!aDracoStat.ok() || aDracoStat.value().get() == NULL)
+  {
+    reportError (TCollection_AsciiString("Buffer '") + aName + "' refers to Draco data that cannot be decoded '" + theGltfData.StreamUri + "'.");
+    return false;
+  }
+
+  const Standard_Integer aNbNodes = (Standard_Integer )aDracoStat.value()->num_points();
+  const Standard_Integer aNbTris  = (Standard_Integer )aDracoStat.value()->num_faces();
+  if (aNbNodes < 0)
+  {
+    reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines an empty array.");
+    return false;
+  }
+  if ((int64_t )aDracoStat.value()->num_points() > std::numeric_limits<Standard_Integer>::max())
+  {
+    reportError (TCollection_AsciiString ("Buffer '") + aName + "' defines too big array.");
+    return false;
+  }
+  if (!setNbPositionNodes (theDestMesh, aNbNodes))
+  {
+    return false;
+  }
+
+  if (aNbTris > 0
+  && !setNbTriangles (theDestMesh, aNbTris))
+  {
+    return false;
+  }
+
+  // copy vertex attributes
+  for (int32_t anAttrIter = 0; anAttrIter < aDracoStat.value()->num_attributes(); ++anAttrIter)
+  {
+    const draco::PointAttribute*      anAttrib      = aDracoStat.value()->attribute (anAttrIter);
+    const RWGltf_GltfArrayType        aWrapType     = arrayTypeFromDraco(anAttrib->attribute_type());
+    const RWGltf_GltfAccessorLayout   aWrapLayout   = layoutFromDraco   (anAttrib->num_components());
+    const RWGltf_GltfAccessorCompType aWrapCompType = compTypeFromDraco (anAttrib->data_type());
+    switch (aWrapType)
+    {
+      case RWGltf_GltfArrayType_Position:
+      {
+        if (aWrapCompType != RWGltf_GltfAccessorCompType_Float32
+         || aWrapLayout != RWGltf_GltfAccessorLayout_Vec3)
+        {
+          reportError (TCollection_AsciiString ("Buffer '") + aName + "' has unsupported position data type.");
+          return false;
+        }
+
+        for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          const Graphic3d_Vec3* aVec3 = reinterpret_cast<const Graphic3d_Vec3* >(anAttrib->GetAddressOfMappedIndex (draco::PointIndex (aVertIter)));
+          if (aVec3 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
+            return false;
+          }
+
+          gp_Pnt anXYZ (aVec3->x(), aVec3->y(), aVec3->z());
+          myCoordSysConverter.TransformPosition (anXYZ.ChangeCoord());
+          setNodePosition (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, anXYZ);
+        }
+      }
+      case RWGltf_GltfArrayType_Normal:
+      {
+        if (aWrapCompType != RWGltf_GltfAccessorCompType_Float32
+         || aWrapLayout != RWGltf_GltfAccessorLayout_Vec3)
+        {
+          Message::SendTrace (TCollection_AsciiString() + "Vertex normals in unsupported format have been skipped while reading glTF triangulation '" + aName + "'");
+          break;
+        }
+
+        if (!setNbNormalNodes (theDestMesh, aNbNodes))
+        {
+          return false;
+        }
+
+        for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          const Graphic3d_Vec3* aVec3 = reinterpret_cast<const Graphic3d_Vec3* >(anAttrib->GetAddressOfMappedIndex (draco::PointIndex (aVertIter)));
+          if (aVec3 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
+            return false;
+          }
+          if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
+          {
+            Graphic3d_Vec3 aVec3Copy = *aVec3;
+            myCoordSysConverter.TransformNormal (aVec3Copy);
+            setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, aVec3Copy);
+          }
+          else
+          {
+            setNodeNormal (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Vec3f(0.0, 0.0, 1.0));
+          }
+        }
+        break;
+      }
+      case RWGltf_GltfArrayType_TCoord0:
+      {
+        if (aWrapCompType != RWGltf_GltfAccessorCompType_Float32
+         || aWrapLayout != RWGltf_GltfAccessorLayout_Vec2)
+        {
+          Message::SendTrace (TCollection_AsciiString() + "Vertex UV coordinates in unsupported format have been skipped while reading glTF triangulation '" + aName + "'");
+          break;
+        }
+
+        if (!setNbUVNodes (theDestMesh, aNbNodes))
+        {
+          return false;
+        }
+
+        for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          const Graphic3d_Vec2* aVec2 = reinterpret_cast<const Graphic3d_Vec2* >(anAttrib->GetAddressOfMappedIndex (draco::PointIndex (aVertIter)));
+          if (aVec2 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + aName + "' reading error.");
+            return false;
+          }
+
+          // Y should be flipped (relative to image layout used by OCCT)
+          float aTexY = 1.0f - aVec2->y();
+          setNodeUV (theDestMesh, THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt2d (aVec2->x(), aTexY));
+        }
+        break;
+      }
+      default:
+      {
+        break;
+      }
+    }
+  }
+
+  // copy triangles
+  Standard_Integer aLastTriIndex = 0;
+  for (Standard_Integer aFaceIter = 0; aFaceIter < aNbTris; ++aFaceIter)
+  {
+    const draco::Mesh::Face& aFace = aDracoStat.value()->face (draco::FaceIndex (aFaceIter));
+    Poly_Triangle aVec3;
+    aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + aFace[0].value();
+    aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + aFace[1].value();
+    aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + aFace[2].value();
+    const Standard_Integer wasSet = setTriangle (theDestMesh, THE_LOWER_TRI_INDEX + aLastTriIndex, aVec3);
+    if (!wasSet)
+    {
+      reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to invalid indices.");
+    }
+    if (wasSet > 0)
+    {
+      ++aLastTriIndex;
+    }
+  }
+
+  const Standard_Integer aNbDegenerate = aNbTris - aLastTriIndex;
+  if (aNbDegenerate > 0)
+  {
+    if (aNbDegenerate == aNbTris)
+    {
+      Message::SendWarning (TCollection_AsciiString("Buffer '") + aName + "' has been skipped (all elements are degenerative in)");
+      return false;
+    }
+    theSourceGltfMesh->ChangeDegeneratedTriNb() += aNbDegenerate;
+    if (myLoadingStatistic == NULL && myToPrintDebugMessages)
+    {
+      Message::SendTrace (TCollection_AsciiString() + aNbDegenerate
+                          + " degenerate triangles have been skipped while reading glTF triangulation '" + aName + "'");
+    }
+    if (!setNbTriangles (theDestMesh, aLastTriIndex, true))
+    {
+      return false;
+    }
+  }
+  return true;
+#else
+  (void )theDestMesh;
+  reportError (TCollection_AsciiString ("Buffer '") + aName + "' refers to unsupported compressed data.");
+  return false;
+#endif
+}
+
 // =======================================================================
 // function : load
 // purpose  :
@@ -164,22 +429,39 @@ bool RWGltf_TriangulationReader::load (const Handle(RWMesh_TriangulationSource)&
     return false;
   }
 
+  bool hasCompressed = false;
   for (NCollection_Sequence<RWGltf_GltfPrimArrayData>::Iterator aDataIter (aSourceGltfMesh->Data()); aDataIter.More(); aDataIter.Next())
   {
     const RWGltf_GltfPrimArrayData& aData = aDataIter.Value();
+    const TCollection_AsciiString& aName = aSourceGltfMesh->Id();
     if (!aData.StreamData.IsNull())
     {
-      Message::SendWarning (TCollection_AsciiString("Buffer '") + aSourceGltfMesh->Id() +
+      Message::SendWarning (TCollection_AsciiString("Buffer '") + aName +
         "' contains stream data that cannot be loaded during deferred data loading.");
       continue;
     }
     else if (aData.StreamUri.IsEmpty())
     {
-      reportError (TCollection_AsciiString ("Buffer '") + aSourceGltfMesh->Id() + "' does not define uri.");
+      reportError (TCollection_AsciiString ("Buffer '") + aName + "' does not define uri.");
       return false;
     }
 
-    if (!readFileData (aSourceGltfMesh, aData, theDestMesh, theFileSystem))
+    if (aData.Accessor.IsCompressed)
+    {
+      if (hasCompressed)
+      {
+        // already decoded (compressed stream defines all attributes at once)
+        continue;
+      }
+      if (!readDracoBuffer (aSourceGltfMesh, aData, theDestMesh, theFileSystem))
+      {
+        return false;
+      }
+
+      // keep decoding - there are might be uncompressed attributes in addition to compressed
+      hasCompressed = true;
+    }
+    else if (!readFileData (aSourceGltfMesh, aData, theDestMesh, theFileSystem))
     {
       return false;
     }
index 821c08d..30ef3fb 100644 (file)
@@ -97,6 +97,16 @@ protected:
                                            const RWGltf_GltfAccessor& theAccessor,
                                            RWGltf_GltfArrayType theType) const;
 
+  //! Reads primitive array from file data compressed in Draco format.
+  //! @param theSourceGltfMesh source glTF triangulation
+  //! @param theGltfData       primitive array element (Uri of file stream should not be empty)
+  //! @param theDestMesh       triangulation to be modified
+  //! @param theFileSystem     shared file system to read from
+  Standard_EXPORT virtual bool readDracoBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theSourceGltfMesh,
+                                                const RWGltf_GltfPrimArrayData& theGltfData,
+                                                const Handle(Poly_Triangulation)& theDestMesh,
+                                                const Handle(OSD_FileSystem)& theFileSystem) const;
+
 protected:
 
   Handle(Poly_Triangulation) myTriangulation;
index 8760fee..5e298d1 100755 (executable)
@@ -2,7 +2,6 @@ TKernel
 TKService
 TKMath
 CSF_TBB
-CSF_FREETYPE
 CSF_OpenGlLibs
 CSF_user32
 CSF_gdi32
index 6d9bc46..b1331b6 100644 (file)
@@ -8,3 +8,4 @@ TKBRep
 TKG3d
 TKService
 CSF_RapidJSON
+CSF_Draco