0030691: Data Exchange - implement import of mesh data from files in glTF format
authorkgv <kgv@opencascade.com>
Tue, 18 Jun 2019 16:41:15 +0000 (19:41 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 21 Jun 2019 15:19:08 +0000 (18:19 +0300)
Added RWGltf_CafReader class implementing glTF reader.
Added readgltf Draw Harness command for reading glTF files.

46 files changed:
CMakeLists.txt
adm/UDLIST
adm/cmake/rapidjson.cmake [new file with mode: 0644]
adm/cmake/vardescr.cmake
adm/genconf.tcl
adm/genconfdeps.tcl
adm/templates/env.bat
dox/overview/overview.md
src/Draw/Draw_BasicCommands.cxx
src/RWGltf/FILES [new file with mode: 0644]
src/RWGltf/RWGltf_CafReader.cxx [new file with mode: 0644]
src/RWGltf/RWGltf_CafReader.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfAccessor.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfAccessorCompType.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfAccessorLayout.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfArrayType.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfBufferView.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfBufferViewTarget.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfFace.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfJsonParser.cxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfJsonParser.pxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfLatePrimitiveArray.cxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfPrimArrayData.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfPrimitiveMode.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_GltfRootElement.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_MaterialCommon.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_MaterialMetallicRoughness.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_PrimitiveArrayReader.cxx [new file with mode: 0644]
src/RWGltf/RWGltf_PrimitiveArrayReader.hxx [new file with mode: 0644]
src/RWGltf/RWGltf_TriangulationReader.cxx [new file with mode: 0644]
src/RWGltf/RWGltf_TriangulationReader.hxx [new file with mode: 0644]
src/Standard/Standard_TypeDef.hxx
src/TKRWMesh/EXTERNLIB
src/TKRWMesh/PACKAGES
src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx
tests/de_mesh/gltf_read/begin [new file with mode: 0644]
tests/de_mesh/gltf_read/brainstem [new file with mode: 0644]
tests/de_mesh/gltf_read/buggy [new file with mode: 0644]
tests/de_mesh/gltf_read/end [new file with mode: 0644]
tests/de_mesh/gltf_read/engine [new file with mode: 0644]
tests/de_mesh/gltf_read/helmet [new file with mode: 0644]
tests/de_mesh/gltf_read/lantern [new file with mode: 0644]
tests/de_mesh/gltf_read/orient [new file with mode: 0644]
tests/de_mesh/grids.list
tests/de_mesh/parse.rules [new file with mode: 0644]

index dc64c3c..91b72e5 100644 (file)
@@ -633,6 +633,26 @@ else()
   OCCT_CHECK_AND_UNSET ("INSTALL_TBB")
 endif()
 
   OCCT_CHECK_AND_UNSET ("INSTALL_TBB")
 endif()
 
+# RapidJSON
+# search for CSF_RapidJSON variable in EXTERNLIB of each being used toolkit
+OCCT_IS_PRODUCT_REQUIRED (CSF_RapidJSON CAN_USE_RAPIDJSON)
+if (CAN_USE_RAPIDJSON)
+  set (USE_RAPIDJSON OFF CACHE BOOL "${USE_RAPIDJSON_DESCR}")
+
+  if (USE_RAPIDJSON)
+    add_definitions (-DHAVE_RAPIDJSON)
+    OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/rapidjson")
+  else()
+    OCCT_CHECK_AND_UNSET_GROUP ("3RDPARTY_RAPIDJSON")
+    OCCT_CHECK_AND_UNSET ("INSTALL_RAPIDJSON")
+  endif()
+else()
+  OCCT_CHECK_AND_UNSET ("USE_RAPIDJSON")
+
+  OCCT_CHECK_AND_UNSET_GROUP ("3RDPARTY_RAPIDJSON")
+  OCCT_CHECK_AND_UNSET ("INSTALL_RAPIDJSON")
+endif()
+
 # EIGEN
 if (CAN_USE_EIGEN)
   set (USE_EIGEN OFF CACHE BOOL "${USE_EIGEN_DESCR}")
 # EIGEN
 if (CAN_USE_EIGEN)
   set (USE_EIGEN OFF CACHE BOOL "${USE_EIGEN_DESCR}")
index 032662b..7f39f5e 100644 (file)
@@ -440,4 +440,5 @@ t TKVCAF
 n XCAFView
 n XCAFNoteObjects
 t TKRWMesh
 n XCAFView
 n XCAFNoteObjects
 t TKRWMesh
+n RWGltf
 n RWMesh
 n RWMesh
diff --git a/adm/cmake/rapidjson.cmake b/adm/cmake/rapidjson.cmake
new file mode 100644 (file)
index 0000000..de8fae2
--- /dev/null
@@ -0,0 +1,86 @@
+# RapidJSON
+
+if (NOT DEFINED INSTALL_RAPIDJSON)
+  set (INSTALL_RAPIDJSON OFF CACHE BOOL "${INSTALL_RAPIDJSON_DESCR}")
+endif()
+
+# RapidJSON directory
+if (NOT DEFINED 3RDPARTY_RAPIDJSON_DIR)
+  set (3RDPARTY_RAPIDJSON_DIR "" CACHE PATH "The directory containing RapidJSON")
+endif()
+
+# search for RapidJSON in user defined directory
+if (3RDPARTY_DIR AND EXISTS "${3RDPARTY_DIR}")
+  if (NOT 3RDPARTY_RAPIDJSON_DIR OR NOT EXISTS "${3RDPARTY_RAPIDJSON_DIR}")
+    FIND_PRODUCT_DIR("${3RDPARTY_DIR}" RapidJSON RAPIDJSON_DIR_NAME)
+    if (RAPIDJSON_DIR_NAME)
+      set (3RDPARTY_RAPIDJSON_DIR "${3RDPARTY_DIR}/${RAPIDJSON_DIR_NAME}" CACHE PATH "The directory containing RapidJSON" FORCE)
+    endif()
+  endif()
+endif()
+
+if (NOT DEFINED 3RDPARTY_RAPIDJSON_INCLUDE_DIR)
+  set (3RDPARTY_RAPIDJSON_INCLUDE_DIR  "" CACHE FILEPATH "The directory containing headers of the RAPIDJSON")
+endif()
+
+if (NOT 3RDPARTY_RAPIDJSON_INCLUDE_DIR OR NOT EXISTS "${3RDPARTY_RAPIDJSON_INCLUDE_DIR}")
+
+  set (HEADER_NAMES rapidjson/rapidjson.h)
+
+  set (3RDPARTY_RAPIDJSON_INCLUDE_DIR "3RDPARTY_RAPIDJSON_INCLUDE_DIR-NOTFOUND" CACHE PATH "the path to RapidJSON header file" FORCE)
+
+  if (3RDPARTY_RAPIDJSON_DIR AND EXISTS "${3RDPARTY_RAPIDJSON_DIR}")
+    find_path (3RDPARTY_RAPIDJSON_INCLUDE_DIR NAMES ${HEADER_NAMES}
+                                              PATHS ${3RDPARTY_RAPIDJSON_DIR}
+                                              PATH_SUFFIXES include rapidjson
+                                              CMAKE_FIND_ROOT_PATH_BOTH
+                                              NO_DEFAULT_PATH)
+  else()
+    find_path (3RDPARTY_RAPIDJSON_INCLUDE_DIR NAMES ${HEADER_NAMES}
+                                              PATH_SUFFIXES include rapidjson
+                                              CMAKE_FIND_ROOT_PATH_BOTH)
+  endif()
+
+  # use default (CMake) RapidJSON search
+  if (NOT 3RDPARTY_RAPIDJSON_INCLUDE_DIR OR NOT EXISTS "${3RDPARTY_RAPIDJSON_INCLUDE_DIR}")
+    if (3RDPARTY_RAPIDJSON_DIR AND EXISTS "${3RDPARTY_RAPIDJSON_DIR}")
+      set (CACHED_RAPIDJSON_DIR $ENV{RapidJSON_DIR})
+      set (ENV{RapidJSON_DIR} "${3RDPARTY_RAPIDJSON_DIR}")
+    endif()
+
+    find_package(RapidJSON QUIET)
+
+    # restore ENV{RapidJSON_DIR}
+    if (3RDPARTY_RAPIDJSON_DIR AND EXISTS "${3RDPARTY_RAPIDJSON_DIR}")
+      set (ENV{RapidJSON_DIR} ${CACHED_RAPIDJSON_DIR})
+    endif()
+
+    if (${RAPIDJSON_FOUND})
+      set (3RDPARTY_RAPIDJSON_INCLUDE_DIR "${RAPIDJSON_INCLUDE_DIR}" CACHE PATH "the path to RapidJSON header file" FORCE)
+      set (3RDPARTY_RAPIDJSON_DIR         "${RAPIDJSON_ROOT_DIR}"    CACHE PATH "The directory containing RapidJSON" FORCE)
+    endif()
+  endif()
+endif()
+
+if (3RDPARTY_RAPIDJSON_INCLUDE_DIR AND EXISTS "${3RDPARTY_RAPIDJSON_INCLUDE_DIR}")
+  list (APPEND 3RDPARTY_INCLUDE_DIRS "${3RDPARTY_RAPIDJSON_INCLUDE_DIR}")
+
+  # Install header files
+  if (INSTALL_RAPIDJSON)
+    file(GLOB RAPIDJSON_SUBDIRS "${3RDPARTY_RAPIDJSON_INCLUDE_DIR}/*")
+    foreach(SUBDIR ${RAPIDJSON_SUBDIRS})
+      if(IS_DIRECTORY "${SUBDIR}")
+        install (DIRECTORY "${SUBDIR}" DESTINATION "${INSTALL_DIR_INCLUDE}")
+      else()
+        install (FILES "${SUBDIR}" DESTINATION "${INSTALL_DIR_INCLUDE}")
+      endif()
+    endforeach()
+  endif()
+else()
+  list (APPEND 3RDPARTY_NOT_INCLUDED 3RDPARTY_RAPIDJSON_INCLUDE_DIR)
+
+  set (3RDPARTY_RAPIDJSON_INCLUDE_DIR "" CACHE PATH "the path to RapidJSON header file" FORCE)
+endif()
+
+# unset all redundant variables
+OCCT_CHECK_AND_UNSET(RapidJSON_DIR)
index 29fa030..845fc28 100644 (file)
@@ -94,6 +94,7 @@ INSTALL_MESSAGE (INSTALL_EGL              "EGL binaries")
 INSTALL_MESSAGE (INSTALL_GLES2            "OpenGL ES 2.0 binaries")
 INSTALL_MESSAGE (INSTALL_FREETYPE         "FreeType binaries")
 INSTALL_MESSAGE (INSTALL_TBB              "TBB binaries")
 INSTALL_MESSAGE (INSTALL_GLES2            "OpenGL ES 2.0 binaries")
 INSTALL_MESSAGE (INSTALL_FREETYPE         "FreeType binaries")
 INSTALL_MESSAGE (INSTALL_TBB              "TBB binaries")
+INSTALL_MESSAGE (INSTALL_RAPIDJSON        "RapidJSON header files")
 INSTALL_MESSAGE (INSTALL_TCL              "TCL binaries")
 INSTALL_MESSAGE (INSTALL_TK               "TK binaries")
 INSTALL_MESSAGE (INSTALL_VTK              "VTK binaries ")
 INSTALL_MESSAGE (INSTALL_TCL              "TCL binaries")
 INSTALL_MESSAGE (INSTALL_TK               "TK binaries")
 INSTALL_MESSAGE (INSTALL_VTK              "VTK binaries ")
@@ -161,6 +162,10 @@ set (USE_FREEIMAGE_DESCR
 "Indicates whether Freeimage product should be used in OCCT visualization
 module for support of popular graphics image formats (PNG, BMP etc)")
 
 "Indicates whether Freeimage product should be used in OCCT visualization
 module for support of popular graphics image formats (PNG, BMP etc)")
 
+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_EGL_DESCR
 "Indicates whether EGL should be used in OCCT visualization
 module instead of conventional OpenGL context creation APIs")
 set (USE_EGL_DESCR
 "Indicates whether EGL should be used in OCCT visualization
 module instead of conventional OpenGL context creation APIs")
index 2b46427..619e78c 100644 (file)
@@ -189,6 +189,9 @@ proc wokdep:gui:UpdateList {} {
     }
     wokdep:SearchStandardLibrary  anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs "liblzma" "lzma.h" "$aCheckLib" {"lzma" "xz"}
   }
     }
     wokdep:SearchStandardLibrary  anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs "liblzma" "lzma.h" "$aCheckLib" {"lzma" "xz"}
   }
