0032540: RWGltf_CafReader - optional "scene" parameter encoded as mandatory
[occt.git] / src / RWGltf / RWGltf_CafReader.cxx
1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
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.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <RWGltf_CafReader.hxx>
16
17 #include "RWGltf_GltfJsonParser.hxx"
18 #include <RWGltf_TriangulationReader.hxx>
19
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
29 #include <fstream>
30
31 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
32
33 //! Abstract base functor for parallel execution of glTF data loading.
34 class RWGltf_CafReader::CafReader_GltfBaseLoadingFunctor
35 {
36 public:
37
38   //! Main constructor.
39   CafReader_GltfBaseLoadingFunctor (NCollection_Vector<TopoDS_Face>& theFaceList,
40                                     const Message_ProgressRange& theProgress,
41                                     const OSD_ThreadPool::Launcher& theThreadPool)
42   : myFaceList  (&theFaceList),
43     myProgress  (theProgress, "Loading glTF triangulation", Max (1, theFaceList.Size())),
44     myThreadPool(theThreadPool)
45   {
46     //
47   }
48
49   //! Execute task for a face with specified index.
50   void operator() (int theThreadIndex,
51                    int theFaceIndex) const
52   {
53     TopLoc_Location aDummyLoc;
54     TopoDS_Face& aFace = myFaceList->ChangeValue (theFaceIndex);
55     Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (aFace, aDummyLoc));
56     Handle(Poly_Triangulation) aPolyData = loadData (aLateData, theThreadIndex);
57     if (!aPolyData.IsNull())
58     {
59       BRep_Builder aBuilder;
60       aBuilder.UpdateFace (aFace, aPolyData); // replace all "proxy"-triangulations of face by loaded active one.
61     }
62     if (myThreadPool.HasThreads())
63     {
64       Standard_Mutex::Sentry aLock (&myMutex);
65       myProgress.Next();
66     }
67     else
68     {
69       myProgress.Next();
70     }
71   }
72
73 protected:
74
75   //! Load primitive array.
76   virtual Handle(Poly_Triangulation) loadData (const Handle(RWGltf_GltfLatePrimitiveArray)& theLateData,
77                                                int theThreadIndex) const = 0;
78
79 protected:
80
81   NCollection_Vector<TopoDS_Face>* myFaceList;
82   mutable Standard_Mutex           myMutex;
83   mutable Message_ProgressScope    myProgress;
84   const OSD_ThreadPool::Launcher&  myThreadPool;
85 };
86 //! Functor for parallel execution of all glTF data loading.
87 class RWGltf_CafReader::CafReader_GltfFullDataLoadingFunctor : public RWGltf_CafReader::CafReader_GltfBaseLoadingFunctor
88 {
89 public:
90
91   struct GltfReaderTLS
92   {
93     Handle(OSD_FileSystem) FileSystem;
94   };
95
96   //! Main constructor.
97   CafReader_GltfFullDataLoadingFunctor (RWGltf_CafReader* myCafReader,
98                                         NCollection_Vector<TopoDS_Face>& theFaceList,
99                                         const Message_ProgressRange& theProgress,
100                                         const OSD_ThreadPool::Launcher& theThreadPool)
101   : CafReader_GltfBaseLoadingFunctor (theFaceList, theProgress, theThreadPool),
102     myCafReader (myCafReader),
103     myTlsData   (theThreadPool.LowerThreadIndex(), theThreadPool.UpperThreadIndex())
104   {
105     //
106   }
107
108 protected:
109
110   //! Load primitive array.
111   virtual Handle(Poly_Triangulation) loadData (const Handle(RWGltf_GltfLatePrimitiveArray)& theLateData,
112                                                int theThreadIndex) const Standard_OVERRIDE
113   {
114     GltfReaderTLS& aTlsData = myTlsData.ChangeValue (theThreadIndex);
115     if (aTlsData.FileSystem.IsNull())
116     {
117       aTlsData.FileSystem = new OSD_CachedFileSystem();
118     }
119     // Load stream data if exists
120     if (Handle(Poly_Triangulation) aStreamLoadedData = theLateData->LoadStreamData())
121     {
122       return aStreamLoadedData;
123     }
124     // Load file data
125     if (myCafReader->ToKeepLateData())
126     {
127       theLateData->LoadDeferredData (aTlsData.FileSystem);
128       return Handle(Poly_Triangulation)();
129     }
130     return theLateData->DetachedLoadDeferredData (aTlsData.FileSystem);
131   }
132
133 private:
134
135   RWGltf_CafReader* myCafReader;
136   mutable NCollection_Array1<GltfReaderTLS> myTlsData;
137 };
138
139 //! Functor for parallel execution of loading of only glTF data saved in stream buffers.
140 class RWGltf_CafReader::CafReader_GltfStreamDataLoadingFunctor : public RWGltf_CafReader::CafReader_GltfBaseLoadingFunctor
141 {
142 public:
143
144   //! Main constructor.
145   CafReader_GltfStreamDataLoadingFunctor (NCollection_Vector<TopoDS_Face>& theFaceList,
146                                           const Message_ProgressRange& theProgress,
147                                           const OSD_ThreadPool::Launcher& theThreadPool)
148   : CafReader_GltfBaseLoadingFunctor (theFaceList, theProgress, theThreadPool)
149   {
150     //
151   }
152
153 protected:
154
155   //! Load primitive array.
156   virtual Handle(Poly_Triangulation) loadData (const Handle(RWGltf_GltfLatePrimitiveArray)& theLateData,
157                                                int theThreadIndex) const Standard_OVERRIDE
158   {
159     (void )theThreadIndex;
160     return theLateData->LoadStreamData();
161   }
162 };
163
164 //================================================================
165 // Function : Constructor
166 // Purpose  :
167 //================================================================
168 RWGltf_CafReader::RWGltf_CafReader()
169 : myToParallel (false),
170   myToSkipEmptyNodes (true),
171   myToLoadAllScenes (false),
172   myUseMeshNameAsFallback (true),
173   myIsDoublePrecision (false),
174   myToSkipLateDataLoading (false),
175   myToKeepLateData (true),
176   myToPrintDebugMessages (false)
177 {
178   myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
179   myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
180 }
181
182 //================================================================
183 // Function : performMesh
184 // Purpose  :
185 //================================================================
186 Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
187                                                 const Message_ProgressRange& theProgress,
188                                                 const Standard_Boolean theToProbe)
189 {
190   Message_ProgressScope aPSentry (theProgress, "Reading glTF", 2);
191   aPSentry.Show();
192
193   const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
194   opencascade::std::shared_ptr<std::istream> aFile = aFileSystem->OpenIStream (theFile, std::ios::in | std::ios::binary);
195   if (aFile.get() == NULL || !aFile->good())
196   {
197     Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is not found");
198     return false;
199   }
200
201   bool isBinaryFile = false;
202   char aGlbHeader[12] = {};
203   aFile->read (aGlbHeader, sizeof (aGlbHeader));
204   int64_t aBinBodyOffset  = 0;
205   int64_t aBinBodyLen     = 0;
206   int64_t aJsonBodyOffset = 0;
207   int64_t aJsonBodyLen    = 0;
208   if (::strncmp (aGlbHeader, "glTF", 4) == 0)
209   {
210     isBinaryFile = true;
211     const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
212     const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
213     if (*aVer == 1)
214     {
215       if (*aLen < 20)
216       {
217         Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format");
218         return false;
219       }
220
221       char aHeader1[8] = {};
222       aFile->read (aHeader1, sizeof (aHeader1));
223
224       const uint32_t* aSceneLen    = (const uint32_t* )(aHeader1 + 0);
225       const uint32_t* aSceneFormat = (const uint32_t* )(aHeader1 + 4);
226       aJsonBodyOffset = 20;
227       aJsonBodyLen    = int64_t(*aSceneLen);
228
229       aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
230       aBinBodyLen    = int64_t(*aLen) - aBinBodyOffset;
231       if (*aSceneFormat != 0)
232       {
233         Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format");
234         return false;
235       }
236     }
237     else //if (*aVer == 2)
238     {
239       if (*aVer != 2)
240       {
241         Message::SendWarning (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer));
242       }
243
244       for (int aChunkIter = 0; !aFile->eof() && aChunkIter < 2; ++aChunkIter)
245       {
246         char aChunkHeader2[8] = {};
247         if (int64_t (aFile->tellg()) + int64_t (sizeof (aChunkHeader2)) > int64_t (*aLen))
248         {
249           break;
250         }
251
252         aFile->read (aChunkHeader2, sizeof (aChunkHeader2));
253         if (!aFile->good())
254         {
255           Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format");
256           return false;
257         }
258
259         const uint32_t* aChunkLen  = (const uint32_t* )(aChunkHeader2 + 0);
260         const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
261         if (*aChunkType == 0x4E4F534A)
262         {
263           aJsonBodyOffset = int64_t (aFile->tellg());
264           aJsonBodyLen    = int64_t (*aChunkLen);
265         }
266         else if (*aChunkType == 0x004E4942)
267         {
268           aBinBodyOffset = int64_t (aFile->tellg());
269           aBinBodyLen    = int64_t (*aChunkLen);
270         }
271         if (*aChunkLen != 0)
272         {
273           aFile->seekg (*aChunkLen, std::ios_base::cur);
274         }
275       }
276
277       aFile->seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
278     }
279   }
280   else
281   {
282     aFile->seekg (0, std::ios_base::beg);
283   }
284
285   TCollection_AsciiString anErrPrefix = TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n";
286   RWGltf_GltfJsonParser aDoc (myRootShapes);
287   aDoc.SetFilePath (theFile);
288   aDoc.SetProbeHeader (theToProbe);
289   aDoc.SetExternalFiles (myExternalFiles);
290   aDoc.SetMetadata (myMetadata);
291   aDoc.SetErrorPrefix (anErrPrefix);
292   aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
293   aDoc.SetSkipEmptyNodes (myToSkipEmptyNodes);
294   aDoc.SetLoadAllScenes (myToLoadAllScenes);
295   aDoc.SetMeshNameAsFallback (myUseMeshNameAsFallback);
296   if (!theToProbe)
297   {
298     aDoc.SetAttributeMap (myAttribMap);
299   }
300   if (isBinaryFile)
301   {
302     aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
303   }
304
305 #ifdef HAVE_RAPIDJSON
306   rapidjson::ParseResult aRes;
307   rapidjson::IStreamWrapper aFileStream (*aFile);
308   if (isBinaryFile)
309   {
310     aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
311   }
312   else
313   {
314     aRes = aDoc.ParseStream (aFileStream);
315   }
316   if (aRes.IsError())
317   {
318     if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
319     {
320       Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is empty");
321       return false;
322     }
323     TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
324     Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
325                      + anErrDesc + " [at offset " + (int )aRes.Offset() + "].");
326     return false;
327   }
328 #endif
329
330   if (!aDoc.Parse (aPSentry.Next()))
331   {
332     return false;
333   }
334
335   if (!theToProbe
336    && !readLateData (aDoc.FaceList(), theFile, aPSentry.Next()))
337   {
338     return false;
339   }
340
341   return true;
342 }
343
344 //================================================================
345 // Function : createMeshReaderContext
346 // Purpose  :
347 //================================================================
348 Handle(RWMesh_TriangulationReader) RWGltf_CafReader::createMeshReaderContext() const
349 {
350   Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
351   aReader->SetDoublePrecision (myIsDoublePrecision);
352   aReader->SetCoordinateSystemConverter (myCoordSysConverter);
353   aReader->SetToSkipDegenerates (false);
354   aReader->SetToPrintDebugMessages (myToPrintDebugMessages);
355   return aReader;
356 }
357
358 //================================================================
359 // Function : readLateData
360 // Purpose  :
361 //================================================================
362 Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
363                                                  const TCollection_AsciiString& theFile,
364                                                  const Message_ProgressRange& theProgress)
365 {
366   Handle(RWGltf_TriangulationReader) aReader = Handle(RWGltf_TriangulationReader)::DownCast(createMeshReaderContext());
367   aReader->SetFileName (theFile);
368   updateLateDataReader (theFaces, aReader);
369
370   if (myToSkipLateDataLoading)
371   {
372     // Load glTF data encoded in base64. It should not be skipped and saved in "proxy" object to be loaded later.
373     const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
374     const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
375     OSD_ThreadPool::Launcher aLauncher(*aThreadPool, aNbThreads);
376     CafReader_GltfStreamDataLoadingFunctor aFunctor(theFaces, theProgress, aLauncher);
377     aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
378
379     return Standard_True;
380   }
381
382   aReader->StartStatistic();
383
384   const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
385   const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
386   OSD_ThreadPool::Launcher aLauncher (*aThreadPool, aNbThreads);
387
388   CafReader_GltfFullDataLoadingFunctor aFunctor (this, theFaces, theProgress, aLauncher);
389   aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
390
391   aReader->PrintStatistic();
392   aReader->StopStatistic();
393
394   return Standard_True;
395 }
396
397 //================================================================
398 // Function : updateLateDataReader
399 // Purpose  :
400 //================================================================
401 void RWGltf_CafReader::updateLateDataReader (NCollection_Vector<TopoDS_Face>& theFaces,
402                                              const Handle(RWMesh_TriangulationReader)& theReader) const
403 {
404   TopLoc_Location aDummyLoc;
405   for (NCollection_Vector<TopoDS_Face>::Iterator aFaceIter(theFaces); aFaceIter.More(); aFaceIter.Next())
406   {
407     const TopoDS_Face& aFace = aFaceIter.Value();
408     for (Poly_ListOfTriangulation::Iterator anIter(BRep_Tool::Triangulations (aFace, aDummyLoc)); anIter.More(); anIter.Next())
409     {
410       Handle(RWGltf_GltfLatePrimitiveArray) aData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast(anIter.Value());
411       if (!aData.IsNull())
412       {
413         aData->SetReader (theReader);
414       }
415     }
416   }
417 }