1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include <RWGltf_CafReader.hxx>
17 #include "RWGltf_GltfJsonParser.hxx"
18 #include <RWGltf_TriangulationReader.hxx>
20 #include <BRep_Builder.hxx>
21 #include <BRep_Tool.hxx>
22 #include <Message.hxx>
23 #include <Message_Messenger.hxx>
24 #include <Message_ProgressScope.hxx>
25 #include <OSD_CachedFileSystem.hxx>
26 #include <OSD_FileSystem.hxx>
27 #include <OSD_ThreadPool.hxx>
28 #include <RWGltf_GltfLatePrimitiveArray.hxx>
30 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
32 //! Abstract base functor for parallel execution of glTF data loading.
33 class RWGltf_CafReader::CafReader_GltfBaseLoadingFunctor
38 CafReader_GltfBaseLoadingFunctor (NCollection_Vector<TopoDS_Face>& theFaceList,
39 const Message_ProgressRange& theProgress,
40 const OSD_ThreadPool::Launcher& theThreadPool)
41 : myFaceList (&theFaceList),
42 myProgress (theProgress, "Loading glTF triangulation", Max (1, theFaceList.Size())),
43 myThreadPool(theThreadPool)
48 //! Execute task for a face with specified index.
49 void operator() (int theThreadIndex,
50 int theFaceIndex) const
52 TopLoc_Location aDummyLoc;
53 TopoDS_Face& aFace = myFaceList->ChangeValue (theFaceIndex);
54 Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (aFace, aDummyLoc));
55 Handle(Poly_Triangulation) aPolyData = loadData (aLateData, theThreadIndex);
56 if (!aPolyData.IsNull())
58 BRep_Builder aBuilder;
59 aBuilder.UpdateFace (aFace, aPolyData); // replace all "proxy"-triangulations of face by loaded active one.
61 if (myThreadPool.HasThreads())
63 Standard_Mutex::Sentry aLock (&myMutex);
74 //! Load primitive array.
75 virtual Handle(Poly_Triangulation) loadData (const Handle(RWGltf_GltfLatePrimitiveArray)& theLateData,
76 int theThreadIndex) const = 0;
80 NCollection_Vector<TopoDS_Face>* myFaceList;
81 mutable Standard_Mutex myMutex;
82 mutable Message_ProgressScope myProgress;
83 const OSD_ThreadPool::Launcher& myThreadPool;
85 //! Functor for parallel execution of all glTF data loading.
86 class RWGltf_CafReader::CafReader_GltfFullDataLoadingFunctor : public RWGltf_CafReader::CafReader_GltfBaseLoadingFunctor
92 Handle(OSD_FileSystem) FileSystem;
96 CafReader_GltfFullDataLoadingFunctor (RWGltf_CafReader* myCafReader,
97 NCollection_Vector<TopoDS_Face>& theFaceList,
98 const Message_ProgressRange& theProgress,
99 const OSD_ThreadPool::Launcher& theThreadPool)
100 : CafReader_GltfBaseLoadingFunctor (theFaceList, theProgress, theThreadPool),
101 myCafReader (myCafReader),
102 myTlsData (theThreadPool.LowerThreadIndex(), theThreadPool.UpperThreadIndex())
109 //! Load primitive array.
110 virtual Handle(Poly_Triangulation) loadData (const Handle(RWGltf_GltfLatePrimitiveArray)& theLateData,
111 int theThreadIndex) const Standard_OVERRIDE
113 GltfReaderTLS& aTlsData = myTlsData.ChangeValue (theThreadIndex);
114 if (aTlsData.FileSystem.IsNull())
116 aTlsData.FileSystem = new OSD_CachedFileSystem();
118 // Load stream data if exists
119 if (Handle(Poly_Triangulation) aStreamLoadedData = theLateData->LoadStreamData())
121 return aStreamLoadedData;
124 if (myCafReader->ToKeepLateData())
126 theLateData->LoadDeferredData (aTlsData.FileSystem);
127 return Handle(Poly_Triangulation)();
129 return theLateData->DetachedLoadDeferredData (aTlsData.FileSystem);
134 RWGltf_CafReader* myCafReader;
135 mutable NCollection_Array1<GltfReaderTLS> myTlsData;
138 //! Functor for parallel execution of loading of only glTF data saved in stream buffers.
139 class RWGltf_CafReader::CafReader_GltfStreamDataLoadingFunctor : public RWGltf_CafReader::CafReader_GltfBaseLoadingFunctor
143 //! Main constructor.
144 CafReader_GltfStreamDataLoadingFunctor (NCollection_Vector<TopoDS_Face>& theFaceList,
145 const Message_ProgressRange& theProgress,
146 const OSD_ThreadPool::Launcher& theThreadPool)
147 : CafReader_GltfBaseLoadingFunctor (theFaceList, theProgress, theThreadPool)
154 //! Load primitive array.
155 virtual Handle(Poly_Triangulation) loadData (const Handle(RWGltf_GltfLatePrimitiveArray)& theLateData,
156 int theThreadIndex) const Standard_OVERRIDE
158 (void )theThreadIndex;
159 return theLateData->LoadStreamData();
163 //================================================================
164 // Function : Constructor
166 //================================================================
167 RWGltf_CafReader::RWGltf_CafReader()
168 : myToParallel (false),
169 myToSkipEmptyNodes (true),
170 myToLoadAllScenes (false),
171 myUseMeshNameAsFallback (true),
172 myIsDoublePrecision (false),
173 myToSkipLateDataLoading (false),
174 myToKeepLateData (true),
175 myToPrintDebugMessages (false)
177 myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
178 myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
181 //================================================================
182 // Function : performMesh
184 //================================================================
185 Standard_Boolean RWGltf_CafReader::performMesh (std::istream& theStream,
186 const TCollection_AsciiString& theFile,
187 const Message_ProgressRange& theProgress,
188 const Standard_Boolean theToProbe)
190 Message_ProgressScope aPSentry(theProgress, "Reading glTF", 2);
193 if (!theStream.good())
195 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is not found");
199 bool isBinaryFile = false;
200 char aGlbHeader[12] = {};
201 theStream.read (aGlbHeader, sizeof (aGlbHeader));
202 int64_t aBinBodyOffset = 0;
203 int64_t aBinBodyLen = 0;
204 int64_t aJsonBodyOffset = 0;
205 int64_t aJsonBodyLen = 0;
206 if (::strncmp (aGlbHeader, "glTF", 4) == 0)
209 const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
210 const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
215 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format");
219 char aHeader1[8] = {};
220 theStream.read (aHeader1, sizeof (aHeader1));
222 const uint32_t* aSceneLen = (const uint32_t* )(aHeader1 + 0);
223 const uint32_t* aSceneFormat = (const uint32_t* )(aHeader1 + 4);
224 aJsonBodyOffset = 20;
225 aJsonBodyLen = int64_t(*aSceneLen);
227 aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
228 aBinBodyLen = int64_t(*aLen) - aBinBodyOffset;
229 if (*aSceneFormat != 0)
231 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format");
235 else //if (*aVer == 2)
239 Message::SendWarning (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer));
242 for (int aChunkIter = 0; !theStream.eof() && aChunkIter < 2; ++aChunkIter)
244 char aChunkHeader2[8] = {};
245 if (int64_t (theStream.tellg()) + int64_t (sizeof (aChunkHeader2)) > int64_t (*aLen))
250 theStream.read (aChunkHeader2, sizeof (aChunkHeader2));
251 if (!theStream.good())
253 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format");
257 const uint32_t* aChunkLen = (const uint32_t* )(aChunkHeader2 + 0);
258 const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
259 if (*aChunkType == 0x4E4F534A)
261 aJsonBodyOffset = int64_t (theStream.tellg());
262 aJsonBodyLen = int64_t (*aChunkLen);
264 else if (*aChunkType == 0x004E4942)
266 aBinBodyOffset = int64_t (theStream.tellg());
267 aBinBodyLen = int64_t (*aChunkLen);
271 theStream.seekg (*aChunkLen, std::ios_base::cur);
275 theStream.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
280 theStream.seekg (0, std::ios_base::beg);
283 TCollection_AsciiString anErrPrefix = TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n";
284 RWGltf_GltfJsonParser aDoc (myRootShapes);
285 aDoc.SetFilePath (theFile);
286 aDoc.SetProbeHeader (theToProbe);
287 aDoc.SetExternalFiles (myExternalFiles);
288 aDoc.SetMetadata (myMetadata);
289 aDoc.SetErrorPrefix (anErrPrefix);
290 aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
291 aDoc.SetSkipEmptyNodes (myToSkipEmptyNodes);
292 aDoc.SetLoadAllScenes (myToLoadAllScenes);
293 aDoc.SetMeshNameAsFallback (myUseMeshNameAsFallback);
296 aDoc.SetAttributeMap (myAttribMap);
300 aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
303 #ifdef HAVE_RAPIDJSON
304 rapidjson::ParseResult aRes;
305 rapidjson::IStreamWrapper aFileStream (theStream);
308 aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper>(aFileStream);
312 aRes = aDoc.ParseStream (aFileStream);
316 if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
318 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is empty");
321 TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
322 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
323 + anErrDesc + " [at offset " + (int )aRes.Offset() + "].");
328 if (!aDoc.Parse (aPSentry.Next()))
334 && !readLateData (aDoc.FaceList(), theFile, aPSentry.Next()))
342 //================================================================
343 // Function : createMeshReaderContext
345 //================================================================
346 Handle(RWMesh_TriangulationReader) RWGltf_CafReader::createMeshReaderContext() const
348 Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
349 aReader->SetDoublePrecision (myIsDoublePrecision);
350 aReader->SetCoordinateSystemConverter (myCoordSysConverter);
351 aReader->SetToSkipDegenerates (false);
352 aReader->SetToPrintDebugMessages (myToPrintDebugMessages);
356 //================================================================
357 // Function : readLateData
359 //================================================================
360 Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
361 const TCollection_AsciiString& theFile,
362 const Message_ProgressRange& theProgress)
364 Handle(RWGltf_TriangulationReader) aReader = Handle(RWGltf_TriangulationReader)::DownCast(createMeshReaderContext());
365 aReader->SetFileName (theFile);
366 updateLateDataReader (theFaces, aReader);
368 if (myToSkipLateDataLoading)
370 // Load glTF data encoded in base64. It should not be skipped and saved in "proxy" object to be loaded later.
371 const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
372 const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
373 OSD_ThreadPool::Launcher aLauncher(*aThreadPool, aNbThreads);
374 CafReader_GltfStreamDataLoadingFunctor aFunctor(theFaces, theProgress, aLauncher);
375 aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
377 return Standard_True;
380 aReader->StartStatistic();
382 const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
383 const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
384 OSD_ThreadPool::Launcher aLauncher (*aThreadPool, aNbThreads);
386 CafReader_GltfFullDataLoadingFunctor aFunctor (this, theFaces, theProgress, aLauncher);
387 aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
389 aReader->PrintStatistic();
390 aReader->StopStatistic();
392 return Standard_True;
395 //================================================================
396 // Function : updateLateDataReader
398 //================================================================
399 void RWGltf_CafReader::updateLateDataReader (NCollection_Vector<TopoDS_Face>& theFaces,
400 const Handle(RWMesh_TriangulationReader)& theReader) const
402 TopLoc_Location aDummyLoc;
403 for (NCollection_Vector<TopoDS_Face>::Iterator aFaceIter(theFaces); aFaceIter.More(); aFaceIter.Next())
405 const TopoDS_Face& aFace = aFaceIter.Value();
406 for (Poly_ListOfTriangulation::Iterator anIter(BRep_Tool::Triangulations (aFace, aDummyLoc)); anIter.More(); anIter.Next())
408 Handle(RWGltf_GltfLatePrimitiveArray) aData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(anIter.Value());
411 aData->SetReader (theReader);