+  if { "$::HAVE_RAPIDJSON" == "true" } {
+    wokdep:SearchRapidJson anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
+  }
 
   if { "$::CHECK_QT4" == "true" } {
     wokdep:SearchQt4     anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
 
   if { "$::CHECK_QT4" == "true" } {
     wokdep:SearchQt4     anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
@@ -456,6 +459,9 @@ ttk::label    .myFrame.myChecks.myZLibLbl       -text "Use zlib"
 checkbutton   .myFrame.myChecks.myLzmaCheck     -offvalue "false" -onvalue "true" -variable HAVE_LIBLZMA   -command wokdep:gui:UpdateList
 ttk::label    .myFrame.myChecks.myLzmaLbl       -text "Use liblzma"
 
 checkbutton   .myFrame.myChecks.myLzmaCheck     -offvalue "false" -onvalue "true" -variable HAVE_LIBLZMA   -command wokdep:gui:UpdateList
 ttk::label    .myFrame.myChecks.myLzmaLbl       -text "Use liblzma"
 
+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.myQt4Check      -offvalue "false" -onvalue "true" -variable CHECK_QT4      -command wokdep:gui:UpdateList
 ttk::label    .myFrame.myChecks.myQt4Lbl        -text "Search Qt4"
 checkbutton   .myFrame.myChecks.myJDKCheck      -offvalue "false" -onvalue "true" -variable CHECK_JDK      -command wokdep:gui:UpdateList
 checkbutton   .myFrame.myChecks.myQt4Check      -offvalue "false" -onvalue "true" -variable CHECK_QT4      -command wokdep:gui:UpdateList
 ttk::label    .myFrame.myChecks.myQt4Lbl        -text "Search Qt4"
 checkbutton   .myFrame.myChecks.myJDKCheck      -offvalue "false" -onvalue "true" -variable CHECK_JDK      -command wokdep:gui:UpdateList
@@ -564,8 +570,8 @@ if { "$::tcl_platform(os)" != "Darwin" } {
 grid .myFrame.myChecks.myZLibCheck     -row $aCheckRowIter -column 6 -sticky e
 grid .myFrame.myChecks.myZLibLbl       -row $aCheckRowIter -column 7 -sticky w
 
 grid .myFrame.myChecks.myZLibCheck     -row $aCheckRowIter -column 6 -sticky e
 grid .myFrame.myChecks.myZLibLbl       -row $aCheckRowIter -column 7 -sticky w
 
-grid .myFrame.myChecks.myQt4Check      -row $aCheckRowIter -column 10 -sticky e
-grid .myFrame.myChecks.myQt4Lbl        -row $aCheckRowIter -column 11 -sticky w
+grid .myFrame.myChecks.myQt4Check      -row $aCheckRowIter -column 12 -sticky e
+grid .myFrame.myChecks.myQt4Lbl        -row $aCheckRowIter -column 13 -sticky w
 
 incr aCheckRowIter
 grid .myFrame.myChecks.myFFmpegCheck   -row $aCheckRowIter -column 0 -sticky e
 
 incr aCheckRowIter
 grid .myFrame.myChecks.myFFmpegCheck   -row $aCheckRowIter -column 0 -sticky e
@@ -578,8 +584,8 @@ if { "$::tcl_platform(platform)" == "windows" } {
 }
 grid .myFrame.myChecks.myLzmaCheck     -row $aCheckRowIter -column 6 -sticky e
 grid .myFrame.myChecks.myLzmaLbl       -row $aCheckRowIter -column 7 -sticky w
 }
 grid .myFrame.myChecks.myLzmaCheck     -row $aCheckRowIter -column 6 -sticky e
 grid .myFrame.myChecks.myLzmaLbl       -row $aCheckRowIter -column 7 -sticky w
-grid .myFrame.myChecks.myJDKCheck      -row $aCheckRowIter -column 10 -sticky e
-grid .myFrame.myChecks.myJDKLbl        -row $aCheckRowIter -column 11 -sticky w
+grid .myFrame.myChecks.myJDKCheck      -row $aCheckRowIter -column 12 -sticky e
+grid .myFrame.myChecks.myJDKLbl        -row $aCheckRowIter -column 13 -sticky w
 
 incr aCheckRowIter
 if { "$::tcl_platform(os)" == "Darwin" } {
 
 incr aCheckRowIter
 if { "$::tcl_platform(os)" == "Darwin" } {
@@ -588,6 +594,10 @@ if { "$::tcl_platform(os)" == "Darwin" } {
   incr aCheckRowIter
 }
 
   incr aCheckRowIter
 }
 
+grid .myFrame.myChecks.myRapidJsonCheck -row $aCheckRowIter -column 6 -sticky e
+grid .myFrame.myChecks.myRapidJsonLbl   -row $aCheckRowIter -column 7 -sticky w
+incr aCheckRowIter
+
 # Additional headers search paths
 grid .myFrame.myIncLbl    -row $aRowIter -column 0 -columnspan 10 -sticky w
 incr aRowIter
 # Additional headers search paths
 grid .myFrame.myIncLbl    -row $aRowIter -column 0 -columnspan 10 -sticky w
 incr aRowIter
index 520fe34..0690d40 100644 (file)
@@ -68,7 +68,7 @@ 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
 }
 
 # 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_FREEIMAGE HAVE_FFMPEG HAVE_TBB HAVE_GLES2 HAVE_D3D HAVE_VTK HAVE_ZLIB HAVE_LIBLZMA HAVE_OPENCL CHECK_QT4 CHECK_JDK MACOSX_USE_GLX HAVE_RelWithDebInfo}
+set THE_ENV_VARIABLES {HAVE_FREEIMAGE HAVE_FFMPEG HAVE_TBB HAVE_GLES2 HAVE_D3D HAVE_VTK HAVE_ZLIB HAVE_LIBLZMA HAVE_RAPIDJSON HAVE_OPENCL CHECK_QT4 CHECK_JDK MACOSX_USE_GLX HAVE_RelWithDebInfo}
 foreach anEnvIter $THE_ENV_VARIABLES {
   set ${anEnvIter} "false"
   if { [info exists ::env(${anEnvIter})] } {
 foreach anEnvIter $THE_ENV_VARIABLES {
   set ${anEnvIter} "false"
   if { [info exists ::env(${anEnvIter})] } {
@@ -858,6 +858,25 @@ proc wokdep:SearchGLES {theErrInc theErrLib32 theErrLib64 theErrBin32 theErrBin6
   return "$isFound"
 }
 
   return "$isFound"
 }
 
+# Search RapidJSON headers
+proc wokdep:SearchRapidJson {theErrInc theErrLib32 theErrLib64 theErrBin32 theErrBin64} {
+  upvar $theErrInc anErrInc
+
+  set isFound "true"
+  set aRJHPath [wokdep:SearchHeader "rapidjson/rapidjson.h"]
+  if { "$aRJHPath"  == "" } {
+    set aPath [wokdep:Preferred [glob -nocomplain -directory "$::PRODUCTS_PATH" -type d *{rapidjson}*] "$::VCVER" "$::ARCH" ]
+    if { "$aPath" != "" && [file exists "$aPath/include/rapidjson/rapidjson.h"] } {
+      lappend ::CSF_OPT_INC "$aPath/include"
+    } else {
+      lappend anErrInc "Error: 'rapidjson/rapidjson.h' not found (RapidJSON)"
+      set isFound "false"
+    }
+  }
+
+  return "$isFound"
+}
+
 # Auxiliary function, gets VTK version to set default search directory
 proc wokdep:VtkVersion { thePath } {
   set aResult "6.1"
 # Auxiliary function, gets VTK version to set default search directory
 proc wokdep:VtkVersion { thePath } {
   set aResult "6.1"
index 57d4486..a478670 100644 (file)
@@ -24,6 +24,7 @@ set "HAVE_GLES2=false"
 set "HAVE_D3D=false"
 set "HAVE_ZLIB=false"
 set "HAVE_LIBLZMA=false"
 set "HAVE_D3D=false"
 set "HAVE_ZLIB=false"
 set "HAVE_LIBLZMA=false"
+set "HAVE_RAPIDJSON=false"
 set "CSF_OPT_INC="
 set "CSF_OPT_LIB32="
 set "CSF_OPT_LIB64="
 set "CSF_OPT_INC="
 set "CSF_OPT_LIB32="
 set "CSF_OPT_LIB64="
@@ -163,6 +164,7 @@ if ["%HAVE_GLES2%"]     == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DH
 if ["%HAVE_D3D%"]       == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_D3D"       & set "CSF_DEFINES=HAVE_D3D;%CSF_DEFINES%"
 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_D3D%"]       == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_D3D"       & set "CSF_DEFINES=HAVE_D3D;%CSF_DEFINES%"
 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%"
 
 rem Eliminate VS warning
 if ["%CSF_DEFINES%"]  == [""] set "CSF_DEFINES=;"
 
 rem Eliminate VS warning
 if ["%CSF_DEFINES%"]  == [""] set "CSF_DEFINES=;"
index e3dc2e1..c9ead98 100644 (file)
@@ -222,6 +222,7 @@ for which OCCT is certified to work.
 | Freetype (for text rendering) | FreeType 2.4.11-2.7.1 https://sourceforge.net/projects/freetype/files/ |
 | FreeImage (optional, for support of common 2D graphic formats) | FreeImage 3.17.0+ https://sourceforge.net/projects/freeimage/files |
 | FFmpeg (optional, for video recording) | FFmpeg 3.1+ https://www.ffmpeg.org/download.html |
 | Freetype (for text rendering) | FreeType 2.4.11-2.7.1 https://sourceforge.net/projects/freetype/files/ |
 | FreeImage (optional, for support of common 2D graphic formats) | FreeImage 3.17.0+ https://sourceforge.net/projects/freeimage/files |
 | FFmpeg (optional, for video recording) | FFmpeg 3.1+ https://www.ffmpeg.org/download.html |
+| RapidJSON (optional, for reading glTF) | RapidJSON 1.1+ http://rapidjson.org/ |
 | Intel TBB (optional, for multithreaded algorithms) | TBB 4.x or 5.x https://www.threadingbuildingblocks.org/ |
 | VTK (for VTK Integration Services | VTK 6.1+ http://www.vtk.org/download/ |
 | Doxygen (optional for building documentation) | Doxygen 1.8.5+ https://www.stack.nl/~dimitri/doxygen/download.html |
 | Intel TBB (optional, for multithreaded algorithms) | TBB 4.x or 5.x https://www.threadingbuildingblocks.org/ |
 | VTK (for VTK Integration Services | VTK 6.1+ http://www.vtk.org/download/ |
 | Doxygen (optional for building documentation) | Doxygen 1.8.5+ https://www.stack.nl/~dimitri/doxygen/download.html |
index 638fcfd..b0cd38a 100644 (file)
@@ -362,6 +362,11 @@ static Standard_Integer dversion(Draw_Interpretor& di, Standard_Integer, const c
 #else
   di << "OpenGL: desktop\n";
 #endif
 #else
   di << "OpenGL: desktop\n";
 #endif
+#ifdef HAVE_RAPIDJSON
+  di << "RapidJSON enabled (HAVE_RAPIDJSON)\n";
+#else
+  di << "RapidJSON disabled\n";
+#endif
 #ifdef HAVE_VTK
   di << "VTK enabled (HAVE_VTK)\n";
 #else
 #ifdef HAVE_VTK
   di << "VTK enabled (HAVE_VTK)\n";
 #else
diff --git a/src/RWGltf/FILES b/src/RWGltf/FILES
new file mode 100644 (file)
index 0000000..cd6d8a8
--- /dev/null
@@ -0,0 +1,22 @@
+RWGltf_GltfAccessor.hxx
+RWGltf_GltfAccessorCompType.hxx
+RWGltf_GltfAccessorLayout.hxx
+RWGltf_GltfArrayType.hxx
+RWGltf_GltfBufferView.hxx
+RWGltf_GltfBufferViewTarget.hxx
+RWGltf_GltfFace.hxx
+RWGltf_GltfLatePrimitiveArray.cxx
+RWGltf_GltfLatePrimitiveArray.hxx
+RWGltf_GltfPrimArrayData.hxx
+RWGltf_GltfPrimitiveMode.hxx
+RWGltf_GltfRootElement.hxx
+RWGltf_MaterialCommon.hxx
+RWGltf_MaterialMetallicRoughness.hxx
+RWGltf_CafReader.cxx
+RWGltf_CafReader.hxx
+RWGltf_GltfJsonParser.cxx
+RWGltf_GltfJsonParser.pxx
+RWGltf_PrimitiveArrayReader.cxx
+RWGltf_PrimitiveArrayReader.hxx
+RWGltf_TriangulationReader.cxx
+RWGltf_TriangulationReader.hxx
diff --git a/src/RWGltf/RWGltf_CafReader.cxx b/src/RWGltf/RWGltf_CafReader.cxx
new file mode 100644 (file)
index 0000000..f17c3db
--- /dev/null
@@ -0,0 +1,295 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <RWGltf_CafReader.hxx>
+
+#include "RWGltf_GltfJsonParser.pxx"
+#include <RWGltf_TriangulationReader.hxx>
+
+#include <BRep_Builder.hxx>
+#include <BRep_Tool.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <Message_ProgressSentry.hxx>
+#include <OSD_OpenFile.hxx>
+#include <OSD_ThreadPool.hxx>
+
+#include <fstream>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
+
+//! Functor for parallel execution.
+class RWGltf_CafReader::CafReader_GltfReaderFunctor
+{
+public:
+
+  struct GltfReaderTLS
+  {
+    Handle(RWGltf_PrimitiveArrayReader) Reader;
+  };
+
+  //! Main constructor.
+  CafReader_GltfReaderFunctor (RWGltf_CafReader* myCafReader,
+                               NCollection_Vector<TopoDS_Face>& theFaceList,
+                               Message_ProgressSentry& theSentry,
+                               const OSD_ThreadPool::Launcher& theThreadPool,
+                               const TCollection_AsciiString& theErrPrefix)
+  : myCafReader (myCafReader),
+    myFaceList  (&theFaceList),
+    mySentry    (&theSentry),
+    myErrPrefix (theErrPrefix),
+    myThreadPool(theThreadPool),
+    myTlsData   (theThreadPool.LowerThreadIndex(), theThreadPool.UpperThreadIndex())
+  {
+    //
+  }
+
+  //! Execute task for a face with specified index.
+  void operator() (int theThreadIndex,
+                   int theFaceIndex) const
+  {
+    GltfReaderTLS& aTlsData = myTlsData.ChangeValue (theThreadIndex);
+    if (aTlsData.Reader.IsNull())
+    {
+      aTlsData.Reader = myCafReader->createMeshReaderContext();
+      aTlsData.Reader->SetErrorPrefix (myErrPrefix);
+      aTlsData.Reader->SetCoordinateSystemConverter (myCafReader->myCoordSysConverter);
+    }
+
+    TopLoc_Location aDummyLoc;
+    TopoDS_Face& aFace = myFaceList->ChangeValue (theFaceIndex);
+    Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (aFace, aDummyLoc));
+    Handle(Poly_Triangulation) aPolyData = aTlsData.Reader->Load (aLateData);
+    BRep_Builder aBuilder;
+    aBuilder.UpdateFace (aFace, aPolyData);
+
+    if (myThreadPool.HasThreads())
+    {
+      Standard_Mutex::Sentry aLock (&myMutex);
+      mySentry->Next();
+    }
+    else
+    {
+      mySentry->Next();
+    }
+  }
+
+private:
+
+  RWGltf_CafReader* myCafReader;
+  NCollection_Vector<TopoDS_Face>* myFaceList;
+  Message_ProgressSentry*   mySentry;
+  TCollection_AsciiString   myErrPrefix;
+  mutable Standard_Mutex    myMutex;
+  const OSD_ThreadPool::Launcher& myThreadPool;
+  mutable NCollection_Array1<GltfReaderTLS>
+                            myTlsData;
+
+};
+
+//================================================================
+// Function : Constructor
+// Purpose  :
+//================================================================
+RWGltf_CafReader::RWGltf_CafReader()
+: myToParallel (false)
+{
+  myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
+  myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
+}
+
+//================================================================
+// Function : performMesh
+// Purpose  :
+//================================================================
+Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
+                                                const Handle(Message_ProgressIndicator)& theProgress,
+                                                const Standard_Boolean theToProbe)
+{
+  std::ifstream aFile;
+  OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary);
+  if (!aFile.is_open()
+   || !aFile.good())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is not found!", Message_Fail);
+    return false;
+  }
+
+  bool isBinaryFile = false;
+  char aGlbHeader[12] = {};
+  aFile.read (aGlbHeader, sizeof(aGlbHeader));
+  int64_t aBinBodyOffset  = 0;
+  int64_t aBinBodyLen     = 0;
+  int64_t aJsonBodyOffset = 0;
+  int64_t aJsonBodyLen    = 0;
+  if (::strncmp (aGlbHeader, "glTF", 4) == 0)
+  {
+    isBinaryFile = true;
+    const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
+    const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
+    if (*aVer == 1)
+    {
+      if (*aLen < 20)
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format!", Message_Fail);
+        return false;
+      }
+
+      char aHeader1[8] = {};
+      aFile.read (aHeader1, sizeof(aHeader1));
+
+      const uint32_t* aSceneLen    = (const uint32_t* )(aHeader1 + 0);
+      const uint32_t* aSceneFormat = (const uint32_t* )(aHeader1 + 4);
+      aJsonBodyOffset = 20;
+      aJsonBodyLen    = int64_t(*aSceneLen);
+
+      aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
+      aBinBodyLen    = int64_t(*aLen) - aBinBodyOffset;
+      if (*aSceneFormat != 0)
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format!", Message_Fail);
+        return false;
+      }
+    }
+    else //if (*aVer == 2)
+    {
+      if (*aVer != 2)
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer) + "!", Message_Warning);
+      }
+
+      for (int aChunkIter = 0; !aFile.eof() && aChunkIter < 2; ++aChunkIter)
+      {
+        char aChunkHeader2[8] = {};
+        if (int64_t(aFile.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(aLen))
+        {
+          break;
+        }
+
+        aFile.read (aChunkHeader2, sizeof(aChunkHeader2));
+        if (!aFile.good())
+        {
+          Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format!", Message_Fail);
+          return false;
+        }
+
+        const uint32_t* aChunkLen  = (const uint32_t* )(aChunkHeader2 + 0);
+        const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
+        if (*aChunkType == 0x4E4F534A)
+        {
+          aJsonBodyOffset = int64_t(aFile.tellg());
+          aJsonBodyLen    = int64_t(*aChunkLen);
+        }
+        else if (*aChunkType == 0x004E4942)
+        {
+          aBinBodyOffset = int64_t(aFile.tellg());
+          aBinBodyLen    = int64_t(*aChunkLen);
+        }
+        if (*aChunkLen != 0)
+        {
+          aFile.seekg (*aChunkLen, std::ios_base::cur);
+        }
+      }
+
+      aFile.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
+    }
+  }
+  else
+  {
+    aFile.seekg (0, std::ios_base::beg);
+  }
+
+  TCollection_AsciiString anErrPrefix = TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n";
+  RWGltf_GltfJsonParser aDoc (myRootShapes);
+  aDoc.SetFilePath (theFile);
+  aDoc.SetProbeHeader (theToProbe);
+  aDoc.SetExternalFiles (myExternalFiles);
+  aDoc.SetErrorPrefix (anErrPrefix);
+  aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
+  if (!theToProbe)
+  {
+    aDoc.SetAttributeMap (myAttribMap);
+  }
+  if (isBinaryFile)
+  {
+    aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
+  }
+
+#ifdef HAVE_RAPIDJSON
+  rapidjson::ParseResult aRes;
+  rapidjson::IStreamWrapper aFileStream (aFile);
+  if (isBinaryFile)
+  {
+    aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
+  }
+  else
+  {
+    aRes = aDoc.ParseStream (aFileStream);
+  }
+  if (aRes.IsError())
+  {
+    if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
+    {
+      Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is empty!", Message_Fail);
+      return false;
+    }
+    TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
+                                     + anErrDesc + ".", Message_Fail);
+    return false;
+  }
+#endif
+
+  if (!aDoc.Parse (theProgress))
+  {
+    return false;
+  }
+
+  if (!theToProbe
+   && !readLateData (aDoc.FaceList(), theFile, theProgress))
+  {
+    return false;
+  }
+
+  return true;
+}
+
+//================================================================
+// Function : createMeshReaderContext
+// Purpose  :
+//================================================================
+Handle(RWGltf_PrimitiveArrayReader) RWGltf_CafReader::createMeshReaderContext()
+{
+  Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
+  return aReader;
+}
+
+//================================================================
+// Function : readLateData
+// Purpose  :
+//================================================================
+Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
+                                                 const TCollection_AsciiString& theFile,
+                                                 const Handle(Message_ProgressIndicator)& theProgress)
+{
+  Message_ProgressSentry aPSentryTris (theProgress, "Loading glTF triangulation", 0, Max (1, theFaces.Size()), 1);
+  const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
+  const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
+  OSD_ThreadPool::Launcher aLauncher (*aThreadPool, aNbThreads);
+
+  CafReader_GltfReaderFunctor aFunctor (this, theFaces, aPSentryTris, aLauncher,
+                                        TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n");
+  aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
+  return Standard_True;
+}
diff --git a/src/RWGltf/RWGltf_CafReader.hxx b/src/RWGltf/RWGltf_CafReader.hxx
new file mode 100644 (file)
index 0000000..ba335c1
--- /dev/null
@@ -0,0 +1,65 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_CafReader_HeaderFile
+#define _RWGltf_CafReader_HeaderFile
+
+#include <NCollection_Vector.hxx>
+#include <RWMesh_CafReader.hxx>
+#include <TopoDS_Face.hxx>
+
+class RWGltf_PrimitiveArrayReader;
+
+//! The glTF (GL Transmission Format) mesh reader into XDE document.
+class RWGltf_CafReader : public RWMesh_CafReader
+{
+  DEFINE_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT RWGltf_CafReader();
+
+  //! Return TRUE if multithreaded optimizations are allowed; FALSE by default.
+  bool ToParallel() const { return myToParallel; }
+
+  //! Setup multithreaded execution.
+  void SetParallel (bool theToParallel) { myToParallel = theToParallel; }
+
+protected:
+
+  //! Read the mesh from specified file.
+  Standard_EXPORT virtual Standard_Boolean performMesh (const TCollection_AsciiString& theFile,
+                                                        const Handle(Message_ProgressIndicator)& theProgress,
+                                                        const Standard_Boolean theToProbe) Standard_OVERRIDE;
+
+  //! Create primitive array reader context.
+  //! Can be overridden by sub-class to read triangulation into application-specific data structures instead of Poly_Triangulation.
+  //! Default implementation creates RWGltf_TriangulationReader.
+  Standard_EXPORT virtual Handle(RWGltf_PrimitiveArrayReader) createMeshReaderContext();
+
+  //! Read late data from RWGltf_GltfLatePrimitiveArray stored as Poly_Triangulation within faces.
+  Standard_EXPORT virtual Standard_Boolean readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
+                                                         const TCollection_AsciiString& theFile,
+                                                         const Handle(Message_ProgressIndicator)& theProgress);
+protected:
+
+  class CafReader_GltfReaderFunctor;
+
+protected:
+
+  Standard_Boolean myToParallel; //!< flag to use multithreading; FALSE by default
+
+};
+
+#endif // _RWGltf_CafReader_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfAccessor.hxx b/src/RWGltf/RWGltf_GltfAccessor.hxx
new file mode 100644 (file)
index 0000000..ced6790
--- /dev/null
@@ -0,0 +1,48 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfAccessor_HeaderFile
+#define _RWGltf_GltfAccessor_HeaderFile
+
+#include <Graphic3d_BndBox3d.hxx>
+#include <RWGltf_GltfAccessorCompType.hxx>
+#include <RWGltf_GltfAccessorLayout.hxx>
+#include <Standard_TypeDef.hxx>
+
+//! Low-level glTF data structure defining Accessor.
+struct RWGltf_GltfAccessor
+{
+  static const int INVALID_ID = -1;
+public:
+
+  int                         Id;            //!< identifier
+  int64_t                     ByteOffset;    //!< byte offset
+  int64_t                     Count;         //!< size
+  int32_t                     ByteStride;    //!< [0, 255] for glTF 1.0
+  RWGltf_GltfAccessorLayout   Type;          //!< layout type
+  RWGltf_GltfAccessorCompType ComponentType; //!< component type
+  Graphic3d_BndBox3d          BndBox;        //!< bounding box
+
+  //! Empty constructor.
+  RWGltf_GltfAccessor()
+  : Id (INVALID_ID),
+    ByteOffset (0),
+    Count (0),
+    ByteStride (0),
+    Type (RWGltf_GltfAccessorLayout_UNKNOWN),
+    ComponentType (RWGltf_GltfAccessorCompType_UNKNOWN) {}
+
+};
+
+#endif // _RWGltf_GltfAccessor_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfAccessorCompType.hxx b/src/RWGltf/RWGltf_GltfAccessorCompType.hxx
new file mode 100644 (file)
index 0000000..006857f
--- /dev/null
@@ -0,0 +1,30 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfAccessorCompType_HeaderFile
+#define _RWGltf_GltfAccessorCompType_HeaderFile
+
+//! Low-level glTF enumeration defining Accessor component type.
+enum RWGltf_GltfAccessorCompType
+{
+  RWGltf_GltfAccessorCompType_UNKNOWN, //!< unknown or invalid type
+  RWGltf_GltfAccessorCompType_Int8    = 5120, //!< GL_BYTE
+  RWGltf_GltfAccessorCompType_UInt8   = 5121, //!< GL_UNSIGNED_BYTE
+  RWGltf_GltfAccessorCompType_Int16   = 5122, //!< GL_SHORT
+  RWGltf_GltfAccessorCompType_UInt16  = 5123, //!< GL_UNSIGNED_SHORT
+  RWGltf_GltfAccessorCompType_UInt32  = 5125, //!< GL_UNSIGNED_INT
+  RWGltf_GltfAccessorCompType_Float32 = 5126, //!< GL_FLOAT
+};
+
+#endif // _RWGltf_GltfAccessorCompType_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfAccessorLayout.hxx b/src/RWGltf/RWGltf_GltfAccessorLayout.hxx
new file mode 100644 (file)
index 0000000..0fdc818
--- /dev/null
@@ -0,0 +1,68 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfAccessorLayout_HeaderFile
+#define _RWGltf_GltfAccessorLayout_HeaderFile
+
+#include <Standard_CString.hxx>
+
+//! Low-level glTF enumeration defining Accessor layout.
+//! Similar to Graphic3d_TypeOfData but does not define actual type and includes matrices.
+enum RWGltf_GltfAccessorLayout
+{
+  RWGltf_GltfAccessorLayout_UNKNOWN, //!< unknown or invalid type
+  RWGltf_GltfAccessorLayout_Scalar,  //!< "SCALAR"
+  RWGltf_GltfAccessorLayout_Vec2,    //!< "VEC2"
+  RWGltf_GltfAccessorLayout_Vec3,    //!< "VEC3"
+  RWGltf_GltfAccessorLayout_Vec4,    //!< "VEC4"
+  RWGltf_GltfAccessorLayout_Mat2,    //!< "MAT2"
+  RWGltf_GltfAccessorLayout_Mat3,    //!< "MAT3"
+  RWGltf_GltfAccessorLayout_Mat4,    //!< "MAT4"
+};
+
+//! Parse GltfAccessorLayout from string.
+inline RWGltf_GltfAccessorLayout RWGltf_GltfParseAccessorType (const char* theType)
+{
+  if (IsEqual ("SCALAR", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Scalar;
+  }
+  else if (IsEqual ("VEC2", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Vec2;
+  }
+  else if (IsEqual ("VEC3", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Vec3;
+  }
+  else if (IsEqual ("VEC4", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Vec4;
+  }
+  else if (IsEqual ("MAT2", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Mat2;
+  }
+  else if (IsEqual ("MAT3", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Mat3;
+  }
+  else if (IsEqual ("MAT4", theType))
+  {
+    return RWGltf_GltfAccessorLayout_Mat4;
+  }
+  return RWGltf_GltfAccessorLayout_UNKNOWN;
+}
+
+#endif // _RWGltf_GltfAccessorLayout_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfArrayType.hxx b/src/RWGltf/RWGltf_GltfArrayType.hxx
new file mode 100644 (file)
index 0000000..391bcf4
--- /dev/null
@@ -0,0 +1,68 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfArrayType_HeaderFile
+#define _RWGltf_GltfArrayType_HeaderFile
+
+#include <Standard_CString.hxx>
+
+//! Low-level glTF enumeration defining Array type.
+enum RWGltf_GltfArrayType
+{
+  RWGltf_GltfArrayType_UNKNOWN,  //!< unknown or invalid type
+  RWGltf_GltfArrayType_Indices,  //!< "indices"    within "primitive"  element
+  RWGltf_GltfArrayType_Position, //!< "POSITION"   within "attributes" element
+  RWGltf_GltfArrayType_Normal,   //!< "NORMAL"     within "attributes" element
+  RWGltf_GltfArrayType_Color,    //!< "COLOR"      within "attributes" element
+  RWGltf_GltfArrayType_TCoord0,  //!< "TEXCOORD_0" within "attributes" element
+  RWGltf_GltfArrayType_TCoord1,  //!< "TEXCOORD_1" within "attributes" element
+  RWGltf_GltfArrayType_Joint,    //!< "JOINT"      within "attributes" element
+  RWGltf_GltfArrayType_Weight,   //!< "WEIGHT"     within "attributes" element
+};
+
+//! Parse GltfArrayType from string.
+inline RWGltf_GltfArrayType RWGltf_GltfParseAttribType (const char* theType)
+{
+  if (IsEqual ("POSITION", theType))
+  {
+    return RWGltf_GltfArrayType_Position;
+  }
+  else if (IsEqual ("NORMAL", theType))
+  {
+    return RWGltf_GltfArrayType_Normal;
+  }
+  else if (IsEqual ("COLOR", theType))
+  {
+    return RWGltf_GltfArrayType_Color;
+  }
+  else if (IsEqual ("TEXCOORD_0", theType))
+  {
+    return RWGltf_GltfArrayType_TCoord0;
+  }
+  else if (IsEqual ("TEXCOORD_1", theType))
+  {
+    return RWGltf_GltfArrayType_TCoord1;
+  }
+  else if (IsEqual ("JOINT", theType))
+  {
+    return RWGltf_GltfArrayType_Joint;
+  }
+  else if (IsEqual ("WEIGHT", theType))
+  {
+    return RWGltf_GltfArrayType_Weight;
+  }
+  return RWGltf_GltfArrayType_UNKNOWN;
+}
+
+#endif // _RWGltf_GltfArrayType_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfBufferView.hxx b/src/RWGltf/RWGltf_GltfBufferView.hxx
new file mode 100644 (file)
index 0000000..dfdd6ae
--- /dev/null
@@ -0,0 +1,38 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfBufferView_HeaderFile
+#define _RWGltf_GltfBufferView_HeaderFile
+
+#include <RWGltf_GltfBufferViewTarget.hxx>
+#include <Standard_TypeDef.hxx>
+
+//! Low-level glTF data structure defining BufferView.
+struct RWGltf_GltfBufferView
+{
+  static const int INVALID_ID = -1;
+public:
+
+  int                         Id;
+  int64_t                     ByteOffset;
+  int64_t                     ByteLength;
+  int32_t                     ByteStride; //!< [0, 255]
+  RWGltf_GltfBufferViewTarget Target;
+
+  RWGltf_GltfBufferView()
+  : Id (INVALID_ID), ByteOffset (0), ByteLength (0), ByteStride (0), Target (RWGltf_GltfBufferViewTarget_UNKNOWN) {}
+
+};
+
+#endif // _RWGltf_GltfBufferView_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfBufferViewTarget.hxx b/src/RWGltf/RWGltf_GltfBufferViewTarget.hxx
new file mode 100644 (file)
index 0000000..d410aa9
--- /dev/null
@@ -0,0 +1,26 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfBufferViewTarget_HeaderFile
+#define _RWGltf_GltfBufferViewTarget_HeaderFile
+
+//! Low-level glTF enumeration defining BufferView target.
+enum RWGltf_GltfBufferViewTarget
+{
+  RWGltf_GltfBufferViewTarget_UNKNOWN,                      //!< unknown or invalid type
+  RWGltf_GltfBufferViewTarget_ARRAY_BUFFER         = 34962, //!< GL_ARRAY_BUFFER
+  RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER = 34963, //!< GL_ELEMENT_ARRAY_BUFFER
+};
+
+#endif // _RWGltf_GltfBufferViewTarget_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfFace.hxx b/src/RWGltf/RWGltf_GltfFace.hxx
new file mode 100644 (file)
index 0000000..c90a9c1
--- /dev/null
@@ -0,0 +1,29 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfFace_HeaderFile
+#define _RWGltf_GltfFace_HeaderFile
+
+#include <RWGltf_GltfAccessor.hxx>
+
+//! Low-level glTF data structure holding single Face (one primitive array) definition.
+struct RWGltf_GltfFace
+{
+  RWGltf_GltfAccessor NodePos;  //!< accessor for nodal positions
+  RWGltf_GltfAccessor NodeNorm; //!< accessor for nodal normals
+  RWGltf_GltfAccessor NodeUV;   //!< accessor for nodal UV texture coordinates
+  RWGltf_GltfAccessor Indices;  //!< accessor for indexes
+};
+
+#endif // _RWGltf_GltfFace_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfJsonParser.cxx b/src/RWGltf/RWGltf_GltfJsonParser.cxx
new file mode 100644 (file)
index 0000000..154f331
--- /dev/null
@@ -0,0 +1,1623 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "RWGltf_GltfJsonParser.pxx"
+
+#include <BRep_Builder.hxx>
+#include <gp_Quaternion.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <Message_ProgressSentry.hxx>
+#include <OSD_File.hxx>
+#include <OSD_OpenFile.hxx>
+#include <OSD_Path.hxx>
+#include <OSD_ThreadPool.hxx>
+#include <Precision.hxx>
+#include <FSD_Base64Decoder.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+
+#include <fstream>
+
+#ifdef HAVE_RAPIDJSON
+namespace
+{
+  //! Material extension.
+  const char THE_KHR_materials_common[] = "KHR_materials_common";
+  const char THE_KHR_binary_glTF[]      = "KHR_binary_glTF";
+}
+
+//! Find member of the object in a safe way.
+inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
+                                                 const RWGltf_JsonValue& theName)
+{
+  if (!theObject.IsObject()
+   || !theName.IsString())
+  {
+    return NULL;
+  }
+
+  rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
+  return anIter != theObject.MemberEnd()
+       ? &anIter->value
+       : NULL;
+}
+
+//! Find member of the object in a safe way.
+inline const RWGltf_JsonValue* findObjectMember (const RWGltf_JsonValue& theObject,
+                                                 const char*  theName)
+{
+  if (!theObject.IsObject())
+  {
+    return NULL;
+  }
+
+  rapidjson::Document::ConstMemberIterator anIter = theObject.FindMember (theName);
+  return anIter != theObject.MemberEnd()
+       ? &anIter->value
+       : NULL;
+}
+
+// =======================================================================
+// function : RWGltf_GltfJsonParser::FormatParseError
+// purpose  :
+// =======================================================================
+const char* RWGltf_GltfJsonParser::FormatParseError (rapidjson::ParseErrorCode theCode)
+{
+  switch (theCode)
+  {
+    case rapidjson::kParseErrorNone:                          return "";
+    case rapidjson::kParseErrorDocumentEmpty:                 return "Empty Document";
+    case rapidjson::kParseErrorDocumentRootNotSingular:       return "The document root must not follow by other values";
+    case rapidjson::kParseErrorValueInvalid:                  return "Invalid value";
+    case rapidjson::kParseErrorObjectMissName:                return "Missing a name for object member";
+    case rapidjson::kParseErrorObjectMissColon:               return "Missing a colon after a name of object member";
+    case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket: return "Missing a comma or '}' after an object member";
+    case rapidjson::kParseErrorArrayMissCommaOrSquareBracket: return "Missing a comma or ']' after an array element";
+    case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex: return "Incorrect hex digit after \\u escape in string";
+    case rapidjson::kParseErrorStringUnicodeSurrogateInvalid: return "The surrogate pair in string is invalid";
+    case rapidjson::kParseErrorStringEscapeInvalid:           return "Invalid escape character in string";
+    case rapidjson::kParseErrorStringMissQuotationMark:       return "Missing a closing quotation mark in string";
+    case rapidjson::kParseErrorStringInvalidEncoding:         return "Invalid encoding in string";
+    case rapidjson::kParseErrorNumberTooBig:                  return "Number is too big to be stored in double";
+    case rapidjson::kParseErrorNumberMissFraction:            return "Miss fraction part in number";
+    case rapidjson::kParseErrorNumberMissExponent:            return "Miss exponent in number";
+    case rapidjson::kParseErrorTermination:                   return "Parsing was terminated";
+    case rapidjson::kParseErrorUnspecificSyntaxError:         return "Unspecific syntax error";
+  }
+  return "UNKOWN syntax error";
+}
+
+// =======================================================================
+// function : GltfElementMap::Init
+// purpose  :
+// =======================================================================
+void RWGltf_GltfJsonParser::GltfElementMap::Init (const TCollection_AsciiString& theRootName,
+                                                  const RWGltf_JsonValue* theRoot)
+{
+  myRoot = theRoot;
+  myChildren.Clear();
+  if (theRoot == NULL)
+  {
+    return;
+  }
+
+  if (theRoot->IsObject())
+  {
+    // glTF 1.0
+    for (ConstMemberIterator aChildIter = theRoot->MemberBegin(); aChildIter != theRoot->MemberEnd(); ++aChildIter)
+    {
+      if (!aChildIter->name.IsString())
+      {
+        continue;
+      }
+
+      const TCollection_AsciiString aKey (aChildIter->name.GetString());
+      if (!myChildren.Bind (aKey, &aChildIter->value))
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("Invalid glTF syntax - key '")
+                                         + aKey + "' is already defined in '" + theRootName + "'.", Message_Warning);
+      }
+    }
+  }
+  else if (theRoot->IsArray())
+  {
+    // glTF 2.0
+    int aChildIndex = 0;
+    for (rapidjson::Value::ConstValueIterator aChildIter = theRoot->Begin(); aChildIter != theRoot->End(); ++aChildIter, ++aChildIndex)
+    {
+      myChildren.Bind (TCollection_AsciiString (aChildIndex), aChildIter);
+    }
+  }
+}
+#endif
+
+// Auxiliary macros for formatting message.
+#define reportGltfError(theMsg)   reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Fail);
+#define reportGltfWarning(theMsg) reportGltfSyntaxProblem(TCollection_AsciiString() + theMsg, Message_Warning);
+
+// =======================================================================
+// function : reportGltfSyntaxProblem
+// purpose  :
+// =======================================================================
+void RWGltf_GltfJsonParser::reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg,
+                                                    Message_Gravity theGravity)
+{
+  Message::DefaultMessenger()->Send (myErrorPrefix + theMsg, theGravity);
+}
+
+// =======================================================================
+// function : RWGltf_GltfJsonParser
+// purpose  :
+// =======================================================================
+RWGltf_GltfJsonParser::RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes)
+: myRootShapes(&theRootShapes),
+  myAttribMap (NULL),
+  myExternalFiles (NULL),
+  myBinBodyOffset (0),
+  myBinBodyLen (0),
+  myIsBinary (false),
+  myIsGltf1 (false),
+  myToSkipEmptyNodes (true),
+  myToProbeHeader (false)
+{
+  myCSTrsf.SetInputLengthUnit (1.0); // meters
+  myCSTrsf.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
+}
+
+// =======================================================================
+// function : SetFilePath
+// purpose  :
+// =======================================================================
+void RWGltf_GltfJsonParser::SetFilePath (const TCollection_AsciiString& theFilePath)
+{
+  myFilePath = theFilePath;
+  // determine file location to load associated files
+  TCollection_AsciiString aFileName;
+  OSD_Path::FolderAndFileFromPath (theFilePath, myFolder, aFileName);
+}
+
+#ifdef HAVE_RAPIDJSON
+// =======================================================================
+// function : gltfParseRoots
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseRoots()
+{
+  // find glTF root elements for smooth navigation
+  RWGltf_JsonValue aNames[RWGltf_GltfRootElement_NB];
+  for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
+  {
+    aNames[aRootNameIter] = rapidjson::StringRef (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter));
+  }
+
+  for (ConstMemberIterator aRootIter = MemberBegin();
+       aRootIter != MemberEnd(); ++aRootIter)
+  {
+    for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB; ++aRootNameIter)
+    {
+      if (myGltfRoots[aRootNameIter].IsNull()
+            && aNames[aRootNameIter] == aRootIter->name)
+      {
+        // we will not modify JSON document, thus it is OK to keep the pointers
+        myGltfRoots[aRootNameIter].Init (RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter), &aRootIter->value);
+        break;
+      }
+    }
+  }
+
+  for (int aRootNameIter = 0; aRootNameIter < RWGltf_GltfRootElement_NB_MANDATORY; ++aRootNameIter)
+  {
+    if (myGltfRoots[aRootNameIter].IsNull())
+    {
+      reportGltfError ("Member '" + RWGltf_GltfRootElementName ((RWGltf_GltfRootElement )aRootNameIter) + "' is not found.");
+      return false;
+    }
+  }
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseAsset
+// purpose  :
+// =======================================================================
+void RWGltf_GltfJsonParser::gltfParseAsset()
+{
+  const RWGltf_JsonValue* anAsset = myGltfRoots[RWGltf_GltfRootElement_Asset].Root();
+  if (anAsset == NULL)
+  {
+    return;
+  }
+
+  if (const RWGltf_JsonValue* aVersion = findObjectMember (*anAsset, "version"))
+  {
+    if (aVersion->IsString())
+    {
+      TCollection_AsciiString aVerStr (aVersion->GetString());
+      myIsGltf1 = aVerStr.StartsWith ("1.");
+    }
+  }
+
+  if (const RWGltf_JsonValue* aGenerator = findObjectMember (*anAsset, "generator"))
+  {
+    if (aGenerator->IsString())
+    {
+      myMetadata.Add ("generator", aGenerator->GetString());
+    }
+  }
+  if (const RWGltf_JsonValue* aCopyRight = findObjectMember (*anAsset, "copyright"))
+  {
+    if (aCopyRight->IsString())
+    {
+      myMetadata.Add ("copyright", aCopyRight->GetString());
+    }
+  }
+}
+
+// =======================================================================
+// function : gltfParseMaterials
+// purpose  :
+// =======================================================================
+void RWGltf_GltfJsonParser::gltfParseMaterials()
+{
+  const RWGltf_JsonValue* aMatList = myGltfRoots[RWGltf_GltfRootElement_Materials].Root();
+  if (aMatList == NULL)
+  {
+    return;
+  }
+  else if (aMatList->IsObject())
+  {
+    // glTF 1.0
+    for (ConstMemberIterator aMatIter = aMatList->MemberBegin();
+         aMatIter != aMatList->MemberEnd(); ++aMatIter)
+    {
+      Handle(RWGltf_MaterialCommon) aMat;
+      const RWGltf_JsonValue& aMatNode = aMatIter->value;
+      const RWGltf_JsonValue& aMatId   = aMatIter->name;
+      const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
+      if (!gltfParseCommonMaterial (aMat, aMatNode))
+      {
+        if (!gltfParseStdMaterial (aMat, aMatNode))
+        {
+          continue;
+        }
+      }
+
+      if (aNameVal != NULL
+       && aNameVal->IsString())
+      {
+        aMat->Name = aNameVal->GetString();
+      }
+      aMat->Id = aMatId.GetString();
+      myMaterialsCommon.Bind (aMat->Id, aMat);
+    }
+  }
+  else if (aMatList->IsArray())
+  {
+    // glTF 2.0
+    int aMatIndex = 0;
+    for (rapidjson::Value::ConstValueIterator aMatIter = aMatList->Begin(); aMatIter != aMatList->End(); ++aMatIter, ++aMatIndex)
+    {
+      Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
+      const RWGltf_JsonValue& aMatNode = *aMatIter;
+      const RWGltf_JsonValue* aNameVal = findObjectMember (aMatNode, "name");
+      if (gltfParsePbrMaterial (aMatPbr,  aMatNode))
+      {
+        if (aNameVal != NULL
+         && aNameVal->IsString())
+        {
+          aMatPbr->Name = aNameVal->GetString();
+        }
+        aMatPbr->Id = TCollection_AsciiString ("mat_") + aMatIndex;
+        myMaterialsPbr.Bind (TCollection_AsciiString (aMatIndex), aMatPbr);
+      }
+
+      Handle(RWGltf_MaterialCommon) aMatCommon;
+      if (gltfParseCommonMaterial(aMatCommon, aMatNode)
+       || gltfParseStdMaterial   (aMatCommon, aMatNode))
+      {
+        if (aNameVal != NULL
+         && aNameVal->IsString())
+        {
+          aMatCommon->Name = aNameVal->GetString();
+        }
+        aMatCommon->Id = TCollection_AsciiString ("mat_") + aMatIndex;
+        myMaterialsCommon.Bind (TCollection_AsciiString (aMatIndex), aMatCommon);
+      }
+    }
+  }
+}
+
+// =======================================================================
+// function : gltfParseStdMaterial
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
+                                                  const RWGltf_JsonValue& theMatNode)
+{
+  //const RWGltf_JsonValue* aTechVal = findObjectMember (theMatNode, "technique");
+  const RWGltf_JsonValue* aValues  = findObjectMember (theMatNode, "values");
+  if (aValues == NULL)
+  {
+    return false;
+  }
+
+  const RWGltf_JsonValue* anAmbVal = findObjectMember (*aValues, "ambient");
+  const RWGltf_JsonValue* aDiffVal = findObjectMember (*aValues, "diffuse");
+  const RWGltf_JsonValue* anEmiVal = findObjectMember (*aValues, "emission");
+  const RWGltf_JsonValue* aSpecVal = findObjectMember (*aValues, "specular");
+  const RWGltf_JsonValue* aShinVal = findObjectMember (*aValues, "shininess");
+  if (anAmbVal == NULL
+   && aDiffVal == NULL
+   && anEmiVal == NULL
+   && aSpecVal == NULL
+   && aShinVal == NULL)
+  {
+    return false;
+  }
+
+  theMat = new RWGltf_MaterialCommon();
+
+  Graphic3d_Vec4d anAmb, aDiff, anEmi, aSpec;
+  if (anAmbVal != NULL
+   && anAmbVal->IsString())
+  {
+    gltfParseTexture (theMat->AmbientTexture, anAmbVal);
+  }
+  else if (gltfReadVec4   (anAmb, anAmbVal)
+        && validateColor4 (anAmb))
+  {
+    theMat->AmbientColor = Quantity_Color (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_RGB);
+  }
+
+  if (aDiffVal != NULL
+   && aDiffVal->IsString())
+  {
+    gltfParseTexture (theMat->DiffuseTexture, aDiffVal);
+  }
+  else if (gltfReadVec4   (aDiff, aDiffVal)
+        && validateColor4 (aDiff))
+  {
+    theMat->DiffuseColor = Quantity_Color (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_RGB);
+    theMat->Transparency = float(1.0 - aDiff.a());
+  }
+
+  if (gltfReadVec4   (anEmi, anEmiVal)
+   && validateColor4 (anEmi))
+  {
+    theMat->EmissiveColor = Quantity_Color (anEmi.r(), anEmi.g(), anEmi.b(), Quantity_TOC_RGB);
+  }
+
+  if (aSpecVal != NULL
+   && aSpecVal->IsString())
+  {
+    gltfParseTexture (theMat->SpecularTexture, aSpecVal);
+  }
+  if (gltfReadVec4   (aSpec, aSpecVal)
+   && validateColor4 (aSpec))
+  {
+    theMat->SpecularColor = Quantity_Color (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_RGB);
+  }
+
+  if (aShinVal != NULL
+   && aShinVal->IsNumber())
+  {
+    const double aSpecular = aShinVal->GetDouble();
+    if (aSpecular >= 0)
+    {
+      theMat->Shininess = (float )Min (aSpecular / 1000.0, 1.0);
+    }
+  }
+  return true;
+}
+
+// =======================================================================
+// function : gltfParsePbrMaterial
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
+                                                  const RWGltf_JsonValue& theMatNode)
+{
+  /*if (const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions"))
+  {
+    if (const RWGltf_JsonValue* anExtDefVal = findObjectMember (*anExtVal, "KHR_materials_pbrSpecularGlossiness"))
+    {
+      const RWGltf_JsonValue* aDiffTexVal = findObjectMember (*anExtDefVal, "diffuseTexture");
+      const RWGltf_JsonValue* aSpecTexVal = findObjectMember (*anExtDefVal, "specularGlossinessTexture");
+    }
+  }*/
+
+  const RWGltf_JsonValue* aMetalRoughVal    = findObjectMember (theMatNode, "pbrMetallicRoughness");
+  const RWGltf_JsonValue* aNormTexVal       = findObjectMember (theMatNode, "normalTexture");
+  const RWGltf_JsonValue* anEmissFactorVal  = findObjectMember (theMatNode, "emissiveFactor");
+  const RWGltf_JsonValue* anEmissTexVal     = findObjectMember (theMatNode, "emissiveTexture");
+  const RWGltf_JsonValue* anOcclusionTexVal = findObjectMember (theMatNode, "occlusionTexture");
+  if (aMetalRoughVal == NULL)
+  {
+    return false;
+  }
+
+  theMat = new RWGltf_MaterialMetallicRoughness();
+  const RWGltf_JsonValue* aBaseColorFactorVal = findObjectMember (*aMetalRoughVal, "baseColorFactor");
+  const RWGltf_JsonValue* aBaseColorTexVal    = findObjectMember (*aMetalRoughVal, "baseColorTexture");
+  const RWGltf_JsonValue* aMetallicFactorVal  = findObjectMember (*aMetalRoughVal, "metallicFactor");
+  const RWGltf_JsonValue* aRoughnessFactorVal = findObjectMember (*aMetalRoughVal, "roughnessFactor");
+  const RWGltf_JsonValue* aMetalRoughTexVal   = findObjectMember (*aMetalRoughVal, "metallicRoughnessTexture");
+
+  if (aBaseColorTexVal != NULL
+   && aBaseColorTexVal->IsObject())
+  {
+    if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aBaseColorTexVal, "index"))
+    {
+      gltfParseTexture (theMat->BaseColorTexture, aTexIndexVal);
+    }
+  }
+
+  Graphic3d_Vec4d aBaseColorFactor;
+  if (gltfReadVec4   (aBaseColorFactor, aBaseColorFactorVal)
+   && validateColor4 (aBaseColorFactor))
+  {
+    theMat->BaseColor = Quantity_ColorRGBA (Graphic3d_Vec4 (aBaseColorFactor));
+  }
+
+  Graphic3d_Vec3d anEmissiveFactor;
+  if (gltfReadVec3   (anEmissiveFactor, anEmissFactorVal)
+   && validateColor3 (anEmissiveFactor))
+  {
+    theMat->EmissiveFactor = Graphic3d_Vec3 (anEmissiveFactor);
+  }
+
+  if (aMetalRoughTexVal != NULL
+   && aMetalRoughTexVal->IsObject())
+  {
+    if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aMetalRoughTexVal, "index"))
+    {
+      gltfParseTexture (theMat->MetallicRoughnessTexture, aTexIndexVal);
+    }
+  }
+
+  if (aMetallicFactorVal != NULL
+   && aMetallicFactorVal->IsNumber())
+  {
+    theMat->Metallic = (float )aMetallicFactorVal->GetDouble();
+  }
+
+  if (aRoughnessFactorVal != NULL
+   && aRoughnessFactorVal->IsNumber())
+  {
+    theMat->Roughness = (float )aRoughnessFactorVal->GetDouble();
+  }
+
+  if (aNormTexVal != NULL
+   && aNormTexVal->IsObject())
+  {
+    if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*aNormTexVal, "index"))
+    {
+      gltfParseTexture (theMat->NormalTexture, aTexIndexVal);
+    }
+  }
+
+  if (anEmissTexVal != NULL
+   && anEmissTexVal->IsObject())
+  {
+    if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anEmissTexVal, "index"))
+    {
+      gltfParseTexture (theMat->EmissiveTexture, aTexIndexVal);
+    }
+  }
+
+  if (anOcclusionTexVal != NULL
+   && anOcclusionTexVal->IsObject())
+  {
+    if (const RWGltf_JsonValue* aTexIndexVal = findObjectMember (*anOcclusionTexVal, "index"))
+    {
+      gltfParseTexture (theMat->OcclusionTexture, aTexIndexVal);
+    }
+  }
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseCommonMaterial
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
+                                                     const RWGltf_JsonValue& theMatNode)
+{
+  const RWGltf_JsonValue* anExtVal = findObjectMember (theMatNode, "extensions");
+  if (anExtVal == NULL)
+  {
+    return false;
+  }
+
+  const RWGltf_JsonValue* aMatCommon = findObjectMember (*anExtVal, THE_KHR_materials_common);
+  if (aMatCommon == NULL)
+  {
+    return false;
+  }
+
+  if (!gltfParseStdMaterial (theMat, *aMatCommon))
+  {
+    return false;
+  }
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseTexture
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseTexture (Handle(Image_Texture)& theTexture,
+                                              const RWGltf_JsonValue* theTextureId)
+{
+  if (theTextureId == NULL
+  ||  myGltfRoots[RWGltf_GltfRootElement_Textures].IsNull()
+  ||  myGltfRoots[RWGltf_GltfRootElement_Images].IsNull())
+  {
+    return false;
+  }
+
+  const TCollection_AsciiString aTextureId = getKeyString (*theTextureId);
+  const RWGltf_JsonValue* aTexNode = myGltfRoots[RWGltf_GltfRootElement_Textures].FindChild (*theTextureId);
+  if (aTexNode == NULL)
+  {
+    reportGltfWarning ("Texture node '" + aTextureId + "' is not found.");
+    return false;
+  }
+
+  const RWGltf_JsonValue* aSrcVal  = findObjectMember (*aTexNode, "source");
+  const RWGltf_JsonValue* aTargVal = findObjectMember (*aTexNode, "target");
+  if (aSrcVal == NULL)
+  {
+    reportGltfWarning ("Invalid texture node '" + aTextureId + "' without a 'source' property.");
+    return false;
+  }
+  if (aTargVal != NULL
+   && aTargVal->IsNumber()
+   && aTargVal->GetInt() != 3553) // GL_TEXTURE_2D
+  {
+    return false;
+  }
+
+  const RWGltf_JsonValue* anImgNode = myGltfRoots[RWGltf_GltfRootElement_Images].FindChild (*aSrcVal);
+  if (anImgNode == NULL)
+  {
+    reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to non-existing image '" + getKeyString (*aSrcVal) + "'.");
+    return false;
+  }
+
+  if (myIsBinary)
+  {
+    const RWGltf_JsonValue* aBinVal = NULL;
+    const RWGltf_JsonValue* aBufferViewName = findObjectMember (*anImgNode, "bufferView");
+    if (aBufferViewName != NULL)
+    {
+      aBinVal = anImgNode;
+    }
+    else if (myIsGltf1)
+    {
+      const RWGltf_JsonValue* anExtVal = findObjectMember (*anImgNode, "extensions");
+      if (anExtVal != NULL)
+      {
+        aBinVal = findObjectMember (*anExtVal, THE_KHR_binary_glTF);
+        if (aBinVal != NULL)
+        {
+          aBufferViewName = findObjectMember (*aBinVal, "bufferView");
+        }
+      }
+    }
+
+    if (aBinVal != NULL)
+    {
+      //const RWGltf_JsonValue* aMimeTypeVal = findObjectMember (*aBinVal, "mimeType");
+      //const RWGltf_JsonValue* aWidthVal    = findObjectMember (*aBinVal, "width");
+      //const RWGltf_JsonValue* aHeightVal   = findObjectMember (*aBinVal, "height");
+      if (aBufferViewName == NULL)
+      {
+        reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid data source.");
+        return false;
+      }
+
+      const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
+      if (aBufferView == NULL
+      || !aBufferView->IsObject())
+      {
+        reportGltfWarning ("Invalid texture node '" + aTextureId + "' points to invalid buffer view '" + getKeyString (*aBufferViewName) + "'.");
+        return false;
+      }
+
+      const RWGltf_JsonValue* aBufferName = findObjectMember (*aBufferView, "buffer");
+      const RWGltf_JsonValue* aByteLength = findObjectMember (*aBufferView, "byteLength");
+      const RWGltf_JsonValue* aByteOffset = findObjectMember (*aBufferView, "byteOffset");
+      if (aBufferName != NULL
+      &&  aBufferName->IsString()
+      && !IsEqual (aBufferName->GetString(), "binary_glTF"))
+      {
+        reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' does not define binary_glTF buffer.");
+        return false;
+      }
+
+      RWGltf_GltfBufferView aBuffView;
+      aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
+                           ? (int64_t )aByteOffset->GetDouble()
+                           : 0;
+      aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
+                           ? (int64_t )aByteLength->GetDouble()
+                           : 0;
+      if (aBuffView.ByteLength < 0)
+      {
+        reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteLength.");
+        return false;
+      }
+      else if (aBuffView.ByteOffset < 0)
+      {
+        reportGltfError ("BufferView '" + getKeyString (*aBufferViewName) + "' defines invalid byteOffset.");
+        return false;
+      }
+
+
+      const int64_t anOffset = myBinBodyOffset + aBuffView.ByteOffset;
+      theTexture = new Image_Texture (myFilePath, anOffset, aBuffView.ByteLength);
+      return true;
+    }
+  }
+
+  const RWGltf_JsonValue* anUriVal = findObjectMember (*anImgNode, "uri");
+  if (anUriVal == NULL
+  || !anUriVal->IsString())
+  {
+    return false;
+  }
+
+  const char* anUriData = anUriVal->GetString();
+  if (::strncmp (anUriData, "data:", 5) == 0) // data:image/png;base64
+  {
+    // uncompressing base64 here is inefficient, because the same image can be shared by several nodes
+    const char* aDataStart = anUriData + 5;
+    for (const char* aDataIter = aDataStart; *aDataIter != '\0'; ++aDataIter)
+    {
+      if (::memcmp (aDataIter, ";base64,", 8) == 0)
+      {
+        const char* aBase64End  = anUriData + anUriVal->GetStringLength();
+        const char* aBase64Data = aDataIter + 8;
+        const size_t aBase64Len = size_t(aBase64End - aBase64Data);
+        //const TCollection_AsciiString aMime (aDataStart, aDataIter - aDataStart);
+        Handle(NCollection_Buffer) aData = FSD_Base64Decoder::Decode ((const Standard_Byte* )aBase64Data, aBase64Len);
+        theTexture = new Image_Texture (aData, myFilePath + "@" + getKeyString (*aSrcVal));
+        return true;
+      }
+    }
+    Message::DefaultMessenger()->Send ("glTF reader - embedded image has been skipped", Message_Warning);
+    return false;
+  }
+
+  TCollection_AsciiString anImageFile = myFolder + anUriVal->GetString();
+  theTexture = new Image_Texture (anImageFile);
+  if (myExternalFiles != NULL)
+  {
+    myExternalFiles->Add (anImageFile);
+  }
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseScene
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress)
+{
+  // search default scene
+  const RWGltf_JsonValue* aDefScene = myGltfRoots[RWGltf_GltfRootElement_Scenes].FindChild (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root());
+  if (aDefScene == NULL)
+  {
+    reportGltfError ("Default scene is not found.");
+    return false;
+  }
+
+  const RWGltf_JsonValue* aSceneNodes = findObjectMember (*aDefScene, "nodes");
+  if (aSceneNodes == NULL
+  || !aSceneNodes->IsArray())
+  {
+    reportGltfError ("Empty scene '" + getKeyString (*myGltfRoots[RWGltf_GltfRootElement_Scene].Root()) + "'.");
+    return false;
+  }
+
+  return gltfParseSceneNodes (*myRootShapes, *aSceneNodes, theProgress);
+}
+
+// =======================================================================
+// function : gltfParseSceneNodes
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
+                                                 const RWGltf_JsonValue& theSceneNodes,
+                                                 const Handle(Message_ProgressIndicator)& theProgress)
+{
+  if (!theSceneNodes.IsArray())
+  {
+    reportGltfError ("Scene nodes is not array.");
+    return false;
+  }
+
+  Message_ProgressSentry aPSentry (theProgress, "Reading scene nodes", 0, theSceneNodes.Size(), 1);
+  for (rapidjson::Value::ConstValueIterator aSceneNodeIter = theSceneNodes.Begin();
+       aSceneNodeIter != theSceneNodes.End() && aPSentry.More(); ++aSceneNodeIter, aPSentry.Next())
+  {
+    const RWGltf_JsonValue* aSceneNode = myGltfRoots[RWGltf_GltfRootElement_Nodes].FindChild (*aSceneNodeIter);
+    if (aSceneNode == NULL)
+    {
+      reportGltfWarning ("Scene refers to non-existing node '" + getKeyString (*aSceneNodeIter) + "'.");
+      return true;
+    }
+
+    TopoDS_Shape aNodeShape;
+    if (!gltfParseSceneNode (aNodeShape, getKeyString (*aSceneNodeIter), *aSceneNode, theProgress))
+    {
+      return false;
+    }
+
+    if (aNodeShape.IsNull())
+    {
+      continue;
+    }
+    else if (myToSkipEmptyNodes
+         && !TopExp_Explorer (aNodeShape, TopAbs_FACE).More())
+    {
+      continue;
+    }
+
+    theShapeSeq.Append (aNodeShape);
+  }
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseSceneNode
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseSceneNode (TopoDS_Shape& theNodeShape,
+                                                const TCollection_AsciiString& theSceneNodeId,
+                                                const RWGltf_JsonValue& theSceneNode,
+                                                const Handle(Message_ProgressIndicator)& theProgress)
+{
+  const RWGltf_JsonValue* aName         = findObjectMember (theSceneNode, "name");
+  //const RWGltf_JsonValue* aJointName    = findObjectMember (theSceneNode, "jointName");
+  const RWGltf_JsonValue* aChildren     = findObjectMember (theSceneNode, "children");
+  const RWGltf_JsonValue* aMeshes_1     = findObjectMember (theSceneNode, "meshes");
+  const RWGltf_JsonValue* aMesh_2       = findObjectMember (theSceneNode, "mesh");
+  //const RWGltf_JsonValue* aCamera       = findObjectMember (theSceneNode, "camera");
+  const RWGltf_JsonValue* aTrsfMatVal   = findObjectMember (theSceneNode, "matrix");
+  const RWGltf_JsonValue* aTrsfRotVal   = findObjectMember (theSceneNode, "rotation");
+  const RWGltf_JsonValue* aTrsfScaleVal = findObjectMember (theSceneNode, "scale");
+  const RWGltf_JsonValue* aTrsfTransVal = findObjectMember (theSceneNode, "translation");
+  if (findNodeShape (theNodeShape, theSceneNodeId))
+  {
+    return true;
+  }
+
+  TopLoc_Location aNodeLoc;
+  const bool hasTrs = aTrsfRotVal   != NULL
+                   || aTrsfScaleVal != NULL
+                   || aTrsfTransVal != NULL;
+  if (aTrsfMatVal != NULL)
+  {
+    if (hasTrs)
+    {
+      reportGltfError ("Scene node '" + theSceneNodeId + "' defines ambiguous transformation.");
+      return false;
+    }
+    else if (!aTrsfMatVal->IsArray()
+           || aTrsfMatVal->Size() != 16)
+    {
+      reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix array.");
+      return false;
+    }
+
+    Graphic3d_Mat4d aMat4;
+    for (int aColIter = 0; aColIter < 4; ++aColIter)
+    {
+      for (int aRowIter = 0; aRowIter < 4; ++aRowIter)
+      {
+        const RWGltf_JsonValue& aGenVal = (*aTrsfMatVal)[aColIter * 4 + aRowIter];
+        if (!aGenVal.IsNumber())
+        {
+          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid transformation matrix.");
+          return false;
+        }
+        aMat4.SetValue (aRowIter, aColIter, aGenVal.GetDouble());
+      }
+    }
+
+    if (!aMat4.IsIdentity())
+    {
+      gp_Trsf aTrsf;
+      aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
+                       aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
+                       aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
+      myCSTrsf.TransformTransformation (aTrsf);
+      if (aTrsf.Form() != gp_Identity)
+      {
+        aNodeLoc = TopLoc_Location (aTrsf);
+      }
+    }
+  }
+  else if (hasTrs)
+  {
+    gp_Trsf aTrsf;
+    if (aTrsfRotVal != NULL)
+    {
+      if (!aTrsfRotVal->IsArray()
+        || aTrsfRotVal->Size() != 4)
+      {
+        reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation quaternion.");
+        return false;
+      }
+
+      Graphic3d_Vec4d aRotVec4;
+      for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
+      {
+        const RWGltf_JsonValue& aGenVal = (*aTrsfRotVal)[aCompIter];
+        if (!aGenVal.IsNumber())
+        {
+          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid rotation.");
+          return false;
+        }
+        aRotVec4[aCompIter] = aGenVal.GetDouble();
+      }
+      const gp_Quaternion aQuaternion (aRotVec4.x(), aRotVec4.y(), aRotVec4.z(), aRotVec4.w());
+      if (Abs (aQuaternion.X())       > gp::Resolution()
+       || Abs (aQuaternion.Y())       > gp::Resolution()
+       || Abs (aQuaternion.Z())       > gp::Resolution()
+       || Abs (aQuaternion.W() - 1.0) > gp::Resolution())
+      {
+        aTrsf.SetRotation (aQuaternion);
+      }
+    }
+
+    if (aTrsfTransVal != NULL)
+    {
+      if (!aTrsfTransVal->IsArray()
+        || aTrsfTransVal->Size() != 3)
+      {
+        reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation vector.");
+        return false;
+      }
+
+      gp_XYZ aTransVec;
+      for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+      {
+        const RWGltf_JsonValue& aGenVal = (*aTrsfTransVal)[aCompIter];
+        if (!aGenVal.IsNumber())
+        {
+          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid translation.");
+          return false;
+        }
+        aTransVec.SetCoord (aCompIter + 1, aGenVal.GetDouble());
+      }
+      aTrsf.SetTranslationPart (aTransVec);
+    }
+
+    if (aTrsfScaleVal != NULL)
+    {
+      Graphic3d_Vec3d aScaleVec;
+      if (!aTrsfScaleVal->IsArray()
+        || aTrsfScaleVal->Size() != 3)
+      {
+        reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale vector.");
+        return false;
+      }
+      for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+      {
+        const RWGltf_JsonValue& aGenVal = (*aTrsfScaleVal)[aCompIter];
+        if (!aGenVal.IsNumber())
+        {
+          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
+          return false;
+        }
+        aScaleVec[aCompIter] = aGenVal.GetDouble();
+        if (Abs (aScaleVec[aCompIter]) <= gp::Resolution())
+        {
+          reportGltfError ("Scene node '" + theSceneNodeId + "' defines invalid scale.");
+          return false;
+        }
+      }
+
+      if (Abs (aScaleVec.x() - aScaleVec.y()) > Precision::Confusion()
+       || Abs (aScaleVec.y() - aScaleVec.z()) > Precision::Confusion()
+       || Abs (aScaleVec.x() - aScaleVec.z()) > Precision::Confusion())
+      {
+        Graphic3d_Mat4d aScaleMat;
+        aScaleMat.SetDiagonal (aScaleVec);
+
+        Graphic3d_Mat4d aMat4;
+        aTrsf.GetMat4 (aMat4);
+
+        aMat4 = aMat4 * aScaleMat;
+        aTrsf = gp_Trsf();
+        aTrsf.SetValues (aMat4.GetValue (0, 0), aMat4.GetValue (0, 1), aMat4.GetValue (0, 2), aMat4.GetValue (0, 3),
+                         aMat4.GetValue (1, 0), aMat4.GetValue (1, 1), aMat4.GetValue (1, 2), aMat4.GetValue (1, 3),
+                         aMat4.GetValue (2, 0), aMat4.GetValue (2, 1), aMat4.GetValue (2, 2), aMat4.GetValue (2, 3));
+
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("glTF reader, scene node '")
+                                         + theSceneNodeId + "' defines unsupported scaling "
+                                         + aScaleVec.x() + " " + aScaleVec.y() + " " + aScaleVec.z(), Message_Warning);
+      }
+      else if (Abs (aScaleVec.x() - 1.0) > Precision::Confusion())
+      {
+        aTrsf.SetScaleFactor (aScaleVec.x());
+      }
+    }
+
+    myCSTrsf.TransformTransformation (aTrsf);
+    if (aTrsf.Form() != gp_Identity)
+    {
+      aNodeLoc = TopLoc_Location (aTrsf);
+    }
+  }
+
+  BRep_Builder aBuilder;
+  TopoDS_Compound aNodeShape;
+  aBuilder.MakeCompound (aNodeShape);
+  TopTools_SequenceOfShape aChildShapes;
+  int aNbSubShapes = 0;
+  if (aChildren != NULL
+  && !gltfParseSceneNodes (aChildShapes, *aChildren, theProgress))
+  {
+    theNodeShape = aNodeShape;
+    bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+    return false;
+  }
+  for (TopTools_SequenceOfShape::Iterator aChildShapeIter (aChildShapes); aChildShapeIter.More(); aChildShapeIter.Next())
+  {
+    aBuilder.Add (aNodeShape, aChildShapeIter.Value());
+    ++aNbSubShapes;
+  }
+
+  if (aMeshes_1 != NULL
+   && aMeshes_1->IsArray())
+  {
+    // glTF 1.0
+    Message_ProgressSentry aPSentry (theProgress, "Reading scene meshes", 0, aMeshes_1->Size(), 1);
+    for (rapidjson::Value::ConstValueIterator aMeshIter = aMeshes_1->Begin();
+         aMeshIter != aMeshes_1->End() && aPSentry.More(); ++aMeshIter, aPSentry.Next())
+    {
+      const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMeshIter);
+      if (aMesh == NULL)
+      {
+        theNodeShape = aNodeShape;
+        bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+        reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
+        return false;
+      }
+
+      TopoDS_Shape aMeshShape;
+      if (!gltfParseMesh (aMeshShape, getKeyString (*aMeshIter), *aMesh, theProgress))
+      {
+        theNodeShape = aNodeShape;
+        bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+        return false;
+      }
+      if (!aMeshShape.IsNull())
+      {
+        aBuilder.Add (aNodeShape, aMeshShape);
+        ++aNbSubShapes;
+      }
+    }
+  }
+  if (aMesh_2 != NULL)
+  {
+    // glTF 2.0
+    const RWGltf_JsonValue* aMesh = myGltfRoots[RWGltf_GltfRootElement_Meshes].FindChild (*aMesh_2);
+    if (aMesh == NULL)
+    {
+      theNodeShape = aNodeShape;
+      bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+      reportGltfError ("Scene node '" + theSceneNodeId + "' refers to non-existing mesh.");
+      return false;
+    }
+
+    TopoDS_Shape aMeshShape;
+    if (!gltfParseMesh (aMeshShape, getKeyString (*aMesh_2), *aMesh, theProgress))
+    {
+      theNodeShape = aNodeShape;
+      bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+      return false;
+    }
+    if (!aMeshShape.IsNull())
+    {
+      aBuilder.Add (aNodeShape, aMeshShape);
+      ++aNbSubShapes;
+    }
+  }
+
+  if (aNbSubShapes == 1)
+  {
+    theNodeShape = TopoDS_Iterator (aNodeShape).Value();
+  }
+  else
+  {
+    theNodeShape = aNodeShape;
+  }
+  bindNodeShape (theNodeShape, aNodeLoc, theSceneNodeId, aName);
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseMesh
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseMesh (TopoDS_Shape& theMeshShape,
+                                           const TCollection_AsciiString& theMeshId,
+                                           const RWGltf_JsonValue& theMesh,
+                                           const Handle(Message_ProgressIndicator)& theProgress)
+{
+  const RWGltf_JsonValue* aName  = findObjectMember (theMesh, "name");
+  const RWGltf_JsonValue* aPrims = findObjectMember (theMesh, "primitives");
+  if (!aPrims->IsArray())
+  {
+    reportGltfError ("Primitive array attributes within Mesh '" + theMeshId + "' is not an array.");
+    return false;
+  }
+
+  if (findMeshShape (theMeshShape, theMeshId))
+  {
+    return true;
+  }
+
+  BRep_Builder aBuilder;
+  TopoDS_Compound aMeshShape;
+  int aNbFaces = 0;
+  for (rapidjson::Value::ConstValueIterator aPrimArrIter = aPrims->Begin();
+       aPrimArrIter != aPrims->End(); ++aPrimArrIter)
+  {
+    TCollection_AsciiString aUserName;
+    if (aName != NULL
+     && aName->IsString())
+    {
+      aUserName = aName->GetString();
+    }
+
+    Handle(RWGltf_GltfLatePrimitiveArray) aMeshData = new RWGltf_GltfLatePrimitiveArray (theMeshId, aUserName);
+    if (!gltfParsePrimArray (aMeshData, theMeshId, *aPrimArrIter, theProgress))
+    {
+      return false;
+    }
+
+    if (!aMeshData->Data().IsEmpty())
+    {
+      if (aMeshShape.IsNull())
+      {
+        aBuilder.MakeCompound (aMeshShape);
+      }
+
+      TopoDS_Face aFace;
+      aBuilder.MakeFace (aFace, aMeshData);
+      aBuilder.Add (aMeshShape, aFace);
+      if (myAttribMap != NULL
+       && aMeshData->HasStyle())
+      {
+        RWMesh_NodeAttributes aShapeAttribs;
+        aShapeAttribs.RawName = aUserName;
+        aShapeAttribs.Style.SetColorSurf (aMeshData->BaseColor());
+        myAttribMap->Bind (aFace, aShapeAttribs);
+      }
+      myFaceList.Append (aFace);
+      ++aNbFaces;
+    }
+  }
+
+  if (aNbFaces == 1)
+  {
+    theMeshShape = TopoDS_Iterator (aMeshShape).Value();
+  }
+  else
+  {
+    theMeshShape = aMeshShape;
+  }
+  bindMeshShape (theMeshShape, theMeshId, aName);
+  return true;
+}
+
+// =======================================================================
+// function : gltfParsePrimArray
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                                const TCollection_AsciiString& theMeshId,
+                                                const RWGltf_JsonValue& thePrimArray,
+                                                const Handle(Message_ProgressIndicator)& /*theProgress*/)
+{
+  const RWGltf_JsonValue* anAttribs = findObjectMember (thePrimArray, "attributes");
+  const RWGltf_JsonValue* anIndices = findObjectMember (thePrimArray, "indices");
+  const RWGltf_JsonValue* aMaterial = findObjectMember (thePrimArray, "material");
+  const RWGltf_JsonValue* aModeVal  = findObjectMember (thePrimArray, "mode");
+  RWGltf_GltfPrimitiveMode aMode = RWGltf_GltfPrimitiveMode_Triangles;
+  if (anAttribs == NULL
+  || !anAttribs->IsObject())
+  {
+    reportGltfError ("Primitive array within Mesh '" + theMeshId + "' defines no attributes.");
+    return false;
+  }
+  else if (aModeVal != NULL)
+  {
+    aMode = RWGltf_GltfPrimitiveMode_UNKNOWN;
+    if (aModeVal->IsInt())
+    {
+      aMode = (RWGltf_GltfPrimitiveMode )aModeVal->GetInt();
+    }
+    if (aMode < RWGltf_GltfPrimitiveMode_Points
+     || aMode > RWGltf_GltfPrimitiveMode_TriangleFan)
+    {
+      reportGltfError ("Primitive array within Mesh '" + theMeshId + "' has unknown mode.");
+      return false;
+    }
+  }
+  if (aMode != RWGltf_GltfPrimitiveMode_Triangles)
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Primitive array within Mesh '"
+                                                   + theMeshId + "' skipped due to unsupported mode.", Message_Warning);
+    return true;
+  }
+  theMeshData->SetPrimitiveMode (aMode);
+
+  // assign material
+  if (aMaterial != NULL)
+  {
+    Handle(RWGltf_MaterialMetallicRoughness) aMatPbr;
+    if (myMaterialsPbr.Find (getKeyString (*aMaterial), aMatPbr))
+    {
+      theMeshData->SetMaterialPbr (aMatPbr);
+    }
+
+    Handle(RWGltf_MaterialCommon) aMatCommon;
+    if (myMaterialsCommon.Find (getKeyString (*aMaterial), aMatCommon))
+    {
+      theMeshData->SetMaterialCommon (aMatCommon);
+    }
+  }
+
+  bool hasPositions = false;
+  for (rapidjson::Value::ConstMemberIterator anAttribIter = anAttribs->MemberBegin();
+       anAttribIter != anAttribs->MemberEnd(); ++anAttribIter)
+  {
+    const TCollection_AsciiString anAttribId = getKeyString (anAttribIter->value);
+    if (anAttribId.IsEmpty())
+    {
+      reportGltfError ("Primitive array attribute accessor key within Mesh '" + theMeshId + "' is not a string.");
+      return false;
+    }
+
+    RWGltf_GltfArrayType aType = RWGltf_GltfParseAttribType (anAttribIter->name.GetString());
+    if (aType == RWGltf_GltfArrayType_UNKNOWN)
+    {
+      // just ignore unknown attributes
+      continue;
+    }
+
+    const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (anAttribIter->value);
+    if (anAccessor == NULL
+    || !anAccessor->IsObject())
+    {
+      reportGltfError ("Primitive array attribute accessor key '" + anAttribId + "' points to non-existing object.");
+      return false;
+    }
+    else if (!gltfParseAccessor (theMeshData, anAttribId, *anAccessor, aType))
+    {
+      return false;
+    }
+    else if (aType == RWGltf_GltfArrayType_Position)
+    {
+      hasPositions = true;
+    }
+  }
+  if (!hasPositions)
+  {
+    reportGltfError ("Primitive array within Mesh '" + theMeshId + "' does not define vertex positions.");
+    return false;
+  }
+
+  if (anIndices != NULL)
+  {
+    const TCollection_AsciiString anIndicesId = getKeyString (*anIndices);
+    const RWGltf_JsonValue* anAccessor = myGltfRoots[RWGltf_GltfRootElement_Accessors].FindChild (*anIndices);
+    if (anAccessor == NULL
+    || !anAccessor->IsObject())
+    {
+      reportGltfError ("Primitive array indices accessor key '" + anIndicesId + "' points to non-existing object.");
+      return false;
+    }
+    else if (!gltfParseAccessor (theMeshData, anIndicesId, *anAccessor, RWGltf_GltfArrayType_Indices))
+    {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// =======================================================================
+// function : gltfParseAccessor
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                               const TCollection_AsciiString& theName,
+                                               const RWGltf_JsonValue& theAccessor,
+                                               const RWGltf_GltfArrayType theType)
+{
+  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* aByteStride     = findObjectMember (theAccessor, "byteStride");
+  const RWGltf_JsonValue* aCompType       = findObjectMember (theAccessor, "componentType");
+  const RWGltf_JsonValue* aCount          = findObjectMember (theAccessor, "count");
+  if (aTypeStr == NULL
+  || !aTypeStr->IsString())
+  {
+    reportGltfError ("Accessor '" + theName + "' does not define type.");
+    return false;
+  }
+  aStruct.Type = RWGltf_GltfParseAccessorType (aTypeStr->GetString());
+  if (aStruct.Type == RWGltf_GltfAccessorLayout_UNKNOWN)
+  {
+    reportGltfError ("Accessor '" + theName + "' has invalid type.");
+    return false;
+  }
+
+  if (aBufferViewName == NULL)
+  {
+    reportGltfError ("Accessor '" + theName + "' does not define bufferView.");
+    return false;
+  }
+  if (aCompType == NULL
+  || !aCompType->IsInt())
+  {
+    reportGltfError ("Accessor '" + theName + "' does not define componentType.");
+    return false;
+  }
+  aStruct.ComponentType = (RWGltf_GltfAccessorCompType )aCompType->GetInt();
+  if (aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int8
+   && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt8
+   && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Int16
+   && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt16
+   && aStruct.ComponentType != RWGltf_GltfAccessorCompType_UInt32
+   && aStruct.ComponentType != RWGltf_GltfAccessorCompType_Float32)
+  {
+    reportGltfError ("Accessor '" + theName + "' defines invalid componentType value.");
+    return false;
+  }
+
+  if (aCount == NULL
+  || !aCount->IsNumber())
+  {
+    reportGltfError ("Accessor '" + theName + "' does not define count.");
+    return false;
+  }
+
+  aStruct.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
+                     ? (int64_t )aByteOffset->GetDouble()
+                     : 0;
+  aStruct.ByteStride = aByteStride != NULL && aByteStride->IsInt()
+                     ? aByteStride->GetInt()
+                     : 0;
+  aStruct.Count = (int64_t )aCount->GetDouble();
+
+  if (aStruct.ByteOffset < 0)
+  {
+    reportGltfError ("Accessor '" + theName + "' defines invalid byteOffset.");
+    return false;
+  }
+  else if (aStruct.ByteStride < 0
+        || aStruct.ByteStride > 255)
+  {
+    reportGltfError ("Accessor '" + theName + "' defines invalid byteStride.");
+    return false;
+  }
+  else if (aStruct.Count < 1)
+  {
+    reportGltfError ("Accessor '" + theName + "' defines invalid count.");
+    return false;
+  }
+
+  // Read Min/Max values for POSITION type. It is used for bounding boxes
+  if (theType == RWGltf_GltfArrayType_Position)
+  {
+    const RWGltf_JsonValue* aMin = findObjectMember (theAccessor, "min");
+    const RWGltf_JsonValue* aMax = findObjectMember (theAccessor, "max");
+    if (aMin != NULL && aMax != NULL)
+    {
+      // Note: Min/Max values can be not defined in glTF file.
+      // In this case it is not used only.
+      if (!aMin->IsArray()   || !aMax->IsArray() ||
+           aMin->Size() != 3 ||  aMax->Size() != 3)
+      {
+        reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max values.");
+      }
+      else
+      {
+        bool isValidMinMax = true;
+        gp_Pnt aMinPnt, aMaxPnt;
+        for (int anIter = 0; anIter < 3; ++anIter)
+        {
+          const RWGltf_JsonValue& aMinVal = (*aMin)[anIter];
+          const RWGltf_JsonValue& aMaxVal = (*aMax)[anIter];
+          if (!aMinVal.IsNumber() || !aMaxVal.IsNumber())
+          {
+            reportGltfWarning ("Accessor '" + theName + "' defines invalid min/max value.");
+            isValidMinMax = false;
+            break;
+          }
+          aMinPnt.SetCoord (anIter + 1, aMinVal.GetDouble());
+          aMinPnt.SetCoord (anIter + 1, aMaxVal.GetDouble());
+        }
+        if (isValidMinMax)
+        {
+          myCSTrsf.TransformPosition (aMinPnt.ChangeCoord());
+          myCSTrsf.TransformPosition (aMaxPnt.ChangeCoord());
+
+          Bnd_Box aBox;
+          aBox.Add (aMinPnt);
+          aBox.Add (aMaxPnt);
+
+          theMeshData->SetBoundingBox (aBox);
+        }
+      }
+    }
+  }
+
+  const RWGltf_JsonValue* aBufferView = myGltfRoots[RWGltf_GltfRootElement_BufferViews].FindChild (*aBufferViewName);
+  if (aBufferView == NULL
+  || !aBufferView->IsObject())
+  {
+    reportGltfError ("Accessor '" + theName + "' refers to non-existing bufferView.");
+    return false;
+  }
+
+  return gltfParseBufferView (theMeshData, getKeyString (*aBufferViewName), *aBufferView, aStruct, theType);
+}
+
+// =======================================================================
+// function : gltfParseBufferView
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                                 const TCollection_AsciiString& theName,
+                                                 const RWGltf_JsonValue& theBufferView,
+                                                 const RWGltf_GltfAccessor& theAccessor,
+                                                 const RWGltf_GltfArrayType theType)
+{
+  RWGltf_GltfBufferView aBuffView;
+  const RWGltf_JsonValue* aBufferName = findObjectMember (theBufferView, "buffer");
+  const RWGltf_JsonValue* aByteLength = findObjectMember (theBufferView, "byteLength");
+  const RWGltf_JsonValue* aByteOffset = findObjectMember (theBufferView, "byteOffset");
+  const RWGltf_JsonValue* aTarget     = findObjectMember (theBufferView, "target");
+  if (aBufferName == NULL)
+  {
+    reportGltfError ("BufferView '" + theName + "' does not define buffer.");
+    return false;
+  }
+
+  aBuffView.ByteOffset = aByteOffset != NULL && aByteOffset->IsNumber()
+                       ? (int64_t )aByteOffset->GetDouble()
+                       : 0;
+  aBuffView.ByteLength = aByteLength != NULL && aByteLength->IsNumber()
+                       ? (int64_t )aByteLength->GetDouble()
+                       : 0;
+  if (aTarget != NULL && aTarget->IsInt())
+  {
+    aBuffView.Target = (RWGltf_GltfBufferViewTarget )aTarget->GetInt();
+    if (aBuffView.Target != RWGltf_GltfBufferViewTarget_ARRAY_BUFFER
+     && aBuffView.Target != RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER)
+    {
+      reportGltfError ("BufferView '" + theName + "' defines invalid target.");
+      return false;
+    }
+  }
+
+  if (aBuffView.ByteLength < 0)
+  {
+    reportGltfError ("BufferView '" + theName + "' defines invalid byteLength.");
+    return false;
+  }
+  else if (aBuffView.ByteOffset < 0)
+  {
+    reportGltfError ("BufferView '" + theName + "' defines invalid byteOffset.");
+    return false;
+  }
+
+  const RWGltf_JsonValue* aBuffer = myGltfRoots[RWGltf_GltfRootElement_Buffers].FindChild (*aBufferName);
+  if (aBuffer == NULL
+  || !aBuffer->IsObject())
+  {
+    reportGltfError ("BufferView '" + theName + "' refers to non-existing buffer.");
+    return false;
+  }
+
+  return gltfParseBuffer (theMeshData, getKeyString (*aBufferName), *aBuffer, theAccessor, aBuffView, theType);
+}
+
+// =======================================================================
+// function : gltfParseBuffer
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                             const TCollection_AsciiString& theName,
+                                             const RWGltf_JsonValue& theBuffer,
+                                             const RWGltf_GltfAccessor&     theAccessor,
+                                             const RWGltf_GltfBufferView&   theView,
+                                             const RWGltf_GltfArrayType     theType)
+{
+  //const RWGltf_JsonValue* aType       = findObjectMember (theBuffer, "type");
+  //const RWGltf_JsonValue* aByteLength = findObjectMember (theBuffer, "byteLength");
+  const RWGltf_JsonValue* anUriVal      = findObjectMember (theBuffer, "uri");
+
+  int64_t anOffset = theView.ByteOffset + theAccessor.ByteOffset;
+  bool isBinary = false;
+  if (myIsBinary)
+  {
+    isBinary = IsEqual ("binary_glTF", theName) // glTF 1.0
+            || anUriVal == NULL;                // glTF 2.0
+  }
+  if (isBinary)
+  {
+    anOffset += myBinBodyOffset;
+
+    RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
+    aData.Accessor = theAccessor;
+    aData.StreamOffset = anOffset;
+    aData.StreamUri = myFilePath;
+    return true;
+  }
+
+  if (anUriVal == NULL
+  || !anUriVal->IsString())
+  {
+    reportGltfError ("Buffer '" + theName + "' does not define uri.");
+    return false;
+  }
+
+  const char* anUriData = anUriVal->GetString();
+  if (::strncmp (anUriData, "data:application/octet-stream;base64,", 37) == 0)
+  {
+    RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
+    aData.Accessor = theAccessor;
+    aData.StreamOffset = anOffset;
+    if (!myDecodedBuffers.Find (theName, aData.StreamData))
+    {
+      // it is better decoding in multiple threads
+      aData.StreamData = FSD_Base64Decoder::Decode ((const Standard_Byte* )anUriData + 37, anUriVal->GetStringLength() - 37);
+      myDecodedBuffers.Bind (theName, aData.StreamData);
+    }
+    return true;
+  }
+  else
+  {
+    TCollection_AsciiString anUri = anUriData;
+    if (anUri.IsEmpty())
+    {
+      reportGltfError ("Buffer '" + theName + "' does not define uri.");
+      return false;
+    }
+
+    TCollection_AsciiString aPath = myFolder + anUri;
+    bool isFileExist = false;
+    if (!myProbedFiles.Find (aPath, isFileExist))
+    {
+      isFileExist = OSD_File (aPath).Exists();
+      myProbedFiles.Bind (aPath, isFileExist);
+    }
+    if (!isFileExist)
+    {
+      reportGltfError ("Buffer '" + theName + "' refers to non-existing file '" + anUri + "'.");
+      return false;
+    }
+
+    RWGltf_GltfPrimArrayData& aData = theMeshData->AddPrimArrayData (theType);
+    aData.Accessor = theAccessor;
+    aData.StreamOffset = anOffset;
+    aData.StreamUri = myFolder + anUri;
+    if (myExternalFiles != NULL)
+    {
+      myExternalFiles->Add (aData.StreamUri);
+    }
+    return true;
+  }
+}
+
+// =======================================================================
+// function : bindNamedShape
+// purpose  :
+// =======================================================================
+void RWGltf_GltfJsonParser::bindNamedShape (TopoDS_Shape& theShape,
+                                            ShapeMapGroup theGroup,
+                                            const TopLoc_Location& theLoc,
+                                            const TCollection_AsciiString& theId,
+                                            const RWGltf_JsonValue* theUserName)
+{
+  if (theShape.IsNull())
+  {
+    return;
+  }
+
+  if (!theLoc.IsIdentity())
+  {
+    theShape.Location (theLoc);
+  }
+
+  TCollection_AsciiString aUserName;
+  if (theUserName != NULL
+   && theUserName->IsString())
+  {
+    aUserName = theUserName->GetString();
+  }
+  else if (myIsGltf1)
+  {
+    aUserName = theId;
+  }
+
+  myShapeMap[theGroup].Bind (theId, theShape);
+  if (myAttribMap != NULL)
+  {
+    RWMesh_NodeAttributes aShapeAttribs;
+    aShapeAttribs.Name    = aUserName;
+    aShapeAttribs.RawName = theId;
+    if (theShape.ShapeType() == TopAbs_FACE)
+    {
+      TopLoc_Location aDummy;
+      if (Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (TopoDS::Face (theShape), aDummy)))
+      {
+        if (aLateData->HasStyle())
+        {
+          aShapeAttribs.Style.SetColorSurf (aLateData->BaseColor());
+        }
+      }
+    }
+    myAttribMap->Bind (theShape, aShapeAttribs);
+  }
+}
+#endif
+
+// =======================================================================
+// function : Parse
+// purpose  :
+// =======================================================================
+bool RWGltf_GltfJsonParser::Parse (const Handle(Message_ProgressIndicator)& theProgress)
+{
+  Message_ProgressSentry aPSentry (theProgress, "Reading Gltf", 0, 2, 1);
+#ifdef HAVE_RAPIDJSON
+  {
+    if (!gltfParseRoots())
+    {
+      return false;
+    }
+
+    gltfParseAsset();
+    gltfParseMaterials();
+    if (!gltfParseScene (theProgress))
+    {
+      return false;
+    }
+  }
+  aPSentry.Next();
+  if (!aPSentry.More())
+  {
+    return false;
+  }
+  return true;
+#else
+  Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support.", Message_Fail);
+  return false;
+#endif
+}
diff --git a/src/RWGltf/RWGltf_GltfJsonParser.pxx b/src/RWGltf/RWGltf_GltfJsonParser.pxx
new file mode 100644 (file)
index 0000000..92e605f
--- /dev/null
@@ -0,0 +1,416 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfJsonParser_HeaderFile
+#define _RWGltf_GltfJsonParser_HeaderFile
+
+#include <Graphic3d_Vec.hxx>
+#include <Message_Gravity.hxx>
+#include <NCollection_DataMap.hxx>
+#include <NCollection_IndexedMap.hxx>
+#include <RWGltf_GltfLatePrimitiveArray.hxx>
+#include <RWGltf_GltfBufferView.hxx>
+#include <RWGltf_GltfRootElement.hxx>
+#include <RWGltf_MaterialCommon.hxx>
+#include <RWGltf_MaterialMetallicRoughness.hxx>
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <RWMesh_NodeAttributes.hxx>
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopTools_SequenceOfShape.hxx>
+
+// workaround name collisions with XLib
+#ifdef None
+  #undef None
+#endif
+#ifdef Bool
+  #undef Bool
+#endif
+
+#ifdef HAVE_RAPIDJSON
+  //#define RAPIDJSON_ASSERT
+  #include <rapidjson/document.h>
+  #include <rapidjson/prettywriter.h>
+  #include <rapidjson/stringbuffer.h>
+  #include <rapidjson/istreamwrapper.h>
+  #include <rapidjson/ostreamwrapper.h>
+
+  typedef rapidjson::Document::ValueType RWGltf_JsonValue;
+#endif
+
+class Message_ProgressIndicator;
+
+//! INTERNAL tool for parsing glTF document (JSON structure).
+class RWGltf_GltfJsonParser
+#ifdef HAVE_RAPIDJSON
+: public rapidjson::Document
+#endif
+{
+public:
+
+#ifdef HAVE_RAPIDJSON
+  //! Auxiliary method for formatting error code.
+  Standard_EXPORT static const char* FormatParseError (rapidjson::ParseErrorCode theCode);
+#endif
+
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT RWGltf_GltfJsonParser (TopTools_SequenceOfShape& theRootShapes);
+
+  //! Set file path.
+  Standard_EXPORT void SetFilePath (const TCollection_AsciiString& theFilePath);
+
+  //! Set flag for probing file without complete reading.
+  void SetProbeHeader (bool theToProbe) { myToProbeHeader = theToProbe; }
+
+  //! Return prefix for reporting issues.
+  const TCollection_AsciiString& ErrorPrefix() const { return myErrorPrefix; }
+
+  //! Set prefix for reporting issues.
+  void SetErrorPrefix (const TCollection_AsciiString& theErrPrefix) { myErrorPrefix = theErrPrefix; }
+
+  //! Set map for storing node attributes.
+  void SetAttributeMap (RWMesh_NodeAttributeMap& theAttribMap) { myAttribMap = &theAttribMap; }
+
+  //! Set list for storing external files.
+  void SetExternalFiles (NCollection_IndexedMap<TCollection_AsciiString>& theExternalFiles) { myExternalFiles = &theExternalFiles; }
+
+  //! Return transformation from glTF to OCCT coordinate system.
+  const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCSTrsf; }
+
+  //! Set transformation from glTF to OCCT coordinate system.
+  void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCSTrsf = theConverter; }
+
+  //! Initialize binary format.
+  void SetBinaryFormat (int64_t theBinBodyOffset,
+                        int64_t theBinBodyLen)
+  {
+    myIsBinary      = true;
+    myBinBodyOffset = theBinBodyOffset;
+    myBinBodyLen    = theBinBodyLen;
+  }
+
+  //! Parse glTF document.
+  Standard_EXPORT bool Parse (const Handle(Message_ProgressIndicator)& theProgress);
+
+  //! Return metadata map.
+  const TColStd_IndexedDataMapOfStringString& Metadata() const { return myMetadata; }
+
+  //! Return face list for loading triangulation.
+  NCollection_Vector<TopoDS_Face>& FaceList() { return myFaceList; }
+
+protected:
+#ifdef HAVE_RAPIDJSON
+  //! Search mandatory root elements in the document.
+  //! Return FALSE if some mandatory element is missing.
+  Standard_EXPORT bool gltfParseRoots();
+
+  //! Parse default scene.
+  Standard_EXPORT bool gltfParseScene (const Handle(Message_ProgressIndicator)& theProgress);
+
+  //! Parse document metadata.
+  Standard_EXPORT void gltfParseAsset();
+
+protected:
+
+  //! Parse materials defined in the document.
+  Standard_EXPORT void gltfParseMaterials();
+
+  //! Parse standard material.
+  Standard_EXPORT bool gltfParseStdMaterial (Handle(RWGltf_MaterialCommon)& theMat,
+                                             const RWGltf_JsonValue& theMatNode);
+
+  //! Parse pbrMetallicRoughness material.
+  Standard_EXPORT bool gltfParsePbrMaterial (Handle(RWGltf_MaterialMetallicRoughness)& theMat,
+                                             const RWGltf_JsonValue& theMatNode);
+
+  //! Parse common material (KHR_materials_common extension).
+  Standard_EXPORT bool gltfParseCommonMaterial (Handle(RWGltf_MaterialCommon)& theMat,
+                                                const RWGltf_JsonValue& theMatNode);
+
+  //! Parse texture definition.
+  Standard_EXPORT bool gltfParseTexture (Handle(Image_Texture)& theTexture,
+                                         const RWGltf_JsonValue* theTextureId);
+
+protected:
+
+  //! Parse scene array of nodes recursively.
+  Standard_EXPORT bool gltfParseSceneNodes (TopTools_SequenceOfShape& theShapeSeq,
+                                            const RWGltf_JsonValue& theSceneNodes,
+                                            const Handle(Message_ProgressIndicator)& theProgress);
+
+  //! Parse scene node recursively.
+  Standard_EXPORT bool gltfParseSceneNode (TopoDS_Shape& theNodeShape,
+                                           const TCollection_AsciiString& theSceneNodeId,
+                                           const RWGltf_JsonValue& theSceneNode,
+                                           const Handle(Message_ProgressIndicator)& theProgress);
+
+  //! Parse mesh element.
+  Standard_EXPORT bool gltfParseMesh (TopoDS_Shape& theMeshShape,
+                                      const TCollection_AsciiString& theMeshId,
+                                      const RWGltf_JsonValue& theMesh,
+                                      const Handle(Message_ProgressIndicator)& theProgress);
+
+  //! Parse primitive array.
+  Standard_EXPORT bool gltfParsePrimArray (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                           const TCollection_AsciiString& theMeshName,
+                                           const RWGltf_JsonValue& thePrimArray,
+                                           const Handle(Message_ProgressIndicator)& theProgress);
+
+  //! Parse accessor.
+  Standard_EXPORT bool gltfParseAccessor (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                          const TCollection_AsciiString& theName,
+                                          const RWGltf_JsonValue& theAccessor,
+                                          const RWGltf_GltfArrayType theType);
+
+  //! Parse buffer view.
+  Standard_EXPORT bool gltfParseBufferView (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                            const TCollection_AsciiString& theName,
+                                            const RWGltf_JsonValue& theBufferView,
+                                            const RWGltf_GltfAccessor& theAccessor,
+                                            const RWGltf_GltfArrayType theType);
+
+  //! Parse buffer.
+  Standard_EXPORT bool gltfParseBuffer (const Handle(RWGltf_GltfLatePrimitiveArray)& theMeshData,
+                                        const TCollection_AsciiString& theName,
+                                        const RWGltf_JsonValue& theBuffer,
+                                        const RWGltf_GltfAccessor&   theAccessor,
+                                        const RWGltf_GltfBufferView& theView,
+                                        const RWGltf_GltfArrayType   theType);
+
+protected:
+
+  //! Read vec4 from specified item.
+  static bool gltfReadVec4 (Graphic3d_Vec4d& theVec4,
+                            const RWGltf_JsonValue* theVal)
+  {
+    if (theVal == NULL
+    || !theVal->IsArray()
+    ||  theVal->Size() != 4)
+    {
+      return false;
+    }
+
+    for (int aCompIter = 0; aCompIter < 4; ++aCompIter)
+    {
+      const RWGltf_JsonValue& aGenVal = (*theVal)[aCompIter];
+      if (!aGenVal.IsNumber())
+      {
+        return false;
+      }
+      theVec4[aCompIter] = aGenVal.GetDouble();
+    }
+    return true;
+  }
+
+  //! Validate color
+  static bool validateColor4 (const Graphic3d_Vec4d& theVec)
+  {
+    return theVec.r() >= 0.0 && theVec.r() <= 1.0
+        && theVec.g() >= 0.0 && theVec.g() <= 1.0
+        && theVec.b() >= 0.0 && theVec.b() <= 1.0
+        && theVec.a() >= 0.0 && theVec.a() <= 1.0;
+  }
+
+  //! Read vec3 from specified item.
+  static bool gltfReadVec3 (Graphic3d_Vec3d& theVec3,
+                            const RWGltf_JsonValue* theVal)
+  {
+    if (theVal == NULL
+    || !theVal->IsArray()
+    ||  theVal->Size() != 3)
+    {
+      return false;
+    }
+
+    for (int aCompIter = 0; aCompIter < 3; ++aCompIter)
+    {
+      const RWGltf_JsonValue& aGenVal = (*theVal)[aCompIter];
+      if (!aGenVal.IsNumber())
+      {
+        return false;
+      }
+      theVec3[aCompIter] = aGenVal.GetDouble();
+    }
+    return true;
+  }
+
+  //! Validate color
+  static bool validateColor3 (const Graphic3d_Vec3d& theVec)
+  {
+    return theVec.r() >= 0.0 && theVec.r() <= 1.0
+        && theVec.g() >= 0.0 && theVec.g() <= 1.0
+        && theVec.b() >= 0.0 && theVec.b() <= 1.0;
+  }
+
+protected:
+
+  //! Groups for re-using shapes.
+  enum ShapeMapGroup
+  {
+    ShapeMapGroup_Nodes,  //!< nodes
+    ShapeMapGroup_Meshes, //!< meshes
+  };
+
+  //! Bind name attribute.
+  void bindNodeShape (TopoDS_Shape& theShape,
+                      const TopLoc_Location& theLoc,
+                      const TCollection_AsciiString& theNodeId,
+                      const RWGltf_JsonValue* theUserName)
+  {
+    bindNamedShape (theShape, ShapeMapGroup_Nodes, theLoc, theNodeId, theUserName);
+  }
+
+  //! Bind name attribute.
+  void bindMeshShape (TopoDS_Shape& theShape,
+                      const TCollection_AsciiString& theMeshId,
+                      const RWGltf_JsonValue* theUserName)
+  {
+    bindNamedShape (theShape, ShapeMapGroup_Meshes, TopLoc_Location(), theMeshId, theUserName);
+  }
+
+  //! Find named shape.
+  bool findNodeShape (TopoDS_Shape& theShape,
+                      const TCollection_AsciiString& theNodeId) const
+  {
+    return findNamedShape (theShape, ShapeMapGroup_Nodes, theNodeId);
+  }
+
+  //! Find named shape.
+  bool findMeshShape (TopoDS_Shape& theShape,
+                      const TCollection_AsciiString& theMeshId) const
+  {
+    return findNamedShape (theShape, ShapeMapGroup_Meshes, theMeshId);
+  }
+
+  //! Bind name attribute.
+  Standard_EXPORT void bindNamedShape (TopoDS_Shape& theShape,
+                                       ShapeMapGroup theGroup,
+                                       const TopLoc_Location& theLoc,
+                                       const TCollection_AsciiString& theId,
+                                       const RWGltf_JsonValue* theUserName);
+
+  //! Find named shape.
+  bool findNamedShape (TopoDS_Shape& theShape,
+                       ShapeMapGroup theGroup,
+                       const TCollection_AsciiString& theId) const
+  {
+    return myShapeMap[theGroup].Find (theId, theShape);
+  }
+
+  //! Return the string representation of the key.
+  static TCollection_AsciiString getKeyString (const RWGltf_JsonValue& theValue)
+  {
+    if (theValue.IsString())
+    {
+      return TCollection_AsciiString (theValue.GetString());
+    }
+    else if (theValue.IsInt())
+    {
+      return TCollection_AsciiString (theValue.GetInt());
+    }
+    return TCollection_AsciiString();
+  }
+
+protected:
+
+  //! Auxiliary structure for fast look-up of document sub-nodes of specified node.
+  class GltfElementMap
+  {
+  public:
+
+    //! Empty constructor.
+    GltfElementMap() : myRoot (NULL) {}
+
+    //! Return TRUE if this element is NULL.
+    bool IsNull() const { return myRoot == NULL; }
+
+    //! Access this node.
+    const RWGltf_JsonValue* Root() const { return myRoot; }
+
+    //! Find the child node with specified key.
+    const RWGltf_JsonValue* FindChild (const TCollection_AsciiString& theKey)
+    {
+      const RWGltf_JsonValue* aNode = NULL;
+      return myChildren.Find (theKey, aNode)
+           ? aNode
+           : NULL;
+    }
+
+    //! Find the child node with specified key.
+    const RWGltf_JsonValue* FindChild (const RWGltf_JsonValue& theKey)
+    {
+      const TCollection_AsciiString aKey = getKeyString (theKey);
+      if (aKey.IsEmpty())
+      {
+        return NULL;
+      }
+
+      const RWGltf_JsonValue* aNode = NULL;
+      return myChildren.Find (aKey, aNode)
+           ? aNode
+           : NULL;
+    }
+
+    //! Initialize the element.
+    void Init (const TCollection_AsciiString& theRootName,
+               const RWGltf_JsonValue* theRoot);
+
+  private:
+
+    NCollection_DataMap<TCollection_AsciiString, const RWGltf_JsonValue*, TCollection_AsciiString> myChildren;
+    const RWGltf_JsonValue* myRoot;
+
+  };
+#endif
+protected:
+
+  //! Print message about invalid glTF syntax.
+  void reportGltfSyntaxProblem (const TCollection_AsciiString& theMsg, Message_Gravity theGravity);
+
+protected:
+
+  TopTools_SequenceOfShape*        myRootShapes;    //!< sequence of result root shapes
+  RWMesh_NodeAttributeMap*         myAttribMap;     //!< shape attributes
+  NCollection_IndexedMap<TCollection_AsciiString>*
+                                   myExternalFiles; //!< list of external file references
+  RWMesh_CoordinateSystemConverter myCSTrsf;        //!< transformation from glTF to OCCT coordinate system
+
+  TColStd_IndexedDataMapOfStringString myMetadata; //!< file metadata
+  NCollection_DataMap<TCollection_AsciiString, Handle(RWGltf_MaterialMetallicRoughness)> myMaterialsPbr;
+  NCollection_DataMap<TCollection_AsciiString, Handle(RWGltf_MaterialCommon)> myMaterialsCommon;
+  NCollection_DataMap<TCollection_AsciiString, TopoDS_Shape> myShapeMap[2];
+
+  NCollection_DataMap<TCollection_AsciiString, bool> myProbedFiles;
+  NCollection_DataMap<TCollection_AsciiString, Handle(NCollection_Buffer)> myDecodedBuffers;
+  NCollection_Vector<TopoDS_Face> myFaceList; //!< face list for loading triangulation
+
+  TCollection_AsciiString   myFilePath;       //!< file path
+  TCollection_AsciiString   myFolder;         //!< folder
+  TCollection_AsciiString   myErrorPrefix;    //!< invalid syntax error prefix
+  int64_t                   myBinBodyOffset;  //!< offset to binary body
+  int64_t                   myBinBodyLen;     //!< binary body length
+  bool                      myIsBinary;       //!< binary document
+  bool                      myIsGltf1;        //!< obsolete glTF 1.0 version format
+  bool                      myToSkipEmptyNodes; //!< ignore nodes without Geometry
+  bool                      myToProbeHeader;  //!< flag to probe header without full reading, FALSE by default
+
+#ifdef HAVE_RAPIDJSON
+  GltfElementMap myGltfRoots[RWGltf_GltfRootElement_NB]; //!< glTF format root elements
+#endif
+
+};
+
+#endif // _RWGltf_GltfJsonParser_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfLatePrimitiveArray.cxx b/src/RWGltf/RWGltf_GltfLatePrimitiveArray.cxx
new file mode 100644 (file)
index 0000000..fa486da
--- /dev/null
@@ -0,0 +1,133 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2018-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <RWGltf_GltfLatePrimitiveArray.hxx>
+
+#include <RWGltf_MaterialMetallicRoughness.hxx>
+#include <RWGltf_MaterialCommon.hxx>
+
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <OSD_OpenFile.hxx>
+#include <Standard_ArrayStreamBuffer.hxx>
+
+#include <fstream>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWGltf_GltfLatePrimitiveArray, Poly_Triangulation)
+
+// =======================================================================
+// function : RWGltf_GltfLatePrimitiveArray
+// purpose  :
+// =======================================================================
+RWGltf_GltfLatePrimitiveArray::RWGltf_GltfLatePrimitiveArray (const TCollection_AsciiString& theId,
+                                                              const TCollection_AsciiString& theName)
+: Poly_Triangulation (3, 1, false),
+  myId (theId),
+  myName (theName),
+  myPrimMode (RWGltf_GltfPrimitiveMode_UNKNOWN)
+{
+  SetBoundingBox (Bnd_Box());
+}
+
+// =======================================================================
+// function : ~RWGltf_GltfLatePrimitiveArray
+// purpose  :
+// =======================================================================
+RWGltf_GltfLatePrimitiveArray::~RWGltf_GltfLatePrimitiveArray()
+{
+  //
+}
+
+// =======================================================================
+// function : BaseColor
+// purpose  :
+// =======================================================================
+Quantity_ColorRGBA RWGltf_GltfLatePrimitiveArray::BaseColor() const
+{
+  if (!myMaterialPbr.IsNull())
+  {
+    return myMaterialPbr->BaseColor;
+  }
+  else if (!myMaterialCommon.IsNull())
+  {
+    return Quantity_ColorRGBA (myMaterialCommon->DiffuseColor, 1.0f - myMaterialCommon->Transparency);
+  }
+  return Quantity_ColorRGBA();
+}
+
+// =======================================================================
+// function : AddPrimArrayData
+// purpose  :
+// =======================================================================
+RWGltf_GltfPrimArrayData& RWGltf_GltfLatePrimitiveArray::AddPrimArrayData (RWGltf_GltfArrayType theType)
+{
+  if (theType == RWGltf_GltfArrayType_Position)
+  {
+    // make sure positions go first
+    myData.Prepend (RWGltf_GltfPrimArrayData (theType));
+    return myData.ChangeFirst();
+  }
+  else if (theType == RWGltf_GltfArrayType_Indices)
+  {
+    // make sure indexes go after vertex positions but before any other vertex attributes
+    if (myData.First().Type == RWGltf_GltfArrayType_Position)
+    {
+      myData.InsertAfter (myData.Lower(), RWGltf_GltfPrimArrayData (theType));
+      return myData.ChangeValue (myData.Lower() + 1);
+    }
+    else
+    {
+      myData.Prepend (RWGltf_GltfPrimArrayData (theType));
+      return myData.ChangeFirst();
+    }
+  }
+  else
+  {
+    myData.Append (RWGltf_GltfPrimArrayData (theType));
+    return myData.ChangeLast();
+  }
+}
+
+// =======================================================================
+// function : SetBoundingBox
+// purpose  :
+// =======================================================================
+void RWGltf_GltfLatePrimitiveArray::SetBoundingBox (const Bnd_Box& theBox)
+{
+  myBox = theBox;
+
+  if (theBox.IsVoid())
+  {
+    Poly_Triangulation::myNodes = TColgp_Array1OfPnt();
+    Poly_Triangulation::myTriangles = Poly_Array1OfTriangle();
+    return;
+  }
+
+  // define 8 nodes so that AABB will be huge enough to include mesh even with transformation applied
+  Poly_Triangulation::myNodes.Resize (1, 8, false);
+  const gp_Pnt aMin = theBox.CornerMin();
+  const gp_Pnt aMax = theBox.CornerMax();
+  Poly_Triangulation::ChangeNode(1).SetCoord(aMin.X(), aMin.Y(), aMin.Z());
+  Poly_Triangulation::ChangeNode(2).SetCoord(aMax.X(), aMax.Y(), aMax.Z());
+  Poly_Triangulation::ChangeNode(3).SetCoord(aMin.X(), aMin.Y(), aMax.Z());
+  Poly_Triangulation::ChangeNode(4).SetCoord(aMin.X(), aMax.Y(), aMax.Z());
+  Poly_Triangulation::ChangeNode(5).SetCoord(aMax.X(), aMax.Y(), aMin.Z());
+  Poly_Triangulation::ChangeNode(6).SetCoord(aMax.X(), aMin.Y(), aMin.Z());
+  Poly_Triangulation::ChangeNode(7).SetCoord(aMin.X(), aMax.Y(), aMin.Z());
+  Poly_Triangulation::ChangeNode(8).SetCoord(aMax.X(), aMin.Y(), aMax.Z());
+
+  Poly_Triangulation::myTriangles.Resize (1, 1, false);
+  Poly_Triangulation::ChangeTriangle (1).Set (1, 2, 1);
+  //Poly_Triangulation::myTriangles = Poly_Array1OfTriangle();
+}
diff --git a/src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx b/src/RWGltf/RWGltf_GltfLatePrimitiveArray.hxx
new file mode 100644 (file)
index 0000000..07eb60e
--- /dev/null
@@ -0,0 +1,99 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2018-2019 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_GltfLatePrimitiveArray_HeaderFile
+#define _RWGltf_GltfLatePrimitiveArray_HeaderFile
+
+#include <Bnd_Box.hxx>
+#include <NCollection_Sequence.hxx>
+#include <Poly_Triangulation.hxx>
+#include <RWGltf_GltfPrimArrayData.hxx>
+#include <RWGltf_GltfPrimitiveMode.hxx>
+#include <Quantity_ColorRGBA.hxx>
+
+class RWGltf_MaterialMetallicRoughness;
+class RWGltf_MaterialCommon;
+
+//! Mesh data wrapper for delayed primitive array loading from glTF file.
+//! Class inherits Poly_Triangulation so that it can be put temporarily into TopoDS_Face within assembly structure,
+//! to be replaced with proper Poly_Triangulation loaded later on.
+class RWGltf_GltfLatePrimitiveArray : public Poly_Triangulation
+{
+  DEFINE_STANDARD_RTTIEXT(RWGltf_GltfLatePrimitiveArray, Poly_Triangulation)
+public:
+
+  //! Constructor.
+  Standard_EXPORT RWGltf_GltfLatePrimitiveArray (const TCollection_AsciiString& theId,
+                                                 const TCollection_AsciiString& theName);
+
+  //! Destructor.
+  Standard_EXPORT virtual ~RWGltf_GltfLatePrimitiveArray();
+
+  //! Entity id.
+  const TCollection_AsciiString& Id() const { return myId; }
+
+  //! Entity name.
+  const TCollection_AsciiString& Name() const { return myName; }
+
+  //! Assign entity name.
+  void SetName (const TCollection_AsciiString& theName) { myName = theName; }
+
+  //! Return type of primitive array.
+  RWGltf_GltfPrimitiveMode PrimitiveMode() const { return myPrimMode; }
+
+  //! Set type of primitive array.
+  void SetPrimitiveMode (RWGltf_GltfPrimitiveMode theMode) { myPrimMode = theMode; }
+
+  //! Return true if primitive array has assigned material
+  bool HasStyle() const { return !myMaterialPbr.IsNull() || !myMaterialCommon.IsNull(); }
+
+  //! Return base color.
+  Standard_EXPORT Quantity_ColorRGBA BaseColor() const;
+
+  //! Return PBR material definition.
+  const Handle(RWGltf_MaterialMetallicRoughness)& MaterialPbr() const { return myMaterialPbr; }
+
+  //! Set PBR material definition.
+  void SetMaterialPbr (const Handle(RWGltf_MaterialMetallicRoughness)& theMat) { myMaterialPbr = theMat; }
+
+  //! Return common (obsolete) material definition.
+  const Handle(RWGltf_MaterialCommon)& MaterialCommon() const { return myMaterialCommon; }
+
+  //! Set common (obsolete) material definition.
+  void SetMaterialCommon (const Handle(RWGltf_MaterialCommon)& theMat) { myMaterialCommon = theMat; }
+
+  //! Return primitive array data elements.
+  const NCollection_Sequence<RWGltf_GltfPrimArrayData>& Data() const { return myData; }
+
+  //! Add primitive array data element.
+  Standard_EXPORT RWGltf_GltfPrimArrayData& AddPrimArrayData (RWGltf_GltfArrayType theType);
+
+  //! This method sets input bounding box and assigns a FAKE data to underlying Poly_Triangulation
+  //! as Min/Max corners of bounding box, so that standard tools like BRepBndLib::Add()
+  //! can be used transparently for computing bounding box of this face.
+  Standard_EXPORT void SetBoundingBox (const Bnd_Box& theBox);
+
+private:
+
+  NCollection_Sequence<RWGltf_GltfPrimArrayData> myData;
+  Handle(RWGltf_MaterialMetallicRoughness) myMaterialPbr;    //!< PBR material
+  Handle(RWGltf_MaterialCommon)            myMaterialCommon; //!< common (obsolete) material
+  Bnd_Box                  myBox;        //!< bounding box
+  TCollection_AsciiString  myId;         //!< entity id
+  TCollection_AsciiString  myName;       //!< entity name
+  RWGltf_GltfPrimitiveMode myPrimMode;   //!< type of primitive array
+
+};
+
+#endif // _RWGltf_GltfLatePrimitiveArray_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfPrimArrayData.hxx b/src/RWGltf/RWGltf_GltfPrimArrayData.hxx
new file mode 100644 (file)
index 0000000..752f320
--- /dev/null
@@ -0,0 +1,41 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2018-2019 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_GltfPrimArrayData_HeaderFile
+#define _RWGltf_GltfPrimArrayData_HeaderFile
+
+#include <NCollection_Buffer.hxx>
+#include <RWGltf_GltfAccessor.hxx>
+#include <RWGltf_GltfArrayType.hxx>
+#include <TCollection_AsciiString.hxx>
+
+//! An element within primitive array - vertex attribute or element indexes.
+class RWGltf_GltfPrimArrayData
+{
+public:
+  Handle(NCollection_Buffer) StreamData;
+  TCollection_AsciiString    StreamUri;
+  int64_t                    StreamOffset;
+
+  RWGltf_GltfAccessor        Accessor;
+  RWGltf_GltfArrayType       Type;
+
+  RWGltf_GltfPrimArrayData()
+  : StreamOffset (0), Type (RWGltf_GltfArrayType_UNKNOWN) {}
+
+  RWGltf_GltfPrimArrayData (RWGltf_GltfArrayType theType)
+  : StreamOffset (0), Type (theType) {}
+};
+
+#endif // _RWGltf_GltfPrimArrayData_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfPrimitiveMode.hxx b/src/RWGltf/RWGltf_GltfPrimitiveMode.hxx
new file mode 100644 (file)
index 0000000..6ab0f6b
--- /dev/null
@@ -0,0 +1,32 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfPrimitiveMode_HeaderFile
+#define _RWGltf_GltfPrimitiveMode_HeaderFile
+
+//! Low-level glTF enumeration defining Primitive type.
+//! Similar to Graphic3d_TypeOfData but does not define actual type and includes matrices.
+enum RWGltf_GltfPrimitiveMode
+{
+  RWGltf_GltfPrimitiveMode_UNKNOWN       = -1, //!< unknown or invalid type
+  RWGltf_GltfPrimitiveMode_Points        =  0, //!< GL_POINTS
+  RWGltf_GltfPrimitiveMode_Lines         =  1, //!< GL_LINES
+  RWGltf_GltfPrimitiveMode_LineLoop      =  2, //!< GL_LINE_LOOP
+  RWGltf_GltfPrimitiveMode_LineStrip     =  3, //!< GL_LINE_STRIP
+  RWGltf_GltfPrimitiveMode_Triangles     =  4, //!< GL_TRIANGLES
+  RWGltf_GltfPrimitiveMode_TriangleStrip =  5, //!< GL_TRIANGLE_STRIP
+  RWGltf_GltfPrimitiveMode_TriangleFan   =  6, //!< GL_TRIANGLE_FAN
+};
+
+#endif // _RWGltf_GltfPrimitiveMode_HeaderFile
diff --git a/src/RWGltf/RWGltf_GltfRootElement.hxx b/src/RWGltf/RWGltf_GltfRootElement.hxx
new file mode 100644 (file)
index 0000000..7d8d0e6
--- /dev/null
@@ -0,0 +1,73 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_GltfRootElement_HeaderFile
+#define _RWGltf_GltfRootElement_HeaderFile
+
+//! Root elements within glTF JSON document.
+enum RWGltf_GltfRootElement
+{
+  RWGltf_GltfRootElement_Asset,        //!< "asset"       element, mandatory
+  RWGltf_GltfRootElement_Scenes,       //!< "scenes"      element, mandatory
+  RWGltf_GltfRootElement_Scene,        //!< "scene"       element, mandatory
+  RWGltf_GltfRootElement_Nodes,        //!< "nodes"       element, mandatory
+  RWGltf_GltfRootElement_Meshes,       //!< "meshes"      element, mandatory
+  RWGltf_GltfRootElement_Accessors,    //!< "accessors"   element, mandatory
+  RWGltf_GltfRootElement_BufferViews,  //!< "bufferViews" element, mandatory
+  RWGltf_GltfRootElement_Buffers,      //!< "buffers"     element, mandatory
+  RWGltf_GltfRootElement_NB_MANDATORY, //!< number of mandatory elements
+  // optional elements
+  RWGltf_GltfRootElement_Animations = RWGltf_GltfRootElement_NB_MANDATORY,  //!< "animations" element
+  RWGltf_GltfRootElement_Materials,    //!< "materials"  element,
+  RWGltf_GltfRootElement_Programs,     //!< "programs"   element,
+  RWGltf_GltfRootElement_Samplers,     //!< "samplers"   element,
+  RWGltf_GltfRootElement_Shaders,      //!< "shaders"    element,
+  RWGltf_GltfRootElement_Skins,        //!< "skins"      element,
+  RWGltf_GltfRootElement_Techniques,   //!< "techniques" element,
+  RWGltf_GltfRootElement_Textures,     //!< "textures"   element,
+  RWGltf_GltfRootElement_Images,       //!< "images"     element,
+  RWGltf_GltfRootElement_ExtensionsUsed,     //!< "extensionsUsed"     element,
+  RWGltf_GltfRootElement_ExtensionsRequired, //!< "extensionsRequired" element,
+  RWGltf_GltfRootElement_NB            //!< overall number of elements
+};
+
+//! Root elements within glTF JSON document - names array.
+inline const char* RWGltf_GltfRootElementName (RWGltf_GltfRootElement theElem)
+{
+  static const char* THE_ROOT_NAMES[RWGltf_GltfRootElement_NB] =
+  {
+    "asset",
+    "scenes",
+    "scene",
+    "nodes",
+    "meshes",
+    "accessors",
+    "bufferViews",
+    "buffers",
+    "animations",
+    "materials",
+    "programs",
+    "samplers",
+    "shaders",
+    "skins",
+    "techniques",
+    "textures",
+    "images",
+    "extensionsUsed",
+    "extensionsRequired"
+  };
+  return THE_ROOT_NAMES[theElem];
+}
+
+#endif // _RWGltf_GltfRootElement_HeaderFile
diff --git a/src/RWGltf/RWGltf_MaterialCommon.hxx b/src/RWGltf/RWGltf_MaterialCommon.hxx
new file mode 100644 (file)
index 0000000..7266cf9
--- /dev/null
@@ -0,0 +1,48 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 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_MaterialCommon_HeaderFile
+#define _RWGltf_MaterialCommon_HeaderFile
+
+#include <Image_Texture.hxx>
+#include <Quantity_ColorRGBA.hxx>
+
+//! glTF 1.0 format common (obsolete) material definition.
+class RWGltf_MaterialCommon : public Standard_Transient
+{
+public:
+
+  Handle(Image_Texture)   AmbientTexture;  //!< image defining ambient color
+  Handle(Image_Texture)   DiffuseTexture;  //!< image defining diffuse color
+  Handle(Image_Texture)   SpecularTexture; //!< image defining specular color
+  TCollection_AsciiString Id;              //!< material identifier
+  TCollection_AsciiString Name;            //!< material name
+  Quantity_Color          AmbientColor;
+  Quantity_Color          DiffuseColor;
+  Quantity_Color          SpecularColor;
+  Quantity_Color          EmissiveColor;
+  Standard_ShortReal      Shininess;
+  Standard_ShortReal      Transparency;
+
+  RWGltf_MaterialCommon()
+  : AmbientColor (0.1, 0.1, 0.1, Quantity_TOC_RGB),
+    DiffuseColor (0.8, 0.8, 0.8, Quantity_TOC_RGB),
+    SpecularColor(0.2, 0.2, 0.2, Quantity_TOC_RGB),
+    EmissiveColor(0.0, 0.0, 0.0, Quantity_TOC_RGB),
+    Shininess (1.0f),
+    Transparency (0.0f) {}
+
+};
+
+#endif // _RWGltf_MaterialCommon_HeaderFile
diff --git a/src/RWGltf/RWGltf_MaterialMetallicRoughness.hxx b/src/RWGltf/RWGltf_MaterialMetallicRoughness.hxx
new file mode 100644 (file)
index 0000000..60f279c
--- /dev/null
@@ -0,0 +1,50 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2015-2019 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_MaterialMetallicRoughness_HeaderFile
+#define _RWGltf_MaterialMetallicRoughness_HeaderFile
+
+#include <Graphic3d_Vec.hxx>
+#include <Quantity_ColorRGBA.hxx>
+#include <Standard_Transient.hxx>
+#include <TCollection_AsciiString.hxx>
+
+class Image_Texture;
+
+//! glTF 2.0 format PBR material definition.
+class RWGltf_MaterialMetallicRoughness : public Standard_Transient
+{
+public:
+
+  Handle(Image_Texture)   BaseColorTexture;         //!< RGB texture for the base color
+  Handle(Image_Texture)   MetallicRoughnessTexture; //!< RG texture packing the metallic and roughness properties together
+  Handle(Image_Texture)   EmissiveTexture;          //!< RGB emissive map controls the color and intensity of the light being emitted by the material
+  Handle(Image_Texture)   OcclusionTexture;         //!< R occlusion map indicating areas of indirect lighting
+  Handle(Image_Texture)   NormalTexture;            //!< normal map
+  TCollection_AsciiString Id;                       //!< material identifier
+  TCollection_AsciiString Name;                     //!< material name
+  Quantity_ColorRGBA      BaseColor;                //!< base color (or scale factor to the texture); [1.0, 1.0, 1.0, 1.0] by default
+  Graphic3d_Vec3          EmissiveFactor;           //!< emissive color; [0.0, 0.0, 0.0] by default
+  Standard_ShortReal      Metallic;                 //!< metalness  (or scale factor to the texture) within range [0.0, 1.0]; 1.0 by default
+  Standard_ShortReal      Roughness;                //!< roughness  (or scale factor to the texture) within range [0.0, 1.0]; 1.0 by default
+
+ RWGltf_MaterialMetallicRoughness()
+  : BaseColor (1.0f, 1.0f, 1.0f, 1.0f),
+    EmissiveFactor (0.0f, 0.0f, 0.0f),
+    Metallic  (0.0f),
+    Roughness (0.0f) {}
+
+};
+
+#endif // _RWGltf_MaterialMetallicRoughness_HeaderFile
diff --git a/src/RWGltf/RWGltf_PrimitiveArrayReader.cxx b/src/RWGltf/RWGltf_PrimitiveArrayReader.cxx
new file mode 100644 (file)
index 0000000..35290dd
--- /dev/null
@@ -0,0 +1,101 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <RWGltf_PrimitiveArrayReader.hxx>
+
+#include <RWGltf_GltfLatePrimitiveArray.hxx>
+
+#include <BRep_Builder.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <OSD_OpenFile.hxx>
+#include <Standard_ArrayStreamBuffer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWGltf_PrimitiveArrayReader, Standard_Transient)
+
+// =======================================================================
+// function : reportError
+// purpose  :
+// =======================================================================
+void RWGltf_PrimitiveArrayReader::reportError (const TCollection_AsciiString& theText)
+{
+  Message::DefaultMessenger()->Send (myErrorPrefix + theText, Message_Fail);
+}
+
+// =======================================================================
+// function : load
+// purpose  :
+// =======================================================================
+bool RWGltf_PrimitiveArrayReader::load (const Handle(RWGltf_GltfLatePrimitiveArray)& theMesh)
+{
+  reset();
+  if (theMesh.IsNull()
+   || theMesh->PrimitiveMode() == RWGltf_GltfPrimitiveMode_UNKNOWN)
+  {
+    return false;
+  }
+
+  for (NCollection_Sequence<RWGltf_GltfPrimArrayData>::Iterator aDataIter (theMesh->Data()); aDataIter.More(); aDataIter.Next())
+  {
+    const RWGltf_GltfPrimArrayData& aData = aDataIter.Value();
+    if (!aData.StreamData.IsNull())
+    {
+      Standard_ArrayStreamBuffer aStreamBuffer ((const char* )aData.StreamData->Data(), aData.StreamData->Size());
+      std::istream aStream (&aStreamBuffer);
+      aStream.seekg ((std::streamoff )aData.StreamOffset, std::ios_base::beg);
+      if (!readBuffer (aStream, theMesh->Id(), aData.Accessor, aData.Type, theMesh->PrimitiveMode()))
+      {
+        return false;
+      }
+      continue;
+    }
+    else if (aData.StreamUri.IsEmpty())
+    {
+      reportError (TCollection_AsciiString ("Buffer '") + theMesh->Id() + "' does not define uri.");
+      return false;
+    }
+
+    if (mySharedStream.Path != aData.StreamUri)
+    {
+      mySharedStream.Stream.close();
+      mySharedStream.Path = aData.StreamUri;
+    }
+    if (!mySharedStream.Stream.is_open())
+    {
+      OSD_OpenStream (mySharedStream.Stream, aData.StreamUri.ToCString(), std::ios::in | std::ios::binary);
+      if (!mySharedStream.Stream.is_open())
+      {
+        mySharedStream.Stream.close();
+        reportError (TCollection_AsciiString ("Buffer '") + theMesh->Id() + "refers to non-existing file '" + aData.StreamUri + "'.");
+        return false;
+      }
+    }
+
+    mySharedStream.Stream.seekg ((std::streamoff )aData.StreamOffset, std::ios_base::beg);
+    if (!mySharedStream.Stream.good())
+    {
+      mySharedStream.Stream.close();
+      reportError (TCollection_AsciiString ("Buffer '") + theMesh->Id() + "refers to invalid location.");
+      return false;
+    }
+
+    if (!readBuffer (mySharedStream.Stream, theMesh->Id(), aData.Accessor, aData.Type, theMesh->PrimitiveMode()))
+    {
+      return false;
+    }
+  }
+  return true;
+}
diff --git a/src/RWGltf/RWGltf_PrimitiveArrayReader.hxx b/src/RWGltf/RWGltf_PrimitiveArrayReader.hxx
new file mode 100644 (file)
index 0000000..41774b4
--- /dev/null
@@ -0,0 +1,100 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2019 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_PrimitiveArrayReader_HeaderFile
+#define _RWGltf_PrimitiveArrayReader_HeaderFile
+
+#include <Poly_Triangulation.hxx>
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <RWGltf_GltfAccessor.hxx>
+#include <RWGltf_GltfArrayType.hxx>
+#include <RWGltf_GltfPrimitiveMode.hxx>
+#include <TCollection_AsciiString.hxx>
+
+class RWGltf_GltfLatePrimitiveArray;
+
+//! The interface for shared file.
+struct RWGltf_GltfSharedIStream
+{
+  std::ifstream           Stream; //!< shared file
+  TCollection_AsciiString Path;   //!< path to currently opened stream
+};
+
+//! Interface for reading primitive array from glTF buffer.
+class RWGltf_PrimitiveArrayReader : public Standard_Transient
+{
+  DEFINE_STANDARD_RTTIEXT(RWGltf_PrimitiveArrayReader, Standard_Transient)
+public:
+
+  //! Constructor.
+  RWGltf_PrimitiveArrayReader() {}
+
+  //! Return prefix for reporting issues.
+  const TCollection_AsciiString& ErrorPrefix() const { return myErrorPrefix; }
+
+  //! Set prefix for reporting issues.
+  void SetErrorPrefix (const TCollection_AsciiString& theErrPrefix) { myErrorPrefix = theErrPrefix; }
+
+  //! Return transformation from glTF to OCCT coordinate system.
+  const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCoordSysConverter; }
+
+  //! Set transformation from glTF to OCCT coordinate system.
+  void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCoordSysConverter = theConverter; }
+
+  //! Load primitive array.
+  Handle(Poly_Triangulation) Load (const Handle(RWGltf_GltfLatePrimitiveArray)& theMesh)
+  {
+    if (load (theMesh))
+    {
+      return result();
+    }
+    return Handle(Poly_Triangulation)();
+  }
+
+protected:
+
+  //! Reset cache before loading primitive array.
+  Standard_EXPORT virtual void reset() = 0;
+
+  //! Load primitive array.
+  Standard_EXPORT virtual bool load (const Handle(RWGltf_GltfLatePrimitiveArray)& theMesh);
+
+  //! Return result primitive array.
+  Standard_EXPORT virtual Handle(Poly_Triangulation) result() = 0;
+
+  //! Read primitive array data.
+  //! @param theStream   input stream to read from
+  //! @param theName     entity name for logging errors
+  //! @param theAccessor buffer accessor
+  //! @param theType     array type
+  //! @param theMode     primitive mode
+  //! @return FALSE on error
+  Standard_EXPORT virtual bool readBuffer (std::istream& theStream,
+                                           const TCollection_AsciiString& theName,
+                                           const RWGltf_GltfAccessor& theAccessor,
+                                           RWGltf_GltfArrayType theType,
+                                           RWGltf_GltfPrimitiveMode theMode) = 0;
+
+  //! Report error.
+  Standard_EXPORT virtual void reportError (const TCollection_AsciiString& theText);
+
+protected:
+
+  TCollection_AsciiString          myErrorPrefix;
+  RWGltf_GltfSharedIStream         mySharedStream;
+  RWMesh_CoordinateSystemConverter myCoordSysConverter;
+
+};
+
+#endif // _RWGltf_PrimitiveArrayReader_HeaderFile
diff --git a/src/RWGltf/RWGltf_TriangulationReader.cxx b/src/RWGltf/RWGltf_TriangulationReader.cxx
new file mode 100644 (file)
index 0000000..8485afc
--- /dev/null
@@ -0,0 +1,434 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <RWGltf_TriangulationReader.hxx>
+
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <Standard_ReadBuffer.hxx>
+
+#include <BRep_Builder.hxx>
+#include <Graphic3d_Vec.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+
+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;
+}
+
+IMPLEMENT_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWGltf_PrimitiveArrayReader)
+
+// =======================================================================
+// function : RWGltf_TriangulationReader
+// purpose  :
+// =======================================================================
+RWGltf_TriangulationReader::RWGltf_TriangulationReader()
+{
+  //
+}
+
+// =======================================================================
+// function : reset
+// purpose  :
+// =======================================================================
+void RWGltf_TriangulationReader::reset()
+{
+  myTriangulation = new Poly_Triangulation (1, 1, true);
+  {
+    TColgp_Array1OfPnt anEmpty;
+    myTriangulation->ChangeNodes().Move (anEmpty);
+  }
+  {
+    TColgp_Array1OfPnt2d anEmpty;
+    myTriangulation->ChangeUVNodes().Move (anEmpty);
+  }
+  {
+    Poly_Array1OfTriangle anEmpty;
+    myTriangulation->ChangeTriangles().Move (anEmpty);
+  }
+}
+
+// =======================================================================
+// function : result
+// purpose  :
+// =======================================================================
+Handle(Poly_Triangulation) RWGltf_TriangulationReader::result()
+{
+  if (myTriangulation->NbNodes() < 1)
+  {
+    return Handle(Poly_Triangulation)();
+  }
+  if (myTriangulation->UVNodes().Size() != myTriangulation->NbNodes())
+  {
+    myTriangulation->RemoveUVNodes();
+  }
+
+  if (myTriangulation->NbTriangles() < 1)
+  {
+    // reconstruct indexes
+    const Standard_Integer aNbTris = myTriangulation->NbNodes() / 3;
+    if (!setNbTriangles (aNbTris))
+    {
+      return Handle(Poly_Triangulation)();
+    }
+
+    for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
+    {
+      setTriangle (THE_LOWER_TRI_INDEX + aTriIter,
+                   Poly_Triangle (THE_LOWER_NODE_INDEX + aTriIter * 3 + 0,
+                                  THE_LOWER_NODE_INDEX + aTriIter * 3 + 1,
+                                  THE_LOWER_NODE_INDEX + aTriIter * 3 + 2));
+    }
+  }
+
+  return myTriangulation;
+}
+
+// =======================================================================
+// function : readBuffer
+// purpose  :
+// =======================================================================
+bool RWGltf_TriangulationReader::readBuffer (std::istream& theStream,
+                                             const TCollection_AsciiString& theName,
+                                             const RWGltf_GltfAccessor& theAccessor,
+                                             RWGltf_GltfArrayType theType,
+                                             RWGltf_GltfPrimitiveMode theMode)
+{
+  if (theMode != RWGltf_GltfPrimitiveMode_Triangles)
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString("Buffer '") + theName + "' skipped unsupported primitive array.", Message_Warning);
+    return true;
+  }
+
+  switch (theType)
+  {
+    case RWGltf_GltfArrayType_Indices:
+    {
+      if (theAccessor.Type != RWGltf_GltfAccessorLayout_Scalar)
+      {
+        break;
+      }
+
+      Poly_Triangle aVec3;
+      if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
+      {
+        if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
+        {
+          reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
+          return false;
+        }
+
+        const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
+        if (!setNbTriangles (aNbTris))
+        {
+          return false;
+        }
+        const size_t aStride = theAccessor.ByteStride != 0
+                             ? theAccessor.ByteStride
+                             : sizeof(uint16_t);
+        Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
+        for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
+        {
+          if (const uint16_t* anIndex0 = aBuffer.ReadChunk<uint16_t> (theStream))
+          {
+            aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
+          }
+          if (const uint16_t* anIndex1 = aBuffer.ReadChunk<uint16_t> (theStream))
+          {
+            aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
+          }
+          if (const uint16_t* anIndex2 = aBuffer.ReadChunk<uint16_t> (theStream))
+          {
+            aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
+          }
+          else
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+
+          if (!setTriangle (THE_LOWER_TRI_INDEX + aTriIter, aVec3))
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' refers to invalid indices.");
+          }
+        }
+      }
+      else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt32)
+      {
+        if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
+        {
+          reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
+          return false;
+        }
+
+        const int aNbTris = (Standard_Integer )(theAccessor.Count / 3);
+        if (!setNbTriangles (aNbTris))
+        {
+          return false;
+        }
+        const size_t aStride = theAccessor.ByteStride != 0
+                             ? theAccessor.ByteStride
+                             : sizeof(uint32_t);
+        Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
+        for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
+        {
+          if (const uint32_t* anIndex0 = aBuffer.ReadChunk<uint32_t> (theStream))
+          {
+            aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + *anIndex0;
+          }
+          if (const uint32_t* anIndex1 = aBuffer.ReadChunk<uint32_t> (theStream))
+          {
+            aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + *anIndex1;
+          }
+          if (const uint32_t* anIndex2 = aBuffer.ReadChunk<uint32_t> (theStream))
+          {
+            aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + *anIndex2;
+          }
+          else
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+
+          if (!setTriangle (THE_LOWER_TRI_INDEX + aTriIter, aVec3))
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' refers to invalid indices.");
+          }
+        }
+      }
+      else if (theAccessor.ComponentType == RWGltf_GltfAccessorCompType_UInt8)
+      {
+        if ((theAccessor.Count / 3) > std::numeric_limits<Standard_Integer>::max())
+        {
+          reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
+          return false;
+        }
+
+        const Standard_Integer aNbTris = (Standard_Integer )(theAccessor.Count / 3);
+        if (!setNbTriangles (aNbTris))
+        {
+          return false;
+        }
+        const size_t aStride = theAccessor.ByteStride != 0
+                             ? theAccessor.ByteStride
+                             : sizeof(uint8_t);
+        Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
+        for (Standard_Integer aTriIter = 0; aTriIter < aNbTris; ++aTriIter)
+        {
+          if (const uint8_t* anIndex0 = aBuffer.ReadChunk<uint8_t> (theStream))
+          {
+            aVec3.ChangeValue (1) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex0;
+          }
+          if (const uint8_t* anIndex1 = aBuffer.ReadChunk<uint8_t> (theStream))
+          {
+            aVec3.ChangeValue (2) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex1;
+          }
+          if (const uint8_t* anIndex2 = aBuffer.ReadChunk<uint8_t> (theStream))
+          {
+            aVec3.ChangeValue (3) = THE_LOWER_NODE_INDEX + (Standard_Integer )*anIndex2;
+          }
+          else
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+
+          if (!setTriangle (THE_LOWER_TRI_INDEX + aTriIter, aVec3))
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' refers to invalid indices.");
+          }
+        }
+      }
+      else
+      {
+        break;
+      }
+
+      break;
+    }
+    case RWGltf_GltfArrayType_Position:
+    {
+      if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
+       || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
+      {
+        break;
+      }
+      else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
+      {
+        reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
+        return false;
+      }
+
+      const size_t aStride = theAccessor.ByteStride != 0
+                           ? theAccessor.ByteStride
+                           : sizeof(Graphic3d_Vec3);
+      const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
+      if (!setNbPositionNodes (aNbNodes))
+      {
+        return false;
+      }
+
+      Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
+      if (!myCoordSysConverter.IsEmpty())
+      {
+        for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
+          if (aVec3 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+
+          gp_Pnt anXYZ (aVec3->x(), aVec3->y(), aVec3->z());
+          myCoordSysConverter.TransformPosition (anXYZ.ChangeCoord());
+          setNodePosition (THE_LOWER_NODE_INDEX + aVertIter, anXYZ);
+        }
+      }
+      else
+      {
+        for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
+          if (aVec3 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+          setNodePosition (THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt (aVec3->x(), aVec3->y(), aVec3->z()));
+        }
+      }
+      break;
+    }
+    case RWGltf_GltfArrayType_Normal:
+    {
+      if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
+       || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec3)
+      {
+        break;
+      }
+      else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
+      {
+        reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
+        return false;
+      }
+
+      const size_t aStride = theAccessor.ByteStride != 0
+                           ? theAccessor.ByteStride
+                           : sizeof(Graphic3d_Vec3);
+      const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
+      if (!setNbNormalNodes (aNbNodes))
+      {
+        return false;
+      }
+      Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
+      if (!myCoordSysConverter.IsEmpty())
+      {
+        for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
+          if (aVec3 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+          if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
+          {
+            myCoordSysConverter.TransformNormal (*aVec3);
+            setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (aVec3->x(), aVec3->y(), aVec3->z()));
+          }
+          else
+          {
+            setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (0.0, 0.0, 1.0));
+          }
+        }
+      }
+      else
+      {
+        for (Standard_Integer aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+        {
+          const Graphic3d_Vec3* aVec3 = aBuffer.ReadChunk<Graphic3d_Vec3> (theStream);
+          if (aVec3 == NULL)
+          {
+            reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+            return false;
+          }
+          if (aVec3->SquareModulus() >= THE_NORMAL_PREC2)
+          {
+            setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (aVec3->x(), aVec3->y(), aVec3->z()));
+          }
+          else
+          {
+            setNodeNormal (THE_LOWER_NODE_INDEX + aVertIter, gp_Dir (0.0, 0.0, 1.0));
+          }
+        }
+      }
+      break;
+    }
+    case RWGltf_GltfArrayType_TCoord0:
+    {
+      if (theAccessor.ComponentType != RWGltf_GltfAccessorCompType_Float32
+       || theAccessor.Type != RWGltf_GltfAccessorLayout_Vec2)
+      {
+        break;
+      }
+      else if (theAccessor.Count > std::numeric_limits<Standard_Integer>::max())
+      {
+        reportError (TCollection_AsciiString ("Buffer '") + theName + "' defines too big array.");
+        return false;
+      }
+
+      const size_t aStride = theAccessor.ByteStride != 0
+                           ? theAccessor.ByteStride
+                           : sizeof(Graphic3d_Vec2);
+      const Standard_Integer aNbNodes = (Standard_Integer )theAccessor.Count;
+      if (!setNbUVNodes (aNbNodes))
+      {
+        return false;
+      }
+
+      Standard_ReadBuffer aBuffer (theAccessor.Count * aStride, aStride);
+      for (int aVertIter = 0; aVertIter < aNbNodes; ++aVertIter)
+      {
+        Graphic3d_Vec2* aVec2 = aBuffer.ReadChunk<Graphic3d_Vec2> (theStream);
+        if (aVec2 == NULL)
+        {
+          reportError (TCollection_AsciiString ("Buffer '") + theName + "' reading error.");
+          return false;
+        }
+
+        // Y should be flipped (relative to image layout used by OCCT)
+        aVec2->y() = 1.0f - aVec2->y();
+        setNodeUV (THE_LOWER_NODE_INDEX + aVertIter, gp_Pnt2d (aVec2->x(), aVec2->y()));
+      }
+      break;
+    }
+    case RWGltf_GltfArrayType_Color:
+    case RWGltf_GltfArrayType_TCoord1:
+    case RWGltf_GltfArrayType_Joint:
+    case RWGltf_GltfArrayType_Weight:
+    {
+      return true;
+    }
+    case RWGltf_GltfArrayType_UNKNOWN:
+    {
+      return false;
+    }
+  }
+  return true;
+}
diff --git a/src/RWGltf/RWGltf_TriangulationReader.hxx b/src/RWGltf/RWGltf_TriangulationReader.hxx
new file mode 100644 (file)
index 0000000..37bf9bc
--- /dev/null
@@ -0,0 +1,148 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2019 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_TriangulationReader_HeaderFile
+#define _RWGltf_TriangulationReader_HeaderFile
+
+#include <RWGltf_PrimitiveArrayReader.hxx>
+
+//! RWGltf_PrimitiveArrayReader implementation creating Poly_Triangulation.
+class RWGltf_TriangulationReader : public RWGltf_PrimitiveArrayReader
+{
+  DEFINE_STANDARD_RTTIEXT(RWGltf_TriangulationReader, RWGltf_PrimitiveArrayReader)
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT RWGltf_TriangulationReader();
+
+protected:
+
+  //! Create Poly_Triangulation from collected data
+  Standard_EXPORT virtual Handle(Poly_Triangulation) result() Standard_OVERRIDE;
+
+  //! Reset cache before loading primitive array.
+  Standard_EXPORT virtual void reset() Standard_OVERRIDE;
+
+  //! Fill triangulation data and ignore non-triangulation primitives.
+  //! @param theStream   input stream to read from
+  //! @param theName     entity name for logging errors
+  //! @param theAccessor buffer accessor
+  //! @param theType     array type
+  //! @param theMode     primitive mode
+  //! @return FALSE on error
+  Standard_EXPORT virtual bool readBuffer (std::istream& theStream,
+                                           const TCollection_AsciiString& theName,
+                                           const RWGltf_GltfAccessor& theAccessor,
+                                           RWGltf_GltfArrayType theType,
+                                           RWGltf_GltfPrimitiveMode theMode) Standard_OVERRIDE;
+
+protected: //! @name interface for filling triangulation data
+
+  //! Resize array of position nodes to specified size.
+  virtual bool setNbPositionNodes (Standard_Integer theNbNodes)
+  {
+    if (theNbNodes <= 0)
+    {
+      return false;
+    }
+    myTriangulation->ChangeNodes().Resize (1, theNbNodes, false);
+    return true;
+  }
+
+  //! Set node position.
+  //! @param theIndex node index starting from 1
+  //! @param thePnt   node position
+  virtual void setNodePosition (Standard_Integer theIndex,
+                                const gp_Pnt& thePnt)
+  {
+    myTriangulation->ChangeNode (theIndex) = thePnt;
+  }
+
+  //! Resize array of UV nodes to specified size.
+  virtual bool setNbUVNodes (Standard_Integer theNbNodes)
+  {
+    if (theNbNodes <= 0
+     || myTriangulation->NbNodes() != theNbNodes)
+    {
+      return false;
+    }
+    myTriangulation->ChangeUVNodes().Resize (1, theNbNodes, false);
+    return true;
+  }
+
+  //! Set node UV texture coordinates.
+  //! @param theIndex node index starting from 1
+  //! @param theUV    node UV coordinates
+  virtual void setNodeUV (Standard_Integer theIndex,
+                          const gp_Pnt2d& theUV)
+  {
+    myTriangulation->ChangeUVNode (theIndex) = theUV;
+  }
+
+  //! Resize array of nodes normals to specified size.
+  virtual bool setNbNormalNodes (Standard_Integer theNbNodes)
+  {
+    if (theNbNodes <= 0
+     || myTriangulation->NbNodes() != theNbNodes)
+    {
+      return false;
+    }
+    myTriangulation->SetNormals (new TShort_HArray1OfShortReal (1, theNbNodes * 3));
+    return true;
+  }
+
+  //! Set node normal.
+  //! @param theIndex  node index starting from 1
+  //! @param theNormal node normal
+  virtual void setNodeNormal (Standard_Integer theIndex,
+                              const gp_Dir& theNormal)
+  {
+    myTriangulation->SetNormal (theIndex, theNormal);
+  }
+
+  //! Resize array of triangles to specified size.
+  virtual bool setNbTriangles (Standard_Integer theNbTris)
+  {
+    if (theNbTris >= 1)
+    {
+      myTriangulation->ChangeTriangles().Resize (1, theNbTris, false);
+      return true;
+    }
+    return false;
+  }
+
+  //! Add triangle element.
+  //! @param theIndex    triangle index starting from 1
+  //! @param theTriangle triangle nodes starting from 1
+  //! @return FALSE if node indexes are out of range
+  virtual bool setTriangle (Standard_Integer theIndex,
+                            const Poly_Triangle& theTriangle)
+  {
+    if (theTriangle.Value (1) < myTriangulation->Nodes().Lower() || theTriangle.Value (1) > myTriangulation->Nodes().Upper()
+     || theTriangle.Value (2) < myTriangulation->Nodes().Lower() || theTriangle.Value (2) > myTriangulation->Nodes().Upper()
+     || theTriangle.Value (3) < myTriangulation->Nodes().Lower() || theTriangle.Value (3) > myTriangulation->Nodes().Upper())
+    {
+      return false;
+    }
+    myTriangulation->ChangeTriangle (theIndex) = theTriangle;
+    return true;
+  }
+
+protected:
+
+  Handle(Poly_Triangulation) myTriangulation;
+
+};
+
+#endif // _RWGltf_TriangulationReader_HeaderFile
index 6216e25..13a1160 100755 (executable)
   typedef unsigned __int32  uint32_t;
   typedef unsigned __int64  uint64_t;
 
   typedef unsigned __int32  uint32_t;
   typedef unsigned __int64  uint64_t;
 
