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