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 <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>
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>
44 #include <RWGltf_GltfOStreamWriter.hxx>
48 #include <Standard_WarningsDisable.hxx>
49 #include <draco/compression/encode.h>
50 #include <Standard_WarningsRestore.hxx>
53 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
57 //! Write three float values.
58 static void writeVec3 (std::ostream& theStream,
59 const gp_XYZ& theVec3)
61 Graphic3d_Vec3 aVec3 (float(theVec3.X()), float(theVec3.Y()), float(theVec3.Z()));
62 theStream.write ((const char* )aVec3.GetData(), sizeof(aVec3));
65 //! Write three float values.
66 static void writeVec3 (std::ostream& theStream,
67 const Graphic3d_Vec3& theVec3)
69 theStream.write ((const char* )theVec3.GetData(), sizeof(theVec3));
72 //! Write two float values.
73 static void writeVec2 (std::ostream& theStream,
76 Graphic3d_Vec2 aVec2 (float(theVec2.X()), float(theVec2.Y()));
77 theStream.write ((const char* )aVec2.GetData(), sizeof(aVec2));
80 //! Write triangle indices.
81 static void writeTriangle32 (std::ostream& theStream,
82 const Graphic3d_Vec3i& theTri)
84 theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
87 //! Write triangle indices.
88 static void writeTriangle16 (std::ostream& theStream,
89 const NCollection_Vec3<uint16_t>& theTri)
91 theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
95 //! Write nodes to Draco mesh
96 static void writeNodesToDracoMesh (draco::Mesh& theMesh,
97 const std::vector<Graphic3d_Vec3>& theNodes)
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)
111 aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theNodes[aNodeInd].GetData());
115 //! Write normals to Draco mesh
116 static void writeNormalsToDracoMesh (draco::Mesh& theMesh,
117 const std::vector<Graphic3d_Vec3>& theNormals)
119 if (theNormals.empty())
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)
131 aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theNormals[aNormInd].GetData());
135 //! Write texture UV coordinates to Draco mesh
136 static void writeTexCoordsToDracoMesh (draco::Mesh& theMesh,
137 const std::vector<Graphic3d_Vec2>& theTexCoord)
139 if (theTexCoord.empty())
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)
151 aPtr->SetAttributeValue (aPtr->mapped_index(anIndex), theTexCoord[aTexInd].GetData());
155 //! Write indices to Draco mesh
156 static void writeIndicesToDracoMesh (draco::Mesh& theMesh,
157 const std::vector<Poly_Triangle>& theIndices)
159 draco::Mesh::Face aFace;
161 for (size_t anInd = 0; anInd < theIndices.size(); ++anInd, ++anIndex)
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);
173 //================================================================
174 // Function : Constructor
176 //================================================================
177 RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
178 Standard_Boolean theIsBinary)
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),
190 myCSTrsf.SetOutputLengthUnit (1.0); // meters
191 myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
193 TCollection_AsciiString aFolder, aFileName, aShortFileNameBase, aFileExt;
194 OSD_Path::FolderAndFileFromPath (theFile, aFolder, aFileName);
195 OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
197 myBinFileNameShort = aShortFileNameBase + ".bin" + (myIsBinary ? ".tmp" : "");
198 myBinFileNameFull = !aFolder.IsEmpty() ? aFolder + myBinFileNameShort : myBinFileNameShort;
201 //================================================================
202 // Function : Destructor
204 //================================================================
205 RWGltf_CafWriter::~RWGltf_CafWriter()
210 //================================================================
211 // Function : formatName
213 //================================================================
214 TCollection_AsciiString RWGltf_CafWriter::formatName (RWMesh_NameFormat theFormat,
215 const TDF_Label& theLabel,
216 const TDF_Label& theRefLabel) const
218 return RWMesh::FormatName (theFormat, theLabel, theRefLabel);
221 //================================================================
222 // Function : toSkipFaceMesh
224 //================================================================
225 Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
227 return theFaceIter.IsEmptyMesh();
230 // =======================================================================
231 // function : saveNodes
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
240 if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
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;
249 if (theMesh.get() == nullptr)
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");
255 theGltfFace.NodePos.Count += theFaceIter.NbNodes();
257 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
258 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
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)
265 theMesh->NodesVec.push_back(Graphic3d_Vec3(float(aNode.X()), float(aNode.Y()), float(aNode.Z())));
269 writeVec3(theBinFile, aNode);
274 // =======================================================================
275 // function : saveNormals
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
284 if (!theFaceIter.HasNormals())
289 if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
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;
298 if (theMesh.get() == nullptr)
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");
304 theGltfFace.NodeNorm.Count += theFaceIter.NbNodes();
306 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
307 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
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)
314 theMesh->NormalsVec.push_back(aVecNormal);
318 writeVec3(theBinFile, aVecNormal);
323 // =======================================================================
324 // function : saveTextCoords
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
333 if (!theFaceIter.HasTexCoords())
337 if (!myIsForcedUVExport)
339 if (theFaceIter.FaceStyle().Material().IsNull())
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())
354 if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
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;
363 if (theMesh.get() == nullptr)
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");
369 theGltfFace.NodeUV.Count += theFaceIter.NbNodes();
371 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
372 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
374 gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
375 aTexCoord.SetY (1.0 - aTexCoord.Y());
376 if (theMesh.get() != nullptr)
378 theMesh->TexCoordsVec.push_back(Graphic3d_Vec2((float)aTexCoord.X(), (float)aTexCoord.Y()));
382 writeVec2(theBinFile, aTexCoord.XY());
387 // =======================================================================
388 // function : saveIndices
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)
397 if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
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;
408 if (theMesh.get() == nullptr)
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");
418 const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower();
419 theGltfFace.NbIndexedNodes += theFaceIter.NbNodes();
420 theGltfFace.Indices.Count += theFaceIter.NbTriangles() * 3;
422 const Standard_Integer anElemLower = theFaceIter.ElemLower();
423 const Standard_Integer anElemUpper = theFaceIter.ElemUpper();
424 for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper; ++anElemIter)
426 Poly_Triangle aTri = theFaceIter.TriangleOriented (anElemIter);
427 aTri(1) += aNodeFirst;
428 aTri(2) += aNodeFirst;
429 aTri(3) += aNodeFirst;
430 if (theMesh.get() != nullptr)
432 theMesh->IndicesVec.push_back(aTri);
436 if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
438 writeTriangle16(theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
442 writeTriangle32(theBinFile, Graphic3d_Vec3i(aTri(1), aTri(2), aTri(3)));
448 // =======================================================================
449 // function : Perform
451 // =======================================================================
452 bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
453 const TColStd_IndexedDataMapOfStringString& theFileInfo,
454 const Message_ProgressRange& theProgress)
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);
462 // =======================================================================
463 // function : Perform
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)
472 Standard_Real aLengthUnit = 1.;
473 if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument, aLengthUnit))
475 myCSTrsf.SetInputLengthUnit(aLengthUnit);
477 const Standard_Integer aDefSamplerId = 0;
478 myMaterialMap = new RWGltf_GltfMaterialMap (myFile, aDefSamplerId);
479 myMaterialMap->SetDefaultStyle (myDefaultStyle);
481 Message_ProgressScope aPSentry (theProgress, "Writing glTF file", 2);
482 if (!writeBinData (theDocument, theRootLabels, theLabelFilter, aPSentry.Next()))
487 if (!aPSentry.More())
492 return writeJson (theDocument, theRootLabels, theLabelFilter, theFileInfo, aPSentry.Next());
495 // =======================================================================
496 // function : writeBinData
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)
505 if (myDracoParameters.DracoCompression)
507 Message::SendFail ("Error: cannot use Draco compression, Draco library missing.");
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;
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;
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;
530 myBuffViewInd.Id = RWGltf_GltfAccessor::INVALID_ID;
531 myBuffViewInd.ByteOffset = 0;
532 myBuffViewInd.ByteLength = 0;
533 myBuffViewInd.Target = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
535 myBuffViewsDraco.clear();
537 myBinDataMap.Clear();
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())
545 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created");
549 Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4);
550 const RWGltf_GltfArrayType anArrTypes[4] =
552 RWGltf_GltfArrayType_Position,
553 RWGltf_GltfArrayType_Normal,
554 RWGltf_GltfArrayType_TCoord0,
555 RWGltf_GltfArrayType_Indices
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())
563 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
564 if (theLabelFilter != NULL
565 && !theLabelFilter->Contains (aDocNode.Id))
570 // transformation will be stored at scene nodes
571 aMergedFaces.Clear (false);
573 RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
576 RWGltf_StyledShape aStyledShape (aFaceIter.ExploredShape(), aDocNode.Style);
577 if (myBinDataMap.Contains (aStyledShape))
582 Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList();
583 myBinDataMap.Add (aStyledShape, aGltfFaceList);
584 for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
586 if (toSkipFaceMesh (aFaceIter))
591 Handle(RWGltf_GltfFace) aGltfFace;
592 if (!aMergedFaces.Find (aFaceIter.FaceStyle(), aGltfFace))
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);
601 else if (myToSplitIndices16
602 && aGltfFace->NbIndexedNodes < std::numeric_limits<uint16_t>::max()
603 && (aGltfFace->NbIndexedNodes + aFaceIter.NbNodes()) >= std::numeric_limits<uint16_t>::max())
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);
615 if (aGltfFace->Shape.ShapeType() != TopAbs_COMPOUND)
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;
623 BRep_Builder().Add (aGltfFace->Shape, aFaceIter.Face());
624 aGltfFace->NbIndexedNodes += aFaceIter.NbNodes();
630 for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
632 RWGltf_StyledShape aStyledShape (aFaceIter.Face(), aFaceIter.FaceStyle());
633 if (toSkipFaceMesh (aFaceIter)
634 || myBinDataMap.Contains (aStyledShape))
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);
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)
655 const RWGltf_GltfArrayType anArrType = (RWGltf_GltfArrayType )anArrTypes[aTypeIter];
656 RWGltf_GltfBufferView* aBuffView = NULL;
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;
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())
671 const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
672 if (!aWrittenFaces.Add (aGltfFaceList)) // skip repeating faces
677 std::shared_ptr<RWGltf_CafWriter::Mesh> aMeshPtr;
680 if (myDracoParameters.DracoCompression)
682 if (aMeshIndex <= aMeshes.size())
684 aMeshPtr = aMeshes.at(aMeshIndex - 1);
688 aMeshes.push_back(std::make_shared<RWGltf_CafWriter::Mesh>(RWGltf_CafWriter::Mesh()));
689 aMeshPtr = aMeshes.back();
694 for (RWGltf_GltfFaceList::Iterator aGltfFaceIter (*aGltfFaceList); aGltfFaceIter.More() && aPSentryBin.More(); aGltfFaceIter.Next())
696 const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceIter.Value();
698 Handle(RWGltf_GltfFace) anOldGltfFace;
699 if (aWrittenPrimData.Find (aGltfFace->Shape, anOldGltfFace))
703 case RWGltf_GltfArrayType_Position:
705 aGltfFace->NodePos = anOldGltfFace->NodePos;
708 case RWGltf_GltfArrayType_Normal:
710 aGltfFace->NodeNorm = anOldGltfFace->NodeNorm;
713 case RWGltf_GltfArrayType_TCoord0:
715 aGltfFace->NodeUV = anOldGltfFace->NodeUV;
718 case RWGltf_GltfArrayType_Indices:
720 aGltfFace->Indices = anOldGltfFace->Indices;
730 aWrittenPrimData.Bind (aGltfFace->Shape, aGltfFace);
732 for (RWMesh_FaceIterator aFaceIter (aGltfFace->Shape, aGltfFace->Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
736 case RWGltf_GltfArrayType_Position:
738 aGltfFace->NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step
739 saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
742 case RWGltf_GltfArrayType_Normal:
744 saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
747 case RWGltf_GltfArrayType_TCoord0:
749 saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
752 case RWGltf_GltfArrayType_Indices:
754 saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors, aMeshPtr);
763 if (!aBinFile->good())
765 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be written");
770 // add alignment by 4 bytes (might happen on RWGltf_GltfAccessorCompType_UInt16 indices)
771 if (!myDracoParameters.DracoCompression)
773 int64_t aContentLen64 = (int64_t)aBinFile->tellp();
774 while (aContentLen64 % 4 != 0)
776 aBinFile->write(" ", 1);
783 if (!myDracoParameters.DracoCompression)
785 aBuffView->ByteLength = (int64_t)aBinFile->tellp() - aBuffView->ByteOffset;
787 if (!aPSentryBin.More())
795 if (myDracoParameters.DracoCompression)
798 OSD_Timer aDracoTimer;
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)
810 const std::shared_ptr<RWGltf_CafWriter::Mesh>& aCurrentMesh = aMeshes[aMeshInd];
811 if (aCurrentMesh->NodesVec.empty())
816 draco::Mesh aDracoMesh;
817 writeNodesToDracoMesh (aDracoMesh, aCurrentMesh->NodesVec);
818 if (!aCurrentMesh->NormalsVec.empty())
820 writeNormalsToDracoMesh (aDracoMesh, aCurrentMesh->NormalsVec);
822 if (!aCurrentMesh->TexCoordsVec.empty())
824 writeTexCoordsToDracoMesh (aDracoMesh, aCurrentMesh->TexCoordsVec);
826 writeIndicesToDracoMesh (aDracoMesh, aCurrentMesh->IndicesVec);
828 draco::EncoderBuffer anEncoderBuff;
829 draco::Status aStatus = aDracoEncoder.EncodeMeshToBuffer (aDracoMesh, &anEncoderBuff);
832 Message::SendFail (TCollection_AsciiString("Error: mesh cannot be encoded in draco buffer."));
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())
842 Message::SendFail (TCollection_AsciiString("File '") + myBinFileNameFull + "' cannot be written");
846 int64_t aLength = (int64_t)aBinFile->tellp();
847 while (aLength % 4 != 0)
849 aBinFile->write(" ", 1);
853 aBuffViewDraco.ByteLength = aLength - aBuffViewDraco.ByteOffset;
854 myBuffViewsDraco.push_back (aBuffViewDraco);
857 Message::SendInfo (TCollection_AsciiString("Draco compression time: ") + aDracoTimer.ElapsedTime() + " s");
862 && myToEmbedTexturesInGlb)
864 // save unique image textures
865 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
866 aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
868 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
869 if (theLabelFilter != NULL
870 && !theLabelFilter->Contains (aDocNode.Id))
875 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
876 aFaceIter.More(); aFaceIter.Next())
878 if (toSkipFaceMesh (aFaceIter))
883 myMaterialMap->AddGlbImages (*aBinFile, aFaceIter.FaceStyle());
889 if (myBuffViewPos.ByteLength > 0)
891 myBuffViewPos.Id = aBuffViewId++;
893 if (myBuffViewNorm.ByteLength > 0)
895 myBuffViewNorm.Id = aBuffViewId++;
897 if (myBuffViewTextCoord.ByteLength > 0)
899 myBuffViewTextCoord.Id = aBuffViewId++;
901 if (myBuffViewInd.ByteLength > 0)
903 myBuffViewInd.Id = aBuffViewId++;
905 // myMaterialMap->FlushGlbBufferViews() will put image bufferView's IDs at the end of list
907 myBinDataLen64 = aBinFile->tellp();
909 if (!aBinFile->good())
911 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be written");
918 //================================================================
919 // Function : writeJson
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)
928 #ifdef HAVE_RAPIDJSON
931 // write vertex arrays
932 Message_ProgressScope aPSentryBin (theProgress, "Header data", 2);
934 const Standard_Integer aBinDataBufferId = 0;
935 const Standard_Integer aDefSceneId = 0;
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())
943 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created");
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;
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));
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())
967 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
968 if (theLabelFilter != NULL
969 && !theLabelFilter->Contains (aDocNode.Id))
974 bool hasMeshData = false;
975 if (!aDocNode.IsAssembly)
977 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
979 if (!toSkipFaceMesh (aFaceIter))
988 aSceneNodeMap.Add (aDocNode);
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");
998 rapidjson::OStreamWrapper aFileStream (*aGltfContentFile);
999 myWriter.reset (new RWGltf_GltfOStreamWriter (aFileStream));
1001 myWriter->StartObject();
1003 writeAccessors (aSceneNodeMap);
1005 writeAsset (theFileInfo);
1006 writeBufferViews (aBinDataBufferId);
1010 writeImages (aSceneNodeMap);
1011 writeMaterials (aSceneNodeMap);
1012 writeMeshes (aSceneNodeMap);
1015 if (!aPSentryBin.More())
1020 // root nodes indices starting from 0
1021 NCollection_Sequence<Standard_Integer> aSceneRootNodeInds;
1022 writeNodes (theDocument, theRootLabels, theLabelFilter, aSceneNodeMap, aSceneRootNodeInds);
1024 writeScene (aDefSceneId);
1025 writeScenes (aSceneRootNodeInds);
1027 writeTextures (aSceneNodeMap);
1029 myWriter->EndObject();
1033 aGltfContentFile->flush();
1034 if (!aGltfContentFile->good())
1036 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
1039 aGltfContentFile.reset();
1043 int64_t aContentLen64 = (int64_t )aGltfContentFile->tellp() - 20;
1044 while (aContentLen64 % 4 != 0)
1046 aGltfContentFile->write (" ", 1);
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);
1055 const int64_t aFullLen64 = aContentLen64 + 20 + myBinDataLen64 + 8;
1056 if (aFullLen64 < std::numeric_limits<uint32_t>::max())
1059 std::shared_ptr<std::istream> aBinFile = aFileSystem->OpenIStream (myBinFileNameFull, std::ios::in | std::ios::binary);
1060 if (aBinFile.get() == NULL || !aBinFile->good())
1062 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened");
1066 for (; aBinFile->good();)
1068 aBinFile->read (aBuffer, 4096);
1069 const Standard_Integer aReadLen = (Standard_Integer )aBinFile->gcount();
1074 aGltfContentFile->write (aBuffer, aReadLen);
1077 OSD_Path aBinFilePath (myBinFileNameFull);
1078 OSD_File (aBinFilePath).Remove();
1079 if (OSD_File (aBinFilePath).Exists())
1081 Message::SendFail (TCollection_AsciiString ("Unable to remove temporary glTF content file '") + myBinFileNameFull + "'");
1086 Message::SendFail ("glTF file content is too big for binary format");
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);
1096 aGltfContentFile->flush();
1097 if (!aGltfContentFile->good())
1099 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
1102 aGltfContentFile.reset();
1107 (void )theRootLabels;
1108 (void )theLabelFilter;
1111 Message::SendFail ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
1116 // =======================================================================
1117 // function : writeAccessors
1119 // =======================================================================
1120 void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& )
1122 #ifdef HAVE_RAPIDJSON
1123 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
1125 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
1126 myWriter->StartArray();
1128 const RWGltf_GltfArrayType anArrTypes[4] =
1130 RWGltf_GltfArrayType_Position,
1131 RWGltf_GltfArrayType_Normal,
1132 RWGltf_GltfArrayType_TCoord0,
1133 RWGltf_GltfArrayType_Indices
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)
1140 const RWGltf_GltfArrayType anArrType = (RWGltf_GltfArrayType )anArrTypes[aTypeIter];
1141 aWrittenFaces.Clear (false);
1142 for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More(); aBinDataIter.Next())
1144 const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
1145 if (!aWrittenFaces.Add (aGltfFaceList)) // skip repeating faces
1150 for (RWGltf_GltfFaceList::Iterator aFaceIter (*aGltfFaceList); aFaceIter.More(); aFaceIter.Next())
1152 const Handle(RWGltf_GltfFace)& aGltfFace = aFaceIter.Value();
1155 case RWGltf_GltfArrayType_Position:
1157 const int anAccessorId = aGltfFace->NodePos.Id;
1158 if (anAccessorId == RWGltf_GltfAccessor::INVALID_ID
1159 || !aWrittenIds.Add (anAccessorId))
1164 if (anAccessorId != aNbAccessors)
1166 throw Standard_ProgramError ("Internal error: RWGltf_CafWriter::writeAccessors()");
1169 writePositions (*aGltfFace);
1172 case RWGltf_GltfArrayType_Normal:
1174 const int anAccessorId = aGltfFace->NodeNorm.Id;
1175 if (anAccessorId == RWGltf_GltfAccessor::INVALID_ID
1176 || !aWrittenIds.Add (anAccessorId))
1181 if (anAccessorId != aNbAccessors)
1183 throw Standard_ProgramError ("Internal error: RWGltf_CafWriter::writeAccessors()");
1186 writeNormals (*aGltfFace);
1189 case RWGltf_GltfArrayType_TCoord0:
1191 const int anAccessorId = aGltfFace->NodeUV.Id;
1192 if (anAccessorId == RWGltf_GltfAccessor::INVALID_ID
1193 || !aWrittenIds.Add (anAccessorId)
1199 if (anAccessorId != aNbAccessors)
1201 throw Standard_ProgramError ("Internal error: RWGltf_CafWriter::writeAccessors()");
1204 writeTextCoords (*aGltfFace);
1207 case RWGltf_GltfArrayType_Indices:
1209 const int anAccessorId = aGltfFace->Indices.Id;
1210 if (anAccessorId == RWGltf_GltfAccessor::INVALID_ID
1211 || !aWrittenIds.Add (anAccessorId)
1217 if (anAccessorId != aNbAccessors)
1219 throw Standard_ProgramError ("Internal error: RWGltf_CafWriter::writeAccessors()");
1222 writeIndices (*aGltfFace);
1234 myWriter->EndArray();
1238 // =======================================================================
1239 // function : writePositions
1241 // =======================================================================
1242 void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
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)
1251 myWriter->StartObject();
1252 if (!myDracoParameters.DracoCompression)
1254 myWriter->Key ("bufferView");
1255 myWriter->Int (myBuffViewPos.Id);
1256 myWriter->Key ("byteOffset");
1257 myWriter->Int64 (theGltfFace.NodePos.ByteOffset);
1259 myWriter->Key ("componentType");
1260 myWriter->Int (theGltfFace.NodePos.ComponentType);
1261 myWriter->Key ("count");
1262 myWriter->Int64 (theGltfFace.NodePos.Count);
1264 if (theGltfFace.NodePos.BndBox.IsValid())
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();
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();
1280 myWriter->Key ("type");
1281 myWriter->String ("VEC3");
1283 myWriter->EndObject();
1289 // =======================================================================
1290 // function : writeNormals
1292 // =======================================================================
1293 void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
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)
1302 myWriter->StartObject();
1303 if (!myDracoParameters.DracoCompression)
1305 myWriter->Key ("bufferView");
1306 myWriter->Int (myBuffViewNorm.Id);
1307 myWriter->Key ("byteOffset");
1308 myWriter->Int64 (theGltfFace.NodeNorm.ByteOffset);
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
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();
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();
1331 myWriter->Key ("type");
1332 myWriter->String ("VEC3");
1334 myWriter->EndObject();
1340 // =======================================================================
1341 // function : writeTextCoords
1343 // =======================================================================
1344 void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
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)
1353 myWriter->StartObject();
1354 if (!myDracoParameters.DracoCompression)
1356 myWriter->Key ("bufferView");
1357 myWriter->Int (myBuffViewTextCoord.Id);
1358 myWriter->Key ("byteOffset");
1359 myWriter->Int64 (theGltfFace.NodeUV.ByteOffset);
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
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();
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();
1382 myWriter->Key ("type");
1383 myWriter->String ("VEC2");
1385 myWriter->EndObject();
1391 // =======================================================================
1392 // function : writeIndices
1394 // =======================================================================
1395 void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
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)
1404 myWriter->StartObject();
1405 if (!myDracoParameters.DracoCompression)
1407 myWriter->Key("bufferView");
1408 myWriter->Int(myBuffViewInd.Id);
1409 myWriter->Key("byteOffset");
1410 myWriter->Int64(theGltfFace.Indices.ByteOffset);
1412 myWriter->Key ("componentType");
1413 myWriter->Int (theGltfFace.Indices.ComponentType);
1414 myWriter->Key ("count");
1415 myWriter->Int64 (theGltfFace.Indices.Count);
1417 myWriter->Key ("type");
1418 myWriter->String ("SCALAR");
1420 myWriter->EndObject();
1426 // =======================================================================
1427 // function : writeAnimations
1429 // =======================================================================
1430 void RWGltf_CafWriter::writeAnimations()
1432 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAnimations()");
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();
1440 // =======================================================================
1441 // function : writeAsset
1443 // =======================================================================
1444 void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo)
1446 #ifdef HAVE_RAPIDJSON
1447 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAsset()");
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
1456 bool anIsStarted = false;
1457 for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
1461 myWriter->Key ("extras");
1462 myWriter->StartObject();
1465 myWriter->Key (aKeyValueIter.Key().ToCString());
1466 myWriter->String (aKeyValueIter.Value().ToCString());
1470 myWriter->EndObject();
1473 myWriter->EndObject();
1479 // =======================================================================
1480 // function : writeBufferViews
1482 // =======================================================================
1483 void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBufferId)
1485 #ifdef HAVE_RAPIDJSON
1486 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
1488 int aBuffViewId = 0;
1489 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
1490 myWriter->StartArray();
1491 if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1507 if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1523 if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1539 if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1553 if (myDracoParameters.DracoCompression)
1555 for (size_t aBufInd = 0; aBufInd != myBuffViewsDraco.size(); ++aBufInd)
1557 if (myBuffViewsDraco[aBufInd].Id != RWGltf_GltfAccessor::INVALID_ID)
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();
1572 myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
1574 myWriter->EndArray();
1576 (void )theBinDataBufferId;
1580 // =======================================================================
1581 // function : writeBuffers
1583 // =======================================================================
1584 void RWGltf_CafWriter::writeBuffers()
1586 #ifdef HAVE_RAPIDJSON
1587 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBuffers()");
1589 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Buffers));
1590 myWriter->StartArray();
1592 myWriter->StartObject();
1594 myWriter->Key ("byteLength");
1595 myWriter->Int64 (myBinDataLen64);
1598 myWriter->Key ("uri");
1599 myWriter->String (myBinFileNameShort.ToCString());
1602 myWriter->EndObject();
1604 myWriter->EndArray();
1608 // =======================================================================
1609 // function : writeExtensions
1611 // =======================================================================
1612 void RWGltf_CafWriter::writeExtensions()
1614 #ifdef HAVE_RAPIDJSON
1615 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
1617 if (myDracoParameters.DracoCompression)
1619 myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_ExtensionsUsed));
1621 myWriter->StartArray();
1623 myWriter->Key("KHR_draco_mesh_compression");
1625 myWriter->EndArray();
1627 myWriter->Key(RWGltf_GltfRootElementName(RWGltf_GltfRootElement_ExtensionsRequired));
1629 myWriter->StartArray();
1631 myWriter->Key("KHR_draco_mesh_compression");
1633 myWriter->EndArray();
1638 // =======================================================================
1639 // function : writeImages
1641 // =======================================================================
1642 void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1644 #ifdef HAVE_RAPIDJSON
1645 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
1647 // empty RWGltf_GltfRootElement_Images section should NOT be written to avoid validator errors
1649 && myToEmbedTexturesInGlb)
1651 myMaterialMap->FlushGlbImages (myWriter.get());
1655 bool anIsStarted = false;
1656 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1658 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1659 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1661 myMaterialMap->AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1666 myWriter->EndArray();
1670 (void )theSceneNodeMap;
1674 // =======================================================================
1675 // function : writeMaterials
1677 // =======================================================================
1678 void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1680 #ifdef HAVE_RAPIDJSON
1681 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
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())
1687 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1688 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1690 myMaterialMap->AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1695 myWriter->EndArray();
1698 (void )theSceneNodeMap;
1702 // =======================================================================
1703 // function : writePrimArray
1705 // =======================================================================
1706 void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace,
1707 const TCollection_AsciiString& theName,
1708 const int theDracoBufInd,
1709 bool& theToStartPrims)
1711 #ifdef HAVE_RAPIDJSON
1712 if (theToStartPrims)
1714 theToStartPrims = false;
1715 myWriter->StartObject();
1716 if (!theName.IsEmpty())
1718 myWriter->Key ("name");
1719 myWriter->String (theName.ToCString());
1721 myWriter->Key ("primitives");
1722 myWriter->StartArray();
1725 const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (theGltfFace.Style);
1726 myWriter->StartObject();
1728 myWriter->Key ("attributes");
1729 myWriter->StartObject();
1731 if (theGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1733 myWriter->Key ("NORMAL");
1734 myWriter->Int (theGltfFace.NodeNorm.Id);
1736 myWriter->Key ("POSITION");
1737 myWriter->Int (theGltfFace.NodePos.Id);
1738 if (theGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1740 myWriter->Key ("TEXCOORD_0");
1741 myWriter->Int (theGltfFace.NodeUV.Id);
1744 myWriter->EndObject();
1746 myWriter->Key ("indices");
1747 myWriter->Int (theGltfFace.Indices.Id);
1748 if (!aMatId.IsEmpty())
1750 myWriter->Key ("material");
1751 myWriter->Int (aMatId.IntegerValue());
1753 myWriter->Key ("mode");
1754 myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
1756 if (myDracoParameters.DracoCompression)
1758 myWriter->Key("extensions");
1759 myWriter->StartObject();
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();
1769 if (theGltfFace.NodePos.Id != RWGltf_GltfAccessor::INVALID_ID)
1771 myWriter->Key("POSITION");
1772 myWriter->Int(anAttrInd++);
1774 if (theGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1776 myWriter->Key("NORMAL");
1777 myWriter->Int(anAttrInd++);
1779 if (theGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1781 myWriter->Key("TEXCOORD_0");
1782 myWriter->Int(anAttrInd++);
1785 myWriter->EndObject();
1786 myWriter->EndObject();
1788 myWriter->EndObject();
1791 myWriter->EndObject();
1795 (void )theToStartPrims;
1796 (void )theDracoBufInd;
1800 // =======================================================================
1801 // function : writeMeshes
1803 // =======================================================================
1804 void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1806 #ifdef HAVE_RAPIDJSON
1807 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
1809 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
1810 myWriter->StartArray();
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())
1817 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1818 const TCollection_AsciiString aNodeName = formatName (myMeshNameFormat, aDocNode.Label, aDocNode.RefLabel);
1820 bool toStartPrims = true;
1821 Standard_Integer aNbFacesInNode = 0;
1822 aWrittenFaces.Clear (false);
1825 TopoDS_Shape aShape;
1826 if (!XCAFDoc_ShapeTool::GetShape (aDocNode.RefLabel, aShape)
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))
1841 for (RWGltf_GltfFaceList::Iterator aFaceGroupIter (*aGltfFaceList); aFaceGroupIter.More(); aFaceGroupIter.Next())
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))
1849 aDracoBufIndMap.Add (aGltfFace->NodePos.Id, aDracoBufInd);
1852 writePrimArray (*aGltfFace, aNodeName, aDracoBufInd, toStartPrims);
1853 if (aTempDracoBufInd != aDracoBufInd)
1855 aDracoBufInd = aTempDracoBufInd;
1857 if (!myDracoParameters.DracoCompression || aDracoBufIndMap.Size() > aPrevSize)
1865 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
1867 if (toSkipFaceMesh (aFaceIter))
1872 RWGltf_StyledShape aStyledShape (aFaceIter.Face(), aFaceIter.FaceStyle());
1873 const Handle(RWGltf_GltfFaceList)& aGltfFaceList = myBinDataMap.FindFromKey (aStyledShape);
1874 if (!aWrittenFaces.Add (aGltfFaceList))
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))
1885 aDracoBufIndMap.Add(aGltfFace->NodePos.Id, aDracoBufInd);
1888 writePrimArray (*aGltfFace, aNodeName, aDracoBufInd, toStartPrims);
1889 if (aTempDracoBufInd != aDracoBufInd)
1891 aDracoBufInd = aTempDracoBufInd;
1893 if (!myDracoParameters.DracoCompression || aDracoBufIndMap.Size() > aPrevSize)
1902 myWriter->EndArray();
1903 myWriter->EndObject();
1906 myWriter->EndArray();
1908 (void )theSceneNodeMap;
1912 // =======================================================================
1913 // function : writeNodes
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)
1922 #ifdef HAVE_RAPIDJSON
1923 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
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())
1930 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
1931 if (theLabelFilter != NULL
1932 && !theLabelFilter->Contains (aDocNode.Id))
1938 //RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), false);
1939 //if (!aFaceIter.More()) { continue; }
1941 Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
1942 if (aDocExplorer.CurrentDepth() == 0)
1944 // save root node index (starting from 0 not 1)
1945 theSceneRootNodeInds.Append (aNodeIndex - 1);
1949 // Write scene nodes using prepared map for correct order of array members
1950 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
1951 myWriter->StartArray();
1953 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
1955 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1957 myWriter->StartObject();
1959 if (aDocNode.IsAssembly)
1961 myWriter->Key ("children");
1962 myWriter->StartArray();
1964 for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
1966 const TDF_Label& aChildLabel = aChildIter.Value();
1967 if (aChildLabel.IsNull())
1972 const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
1973 Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
1976 myWriter->Int (aChildIdx - 1);
1980 myWriter->EndArray();
1983 if (!aDocNode.LocalTrsf.IsIdentity())
1985 gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
1986 if (aTrsf.Form() != gp_Identity)
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();
1999 RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
2000 if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
2002 aTrsfFormat = hasRotation && hasScale && hasTranslation
2003 ? RWGltf_WriterTrsfFormat_Mat4
2004 : RWGltf_WriterTrsfFormat_TRS;
2007 if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
2009 // write full matrix
2010 Graphic3d_Mat4 aMat4;
2011 aTrsf.GetMat4 (aMat4);
2012 if (!aMat4.IsIdentity())
2014 myWriter->Key ("matrix");
2015 myWriter->StartArray();
2016 for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
2018 for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
2020 myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
2023 myWriter->EndArray();
2026 else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
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();
2040 myWriter->Key ("scale");
2041 myWriter->StartArray();
2042 myWriter->Double (aScaleFactor);
2043 myWriter->Double (aScaleFactor);
2044 myWriter->Double (aScaleFactor);
2045 myWriter->EndArray();
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();
2059 if (!aDocNode.IsAssembly)
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);
2065 myWriter->Key ("mesh");
2066 myWriter->Int (aMeshIdx - 1);
2070 const TCollection_AsciiString aNodeName = formatName (myNodeNameFormat, aDocNode.Label, aDocNode.RefLabel);
2071 if (!aNodeName.IsEmpty())
2073 myWriter->Key ("name");
2074 myWriter->String (aNodeName.ToCString());
2077 myWriter->EndObject();
2079 myWriter->EndArray();
2082 (void )theRootLabels;
2083 (void )theLabelFilter;
2084 (void )theSceneNodeMap;
2085 (void )theSceneRootNodeInds;
2089 // =======================================================================
2090 // function : writeSamplers
2092 // =======================================================================
2093 void RWGltf_CafWriter::writeSamplers()
2095 #ifdef HAVE_RAPIDJSON
2096 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
2097 if (myMaterialMap->NbImages() == 0)
2102 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
2103 myWriter->StartArray();
2105 myWriter->StartObject();
2107 //myWriter->Key ("magFilter");
2108 //myWriter->Int (9729);
2109 //myWriter->Key ("minFilter");
2110 //myWriter->Int (9729);
2112 myWriter->EndObject();
2114 myWriter->EndArray();
2118 // =======================================================================
2119 // function : writeScene
2121 // =======================================================================
2122 void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
2124 #ifdef HAVE_RAPIDJSON
2125 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
2127 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
2128 myWriter->Int (theDefSceneId);
2130 (void )theDefSceneId;
2134 // =======================================================================
2135 // function : writeScenes
2137 // =======================================================================
2138 void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
2140 #ifdef HAVE_RAPIDJSON
2141 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
2143 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
2144 myWriter->StartArray();
2146 myWriter->StartObject();
2147 myWriter->Key ("nodes");
2148 myWriter->StartArray();
2149 for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
2151 myWriter->Int (aRootIter.Value());
2153 myWriter->EndArray();
2154 myWriter->EndObject();
2156 myWriter->EndArray();
2158 (void )theSceneRootNodeInds;
2162 // =======================================================================
2163 // function : writeSkins
2165 // =======================================================================
2166 void RWGltf_CafWriter::writeSkins()
2168 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
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();*/
2176 // =======================================================================
2177 // function : writeTextures
2179 // =======================================================================
2180 void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
2182 #ifdef HAVE_RAPIDJSON
2183 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
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())
2189 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
2190 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
2192 myMaterialMap->AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
2197 myWriter->EndArray();
2200 (void )theSceneNodeMap;