-  typedef __int8   int8_t;
-  typedef __int16  int16_t;
-  typedef __int32  int32_t;
-  typedef __int64  int64_t;
+  typedef signed __int8   int8_t;
+  typedef signed __int16  int16_t;
+  typedef signed __int32  int32_t;
+  typedef signed __int64  int64_t;
 #else
   #include <stdint.h>
 #endif
 #else
   #include <stdint.h>
 #endif
index ab12f64..6d9bc46 100644 (file)
@@ -7,3 +7,4 @@ TKV3d
 TKBRep
 TKG3d
 TKService
 TKBRep
 TKG3d
 TKService
+CSF_RapidJSON
index 9330e3e..5bfd2a3 100644 (file)
@@ -1 +1,2 @@
+RWGltf
 RWMesh
 RWMesh
index db3a05f..3bcade2 100644 (file)
@@ -19,6 +19,8 @@
 #include <Bnd_Box.hxx>
 #include <BRep_Builder.hxx>
 #include <DBRep.hxx>
 #include <Bnd_Box.hxx>
 #include <BRep_Builder.hxx>
 #include <DBRep.hxx>
+#include <DDocStd.hxx>
+#include <DDocStd_DrawDocument.hxx>
 #include <Draw.hxx>
 #include <Draw_Interpretor.hxx>
 #include <Draw_PluginMacro.hxx>
 #include <Draw.hxx>
 #include <Draw_Interpretor.hxx>
 #include <Draw_PluginMacro.hxx>
