0031312: Data Exchange - RWGltf_CafReader fails reading texture embedded into base64...
[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()
803bdcdf 106: myToParallel (false),
107 myToSkipEmptyNodes (true),
108 myUseMeshNameAsFallback (true)
0a419c51 109{
110 myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
111 myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
112}
113
114//================================================================
115// Function : performMesh
116// Purpose :
117//================================================================
118Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
119 const Handle(Message_ProgressIndicator)& theProgress,
120 const Standard_Boolean theToProbe)
121{
122 std::ifstream aFile;
123 OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary);
124 if (!aFile.is_open()
125 || !aFile.good())
126 {
127 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is not found!", Message_Fail);
128 return false;
129 }
130
131 bool isBinaryFile = false;
132 char aGlbHeader[12] = {};
133 aFile.read (aGlbHeader, sizeof(aGlbHeader));
134 int64_t aBinBodyOffset = 0;
135 int64_t aBinBodyLen = 0;
136 int64_t aJsonBodyOffset = 0;
137 int64_t aJsonBodyLen = 0;
138 if (::strncmp (aGlbHeader, "glTF", 4) == 0)
139 {
140 isBinaryFile = true;
141 const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
142 const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
143 if (*aVer == 1)
144 {
145 if (*aLen < 20)
146 {
147 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format!", Message_Fail);
148 return false;
149 }
150
151 char aHeader1[8] = {};
152 aFile.read (aHeader1, sizeof(aHeader1));
153
154 const uint32_t* aSceneLen = (const uint32_t* )(aHeader1 + 0);
155 const uint32_t* aSceneFormat = (const uint32_t* )(aHeader1 + 4);
156 aJsonBodyOffset = 20;
157 aJsonBodyLen = int64_t(*aSceneLen);
158
159 aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
160 aBinBodyLen = int64_t(*aLen) - aBinBodyOffset;
161 if (*aSceneFormat != 0)
162 {
163 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format!", Message_Fail);
164 return false;
165 }
166 }
167 else //if (*aVer == 2)
168 {
169 if (*aVer != 2)
170 {
171 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer) + "!", Message_Warning);
172 }
173
174 for (int aChunkIter = 0; !aFile.eof() && aChunkIter < 2; ++aChunkIter)
175 {
176 char aChunkHeader2[8] = {};
9975d32a 177 if (int64_t(aFile.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(*aLen))
0a419c51 178 {
179 break;
180 }
181
182 aFile.read (aChunkHeader2, sizeof(aChunkHeader2));
183 if (!aFile.good())
184 {
185 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format!", Message_Fail);
186 return false;
187 }
188
189 const uint32_t* aChunkLen = (const uint32_t* )(aChunkHeader2 + 0);
190 const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
191 if (*aChunkType == 0x4E4F534A)
192 {
193 aJsonBodyOffset = int64_t(aFile.tellg());
194 aJsonBodyLen = int64_t(*aChunkLen);
195 }
196 else if (*aChunkType == 0x004E4942)
197 {
198 aBinBodyOffset = int64_t(aFile.tellg());
199 aBinBodyLen = int64_t(*aChunkLen);
200 }
201 if (*aChunkLen != 0)
202 {
203 aFile.seekg (*aChunkLen, std::ios_base::cur);
204 }
205 }
206
207 aFile.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
208 }
209 }
210 else
211 {
212 aFile.seekg (0, std::ios_base::beg);
213 }
214
215 TCollection_AsciiString anErrPrefix = TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n";
216 RWGltf_GltfJsonParser aDoc (myRootShapes);
217 aDoc.SetFilePath (theFile);
218 aDoc.SetProbeHeader (theToProbe);
219 aDoc.SetExternalFiles (myExternalFiles);
82c59511 220 aDoc.SetMetadata (myMetadata);
0a419c51 221 aDoc.SetErrorPrefix (anErrPrefix);
222 aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
803bdcdf 223 aDoc.SetSkipEmptyNodes (myToSkipEmptyNodes);
224 aDoc.SetMeshNameAsFallback (myUseMeshNameAsFallback);
0a419c51 225 if (!theToProbe)
226 {
227 aDoc.SetAttributeMap (myAttribMap);
228 }
229 if (isBinaryFile)
230 {
231 aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
232 }
233
234#ifdef HAVE_RAPIDJSON
235 rapidjson::ParseResult aRes;
236 rapidjson::IStreamWrapper aFileStream (aFile);
237 if (isBinaryFile)
238 {
239 aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
240 }
241 else
242 {
243 aRes = aDoc.ParseStream (aFileStream);
244 }
245 if (aRes.IsError())
246 {
247 if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
248 {
249 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is empty!", Message_Fail);
250 return false;
251 }
252 TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
253 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
9975d32a 254 + anErrDesc + " [at offset " + (int )aRes.Offset() + "].", Message_Fail);
0a419c51 255 return false;
256 }
257#endif
258
259 if (!aDoc.Parse (theProgress))
260 {
261 return false;
262 }
263
264 if (!theToProbe
265 && !readLateData (aDoc.FaceList(), theFile, theProgress))
266 {
267 return false;
268 }
269
270 return true;
271}
272
273//================================================================
274// Function : createMeshReaderContext
275// Purpose :
276//================================================================
277Handle(RWGltf_PrimitiveArrayReader) RWGltf_CafReader::createMeshReaderContext()
278{
279 Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
280 return aReader;
281}
282
283//================================================================
284// Function : readLateData
285// Purpose :
286//================================================================
287Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
288 const TCollection_AsciiString& theFile,
289 const Handle(Message_ProgressIndicator)& theProgress)
290{
291 Message_ProgressSentry aPSentryTris (theProgress, "Loading glTF triangulation", 0, Max (1, theFaces.Size()), 1);
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
296 CafReader_GltfReaderFunctor aFunctor (this, theFaces, aPSentryTris, aLauncher,
297 TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n");
298 aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
299 return Standard_True;
300}