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