@@ -40,6 +42,7 @@
 #include <Quantity_Color.hxx>
 #include <Quantity_HArray1OfColor.hxx>
 #include <Quantity_NameOfColor.hxx>
 #include <Quantity_Color.hxx>
 #include <Quantity_HArray1OfColor.hxx>
 #include <Quantity_NameOfColor.hxx>
+#include <RWGltf_CafReader.hxx>
 #include <RWStl.hxx>
 #include <SelectMgr_SelectionManager.hxx>
 #include <Standard_ErrorHandler.hxx>
 #include <RWStl.hxx>
 #include <SelectMgr_SelectionManager.hxx>
 #include <Standard_ErrorHandler.hxx>
 #include <TColStd_Array1OfReal.hxx>
 #include <TColStd_HPackedMapOfInteger.hxx>
 #include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
 #include <TColStd_Array1OfReal.hxx>
 #include <TColStd_HPackedMapOfInteger.hxx>
 #include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
+#include <TDataStd_Name.hxx>
+#include <TDocStd_Application.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopoDS_Shape.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopoDS_Shape.hxx>
+#include <UnitsAPI.hxx>
 #include <UnitsMethods.hxx>
 #include <V3d_View.hxx>
 #include <ViewerTest.hxx>
 #include <UnitsMethods.hxx>
 #include <V3d_View.hxx>
 #include <ViewerTest.hxx>
