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