0030811: Data Exchange, RWGltf_CafReader - fix inaccessibility of properties
[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.pxx"
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_ProgressSentry.hxx>
25 #include <OSD_OpenFile.hxx>
26 #include <OSD_ThreadPool.hxx>
27
28 #include <fstream>
29
30 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
31
32 //! Functor for parallel execution.
33 class RWGltf_CafReader::CafReader_GltfReaderFunctor
34 {
35 public:
36
37   struct GltfReaderTLS
38   {
39     Handle(RWGltf_PrimitiveArrayReader) Reader;
40   };
41
42   //! Main constructor.
43   CafReader_GltfReaderFunctor (RWGltf_CafReader* myCafReader,
44                                NCollection_Vector<TopoDS_Face>& theFaceList,
45                                Message_ProgressSentry& theSentry,
46                                const OSD_ThreadPool::Launcher& theThreadPool,
47                                const TCollection_AsciiString& theErrPrefix)
48   : myCafReader (myCafReader),
49     myFaceList  (&theFaceList),
50     mySentry    (&theSentry),
51     myErrPrefix (theErrPrefix),
52     myThreadPool(theThreadPool),
53     myTlsData   (theThreadPool.LowerThreadIndex(), theThreadPool.UpperThreadIndex())
54   {
55     //
56   }
57
58   //! Execute task for a face with specified index.
59   void operator() (int theThreadIndex,
60                    int theFaceIndex) const
61   {
62     GltfReaderTLS& aTlsData = myTlsData.ChangeValue (theThreadIndex);
63     if (aTlsData.Reader.IsNull())
64     {
65       aTlsData.Reader = myCafReader->createMeshReaderContext();
66       aTlsData.Reader->SetErrorPrefix (myErrPrefix);
67       aTlsData.Reader->SetCoordinateSystemConverter (myCafReader->myCoordSysConverter);
68     }
69
70     TopLoc_Location aDummyLoc;
71     TopoDS_Face& aFace = myFaceList->ChangeValue (theFaceIndex);
72     Handle(RWGltf_GltfLatePrimitiveArray) aLateData = Handle(RWGltf_GltfLatePrimitiveArray)::DownCast (BRep_Tool::Triangulation (aFace, aDummyLoc));
73     Handle(Poly_Triangulation) aPolyData = aTlsData.Reader->Load (aLateData);
74     BRep_Builder aBuilder;
75     aBuilder.UpdateFace (aFace, aPolyData);
76
77     if (myThreadPool.HasThreads())
78     {
79       Standard_Mutex::Sentry aLock (&myMutex);
80       mySentry->Next();
81     }
82     else
83     {
84       mySentry->Next();
85     }
86   }
87
88 private:
89
90   RWGltf_CafReader* myCafReader;
91   NCollection_Vector<TopoDS_Face>* myFaceList;
92   Message_ProgressSentry*   mySentry;
93   TCollection_AsciiString   myErrPrefix;
94   mutable Standard_Mutex    myMutex;
95   const OSD_ThreadPool::Launcher& myThreadPool;
96   mutable NCollection_Array1<GltfReaderTLS>
97                             myTlsData;
98
99 };
100
101 //================================================================
102 // Function : Constructor
103 // Purpose  :
104 //================================================================
105 RWGltf_CafReader::RWGltf_CafReader()
106 : myToParallel (false)
107 {
108   myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
109   myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
110 }
111
112 //================================================================
113 // Function : performMesh
114 // Purpose  :
115 //================================================================
116 Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
117                                                 const Handle(Message_ProgressIndicator)& theProgress,
118                                                 const Standard_Boolean theToProbe)
119 {
120   std::ifstream aFile;
121   OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary);
122   if (!aFile.is_open()
123    || !aFile.good())
124   {
125     Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is not found!", Message_Fail);
126     return false;
127   }
128
129   bool isBinaryFile = false;
130   char aGlbHeader[12] = {};
131   aFile.read (aGlbHeader, sizeof(aGlbHeader));
132   int64_t aBinBodyOffset  = 0;
133   int64_t aBinBodyLen     = 0;
134   int64_t aJsonBodyOffset = 0;
135   int64_t aJsonBodyLen    = 0;
136   if (::strncmp (aGlbHeader, "glTF", 4) == 0)
137   {
138     isBinaryFile = true;
139     const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
140     const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
141     if (*aVer == 1)
142     {
143       if (*aLen < 20)
144       {
145         Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format!", Message_Fail);
146         return false;
147       }
148
149       char aHeader1[8] = {};
150       aFile.read (aHeader1, sizeof(aHeader1));
151
152       const uint32_t* aSceneLen    = (const uint32_t* )(aHeader1 + 0);
153       const uint32_t* aSceneFormat = (const uint32_t* )(aHeader1 + 4);
154       aJsonBodyOffset = 20;
155       aJsonBodyLen    = int64_t(*aSceneLen);
156
157       aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
158       aBinBodyLen    = int64_t(*aLen) - aBinBodyOffset;
159       if (*aSceneFormat != 0)
160       {
161         Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format!", Message_Fail);
162         return false;
163       }
164     }
165     else //if (*aVer == 2)
166     {
167       if (*aVer != 2)
168       {
169         Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer) + "!", Message_Warning);
170       }
171
172       for (int aChunkIter = 0; !aFile.eof() && aChunkIter < 2; ++aChunkIter)
173       {
174         char aChunkHeader2[8] = {};
175         if (int64_t(aFile.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(aLen))
176         {
177           break;
178         }
179
180         aFile.read (aChunkHeader2, sizeof(aChunkHeader2));
181         if (!aFile.good())
182         {
183           Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format!", Message_Fail);
184           return false;
185         }
186
187         const uint32_t* aChunkLen  = (const uint32_t* )(aChunkHeader2 + 0);
188         const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
189         if (*aChunkType == 0x4E4F534A)
190         {
191           aJsonBodyOffset = int64_t(aFile.tellg());
192           aJsonBodyLen    = int64_t(*aChunkLen);
193         }
194         else if (*aChunkType == 0x004E4942)
195         {
196           aBinBodyOffset = int64_t(aFile.tellg());
197           aBinBodyLen    = int64_t(*aChunkLen);
198         }
199         if (*aChunkLen != 0)
200         {
201           aFile.seekg (*aChunkLen, std::ios_base::cur);
202         }
203       }
204
205       aFile.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
206     }
207   }
208   else
209   {
210     aFile.seekg (0, std::ios_base::beg);
211   }
212
213   TCollection_AsciiString anErrPrefix = TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n";
214   RWGltf_GltfJsonParser aDoc (myRootShapes);
215   aDoc.SetFilePath (theFile);
216   aDoc.SetProbeHeader (theToProbe);
217   aDoc.SetExternalFiles (myExternalFiles);
218   aDoc.SetMetadata (myMetadata);
219   aDoc.SetErrorPrefix (anErrPrefix);
220   aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
221   if (!theToProbe)
222   {
223     aDoc.SetAttributeMap (myAttribMap);
224   }
225   if (isBinaryFile)
226   {
227     aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
228   }
229
230 #ifdef HAVE_RAPIDJSON
231   rapidjson::ParseResult aRes;
232   rapidjson::IStreamWrapper aFileStream (aFile);
233   if (isBinaryFile)
234   {
235     aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
236   }
237   else
238   {
239     aRes = aDoc.ParseStream (aFileStream);
240   }
241   if (aRes.IsError())
242   {
243     if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
244     {
245       Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is empty!", Message_Fail);
246       return false;
247     }
248     TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
249     Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
250                                      + anErrDesc + ".", Message_Fail);
251     return false;
252   }
253 #endif
254
255   if (!aDoc.Parse (theProgress))
256   {
257     return false;
258   }
259
260   if (!theToProbe
261    && !readLateData (aDoc.FaceList(), theFile, theProgress))
262   {
263     return false;
264   }
265
266   return true;
267 }
268
269 //================================================================
270 // Function : createMeshReaderContext
271 // Purpose  :
272 //================================================================
273 Handle(RWGltf_PrimitiveArrayReader) RWGltf_CafReader::createMeshReaderContext()
274 {
275   Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
276   return aReader;
277 }
278
279 //================================================================
280 // Function : readLateData
281 // Purpose  :
282 //================================================================
283 Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
284                                                  const TCollection_AsciiString& theFile,
285                                                  const Handle(Message_ProgressIndicator)& theProgress)
286 {
287   Message_ProgressSentry aPSentryTris (theProgress, "Loading glTF triangulation", 0, Max (1, theFaces.Size()), 1);
288   const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
289   const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
290   OSD_ThreadPool::Launcher aLauncher (*aThreadPool, aNbThreads);
291
292   CafReader_GltfReaderFunctor aFunctor (this, theFaces, aPSentryTris, aLauncher,
293                                         TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n");
294   aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
295   return Standard_True;
296 }