@@ -77,6 +83,126 @@ extern Standard_Boolean VDisplayAISObject (const TCollection_AsciiString& theNam
                                            const Handle(AIS_InteractiveObject)& theAISObj,
                                            Standard_Boolean theReplaceIfExists = Standard_True);
 
                                            const Handle(AIS_InteractiveObject)& theAISObj,
                                            Standard_Boolean theReplaceIfExists = Standard_True);
 
+//=============================================================================
+//function : ReadGltf
+//purpose  : Reads glTF file
+//=============================================================================
+static Standard_Integer ReadGltf (Draw_Interpretor& theDI,
+                                  Standard_Integer theNbArgs,
+                                  const char** theArgVec)
+{
+  TCollection_AsciiString aDestName, aFilePath;
+  Standard_Boolean toUseExistingDoc = Standard_False;
+  Standard_Real aSystemUnitFactor = UnitsMethods::GetCasCadeLengthUnit() * 0.001;
+  Standard_Boolean toListExternalFiles = Standard_False;
+  Standard_Boolean isParallel = Standard_False;
+  Standard_Boolean isNoDoc = (TCollection_AsciiString(theArgVec[0]) == "readgltf");
+  for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+  {
+    TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
+    anArgCase.LowerCase();
+    if (!isNoDoc
+     && (anArgCase == "-nocreate"
+      || anArgCase == "-nocreatedoc"))
+    {
+      toUseExistingDoc = Standard_True;
+      if (anArgIter + 1 < theNbArgs
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], toUseExistingDoc))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (anArgCase == "-parallel")
+    {
+      isParallel = Standard_True;
+      if (anArgIter + 1 < theNbArgs
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], isParallel))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (anArgCase == "-listexternalfiles"
+          || anArgCase == "-listexternals"
+          || anArgCase == "-listexternal"
+          || anArgCase == "-external"
+          || anArgCase == "-externalfiles")
+    {
+      toListExternalFiles = Standard_True;
+    }
+    else if (aDestName.IsEmpty())
+    {
+      aDestName = theArgVec[anArgIter];
+    }
+    else if (aFilePath.IsEmpty())
+    {
+      aFilePath = theArgVec[anArgIter];
+    }
+    else
+    {
+      std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+      return 1;
+    }
+  }
+  if (aFilePath.IsEmpty())
+  {
+    std::cout << "Syntax error: wrong number of arguments\n";
+    return 1;
+  }
+
+  Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (theDI, 1);
+  Handle(TDocStd_Document) aDoc;
+  if (!toListExternalFiles
+   && !isNoDoc)
+  {
+    Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
+    Standard_CString aNameVar = aDestName.ToCString();
+    DDocStd::GetDocument (aNameVar, aDoc, Standard_False);
+    if (aDoc.IsNull())
+    {
+      if (toUseExistingDoc)
+      {
+        std::cout << "Error: document with name " << aDestName << " does not exist\n";
+        return 1;
+      }
+      anApp->NewDocument (TCollection_ExtendedString ("BinXCAF"), aDoc);
+    }
+    else if (!toUseExistingDoc)
+    {
+      std::cout << "Error: document with name " << aDestName << " already exists\n";
+      return 1;
+    }
+  }
+
+  RWGltf_CafReader aReader;
+  aReader.SetSystemLengthUnit (aSystemUnitFactor);
+  aReader.SetSystemCoordinateSystem (RWMesh_CoordinateSystem_Zup);
+  aReader.SetDocument (aDoc);
+  aReader.SetParallel (isParallel);
+  if (toListExternalFiles)
+  {
+    aReader.ProbeHeader (aFilePath);
+    for (NCollection_IndexedMap<TCollection_AsciiString>::Iterator aFileIter (aReader.ExternalFiles()); aFileIter.More(); aFileIter.Next())
+    {
+      theDI << "\"" << aFileIter.Value() << "\" ";
+    }
+  }
+  else
+  {
+    aReader.Perform (aFilePath, aProgress);
+    if (isNoDoc)
+    {
+      DBRep::Set (aDestName.ToCString(), aReader.SingleShape());
+    }
+    else
+    {
+      Handle(DDocStd_DrawDocument) aDrawDoc = new DDocStd_DrawDocument (aDoc);
+      TDataStd_Name::Set (aDoc->GetData()->Root(), aDestName.ToCString());
+      Draw::Set (aDestName.ToCString(), aDrawDoc);
+    }
+  }
+  return 0;
+}
+
 static Standard_Integer writestl
 (Draw_Interpretor& di, Standard_Integer argc, const char** argv)
 {
 static Standard_Integer writestl
 (Draw_Interpretor& di, Standard_Integer argc, const char** argv)
 {
@@ -1259,6 +1385,16 @@ void  XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
   const char* g = "XSTEP-STL/VRML";  // Step transfer file commands
   //XSDRAW::LoadDraw(theCommands);
 
   const char* g = "XSTEP-STL/VRML";  // Step transfer file commands
   //XSDRAW::LoadDraw(theCommands);
 
+  theCommands.Add ("ReadGltf",
+                   "ReadGltf Doc file [-parallel {on|off}] [-listExternalFiles] [-noCreateDoc]"
+                   "\n\t\t: Read glTF file into XDE document."
+                   "\n\t\t:   -listExternalFiles do not read mesh and only list external files"
+                   "\n\t\t:   -noCreateDoc read into existing XDE document",
+                   __FILE__, ReadGltf, g);
+  theCommands.Add ("readgltf",
+                   "readgltf shape file"
+                   "\n\t\t: Same as ReadGltf but reads glTF file into a shape instead of a document.",
+                   __FILE__, ReadGltf, g);
   theCommands.Add ("writevrml", "shape file [version VRML#1.0/VRML#2.0 (1/2): 2 by default] [representation shaded/wireframe/both (0/1/2): 1 by default]",__FILE__,writevrml,g);
   theCommands.Add ("writestl",  "shape file [ascii/binary (0/1) : 1 by default] [InParallel (0/1) : 0 by default]",__FILE__,writestl,g);
   theCommands.Add ("readstl",
   theCommands.Add ("writevrml", "shape file [version VRML#1.0/VRML#2.0 (1/2): 2 by default] [representation shaded/wireframe/both (0/1/2): 1 by default]",__FILE__,writevrml,g);
   theCommands.Add ("writestl",  "shape file [ascii/binary (0/1) : 1 by default] [InParallel (0/1) : 0 by default]",__FILE__,writestl,g);
   theCommands.Add ("readstl",
diff --git a/tests/de_mesh/gltf_read/begin b/tests/de_mesh/gltf_read/begin
new file mode 100644 (file)
index 0000000..a6de429
--- /dev/null
@@ -0,0 +1,2 @@
+pload XDE OCAF MODELING VISUALIZATION
+catch { Close D }
diff --git a/tests/de_mesh/gltf_read/brainstem b/tests/de_mesh/gltf_read/brainstem
new file mode 100644 (file)
index 0000000..b972212
--- /dev/null
@@ -0,0 +1,8 @@
+puts "========"
+puts "0030691: test glTF reader on standard sample models"
+puts "========"
+
+ReadGltf D [locate_data_file bug30691_BrainStem.gltf]
+XGetOneShape s D
+checknbshapes s -face 59 -compound 1
+checktrinfo s -tri 61666 -nod 34159
diff --git a/tests/de_mesh/gltf_read/buggy b/tests/de_mesh/gltf_read/buggy
new file mode 100644 (file)
index 0000000..3b7c76c
--- /dev/null
@@ -0,0 +1,8 @@
+puts "========"
+puts "0030691: test glTF reader on standard sample models"
+puts "========"
+
+ReadGltf D [locate_data_file bug30691_Buggy.glb]
+XGetOneShape s D
+checknbshapes s -face 148 -compound 48
+checktrinfo s -tri 531955 -nod 412855
diff --git a/tests/de_mesh/gltf_read/end b/tests/de_mesh/gltf_read/end
new file mode 100644 (file)
index 0000000..bdd2587
--- /dev/null
@@ -0,0 +1,6 @@
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
diff --git a/tests/de_mesh/gltf_read/engine b/tests/de_mesh/gltf_read/engine
new file mode 100644 (file)
index 0000000..cd152b8
--- /dev/null
@@ -0,0 +1,8 @@
+puts "========"
+puts "0030691: test glTF reader on standard sample models"
+puts "========"
+
+ReadGltf D [locate_data_file bug30691_2CylinderEngine.glb]
+XGetOneShape s D
+checknbshapes s -face 34 -compound 18
+checktrinfo s -tri 121496 -nod 84657
diff --git a/tests/de_mesh/gltf_read/helmet b/tests/de_mesh/gltf_read/helmet
new file mode 100644 (file)
index 0000000..e03aa51
--- /dev/null
@@ -0,0 +1,8 @@
+puts "========"
+puts "0030691: test glTF reader on standard sample models"
+puts "========"
+
+ReadGltf D [locate_data_file bug30691_DamagedHelmet.gltf]
+XGetOneShape s D
+checknbshapes s -face 1 -compound 0
+checktrinfo s -tri 15452 -nod 14556
diff --git a/tests/de_mesh/gltf_read/lantern b/tests/de_mesh/gltf_read/lantern
new file mode 100644 (file)
index 0000000..d9d1f79
--- /dev/null
@@ -0,0 +1,8 @@
+puts "========"
+puts "0030691: test glTF reader on standard sample models"
+puts "========"
+
+ReadGltf D [locate_data_file bug30691_Lantern.glb]
+XGetOneShape s D
+checknbshapes s -face 3 -compound 1
+checktrinfo s -tri 5394 -nod 4145
diff --git a/tests/de_mesh/gltf_read/orient b/tests/de_mesh/gltf_read/orient
new file mode 100644 (file)
index 0000000..42cdf87
--- /dev/null
@@ -0,0 +1,8 @@
+puts "========"
+puts "0030691: test glTF reader on standard sample models"
+puts "========"
+
+ReadGltf D [locate_data_file bug30691_OrientationTest.glb]
+XGetOneShape s D
+checknbshapes s -face 13 -compound 1
+checktrinfo s -tri 524 -nod 1048
index 6bef0c8..d930f98 100644 (file)
@@ -1,2 +1,3 @@
 001 stl_read
 002 shape_write_stl
 001 stl_read
 002 shape_write_stl
+003 gltf_read
diff --git a/tests/de_mesh/parse.rules b/tests/de_mesh/parse.rules
new file mode 100644 (file)
index 0000000..b69b184
--- /dev/null
@@ -0,0 +1 @@
+SKIPPED /Error: glTF reader is unavailable - OCCT has been built without RapidJSON support/