0032527: Data Exchange, RWGltf_CafWriter - make name format configurable
[occt.git] / src / RWGltf / RWGltf_CafWriter.cxx
1 // Copyright (c) 2017-2019 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <RWGltf_CafWriter.hxx>
15
16 #include <gp_Quaternion.hxx>
17 #include <Message.hxx>
18 #include <Message_Messenger.hxx>
19 #include <Message_ProgressScope.hxx>
20 #include <NCollection_DataMap.hxx>
21 #include <OSD_FileSystem.hxx>
22 #include <OSD_File.hxx>
23 #include <OSD_Path.hxx>
24 #include <Poly_Triangulation.hxx>
25 #include <RWGltf_GltfAccessorLayout.hxx>
26 #include <RWGltf_GltfMaterialMap.hxx>
27 #include <RWGltf_GltfPrimitiveMode.hxx>
28 #include <RWGltf_GltfRootElement.hxx>
29 #include <RWGltf_GltfSceneNodeMap.hxx>
30 #include <RWMesh.hxx>
31 #include <RWMesh_FaceIterator.hxx>
32 #include <TDataStd_Name.hxx>
33 #include <TDF_Tool.hxx>
34 #include <TDocStd_Document.hxx>
35 #include <XCAFDoc_DocumentTool.hxx>
36 #include <XCAFDoc_ShapeTool.hxx>
37 #include <XCAFPrs_DocumentExplorer.hxx>
38
39 #ifdef HAVE_RAPIDJSON
40   #include <RWGltf_GltfOStreamWriter.hxx>
41 #endif
42
43 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
44
45 namespace
46 {
47   //! Write three float values.
48   static void writeVec3 (std::ostream& theStream,
49                          const gp_XYZ& theVec3)
50   {
51     Graphic3d_Vec3 aVec3 (float(theVec3.X()), float(theVec3.Y()), float(theVec3.Z()));
52     theStream.write ((const char* )aVec3.GetData(), sizeof(aVec3));
53   }
54
55   //! Write three float values.
56   static void writeVec3 (std::ostream& theStream,
57                          const Graphic3d_Vec3& theVec3)
58   {
59     theStream.write ((const char* )theVec3.GetData(), sizeof(theVec3));
60   }
61
62   //! Write two float values.
63   static void writeVec2 (std::ostream& theStream,
64                          const gp_XY&  theVec2)
65   {
66     Graphic3d_Vec2 aVec2 (float(theVec2.X()), float(theVec2.Y()));
67     theStream.write ((const char* )aVec2.GetData(), sizeof(aVec2));
68   }
69
70   //! Write triangle indices.
71   static void writeTriangle32 (std::ostream& theStream,
72                                const Graphic3d_Vec3i& theTri)
73   {
74     theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
75   }
76
77   //! Write triangle indices.
78   static void writeTriangle16 (std::ostream& theStream,
79                                const NCollection_Vec3<uint16_t>& theTri)
80   {
81     theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
82   }
83 }
84
85 //================================================================
86 // Function : Constructor
87 // Purpose  :
88 //================================================================
89 RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
90                                     Standard_Boolean theIsBinary)
91 : myFile          (theFile),
92   myTrsfFormat    (RWGltf_WriterTrsfFormat_Compact),
93   myNodeNameFormat(RWMesh_NameFormat_InstanceOrProduct),
94   myMeshNameFormat(RWMesh_NameFormat_Product),
95   myIsBinary      (theIsBinary),
96   myIsForcedUVExport (false),
97   myToEmbedTexturesInGlb (true),
98   myBinDataLen64  (0)
99 {
100   myCSTrsf.SetOutputLengthUnit (1.0); // meters
101   myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
102
103   TCollection_AsciiString aFolder, aFileName, aShortFileNameBase, aFileExt;
104   OSD_Path::FolderAndFileFromPath (theFile, aFolder, aFileName);
105   OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
106
107   myBinFileNameShort = aShortFileNameBase + ".bin" + (myIsBinary ? ".tmp" : "");
108   myBinFileNameFull = !aFolder.IsEmpty() ? aFolder + myBinFileNameShort : myBinFileNameShort;
109 }
110
111 //================================================================
112 // Function : Destructor
113 // Purpose  :
114 //================================================================
115 RWGltf_CafWriter::~RWGltf_CafWriter()
116 {
117   myWriter.reset();
118 }
119
120 //================================================================
121 // Function : formatName
122 // Purpose  :
123 //================================================================
124 TCollection_AsciiString RWGltf_CafWriter::formatName (RWMesh_NameFormat theFormat,
125                                                       const TDF_Label& theLabel,
126                                                       const TDF_Label& theRefLabel) const
127 {
128   return RWMesh::FormatName (theFormat, theLabel, theRefLabel);
129 }
130
131 //================================================================
132 // Function : toSkipFaceMesh
133 // Purpose  :
134 //================================================================
135 Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
136 {
137   return theFaceIter.IsEmptyMesh();
138 }
139
140 // =======================================================================
141 // function : saveNodes
142 // purpose  :
143 // =======================================================================
144 void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
145                                   std::ostream& theBinFile,
146                                   const RWMesh_FaceIterator& theFaceIter,
147                                   Standard_Integer& theAccessorNb) const
148 {
149   theGltfFace.NodePos.Id            = theAccessorNb++;
150   theGltfFace.NodePos.Count         = theFaceIter.NbNodes();
151   theGltfFace.NodePos.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewPos.ByteOffset;
152   theGltfFace.NodePos.Type          = RWGltf_GltfAccessorLayout_Vec3;
153   theGltfFace.NodePos.ComponentType = RWGltf_GltfAccessorCompType_Float32;
154
155   const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
156   for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
157   {
158     gp_XYZ aNode = theFaceIter.NodeTransformed (aNodeIter).XYZ();
159     myCSTrsf.TransformPosition (aNode);
160     theGltfFace.NodePos.BndBox.Add (Graphic3d_Vec3d(aNode.X(), aNode.Y(), aNode.Z()));
161     writeVec3 (theBinFile, aNode);
162   }
163 }
164
165 // =======================================================================
166 // function : saveNormals
167 // purpose  :
168 // =======================================================================
169 void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
170                                     std::ostream& theBinFile,
171                                     RWMesh_FaceIterator& theFaceIter,
172                                     Standard_Integer& theAccessorNb) const
173 {
174   if (!theFaceIter.HasNormals())
175   {
176     return;
177   }
178
179   theGltfFace.NodeNorm.Id            = theAccessorNb++;
180   theGltfFace.NodeNorm.Count         = theFaceIter.NbNodes();
181   theGltfFace.NodeNorm.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewNorm.ByteOffset;
182   theGltfFace.NodeNorm.Type          = RWGltf_GltfAccessorLayout_Vec3;
183   theGltfFace.NodeNorm.ComponentType = RWGltf_GltfAccessorCompType_Float32;
184
185   const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
186   for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
187   {
188     const gp_Dir aNormal = theFaceIter.NormalTransformed (aNodeIter);
189     Graphic3d_Vec3 aVecNormal ((float )aNormal.X(), (float )aNormal.Y(), (float )aNormal.Z());
190     myCSTrsf.TransformNormal (aVecNormal);
191     writeVec3 (theBinFile, aVecNormal);
192   }
193 }
194
195 // =======================================================================
196 // function : saveTextCoords
197 // purpose  :
198 // =======================================================================
199 void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
200                                        std::ostream& theBinFile,
201                                        const RWMesh_FaceIterator& theFaceIter,
202                                        Standard_Integer& theAccessorNb) const
203 {
204   if (!theFaceIter.HasTexCoords())
205   {
206     return;
207   }
208   if (!myIsForcedUVExport)
209   {
210     if (theFaceIter.FaceStyle().Material().IsNull())
211     {
212       return;
213     }
214
215     if (RWGltf_GltfMaterialMap::baseColorTexture (theFaceIter.FaceStyle().Material()).IsNull()
216      && theFaceIter.FaceStyle().Material()->PbrMaterial().MetallicRoughnessTexture.IsNull()
217      && theFaceIter.FaceStyle().Material()->PbrMaterial().EmissiveTexture.IsNull()
218      && theFaceIter.FaceStyle().Material()->PbrMaterial().OcclusionTexture.IsNull()
219      && theFaceIter.FaceStyle().Material()->PbrMaterial().NormalTexture.IsNull())
220     {
221       return;
222     }
223   }
224
225   theGltfFace.NodeUV.Id            = theAccessorNb++;
226   theGltfFace.NodeUV.Count         = theFaceIter.NbNodes();
227   theGltfFace.NodeUV.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
228   theGltfFace.NodeUV.Type          = RWGltf_GltfAccessorLayout_Vec2;
229   theGltfFace.NodeUV.ComponentType = RWGltf_GltfAccessorCompType_Float32;
230   const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
231   for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
232   {
233     gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
234     aTexCoord.SetY (1.0 - aTexCoord.Y());
235     writeVec2 (theBinFile, aTexCoord.XY());
236   }
237 }
238
239 // =======================================================================
240 // function : saveIndices
241 // purpose  :
242 // =======================================================================
243 void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
244                                     std::ostream& theBinFile,
245                                     const RWMesh_FaceIterator& theFaceIter,
246                                     Standard_Integer& theAccessorNb)
247 {
248   theGltfFace.Indices.Id            = theAccessorNb++;
249   theGltfFace.Indices.Count         = theFaceIter.NbTriangles() * 3;
250   theGltfFace.Indices.ByteOffset    = (int64_t )theBinFile.tellp() - myBuffViewInd.ByteOffset;
251   theGltfFace.Indices.Type          = RWGltf_GltfAccessorLayout_Scalar;
252   theGltfFace.Indices.ComponentType = theGltfFace.NodePos.Count > std::numeric_limits<uint16_t>::max()
253                                     ? RWGltf_GltfAccessorCompType_UInt32
254                                     : RWGltf_GltfAccessorCompType_UInt16;
255
256   const Standard_Integer anElemLower = theFaceIter.ElemLower();
257   const Standard_Integer anElemUpper = theFaceIter.ElemUpper();
258   for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper; ++anElemIter)
259   {
260     Poly_Triangle aTri = theFaceIter.TriangleOriented (anElemIter);
261     aTri(1) -= anElemLower;
262     aTri(2) -= anElemLower;
263     aTri(3) -= anElemLower;
264     if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
265     {
266       writeTriangle16 (theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
267     }
268     else
269     {
270       writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
271     }
272   }
273   if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
274   {
275     // alignment by 4 bytes
276     int64_t aContentLen64 = (int64_t)theBinFile.tellp();
277     while (aContentLen64 % 4 != 0)
278     {
279       theBinFile.write (" ", 1);
280       ++aContentLen64;
281     }
282   }
283 }
284
285 // =======================================================================
286 // function : Perform
287 // purpose  :
288 // =======================================================================
289 bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
290                                 const TColStd_IndexedDataMapOfStringString& theFileInfo,
291                                 const Message_ProgressRange& theProgress)
292 {
293   TDF_LabelSequence aRoots;
294   Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (theDocument->Main());
295   aShapeTool->GetFreeShapes (aRoots);
296   return Perform (theDocument, aRoots, NULL, theFileInfo, theProgress);
297 }
298
299 // =======================================================================
300 // function : Perform
301 // purpose  :
302 // =======================================================================
303 bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
304                                 const TDF_LabelSequence& theRootLabels,
305                                 const TColStd_MapOfAsciiString* theLabelFilter,
306                                 const TColStd_IndexedDataMapOfStringString& theFileInfo,
307                                 const Message_ProgressRange& theProgress)
308 {
309   const Standard_Integer aDefSamplerId = 0;
310   myMaterialMap = new RWGltf_GltfMaterialMap (myFile, aDefSamplerId);
311   myMaterialMap->SetDefaultStyle (myDefaultStyle);
312
313   Message_ProgressScope aPSentry (theProgress, "Writing glTF file", 2);
314   if (!writeBinData (theDocument, theRootLabels, theLabelFilter, aPSentry.Next()))
315   {
316     return false;
317   }
318
319   if (!aPSentry.More())
320   {
321     return false;
322   }
323
324   return writeJson (theDocument, theRootLabels, theLabelFilter, theFileInfo, aPSentry.Next());
325 }
326
327 // =======================================================================
328 // function : writeBinData
329 // purpose  :
330 // =======================================================================
331 bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument,
332                                      const TDF_LabelSequence& theRootLabels,
333                                      const TColStd_MapOfAsciiString* theLabelFilter,
334                                      const Message_ProgressRange& theProgress)
335 {
336   myBuffViewPos.Id               = RWGltf_GltfAccessor::INVALID_ID;
337   myBuffViewPos.ByteOffset       = 0;
338   myBuffViewPos.ByteLength       = 0;
339   myBuffViewPos.ByteStride       = 12;
340   myBuffViewPos.Target           = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
341
342   myBuffViewNorm.Id              = RWGltf_GltfAccessor::INVALID_ID;
343   myBuffViewNorm.ByteOffset      = 0;
344   myBuffViewNorm.ByteLength      = 0;
345   myBuffViewNorm.ByteStride      = 12;
346   myBuffViewNorm.Target          = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
347
348   myBuffViewTextCoord.Id         = RWGltf_GltfAccessor::INVALID_ID;
349   myBuffViewTextCoord.ByteOffset = 0;
350   myBuffViewTextCoord.ByteLength = 0;
351   myBuffViewTextCoord.ByteStride = 8;
352   myBuffViewTextCoord.Target     = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
353
354   myBuffViewInd.Id               = RWGltf_GltfAccessor::INVALID_ID;
355   myBuffViewInd.ByteOffset       = 0;
356   myBuffViewInd.ByteLength       = 0;
357   myBuffViewInd.Target           = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
358
359   myBinDataMap.Clear();
360   myBinDataLen64 = 0;
361
362   const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
363   opencascade::std::shared_ptr<std::ostream> aBinFile = aFileSystem->OpenOStream (myBinFileNameFull, std::ios::out | std::ios::binary);
364   if (aBinFile.get() == NULL
365    || !aBinFile->good())
366   {
367     Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created");
368     return false;
369   }
370
371   Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4);
372
373   Standard_Integer aNbAccessors = 0;
374
375   // write positions
376   myBuffViewPos.ByteOffset = aBinFile->tellp();
377   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
378        aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
379   {
380     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
381     if (theLabelFilter != NULL
382     && !theLabelFilter->Contains (aDocNode.Id))
383     {
384       continue;
385     }
386
387     // transformation will be stored at scene nodes
388     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
389     {
390       if (myBinDataMap.IsBound (aFaceIter.Face())
391        || toSkipFaceMesh (aFaceIter))
392       {
393         continue;
394       }
395
396       RWGltf_GltfFace aGltfFace;
397       saveNodes (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
398
399       if (!aBinFile->good())
400       {
401         Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
402         return false;
403       }
404
405       myBinDataMap.Bind (aFaceIter.Face(), aGltfFace);
406     }
407   }
408   myBuffViewPos.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewPos.ByteOffset;
409   if (!aPSentryBin.More())
410   {
411     return false;
412   }
413   aPSentryBin.Next();
414
415   // write normals
416   myBuffViewNorm.ByteOffset = aBinFile->tellp();
417   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
418        aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
419   {
420     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
421     if (theLabelFilter != NULL
422     && !theLabelFilter->Contains (aDocNode.Id))
423     {
424       continue;
425     }
426     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
427     {
428       if (toSkipFaceMesh (aFaceIter))
429       {
430         continue;
431       }
432
433       RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
434       if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
435       {
436         continue;
437       }
438
439       saveNormals (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
440
441       if (!aBinFile->good())
442       {
443         Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
444         return false;
445       }
446     }
447   }
448   myBuffViewNorm.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewNorm.ByteOffset;
449   if (!aPSentryBin.More())
450   {
451     return false;
452   }
453   aPSentryBin.Next();
454
455   // write texture coordinates
456   myBuffViewTextCoord.ByteOffset = aBinFile->tellp();
457   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
458        aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
459   {
460     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
461     if (theLabelFilter != NULL
462     && !theLabelFilter->Contains (aDocNode.Id))
463     {
464       continue;
465     }
466
467     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
468     {
469       if (toSkipFaceMesh (aFaceIter))
470       {
471         continue;
472       }
473
474       RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
475       if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
476       {
477         continue;
478       }
479
480       saveTextCoords (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
481
482       if (!aBinFile->good())
483       {
484         Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
485         return false;
486       }
487     }
488   }
489   myBuffViewTextCoord.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewTextCoord.ByteOffset;
490   if (!aPSentryBin.More())
491   {
492     return false;
493   }
494   aPSentryBin.Next();
495
496   // write indices
497   myBuffViewInd.ByteOffset = aBinFile->tellp();
498   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
499        aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
500   {
501     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
502     if (theLabelFilter != NULL
503     && !theLabelFilter->Contains (aDocNode.Id))
504     {
505       continue;
506     }
507
508     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
509     {
510       if (toSkipFaceMesh (aFaceIter))
511       {
512         continue;
513       }
514
515       RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
516       if (aGltfFace.Indices.Id != RWGltf_GltfAccessor::INVALID_ID)
517       {
518         continue;
519       }
520
521       saveIndices (aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
522
523       if (!aBinFile->good())
524       {
525         Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
526         return false;
527       }
528     }
529   }
530   myBuffViewInd.ByteLength = (int64_t )aBinFile->tellp() - myBuffViewInd.ByteOffset;
531
532   if (myIsBinary
533    && myToEmbedTexturesInGlb)
534   {
535     // save unique image textures
536     for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
537          aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
538     {
539       const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
540       if (theLabelFilter != NULL
541       && !theLabelFilter->Contains (aDocNode.Id))
542       {
543         continue;
544       }
545
546       for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
547            aFaceIter.More(); aFaceIter.Next())
548       {
549         if (toSkipFaceMesh (aFaceIter))
550         {
551           continue;
552         }
553
554         myMaterialMap->AddGlbImages (*aBinFile, aFaceIter.FaceStyle());
555       }
556     }
557   }
558
559   int aBuffViewId = 0;
560   if (myBuffViewPos.ByteLength > 0)
561   {
562     myBuffViewPos.Id = aBuffViewId++;
563   }
564   if (myBuffViewNorm.ByteLength > 0)
565   {
566     myBuffViewNorm.Id = aBuffViewId++;
567   }
568   if (myBuffViewTextCoord.ByteLength > 0)
569   {
570     myBuffViewTextCoord.Id = aBuffViewId++;
571   }
572   if (myBuffViewInd.ByteLength > 0)
573   {
574     myBuffViewInd.Id = aBuffViewId++;
575   }
576   // myMaterialMap->FlushGlbBufferViews() will put image bufferView's IDs at the end of list
577
578   myBinDataLen64 = aBinFile->tellp();
579   aBinFile->flush();
580   if (!aBinFile->good())
581   {
582     Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
583     return false;
584   }
585   aBinFile.reset();
586   return true;
587 }
588
589 //================================================================
590 // Function : writeJson
591 // Purpose  :
592 //================================================================
593 bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)&  theDocument,
594                                   const TDF_LabelSequence&         theRootLabels,
595                                   const TColStd_MapOfAsciiString*  theLabelFilter,
596                                   const TColStd_IndexedDataMapOfStringString& theFileInfo,
597                                   const Message_ProgressRange& theProgress)
598 {
599 #ifdef HAVE_RAPIDJSON
600   myWriter.reset();
601
602   // write vertex arrays
603   Message_ProgressScope aPSentryBin (theProgress, "Header data", 2);
604
605   const Standard_Integer aBinDataBufferId = 0;
606   const Standard_Integer aDefSceneId      = 0;
607
608   const TCollection_AsciiString aFileNameGltf = myFile;
609   const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
610   opencascade::std::shared_ptr<std::ostream> aGltfContentFile = aFileSystem->OpenOStream (aFileNameGltf, std::ios::out | std::ios::binary);
611   if (aGltfContentFile.get() == NULL
612    || !aGltfContentFile->good())
613   {
614     Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created");
615     return false;
616   }
617   if (myIsBinary)
618   {
619     const char* aMagic = "glTF";
620     uint32_t aVersion       = 2;
621     uint32_t aLength        = 0;
622     uint32_t aContentLength = 0;
623     uint32_t aContentType   = 0x4E4F534A;
624
625     aGltfContentFile->write (aMagic, 4);
626     aGltfContentFile->write ((const char* )&aVersion,       sizeof(aVersion));
627     aGltfContentFile->write ((const char* )&aLength,        sizeof(aLength));
628     aGltfContentFile->write ((const char* )&aContentLength, sizeof(aContentLength));
629     aGltfContentFile->write ((const char* )&aContentType,   sizeof(aContentType));
630   }
631
632   // Prepare an indexed map of scene nodes (without assemblies) in correct order.
633   // Note: this is also order of meshes in glTF document array.
634   RWGltf_GltfSceneNodeMap aSceneNodeMap;
635   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
636        aDocExplorer.More(); aDocExplorer.Next())
637   {
638     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
639     if (theLabelFilter != NULL
640     && !theLabelFilter->Contains (aDocNode.Id))
641     {
642       continue;
643     }
644
645     bool hasMeshData = false;
646     if (!aDocNode.IsAssembly)
647     {
648       for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
649       {
650         if (!toSkipFaceMesh (aFaceIter))
651         {
652           hasMeshData = true;
653           break;
654         }
655       }
656     }
657     if (hasMeshData)
658     {
659       aSceneNodeMap.Add (aDocNode);
660     }
661     else
662     {
663       // glTF disallows empty meshes / primitive arrays
664       const TCollection_AsciiString aNodeName = formatName (RWMesh_NameFormat_ProductOrInstance, aDocNode.Label, aDocNode.RefLabel);
665       Message::SendWarning (TCollection_AsciiString("RWGltf_CafWriter skipped node '") + aNodeName + "' without triangulation data");
666     }
667   }
668
669   rapidjson::OStreamWrapper aFileStream (*aGltfContentFile);
670   myWriter.reset (new RWGltf_GltfOStreamWriter (aFileStream));
671
672   myWriter->StartObject();
673
674   writeAccessors (aSceneNodeMap);
675   writeAnimations();
676   writeAsset (theFileInfo);
677   writeBufferViews (aBinDataBufferId);
678   writeBuffers();
679   writeExtensions();
680
681   writeImages    (aSceneNodeMap);
682   writeMaterials (aSceneNodeMap);
683   writeMeshes    (aSceneNodeMap);
684
685   aPSentryBin.Next();
686   if (!aPSentryBin.More())
687   {
688     return false;
689   }
690
691   // root nodes indices starting from 0
692   NCollection_Sequence<Standard_Integer> aSceneRootNodeInds;
693   writeNodes (theDocument, theRootLabels, theLabelFilter, aSceneNodeMap, aSceneRootNodeInds);
694   writeSamplers();
695   writeScene (aDefSceneId);
696   writeScenes (aSceneRootNodeInds);
697   writeSkins();
698   writeTextures (aSceneNodeMap);
699
700   myWriter->EndObject();
701
702   if (!myIsBinary)
703   {
704     aGltfContentFile->flush();
705     if (!aGltfContentFile->good())
706     {
707       Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
708       return false;
709     }
710     aGltfContentFile.reset();
711     return true;
712   }
713
714   int64_t aContentLen64 = (int64_t )aGltfContentFile->tellp() - 20;
715   while (aContentLen64 % 4 != 0)
716   {
717     aGltfContentFile->write (" ", 1);
718     ++aContentLen64;
719   }
720
721   const uint32_t aBinLength = (uint32_t )myBinDataLen64;
722   const uint32_t aBinType   = 0x004E4942;
723   aGltfContentFile->write ((const char*)&aBinLength, 4);
724   aGltfContentFile->write ((const char*)&aBinType,   4);
725
726   const int64_t aFullLen64 = aContentLen64 + 20 + myBinDataLen64 + 8;
727   if (aFullLen64 < std::numeric_limits<uint32_t>::max())
728   {
729     {
730       opencascade::std::shared_ptr<std::istream> aBinFile = aFileSystem->OpenIStream (myBinFileNameFull, std::ios::in | std::ios::binary);
731       if (aBinFile.get() == NULL || !aBinFile->good())
732       {
733         Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened");
734         return false;
735       }
736       char aBuffer[4096];
737       for (; aBinFile->good();)
738       {
739         aBinFile->read (aBuffer, 4096);
740         const Standard_Integer aReadLen = (Standard_Integer )aBinFile->gcount();
741         if (aReadLen == 0)
742         {
743           break;
744         }
745         aGltfContentFile->write (aBuffer, aReadLen);
746       }
747     }
748     OSD_Path aBinFilePath (myBinFileNameFull);
749     OSD_File (aBinFilePath).Remove();
750     if (OSD_File (aBinFilePath).Exists())
751     {
752       Message::SendFail (TCollection_AsciiString ("Unable to remove temporary glTF content file '") + myBinFileNameFull + "'");
753     }
754   }
755   else
756   {
757     Message::SendFail ("glTF file content is too big for binary format");
758     return false;
759   }
760
761   const uint32_t aLength        = (uint32_t )aFullLen64;
762   const uint32_t aContentLength = (uint32_t )aContentLen64;
763   aGltfContentFile->seekp (8);
764   aGltfContentFile->write ((const char* )&aLength,        4);
765   aGltfContentFile->write ((const char* )&aContentLength, 4);
766
767   aGltfContentFile->flush();
768   if (!aGltfContentFile->good())
769   {
770     Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
771     return false;
772   }
773   aGltfContentFile.reset();
774   myWriter.reset();
775   return true;
776 #else
777   (void )theDocument;
778   (void )theRootLabels;
779   (void )theLabelFilter;
780   (void )theFileInfo;
781   (void )theProgress;
782   Message::SendFail ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
783   return false;
784 #endif
785 }
786
787 // =======================================================================
788 // function : writeAccessors
789 // purpose  :
790 // =======================================================================
791 void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
792 {
793 #ifdef HAVE_RAPIDJSON
794   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
795
796   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
797   myWriter->StartArray();
798
799   NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aWrittenFaces;
800   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
801   {
802     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
803     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
804     {
805       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
806         || toSkipFaceMesh (aFaceIter))
807       {
808         continue;
809       }
810
811       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
812       writePositions (aGltfFace);
813     }
814   }
815   aWrittenFaces.Clear();
816   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
817   {
818     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
819     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
820     {
821       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
822         || toSkipFaceMesh (aFaceIter))
823       {
824         continue;
825       }
826
827       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
828       writeNormals (aGltfFace);
829     }
830   }
831   aWrittenFaces.Clear();
832   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
833   {
834     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
835     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
836     {
837       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
838         || toSkipFaceMesh (aFaceIter))
839       {
840         continue;
841       }
842
843       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
844       writeTextCoords (aGltfFace);
845     }
846   }
847   aWrittenFaces.Clear();
848   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
849   {
850     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
851     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
852     {
853       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
854         || toSkipFaceMesh (aFaceIter))
855       {
856         continue;
857       }
858
859       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
860       writeIndices (aGltfFace);
861     }
862   }
863
864   myWriter->EndArray();
865 #else
866   (void )theSceneNodeMap;
867 #endif
868 }
869
870 // =======================================================================
871 // function : writePositions
872 // purpose  :
873 // =======================================================================
874 void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
875 {
876 #ifdef HAVE_RAPIDJSON
877   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writePositions()");
878   if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
879   {
880     return;
881   }
882
883   myWriter->StartObject();
884   myWriter->Key    ("bufferView");
885   myWriter->Int    (myBuffViewPos.Id);
886   myWriter->Key    ("byteOffset");
887   myWriter->Int64  (theGltfFace.NodePos.ByteOffset);
888   myWriter->Key    ("componentType");
889   myWriter->Int    (theGltfFace.NodePos.ComponentType);
890   myWriter->Key    ("count");
891   myWriter->Int64  (theGltfFace.NodePos.Count);
892
893   if (theGltfFace.NodePos.BndBox.IsValid())
894   {
895     myWriter->Key ("max");
896     myWriter->StartArray();
897     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().x());
898     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().y());
899     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().z());
900     myWriter->EndArray();
901
902     myWriter->Key("min");
903     myWriter->StartArray();
904     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().x());
905     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().y());
906     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().z());
907     myWriter->EndArray();
908   }
909   myWriter->Key    ("type");
910   myWriter->String ("VEC3");
911
912   myWriter->EndObject();
913 #else
914   (void )theGltfFace;
915 #endif
916 }
917
918 // =======================================================================
919 // function : writeNormals
920 // purpose  :
921 // =======================================================================
922 void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
923 {
924 #ifdef HAVE_RAPIDJSON
925   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNormals()");
926   if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
927   {
928     return;
929   }
930
931   myWriter->StartObject();
932   myWriter->Key    ("bufferView");
933   myWriter->Int    (myBuffViewNorm.Id);
934   myWriter->Key    ("byteOffset");
935   myWriter->Int64  (theGltfFace.NodeNorm.ByteOffset);
936   myWriter->Key    ("componentType");
937   myWriter->Int    (theGltfFace.NodeNorm.ComponentType);
938   myWriter->Key    ("count");
939   myWriter->Int64  (theGltfFace.NodeNorm.Count);
940   // min/max values are optional, and not very useful for normals - skip them
941   /*{
942     myWriter->Key ("max");
943     myWriter->StartArray();
944     myWriter->Double (1.0);
945     myWriter->Double (1.0);
946     myWriter->Double (1.0);
947     myWriter->EndArray();
948   }
949   {
950     myWriter->Key ("min");
951     myWriter->StartArray();
952     myWriter->Double (0.0);
953     myWriter->Double (0.0);
954     myWriter->Double (0.0);
955     myWriter->EndArray();
956   }*/
957   myWriter->Key    ("type");
958   myWriter->String ("VEC3");
959
960   myWriter->EndObject();
961 #else
962   (void )theGltfFace;
963 #endif
964 }
965
966 // =======================================================================
967 // function : writeTextCoords
968 // purpose  :
969 // =======================================================================
970 void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
971 {
972 #ifdef HAVE_RAPIDJSON
973   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextCoords()");
974   if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
975   {
976     return;
977   }
978
979   myWriter->StartObject();
980   myWriter->Key    ("bufferView");
981   myWriter->Int    (myBuffViewTextCoord.Id);
982   myWriter->Key    ("byteOffset");
983   myWriter->Int64  (theGltfFace.NodeUV.ByteOffset);
984   myWriter->Key    ("componentType");
985   myWriter->Int    (theGltfFace.NodeUV.ComponentType);
986   myWriter->Key    ("count");
987   myWriter->Int64  (theGltfFace.NodeUV.Count);
988   // min/max values are optional, and not very useful for UV coordinates - skip them
989   /*{
990     myWriter->Key ("max");
991     myWriter->StartArray();
992     myWriter->Double (1.0);
993     myWriter->Double (1.0);
994     myWriter->Double (1.0);
995     myWriter->EndArray();
996   }
997   {
998     myWriter->Key ("min");
999     myWriter->StartArray();
1000     myWriter->Double (0.0);
1001     myWriter->Double (0.0);
1002     myWriter->Double (0.0);
1003     myWriter->EndArray();
1004   }*/
1005   myWriter->Key    ("type");
1006   myWriter->String ("VEC2");
1007
1008   myWriter->EndObject();
1009 #else
1010   (void )theGltfFace;
1011 #endif
1012 }
1013
1014 // =======================================================================
1015 // function : writeIndices
1016 // purpose  :
1017 // =======================================================================
1018 void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
1019 {
1020 #ifdef HAVE_RAPIDJSON
1021   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeIndices()");
1022   if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
1023   {
1024     return;
1025   }
1026
1027   myWriter->StartObject();
1028   myWriter->Key    ("bufferView");
1029   myWriter->Int    (myBuffViewInd.Id);
1030   myWriter->Key    ("byteOffset");
1031   myWriter->Int64  (theGltfFace.Indices.ByteOffset);
1032   myWriter->Key    ("componentType");
1033   myWriter->Int    (theGltfFace.Indices.ComponentType);
1034   myWriter->Key    ("count");
1035   myWriter->Int64  (theGltfFace.Indices.Count);
1036
1037   myWriter->Key    ("type");
1038   myWriter->String ("SCALAR");
1039
1040   myWriter->EndObject();
1041 #else
1042   (void )theGltfFace;
1043 #endif
1044 }
1045
1046 // =======================================================================
1047 // function : writeAnimations
1048 // purpose  :
1049 // =======================================================================
1050 void RWGltf_CafWriter::writeAnimations()
1051 {
1052   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAnimations()");
1053
1054   // This section should be skipped if it doesn't contain any information but not be empty
1055   //myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Animations));
1056   //myWriter->StartArray();
1057   //myWriter->EndArray();
1058 }
1059
1060 // =======================================================================
1061 // function : writeAsset
1062 // purpose  :
1063 // =======================================================================
1064 void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo)
1065 {
1066 #ifdef HAVE_RAPIDJSON
1067   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAsset()");
1068
1069   myWriter->Key    (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Asset));
1070   myWriter->StartObject();
1071   myWriter->Key    ("generator");
1072   myWriter->String ("Open CASCADE Technology [dev.opencascade.org]");
1073   myWriter->Key    ("version");
1074   myWriter->String ("2.0"); // glTF format version
1075
1076   bool anIsStarted = false;
1077   for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
1078   {
1079     if (!anIsStarted)
1080     {
1081       myWriter->Key ("extras");
1082       myWriter->StartObject();
1083       anIsStarted = true;
1084     }
1085     myWriter->Key (aKeyValueIter.Key().ToCString());
1086     myWriter->String (aKeyValueIter.Value().ToCString());
1087   }
1088   if (anIsStarted)
1089   {
1090     myWriter->EndObject();
1091   }
1092
1093   myWriter->EndObject();
1094 #else
1095   (void )theFileInfo;
1096 #endif
1097 }
1098
1099 // =======================================================================
1100 // function : writeBufferViews
1101 // purpose  :
1102 // =======================================================================
1103 void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBufferId)
1104 {
1105 #ifdef HAVE_RAPIDJSON
1106   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
1107
1108   int aBuffViewId = 0;
1109   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
1110   myWriter->StartArray();
1111   if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
1112   {
1113     aBuffViewId++;
1114     myWriter->StartObject();
1115     myWriter->Key    ("buffer");
1116     myWriter->Int    (theBinDataBufferId);
1117     myWriter->Key    ("byteLength");
1118     myWriter->Int64  (myBuffViewPos.ByteLength);
1119     myWriter->Key    ("byteOffset");
1120     myWriter->Int64  (myBuffViewPos.ByteOffset);
1121     myWriter->Key    ("byteStride");
1122     myWriter->Int64  (myBuffViewPos.ByteStride);
1123     myWriter->Key    ("target");
1124     myWriter->Int    (myBuffViewPos.Target);
1125     myWriter->EndObject();
1126   }
1127   if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1128   {
1129     aBuffViewId++;
1130     myWriter->StartObject();
1131     myWriter->Key    ("buffer");
1132     myWriter->Int    (theBinDataBufferId);
1133     myWriter->Key    ("byteLength");
1134     myWriter->Int64  (myBuffViewNorm.ByteLength);
1135     myWriter->Key    ("byteOffset");
1136     myWriter->Int64  (myBuffViewNorm.ByteOffset);
1137     myWriter->Key    ("byteStride");
1138     myWriter->Int64  (myBuffViewNorm.ByteStride);
1139     myWriter->Key    ("target");
1140     myWriter->Int    (myBuffViewNorm.Target);
1141     myWriter->EndObject();
1142   }
1143   if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
1144   {
1145     aBuffViewId++;
1146     myWriter->StartObject();
1147     myWriter->Key    ("buffer");
1148     myWriter->Int    (theBinDataBufferId);
1149     myWriter->Key    ("byteLength");
1150     myWriter->Int64  (myBuffViewTextCoord.ByteLength);
1151     myWriter->Key    ("byteOffset");
1152     myWriter->Int64  (myBuffViewTextCoord.ByteOffset);
1153     myWriter->Key    ("byteStride");
1154     myWriter->Int64  (myBuffViewTextCoord.ByteStride);
1155     myWriter->Key    ("target");
1156     myWriter->Int    (myBuffViewTextCoord.Target);
1157     myWriter->EndObject();
1158   }
1159   if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
1160   {
1161     aBuffViewId++;
1162     myWriter->StartObject();
1163     myWriter->Key    ("buffer");
1164     myWriter->Int    (theBinDataBufferId);
1165     myWriter->Key    ("byteLength");
1166     myWriter->Int64  (myBuffViewInd.ByteLength);
1167     myWriter->Key    ("byteOffset");
1168     myWriter->Int64  (myBuffViewInd.ByteOffset);
1169     myWriter->Key    ("target");
1170     myWriter->Int    (myBuffViewInd.Target);
1171     myWriter->EndObject();
1172   }
1173
1174   myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
1175
1176   myWriter->EndArray();
1177 #else
1178   (void )theBinDataBufferId;
1179 #endif
1180 }
1181
1182 // =======================================================================
1183 // function : writeBuffers
1184 // purpose  :
1185 // =======================================================================
1186 void RWGltf_CafWriter::writeBuffers()
1187 {
1188 #ifdef HAVE_RAPIDJSON
1189   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBuffers()");
1190
1191   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Buffers));
1192   myWriter->StartArray();
1193   {
1194     myWriter->StartObject();
1195     {
1196       myWriter->Key   ("byteLength");
1197       myWriter->Int64 (myBinDataLen64);
1198       if (!myIsBinary)
1199       {
1200         myWriter->Key   ("uri");
1201         myWriter->String (myBinFileNameShort.ToCString());
1202       }
1203     }
1204     myWriter->EndObject();
1205   }
1206   myWriter->EndArray();
1207 #endif
1208 }
1209
1210 // =======================================================================
1211 // function : writeExtensions
1212 // purpose  :
1213 // =======================================================================
1214 void RWGltf_CafWriter::writeExtensions()
1215 {
1216   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
1217 }
1218
1219 // =======================================================================
1220 // function : writeImages
1221 // purpose  :
1222 // =======================================================================
1223 void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1224 {
1225 #ifdef HAVE_RAPIDJSON
1226   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
1227
1228   // empty RWGltf_GltfRootElement_Images section should NOT be written to avoid validator errors
1229   if (myIsBinary
1230    && myToEmbedTexturesInGlb)
1231   {
1232     myMaterialMap->FlushGlbImages (myWriter.get());
1233   }
1234   else
1235   {
1236     bool anIsStarted = false;
1237     for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1238     {
1239       const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1240       for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1241       {
1242         myMaterialMap->AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1243       }
1244     }
1245     if (anIsStarted)
1246     {
1247       myWriter->EndArray();
1248     }
1249   }
1250 #else
1251   (void )theSceneNodeMap;
1252 #endif
1253 }
1254
1255 // =======================================================================
1256 // function : writeMaterials
1257 // purpose  :
1258 // =======================================================================
1259 void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1260 {
1261 #ifdef HAVE_RAPIDJSON
1262   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
1263
1264   // empty RWGltf_GltfRootElement_Materials section should NOT be written to avoid validator errors
1265   bool anIsStarted = false;
1266   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1267   {
1268     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1269     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1270     {
1271       myMaterialMap->AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1272     }
1273   }
1274   if (anIsStarted)
1275   {
1276     myWriter->EndArray();
1277   }
1278 #else
1279   (void )theSceneNodeMap;
1280 #endif
1281 }
1282
1283 // =======================================================================
1284 // function : writeMeshes
1285 // purpose  :
1286 // =======================================================================
1287 void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1288 {
1289 #ifdef HAVE_RAPIDJSON
1290   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
1291
1292   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
1293   myWriter->StartArray();
1294
1295   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1296   {
1297     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1298     const TCollection_AsciiString aNodeName = formatName (myMeshNameFormat, aDocNode.Label, aDocNode.RefLabel);
1299
1300     bool toStartPrims = true;
1301     Standard_Integer aNbFacesInNode = 0;
1302     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
1303     {
1304       if (toSkipFaceMesh (aFaceIter))
1305       {
1306         continue;
1307       }
1308
1309       if (toStartPrims)
1310       {
1311         toStartPrims = false;
1312         myWriter->StartObject();
1313         if (!aNodeName.IsEmpty())
1314         {
1315           myWriter->Key ("name");
1316           myWriter->String (aNodeName.ToCString());
1317         }
1318         myWriter->Key ("primitives");
1319         myWriter->StartArray();
1320       }
1321
1322       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
1323       const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (aFaceIter.FaceStyle());
1324       myWriter->StartObject();
1325       {
1326         myWriter->Key ("attributes");
1327         myWriter->StartObject();
1328         {
1329           if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1330           {
1331             myWriter->Key ("NORMAL");
1332             myWriter->Int (aGltfFace.NodeNorm.Id);
1333           }
1334           myWriter->Key ("POSITION");
1335           myWriter->Int (aGltfFace.NodePos.Id);
1336           if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1337           {
1338             myWriter->Key ("TEXCOORD_0");
1339             myWriter->Int (aGltfFace.NodeUV.Id);
1340           }
1341         }
1342         myWriter->EndObject();
1343
1344         myWriter->Key ("indices");
1345         myWriter->Int (aGltfFace.Indices.Id);
1346         if (!aMatId.IsEmpty())
1347         {
1348           myWriter->Key ("material");
1349           myWriter->Int (aMatId.IntegerValue());
1350         }
1351         myWriter->Key ("mode");
1352         myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
1353       }
1354       myWriter->EndObject();
1355     }
1356
1357     if (!toStartPrims)
1358     {
1359       myWriter->EndArray();
1360       myWriter->EndObject();
1361     }
1362   }
1363   myWriter->EndArray();
1364 #else
1365   (void )theSceneNodeMap;
1366 #endif
1367 }
1368
1369 // =======================================================================
1370 // function : writeNodes
1371 // purpose  :
1372 // =======================================================================
1373 void RWGltf_CafWriter::writeNodes (const Handle(TDocStd_Document)&  theDocument,
1374                                    const TDF_LabelSequence&         theRootLabels,
1375                                    const TColStd_MapOfAsciiString*  theLabelFilter,
1376                                    const RWGltf_GltfSceneNodeMap&   theSceneNodeMap,
1377                                    NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1378 {
1379 #ifdef HAVE_RAPIDJSON
1380   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
1381
1382   // Prepare full indexed map of scene nodes in correct order.
1383   RWGltf_GltfSceneNodeMap aSceneNodeMapWithChildren; // indexes starting from 1
1384   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_None);
1385        aDocExplorer.More(); aDocExplorer.Next())
1386   {
1387     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
1388     if (theLabelFilter != NULL
1389     && !theLabelFilter->Contains (aDocNode.Id))
1390     {
1391       continue;
1392     }
1393
1394     // keep empty nodes
1395     //RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), false);
1396     //if (!aFaceIter.More()) { continue; }
1397
1398     Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
1399     if (aDocExplorer.CurrentDepth() == 0)
1400     {
1401       // save root node index (starting from 0 not 1)
1402       theSceneRootNodeInds.Append (aNodeIndex - 1);
1403     }
1404   }
1405
1406   // Write scene nodes using prepared map for correct order of array members
1407   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
1408   myWriter->StartArray();
1409
1410   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
1411   {
1412     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1413
1414     myWriter->StartObject();
1415     {
1416       if (aDocNode.IsAssembly)
1417       {
1418         myWriter->Key ("children");
1419         myWriter->StartArray();
1420         {
1421           for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
1422           {
1423             const TDF_Label& aChildLabel = aChildIter.Value();
1424             if (aChildLabel.IsNull())
1425             {
1426               continue;
1427             }
1428
1429             const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
1430             Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
1431             if (aChildIdx > 0)
1432             {
1433               myWriter->Int (aChildIdx - 1);
1434             }
1435           }
1436         }
1437         myWriter->EndArray();
1438       }
1439     }
1440     if (!aDocNode.LocalTrsf.IsIdentity())
1441     {
1442       gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
1443       if (aTrsf.Form() != gp_Identity)
1444       {
1445         myCSTrsf.TransformTransformation (aTrsf);
1446         const gp_Quaternion aQuaternion = aTrsf.GetRotation();
1447         const bool hasRotation = Abs (aQuaternion.X())       > gp::Resolution()
1448                               || Abs (aQuaternion.Y())       > gp::Resolution()
1449                               || Abs (aQuaternion.Z())       > gp::Resolution()
1450                               || Abs (aQuaternion.W() - 1.0) > gp::Resolution();
1451         const Standard_Real aScaleFactor = aTrsf.ScaleFactor();
1452         const bool hasScale = Abs (aScaleFactor - 1.0) > Precision::Confusion();
1453         const gp_XYZ& aTranslPart = aTrsf.TranslationPart();
1454         const bool hasTranslation = aTranslPart.SquareModulus() > gp::Resolution();
1455
1456         RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
1457         if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
1458         {
1459           aTrsfFormat = hasRotation && hasScale && hasTranslation
1460                       ? RWGltf_WriterTrsfFormat_Mat4
1461                       : RWGltf_WriterTrsfFormat_TRS;
1462         }
1463
1464         if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
1465         {
1466           // write full matrix
1467           Graphic3d_Mat4 aMat4;
1468           aTrsf.GetMat4 (aMat4);
1469           if (!aMat4.IsIdentity())
1470           {
1471             myWriter->Key ("matrix");
1472             myWriter->StartArray();
1473             for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
1474             {
1475               for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
1476               {
1477                 myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
1478               }
1479             }
1480             myWriter->EndArray();
1481           }
1482         }
1483         else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
1484         {
1485           if (hasRotation)
1486           {
1487             myWriter->Key ("rotation");
1488             myWriter->StartArray();
1489             myWriter->Double (aQuaternion.X());
1490             myWriter->Double (aQuaternion.Y());
1491             myWriter->Double (aQuaternion.Z());
1492             myWriter->Double (aQuaternion.W());
1493             myWriter->EndArray();
1494           }
1495           if (hasScale)
1496           {
1497             myWriter->Key ("scale");
1498             myWriter->StartArray();
1499             myWriter->Double (aScaleFactor);
1500             myWriter->Double (aScaleFactor);
1501             myWriter->Double (aScaleFactor);
1502             myWriter->EndArray();
1503           }
1504           if (hasTranslation)
1505           {
1506             myWriter->Key ("translation");
1507             myWriter->StartArray();
1508             myWriter->Double (aTranslPart.X());
1509             myWriter->Double (aTranslPart.Y());
1510             myWriter->Double (aTranslPart.Z());
1511             myWriter->EndArray();
1512           }
1513         }
1514       }
1515     }
1516     if (!aDocNode.IsAssembly)
1517     {
1518       // Mesh order of current node is equal to order of this node in scene nodes map
1519       Standard_Integer aMeshIdx = theSceneNodeMap.FindIndex (aDocNode.Id);
1520       if (aMeshIdx > 0)
1521       {
1522         myWriter->Key ("mesh");
1523         myWriter->Int (aMeshIdx - 1);
1524       }
1525     }
1526     {
1527       const TCollection_AsciiString aNodeName = formatName (myNodeNameFormat, aDocNode.Label, aDocNode.RefLabel);
1528       if (!aNodeName.IsEmpty())
1529       {
1530         myWriter->Key ("name");
1531         myWriter->String (aNodeName.ToCString());
1532       }
1533     }
1534     myWriter->EndObject();
1535   }
1536   myWriter->EndArray();
1537 #else
1538   (void )theDocument;
1539   (void )theRootLabels;
1540   (void )theLabelFilter;
1541   (void )theSceneNodeMap;
1542   (void )theSceneRootNodeInds;
1543 #endif
1544 }
1545
1546 // =======================================================================
1547 // function : writeSamplers
1548 // purpose  :
1549 // =======================================================================
1550 void RWGltf_CafWriter::writeSamplers()
1551 {
1552 #ifdef HAVE_RAPIDJSON
1553   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
1554   if (myMaterialMap->NbImages() == 0)
1555   {
1556     return;
1557   }
1558
1559   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
1560   myWriter->StartArray();
1561   {
1562     myWriter->StartObject();
1563     {
1564       //myWriter->Key ("magFilter");
1565       //myWriter->Int (9729);
1566       //myWriter->Key ("minFilter");
1567       //myWriter->Int (9729);
1568     }
1569     myWriter->EndObject();
1570   }
1571   myWriter->EndArray();
1572 #endif
1573 }
1574
1575 // =======================================================================
1576 // function : writeScene
1577 // purpose  :
1578 // =======================================================================
1579 void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
1580 {
1581 #ifdef HAVE_RAPIDJSON
1582   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
1583
1584   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
1585   myWriter->Int (theDefSceneId);
1586 #else
1587   (void )theDefSceneId;
1588 #endif
1589 }
1590
1591 // =======================================================================
1592 // function : writeScenes
1593 // purpose  :
1594 // =======================================================================
1595 void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1596 {
1597 #ifdef HAVE_RAPIDJSON
1598   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
1599
1600   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
1601   myWriter->StartArray();
1602   {
1603     myWriter->StartObject();
1604     myWriter->Key ("nodes");
1605     myWriter->StartArray();
1606     for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
1607     {
1608       myWriter->Int (aRootIter.Value());
1609     }
1610     myWriter->EndArray();
1611     myWriter->EndObject();
1612   }
1613   myWriter->EndArray();
1614 #else
1615   (void )theSceneRootNodeInds;
1616 #endif
1617 }
1618
1619 // =======================================================================
1620 // function : writeSkins
1621 // purpose  :
1622 // =======================================================================
1623 void RWGltf_CafWriter::writeSkins()
1624 {
1625   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
1626
1627   // This section should be skipped if it doesn't contain any information but not be empty
1628   /*myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Skins));
1629   myWriter->StartArray();
1630   myWriter->EndArray();*/
1631 }
1632
1633 // =======================================================================
1634 // function : writeTextures
1635 // purpose  :
1636 // =======================================================================
1637 void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1638 {
1639 #ifdef HAVE_RAPIDJSON
1640   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
1641
1642   // empty RWGltf_GltfRootElement_Textures section should not be written to avoid validator errors
1643   bool anIsStarted = false;
1644   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1645   {
1646     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1647     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1648     {
1649       myMaterialMap->AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1650     }
1651   }
1652   if (anIsStarted)
1653   {
1654     myWriter->EndArray();
1655   }
1656 #else
1657  (void )theSceneNodeMap;
1658 #endif
1659 }