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