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