Added RWGltf_CafReader class implementing glTF reader.
Added readgltf Draw Harness command for reading glTF files.
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}")
n XCAFView
n XCAFNoteObjects
t TKRWMesh
+n RWGltf
n RWMesh
--- /dev/null
+# 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)
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 ")
"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")
}
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
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
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
}
grid .myFrame.myChecks.myLzmaCheck -row $aCheckRowIter -column 6 -sticky e
grid .myFrame.myChecks.myLzmaLbl -row $aCheckRowIter -column 7 -sticky w
-grid .myFrame.myChecks.myJDKCheck -row $aCheckRowIter -column 10 -sticky e
-grid .myFrame.myChecks.myJDKLbl -row $aCheckRowIter -column 11 -sticky w
+grid .myFrame.myChecks.myJDKCheck -row $aCheckRowIter -column 12 -sticky e
+grid .myFrame.myChecks.myJDKLbl -row $aCheckRowIter -column 13 -sticky w
incr aCheckRowIter
if { "$::tcl_platform(os)" == "Darwin" } {
incr aCheckRowIter
}
+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
}
# 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})] } {
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"
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="
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=;"
| 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 |
#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
--- /dev/null
+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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
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
TKBRep
TKG3d
TKService
+CSF_RapidJSON
#include <Bnd_Box.hxx>
#include <BRep_Builder.hxx>
#include <DBRep.hxx>
+#include <DDocStd.hxx>
+#include <DDocStd_DrawDocument.hxx>
#include <Draw.hxx>
#include <Draw_Interpretor.hxx>
#include <Draw_PluginMacro.hxx>
#include <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>
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)
{
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",
--- /dev/null
+pload XDE OCAF MODELING VISUALIZATION
+catch { Close D }
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
001 stl_read
002 shape_write_stl
+003 gltf_read
--- /dev/null
+SKIPPED /Error: glTF reader is unavailable - OCCT has been built without RapidJSON support/