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),
107 myToSkipEmptyNodes (true),
108 myUseMeshNameAsFallback (true)
110 myCoordSysConverter.SetInputLengthUnit (1.0); // glTF defines model in meters
111 myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
114 //================================================================
115 // Function : performMesh
117 //================================================================
118 Standard_Boolean RWGltf_CafReader::performMesh (const TCollection_AsciiString& theFile,
119 const Handle(Message_ProgressIndicator)& theProgress,
120 const Standard_Boolean theToProbe)
123 OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary);
127 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is not found!", Message_Fail);
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)
141 const uint32_t* aVer = (const uint32_t* )(aGlbHeader + 4);
142 const uint32_t* aLen = (const uint32_t* )(aGlbHeader + 8);
147 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' has broken glTF format!", Message_Fail);
151 char aHeader1[8] = {};
152 aFile.read (aHeader1, sizeof(aHeader1));
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);
159 aBinBodyOffset = aJsonBodyOffset + aJsonBodyLen;
160 aBinBodyLen = int64_t(*aLen) - aBinBodyOffset;
161 if (*aSceneFormat != 0)
163 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported Scene format!", Message_Fail);
167 else //if (*aVer == 2)
171 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unknown version " + int(*aVer) + "!", Message_Warning);
174 for (int aChunkIter = 0; !aFile.eof() && aChunkIter < 2; ++aChunkIter)
176 char aChunkHeader2[8] = {};
177 if (int64_t(aFile.tellg()) + int64_t(sizeof(aChunkHeader2)) > int64_t(*aLen))
182 aFile.read (aChunkHeader2, sizeof(aChunkHeader2));
185 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is written using unsupported format!", Message_Fail);
189 const uint32_t* aChunkLen = (const uint32_t* )(aChunkHeader2 + 0);
190 const uint32_t* aChunkType = (const uint32_t* )(aChunkHeader2 + 4);
191 if (*aChunkType == 0x4E4F534A)
193 aJsonBodyOffset = int64_t(aFile.tellg());
194 aJsonBodyLen = int64_t(*aChunkLen);
196 else if (*aChunkType == 0x004E4942)
198 aBinBodyOffset = int64_t(aFile.tellg());
199 aBinBodyLen = int64_t(*aChunkLen);
203 aFile.seekg (*aChunkLen, std::ios_base::cur);
207 aFile.seekg ((std::streamoff )aJsonBodyOffset, std::ios_base::beg);
212 aFile.seekg (0, std::ios_base::beg);
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);
220 aDoc.SetMetadata (myMetadata);
221 aDoc.SetErrorPrefix (anErrPrefix);
222 aDoc.SetCoordinateSystemConverter (myCoordSysConverter);
223 aDoc.SetSkipEmptyNodes (myToSkipEmptyNodes);
224 aDoc.SetMeshNameAsFallback (myUseMeshNameAsFallback);
227 aDoc.SetAttributeMap (myAttribMap);
231 aDoc.SetBinaryFormat (aBinBodyOffset, aBinBodyLen);
234 #ifdef HAVE_RAPIDJSON
235 rapidjson::ParseResult aRes;
236 rapidjson::IStreamWrapper aFileStream (aFile);
239 aRes = aDoc.ParseStream<rapidjson::kParseStopWhenDoneFlag, rapidjson::UTF8<>, rapidjson::IStreamWrapper> (aFileStream);
243 aRes = aDoc.ParseStream (aFileStream);
247 if (aRes.Code() == rapidjson::kParseErrorDocumentEmpty)
249 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' is empty!", Message_Fail);
252 TCollection_AsciiString anErrDesc (RWGltf_GltfJsonParser::FormatParseError (aRes.Code()));
253 Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + theFile + "' defines invalid JSON document!\n"
254 + anErrDesc + " [at offset " + (int )aRes.Offset() + "].", Message_Fail);
259 if (!aDoc.Parse (theProgress))
265 && !readLateData (aDoc.FaceList(), theFile, theProgress))
273 //================================================================
274 // Function : createMeshReaderContext
276 //================================================================
277 Handle(RWGltf_PrimitiveArrayReader) RWGltf_CafReader::createMeshReaderContext()
279 Handle(RWGltf_TriangulationReader) aReader = new RWGltf_TriangulationReader();
283 //================================================================
284 // Function : readLateData
286 //================================================================
287 Standard_Boolean RWGltf_CafReader::readLateData (NCollection_Vector<TopoDS_Face>& theFaces,
288 const TCollection_AsciiString& theFile,
289 const Handle(Message_ProgressIndicator)& theProgress)
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);
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;