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