0031618: Data Exchange, RWGltf_CafWriter - exporting some models produces glTF files...
[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);
b011420f 1229 {
1230 RWMesh_FaceIterator aFaceIter(aDocNode.RefLabel, TopLoc_Location(), false);
1231 if (!aFaceIter.More())
1232 {
1233 Message::SendWarning (TCollection_AsciiString("RWGltf_CafWriter skipped node '") + aNodeName + "' without triangulation data");
1234 continue;
1235 }
1236 }
01b2f506 1237 myWriter->StartObject();
1238 myWriter->Key ("name");
1239 myWriter->String (aNodeName.ToCString());
1240 myWriter->Key ("primitives");
1241 myWriter->StartArray();
1242
1243 Standard_Integer aNbFacesInNode = 0;
1244 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
1245 {
1246 if (toSkipFaceMesh (aFaceIter))
1247 {
1248 continue;
1249 }
1250
1251 const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
1252 const TCollection_AsciiString aMatId = theMaterialMap.FindMaterial (aFaceIter.FaceStyle());
1253 myWriter->StartObject();
1254 {
1255 myWriter->Key ("attributes");
1256 myWriter->StartObject();
1257 {
1258 if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
1259 {
1260 myWriter->Key ("NORMAL");
1261 myWriter->Int (aGltfFace.NodeNorm.Id);
1262 }
1263 myWriter->Key ("POSITION");
1264 myWriter->Int (aGltfFace.NodePos.Id);
1265 if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
1266 {
1267 myWriter->Key ("TEXCOORD_0");
1268 myWriter->Int (aGltfFace.NodeUV.Id);
1269 }
1270 }
1271 myWriter->EndObject();
1272
1273 myWriter->Key ("indices");
1274 myWriter->Int (aGltfFace.Indices.Id);
1275 if (!aMatId.IsEmpty())
1276 {
1277 myWriter->Key ("material");
1278 myWriter->Int (aMatId.IntegerValue());
1279 }
1280 myWriter->Key ("mode");
1281 myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
1282 }
1283 myWriter->EndObject();
1284 }
1285 myWriter->EndArray();
1286 myWriter->EndObject();
1287 }
1288 myWriter->EndArray();
1289#else
1290 (void )theSceneNodeMap;
1291 (void )theMaterialMap;
1292#endif
1293}
1294
1295// =======================================================================
1296// function : writeNodes
1297// purpose :
1298// =======================================================================
1299void RWGltf_CafWriter::writeNodes (const Handle(TDocStd_Document)& theDocument,
1300 const TDF_LabelSequence& theRootLabels,
1301 const TColStd_MapOfAsciiString* theLabelFilter,
1302 const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1303 NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1304{
1305#ifdef HAVE_RAPIDJSON
1306 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
1307
1308 // Prepare full indexed map of scene nodes in correct order.
1309 RWGltf_GltfSceneNodeMap aSceneNodeMapWithChildren; // indexes starting from 1
1310 for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_None);
1311 aDocExplorer.More(); aDocExplorer.Next())
1312 {
1313 const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
b011420f 1314 {
1315 RWMesh_FaceIterator aFaceIter(aDocNode.RefLabel, TopLoc_Location(), false);
1316 if (!aFaceIter.More())
1317 {
1318 continue;
1319 }
1320 }
01b2f506 1321 if (theLabelFilter != NULL
1322 && !theLabelFilter->Contains (aDocNode.Id))
1323 {
1324 continue;
1325 }
1326
1327 Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
1328 if (aDocExplorer.CurrentDepth() == 0)
1329 {
1330 // save root node index (starting from 0 not 1)
1331 theSceneRootNodeInds.Append (aNodeIndex - 1);
1332 }
1333 }
1334
1335 // Write scene nodes using prepared map for correct order of array members
1336 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
1337 myWriter->StartArray();
1338
1339 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
1340 {
1341 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1342
1343 myWriter->StartObject();
1344 {
1345 if (aDocNode.IsAssembly)
1346 {
1347 myWriter->Key ("children");
1348 myWriter->StartArray();
1349 {
1350 for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
1351 {
1352 const TDF_Label& aChildLabel = aChildIter.Value();
1353 if (aChildLabel.IsNull())
1354 {
1355 continue;
1356 }
1357
1358 const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
1359 Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
1360 if (aChildIdx > 0)
1361 {
1362 myWriter->Int (aChildIdx - 1);
1363 }
1364 }
1365 }
1366 myWriter->EndArray();
1367 }
1368 }
1369 if (!aDocNode.LocalTrsf.IsIdentity())
1370 {
1371 gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
1372 if (aTrsf.Form() != gp_Identity)
1373 {
1374 myCSTrsf.TransformTransformation (aTrsf);
1375 const gp_Quaternion aQuaternion = aTrsf.GetRotation();
1376 const bool hasRotation = Abs (aQuaternion.X()) > gp::Resolution()
1377 || Abs (aQuaternion.Y()) > gp::Resolution()
1378 || Abs (aQuaternion.Z()) > gp::Resolution()
1379 || Abs (aQuaternion.W() - 1.0) > gp::Resolution();
1380 const Standard_Real aScaleFactor = aTrsf.ScaleFactor();
1381 const bool hasScale = Abs (aScaleFactor - 1.0) > Precision::Confusion();
1382 const gp_XYZ& aTranslPart = aTrsf.TranslationPart();
1383 const bool hasTranslation = aTranslPart.SquareModulus() > gp::Resolution();
1384
1385 RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
1386 if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
1387 {
1388 aTrsfFormat = hasRotation && hasScale && hasTranslation
1389 ? RWGltf_WriterTrsfFormat_Mat4
1390 : RWGltf_WriterTrsfFormat_TRS;
1391 }
1392
1393 if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
1394 {
1395 // write full matrix
1396 Graphic3d_Mat4 aMat4;
1397 aTrsf.GetMat4 (aMat4);
1398 if (!aMat4.IsIdentity())
1399 {
1400 myWriter->Key ("matrix");
1401 myWriter->StartArray();
1402 for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
1403 {
1404 for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
1405 {
1406 myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
1407 }
1408 }
1409 myWriter->EndArray();
1410 }
1411 }
1412 else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
1413 {
1414 if (hasRotation)
1415 {
1416 myWriter->Key ("rotation");
1417 myWriter->StartArray();
1418 myWriter->Double (aQuaternion.X());
1419 myWriter->Double (aQuaternion.Y());
1420 myWriter->Double (aQuaternion.Z());
1421 myWriter->Double (aQuaternion.W());
1422 myWriter->EndArray();
1423 }
1424 if (hasScale)
1425 {
1426 myWriter->Key ("scale");
1427 myWriter->StartArray();
1428 myWriter->Double (aScaleFactor);
1429 myWriter->Double (aScaleFactor);
1430 myWriter->Double (aScaleFactor);
1431 myWriter->EndArray();
1432 }
1433 if (hasTranslation)
1434 {
1435 myWriter->Key ("translation");
1436 myWriter->StartArray();
1437 myWriter->Double (aTranslPart.X());
1438 myWriter->Double (aTranslPart.Y());
1439 myWriter->Double (aTranslPart.Z());
1440 myWriter->EndArray();
1441 }
1442 }
1443 }
1444 }
1445 if (!aDocNode.IsAssembly)
1446 {
1447 myWriter->Key ("mesh");
1448 // Mesh order of current node is equal to order of this node in scene nodes map
1449 Standard_Integer aMeshIdx = theSceneNodeMap.FindIndex (aDocNode.Id);
1450 if (aMeshIdx > 0)
1451 {
1452 myWriter->Int (aMeshIdx - 1);
1453 }
1454 }
1455 {
1456 TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.Label);
1457 if (aNodeName.IsEmpty())
1458 {
1459 aNodeName = readNameAttribute (aDocNode.RefLabel);
1460 }
1461 myWriter->Key ("name");
1462 myWriter->String (aNodeName.ToCString());
1463 }
1464 myWriter->EndObject();
1465 }
1466 myWriter->EndArray();
1467#else
1468 (void )theDocument;
1469 (void )theRootLabels;
1470 (void )theLabelFilter;
1471 (void )theSceneNodeMap;
1472 (void )theSceneRootNodeInds;
1473#endif
1474}
1475
1476// =======================================================================
1477// function : writeSamplers
1478// purpose :
1479// =======================================================================
1480void RWGltf_CafWriter::writeSamplers (const RWGltf_GltfMaterialMap& theMaterialMap)
1481{
1482#ifdef HAVE_RAPIDJSON
1483 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
1484 if (theMaterialMap.NbImages() == 0)
1485 {
1486 return;
1487 }
1488
1489 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
1490 myWriter->StartArray();
1491 {
1492 myWriter->StartObject();
1493 {
1494 //myWriter->Key ("magFilter");
1495 //myWriter->Int (9729);
1496 //myWriter->Key ("minFilter");
1497 //myWriter->Int (9729);
1498 }
1499 myWriter->EndObject();
1500 }
1501 myWriter->EndArray();
1502#else
1503 (void )theMaterialMap;
1504#endif
1505}
1506
1507// =======================================================================
1508// function : writeScene
1509// purpose :
1510// =======================================================================
1511void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
1512{
1513#ifdef HAVE_RAPIDJSON
1514 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
1515
1516 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
1517 myWriter->Int (theDefSceneId);
1518#else
1519 (void )theDefSceneId;
1520#endif
1521}
1522
1523// =======================================================================
1524// function : writeScenes
1525// purpose :
1526// =======================================================================
1527void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
1528{
1529#ifdef HAVE_RAPIDJSON
1530 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
1531
1532 myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
1533 myWriter->StartArray();
1534 {
1535 myWriter->StartObject();
1536 myWriter->Key ("nodes");
1537 myWriter->StartArray();
1538 for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
1539 {
1540 myWriter->Int (aRootIter.Value());
1541 }
1542 myWriter->EndArray();
1543 myWriter->EndObject();
1544 }
1545 myWriter->EndArray();
1546#else
1547 (void )theSceneRootNodeInds;
1548#endif
1549}
1550
1551// =======================================================================
1552// function : writeSkins
1553// purpose :
1554// =======================================================================
1555void RWGltf_CafWriter::writeSkins()
1556{
1557 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
1558
1559 // This section should be skipped if it doesn't contain any information but not be empty
1560 /*myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Skins));
1561 myWriter->StartArray();
1562 myWriter->EndArray();*/
1563}
1564
1565// =======================================================================
1566// function : writeTextures
1567// purpose :
1568// =======================================================================
1569void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
1570 RWGltf_GltfMaterialMap& theMaterialMap)
1571{
1572#ifdef HAVE_RAPIDJSON
1573 Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
1574
1575 // empty RWGltf_GltfRootElement_Textures section should not be written to avoid validator errors
1576 bool anIsStarted = false;
1577 for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
1578 {
1579 const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
1580 for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
1581 {
1582 theMaterialMap.AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
1583 }
1584 }
1585 if (anIsStarted)
1586 {
1587 myWriter->EndArray();
1588 }
1589#else
1590 (void )theSceneNodeMap;
1591 (void )theMaterialMap;
1592#endif
1593}