1 // Copyright (c) 2017-2019 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
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.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <RWGltf_CafWriter.hxx>
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>
39 #include <RWGltf_GltfOStreamWriter.hxx>
42 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
46 //! Write three float values.
47 static void writeVec3 (std::ostream& theStream,
48 const gp_XYZ& theVec3)
50 Graphic3d_Vec3 aVec3 (float(theVec3.X()), float(theVec3.Y()), float(theVec3.Z()));
51 theStream.write ((const char* )aVec3.GetData(), sizeof(aVec3));
54 //! Write three float values.
55 static void writeVec3 (std::ostream& theStream,
56 const Graphic3d_Vec3& theVec3)
58 theStream.write ((const char* )theVec3.GetData(), sizeof(theVec3));
61 //! Write two float values.
62 static void writeVec2 (std::ostream& theStream,
65 Graphic3d_Vec2 aVec2 (float(theVec2.X()), float(theVec2.Y()));
66 theStream.write ((const char* )aVec2.GetData(), sizeof(aVec2));
69 //! Write triangle indices.
70 static void writeTriangle32 (std::ostream& theStream,
71 const Graphic3d_Vec3i& theTri)
73 theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
76 //! Write triangle indices.
77 static void writeTriangle16 (std::ostream& theStream,
78 const NCollection_Vec3<uint16_t>& theTri)
80 theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
84 //! Read name attribute.
85 static TCollection_AsciiString readNameAttribute (const TDF_Label& theRefLabel)
87 Handle(TDataStd_Name) aNodeName;
88 if (!theRefLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
90 return TCollection_AsciiString();
92 return TCollection_AsciiString (aNodeName->Get());
97 //================================================================
98 // Function : Constructor
100 //================================================================
101 RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
102 Standard_Boolean theIsBinary)
104 myTrsfFormat (RWGltf_WriterTrsfFormat_Compact),
105 myIsBinary (theIsBinary),
108 myCSTrsf.SetOutputLengthUnit (1.0); // meters
109 myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
111 TCollection_AsciiString aFolder, aFileName, aShortFileNameBase, aFileExt;
112 OSD_Path::FolderAndFileFromPath (theFile, aFolder, aFileName);
113 OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
115 myBinFileNameShort = aShortFileNameBase + ".bin" + (myIsBinary ? ".tmp" : "");
116 myBinFileNameFull = !aFolder.IsEmpty() ? aFolder + myBinFileNameShort : myBinFileNameShort;
119 //================================================================
120 // Function : Destructor
122 //================================================================
123 RWGltf_CafWriter::~RWGltf_CafWriter()
128 //================================================================
129 // Function : toSkipFaceMesh
131 //================================================================
132 Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
134 return theFaceIter.IsEmptyMesh();
137 // =======================================================================
138 // function : saveNodes
140 // =======================================================================
141 void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
142 std::ostream& theBinFile,
143 const RWMesh_FaceIterator& theFaceIter,
144 Standard_Integer& theAccessorNb) const
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;
152 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
153 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
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);
162 // =======================================================================
163 // function : saveNormals
165 // =======================================================================
166 void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
167 std::ostream& theBinFile,
168 RWMesh_FaceIterator& theFaceIter,
169 Standard_Integer& theAccessorNb) const
171 if (!theFaceIter.HasNormals())
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;
182 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
183 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
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);
192 // =======================================================================
193 // function : saveTextCoords
195 // =======================================================================
196 void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
197 std::ostream& theBinFile,
198 const RWMesh_FaceIterator& theFaceIter,
199 Standard_Integer& theAccessorNb) const
201 if (!theFaceIter.HasTexCoords())
205 if (!myIsForcedUVExport)
207 if (theFaceIter.FaceStyle().Material().IsNull())
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())
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)
230 gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
231 aTexCoord.SetY (1.0 - aTexCoord.Y());
232 writeVec2 (theBinFile, aTexCoord.XY());
236 // =======================================================================
237 // function : saveIndices
239 // =======================================================================
240 void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
241 std::ostream& theBinFile,
242 const RWMesh_FaceIterator& theFaceIter,
243 Standard_Integer& theAccessorNb)
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;
253 const Standard_Integer anElemLower = theFaceIter.ElemLower();
254 const Standard_Integer anElemUpper = theFaceIter.ElemUpper();
255 for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper; ++anElemIter)
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)
263 writeTriangle16 (theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
267 writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
270 if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
272 // alignment by 4 bytes
273 int64_t aContentLen64 = (int64_t)theBinFile.tellp();
274 while (aContentLen64 % 4 != 0)
276 theBinFile.write (" ", 1);
282 // =======================================================================
283 // function : Perform
285 // =======================================================================
286 bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
287 const TColStd_IndexedDataMapOfStringString& theFileInfo,
288 const Handle(Message_ProgressIndicator)& theProgress)
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);
296 // =======================================================================
297 // function : Perform
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)
306 Message_ProgressSentry aPSentry (theProgress, "Writing glTF file", 0, 2, 1);
307 if (!writeBinData (theDocument, theRootLabels, theLabelFilter, theProgress))
313 if (!aPSentry.More())
318 return writeJson (theDocument, theRootLabels, theLabelFilter, theFileInfo, theProgress);
321 // =======================================================================
322 // function : writeBinData
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)
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;
346 myBinDataMap.Clear();
349 std::ofstream aBinFile;
350 OSD_OpenStream (aBinFile, myBinFileNameFull.ToCString(), std::ios::out | std::ios::binary);
351 if (!aBinFile.is_open()
354 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created");
358 Message_ProgressSentry aPSentryBin (theProgress, "Binary data", 0, 4, 1);
360 Standard_Integer aNbAccessors = 0;
363 myBuffViewPos.ByteOffset = aBinFile.tellp();
364 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
365 aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
367 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
368 if (theLabelFilter != NULL
369 && !theLabelFilter->Contains (aDocNode.Id))
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())
377 if (myBinDataMap.IsBound (aFaceIter.Face())
378 || toSkipFaceMesh (aFaceIter))
383 RWGltf_GltfFace aGltfFace;
384 saveNodes (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
386 if (!aBinFile.good())
388 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
392 myBinDataMap.Bind (aFaceIter.Face(), aGltfFace);
395 myBuffViewPos.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewPos.ByteOffset;
396 if (!aPSentryBin.More())
403 myBuffViewNorm.ByteOffset = aBinFile.tellp();
404 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
405 aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
407 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
408 if (theLabelFilter != NULL
409 && !theLabelFilter->Contains (aDocNode.Id))
413 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
415 if (toSkipFaceMesh (aFaceIter))
420 RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
421 if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
426 saveNormals (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
428 if (!aBinFile.good())
430 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
435 myBuffViewNorm.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewNorm.ByteOffset;
436 if (!aPSentryBin.More())
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())
447 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
448 if (theLabelFilter != NULL
449 && !theLabelFilter->Contains (aDocNode.Id))
454 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
456 if (toSkipFaceMesh (aFaceIter))
461 RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
462 if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
467 saveTextCoords (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
469 if (!aBinFile.good())
471 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
476 myBuffViewTextCoord.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
477 if (!aPSentryBin.More())
484 myBuffViewInd.ByteOffset = aBinFile.tellp();
485 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
486 aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
488 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
489 if (theLabelFilter != NULL
490 && !theLabelFilter->Contains (aDocNode.Id))
495 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
497 if (toSkipFaceMesh (aFaceIter))
502 RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
503 if (aGltfFace.Indices.Id != RWGltf_GltfAccessor::INVALID_ID)
508 saveIndices (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
510 if (!aBinFile.good())
512 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
517 myBuffViewInd.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewInd.ByteOffset;
520 if (myBuffViewPos.ByteLength > 0)
522 myBuffViewPos.Id = aBuffViewId++;
524 if (myBuffViewNorm.ByteLength > 0)
526 myBuffViewNorm.Id = aBuffViewId++;
528 if (myBuffViewTextCoord.ByteLength > 0)
530 myBuffViewTextCoord.Id = aBuffViewId++;
532 if (myBuffViewInd.ByteLength > 0)
534 myBuffViewInd.Id = aBuffViewId++;
537 myBinDataLen64 = aBinFile.tellp();
539 if (!aBinFile.good())
541 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
547 //================================================================
548 // Function : writeJson
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)
557 #ifdef HAVE_RAPIDJSON
560 // write vertex arrays
561 Message_ProgressSentry aPSentryBin (theProgress, "Header data", 0, 2, 1);
563 const Standard_Integer aBinDataBufferId = 0;
564 const Standard_Integer aDefSamplerId = 0;
565 const Standard_Integer aDefSceneId = 0;
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())
573 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created");
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;
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));
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())
597 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
598 if (theLabelFilter != NULL
599 && !theLabelFilter->Contains (aDocNode.Id))
603 aSceneNodeMap.Add (aDocNode);
606 rapidjson::OStreamWrapper aFileStream (aGltfContentFile);
607 myWriter.reset (new RWGltf_GltfOStreamWriter (aFileStream));
609 myWriter->StartObject();
611 writeAccessors (aSceneNodeMap);
613 writeAsset (theFileInfo);
614 writeBufferViews (aBinDataBufferId);
618 RWGltf_GltfMaterialMap aMaterialMap (myFile, aDefSamplerId);
619 aMaterialMap.SetDefaultStyle (myDefaultStyle);
620 writeImages (aSceneNodeMap, aMaterialMap);
621 writeMaterials (aSceneNodeMap, aMaterialMap);
622 writeMeshes (aSceneNodeMap, aMaterialMap);
625 if (!aPSentryBin.More())
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);
637 writeTextures (aSceneNodeMap, aMaterialMap);
639 myWriter->EndObject();
643 aGltfContentFile.close();
644 if (!aGltfContentFile.good())
646 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
652 int64_t aContentLen64 = (int64_t )aGltfContentFile.tellp() - 20;
653 while (aContentLen64 % 4 != 0)
655 aGltfContentFile.write (" ", 1);
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);
664 const int64_t aFullLen64 = aContentLen64 + 20 + myBinDataLen64 + 8;
665 if (aFullLen64 < std::numeric_limits<uint32_t>::max())
668 std::ifstream aBinFile;
669 OSD_OpenStream (aBinFile, myBinFileNameFull.ToCString(), std::ios::in | std::ios::binary);
670 if (!aBinFile.is_open()
673 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened");
677 for (; aBinFile.good();)
679 aBinFile.read (aBuffer, 4096);
680 const Standard_Integer aReadLen = (Standard_Integer )aBinFile.gcount();
685 aGltfContentFile.write (aBuffer, aReadLen);
688 OSD_Path aBinFilePath (myBinFileNameFull);
689 OSD_File (aBinFilePath).Remove();
690 if (OSD_File (aBinFilePath).Exists())
692 Message::SendFail (TCollection_AsciiString ("Unable to remove temporary glTF content file '") + myBinFileNameFull + "'");
697 Message::SendFail ("glTF file content is too big for binary format");
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);
707 aGltfContentFile.close();
708 if (!aGltfContentFile.good())
710 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
718 (void )theRootLabels;
719 (void )theLabelFilter;
722 Message::SendFail ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
727 // =======================================================================
728 // function : writeAccessors
730 // =======================================================================
731 void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
733 #ifdef HAVE_RAPIDJSON
734 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
736 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
737 myWriter->StartArray();
739 NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aWrittenFaces;
740 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
742 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
743 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
745 if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
746 || toSkipFaceMesh (aFaceIter))
751 const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
752 writePositions (aGltfFace);
755 aWrittenFaces.Clear();
756 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
758 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
759 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
761 if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
762 || toSkipFaceMesh (aFaceIter))
767 const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
768 writeNormals (aGltfFace);
771 aWrittenFaces.Clear();
772 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
774 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
775 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
777 if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
778 || toSkipFaceMesh (aFaceIter))
783 const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
784 writeTextCoords (aGltfFace);
787 aWrittenFaces.Clear();
788 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
790 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
791 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
793 if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
794 || toSkipFaceMesh (aFaceIter))
799 const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
800 writeIndices (aGltfFace);
804 myWriter->EndArray();
806 (void )theSceneNodeMap;
810 // =======================================================================
811 // function : writePositions
813 // =======================================================================
814 void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
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)
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);
833 if (theGltfFace.NodePos.BndBox.IsValid())
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();
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();
849 myWriter->Key ("type");
850 myWriter->String ("VEC3");
852 myWriter->EndObject();
858 // =======================================================================
859 // function : writeNormals
861 // =======================================================================
862 void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
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)
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
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();
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();
897 myWriter->Key ("type");
898 myWriter->String ("VEC3");
900 myWriter->EndObject();
906 // =======================================================================
907 // function : writeTextCoords
909 // =======================================================================
910 void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
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)
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
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();
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();
945 myWriter->Key ("type");
946 myWriter->String ("VEC2");
948 myWriter->EndObject();
954 // =======================================================================
955 // function : writeIndices
957 // =======================================================================
958 void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
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)
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);
977 myWriter->Key ("type");
978 myWriter->String ("SCALAR");
980 myWriter->EndObject();
986 // =======================================================================
987 // function : writeAnimations
989 // =======================================================================
990 void RWGltf_CafWriter::writeAnimations()
992 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAnimations()");
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();
1000 // =======================================================================
1001 // function : writeAsset
1003 // =======================================================================
1004 void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo)
1006 #ifdef HAVE_RAPIDJSON
1007 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAsset()");
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
1016 bool anIsStarted = false;
1017 for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
1021 myWriter->Key ("extras");
1022 myWriter->StartObject();
1025 myWriter->Key (aKeyValueIter.Key().ToCString());
1026 myWriter->String (aKeyValueIter.Value().ToCString());
1030 myWriter->EndObject();
1033 myWriter->EndObject();
1039 // =======================================================================
1040 // function : writeBufferViews
1042 // =======================================================================
1043 void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBufferId)
1045 #ifdef HAVE_RAPIDJSON
1046 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
1048 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
1049 myWriter->StartArray();
1050 if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1065 if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1080 if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1095 if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1108 myWriter->EndArray();
1110 (void )theBinDataBufferId;
1114 // =======================================================================
1115 // function : writeBuffers
1117 // =======================================================================
1118 void RWGltf_CafWriter::writeBuffers()
1120 #ifdef HAVE_RAPIDJSON
1121 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBuffers()");
1123 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Buffers));
1124 myWriter->StartArray();
1126 myWriter->StartObject();
1128 myWriter->Key ("byteLength");
1129 myWriter->Int64 (myBuffViewPos.ByteLength + myBuffViewNorm.ByteLength +
1130 myBuffViewTextCoord.ByteLength + myBuffViewInd.ByteLength);
1133 myWriter->Key ("uri");
1134 myWriter->String (myBinFileNameShort.ToCString());
1137 myWriter->EndObject();
1139 myWriter->EndArray();
1143 // =======================================================================
1144 // function : writeExtensions
1146 // =======================================================================
1147 void RWGltf_CafWriter::writeExtensions()
1149 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
1152 // =======================================================================
1153 // function : writeImages
1155 // =======================================================================
1156 void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1157 RWGltf_GltfMaterialMap& theMaterialMap)
1159 #ifdef HAVE_RAPIDJSON
1160 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
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())
1166 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1167 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1169 theMaterialMap.AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1174 myWriter->EndArray();
1177 (void )theSceneNodeMap;
1178 (void )theMaterialMap;
1182 // =======================================================================
1183 // function : writeMaterials
1185 // =======================================================================
1186 void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1187 RWGltf_GltfMaterialMap& theMaterialMap)
1189 #ifdef HAVE_RAPIDJSON
1190 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
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())
1196 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1197 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1199 theMaterialMap.AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1204 myWriter->EndArray();
1207 (void )theSceneNodeMap;
1208 (void )theMaterialMap;
1212 // =======================================================================
1213 // function : writeMeshes
1215 // =======================================================================
1216 void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1217 const RWGltf_GltfMaterialMap& theMaterialMap)
1219 #ifdef HAVE_RAPIDJSON
1220 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
1222 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
1223 myWriter->StartArray();
1225 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1227 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1228 const TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.RefLabel);
1230 myWriter->StartObject();
1231 myWriter->Key ("name");
1232 myWriter->String (aNodeName.ToCString());
1233 myWriter->Key ("primitives");
1234 myWriter->StartArray();
1236 Standard_Integer aNbFacesInNode = 0;
1237 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
1239 if (toSkipFaceMesh (aFaceIter))
1244 const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
1245 const TCollection_AsciiString aMatId = theMaterialMap.FindMaterial (aFaceIter.FaceStyle());
1246 myWriter->StartObject();
1248 myWriter->Key ("attributes");
1249 myWriter->StartObject();
1251 if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1253 myWriter->Key ("NORMAL");
1254 myWriter->Int (aGltfFace.NodeNorm.Id);
1256 myWriter->Key ("POSITION");
1257 myWriter->Int (aGltfFace.NodePos.Id);
1258 if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1260 myWriter->Key ("TEXCOORD_0");
1261 myWriter->Int (aGltfFace.NodeUV.Id);
1264 myWriter->EndObject();
1266 myWriter->Key ("indices");
1267 myWriter->Int (aGltfFace.Indices.Id);
1268 if (!aMatId.IsEmpty())
1270 myWriter->Key ("material");
1271 myWriter->Int (aMatId.IntegerValue());
1273 myWriter->Key ("mode");
1274 myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
1276 myWriter->EndObject();
1278 myWriter->EndArray();
1279 myWriter->EndObject();
1281 myWriter->EndArray();
1283 (void )theSceneNodeMap;
1284 (void )theMaterialMap;
1288 // =======================================================================
1289 // function : writeNodes
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)
1298 #ifdef HAVE_RAPIDJSON
1299 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
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())
1306 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
1307 if (theLabelFilter != NULL
1308 && !theLabelFilter->Contains (aDocNode.Id))
1313 Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
1314 if (aDocExplorer.CurrentDepth() == 0)
1316 // save root node index (starting from 0 not 1)
1317 theSceneRootNodeInds.Append (aNodeIndex - 1);
1321 // Write scene nodes using prepared map for correct order of array members
1322 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
1323 myWriter->StartArray();
1325 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
1327 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1329 myWriter->StartObject();
1331 if (aDocNode.IsAssembly)
1333 myWriter->Key ("children");
1334 myWriter->StartArray();
1336 for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
1338 const TDF_Label& aChildLabel = aChildIter.Value();
1339 if (aChildLabel.IsNull())
1344 const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
1345 Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
1348 myWriter->Int (aChildIdx - 1);
1352 myWriter->EndArray();
1355 if (!aDocNode.LocalTrsf.IsIdentity())
1357 gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
1358 if (aTrsf.Form() != gp_Identity)
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();
1371 RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
1372 if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
1374 aTrsfFormat = hasRotation && hasScale && hasTranslation
1375 ? RWGltf_WriterTrsfFormat_Mat4
1376 : RWGltf_WriterTrsfFormat_TRS;
1379 if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
1381 // write full matrix
1382 Graphic3d_Mat4 aMat4;
1383 aTrsf.GetMat4 (aMat4);
1384 if (!aMat4.IsIdentity())
1386 myWriter->Key ("matrix");
1387 myWriter->StartArray();
1388 for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
1390 for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
1392 myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
1395 myWriter->EndArray();
1398 else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
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();
1412 myWriter->Key ("scale");
1413 myWriter->StartArray();
1414 myWriter->Double (aScaleFactor);
1415 myWriter->Double (aScaleFactor);
1416 myWriter->Double (aScaleFactor);
1417 myWriter->EndArray();
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();
1431 if (!aDocNode.IsAssembly)
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);
1438 myWriter->Int (aMeshIdx - 1);
1442 TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.Label);
1443 if (aNodeName.IsEmpty())
1445 aNodeName = readNameAttribute (aDocNode.RefLabel);
1447 myWriter->Key ("name");
1448 myWriter->String (aNodeName.ToCString());
1450 myWriter->EndObject();
1452 myWriter->EndArray();
1455 (void )theRootLabels;
1456 (void )theLabelFilter;
1457 (void )theSceneNodeMap;
1458 (void )theSceneRootNodeInds;
1462 // =======================================================================
1463 // function : writeSamplers
1465 // =======================================================================
1466 void RWGltf_CafWriter::writeSamplers (const RWGltf_GltfMaterialMap& theMaterialMap)
1468 #ifdef HAVE_RAPIDJSON
1469 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
1470 if (theMaterialMap.NbImages() == 0)
1475 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
1476 myWriter->StartArray();
1478 myWriter->StartObject();
1480 //myWriter->Key ("magFilter");
1481 //myWriter->Int (9729);
1482 //myWriter->Key ("minFilter");
1483 //myWriter->Int (9729);
1485 myWriter->EndObject();
1487 myWriter->EndArray();
1489 (void )theMaterialMap;
1493 // =======================================================================
1494 // function : writeScene
1496 // =======================================================================
1497 void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
1499 #ifdef HAVE_RAPIDJSON
1500 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
1502 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
1503 myWriter->Int (theDefSceneId);
1505 (void )theDefSceneId;
1509 // =======================================================================
1510 // function : writeScenes
1512 // =======================================================================
1513 void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1515 #ifdef HAVE_RAPIDJSON
1516 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
1518 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
1519 myWriter->StartArray();
1521 myWriter->StartObject();
1522 myWriter->Key ("nodes");
1523 myWriter->StartArray();
1524 for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
1526 myWriter->Int (aRootIter.Value());
1528 myWriter->EndArray();
1529 myWriter->EndObject();
1531 myWriter->EndArray();
1533 (void )theSceneRootNodeInds;
1537 // =======================================================================
1538 // function : writeSkins
1540 // =======================================================================
1541 void RWGltf_CafWriter::writeSkins()
1543 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
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();*/
1551 // =======================================================================
1552 // function : writeTextures
1554 // =======================================================================
1555 void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1556 RWGltf_GltfMaterialMap& theMaterialMap)
1558 #ifdef HAVE_RAPIDJSON
1559 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
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())
1565 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1566 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1568 theMaterialMap.AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1573 myWriter->EndArray();
1576 (void )theSceneNodeMap;
1577 (void )theMaterialMap;