0030811: Data Exchange, RWGltf_CafReader - fix inaccessibility of properties
[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>
24#include <Message_ProgressSentry.hxx>
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,
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
88private:
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//================================================================
105RWGltf_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//================================================================
116Standard_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);
82c59511 218 aDoc.SetMetadata (myMetadata);
0a419c51 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//================================================================
273Handle(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//================================================================
283Standard_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}