0025748: Parallel version of progress indicator
[occt.git] / src / RWGltf / RWGltf_CafReader.cxx
CommitLineData
0a419c51 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>
7e785937 24#include <Message_ProgressScope.hxx>
0a419c51 25#include <OSD_OpenFile.hxx>
26#include <OSD_ThreadPool.hxx>
27
28#include <fstream>
29
30IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
31
32//! Functor for parallel execution.
33class RWGltf_CafReader::CafReader_GltfReaderFunctor
34{
35public:
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,
7e785937 45 const Message_ProgressRange& theProgress,
0a419c51 46 const OSD_ThreadPool::Launcher& theThreadPool,
47 const TCollection_AsciiString& theErrPrefix)
48 : myCafReader (myCafReader),
49 myFaceList (&theFaceList),
0a419c51 50 myErrPrefix (theErrPrefix),
7e785937 51 myProgress (theProgress, "Loading glTF triangulation", Max (1, theFaceList.Size())),
0a419c51 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);
7e785937 80 myProgress.Next();
0a419c51 81 }
82 else
83 {
7e785937 84 myProgress.Next();
0a419c51 85 }
86 }
87
88private:
89
90 RWGltf_CafReader* myCafReader;
91 NCollection_Vector<TopoDS_Face>* myFaceList;
0a419c51 92 TCollection_AsciiString myErrPrefix;
93 mutable Standard_Mutex myMutex;
7e785937 94 mutable Message_ProgressScope myProgress;
0a419c51 95 const OSD_ThreadPool::Launcher& myThreadPool;
7e785937 96 mutable NCollection_Array1<GltfReaderTLS> myTlsData;
0a419c51 97};
98
99//================================================================
100// Function : Constructor
101// Purpose :
102//================================================================
103RWGltf_CafReader::RWGltf_CafReader()
803bdcdf 104: myToParallel (false),
105 myToSkipEmptyNodes (true),
106 myUseMeshNameAsFallback (true)
0a419c51 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//================================================================
116Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
7e785937 117 const Message_ProgressRange& theProgress,
0a419c51 118 const Standard_Boolean theToProbe)
119{
7e785937 120 Message_ProgressScope aPSentry (theProgress, "Reading glTF", 2);
121 aPSentry.Show();
122
0a419c51 123 std::ifstream aFile;
124 OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary);
125 if (!aFile.is_open()
126 || !aFile.good())
127 {
a87b1b37 128 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is not found");
0a419c51 129 return false;
130 }
131
132 bool isBinaryFile = false;
133 char aGlbHeader[12] = {};
134 aFile.read (aGlbHeader, sizeof(aGlbHeader));
135 int64_t aBinBodyOffset = 0;
136 int64_t aBinBodyLen = 0;
137 int64_t aJsonBodyOffset = 0;
138 int64_t aJsonBodyLen = 0;
139 if (::strncmp (aGlbHeader, "glTF", 4) == 0)
140 {
141 isBinaryFile = true;
142 const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
143 const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
144 if (*aVer == 1)
145 {
146 if (*aLen < 20)
147 {
a87b1b37 148 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format");
0a419c51 149 return false;
150 }
151
152 char aHeader1[8] = {};
153 aFile.read (aHeader1, sizeof(aHeader1));
154
155 const uint32_t* aSceneLen = (const uint32_t* )(aHeader1 + 0);
156 const uint32_t* aSceneFormat = (const uint32_t* )(aHeader1 + 4);
157 aJsonBodyOffset = 20;
158 aJsonBodyLen = int64_t(*aSceneLen);
159
160 aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
161 aBinBodyLen = int64_t(*aLen) - aBinBodyOffset;
162 if (*aSceneFormat != 0)
163 {
a87b1b37 164 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format");
0a419c51 165 return false;
166 }
167 }
168 else //if (*aVer == 2)
169 {
170 if (*aVer != 2)
171 {
a87b1b37 172 Message::SendWarning (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer));
0a419c51 173 }
174
175 for (int aChunkIter = 0; !aFile.eof() && aChunkIter < 2; ++aChunkIter)
176 {
177 char aChunkHeader2[8] = {};
9975d32a 178 if (int64_t(aFile.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(*aLen))
0a419c51 179 {
180 break;
181 }
182
183 aFile.read (aChunkHeader2, sizeof(aChunkHeader2));
184 if (!aFile.good())
185 {
a87b1b37 186 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format");
0a419c51 187 return false;
188 }
189
190 const uint32_t* aChunkLen = (const uint32_t* )(aChunkHeader2 + 0);
191 const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
192 if (*aChunkType == 0x4E4F534A)
193 {
194 aJsonBodyOffset = int64_t(aFile.tellg());
195 aJsonBodyLen = int64_t(*aChunkLen);
196 }
197 else if (*aChunkType == 0x004E4942)
198 {
199 aBinBodyOffset = int64_t(aFile.tellg());
200 aBinBodyLen = int64_t(*aChunkLen);
201 }
202 if (*aChunkLen != 0)
203 {
204 aFile.seekg (*aChunkLen, std::ios_base::cur);
205 }
206 }
207
208 aFile.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
209 }
210 }
211 else
212 {
213 aFile.seekg (0, std::ios_base::beg);
214 }
215
216 TCollection_AsciiString anErrPrefix = TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n";
217 RWGltf_GltfJsonParser aDoc (myRootShapes);
218 aDoc.SetFilePath (theFile);
219 aDoc.SetProbeHeader (theToProbe);
220 aDoc.SetExternalFiles (myExternalFiles);
82c59511 221 aDoc.SetMetadata (myMetadata);
0a419c51 222 aDoc.SetErrorPrefix (anErrPrefix);
223 aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
803bdcdf 224 aDoc.SetSkipEmptyNodes (myToSkipEmptyNodes);
225 aDoc.SetMeshNameAsFallback (myUseMeshNameAsFallback);
0a419c51 226 if (!theToProbe)
227 {
228 aDoc.SetAttributeMap (myAttribMap);
229 }
230 if (isBinaryFile)
231 {
232 aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
233 }
234
235#ifdef HAVE_RAPIDJSON
236 rapidjson::ParseResult aRes;
237 rapidjson::IStreamWrapper aFileStream (aFile);
238 if (isBinaryFile)
239 {
240 aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
241 }
242 else
243 {
244 aRes = aDoc.ParseStream (aFileStream);
245 }
246 if (aRes.IsError())
247 {
248 if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
249 {
a87b1b37 250 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' is empty");
0a419c51 251 return false;
252 }
253 TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
a87b1b37 254 Message::SendFail (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
255 + anErrDesc + " [at offset " + (int )aRes.Offset() + "].");
0a419c51 256 return false;
257 }
258#endif
259
7e785937 260 if (!aDoc.Parse (aPSentry.Next()))
0a419c51 261 {
262 return false;
263 }
264
265 if (!theToProbe
7e785937 266 && !readLateData (aDoc.FaceList(), theFile, aPSentry.Next()))
0a419c51 267 {
268 return false;
269 }
270
271 return true;
272}
273
274//================================================================
275// Function : createMeshReaderContext
276// Purpose :
277//================================================================
278Handle(RWGltf_PrimitiveArrayReader) RWGltf_CafReader::createMeshReaderContext()
279{
280 Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
281 return aReader;
282}
283
284//================================================================
285// Function : readLateData
286// Purpose :
287//================================================================
288Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
289 const TCollection_AsciiString& theFile,
7e785937 290 const Message_ProgressRange& theProgress)
0a419c51 291{
0a419c51 292 const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
293 const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
294 OSD_ThreadPool::Launcher aLauncher (*aThreadPool, aNbThreads);
295
7e785937 296 CafReader_GltfReaderFunctor aFunctor (this, theFaces, theProgress, aLauncher,
0a419c51 297 TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n");
298 aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
299 return Standard_True;
300}