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 <Poly_Triangulation.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 <TDataStd_Name.hxx>
35 #include <TDF_Tool.hxx>
36 #include <TDocStd_Document.hxx>
37 #include <TopoDS_Compound.hxx>
38 #include <XCAFDoc_DocumentTool.hxx>
39 #include <XCAFDoc_ShapeTool.hxx>
40 #include <XCAFPrs_DocumentExplorer.hxx>
43 #include <RWGltf_GltfOStreamWriter.hxx>
46 IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
50 //! Write three float values.
51 static void writeVec3 (std::ostream& theStream,
52 const gp_XYZ& theVec3)
54 Graphic3d_Vec3 aVec3 (float(theVec3.X()), float(theVec3.Y()), float(theVec3.Z()));
55 theStream.write ((const char* )aVec3.GetData(), sizeof(aVec3));
58 //! Write three float values.
59 static void writeVec3 (std::ostream& theStream,
60 const Graphic3d_Vec3& theVec3)
62 theStream.write ((const char* )theVec3.GetData(), sizeof(theVec3));
65 //! Write two float values.
66 static void writeVec2 (std::ostream& theStream,
69 Graphic3d_Vec2 aVec2 (float(theVec2.X()), float(theVec2.Y()));
70 theStream.write ((const char* )aVec2.GetData(), sizeof(aVec2));
73 //! Write triangle indices.
74 static void writeTriangle32 (std::ostream& theStream,
75 const Graphic3d_Vec3i& theTri)
77 theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
80 //! Write triangle indices.
81 static void writeTriangle16 (std::ostream& theStream,
82 const NCollection_Vec3<uint16_t>& theTri)
84 theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
88 //================================================================
89 // Function : Constructor
91 //================================================================
92 RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
93 Standard_Boolean theIsBinary)
95 myTrsfFormat (RWGltf_WriterTrsfFormat_Compact),
96 myNodeNameFormat(RWMesh_NameFormat_InstanceOrProduct),
97 myMeshNameFormat(RWMesh_NameFormat_Product),
98 myIsBinary (theIsBinary),
99 myIsForcedUVExport (false),
100 myToEmbedTexturesInGlb (true),
101 myToMergeFaces (false),
102 myToSplitIndices16 (false),
105 myCSTrsf.SetOutputLengthUnit (1.0); // meters
106 myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
108 TCollection_AsciiString aFolder, aFileName, aShortFileNameBase, aFileExt;
109 OSD_Path::FolderAndFileFromPath (theFile, aFolder, aFileName);
110 OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
112 myBinFileNameShort = aShortFileNameBase + ".bin" + (myIsBinary ? ".tmp" : "");
113 myBinFileNameFull = !aFolder.IsEmpty() ? aFolder + myBinFileNameShort : myBinFileNameShort;
116 //================================================================
117 // Function : Destructor
119 //================================================================
120 RWGltf_CafWriter::~RWGltf_CafWriter()
125 //================================================================
126 // Function : formatName
128 //================================================================
129 TCollection_AsciiString RWGltf_CafWriter::formatName (RWMesh_NameFormat theFormat,
130 const TDF_Label& theLabel,
131 const TDF_Label& theRefLabel) const
133 return RWMesh::FormatName (theFormat, theLabel, theRefLabel);
136 //================================================================
137 // Function : toSkipFaceMesh
139 //================================================================
140 Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
142 return theFaceIter.IsEmptyMesh();
145 // =======================================================================
146 // function : saveNodes
148 // =======================================================================
149 void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
150 std::ostream& theBinFile,
151 const RWMesh_FaceIterator& theFaceIter,
152 Standard_Integer& theAccessorNb) const
154 if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
156 theGltfFace.NodePos.Id = theAccessorNb++;
157 theGltfFace.NodePos.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewPos.ByteOffset;
158 theGltfFace.NodePos.Type = RWGltf_GltfAccessorLayout_Vec3;
159 theGltfFace.NodePos.ComponentType = RWGltf_GltfAccessorCompType_Float32;
163 const int64_t aPos = theGltfFace.NodePos.ByteOffset + myBuffViewPos.ByteOffset + theGltfFace.NodePos.Count * sizeof(Graphic3d_Vec3);
164 Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
166 theGltfFace.NodePos.Count += theFaceIter.NbNodes();
168 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
169 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
171 gp_XYZ aNode = theFaceIter.NodeTransformed (aNodeIter).XYZ();
172 myCSTrsf.TransformPosition (aNode);
173 theGltfFace.NodePos.BndBox.Add (Graphic3d_Vec3d(aNode.X(), aNode.Y(), aNode.Z()));
174 writeVec3 (theBinFile, aNode);
178 // =======================================================================
179 // function : saveNormals
181 // =======================================================================
182 void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
183 std::ostream& theBinFile,
184 RWMesh_FaceIterator& theFaceIter,
185 Standard_Integer& theAccessorNb) const
187 if (!theFaceIter.HasNormals())
192 if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
194 theGltfFace.NodeNorm.Id = theAccessorNb++;
195 theGltfFace.NodeNorm.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewNorm.ByteOffset;
196 theGltfFace.NodeNorm.Type = RWGltf_GltfAccessorLayout_Vec3;
197 theGltfFace.NodeNorm.ComponentType = RWGltf_GltfAccessorCompType_Float32;
201 const int64_t aPos = theGltfFace.NodeNorm.ByteOffset + myBuffViewNorm.ByteOffset + theGltfFace.NodeNorm.Count * sizeof(Graphic3d_Vec3);
202 Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
204 theGltfFace.NodeNorm.Count += theFaceIter.NbNodes();
206 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
207 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
209 const gp_Dir aNormal = theFaceIter.NormalTransformed (aNodeIter);
210 Graphic3d_Vec3 aVecNormal ((float )aNormal.X(), (float )aNormal.Y(), (float )aNormal.Z());
211 myCSTrsf.TransformNormal (aVecNormal);
212 writeVec3 (theBinFile, aVecNormal);
216 // =======================================================================
217 // function : saveTextCoords
219 // =======================================================================
220 void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
221 std::ostream& theBinFile,
222 const RWMesh_FaceIterator& theFaceIter,
223 Standard_Integer& theAccessorNb) const
225 if (!theFaceIter.HasTexCoords())
229 if (!myIsForcedUVExport)
231 if (theFaceIter.FaceStyle().Material().IsNull())
236 if (RWGltf_GltfMaterialMap::baseColorTexture (theFaceIter.FaceStyle().Material()).IsNull()
237 && theFaceIter.FaceStyle().Material()->PbrMaterial().MetallicRoughnessTexture.IsNull()
238 && theFaceIter.FaceStyle().Material()->PbrMaterial().EmissiveTexture.IsNull()
239 && theFaceIter.FaceStyle().Material()->PbrMaterial().OcclusionTexture.IsNull()
240 && theFaceIter.FaceStyle().Material()->PbrMaterial().NormalTexture.IsNull())
246 if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
248 theGltfFace.NodeUV.Id = theAccessorNb++;
249 theGltfFace.NodeUV.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
250 theGltfFace.NodeUV.Type = RWGltf_GltfAccessorLayout_Vec2;
251 theGltfFace.NodeUV.ComponentType = RWGltf_GltfAccessorCompType_Float32;
255 const int64_t aPos = theGltfFace.NodeUV.ByteOffset + myBuffViewTextCoord.ByteOffset + theGltfFace.NodeUV.Count * sizeof(Graphic3d_Vec2);
256 Standard_ASSERT_RAISE (aPos == (int64_t )theBinFile.tellp(), "wrong offset");
258 theGltfFace.NodeUV.Count += theFaceIter.NbNodes();
260 const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
261 for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
263 gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
264 aTexCoord.SetY (1.0 - aTexCoord.Y());
265 writeVec2 (theBinFile, aTexCoord.XY());
269 // =======================================================================
270 // function : saveIndices
272 // =======================================================================
273 void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
274 std::ostream& theBinFile,
275 const RWMesh_FaceIterator& theFaceIter,
276 Standard_Integer& theAccessorNb)
278 if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
280 theGltfFace.Indices.Id = theAccessorNb++;
281 theGltfFace.Indices.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewInd.ByteOffset;
282 theGltfFace.Indices.Type = RWGltf_GltfAccessorLayout_Scalar;
283 theGltfFace.Indices.ComponentType = theGltfFace.NodePos.Count > std::numeric_limits<uint16_t>::max()
284 ? RWGltf_GltfAccessorCompType_UInt32
285 : RWGltf_GltfAccessorCompType_UInt16;
289 const int64_t aRefPos = (int64_t )theBinFile.tellp();
290 const int64_t aPos = theGltfFace.Indices.ByteOffset
291 + myBuffViewInd.ByteOffset
292 + theGltfFace.Indices.Count * (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt32 ? sizeof(uint32_t) : sizeof(uint16_t));
293 Standard_ASSERT_RAISE (aPos == aRefPos, "wrong offset");
296 const Standard_Integer aNodeFirst = theGltfFace.NbIndexedNodes - theFaceIter.ElemLower();
297 theGltfFace.NbIndexedNodes += theFaceIter.NbNodes();
298 theGltfFace.Indices.Count += theFaceIter.NbTriangles() * 3;
300 const Standard_Integer anElemLower = theFaceIter.ElemLower();
301 const Standard_Integer anElemUpper = theFaceIter.ElemUpper();
302 for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper; ++anElemIter)
304 Poly_Triangle aTri = theFaceIter.TriangleOriented (anElemIter);
305 aTri(1) += aNodeFirst;
306 aTri(2) += aNodeFirst;
307 aTri(3) += aNodeFirst;
308 if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
310 writeTriangle16 (theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
314 writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
319 // =======================================================================
320 // function : Perform
322 // =======================================================================
323 bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
324 const TColStd_IndexedDataMapOfStringString& theFileInfo,
325 const Message_ProgressRange& theProgress)
327 TDF_LabelSequence aRoots;
328 Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (theDocument->Main());
329 aShapeTool->GetFreeShapes (aRoots);
330 return Perform (theDocument, aRoots, NULL, theFileInfo, theProgress);
333 // =======================================================================
334 // function : Perform
336 // =======================================================================
337 bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
338 const TDF_LabelSequence& theRootLabels,
339 const TColStd_MapOfAsciiString* theLabelFilter,
340 const TColStd_IndexedDataMapOfStringString& theFileInfo,
341 const Message_ProgressRange& theProgress)
343 Standard_Real aLengthUnit = 1.;
344 if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument, aLengthUnit))
346 myCSTrsf.SetInputLengthUnit(aLengthUnit);
348 const Standard_Integer aDefSamplerId = 0;
349 myMaterialMap = new RWGltf_GltfMaterialMap (myFile, aDefSamplerId);
350 myMaterialMap->SetDefaultStyle (myDefaultStyle);
352 Message_ProgressScope aPSentry (theProgress, "Writing glTF file", 2);
353 if (!writeBinData (theDocument, theRootLabels, theLabelFilter, aPSentry.Next()))
358 if (!aPSentry.More())
363 return writeJson (theDocument, theRootLabels, theLabelFilter, theFileInfo, aPSentry.Next());
366 // =======================================================================
367 // function : writeBinData
369 // =======================================================================
370 bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument,
371 const TDF_LabelSequence& theRootLabels,
372 const TColStd_MapOfAsciiString* theLabelFilter,
373 const Message_ProgressRange& theProgress)
375 myBuffViewPos.Id = RWGltf_GltfAccessor::INVALID_ID;
376 myBuffViewPos.ByteOffset = 0;
377 myBuffViewPos.ByteLength = 0;
378 myBuffViewPos.ByteStride = 12;
379 myBuffViewPos.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
381 myBuffViewNorm.Id = RWGltf_GltfAccessor::INVALID_ID;
382 myBuffViewNorm.ByteOffset = 0;
383 myBuffViewNorm.ByteLength = 0;
384 myBuffViewNorm.ByteStride = 12;
385 myBuffViewNorm.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
387 myBuffViewTextCoord.Id = RWGltf_GltfAccessor::INVALID_ID;
388 myBuffViewTextCoord.ByteOffset = 0;
389 myBuffViewTextCoord.ByteLength = 0;
390 myBuffViewTextCoord.ByteStride = 8;
391 myBuffViewTextCoord.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
393 myBuffViewInd.Id = RWGltf_GltfAccessor::INVALID_ID;
394 myBuffViewInd.ByteOffset = 0;
395 myBuffViewInd.ByteLength = 0;
396 myBuffViewInd.Target = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
398 myBinDataMap.Clear();
401 const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
402 opencascade::std::shared_ptr<std::ostream> aBinFile = aFileSystem->OpenOStream (myBinFileNameFull, std::ios::out | std::ios::binary);
403 if (aBinFile.get() == NULL
404 || !aBinFile->good())
406 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created");
410 Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4);
411 const RWGltf_GltfArrayType anArrTypes[4] =
413 RWGltf_GltfArrayType_Position,
414 RWGltf_GltfArrayType_Normal,
415 RWGltf_GltfArrayType_TCoord0,
416 RWGltf_GltfArrayType_Indices
420 NCollection_DataMap<XCAFPrs_Style, Handle(RWGltf_GltfFace), XCAFPrs_Style> aMergedFaces;
421 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
422 aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
424 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
425 if (theLabelFilter != NULL
426 && !theLabelFilter->Contains (aDocNode.Id))
431 // transformation will be stored at scene nodes
432 aMergedFaces.Clear (false);
434 RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
437 if (myBinDataMap.Contains (aFaceIter.ExploredShape()))
442 Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList();
443 myBinDataMap.Add (aFaceIter.ExploredShape(), aGltfFaceList);
444 for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
446 if (toSkipFaceMesh (aFaceIter))
451 Handle(RWGltf_GltfFace) aGltfFace;
452 if (!aMergedFaces.Find (aFaceIter.FaceStyle(), aGltfFace))
454 aGltfFace = new RWGltf_GltfFace();
455 aGltfFaceList->Append (aGltfFace);
456 aGltfFace->Shape = aFaceIter.Face();
457 aGltfFace->Style = aFaceIter.FaceStyle();
458 aGltfFace->NbIndexedNodes = aFaceIter.NbNodes();
459 aMergedFaces.Bind (aFaceIter.FaceStyle(), aGltfFace);
461 else if (myToSplitIndices16
462 && aGltfFace->NbIndexedNodes < std::numeric_limits<uint16_t>::max()
463 && (aGltfFace->NbIndexedNodes + aFaceIter.NbNodes()) >= std::numeric_limits<uint16_t>::max())
465 aMergedFaces.UnBind (aFaceIter.FaceStyle());
466 aGltfFace = new RWGltf_GltfFace();
467 aGltfFaceList->Append (aGltfFace);
468 aGltfFace->Shape = aFaceIter.Face();
469 aGltfFace->Style = aFaceIter.FaceStyle();
470 aGltfFace->NbIndexedNodes = aFaceIter.NbNodes();
471 aMergedFaces.Bind (aFaceIter.FaceStyle(), aGltfFace);
475 if (aGltfFace->Shape.ShapeType() != TopAbs_COMPOUND)
477 TopoDS_Shape anOldShape = aGltfFace->Shape;
478 TopoDS_Compound aComp;
479 BRep_Builder().MakeCompound (aComp);
480 BRep_Builder().Add (aComp, anOldShape);
481 aGltfFace->Shape = aComp;
483 BRep_Builder().Add (aGltfFace->Shape, aFaceIter.Face());
484 aGltfFace->NbIndexedNodes += aFaceIter.NbNodes();
490 for (; aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
492 if (toSkipFaceMesh (aFaceIter)
493 || myBinDataMap.Contains (aFaceIter.Face()))
498 Handle(RWGltf_GltfFaceList) aGltfFaceList = new RWGltf_GltfFaceList();
499 Handle(RWGltf_GltfFace) aGltfFace = new RWGltf_GltfFace();
500 aGltfFace->Shape = aFaceIter.Face();
501 aGltfFace->Style = aFaceIter.FaceStyle();
502 aGltfFaceList->Append (aGltfFace);
503 myBinDataMap.Add (aFaceIter.Face(), aGltfFaceList);
508 Standard_Integer aNbAccessors = 0;
509 NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
510 for (Standard_Integer aTypeIter = 0; aTypeIter < 4; ++aTypeIter)
512 const RWGltf_GltfArrayType anArrType = (RWGltf_GltfArrayType )anArrTypes[aTypeIter];
513 RWGltf_GltfBufferView* aBuffView = NULL;
516 case RWGltf_GltfArrayType_Position: aBuffView = &myBuffViewPos; break;
517 case RWGltf_GltfArrayType_Normal: aBuffView = &myBuffViewNorm; break;
518 case RWGltf_GltfArrayType_TCoord0: aBuffView = &myBuffViewTextCoord; break;
519 case RWGltf_GltfArrayType_Indices: aBuffView = &myBuffViewInd; break;
522 aBuffView->ByteOffset = aBinFile->tellp();
523 aWrittenFaces.Clear (false);
524 for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More() && aPSentryBin.More(); aBinDataIter.Next())
526 const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
527 if (!aWrittenFaces.Add (aGltfFaceList)) // skip repeating faces
532 for (RWGltf_GltfFaceList::Iterator aGltfFaceIter (*aGltfFaceList); aGltfFaceIter.More() && aPSentryBin.More(); aGltfFaceIter.Next())
534 const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceIter.Value();
535 for (RWMesh_FaceIterator aFaceIter (aGltfFace->Shape, aGltfFace->Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
539 case RWGltf_GltfArrayType_Position:
541 aGltfFace->NbIndexedNodes = 0; // reset to zero before RWGltf_GltfArrayType_Indices step
542 saveNodes (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
545 case RWGltf_GltfArrayType_Normal:
547 saveNormals (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
550 case RWGltf_GltfArrayType_TCoord0:
552 saveTextCoords (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
555 case RWGltf_GltfArrayType_Indices:
557 saveIndices (*aGltfFace, *aBinFile, aFaceIter, aNbAccessors);
566 if (!aBinFile->good())
568 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be written");
573 // add alignment by 4 bytes (might happen on RWGltf_GltfAccessorCompType_UInt16 indices)
574 int64_t aContentLen64 = (int64_t)aBinFile->tellp();
575 while (aContentLen64 % 4 != 0)
577 aBinFile->write (" ", 1);
583 aBuffView->ByteLength = (int64_t )aBinFile->tellp() - aBuffView->ByteOffset;
584 if (!aPSentryBin.More())
593 && myToEmbedTexturesInGlb)
595 // save unique image textures
596 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
597 aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
599 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
600 if (theLabelFilter != NULL
601 && !theLabelFilter->Contains (aDocNode.Id))
606 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
607 aFaceIter.More(); aFaceIter.Next())
609 if (toSkipFaceMesh (aFaceIter))
614 myMaterialMap->AddGlbImages (*aBinFile, aFaceIter.FaceStyle());
620 if (myBuffViewPos.ByteLength > 0)
622 myBuffViewPos.Id = aBuffViewId++;
624 if (myBuffViewNorm.ByteLength > 0)
626 myBuffViewNorm.Id = aBuffViewId++;
628 if (myBuffViewTextCoord.ByteLength > 0)
630 myBuffViewTextCoord.Id = aBuffViewId++;
632 if (myBuffViewInd.ByteLength > 0)
634 myBuffViewInd.Id = aBuffViewId++;
636 // myMaterialMap->FlushGlbBufferViews() will put image bufferView's IDs at the end of list
638 myBinDataLen64 = aBinFile->tellp();
640 if (!aBinFile->good())
642 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be written");
649 //================================================================
650 // Function : writeJson
652 //================================================================
653 bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)& theDocument,
654 const TDF_LabelSequence& theRootLabels,
655 const TColStd_MapOfAsciiString* theLabelFilter,
656 const TColStd_IndexedDataMapOfStringString& theFileInfo,
657 const Message_ProgressRange& theProgress)
659 #ifdef HAVE_RAPIDJSON
662 // write vertex arrays
663 Message_ProgressScope aPSentryBin (theProgress, "Header data", 2);
665 const Standard_Integer aBinDataBufferId = 0;
666 const Standard_Integer aDefSceneId = 0;
668 const TCollection_AsciiString aFileNameGltf = myFile;
669 const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
670 opencascade::std::shared_ptr<std::ostream> aGltfContentFile = aFileSystem->OpenOStream (aFileNameGltf, std::ios::out | std::ios::binary);
671 if (aGltfContentFile.get() == NULL
672 || !aGltfContentFile->good())
674 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created");
679 const char* aMagic = "glTF";
680 uint32_t aVersion = 2;
681 uint32_t aLength = 0;
682 uint32_t aContentLength = 0;
683 uint32_t aContentType = 0x4E4F534A;
685 aGltfContentFile->write (aMagic, 4);
686 aGltfContentFile->write ((const char* )&aVersion, sizeof(aVersion));
687 aGltfContentFile->write ((const char* )&aLength, sizeof(aLength));
688 aGltfContentFile->write ((const char* )&aContentLength, sizeof(aContentLength));
689 aGltfContentFile->write ((const char* )&aContentType, sizeof(aContentType));
692 // Prepare an indexed map of scene nodes (without assemblies) in correct order.
693 // Note: this is also order of meshes in glTF document array.
694 RWGltf_GltfSceneNodeMap aSceneNodeMap;
695 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
696 aDocExplorer.More(); aDocExplorer.Next())
698 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
699 if (theLabelFilter != NULL
700 && !theLabelFilter->Contains (aDocNode.Id))
705 bool hasMeshData = false;
706 if (!aDocNode.IsAssembly)
708 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
710 if (!toSkipFaceMesh (aFaceIter))
719 aSceneNodeMap.Add (aDocNode);
723 // glTF disallows empty meshes / primitive arrays
724 const TCollection_AsciiString aNodeName = formatName (RWMesh_NameFormat_ProductOrInstance, aDocNode.Label, aDocNode.RefLabel);
725 Message::SendWarning (TCollection_AsciiString("RWGltf_CafWriter skipped node '") + aNodeName + "' without triangulation data");
729 rapidjson::OStreamWrapper aFileStream (*aGltfContentFile);
730 myWriter.reset (new RWGltf_GltfOStreamWriter (aFileStream));
732 myWriter->StartObject();
734 writeAccessors (aSceneNodeMap);
736 writeAsset (theFileInfo);
737 writeBufferViews (aBinDataBufferId);
741 writeImages (aSceneNodeMap);
742 writeMaterials (aSceneNodeMap);
743 writeMeshes (aSceneNodeMap);
746 if (!aPSentryBin.More())
751 // root nodes indices starting from 0
752 NCollection_Sequence<Standard_Integer> aSceneRootNodeInds;
753 writeNodes (theDocument, theRootLabels, theLabelFilter, aSceneNodeMap, aSceneRootNodeInds);
755 writeScene (aDefSceneId);
756 writeScenes (aSceneRootNodeInds);
758 writeTextures (aSceneNodeMap);
760 myWriter->EndObject();
764 aGltfContentFile->flush();
765 if (!aGltfContentFile->good())
767 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
770 aGltfContentFile.reset();
774 int64_t aContentLen64 = (int64_t )aGltfContentFile->tellp() - 20;
775 while (aContentLen64 % 4 != 0)
777 aGltfContentFile->write (" ", 1);
781 const uint32_t aBinLength = (uint32_t )myBinDataLen64;
782 const uint32_t aBinType = 0x004E4942;
783 aGltfContentFile->write ((const char*)&aBinLength, 4);
784 aGltfContentFile->write ((const char*)&aBinType, 4);
786 const int64_t aFullLen64 = aContentLen64 + 20 + myBinDataLen64 + 8;
787 if (aFullLen64 < std::numeric_limits<uint32_t>::max())
790 opencascade::std::shared_ptr<std::istream> aBinFile = aFileSystem->OpenIStream (myBinFileNameFull, std::ios::in | std::ios::binary);
791 if (aBinFile.get() == NULL || !aBinFile->good())
793 Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened");
797 for (; aBinFile->good();)
799 aBinFile->read (aBuffer, 4096);
800 const Standard_Integer aReadLen = (Standard_Integer )aBinFile->gcount();
805 aGltfContentFile->write (aBuffer, aReadLen);
808 OSD_Path aBinFilePath (myBinFileNameFull);
809 OSD_File (aBinFilePath).Remove();
810 if (OSD_File (aBinFilePath).Exists())
812 Message::SendFail (TCollection_AsciiString ("Unable to remove temporary glTF content file '") + myBinFileNameFull + "'");
817 Message::SendFail ("glTF file content is too big for binary format");
821 const uint32_t aLength = (uint32_t )aFullLen64;
822 const uint32_t aContentLength = (uint32_t )aContentLen64;
823 aGltfContentFile->seekp (8);
824 aGltfContentFile->write ((const char* )&aLength, 4);
825 aGltfContentFile->write ((const char* )&aContentLength, 4);
827 aGltfContentFile->flush();
828 if (!aGltfContentFile->good())
830 Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
833 aGltfContentFile.reset();
838 (void )theRootLabels;
839 (void )theLabelFilter;
842 Message::SendFail ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
847 // =======================================================================
848 // function : writeAccessors
850 // =======================================================================
851 void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& )
853 #ifdef HAVE_RAPIDJSON
854 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
856 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
857 myWriter->StartArray();
859 const RWGltf_GltfArrayType anArrTypes[4] =
861 RWGltf_GltfArrayType_Position,
862 RWGltf_GltfArrayType_Normal,
863 RWGltf_GltfArrayType_TCoord0,
864 RWGltf_GltfArrayType_Indices
866 NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
867 for (Standard_Integer aTypeIter = 0; aTypeIter < 4; ++aTypeIter)
869 const RWGltf_GltfArrayType anArrType = (RWGltf_GltfArrayType )anArrTypes[aTypeIter];
870 aWrittenFaces.Clear (false);
871 for (ShapeToGltfFaceMap::Iterator aBinDataIter (myBinDataMap); aBinDataIter.More(); aBinDataIter.Next())
873 const Handle(RWGltf_GltfFaceList)& aGltfFaceList = aBinDataIter.Value();
874 if (!aWrittenFaces.Add (aGltfFaceList)) // skip repeating faces
879 for (RWGltf_GltfFaceList::Iterator aFaceIter (*aGltfFaceList); aFaceIter.More(); aFaceIter.Next())
881 const Handle(RWGltf_GltfFace)& aGltfFace = aFaceIter.Value();
884 case RWGltf_GltfArrayType_Position:
886 writePositions (*aGltfFace);
889 case RWGltf_GltfArrayType_Normal:
891 writeNormals (*aGltfFace);
894 case RWGltf_GltfArrayType_TCoord0:
896 writeTextCoords (*aGltfFace);
899 case RWGltf_GltfArrayType_Indices:
901 writeIndices (*aGltfFace);
913 myWriter->EndArray();
917 // =======================================================================
918 // function : writePositions
920 // =======================================================================
921 void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
923 #ifdef HAVE_RAPIDJSON
924 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writePositions()");
925 if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
930 myWriter->StartObject();
931 myWriter->Key ("bufferView");
932 myWriter->Int (myBuffViewPos.Id);
933 myWriter->Key ("byteOffset");
934 myWriter->Int64 (theGltfFace.NodePos.ByteOffset);
935 myWriter->Key ("componentType");
936 myWriter->Int (theGltfFace.NodePos.ComponentType);
937 myWriter->Key ("count");
938 myWriter->Int64 (theGltfFace.NodePos.Count);
940 if (theGltfFace.NodePos.BndBox.IsValid())
942 myWriter->Key ("max");
943 myWriter->StartArray();
944 myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().x());
945 myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().y());
946 myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().z());
947 myWriter->EndArray();
949 myWriter->Key("min");
950 myWriter->StartArray();
951 myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().x());
952 myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().y());
953 myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().z());
954 myWriter->EndArray();
956 myWriter->Key ("type");
957 myWriter->String ("VEC3");
959 myWriter->EndObject();
965 // =======================================================================
966 // function : writeNormals
968 // =======================================================================
969 void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
971 #ifdef HAVE_RAPIDJSON
972 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNormals()");
973 if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
978 myWriter->StartObject();
979 myWriter->Key ("bufferView");
980 myWriter->Int (myBuffViewNorm.Id);
981 myWriter->Key ("byteOffset");
982 myWriter->Int64 (theGltfFace.NodeNorm.ByteOffset);
983 myWriter->Key ("componentType");
984 myWriter->Int (theGltfFace.NodeNorm.ComponentType);
985 myWriter->Key ("count");
986 myWriter->Int64 (theGltfFace.NodeNorm.Count);
987 // min/max values are optional, and not very useful for normals - skip them
989 myWriter->Key ("max");
990 myWriter->StartArray();
991 myWriter->Double (1.0);
992 myWriter->Double (1.0);
993 myWriter->Double (1.0);
994 myWriter->EndArray();
997 myWriter->Key ("min");
998 myWriter->StartArray();
999 myWriter->Double (0.0);
1000 myWriter->Double (0.0);
1001 myWriter->Double (0.0);
1002 myWriter->EndArray();
1004 myWriter->Key ("type");
1005 myWriter->String ("VEC3");
1007 myWriter->EndObject();
1013 // =======================================================================
1014 // function : writeTextCoords
1016 // =======================================================================
1017 void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
1019 #ifdef HAVE_RAPIDJSON
1020 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextCoords()");
1021 if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
1026 myWriter->StartObject();
1027 myWriter->Key ("bufferView");
1028 myWriter->Int (myBuffViewTextCoord.Id);
1029 myWriter->Key ("byteOffset");
1030 myWriter->Int64 (theGltfFace.NodeUV.ByteOffset);
1031 myWriter->Key ("componentType");
1032 myWriter->Int (theGltfFace.NodeUV.ComponentType);
1033 myWriter->Key ("count");
1034 myWriter->Int64 (theGltfFace.NodeUV.Count);
1035 // min/max values are optional, and not very useful for UV coordinates - skip them
1037 myWriter->Key ("max");
1038 myWriter->StartArray();
1039 myWriter->Double (1.0);
1040 myWriter->Double (1.0);
1041 myWriter->Double (1.0);
1042 myWriter->EndArray();
1045 myWriter->Key ("min");
1046 myWriter->StartArray();
1047 myWriter->Double (0.0);
1048 myWriter->Double (0.0);
1049 myWriter->Double (0.0);
1050 myWriter->EndArray();
1052 myWriter->Key ("type");
1053 myWriter->String ("VEC2");
1055 myWriter->EndObject();
1061 // =======================================================================
1062 // function : writeIndices
1064 // =======================================================================
1065 void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
1067 #ifdef HAVE_RAPIDJSON
1068 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeIndices()");
1069 if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
1074 myWriter->StartObject();
1075 myWriter->Key ("bufferView");
1076 myWriter->Int (myBuffViewInd.Id);
1077 myWriter->Key ("byteOffset");
1078 myWriter->Int64 (theGltfFace.Indices.ByteOffset);
1079 myWriter->Key ("componentType");
1080 myWriter->Int (theGltfFace.Indices.ComponentType);
1081 myWriter->Key ("count");
1082 myWriter->Int64 (theGltfFace.Indices.Count);
1084 myWriter->Key ("type");
1085 myWriter->String ("SCALAR");
1087 myWriter->EndObject();
1093 // =======================================================================
1094 // function : writeAnimations
1096 // =======================================================================
1097 void RWGltf_CafWriter::writeAnimations()
1099 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAnimations()");
1101 // This section should be skipped if it doesn't contain any information but not be empty
1102 //myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Animations));
1103 //myWriter->StartArray();
1104 //myWriter->EndArray();
1107 // =======================================================================
1108 // function : writeAsset
1110 // =======================================================================
1111 void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo)
1113 #ifdef HAVE_RAPIDJSON
1114 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAsset()");
1116 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Asset));
1117 myWriter->StartObject();
1118 myWriter->Key ("generator");
1119 myWriter->String ("Open CASCADE Technology [dev.opencascade.org]");
1120 myWriter->Key ("version");
1121 myWriter->String ("2.0"); // glTF format version
1123 bool anIsStarted = false;
1124 for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
1128 myWriter->Key ("extras");
1129 myWriter->StartObject();
1132 myWriter->Key (aKeyValueIter.Key().ToCString());
1133 myWriter->String (aKeyValueIter.Value().ToCString());
1137 myWriter->EndObject();
1140 myWriter->EndObject();
1146 // =======================================================================
1147 // function : writeBufferViews
1149 // =======================================================================
1150 void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBufferId)
1152 #ifdef HAVE_RAPIDJSON
1153 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
1155 int aBuffViewId = 0;
1156 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
1157 myWriter->StartArray();
1158 if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
1161 myWriter->StartObject();
1162 myWriter->Key ("buffer");
1163 myWriter->Int (theBinDataBufferId);
1164 myWriter->Key ("byteLength");
1165 myWriter->Int64 (myBuffViewPos.ByteLength);
1166 myWriter->Key ("byteOffset");
1167 myWriter->Int64 (myBuffViewPos.ByteOffset);
1168 myWriter->Key ("byteStride");
1169 myWriter->Int64 (myBuffViewPos.ByteStride);
1170 myWriter->Key ("target");
1171 myWriter->Int (myBuffViewPos.Target);
1172 myWriter->EndObject();
1174 if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1177 myWriter->StartObject();
1178 myWriter->Key ("buffer");
1179 myWriter->Int (theBinDataBufferId);
1180 myWriter->Key ("byteLength");
1181 myWriter->Int64 (myBuffViewNorm.ByteLength);
1182 myWriter->Key ("byteOffset");
1183 myWriter->Int64 (myBuffViewNorm.ByteOffset);
1184 myWriter->Key ("byteStride");
1185 myWriter->Int64 (myBuffViewNorm.ByteStride);
1186 myWriter->Key ("target");
1187 myWriter->Int (myBuffViewNorm.Target);
1188 myWriter->EndObject();
1190 if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
1193 myWriter->StartObject();
1194 myWriter->Key ("buffer");
1195 myWriter->Int (theBinDataBufferId);
1196 myWriter->Key ("byteLength");
1197 myWriter->Int64 (myBuffViewTextCoord.ByteLength);
1198 myWriter->Key ("byteOffset");
1199 myWriter->Int64 (myBuffViewTextCoord.ByteOffset);
1200 myWriter->Key ("byteStride");
1201 myWriter->Int64 (myBuffViewTextCoord.ByteStride);
1202 myWriter->Key ("target");
1203 myWriter->Int (myBuffViewTextCoord.Target);
1204 myWriter->EndObject();
1206 if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
1209 myWriter->StartObject();
1210 myWriter->Key ("buffer");
1211 myWriter->Int (theBinDataBufferId);
1212 myWriter->Key ("byteLength");
1213 myWriter->Int64 (myBuffViewInd.ByteLength);
1214 myWriter->Key ("byteOffset");
1215 myWriter->Int64 (myBuffViewInd.ByteOffset);
1216 myWriter->Key ("target");
1217 myWriter->Int (myBuffViewInd.Target);
1218 myWriter->EndObject();
1221 myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
1223 myWriter->EndArray();
1225 (void )theBinDataBufferId;
1229 // =======================================================================
1230 // function : writeBuffers
1232 // =======================================================================
1233 void RWGltf_CafWriter::writeBuffers()
1235 #ifdef HAVE_RAPIDJSON
1236 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBuffers()");
1238 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Buffers));
1239 myWriter->StartArray();
1241 myWriter->StartObject();
1243 myWriter->Key ("byteLength");
1244 myWriter->Int64 (myBinDataLen64);
1247 myWriter->Key ("uri");
1248 myWriter->String (myBinFileNameShort.ToCString());
1251 myWriter->EndObject();
1253 myWriter->EndArray();
1257 // =======================================================================
1258 // function : writeExtensions
1260 // =======================================================================
1261 void RWGltf_CafWriter::writeExtensions()
1263 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
1266 // =======================================================================
1267 // function : writeImages
1269 // =======================================================================
1270 void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1272 #ifdef HAVE_RAPIDJSON
1273 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
1275 // empty RWGltf_GltfRootElement_Images section should NOT be written to avoid validator errors
1277 && myToEmbedTexturesInGlb)
1279 myMaterialMap->FlushGlbImages (myWriter.get());
1283 bool anIsStarted = false;
1284 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1286 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1287 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1289 myMaterialMap->AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1294 myWriter->EndArray();
1298 (void )theSceneNodeMap;
1302 // =======================================================================
1303 // function : writeMaterials
1305 // =======================================================================
1306 void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1308 #ifdef HAVE_RAPIDJSON
1309 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
1311 // empty RWGltf_GltfRootElement_Materials section should NOT be written to avoid validator errors
1312 bool anIsStarted = false;
1313 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1315 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1316 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1318 myMaterialMap->AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1323 myWriter->EndArray();
1326 (void )theSceneNodeMap;
1330 // =======================================================================
1331 // function : writePrimArray
1333 // =======================================================================
1334 void RWGltf_CafWriter::writePrimArray (const RWGltf_GltfFace& theGltfFace,
1335 const TCollection_AsciiString& theName,
1336 bool& theToStartPrims)
1338 #ifdef HAVE_RAPIDJSON
1339 if (theToStartPrims)
1341 theToStartPrims = false;
1342 myWriter->StartObject();
1343 if (!theName.IsEmpty())
1345 myWriter->Key ("name");
1346 myWriter->String (theName.ToCString());
1348 myWriter->Key ("primitives");
1349 myWriter->StartArray();
1352 const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (theGltfFace.Style);
1353 myWriter->StartObject();
1355 myWriter->Key ("attributes");
1356 myWriter->StartObject();
1358 if (theGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1360 myWriter->Key ("NORMAL");
1361 myWriter->Int (theGltfFace.NodeNorm.Id);
1363 myWriter->Key ("POSITION");
1364 myWriter->Int (theGltfFace.NodePos.Id);
1365 if (theGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1367 myWriter->Key ("TEXCOORD_0");
1368 myWriter->Int (theGltfFace.NodeUV.Id);
1371 myWriter->EndObject();
1373 myWriter->Key ("indices");
1374 myWriter->Int (theGltfFace.Indices.Id);
1375 if (!aMatId.IsEmpty())
1377 myWriter->Key ("material");
1378 myWriter->Int (aMatId.IntegerValue());
1380 myWriter->Key ("mode");
1381 myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
1383 myWriter->EndObject();
1387 (void )theToStartPrims;
1391 // =======================================================================
1392 // function : writeMeshes
1394 // =======================================================================
1395 void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1397 #ifdef HAVE_RAPIDJSON
1398 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
1400 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
1401 myWriter->StartArray();
1403 NCollection_Map<Handle(RWGltf_GltfFaceList)> aWrittenFaces;
1404 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1406 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1407 const TCollection_AsciiString aNodeName = formatName (myMeshNameFormat, aDocNode.Label, aDocNode.RefLabel);
1409 bool toStartPrims = true;
1410 Standard_Integer aNbFacesInNode = 0;
1411 aWrittenFaces.Clear (false);
1414 TopoDS_Shape aShape;
1415 if (!XCAFDoc_ShapeTool::GetShape (aDocNode.RefLabel, aShape)
1421 Handle(RWGltf_GltfFaceList) aGltfFaceList;
1422 aShape.Location (TopLoc_Location());
1423 myBinDataMap.FindFromKey (aShape, aGltfFaceList);
1424 if (!aWrittenFaces.Add (aGltfFaceList))
1429 for (RWGltf_GltfFaceList::Iterator aFaceGroupIter (*aGltfFaceList); aFaceGroupIter.More(); aFaceGroupIter.Next())
1431 const Handle(RWGltf_GltfFace)& aGltfFace = aFaceGroupIter.Value();
1432 writePrimArray (*aGltfFace, aNodeName, toStartPrims);
1437 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
1439 if (toSkipFaceMesh (aFaceIter))
1444 const Handle(RWGltf_GltfFaceList)& aGltfFaceList = myBinDataMap.FindFromKey (aFaceIter.Face());
1445 if (!aWrittenFaces.Add (aGltfFaceList))
1450 const Handle(RWGltf_GltfFace)& aGltfFace = aGltfFaceList->First();
1451 writePrimArray (*aGltfFace, aNodeName, toStartPrims);
1457 myWriter->EndArray();
1458 myWriter->EndObject();
1461 myWriter->EndArray();
1463 (void )theSceneNodeMap;
1467 // =======================================================================
1468 // function : writeNodes
1470 // =======================================================================
1471 void RWGltf_CafWriter::writeNodes (const Handle(TDocStd_Document)& theDocument,
1472 const TDF_LabelSequence& theRootLabels,
1473 const TColStd_MapOfAsciiString* theLabelFilter,
1474 const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1475 NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1477 #ifdef HAVE_RAPIDJSON
1478 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
1480 // Prepare full indexed map of scene nodes in correct order.
1481 RWGltf_GltfSceneNodeMap aSceneNodeMapWithChildren; // indexes starting from 1
1482 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_None);
1483 aDocExplorer.More(); aDocExplorer.Next())
1485 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
1486 if (theLabelFilter != NULL
1487 && !theLabelFilter->Contains (aDocNode.Id))
1493 //RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), false);
1494 //if (!aFaceIter.More()) { continue; }
1496 Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
1497 if (aDocExplorer.CurrentDepth() == 0)
1499 // save root node index (starting from 0 not 1)
1500 theSceneRootNodeInds.Append (aNodeIndex - 1);
1504 // Write scene nodes using prepared map for correct order of array members
1505 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
1506 myWriter->StartArray();
1508 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
1510 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1512 myWriter->StartObject();
1514 if (aDocNode.IsAssembly)
1516 myWriter->Key ("children");
1517 myWriter->StartArray();
1519 for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
1521 const TDF_Label& aChildLabel = aChildIter.Value();
1522 if (aChildLabel.IsNull())
1527 const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
1528 Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
1531 myWriter->Int (aChildIdx - 1);
1535 myWriter->EndArray();
1538 if (!aDocNode.LocalTrsf.IsIdentity())
1540 gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
1541 if (aTrsf.Form() != gp_Identity)
1543 myCSTrsf.TransformTransformation (aTrsf);
1544 const gp_Quaternion aQuaternion = aTrsf.GetRotation();
1545 const bool hasRotation = Abs (aQuaternion.X()) > gp::Resolution()
1546 || Abs (aQuaternion.Y()) > gp::Resolution()
1547 || Abs (aQuaternion.Z()) > gp::Resolution()
1548 || Abs (aQuaternion.W() - 1.0) > gp::Resolution();
1549 const Standard_Real aScaleFactor = aTrsf.ScaleFactor();
1550 const bool hasScale = Abs (aScaleFactor - 1.0) > Precision::Confusion();
1551 const gp_XYZ& aTranslPart = aTrsf.TranslationPart();
1552 const bool hasTranslation = aTranslPart.SquareModulus() > gp::Resolution();
1554 RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
1555 if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
1557 aTrsfFormat = hasRotation && hasScale && hasTranslation
1558 ? RWGltf_WriterTrsfFormat_Mat4
1559 : RWGltf_WriterTrsfFormat_TRS;
1562 if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
1564 // write full matrix
1565 Graphic3d_Mat4 aMat4;
1566 aTrsf.GetMat4 (aMat4);
1567 if (!aMat4.IsIdentity())
1569 myWriter->Key ("matrix");
1570 myWriter->StartArray();
1571 for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
1573 for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
1575 myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
1578 myWriter->EndArray();
1581 else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
1585 myWriter->Key ("rotation");
1586 myWriter->StartArray();
1587 myWriter->Double (aQuaternion.X());
1588 myWriter->Double (aQuaternion.Y());
1589 myWriter->Double (aQuaternion.Z());
1590 myWriter->Double (aQuaternion.W());
1591 myWriter->EndArray();
1595 myWriter->Key ("scale");
1596 myWriter->StartArray();
1597 myWriter->Double (aScaleFactor);
1598 myWriter->Double (aScaleFactor);
1599 myWriter->Double (aScaleFactor);
1600 myWriter->EndArray();
1604 myWriter->Key ("translation");
1605 myWriter->StartArray();
1606 myWriter->Double (aTranslPart.X());
1607 myWriter->Double (aTranslPart.Y());
1608 myWriter->Double (aTranslPart.Z());
1609 myWriter->EndArray();
1614 if (!aDocNode.IsAssembly)
1616 // Mesh order of current node is equal to order of this node in scene nodes map
1617 Standard_Integer aMeshIdx = theSceneNodeMap.FindIndex (aDocNode.Id);
1620 myWriter->Key ("mesh");
1621 myWriter->Int (aMeshIdx - 1);
1625 const TCollection_AsciiString aNodeName = formatName (myNodeNameFormat, aDocNode.Label, aDocNode.RefLabel);
1626 if (!aNodeName.IsEmpty())
1628 myWriter->Key ("name");
1629 myWriter->String (aNodeName.ToCString());
1632 myWriter->EndObject();
1634 myWriter->EndArray();
1637 (void )theRootLabels;
1638 (void )theLabelFilter;
1639 (void )theSceneNodeMap;
1640 (void )theSceneRootNodeInds;
1644 // =======================================================================
1645 // function : writeSamplers
1647 // =======================================================================
1648 void RWGltf_CafWriter::writeSamplers()
1650 #ifdef HAVE_RAPIDJSON
1651 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
1652 if (myMaterialMap->NbImages() == 0)
1657 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
1658 myWriter->StartArray();
1660 myWriter->StartObject();
1662 //myWriter->Key ("magFilter");
1663 //myWriter->Int (9729);
1664 //myWriter->Key ("minFilter");
1665 //myWriter->Int (9729);
1667 myWriter->EndObject();
1669 myWriter->EndArray();
1673 // =======================================================================
1674 // function : writeScene
1676 // =======================================================================
1677 void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
1679 #ifdef HAVE_RAPIDJSON
1680 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
1682 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
1683 myWriter->Int (theDefSceneId);
1685 (void )theDefSceneId;
1689 // =======================================================================
1690 // function : writeScenes
1692 // =======================================================================
1693 void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1695 #ifdef HAVE_RAPIDJSON
1696 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
1698 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
1699 myWriter->StartArray();
1701 myWriter->StartObject();
1702 myWriter->Key ("nodes");
1703 myWriter->StartArray();
1704 for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
1706 myWriter->Int (aRootIter.Value());
1708 myWriter->EndArray();
1709 myWriter->EndObject();
1711 myWriter->EndArray();
1713 (void )theSceneRootNodeInds;
1717 // =======================================================================
1718 // function : writeSkins
1720 // =======================================================================
1721 void RWGltf_CafWriter::writeSkins()
1723 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
1725 // This section should be skipped if it doesn't contain any information but not be empty
1726 /*myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Skins));
1727 myWriter->StartArray();
1728 myWriter->EndArray();*/
1731 // =======================================================================
1732 // function : writeTextures
1734 // =======================================================================
1735 void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
1737 #ifdef HAVE_RAPIDJSON
1738 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
1740 // empty RWGltf_GltfRootElement_Textures section should not be written to avoid validator errors
1741 bool anIsStarted = false;
1742 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1744 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1745 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1747 myMaterialMap->AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1752 myWriter->EndArray();
1755 (void )theSceneNodeMap;