1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-06-10 11:34:06 +03:00
occt/src/RWGltf/RWGltf_CafWriter.cxx
2021-01-20 21:24:17 +03:00

1657 lines
56 KiB
C++

// Copyright (c) 2017-2019 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <RWGltf_CafWriter.hxx>
#include <gp_Quaternion.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <Message_ProgressScope.hxx>
#include <NCollection_DataMap.hxx>
#include <OSD_OpenFile.hxx>
#include <OSD_File.hxx>
#include <OSD_Path.hxx>
#include <Poly_Triangulation.hxx>
#include <RWGltf_GltfAccessorLayout.hxx>
#include <RWGltf_GltfMaterialMap.hxx>
#include <RWGltf_GltfPrimitiveMode.hxx>
#include <RWGltf_GltfRootElement.hxx>
#include <RWGltf_GltfSceneNodeMap.hxx>
#include <RWMesh_FaceIterator.hxx>
#include <TDataStd_Name.hxx>
#include <TDF_Tool.hxx>
#include <TDocStd_Document.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <XCAFPrs_DocumentExplorer.hxx>
#ifdef HAVE_RAPIDJSON
#include <RWGltf_GltfOStreamWriter.hxx>
#endif
IMPLEMENT_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
namespace
{
//! Write three float values.
static void writeVec3 (std::ostream& theStream,
const gp_XYZ& theVec3)
{
Graphic3d_Vec3 aVec3 (float(theVec3.X()), float(theVec3.Y()), float(theVec3.Z()));
theStream.write ((const char* )aVec3.GetData(), sizeof(aVec3));
}
//! Write three float values.
static void writeVec3 (std::ostream& theStream,
const Graphic3d_Vec3& theVec3)
{
theStream.write ((const char* )theVec3.GetData(), sizeof(theVec3));
}
//! Write two float values.
static void writeVec2 (std::ostream& theStream,
const gp_XY& theVec2)
{
Graphic3d_Vec2 aVec2 (float(theVec2.X()), float(theVec2.Y()));
theStream.write ((const char* )aVec2.GetData(), sizeof(aVec2));
}
//! Write triangle indices.
static void writeTriangle32 (std::ostream& theStream,
const Graphic3d_Vec3i& theTri)
{
theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
}
//! Write triangle indices.
static void writeTriangle16 (std::ostream& theStream,
const NCollection_Vec3<uint16_t>& theTri)
{
theStream.write ((const char* )theTri.GetData(), sizeof(theTri));
}
#ifdef HAVE_RAPIDJSON
//! Read name attribute.
static TCollection_AsciiString readNameAttribute (const TDF_Label& theRefLabel)
{
Handle(TDataStd_Name) aNodeName;
if (!theRefLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
{
return TCollection_AsciiString();
}
return TCollection_AsciiString (aNodeName->Get());
}
#endif
}
//================================================================
// Function : Constructor
// Purpose :
//================================================================
RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
Standard_Boolean theIsBinary)
: myFile (theFile),
myTrsfFormat (RWGltf_WriterTrsfFormat_Compact),
myIsBinary (theIsBinary),
myIsForcedUVExport (false),
myToEmbedTexturesInGlb (true),
myBinDataLen64 (0)
{
myCSTrsf.SetOutputLengthUnit (1.0); // meters
myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
TCollection_AsciiString aFolder, aFileName, aShortFileNameBase, aFileExt;
OSD_Path::FolderAndFileFromPath (theFile, aFolder, aFileName);
OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
myBinFileNameShort = aShortFileNameBase + ".bin" + (myIsBinary ? ".tmp" : "");
myBinFileNameFull = !aFolder.IsEmpty() ? aFolder + myBinFileNameShort : myBinFileNameShort;
}
//================================================================
// Function : Destructor
// Purpose :
//================================================================
RWGltf_CafWriter::~RWGltf_CafWriter()
{
myWriter.reset();
}
//================================================================
// Function : toSkipFaceMesh
// Purpose :
//================================================================
Standard_Boolean RWGltf_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
{
return theFaceIter.IsEmptyMesh();
}
// =======================================================================
// function : saveNodes
// purpose :
// =======================================================================
void RWGltf_CafWriter::saveNodes (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
Standard_Integer& theAccessorNb) const
{
theGltfFace.NodePos.Id = theAccessorNb++;
theGltfFace.NodePos.Count = theFaceIter.NbNodes();
theGltfFace.NodePos.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewPos.ByteOffset;
theGltfFace.NodePos.Type = RWGltf_GltfAccessorLayout_Vec3;
theGltfFace.NodePos.ComponentType = RWGltf_GltfAccessorCompType_Float32;
const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
{
gp_XYZ aNode = theFaceIter.NodeTransformed (aNodeIter).XYZ();
myCSTrsf.TransformPosition (aNode);
theGltfFace.NodePos.BndBox.Add (Graphic3d_Vec3d(aNode.X(), aNode.Y(), aNode.Z()));
writeVec3 (theBinFile, aNode);
}
}
// =======================================================================
// function : saveNormals
// purpose :
// =======================================================================
void RWGltf_CafWriter::saveNormals (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
RWMesh_FaceIterator& theFaceIter,
Standard_Integer& theAccessorNb) const
{
if (!theFaceIter.HasNormals())
{
return;
}
theGltfFace.NodeNorm.Id = theAccessorNb++;
theGltfFace.NodeNorm.Count = theFaceIter.NbNodes();
theGltfFace.NodeNorm.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewNorm.ByteOffset;
theGltfFace.NodeNorm.Type = RWGltf_GltfAccessorLayout_Vec3;
theGltfFace.NodeNorm.ComponentType = RWGltf_GltfAccessorCompType_Float32;
const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
{
const gp_Dir aNormal = theFaceIter.NormalTransformed (aNodeIter);
Graphic3d_Vec3 aVecNormal ((float )aNormal.X(), (float )aNormal.Y(), (float )aNormal.Z());
myCSTrsf.TransformNormal (aVecNormal);
writeVec3 (theBinFile, aVecNormal);
}
}
// =======================================================================
// function : saveTextCoords
// purpose :
// =======================================================================
void RWGltf_CafWriter::saveTextCoords (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
Standard_Integer& theAccessorNb) const
{
if (!theFaceIter.HasTexCoords())
{
return;
}
if (!myIsForcedUVExport)
{
if (theFaceIter.FaceStyle().Material().IsNull())
{
return;
}
if (RWGltf_GltfMaterialMap::baseColorTexture (theFaceIter.FaceStyle().Material()).IsNull()
&& theFaceIter.FaceStyle().Material()->PbrMaterial().MetallicRoughnessTexture.IsNull()
&& theFaceIter.FaceStyle().Material()->PbrMaterial().EmissiveTexture.IsNull()
&& theFaceIter.FaceStyle().Material()->PbrMaterial().OcclusionTexture.IsNull()
&& theFaceIter.FaceStyle().Material()->PbrMaterial().NormalTexture.IsNull())
{
return;
}
}
theGltfFace.NodeUV.Id = theAccessorNb++;
theGltfFace.NodeUV.Count = theFaceIter.NbNodes();
theGltfFace.NodeUV.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
theGltfFace.NodeUV.Type = RWGltf_GltfAccessorLayout_Vec2;
theGltfFace.NodeUV.ComponentType = RWGltf_GltfAccessorCompType_Float32;
const Standard_Integer aNodeUpper = theFaceIter.NodeUpper();
for (Standard_Integer aNodeIter = theFaceIter.NodeLower(); aNodeIter <= aNodeUpper; ++aNodeIter)
{
gp_Pnt2d aTexCoord = theFaceIter.NodeTexCoord (aNodeIter);
aTexCoord.SetY (1.0 - aTexCoord.Y());
writeVec2 (theBinFile, aTexCoord.XY());
}
}
// =======================================================================
// function : saveIndices
// purpose :
// =======================================================================
void RWGltf_CafWriter::saveIndices (RWGltf_GltfFace& theGltfFace,
std::ostream& theBinFile,
const RWMesh_FaceIterator& theFaceIter,
Standard_Integer& theAccessorNb)
{
theGltfFace.Indices.Id = theAccessorNb++;
theGltfFace.Indices.Count = theFaceIter.NbTriangles() * 3;
theGltfFace.Indices.ByteOffset = (int64_t )theBinFile.tellp() - myBuffViewInd.ByteOffset;
theGltfFace.Indices.Type = RWGltf_GltfAccessorLayout_Scalar;
theGltfFace.Indices.ComponentType = theGltfFace.NodePos.Count > std::numeric_limits<uint16_t>::max()
? RWGltf_GltfAccessorCompType_UInt32
: RWGltf_GltfAccessorCompType_UInt16;
const Standard_Integer anElemLower = theFaceIter.ElemLower();
const Standard_Integer anElemUpper = theFaceIter.ElemUpper();
for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper; ++anElemIter)
{
Poly_Triangle aTri = theFaceIter.TriangleOriented (anElemIter);
aTri(1) -= anElemLower;
aTri(2) -= anElemLower;
aTri(3) -= anElemLower;
if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
{
writeTriangle16 (theBinFile, NCollection_Vec3<uint16_t>((uint16_t)aTri(1), (uint16_t)aTri(2), (uint16_t)aTri(3)));
}
else
{
writeTriangle32 (theBinFile, Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)));
}
}
if (theGltfFace.Indices.ComponentType == RWGltf_GltfAccessorCompType_UInt16)
{
// alignment by 4 bytes
int64_t aContentLen64 = (int64_t)theBinFile.tellp();
while (aContentLen64 % 4 != 0)
{
theBinFile.write (" ", 1);
++aContentLen64;
}
}
}
// =======================================================================
// function : Perform
// purpose :
// =======================================================================
bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
const TColStd_IndexedDataMapOfStringString& theFileInfo,
const Message_ProgressRange& theProgress)
{
TDF_LabelSequence aRoots;
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (theDocument->Main());
aShapeTool->GetFreeShapes (aRoots);
return Perform (theDocument, aRoots, NULL, theFileInfo, theProgress);
}
// =======================================================================
// function : Perform
// purpose :
// =======================================================================
bool RWGltf_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
const TDF_LabelSequence& theRootLabels,
const TColStd_MapOfAsciiString* theLabelFilter,
const TColStd_IndexedDataMapOfStringString& theFileInfo,
const Message_ProgressRange& theProgress)
{
const Standard_Integer aDefSamplerId = 0;
myMaterialMap = new RWGltf_GltfMaterialMap (myFile, aDefSamplerId);
myMaterialMap->SetDefaultStyle (myDefaultStyle);
Message_ProgressScope aPSentry (theProgress, "Writing glTF file", 2);
if (!writeBinData (theDocument, theRootLabels, theLabelFilter, aPSentry.Next()))
{
return false;
}
if (!aPSentry.More())
{
return false;
}
return writeJson (theDocument, theRootLabels, theLabelFilter, theFileInfo, aPSentry.Next());
}
// =======================================================================
// function : writeBinData
// purpose :
// =======================================================================
bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument,
const TDF_LabelSequence& theRootLabels,
const TColStd_MapOfAsciiString* theLabelFilter,
const Message_ProgressRange& theProgress)
{
myBuffViewPos.Id = RWGltf_GltfAccessor::INVALID_ID;
myBuffViewPos.ByteOffset = 0;
myBuffViewPos.ByteLength = 0;
myBuffViewPos.ByteStride = 12;
myBuffViewPos.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
myBuffViewNorm.Id = RWGltf_GltfAccessor::INVALID_ID;
myBuffViewNorm.ByteOffset = 0;
myBuffViewNorm.ByteLength = 0;
myBuffViewNorm.ByteStride = 12;
myBuffViewNorm.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
myBuffViewTextCoord.Id = RWGltf_GltfAccessor::INVALID_ID;
myBuffViewTextCoord.ByteOffset = 0;
myBuffViewTextCoord.ByteLength = 0;
myBuffViewTextCoord.ByteStride = 8;
myBuffViewTextCoord.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
myBuffViewInd.Id = RWGltf_GltfAccessor::INVALID_ID;
myBuffViewInd.ByteOffset = 0;
myBuffViewInd.ByteLength = 0;
myBuffViewInd.Target = RWGltf_GltfBufferViewTarget_ELEMENT_ARRAY_BUFFER;
myBinDataMap.Clear();
myBinDataLen64 = 0;
std::ofstream aBinFile;
OSD_OpenStream (aBinFile, myBinFileNameFull.ToCString(), std::ios::out | std::ios::binary);
if (!aBinFile.is_open()
|| !aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created");
return false;
}
Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4);
Standard_Integer aNbAccessors = 0;
// write positions
myBuffViewPos.ByteOffset = aBinFile.tellp();
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
// transformation will be stored at scene nodes
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
{
if (myBinDataMap.IsBound (aFaceIter.Face())
|| toSkipFaceMesh (aFaceIter))
{
continue;
}
RWGltf_GltfFace aGltfFace;
saveNodes (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
if (!aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
return false;
}
myBinDataMap.Bind (aFaceIter.Face(), aGltfFace);
}
}
myBuffViewPos.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewPos.ByteOffset;
if (!aPSentryBin.More())
{
return false;
}
aPSentryBin.Next();
// write normals
myBuffViewNorm.ByteOffset = aBinFile.tellp();
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
{
if (toSkipFaceMesh (aFaceIter))
{
continue;
}
RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
{
continue;
}
saveNormals (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
if (!aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
return false;
}
}
}
myBuffViewNorm.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewNorm.ByteOffset;
if (!aPSentryBin.More())
{
return false;
}
aPSentryBin.Next();
// write texture coordinates
myBuffViewTextCoord.ByteOffset = aBinFile.tellp();
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
{
if (toSkipFaceMesh (aFaceIter))
{
continue;
}
RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
{
continue;
}
saveTextCoords (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
if (!aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
return false;
}
}
}
myBuffViewTextCoord.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewTextCoord.ByteOffset;
if (!aPSentryBin.More())
{
return false;
}
aPSentryBin.Next();
// write indices
myBuffViewInd.ByteOffset = aBinFile.tellp();
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More() && aPSentryBin.More(); aFaceIter.Next())
{
if (toSkipFaceMesh (aFaceIter))
{
continue;
}
RWGltf_GltfFace& aGltfFace = myBinDataMap.ChangeFind (aFaceIter.Face());
if (aGltfFace.Indices.Id != RWGltf_GltfAccessor::INVALID_ID)
{
continue;
}
saveIndices (aGltfFace, aBinFile, aFaceIter, aNbAccessors);
if (!aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
return false;
}
}
}
myBuffViewInd.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewInd.ByteOffset;
if (myIsBinary
&& myToEmbedTexturesInGlb)
{
// save unique image textures
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
aDocExplorer.More() && aPSentryBin.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style);
aFaceIter.More(); aFaceIter.Next())
{
if (toSkipFaceMesh (aFaceIter))
{
continue;
}
myMaterialMap->AddGlbImages (aBinFile, aFaceIter.FaceStyle());
}
}
}
int aBuffViewId = 0;
if (myBuffViewPos.ByteLength > 0)
{
myBuffViewPos.Id = aBuffViewId++;
}
if (myBuffViewNorm.ByteLength > 0)
{
myBuffViewNorm.Id = aBuffViewId++;
}
if (myBuffViewTextCoord.ByteLength > 0)
{
myBuffViewTextCoord.Id = aBuffViewId++;
}
if (myBuffViewInd.ByteLength > 0)
{
myBuffViewInd.Id = aBuffViewId++;
}
// myMaterialMap->FlushGlbBufferViews() will put image bufferView's IDs at the end of list
myBinDataLen64 = aBinFile.tellp();
aBinFile.close();
if (!aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written");
return false;
}
return true;
}
//================================================================
// Function : writeJson
// Purpose :
//================================================================
bool RWGltf_CafWriter::writeJson (const Handle(TDocStd_Document)& theDocument,
const TDF_LabelSequence& theRootLabels,
const TColStd_MapOfAsciiString* theLabelFilter,
const TColStd_IndexedDataMapOfStringString& theFileInfo,
const Message_ProgressRange& theProgress)
{
#ifdef HAVE_RAPIDJSON
myWriter.reset();
// write vertex arrays
Message_ProgressScope aPSentryBin (theProgress, "Header data", 2);
const Standard_Integer aBinDataBufferId = 0;
const Standard_Integer aDefSceneId = 0;
const TCollection_AsciiString aFileNameGltf = myFile;
std::ofstream aGltfContentFile;
OSD_OpenStream (aGltfContentFile, aFileNameGltf.ToCString(), std::ios::out | std::ios::binary);
if (!aGltfContentFile.is_open()
|| !aGltfContentFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created");
return false;
}
if (myIsBinary)
{
const char* aMagic = "glTF";
uint32_t aVersion = 2;
uint32_t aLength = 0;
uint32_t aContentLength = 0;
uint32_t aContentType = 0x4E4F534A;
aGltfContentFile.write (aMagic, 4);
aGltfContentFile.write ((const char* )&aVersion, sizeof(aVersion));
aGltfContentFile.write ((const char* )&aLength, sizeof(aLength));
aGltfContentFile.write ((const char* )&aContentLength, sizeof(aContentLength));
aGltfContentFile.write ((const char* )&aContentType, sizeof(aContentType));
}
// Prepare an indexed map of scene nodes (without assemblies) in correct order.
// Note: this is also order of meshes in glTF document array.
RWGltf_GltfSceneNodeMap aSceneNodeMap;
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
aDocExplorer.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
bool hasMeshData = false;
if (!aDocNode.IsAssembly)
{
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
if (!toSkipFaceMesh (aFaceIter))
{
hasMeshData = true;
break;
}
}
}
if (hasMeshData)
{
aSceneNodeMap.Add (aDocNode);
}
else
{
// glTF disallows empty meshes / primitive arrays
const TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.RefLabel);
Message::SendWarning (TCollection_AsciiString("RWGltf_CafWriter skipped node '") + aNodeName + "' without triangulation data");
}
}
rapidjson::OStreamWrapper aFileStream (aGltfContentFile);
myWriter.reset (new RWGltf_GltfOStreamWriter (aFileStream));
myWriter->StartObject();
writeAccessors (aSceneNodeMap);
writeAnimations();
writeAsset (theFileInfo);
writeBufferViews (aBinDataBufferId);
writeBuffers();
writeExtensions();
writeImages (aSceneNodeMap);
writeMaterials (aSceneNodeMap);
writeMeshes (aSceneNodeMap);
aPSentryBin.Next();
if (!aPSentryBin.More())
{
return false;
}
// root nodes indices starting from 0
NCollection_Sequence<Standard_Integer> aSceneRootNodeInds;
writeNodes (theDocument, theRootLabels, theLabelFilter, aSceneNodeMap, aSceneRootNodeInds);
writeSamplers();
writeScene (aDefSceneId);
writeScenes (aSceneRootNodeInds);
writeSkins();
writeTextures (aSceneNodeMap);
myWriter->EndObject();
if (!myIsBinary)
{
aGltfContentFile.close();
if (!aGltfContentFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
return false;
}
return true;
}
int64_t aContentLen64 = (int64_t )aGltfContentFile.tellp() - 20;
while (aContentLen64 % 4 != 0)
{
aGltfContentFile.write (" ", 1);
++aContentLen64;
}
const uint32_t aBinLength = (uint32_t )myBinDataLen64;
const uint32_t aBinType = 0x004E4942;
aGltfContentFile.write ((const char*)&aBinLength, 4);
aGltfContentFile.write ((const char*)&aBinType, 4);
const int64_t aFullLen64 = aContentLen64 + 20 + myBinDataLen64 + 8;
if (aFullLen64 < std::numeric_limits<uint32_t>::max())
{
{
std::ifstream aBinFile;
OSD_OpenStream (aBinFile, myBinFileNameFull.ToCString(), std::ios::in | std::ios::binary);
if (!aBinFile.is_open()
|| !aBinFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened");
return false;
}
char aBuffer[4096];
for (; aBinFile.good();)
{
aBinFile.read (aBuffer, 4096);
const Standard_Integer aReadLen = (Standard_Integer )aBinFile.gcount();
if (aReadLen == 0)
{
break;
}
aGltfContentFile.write (aBuffer, aReadLen);
}
}
OSD_Path aBinFilePath (myBinFileNameFull);
OSD_File (aBinFilePath).Remove();
if (OSD_File (aBinFilePath).Exists())
{
Message::SendFail (TCollection_AsciiString ("Unable to remove temporary glTF content file '") + myBinFileNameFull + "'");
}
}
else
{
Message::SendFail ("glTF file content is too big for binary format");
return false;
}
const uint32_t aLength = (uint32_t )aFullLen64;
const uint32_t aContentLength = (uint32_t )aContentLen64;
aGltfContentFile.seekp (8);
aGltfContentFile.write ((const char* )&aLength, 4);
aGltfContentFile.write ((const char* )&aContentLength, 4);
aGltfContentFile.close();
if (!aGltfContentFile.good())
{
Message::SendFail (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written");
return false;
}
myWriter.reset();
return true;
#else
(void )theDocument;
(void )theRootLabels;
(void )theLabelFilter;
(void )theFileInfo;
(void )theProgress;
Message::SendFail ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined]");
return false;
#endif
}
// =======================================================================
// function : writeAccessors
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAccessors()");
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Accessors));
myWriter->StartArray();
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aWrittenFaces;
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
|| toSkipFaceMesh (aFaceIter))
{
continue;
}
const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
writePositions (aGltfFace);
}
}
aWrittenFaces.Clear();
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
|| toSkipFaceMesh (aFaceIter))
{
continue;
}
const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
writeNormals (aGltfFace);
}
}
aWrittenFaces.Clear();
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
|| toSkipFaceMesh (aFaceIter))
{
continue;
}
const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
writeTextCoords (aGltfFace);
}
}
aWrittenFaces.Clear();
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
if (!aWrittenFaces.Add (aFaceIter.Face()) // skip repeating faces
|| toSkipFaceMesh (aFaceIter))
{
continue;
}
const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
writeIndices (aGltfFace);
}
}
myWriter->EndArray();
#else
(void )theSceneNodeMap;
#endif
}
// =======================================================================
// function : writePositions
// purpose :
// =======================================================================
void RWGltf_CafWriter::writePositions (const RWGltf_GltfFace& theGltfFace)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writePositions()");
if (theGltfFace.NodePos.Id == RWGltf_GltfAccessor::INVALID_ID)
{
return;
}
myWriter->StartObject();
myWriter->Key ("bufferView");
myWriter->Int (myBuffViewPos.Id);
myWriter->Key ("byteOffset");
myWriter->Int64 (theGltfFace.NodePos.ByteOffset);
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.NodePos.ComponentType);
myWriter->Key ("count");
myWriter->Int64 (theGltfFace.NodePos.Count);
if (theGltfFace.NodePos.BndBox.IsValid())
{
myWriter->Key ("max");
myWriter->StartArray();
myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().x());
myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().y());
myWriter->Double (theGltfFace.NodePos.BndBox.CornerMax().z());
myWriter->EndArray();
myWriter->Key("min");
myWriter->StartArray();
myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().x());
myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().y());
myWriter->Double (theGltfFace.NodePos.BndBox.CornerMin().z());
myWriter->EndArray();
}
myWriter->Key ("type");
myWriter->String ("VEC3");
myWriter->EndObject();
#else
(void )theGltfFace;
#endif
}
// =======================================================================
// function : writeNormals
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeNormals (const RWGltf_GltfFace& theGltfFace)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNormals()");
if (theGltfFace.NodeNorm.Id == RWGltf_GltfAccessor::INVALID_ID)
{
return;
}
myWriter->StartObject();
myWriter->Key ("bufferView");
myWriter->Int (myBuffViewNorm.Id);
myWriter->Key ("byteOffset");
myWriter->Int64 (theGltfFace.NodeNorm.ByteOffset);
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.NodeNorm.ComponentType);
myWriter->Key ("count");
myWriter->Int64 (theGltfFace.NodeNorm.Count);
// min/max values are optional, and not very useful for normals - skip them
/*{
myWriter->Key ("max");
myWriter->StartArray();
myWriter->Double (1.0);
myWriter->Double (1.0);
myWriter->Double (1.0);
myWriter->EndArray();
}
{
myWriter->Key ("min");
myWriter->StartArray();
myWriter->Double (0.0);
myWriter->Double (0.0);
myWriter->Double (0.0);
myWriter->EndArray();
}*/
myWriter->Key ("type");
myWriter->String ("VEC3");
myWriter->EndObject();
#else
(void )theGltfFace;
#endif
}
// =======================================================================
// function : writeTextCoords
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeTextCoords (const RWGltf_GltfFace& theGltfFace)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextCoords()");
if (theGltfFace.NodeUV.Id == RWGltf_GltfAccessor::INVALID_ID)
{
return;
}
myWriter->StartObject();
myWriter->Key ("bufferView");
myWriter->Int (myBuffViewTextCoord.Id);
myWriter->Key ("byteOffset");
myWriter->Int64 (theGltfFace.NodeUV.ByteOffset);
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.NodeUV.ComponentType);
myWriter->Key ("count");
myWriter->Int64 (theGltfFace.NodeUV.Count);
// min/max values are optional, and not very useful for UV coordinates - skip them
/*{
myWriter->Key ("max");
myWriter->StartArray();
myWriter->Double (1.0);
myWriter->Double (1.0);
myWriter->Double (1.0);
myWriter->EndArray();
}
{
myWriter->Key ("min");
myWriter->StartArray();
myWriter->Double (0.0);
myWriter->Double (0.0);
myWriter->Double (0.0);
myWriter->EndArray();
}*/
myWriter->Key ("type");
myWriter->String ("VEC2");
myWriter->EndObject();
#else
(void )theGltfFace;
#endif
}
// =======================================================================
// function : writeIndices
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeIndices (const RWGltf_GltfFace& theGltfFace)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeIndices()");
if (theGltfFace.Indices.Id == RWGltf_GltfAccessor::INVALID_ID)
{
return;
}
myWriter->StartObject();
myWriter->Key ("bufferView");
myWriter->Int (myBuffViewInd.Id);
myWriter->Key ("byteOffset");
myWriter->Int64 (theGltfFace.Indices.ByteOffset);
myWriter->Key ("componentType");
myWriter->Int (theGltfFace.Indices.ComponentType);
myWriter->Key ("count");
myWriter->Int64 (theGltfFace.Indices.Count);
myWriter->Key ("type");
myWriter->String ("SCALAR");
myWriter->EndObject();
#else
(void )theGltfFace;
#endif
}
// =======================================================================
// function : writeAnimations
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeAnimations()
{
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAnimations()");
// This section should be skipped if it doesn't contain any information but not be empty
//myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Animations));
//myWriter->StartArray();
//myWriter->EndArray();
}
// =======================================================================
// function : writeAsset
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeAsset()");
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Asset));
myWriter->StartObject();
myWriter->Key ("generator");
myWriter->String ("Open CASCADE Technology [www.opencascade.com]");
myWriter->Key ("version");
myWriter->String ("2.0"); // glTF format version
bool anIsStarted = false;
for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
{
if (!anIsStarted)
{
myWriter->Key ("extras");
myWriter->StartObject();
anIsStarted = true;
}
myWriter->Key (aKeyValueIter.Key().ToCString());
myWriter->String (aKeyValueIter.Value().ToCString());
}
if (anIsStarted)
{
myWriter->EndObject();
}
myWriter->EndObject();
#else
(void )theFileInfo;
#endif
}
// =======================================================================
// function : writeBufferViews
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeBufferViews (const Standard_Integer theBinDataBufferId)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBufferViews()");
int aBuffViewId = 0;
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
myWriter->StartArray();
if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
{
aBuffViewId++;
myWriter->StartObject();
myWriter->Key ("buffer");
myWriter->Int (theBinDataBufferId);
myWriter->Key ("byteLength");
myWriter->Int64 (myBuffViewPos.ByteLength);
myWriter->Key ("byteOffset");
myWriter->Int64 (myBuffViewPos.ByteOffset);
myWriter->Key ("byteStride");
myWriter->Int64 (myBuffViewPos.ByteStride);
myWriter->Key ("target");
myWriter->Int (myBuffViewPos.Target);
myWriter->EndObject();
}
if (myBuffViewNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
{
aBuffViewId++;
myWriter->StartObject();
myWriter->Key ("buffer");
myWriter->Int (theBinDataBufferId);
myWriter->Key ("byteLength");
myWriter->Int64 (myBuffViewNorm.ByteLength);
myWriter->Key ("byteOffset");
myWriter->Int64 (myBuffViewNorm.ByteOffset);
myWriter->Key ("byteStride");
myWriter->Int64 (myBuffViewNorm.ByteStride);
myWriter->Key ("target");
myWriter->Int (myBuffViewNorm.Target);
myWriter->EndObject();
}
if (myBuffViewTextCoord.Id != RWGltf_GltfAccessor::INVALID_ID)
{
aBuffViewId++;
myWriter->StartObject();
myWriter->Key ("buffer");
myWriter->Int (theBinDataBufferId);
myWriter->Key ("byteLength");
myWriter->Int64 (myBuffViewTextCoord.ByteLength);
myWriter->Key ("byteOffset");
myWriter->Int64 (myBuffViewTextCoord.ByteOffset);
myWriter->Key ("byteStride");
myWriter->Int64 (myBuffViewTextCoord.ByteStride);
myWriter->Key ("target");
myWriter->Int (myBuffViewTextCoord.Target);
myWriter->EndObject();
}
if (myBuffViewInd.Id != RWGltf_GltfAccessor::INVALID_ID)
{
aBuffViewId++;
myWriter->StartObject();
myWriter->Key ("buffer");
myWriter->Int (theBinDataBufferId);
myWriter->Key ("byteLength");
myWriter->Int64 (myBuffViewInd.ByteLength);
myWriter->Key ("byteOffset");
myWriter->Int64 (myBuffViewInd.ByteOffset);
myWriter->Key ("target");
myWriter->Int (myBuffViewInd.Target);
myWriter->EndObject();
}
myMaterialMap->FlushGlbBufferViews (myWriter.get(), theBinDataBufferId, aBuffViewId);
myWriter->EndArray();
#else
(void )theBinDataBufferId;
#endif
}
// =======================================================================
// function : writeBuffers
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeBuffers()
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeBuffers()");
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Buffers));
myWriter->StartArray();
{
myWriter->StartObject();
{
myWriter->Key ("byteLength");
myWriter->Int64 (myBinDataLen64);
if (!myIsBinary)
{
myWriter->Key ("uri");
myWriter->String (myBinFileNameShort.ToCString());
}
}
myWriter->EndObject();
}
myWriter->EndArray();
#endif
}
// =======================================================================
// function : writeExtensions
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeExtensions()
{
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeExtensions()");
}
// =======================================================================
// function : writeImages
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeImages()");
// empty RWGltf_GltfRootElement_Images section should NOT be written to avoid validator errors
if (myIsBinary
&& myToEmbedTexturesInGlb)
{
myMaterialMap->FlushGlbImages (myWriter.get());
}
else
{
bool anIsStarted = false;
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter(theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
myMaterialMap->AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
}
}
if (anIsStarted)
{
myWriter->EndArray();
}
}
#else
(void )theSceneNodeMap;
#endif
}
// =======================================================================
// function : writeMaterials
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMaterials()");
// empty RWGltf_GltfRootElement_Materials section should NOT be written to avoid validator errors
bool anIsStarted = false;
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
myMaterialMap->AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
}
}
if (anIsStarted)
{
myWriter->EndArray();
}
#else
(void )theSceneNodeMap;
#endif
}
// =======================================================================
// function : writeMeshes
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeMeshes()");
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Meshes));
myWriter->StartArray();
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
const TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.RefLabel);
bool toStartPrims = true;
Standard_Integer aNbFacesInNode = 0;
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
{
if (toSkipFaceMesh (aFaceIter))
{
continue;
}
if (toStartPrims)
{
toStartPrims = false;
myWriter->StartObject();
myWriter->Key ("name");
myWriter->String (aNodeName.ToCString());
myWriter->Key ("primitives");
myWriter->StartArray();
}
const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
const TCollection_AsciiString aMatId = myMaterialMap->FindMaterial (aFaceIter.FaceStyle());
myWriter->StartObject();
{
myWriter->Key ("attributes");
myWriter->StartObject();
{
if (aGltfFace.NodeNorm.Id != RWGltf_GltfAccessor::INVALID_ID)
{
myWriter->Key ("NORMAL");
myWriter->Int (aGltfFace.NodeNorm.Id);
}
myWriter->Key ("POSITION");
myWriter->Int (aGltfFace.NodePos.Id);
if (aGltfFace.NodeUV.Id != RWGltf_GltfAccessor::INVALID_ID)
{
myWriter->Key ("TEXCOORD_0");
myWriter->Int (aGltfFace.NodeUV.Id);
}
}
myWriter->EndObject();
myWriter->Key ("indices");
myWriter->Int (aGltfFace.Indices.Id);
if (!aMatId.IsEmpty())
{
myWriter->Key ("material");
myWriter->Int (aMatId.IntegerValue());
}
myWriter->Key ("mode");
myWriter->Int (RWGltf_GltfPrimitiveMode_Triangles);
}
myWriter->EndObject();
}
if (!toStartPrims)
{
myWriter->EndArray();
myWriter->EndObject();
}
}
myWriter->EndArray();
#else
(void )theSceneNodeMap;
#endif
}
// =======================================================================
// function : writeNodes
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeNodes (const Handle(TDocStd_Document)& theDocument,
const TDF_LabelSequence& theRootLabels,
const TColStd_MapOfAsciiString* theLabelFilter,
const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeNodes()");
// Prepare full indexed map of scene nodes in correct order.
RWGltf_GltfSceneNodeMap aSceneNodeMapWithChildren; // indexes starting from 1
for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_None);
aDocExplorer.More(); aDocExplorer.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
if (theLabelFilter != NULL
&& !theLabelFilter->Contains (aDocNode.Id))
{
continue;
}
// keep empty nodes
//RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), false);
//if (!aFaceIter.More()) { continue; }
Standard_Integer aNodeIndex = aSceneNodeMapWithChildren.Add (aDocNode);
if (aDocExplorer.CurrentDepth() == 0)
{
// save root node index (starting from 0 not 1)
theSceneRootNodeInds.Append (aNodeIndex - 1);
}
}
// Write scene nodes using prepared map for correct order of array members
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Nodes));
myWriter->StartArray();
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (aSceneNodeMapWithChildren); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
myWriter->StartObject();
{
if (aDocNode.IsAssembly)
{
myWriter->Key ("children");
myWriter->StartArray();
{
for (TDF_ChildIterator aChildIter (aDocNode.RefLabel); aChildIter.More(); aChildIter.Next())
{
const TDF_Label& aChildLabel = aChildIter.Value();
if (aChildLabel.IsNull())
{
continue;
}
const TCollection_AsciiString aChildId = XCAFPrs_DocumentExplorer::DefineChildId (aChildLabel, aDocNode.Id);
Standard_Integer aChildIdx = aSceneNodeMapWithChildren.FindIndex (aChildId);
if (aChildIdx > 0)
{
myWriter->Int (aChildIdx - 1);
}
}
}
myWriter->EndArray();
}
}
if (!aDocNode.LocalTrsf.IsIdentity())
{
gp_Trsf aTrsf = aDocNode.LocalTrsf.Transformation();
if (aTrsf.Form() != gp_Identity)
{
myCSTrsf.TransformTransformation (aTrsf);
const gp_Quaternion aQuaternion = aTrsf.GetRotation();
const bool hasRotation = Abs (aQuaternion.X()) > gp::Resolution()
|| Abs (aQuaternion.Y()) > gp::Resolution()
|| Abs (aQuaternion.Z()) > gp::Resolution()
|| Abs (aQuaternion.W() - 1.0) > gp::Resolution();
const Standard_Real aScaleFactor = aTrsf.ScaleFactor();
const bool hasScale = Abs (aScaleFactor - 1.0) > Precision::Confusion();
const gp_XYZ& aTranslPart = aTrsf.TranslationPart();
const bool hasTranslation = aTranslPart.SquareModulus() > gp::Resolution();
RWGltf_WriterTrsfFormat aTrsfFormat = myTrsfFormat;
if (myTrsfFormat == RWGltf_WriterTrsfFormat_Compact)
{
aTrsfFormat = hasRotation && hasScale && hasTranslation
? RWGltf_WriterTrsfFormat_Mat4
: RWGltf_WriterTrsfFormat_TRS;
}
if (aTrsfFormat == RWGltf_WriterTrsfFormat_Mat4)
{
// write full matrix
Graphic3d_Mat4 aMat4;
aTrsf.GetMat4 (aMat4);
if (!aMat4.IsIdentity())
{
myWriter->Key ("matrix");
myWriter->StartArray();
for (Standard_Integer aColIter = 0; aColIter < 4; ++aColIter)
{
for (Standard_Integer aRowIter = 0; aRowIter < 4; ++aRowIter)
{
myWriter->Double (aMat4.GetValue (aRowIter, aColIter));
}
}
myWriter->EndArray();
}
}
else //if (aTrsfFormat == RWGltf_WriterTrsfFormat_TRS)
{
if (hasRotation)
{
myWriter->Key ("rotation");
myWriter->StartArray();
myWriter->Double (aQuaternion.X());
myWriter->Double (aQuaternion.Y());
myWriter->Double (aQuaternion.Z());
myWriter->Double (aQuaternion.W());
myWriter->EndArray();
}
if (hasScale)
{
myWriter->Key ("scale");
myWriter->StartArray();
myWriter->Double (aScaleFactor);
myWriter->Double (aScaleFactor);
myWriter->Double (aScaleFactor);
myWriter->EndArray();
}
if (hasTranslation)
{
myWriter->Key ("translation");
myWriter->StartArray();
myWriter->Double (aTranslPart.X());
myWriter->Double (aTranslPart.Y());
myWriter->Double (aTranslPart.Z());
myWriter->EndArray();
}
}
}
}
if (!aDocNode.IsAssembly)
{
// Mesh order of current node is equal to order of this node in scene nodes map
Standard_Integer aMeshIdx = theSceneNodeMap.FindIndex (aDocNode.Id);
if (aMeshIdx > 0)
{
myWriter->Key ("mesh");
myWriter->Int (aMeshIdx - 1);
}
}
{
TCollection_AsciiString aNodeName = readNameAttribute (aDocNode.Label);
if (aNodeName.IsEmpty())
{
aNodeName = readNameAttribute (aDocNode.RefLabel);
}
myWriter->Key ("name");
myWriter->String (aNodeName.ToCString());
}
myWriter->EndObject();
}
myWriter->EndArray();
#else
(void )theDocument;
(void )theRootLabels;
(void )theLabelFilter;
(void )theSceneNodeMap;
(void )theSceneRootNodeInds;
#endif
}
// =======================================================================
// function : writeSamplers
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeSamplers()
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
if (myMaterialMap->NbImages() == 0)
{
return;
}
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Samplers));
myWriter->StartArray();
{
myWriter->StartObject();
{
//myWriter->Key ("magFilter");
//myWriter->Int (9729);
//myWriter->Key ("minFilter");
//myWriter->Int (9729);
}
myWriter->EndObject();
}
myWriter->EndArray();
#endif
}
// =======================================================================
// function : writeScene
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeScene (const Standard_Integer theDefSceneId)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScene()");
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scene));
myWriter->Int (theDefSceneId);
#else
(void )theDefSceneId;
#endif
}
// =======================================================================
// function : writeScenes
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeScenes()");
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Scenes));
myWriter->StartArray();
{
myWriter->StartObject();
myWriter->Key ("nodes");
myWriter->StartArray();
for (NCollection_Sequence<Standard_Integer>::Iterator aRootIter (theSceneRootNodeInds); aRootIter.More(); aRootIter.Next())
{
myWriter->Int (aRootIter.Value());
}
myWriter->EndArray();
myWriter->EndObject();
}
myWriter->EndArray();
#else
(void )theSceneRootNodeInds;
#endif
}
// =======================================================================
// function : writeSkins
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeSkins()
{
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSkins()");
// This section should be skipped if it doesn't contain any information but not be empty
/*myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Skins));
myWriter->StartArray();
myWriter->EndArray();*/
}
// =======================================================================
// function : writeTextures
// purpose :
// =======================================================================
void RWGltf_CafWriter::writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap)
{
#ifdef HAVE_RAPIDJSON
Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeTextures()");
// empty RWGltf_GltfRootElement_Textures section should not be written to avoid validator errors
bool anIsStarted = false;
for (RWGltf_GltfSceneNodeMap::Iterator aSceneNodeIter (theSceneNodeMap); aSceneNodeIter.More(); aSceneNodeIter.Next())
{
const XCAFPrs_DocumentNode& aDocNode = aSceneNodeIter.Value();
for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
{
myMaterialMap->AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
}
}
if (anIsStarted)
{
myWriter->EndArray();
}
#else
(void )theSceneNodeMap;
#endif
}