From d9d75a845f4e3ee4f265ad16e7cf84b234d53475 Mon Sep 17 00:00:00 2001 From: kgv Date: Tue, 10 Aug 2021 20:35:14 +0300 Subject: [PATCH] 0032525: Data Exchange, RWGltf_CafReader - support KHR_draco_mesh_compression 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. --- CMakeLists.txt | 19 ++ adm/cmake/draco.cmake | 4 + adm/cmake/occt_csf.cmake | 7 + adm/cmake/vardescr.cmake | 3 + adm/genconf.tcl | 16 +- adm/genconfdeps.tcl | 5 +- adm/genproj.tcl | 3 + adm/templates/env.bat | 26 +- adm/templates/env.sh | 2 + dox/introduction/introduction.md | 5 + src/Draw/Draw_BasicCommands.cxx | 5 + src/RWGltf/RWGltf_GltfAccessor.hxx | 4 +- src/RWGltf/RWGltf_GltfJsonParser.cxx | 32 +- src/RWGltf/RWGltf_GltfJsonParser.hxx | 3 +- src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx | 2 +- src/RWGltf/RWGltf_GltfPrimArrayData.hxx | 5 +- src/RWGltf/RWGltf_TriangulationReader.cxx | 290 ++++++++++++++++++- src/RWGltf/RWGltf_TriangulationReader.hxx | 10 + src/TKOpenGl/EXTERNLIB | 1 - src/TKRWMesh/EXTERNLIB | 1 + 20 files changed, 415 insertions(+), 28 deletions(-) create mode 100644 adm/cmake/draco.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 7eee96bc4c..43440b6388 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 0000000000..53cf90a954 --- /dev/null +++ b/adm/cmake/draco.cmake @@ -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" "") diff --git a/adm/cmake/occt_csf.cmake b/adm/cmake/occt_csf.cmake index 6cf72317b4..bee8ba78d9 100644 --- a/adm/cmake/occt_csf.cmake +++ b/adm/cmake/occt_csf.cmake @@ -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") diff --git a/adm/cmake/vardescr.cmake b/adm/cmake/vardescr.cmake index 9dffc9f4b0..06dab5b4ca 100644 --- a/adm/cmake/vardescr.cmake +++ b/adm/cmake/vardescr.cmake @@ -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") diff --git a/adm/genconf.tcl b/adm/genconf.tcl index d7ab6c8b76..d866a74cd2 100644 --- a/adm/genconf.tcl +++ b/adm/genconf.tcl @@ -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 diff --git a/adm/genconfdeps.tcl b/adm/genconfdeps.tcl index 644a18948b..10ef1978cb 100644 --- a/adm/genconfdeps.tcl +++ b/adm/genconfdeps.tcl @@ -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" diff --git a/adm/genproj.tcl b/adm/genproj.tcl index 04f9c20f91..ce70ef7b4f 100644 --- a/adm/genproj.tcl +++ b/adm/genproj.tcl @@ -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" } diff --git a/adm/templates/env.bat b/adm/templates/env.bat index 78dfa0eec4..a02bea526a 100644 --- a/adm/templates/env.bat +++ b/adm/templates/env.bat @@ -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%" diff --git a/adm/templates/env.sh b/adm/templates/env.sh index b0f29c0d08..4ab363e153 100644 --- a/adm/templates/env.sh +++ b/adm/templates/env.sh @@ -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 diff --git a/dox/introduction/introduction.md b/dox/introduction/introduction.md index c32ef176ca..ae4ce84234 100644 --- a/dox/introduction/introduction.md +++ b/dox/introduction/introduction.md @@ -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+
or ActiveTcl 8.6 | https://www.tcl.tk/software/tcltk/download.html
https://www.activestate.com/activetcl/downloads | DRAW Test Harness | Required | | Qt Desktop: Qt 4.8.6+
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. diff --git a/src/Draw/Draw_BasicCommands.cxx b/src/Draw/Draw_BasicCommands.cxx index 9c2b6c1403..0f8dd40da0 100644 --- a/src/Draw/Draw_BasicCommands.cxx +++ b/src/Draw/Draw_BasicCommands.cxx @@ -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 diff --git a/src/RWGltf/RWGltf_GltfAccessor.hxx b/src/RWGltf/RWGltf_GltfAccessor.hxx index ced6790db0..43087a04be 100644 --- a/src/RWGltf/RWGltf_GltfAccessor.hxx +++ b/src/RWGltf/RWGltf_GltfAccessor.hxx @@ -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) {} }; diff --git a/src/RWGltf/RWGltf_GltfJsonParser.cxx b/src/RWGltf/RWGltf_GltfJsonParser.cxx index 895718d020..c082c5c7b5 100644 --- a/src/RWGltf/RWGltf_GltfJsonParser.cxx +++ b/src/RWGltf/RWGltf_GltfJsonParser.cxx @@ -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) { diff --git a/src/RWGltf/RWGltf_GltfJsonParser.hxx b/src/RWGltf/RWGltf_GltfJsonParser.hxx index 8ba7ab5201..b285932e02 100644 --- a/src/RWGltf/RWGltf_GltfJsonParser.hxx +++ b/src/RWGltf/RWGltf_GltfJsonParser.hxx @@ -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, diff --git a/src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx b/src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx index 1c39f4106a..10e2d299b7 100644 --- a/src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx +++ b/src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx @@ -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 { diff --git a/src/RWGltf/RWGltf_GltfPrimArrayData.hxx b/src/RWGltf/RWGltf_GltfPrimArrayData.hxx index 752f3202e2..18cbac7981 100644 --- a/src/RWGltf/RWGltf_GltfPrimArrayData.hxx +++ b/src/RWGltf/RWGltf_GltfPrimArrayData.hxx @@ -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 diff --git a/src/RWGltf/RWGltf_TriangulationReader.cxx b/src/RWGltf/RWGltf_TriangulationReader.cxx index 09fbf6d3e0..d4dff574bc 100644 --- a/src/RWGltf/RWGltf_TriangulationReader.cxx +++ b/src/RWGltf/RWGltf_TriangulationReader.cxx @@ -21,11 +21,63 @@ #include #include +#ifdef HAVE_DRACO + #include +#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 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 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> 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::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(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(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(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::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; } diff --git a/src/RWGltf/RWGltf_TriangulationReader.hxx b/src/RWGltf/RWGltf_TriangulationReader.hxx index 821c08d350..30ef3fb3be 100644 --- a/src/RWGltf/RWGltf_TriangulationReader.hxx +++ b/src/RWGltf/RWGltf_TriangulationReader.hxx @@ -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; diff --git a/src/TKOpenGl/EXTERNLIB b/src/TKOpenGl/EXTERNLIB index 8760fee2cd..5e298d1ea5 100755 --- a/src/TKOpenGl/EXTERNLIB +++ b/src/TKOpenGl/EXTERNLIB @@ -2,7 +2,6 @@ TKernel TKService TKMath CSF_TBB -CSF_FREETYPE CSF_OpenGlLibs CSF_user32 CSF_gdi32 diff --git a/src/TKRWMesh/EXTERNLIB b/src/TKRWMesh/EXTERNLIB index 6d9bc46945..b1331b6af3 100644 --- a/src/TKRWMesh/EXTERNLIB +++ b/src/TKRWMesh/EXTERNLIB @@ -8,3 +8,4 @@ TKBRep TKG3d TKService CSF_RapidJSON +CSF_Draco