aad1acd9ccc55feeb9786209bae6034257202b59
[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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created");
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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
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::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created");
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::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
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::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened");
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::SendFail (TCollection_AsciiString ("Unable to remove temporary glTF content file '") + myBinFileNameFull + "'");
693     }
694   }
695   else
696   {
697     Message::SendFail ("glTF file content is too big for binary format");
698     return false;
699   }
700
701   const uint32_t aLength        = (uint32_t )aFullLen64;
702   const uint32_t aContentLength = (uint32_t )aContentLen64;
703   aGltfContentFile.seekp (8);
704   aGltfContentFile.write ((const char* )&aLength,        4);
705   aGltfContentFile.write ((const char* )&aContentLength, 4);
706
707   aGltfContentFile.close();
708   if (!aGltfContentFile.good())
709   {
710     Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
711     return false;
712   }
713
714   myWriter.reset();
715   return true;
716 #else
717   (void )theDocument;
718   (void )theRootLabels;
719   (void )theLabelFilter;
720   (void )theFileInfo;
721   (void )theProgress;
722   Message::SendFail ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
723   return false;
724 #endif
725 }
726
727 // =======================================================================
728 // function : writeAccessors
729 // purpose  :
730 // =======================================================================
731 void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
732 {
733 #ifdef HAVE_RAPIDJSON
734   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
735
736   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
737   myWriter->StartArray();
738
739   NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aWrittenFaces;
740   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
741   {
742     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
743     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
744     {
745       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
746         || toSkipFaceMesh (aFaceIter))
747       {
748         continue;
749       }
750
751       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
752       writePositions (aGltfFace);
753     }
754   }
755   aWrittenFaces.Clear();
756   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
757   {
758     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
759     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
760     {
761       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
762         || toSkipFaceMesh (aFaceIter))
763       {
764         continue;
765       }
766
767       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
768       writeNormals (aGltfFace);
769     }
770   }
771   aWrittenFaces.Clear();
772   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
773   {
774     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
775     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
776     {
777       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
778         || toSkipFaceMesh (aFaceIter))
779       {
780         continue;
781       }
782
783       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
784       writeTextCoords (aGltfFace);
785     }
786   }
787   aWrittenFaces.Clear();
788   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
789   {
790     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
791     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
792     {
793       if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
794         || toSkipFaceMesh (aFaceIter))
795       {
796         continue;
797       }
798
799       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
800       writeIndices (aGltfFace);
801     }
802   }
803
804   myWriter->EndArray();
805 #else
806   (void )theSceneNodeMap;
807 #endif
808 }
809
810 // =======================================================================
811 // function : writePositions
812 // purpose  :
813 // =======================================================================
814 void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
815 {
816 #ifdef HAVE_RAPIDJSON
817   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writePositions()");
818   if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
819   {
820     return;
821   }
822
823   myWriter->StartObject();
824   myWriter->Key    ("bufferView");
825   myWriter->Int    (myBuffViewPos.Id);
826   myWriter->Key    ("byteOffset");
827   myWriter->Int64  (theGltfFace.NodePos.ByteOffset);
828   myWriter->Key    ("componentType");
829   myWriter->Int    (theGltfFace.NodePos.ComponentType);
830   myWriter->Key    ("count");
831   myWriter->Int64  (theGltfFace.NodePos.Count);
832
833   if (theGltfFace.NodePos.BndBox.IsValid())
834   {
835     myWriter->Key ("max");
836     myWriter->StartArray();
837     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().x());
838     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().y());
839     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().z());
840     myWriter->EndArray();
841
842     myWriter->Key("min");
843     myWriter->StartArray();
844     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().x());
845     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().y());
846     myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().z());
847     myWriter->EndArray();
848   }
849   myWriter->Key    ("type");
850   myWriter->String ("VEC3");
851
852   myWriter->EndObject();
853 #else
854   (void )theGltfFace;
855 #endif
856 }
857
858 // =======================================================================
859 // function : writeNormals
860 // purpose  :
861 // =======================================================================
862 void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
863 {
864 #ifdef HAVE_RAPIDJSON
865   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNormals()");
866   if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
867   {
868     return;
869   }
870
871   myWriter->StartObject();
872   myWriter->Key    ("bufferView");
873   myWriter->Int    (myBuffViewNorm.Id);
874   myWriter->Key    ("byteOffset");
875   myWriter->Int64  (theGltfFace.NodeNorm.ByteOffset);
876   myWriter->Key    ("componentType");
877   myWriter->Int    (theGltfFace.NodeNorm.ComponentType);
878   myWriter->Key    ("count");
879   myWriter->Int64  (theGltfFace.NodeNorm.Count);
880   // min/max values are optional, and not very useful for normals - skip them
881   /*{
882     myWriter->Key ("max");
883     myWriter->StartArray();
884     myWriter->Double (1.0);
885     myWriter->Double (1.0);
886     myWriter->Double (1.0);
887     myWriter->EndArray();
888   }
889   {
890     myWriter->Key ("min");
891     myWriter->StartArray();
892     myWriter->Double (0.0);
893     myWriter->Double (0.0);
894     myWriter->Double (0.0);
895     myWriter->EndArray();
896   }*/
897   myWriter->Key    ("type");
898   myWriter->String ("VEC3");
899
900   myWriter->EndObject();
901 #else
902   (void )theGltfFace;
903 #endif
904 }
905
906 // =======================================================================
907 // function : writeTextCoords
908 // purpose  :
909 // =======================================================================
910 void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
911 {
912 #ifdef HAVE_RAPIDJSON
913   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextCoords()");
914   if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
915   {
916     return;
917   }
918
919   myWriter->StartObject();
920   myWriter->Key    ("bufferView");
921   myWriter->Int    (myBuffViewTextCoord.Id);
922   myWriter->Key    ("byteOffset");
923   myWriter->Int64  (theGltfFace.NodeUV.ByteOffset);
924   myWriter->Key    ("componentType");
925   myWriter->Int    (theGltfFace.NodeUV.ComponentType);
926   myWriter->Key    ("count");
927   myWriter->Int64  (theGltfFace.NodeUV.Count);
928   // min/max values are optional, and not very useful for UV coordinates - skip them
929   /*{
930     myWriter->Key ("max");
931     myWriter->StartArray();
932     myWriter->Double (1.0);
933     myWriter->Double (1.0);
934     myWriter->Double (1.0);
935     myWriter->EndArray();
936   }
937   {
938     myWriter->Key ("min");
939     myWriter->StartArray();
940     myWriter->Double (0.0);
941     myWriter->Double (0.0);
942     myWriter->Double (0.0);
943     myWriter->EndArray();
944   }*/
945   myWriter->Key    ("type");
946   myWriter->String ("VEC2");
947
948   myWriter->EndObject();
949 #else
950   (void )theGltfFace;
951 #endif
952 }
953
954 // =======================================================================
955 // function : writeIndices
956 // purpose  :
957 // =======================================================================
958 void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
959 {
960 #ifdef HAVE_RAPIDJSON
961   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeIndices()");
962   if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
963   {
964     return;
965   }
966
967   myWriter->StartObject();
968   myWriter->Key    ("bufferView");
969   myWriter->Int    (myBuffViewInd.Id);
970   myWriter->Key    ("byteOffset");
971   myWriter->Int64  (theGltfFace.Indices.ByteOffset);
972   myWriter->Key    ("componentType");
973   myWriter->Int    (theGltfFace.Indices.ComponentType);
974   myWriter->Key    ("count");
975   myWriter->Int64  (theGltfFace.Indices.Count);
976
977   myWriter->Key    ("type");
978   myWriter->String ("SCALAR");
979
980   myWriter->EndObject();
981 #else
982   (void )theGltfFace;
983 #endif
984 }
985
986 // =======================================================================
987 // function : writeAnimations
988 // purpose  :
989 // =======================================================================
990 void RWGltf_CafWriter::writeAnimations()
991 {
992   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAnimations()");
993
994   // This section should be skipped if it doesn't contain any information but not be empty
995   //myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Animations));
996   //myWriter->StartArray();
997   //myWriter->EndArray();
998 }
999
1000 // =======================================================================
1001 // function : writeAsset
1002 // purpose  :
1003 // =======================================================================
1004 void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo)
1005 {
1006 #ifdef HAVE_RAPIDJSON
1007   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAsset()");
1008
1009   myWriter->Key    (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Asset));
1010   myWriter->StartObject();
1011   myWriter->Key    ("generator");
1012   myWriter->String ("Open CASCADE Technology [www.opencascade.com]");
1013   myWriter->Key    ("version");
1014   myWriter->String ("2.0"); // glTF format version
1015
1016   bool anIsStarted = false;
1017   for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
1018   {
1019     if (!anIsStarted)
1020     {
1021       myWriter->Key ("extras");
1022       myWriter->StartObject();
1023       anIsStarted = true;
1024     }
1025     myWriter->Key (aKeyValueIter.Key().ToCString());
1026     myWriter->String (aKeyValueIter.Value().ToCString());
1027   }
1028   if (anIsStarted)
1029   {
1030     myWriter->EndObject();
1031   }
1032
1033   myWriter->EndObject();
1034 #else
1035   (void )theFileInfo;
1036 #endif
1037 }
1038
1039 // =======================================================================
1040 // function : writeBufferViews
1041 // purpose  :
1042 // =======================================================================
1043 void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBufferId)
1044 {
1045 #ifdef HAVE_RAPIDJSON
1046   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
1047
1048   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
1049   myWriter->StartArray();
1050   if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
1051   {
1052     myWriter->StartObject();
1053     myWriter->Key    ("buffer");
1054     myWriter->Int    (theBinDataBufferId);
1055     myWriter->Key    ("byteLength");
1056     myWriter->Int64  (myBuffViewPos.ByteLength);
1057     myWriter->Key    ("byteOffset");
1058     myWriter->Int64  (myBuffViewPos.ByteOffset);
1059     myWriter->Key    ("byteStride");
1060     myWriter->Int64  (myBuffViewPos.ByteStride);
1061     myWriter->Key    ("target");
1062     myWriter->Int    (myBuffViewPos.Target);
1063     myWriter->EndObject();
1064   }
1065   if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1066   {
1067     myWriter->StartObject();
1068     myWriter->Key    ("buffer");
1069     myWriter->Int    (theBinDataBufferId);
1070     myWriter->Key    ("byteLength");
1071     myWriter->Int64  (myBuffViewNorm.ByteLength);
1072     myWriter->Key    ("byteOffset");
1073     myWriter->Int64  (myBuffViewNorm.ByteOffset);
1074     myWriter->Key    ("byteStride");
1075     myWriter->Int64  (myBuffViewNorm.ByteStride);
1076     myWriter->Key    ("target");
1077     myWriter->Int    (myBuffViewNorm.Target);
1078     myWriter->EndObject();
1079   }
1080   if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
1081   {
1082     myWriter->StartObject();
1083     myWriter->Key    ("buffer");
1084     myWriter->Int    (theBinDataBufferId);
1085     myWriter->Key    ("byteLength");
1086     myWriter->Int64  (myBuffViewTextCoord.ByteLength);
1087     myWriter->Key    ("byteOffset");
1088     myWriter->Int64  (myBuffViewTextCoord.ByteOffset);
1089     myWriter->Key    ("byteStride");
1090     myWriter->Int64  (myBuffViewTextCoord.ByteStride);
1091     myWriter->Key    ("target");
1092     myWriter->Int    (myBuffViewTextCoord.Target);
1093     myWriter->EndObject();
1094   }
1095   if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
1096   {
1097     myWriter->StartObject();
1098     myWriter->Key    ("buffer");
1099     myWriter->Int    (theBinDataBufferId);
1100     myWriter->Key    ("byteLength");
1101     myWriter->Int64  (myBuffViewInd.ByteLength);
1102     myWriter->Key    ("byteOffset");
1103     myWriter->Int64  (myBuffViewInd.ByteOffset);
1104     myWriter->Key    ("target");
1105     myWriter->Int    (myBuffViewInd.Target);
1106     myWriter->EndObject();
1107   }
1108   myWriter->EndArray();
1109 #else
1110   (void )theBinDataBufferId;
1111 #endif
1112 }
1113
1114 // =======================================================================
1115 // function : writeBuffers
1116 // purpose  :
1117 // =======================================================================
1118 void RWGltf_CafWriter::writeBuffers()
1119 {
1120 #ifdef HAVE_RAPIDJSON
1121   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBuffers()");
1122
1123   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Buffers));
1124   myWriter->StartArray();
1125   {
1126     myWriter->StartObject();
1127     {
1128       myWriter->Key   ("byteLength");
1129       myWriter->Int64 (myBuffViewPos.ByteLength + myBuffViewNorm.ByteLength +
1130                        myBuffViewTextCoord.ByteLength + myBuffViewInd.ByteLength);
1131       if (!myIsBinary)
1132       {
1133         myWriter->Key   ("uri");
1134         myWriter->String (myBinFileNameShort.ToCString());
1135       }
1136     }
1137     myWriter->EndObject();
1138   }
1139   myWriter->EndArray();
1140 #endif
1141 }
1142
1143 // =======================================================================
1144 // function : writeExtensions
1145 // purpose  :
1146 // =======================================================================
1147 void RWGltf_CafWriter::writeExtensions()
1148 {
1149   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
1150 }
1151
1152 // =======================================================================
1153 // function : writeImages
1154 // purpose  :
1155 // =======================================================================
1156 void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1157                                     RWGltf_GltfMaterialMap& theMaterialMap)
1158 {
1159 #ifdef HAVE_RAPIDJSON
1160   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
1161
1162   // empty RWGltf_GltfRootElement_Images section should NOT be written to avoid validator errors
1163   bool anIsStarted = false;
1164   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1165   {
1166     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1167     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1168     {
1169       theMaterialMap.AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1170     }
1171   }
1172   if (anIsStarted)
1173   {
1174     myWriter->EndArray();
1175   }
1176 #else
1177   (void )theSceneNodeMap;
1178   (void )theMaterialMap;
1179 #endif
1180 }
1181
1182 // =======================================================================
1183 // function : writeMaterials
1184 // purpose  :
1185 // =======================================================================
1186 void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1187                                        RWGltf_GltfMaterialMap& theMaterialMap)
1188 {
1189 #ifdef HAVE_RAPIDJSON
1190   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
1191
1192   // empty RWGltf_GltfRootElement_Materials section should NOT be written to avoid validator errors
1193   bool anIsStarted = false;
1194   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1195   {
1196     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1197     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1198     {
1199       theMaterialMap.AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1200     }
1201   }
1202   if (anIsStarted)
1203   {
1204     myWriter->EndArray();
1205   }
1206 #else
1207   (void )theSceneNodeMap;
1208   (void )theMaterialMap;
1209 #endif
1210 }
1211
1212 // =======================================================================
1213 // function : writeMeshes
1214 // purpose  :
1215 // =======================================================================
1216 void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1217                                     const RWGltf_GltfMaterialMap&  theMaterialMap)
1218 {
1219 #ifdef HAVE_RAPIDJSON
1220   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
1221
1222   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
1223   myWriter->StartArray();
1224
1225   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1226   {
1227     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1228     const TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.RefLabel);
1229
1230     myWriter->StartObject();
1231     myWriter->Key ("name");
1232     myWriter->String (aNodeName.ToCString());
1233     myWriter->Key ("primitives");
1234     myWriter->StartArray();
1235
1236     Standard_Integer aNbFacesInNode = 0;
1237     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
1238     {
1239       if (toSkipFaceMesh (aFaceIter))
1240       {
1241         continue;
1242       }
1243
1244       const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
1245       const TCollection_AsciiString aMatId = theMaterialMap.FindMaterial (aFaceIter.FaceStyle());
1246       myWriter->StartObject();
1247       {
1248         myWriter->Key ("attributes");
1249         myWriter->StartObject();
1250         {
1251           if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1252           {
1253             myWriter->Key ("NORMAL");
1254             myWriter->Int (aGltfFace.NodeNorm.Id);
1255           }
1256           myWriter->Key ("POSITION");
1257           myWriter->Int (aGltfFace.NodePos.Id);
1258           if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1259           {
1260             myWriter->Key ("TEXCOORD_0");
1261             myWriter->Int (aGltfFace.NodeUV.Id);
1262           }
1263         }
1264         myWriter->EndObject();
1265
1266         myWriter->Key ("indices");
1267         myWriter->Int (aGltfFace.Indices.Id);
1268         if (!aMatId.IsEmpty())
1269         {
1270           myWriter->Key ("material");
1271           myWriter->Int (aMatId.IntegerValue());
1272         }
1273         myWriter->Key ("mode");
1274         myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
1275       }
1276       myWriter->EndObject();
1277     }
1278     myWriter->EndArray();
1279     myWriter->EndObject();
1280   }
1281   myWriter->EndArray();
1282 #else
1283   (void )theSceneNodeMap;
1284   (void )theMaterialMap;
1285 #endif
1286 }
1287
1288 // =======================================================================
1289 // function : writeNodes
1290 // purpose  :
1291 // =======================================================================
1292 void RWGltf_CafWriter::writeNodes (const Handle(TDocStd_Document)&  theDocument,
1293                                    const TDF_LabelSequence&         theRootLabels,
1294                                    const TColStd_MapOfAsciiString*  theLabelFilter,
1295                                    const RWGltf_GltfSceneNodeMap&   theSceneNodeMap,
1296                                    NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1297 {
1298 #ifdef HAVE_RAPIDJSON
1299   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
1300
1301   // Prepare full indexed map of scene nodes in correct order.
1302   RWGltf_GltfSceneNodeMap aSceneNodeMapWithChildren; // indexes starting from 1
1303   for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_None);
1304        aDocExplorer.More(); aDocExplorer.Next())
1305   {
1306     const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
1307     if (theLabelFilter != NULL
1308     && !theLabelFilter->Contains (aDocNode.Id))
1309     {
1310       continue;
1311     }
1312
1313     Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
1314     if (aDocExplorer.CurrentDepth() == 0)
1315     {
1316       // save root node index (starting from 0 not 1)
1317       theSceneRootNodeInds.Append (aNodeIndex - 1);
1318     }
1319   }
1320
1321   // Write scene nodes using prepared map for correct order of array members
1322   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
1323   myWriter->StartArray();
1324
1325   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
1326   {
1327     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1328
1329     myWriter->StartObject();
1330     {
1331       if (aDocNode.IsAssembly)
1332       {
1333         myWriter->Key ("children");
1334         myWriter->StartArray();
1335         {
1336           for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
1337           {
1338             const TDF_Label& aChildLabel = aChildIter.Value();
1339             if (aChildLabel.IsNull())
1340             {
1341               continue;
1342             }
1343
1344             const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
1345             Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
1346             if (aChildIdx > 0)
1347             {
1348               myWriter->Int (aChildIdx - 1);
1349             }
1350           }
1351         }
1352         myWriter->EndArray();
1353       }
1354     }
1355     if (!aDocNode.LocalTrsf.IsIdentity())
1356     {
1357       gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
1358       if (aTrsf.Form() != gp_Identity)
1359       {
1360         myCSTrsf.TransformTransformation (aTrsf);
1361         const gp_Quaternion aQuaternion = aTrsf.GetRotation();
1362         const bool hasRotation = Abs (aQuaternion.X())       > gp::Resolution()
1363                               || Abs (aQuaternion.Y())       > gp::Resolution()
1364                               || Abs (aQuaternion.Z())       > gp::Resolution()
1365                               || Abs (aQuaternion.W() - 1.0) > gp::Resolution();
1366         const Standard_Real aScaleFactor = aTrsf.ScaleFactor();
1367         const bool hasScale = Abs (aScaleFactor - 1.0) > Precision::Confusion();
1368         const gp_XYZ& aTranslPart = aTrsf.TranslationPart();
1369         const bool hasTranslation = aTranslPart.SquareModulus() > gp::Resolution();
1370
1371         RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
1372         if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
1373         {
1374           aTrsfFormat = hasRotation && hasScale && hasTranslation
1375                       ? RWGltf_WriterTrsfFormat_Mat4
1376                       : RWGltf_WriterTrsfFormat_TRS;
1377         }
1378
1379         if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
1380         {
1381           // write full matrix
1382           Graphic3d_Mat4 aMat4;
1383           aTrsf.GetMat4 (aMat4);
1384           if (!aMat4.IsIdentity())
1385           {
1386             myWriter->Key ("matrix");
1387             myWriter->StartArray();
1388             for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
1389             {
1390               for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
1391               {
1392                 myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
1393               }
1394             }
1395             myWriter->EndArray();
1396           }
1397         }
1398         else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
1399         {
1400           if (hasRotation)
1401           {
1402             myWriter->Key ("rotation");
1403             myWriter->StartArray();
1404             myWriter->Double (aQuaternion.X());
1405             myWriter->Double (aQuaternion.Y());
1406             myWriter->Double (aQuaternion.Z());
1407             myWriter->Double (aQuaternion.W());
1408             myWriter->EndArray();
1409           }
1410           if (hasScale)
1411           {
1412             myWriter->Key ("scale");
1413             myWriter->StartArray();
1414             myWriter->Double (aScaleFactor);
1415             myWriter->Double (aScaleFactor);
1416             myWriter->Double (aScaleFactor);
1417             myWriter->EndArray();
1418           }
1419           if (hasTranslation)
1420           {
1421             myWriter->Key ("translation");
1422             myWriter->StartArray();
1423             myWriter->Double (aTranslPart.X());
1424             myWriter->Double (aTranslPart.Y());
1425             myWriter->Double (aTranslPart.Z());
1426             myWriter->EndArray();
1427           }
1428         }
1429       }
1430     }
1431     if (!aDocNode.IsAssembly)
1432     {
1433       myWriter->Key ("mesh");
1434       // Mesh order of current node is equal to order of this node in scene nodes map
1435       Standard_Integer aMeshIdx = theSceneNodeMap.FindIndex (aDocNode.Id);
1436       if (aMeshIdx > 0)
1437       {
1438         myWriter->Int (aMeshIdx - 1);
1439       }
1440     }
1441     {
1442       TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.Label);
1443       if (aNodeName.IsEmpty())
1444       {
1445         aNodeName = readNameAttribute (aDocNode.RefLabel);
1446       }
1447       myWriter->Key ("name");
1448       myWriter->String (aNodeName.ToCString());
1449     }
1450     myWriter->EndObject();
1451   }
1452   myWriter->EndArray();
1453 #else
1454   (void )theDocument;
1455   (void )theRootLabels;
1456   (void )theLabelFilter;
1457   (void )theSceneNodeMap;
1458   (void )theSceneRootNodeInds;
1459 #endif
1460 }
1461
1462 // =======================================================================
1463 // function : writeSamplers
1464 // purpose  :
1465 // =======================================================================
1466 void RWGltf_CafWriter::writeSamplers (const RWGltf_GltfMaterialMap& theMaterialMap)
1467 {
1468 #ifdef HAVE_RAPIDJSON
1469   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
1470   if (theMaterialMap.NbImages() == 0)
1471   {
1472     return;
1473   }
1474
1475   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
1476   myWriter->StartArray();
1477   {
1478     myWriter->StartObject();
1479     {
1480       //myWriter->Key ("magFilter");
1481       //myWriter->Int (9729);
1482       //myWriter->Key ("minFilter");
1483       //myWriter->Int (9729);
1484     }
1485     myWriter->EndObject();
1486   }
1487   myWriter->EndArray();
1488 #else
1489   (void )theMaterialMap;
1490 #endif
1491 }
1492
1493 // =======================================================================
1494 // function : writeScene
1495 // purpose  :
1496 // =======================================================================
1497 void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
1498 {
1499 #ifdef HAVE_RAPIDJSON
1500   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
1501
1502   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
1503   myWriter->Int (theDefSceneId);
1504 #else
1505   (void )theDefSceneId;
1506 #endif
1507 }
1508
1509 // =======================================================================
1510 // function : writeScenes
1511 // purpose  :
1512 // =======================================================================
1513 void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1514 {
1515 #ifdef HAVE_RAPIDJSON
1516   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
1517
1518   myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
1519   myWriter->StartArray();
1520   {
1521     myWriter->StartObject();
1522     myWriter->Key ("nodes");
1523     myWriter->StartArray();
1524     for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
1525     {
1526       myWriter->Int (aRootIter.Value());
1527     }
1528     myWriter->EndArray();
1529     myWriter->EndObject();
1530   }
1531   myWriter->EndArray();
1532 #else
1533   (void )theSceneRootNodeInds;
1534 #endif
1535 }
1536
1537 // =======================================================================
1538 // function : writeSkins
1539 // purpose  :
1540 // =======================================================================
1541 void RWGltf_CafWriter::writeSkins()
1542 {
1543   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
1544
1545   // This section should be skipped if it doesn't contain any information but not be empty
1546   /*myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Skins));
1547   myWriter->StartArray();
1548   myWriter->EndArray();*/
1549 }
1550
1551 // =======================================================================
1552 // function : writeTextures
1553 // purpose  :
1554 // =======================================================================
1555 void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1556                                       RWGltf_GltfMaterialMap& theMaterialMap)
1557 {
1558 #ifdef HAVE_RAPIDJSON
1559   Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
1560
1561   // empty RWGltf_GltfRootElement_Textures section should not be written to avoid validator errors
1562   bool anIsStarted = false;
1563   for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1564   {
1565     const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1566     for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1567     {
1568       theMaterialMap.AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1569     }
1570   }
1571   if (anIsStarted)
1572   {
1573     myWriter->EndArray();
1574   }
1575 #else
1576  (void )theSceneNodeMap;
1577  (void )theMaterialMap;
1578 #endif
1579 }