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