1 // Author: Kirill Gavrilov
2 // Copyright (c) 2016-2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
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.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include <RWGltf_CafReader.hxx>
17 #include "RWGltf_GltfJsonParser.pxx"
18 #include <RWGltf_TriangulationReader.hxx>
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>
30 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafReader, RWMesh_CafReader)
32 //! Functor for parallel execution.
33 class RWGltf_CafReader::CafReader_GltfReaderFunctor
39 Handle(RWGltf_PrimitiveArrayReader) Reader;
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())
58 //! Execute task for a face with specified index.
59 void operator() (int theThreadIndex,
60 int theFaceIndex) const
62 GltfReaderTLS& aTlsData = myTlsData.ChangeValue (theThreadIndex);
63 if (aTlsData.Reader.IsNull())
65 aTlsData.Reader = myCafReader->createMeshReaderContext();
66 aTlsData.Reader->SetErrorPrefix (myErrPrefix);
67 aTlsData.Reader->SetCoordinateSystemConverter (myCafReader->myCoordSysConverter);
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);
77 if (myThreadPool.HasThreads())
79 Standard_Mutex::Sentry aLock (&myMutex);
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>
101 //================================================================
102 // Function : Constructor
104 //================================================================
105 RWGltf_CafReader::RWGltf_CafReader()
106 : myToParallel (false)
108 myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
109 myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
112 //================================================================
113 // Function : performMesh
115 //================================================================
116 Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
117 const Handle(Message_ProgressIndicator)& theProgress,
118 const Standard_Boolean theToProbe)
121 OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary);
125 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is not found!", Message_Fail);
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)
139 const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
140 const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
145 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format!", Message_Fail);
149 char aHeader1[8] = {};
150 aFile.read (aHeader1, sizeof(aHeader1));
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);
157 aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
158 aBinBodyLen = int64_t(*aLen) - aBinBodyOffset;
159 if (*aSceneFormat != 0)
161 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format!", Message_Fail);
165 else //if (*aVer == 2)
169 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer) + "!", Message_Warning);
172 for (int aChunkIter = 0; !aFile.eof() && aChunkIter < 2; ++aChunkIter)
174 char aChunkHeader2[8] = {};
175 if (int64_t(aFile.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(aLen))
180 aFile.read (aChunkHeader2, sizeof(aChunkHeader2));
183 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format!", Message_Fail);
187 const uint32_t* aChunkLen = (const uint32_t* )(aChunkHeader2 + 0);
188 const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
189 if (*aChunkType == 0x4E4F534A)
191 aJsonBodyOffset = int64_t(aFile.tellg());
192 aJsonBodyLen = int64_t(*aChunkLen);
194 else if (*aChunkType == 0x004E4942)
196 aBinBodyOffset = int64_t(aFile.tellg());
197 aBinBodyLen = int64_t(*aChunkLen);
201 aFile.seekg (*aChunkLen, std::ios_base::cur);
205 aFile.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
210 aFile.seekg (0, std::ios_base::beg);
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.SetErrorPrefix (anErrPrefix);
219 aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
222 aDoc.SetAttributeMap (myAttribMap);
226 aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
229 #ifdef HAVE_RAPIDJSON
230 rapidjson::ParseResult aRes;
231 rapidjson::IStreamWrapper aFileStream (aFile);
234 aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
238 aRes = aDoc.ParseStream (aFileStream);
242 if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
244 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is empty!", Message_Fail);
247 TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
248 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
249 + anErrDesc + ".", Message_Fail);
254 if (!aDoc.Parse (theProgress))
260 && !readLateData (aDoc.FaceList(), theFile, theProgress))
268 //================================================================
269 // Function : createMeshReaderContext
271 //================================================================
272 Handle(RWGltf_PrimitiveArrayReader) RWGltf_CafReader::createMeshReaderContext()
274 Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
278 //================================================================
279 // Function : readLateData
281 //================================================================
282 Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
283 const TCollection_AsciiString& theFile,
284 const Handle(Message_ProgressIndicator)& theProgress)
286 Message_ProgressSentry aPSentryTris (theProgress, "Loading glTF triangulation", 0, Max (1, theFaces.Size()), 1);
287 const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool();
288 const int aNbThreads = myToParallel ? Min (theFaces.Size(), aThreadPool->NbDefaultThreadsToLaunch()) : 1;
289 OSD_ThreadPool::Launcher aLauncher (*aThreadPool, aNbThreads);
291 CafReader_GltfReaderFunctor aFunctor (this, theFaces, aPSentryTris, aLauncher,
292 TCollection_AsciiString ("File '") + theFile + "' defines invalid glTF!\n");
293 aLauncher.Perform (theFaces.Lower(), theFaces.Upper() + 1, aFunctor);
294 return Standard_True;