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()
 
+# 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}")
index 032662b..7f39f5e 100644 (file)
@@ -440,4 +440,5 @@ t TKVCAF
 n XCAFView
 n XCAFNoteObjects
 t TKRWMesh
+n RWGltf
 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_RAPIDJSON        "RapidJSON header files")
 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)")
 
+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")
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"}
   }
+  if { "$::HAVE_RAPIDJSON" == "true" } {
+    wokdep:SearchRapidJson 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.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
@@ -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.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
@@ -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.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" } {
@@ -588,6 +594,10 @@ if { "$::tcl_platform(os)" == "Darwin" } {
   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
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
-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})] } {
@@ -858,6 +858,25 @@ proc wokdep:SearchGLES {theErrInc theErrLib32 theErrLib64 theErrBin32 theErrBin6
   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"
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_RAPIDJSON=false"
 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_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=;"
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 |
+| 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 |
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
+#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
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 __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
index ab12f64..6d9bc46 100644 (file)
@@ -7,3 +7,4 @@ TKV3d
 TKBRep
 TKG3d
 TKService
+CSF_RapidJSON
index 9330e3e..5bfd2a3 100644 (file)
@@ -1 +1,2 @@
+RWGltf
 RWMesh
index db3a05f..3bcade2 100644 (file)
@@ -19,6 +19,8 @@
 #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>
@@ -40,6 +42,7 @@
 #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 <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 <UnitsAPI.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);
 
+//=============================================================================
+//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)
 {
@@ -1259,6 +1385,16 @@ void  XSDRAWSTLVRML::InitCommands (Draw_Interpretor& 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",
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
+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/