theFileName.Clear();
}
}
+
+// =======================================================================
+// function : FileNameAndExtension
+// purpose :
+// =======================================================================
+void OSD_Path::FileNameAndExtension (const TCollection_AsciiString& theFilePath,
+ TCollection_AsciiString& theName,
+ TCollection_AsciiString& theExtension)
+{
+ const Standard_Integer THE_EXT_MAX_LEN = 20; // this method is supposed to be used with normal extension
+ const Standard_Integer aLen = theFilePath.Length();
+ for (Standard_Integer anExtLen = 1; anExtLen < aLen && anExtLen < THE_EXT_MAX_LEN; ++anExtLen)
+ {
+ if (theFilePath.Value (aLen - anExtLen) == '.')
+ {
+ const Standard_Integer aNameUpper = aLen - anExtLen - 1;
+ if (aNameUpper < 1)
+ {
+ break;
+ }
+
+ theName = theFilePath.SubString (1, aNameUpper);
+ theExtension = theFilePath.SubString (aLen - anExtLen + 1, aLen);
+ theExtension.LowerCase();
+ return;
+ }
+ }
+
+ theName = theFilePath;
+ theExtension.Clear();
+}
TCollection_AsciiString& theFolder,
TCollection_AsciiString& theFileName);
+ //! Return file extension from the name in lower case.
+ //! Extension is expected to be within 20-symbols length, and determined as file name tail after last dot.
+ //! Example: IN theFilePath ='Image.sbs.JPG'
+ //! OUT theName ='Image.sbs'
+ //! OUT theFileName ='jpg'
+ //! @param theFilePath [in] file path
+ //! @param theName [out] file name without extension
+ //! @param theExtension [out] file extension in lower case and without dot
+ Standard_EXPORT static void FileNameAndExtension (const TCollection_AsciiString& theFilePath,
+ TCollection_AsciiString& theName,
+ TCollection_AsciiString& theExtension);
+
//! Detect absolute DOS-path also used in Windows.
//! The total path length is limited to 256 characters.
//! Sample path:
RWGltf_MaterialMetallicRoughness.hxx
RWGltf_CafReader.cxx
RWGltf_CafReader.hxx
+RWGltf_CafWriter.cxx
+RWGltf_CafWriter.hxx
+RWGltf_GltfMaterialMap.cxx
+RWGltf_GltfMaterialMap.hxx
RWGltf_GltfJsonParser.cxx
RWGltf_GltfJsonParser.pxx
+RWGltf_GltfOStreamWriter.hxx
+RWGltf_GltfSceneNodeMap.hxx
RWGltf_PrimitiveArrayReader.cxx
RWGltf_PrimitiveArrayReader.hxx
RWGltf_TriangulationReader.cxx
RWGltf_TriangulationReader.hxx
+RWGltf_WriterTrsfFormat.hxx
--- /dev/null
+// 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_ProgressSentry.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),
+ 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 Handle(Message_ProgressIndicator)& 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 Handle(Message_ProgressIndicator)& theProgress)
+{
+ Message_ProgressSentry aPSentry (theProgress, "Writing glTF file", 0, 2, 1);
+ if (!writeBinData (theDocument, theRootLabels, theLabelFilter, theProgress))
+ {
+ return false;
+ }
+
+ aPSentry.Next();
+ if (!aPSentry.More())
+ {
+ return false;
+ }
+
+ return writeJson (theDocument, theRootLabels, theLabelFilter, theFileInfo, theProgress);
+}
+
+// =======================================================================
+// function : writeBinData
+// purpose :
+// =======================================================================
+bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const Handle(Message_ProgressIndicator)& theProgress)
+{
+ myBuffViewPos.ByteOffset = 0;
+ myBuffViewPos.ByteLength = 0;
+ myBuffViewPos.ByteStride = 12;
+ myBuffViewPos.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
+ myBuffViewNorm.ByteOffset = 0;
+ myBuffViewNorm.ByteLength = 0;
+ myBuffViewNorm.ByteStride = 12;
+ myBuffViewNorm.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
+ myBuffViewTextCoord.ByteOffset = 0;
+ myBuffViewTextCoord.ByteLength = 0;
+ myBuffViewTextCoord.ByteStride = 8;
+ myBuffViewTextCoord.Target = RWGltf_GltfBufferViewTarget_ARRAY_BUFFER;
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be created!", Message_Fail);
+ return false;
+ }
+
+ Message_ProgressSentry aPSentryBin (theProgress, "Binary data", 0, 4, 1);
+
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written!", Message_Fail);
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written!", Message_Fail);
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written!", Message_Fail);
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written!", Message_Fail);
+ return false;
+ }
+ }
+ }
+ myBuffViewInd.ByteLength = (int64_t )aBinFile.tellp() - myBuffViewInd.ByteOffset;
+
+ 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++;
+ }
+
+ myBinDataLen64 = aBinFile.tellp();
+ aBinFile.close();
+ if (!aBinFile.good())
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' can not be written!", Message_Fail);
+ 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 Handle(Message_ProgressIndicator)& theProgress)
+{
+#ifdef HAVE_RAPIDJSON
+ myWriter.reset();
+
+ // write vertex arrays
+ Message_ProgressSentry aPSentryBin (theProgress, "Header data", 0, 2, 1);
+
+ const Standard_Integer aBinDataBufferId = 0;
+ const Standard_Integer aDefSamplerId = 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be created!", Message_Fail);
+ 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;
+ }
+ aSceneNodeMap.Add (aDocNode);
+ }
+
+ rapidjson::OStreamWrapper aFileStream (aGltfContentFile);
+ myWriter.reset (new RWGltf_GltfOStreamWriter (aFileStream));
+
+ myWriter->StartObject();
+
+ writeAccessors (aSceneNodeMap);
+ writeAnimations();
+ writeAsset (theFileInfo);
+ writeBufferViews (aBinDataBufferId);
+ writeBuffers();
+ writeExtensions ();
+
+ RWGltf_GltfMaterialMap aMaterialMap (myFile, aDefSamplerId);
+ aMaterialMap.SetDefaultStyle (myDefaultStyle);
+ writeImages (aSceneNodeMap, aMaterialMap);
+ writeMaterials (aSceneNodeMap, aMaterialMap);
+ writeMeshes (aSceneNodeMap, aMaterialMap);
+
+ 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 (aMaterialMap);
+ writeScene (aDefSceneId);
+ writeScenes (aSceneRootNodeInds);
+ writeSkins();
+ writeTextures (aSceneNodeMap, aMaterialMap);
+
+ myWriter->EndObject();
+
+ if (!myIsBinary)
+ {
+ aGltfContentFile.close();
+ if (!aGltfContentFile.good())
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written!", Message_Fail);
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + myBinFileNameFull + "' cannot be opened!", Message_Fail);
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("Unable to remove temporary glTF content file '")
+ + myBinFileNameFull + "'!", Message_Fail);
+ }
+ }
+ else
+ {
+ Message::DefaultMessenger()->Send ("glTF file content is too big for binary format!", Message_Fail);
+ 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::DefaultMessenger()->Send (TCollection_AsciiString ("File '") + aFileNameGltf + "' can not be written!", Message_Fail);
+ return false;
+ }
+
+ myWriter.reset();
+ return true;
+#else
+ (void )theDocument;
+ (void )theRootLabels;
+ (void )theLabelFilter;
+ (void )theFileInfo;
+ (void )theProgress;
+ Message::DefaultMessenger()->Send ("Error: glTF writer is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined].", Message_Fail);
+ 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()");
+
+ myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_BufferViews));
+ myWriter->StartArray();
+ if (myBuffViewPos.Id != RWGltf_GltfAccessor::INVALID_ID)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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();
+ }
+ 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 (myBuffViewPos.ByteLength + myBuffViewNorm.ByteLength +
+ myBuffViewTextCoord.ByteLength + myBuffViewInd.ByteLength);
+ 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,
+ RWGltf_GltfMaterialMap& theMaterialMap)
+{
+#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
+ 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())
+ {
+ theMaterialMap.AddImages (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
+ }
+ }
+ if (anIsStarted)
+ {
+ myWriter->EndArray();
+ }
+#else
+ (void )theSceneNodeMap;
+ (void )theMaterialMap;
+#endif
+}
+
+// =======================================================================
+// function : writeMaterials
+// purpose :
+// =======================================================================
+void RWGltf_CafWriter::writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ RWGltf_GltfMaterialMap& theMaterialMap)
+{
+#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())
+ {
+ theMaterialMap.AddMaterial (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
+ }
+ }
+ if (anIsStarted)
+ {
+ myWriter->EndArray();
+ }
+#else
+ (void )theSceneNodeMap;
+ (void )theMaterialMap;
+#endif
+}
+
+// =======================================================================
+// function : writeMeshes
+// purpose :
+// =======================================================================
+void RWGltf_CafWriter::writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ const RWGltf_GltfMaterialMap& theMaterialMap)
+{
+#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);
+
+ myWriter->StartObject();
+ myWriter->Key ("name");
+ myWriter->String (aNodeName.ToCString());
+ myWriter->Key ("primitives");
+ myWriter->StartArray();
+
+ Standard_Integer aNbFacesInNode = 0;
+ for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, TopLoc_Location(), true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next(), ++aNbFacesInNode)
+ {
+ if (toSkipFaceMesh (aFaceIter))
+ {
+ continue;
+ }
+
+ const RWGltf_GltfFace& aGltfFace = myBinDataMap.Find (aFaceIter.Face());
+ const TCollection_AsciiString aMatId = theMaterialMap.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();
+ }
+ myWriter->EndArray();
+ myWriter->EndObject();
+ }
+ myWriter->EndArray();
+#else
+ (void )theSceneNodeMap;
+ (void )theMaterialMap;
+#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;
+ }
+
+ 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)
+ {
+ myWriter->Key ("mesh");
+ // 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->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 (const RWGltf_GltfMaterialMap& theMaterialMap)
+{
+#ifdef HAVE_RAPIDJSON
+ Standard_ProgramError_Raise_if (myWriter.get() == NULL, "Internal error: RWGltf_CafWriter::writeSamplers()");
+ if (theMaterialMap.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();
+#else
+ (void )theMaterialMap;
+#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,
+ RWGltf_GltfMaterialMap& theMaterialMap)
+{
+#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())
+ {
+ theMaterialMap.AddTextures (myWriter.get(), aFaceIter.FaceStyle(), anIsStarted);
+ }
+ }
+ if (anIsStarted)
+ {
+ myWriter->EndArray();
+ }
+#else
+ (void )theSceneNodeMap;
+ (void )theMaterialMap;
+#endif
+}
--- /dev/null
+// 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.
+
+#ifndef _RWGltf_CafWriter_HeaderFiler
+#define _RWGltf_CafWriter_HeaderFiler
+
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+#include <TColStd_MapOfAsciiString.hxx>
+#include <TDF_LabelSequence.hxx>
+#include <TopTools_ShapeMapHasher.hxx>
+#include <RWGltf_GltfBufferView.hxx>
+#include <RWGltf_GltfFace.hxx>
+#include <RWGltf_WriterTrsfFormat.hxx>
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <XCAFPrs_Style.hxx>
+
+#include <memory>
+
+class Message_ProgressIndicator;
+class RWMesh_FaceIterator;
+class RWGltf_GltfOStreamWriter;
+class RWGltf_GltfMaterialMap;
+class RWGltf_GltfSceneNodeMap;
+class TDocStd_Document;
+
+//! glTF writer context from XCAF document.
+class RWGltf_CafWriter : public Standard_Transient
+{
+ DEFINE_STANDARD_RTTIEXT(RWGltf_CafWriter, Standard_Transient)
+public:
+
+ //! Main constructor.
+ //! @param theFile [in] path to output glTF file
+ //! @param theIsBinary [in] flag to write into binary glTF format (.glb)
+ Standard_EXPORT RWGltf_CafWriter (const TCollection_AsciiString& theFile,
+ Standard_Boolean theIsBinary);
+
+ //! Destructor.
+ Standard_EXPORT virtual ~RWGltf_CafWriter();
+
+ //! Return transformation from OCCT to glTF coordinate system.
+ const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCSTrsf; }
+
+ //! Return transformation from OCCT to glTF coordinate system.
+ RWMesh_CoordinateSystemConverter& ChangeCoordinateSystemConverter() { return myCSTrsf; }
+
+ //! Set transformation from OCCT to glTF coordinate system.
+ void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCSTrsf = theConverter; }
+
+ //! Return flag to write into binary glTF format (.glb), specified within class constructor.
+ bool IsBinary() const { return myIsBinary; }
+
+ //! Return preferred transformation format for writing into glTF file; RWGltf_WriterTrsfFormat_Compact by default.
+ RWGltf_WriterTrsfFormat TransformationFormat() const { return myTrsfFormat; }
+
+ //! Set preferred transformation format for writing into glTF file.
+ void SetTransformationFormat (RWGltf_WriterTrsfFormat theFormat) { myTrsfFormat = theFormat; }
+
+ //! Return TRUE to export UV coordinates even if there are no mapped texture; FALSE by default.
+ bool IsForcedUVExport() const { return myIsForcedUVExport; }
+
+ //! Set flag to export UV coordinates even if there are no mapped texture; FALSE by default.
+ void SetForcedUVExport (bool theToForce) { myIsForcedUVExport = theToForce; }
+
+ //! Return default material definition to be used for nodes with only color defined.
+ const XCAFPrs_Style& DefaultStyle() const { return myDefaultStyle; }
+
+ //! Set default material definition to be used for nodes with only color defined.
+ void SetDefaultStyle (const XCAFPrs_Style& theStyle) { myDefaultStyle = theStyle; }
+
+ //! Write glTF file and associated binary file.
+ //! Triangulation data should be precomputed within shapes!
+ //! @param theDocument [in] input document
+ //! @param theRootLabels [in] list of root shapes to export
+ //! @param theLabelFilter [in] optional filter with document nodes to export,
+ //! with keys defined by XCAFPrs_DocumentExplorer::DefineChildId() and filled recursively
+ //! (leaves and parent assembly nodes at all levels);
+ //! when not NULL, all nodes not included into the map will be ignored
+ //! @param theFileInfo [in] map with file metadata to put into glTF header section
+ //! @param theProgress [in] optional progress indicator
+ //! @return FALSE on file writing failure
+ Standard_EXPORT virtual bool Perform (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Handle(Message_ProgressIndicator)& theProgress);
+
+ //! Write glTF file and associated binary file.
+ //! Triangulation data should be precomputed within shapes!
+ //! @param theDocument [in] input document
+ //! @param theFileInfo [in] map with file metadata to put into glTF header section
+ //! @param theProgress [in] optional progress indicator
+ //! @return FALSE on file writing failure
+ Standard_EXPORT virtual bool Perform (const Handle(TDocStd_Document)& theDocument,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Handle(Message_ProgressIndicator)& theProgress);
+
+protected:
+
+ //! Write binary data file with triangulation data.
+ //! Triangulation data should be precomputed within shapes!
+ //! @param theDocument [in] input document
+ //! @param theRootLabels [in] list of root shapes to export
+ //! @param theLabelFilter [in] optional filter with document nodes to export
+ //! @param theProgress [in] optional progress indicator
+ //! @return FALSE on file writing failure
+ Standard_EXPORT virtual bool writeBinData (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const Handle(Message_ProgressIndicator)& theProgress);
+
+ //! Write JSON file with glTF structure (should be called after writeBinData()).
+ //! @param theDocument [in] input document
+ //! @param theRootLabels [in] list of root shapes to export
+ //! @param theLabelFilter [in] optional filter with document nodes to export
+ //! @param theFileInfo [in] map with file metadata to put into glTF header section
+ //! @param theProgress [in] optional progress indicator
+ //! @return FALSE on file writing failure
+ Standard_EXPORT virtual bool writeJson (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Handle(Message_ProgressIndicator)& theProgress);
+
+protected:
+
+ //! Return TRUE if face mesh should be skipped (e.g. because it is invalid or empty).
+ Standard_EXPORT virtual Standard_Boolean toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter);
+
+ //! Write mesh nodes into binary file.
+ //! @param theGltfFace [out] glTF face definition
+ //! @param theBinFile [out] output file to write into
+ //! @param theFaceIter [in] current face to write
+ //! @param theAccessorNb [in] [out] last accessor index
+ Standard_EXPORT virtual void saveNodes (RWGltf_GltfFace& theGltfFace,
+ std::ostream& theBinFile,
+ const RWMesh_FaceIterator& theFaceIter,
+ Standard_Integer& theAccessorNb) const;
+
+ //! Write mesh normals into binary file.
+ //! @param theGltfFace [out] glTF face definition
+ //! @param theBinFile [out] output file to write into
+ //! @param theFaceIter [in] current face to write
+ //! @param theAccessorNb [in] [out] last accessor index
+ Standard_EXPORT virtual void saveNormals (RWGltf_GltfFace& theGltfFace,
+ std::ostream& theBinFile,
+ RWMesh_FaceIterator& theFaceIter,
+ Standard_Integer& theAccessorNb) const;
+
+ //! Write mesh texture UV coordinates into binary file.
+ //! @param theGltfFace [out] glTF face definition
+ //! @param theBinFile [out] output file to write into
+ //! @param theFaceIter [in] current face to write
+ //! @param theAccessorNb [in] [out] last accessor index
+ Standard_EXPORT virtual void saveTextCoords (RWGltf_GltfFace& theGltfFace,
+ std::ostream& theBinFile,
+ const RWMesh_FaceIterator& theFaceIter,
+ Standard_Integer& theAccessorNb) const;
+
+ //! Write mesh indexes into binary file.
+ //! @param theGltfFace [out] glTF face definition
+ //! @param theBinFile [out] output file to write into
+ //! @param theFaceIter [in] current face to write
+ //! @param theAccessorNb [in] [out] last accessor index
+ Standard_EXPORT virtual void saveIndices (RWGltf_GltfFace& theGltfFace,
+ std::ostream& theBinFile,
+ const RWMesh_FaceIterator& theFaceIter,
+ Standard_Integer& theAccessorNb);
+
+protected:
+
+ //! Write bufferView for vertex positions within RWGltf_GltfRootElement_Accessors section
+ //! @param theGltfFace [in] face definition to write
+ Standard_EXPORT virtual void writePositions (const RWGltf_GltfFace& theGltfFace);
+
+ //! Write bufferView for vertex normals within RWGltf_GltfRootElement_Accessors section
+ //! @param theGltfFace [in] face definition to write
+ Standard_EXPORT virtual void writeNormals (const RWGltf_GltfFace& theGltfFace);
+
+ //! Write bufferView for vertex texture coordinates within RWGltf_GltfRootElement_Accessors section
+ //! @param theGltfFace [in] face definition to write
+ Standard_EXPORT virtual void writeTextCoords (const RWGltf_GltfFace& theGltfFace);
+
+ //! Write bufferView for triangle indexes within RWGltf_GltfRootElement_Accessors section.
+ //! @param theGltfFace [in] face definition to write
+ Standard_EXPORT virtual void writeIndices (const RWGltf_GltfFace& theGltfFace);
+
+protected:
+
+ //! Write RWGltf_GltfRootElement_Accessors section.
+ //! @param theSceneNodeMap [in] ordered map of scene nodes
+ Standard_EXPORT virtual void writeAccessors (const RWGltf_GltfSceneNodeMap& theSceneNodeMap);
+
+ //! Write RWGltf_GltfRootElement_Animations section (reserved).
+ Standard_EXPORT virtual void writeAnimations();
+
+ //! Write RWGltf_GltfRootElement_Asset section.
+ //! @param theFileInfo [in] optional metadata to write into file header
+ Standard_EXPORT virtual void writeAsset (const TColStd_IndexedDataMapOfStringString& theFileInfo);
+
+ //! Write RWGltf_GltfRootElement_BufferViews section.
+ //! @param theBinDataBufferId [in] index of binary buffer with vertex data
+ Standard_EXPORT virtual void writeBufferViews (const Standard_Integer theBinDataBufferId);
+
+ //! Write RWGltf_GltfRootElement_Buffers section.
+ Standard_EXPORT virtual void writeBuffers();
+
+ //! Write RWGltf_GltfRootElement_ExtensionsUsed/RWGltf_GltfRootElement_ExtensionsRequired sections (reserved).
+ Standard_EXPORT virtual void writeExtensions();
+
+ //! Write RWGltf_GltfRootElement_Images section.
+ //! @param theSceneNodeMap [in] ordered map of scene nodes
+ //! @param theMaterialMap [out] map of materials, filled with image files used by textures
+ Standard_EXPORT virtual void writeImages (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ RWGltf_GltfMaterialMap& theMaterialMap);
+
+ //! Write RWGltf_GltfRootElement_Materials section.
+ //! @param theSceneNodeMap [in] ordered map of scene nodes
+ //! @param theMaterialMap [out] map of materials, filled with materials
+ Standard_EXPORT virtual void writeMaterials (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ RWGltf_GltfMaterialMap& theMaterialMap);
+
+ //! Write RWGltf_GltfRootElement_Meshes section.
+ //! @param theSceneNodeMap [in] ordered map of scene nodes
+ //! @param theMaterialMap [in] map of materials
+ Standard_EXPORT virtual void writeMeshes (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ const RWGltf_GltfMaterialMap& theMaterialMap);
+
+ //! Write RWGltf_GltfRootElement_Nodes section.
+ //! @param theDocument [in] input document
+ //! @param theRootLabels [in] list of root shapes to export
+ //! @param theLabelFilter [in] optional filter with document nodes to export
+ //! @param theSceneNodeMap [in] ordered map of scene nodes
+ //! @param theSceneRootNodeInds [out] sequence of scene nodes pointing to root shapes (to be used for writeScenes())
+ Standard_EXPORT virtual void writeNodes (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds);
+
+ //! Write RWGltf_GltfRootElement_Samplers section.
+ Standard_EXPORT virtual void writeSamplers (const RWGltf_GltfMaterialMap& theMaterialMap);
+
+ //! Write RWGltf_GltfRootElement_Scene section.
+ //! @param theDefSceneId [in] index of default scene (0)
+ Standard_EXPORT virtual void writeScene (const Standard_Integer theDefSceneId);
+
+ //! Write RWGltf_GltfRootElement_Scenes section.
+ //! @param theSceneRootNodeInds [in] sequence of scene nodes pointing to root shapes
+ Standard_EXPORT virtual void writeScenes (const NCollection_Sequence<Standard_Integer>& theSceneRootNodeInds);
+
+ //! Write RWGltf_GltfRootElement_Skins section (reserved).
+ Standard_EXPORT virtual void writeSkins();
+
+ //! Write RWGltf_GltfRootElement_Textures section.
+ //! @param theSceneNodeMap [in] ordered map of scene nodes
+ //! @param theMaterialMap [out] map of materials, filled with textures
+ Standard_EXPORT virtual void writeTextures (const RWGltf_GltfSceneNodeMap& theSceneNodeMap,
+ RWGltf_GltfMaterialMap& theMaterialMap);
+
+protected:
+
+ TCollection_AsciiString myFile; //!< output glTF file
+ TCollection_AsciiString myBinFileNameFull; //!< output file with binary data (full path)
+ TCollection_AsciiString myBinFileNameShort; //!< output file with binary data (short path)
+ RWGltf_WriterTrsfFormat myTrsfFormat; //!< transformation format to write into glTF file
+ Standard_Boolean myIsBinary; //!< flag to write into binary glTF format (.glb)
+ Standard_Boolean myIsForcedUVExport; //!< export UV coordinates even if there are no mapped texture
+ RWMesh_CoordinateSystemConverter myCSTrsf; //!< transformation from OCCT to glTF coordinate system
+ XCAFPrs_Style myDefaultStyle; //!< default material definition to be used for nodes with only color defined
+
+ opencascade::std::shared_ptr<RWGltf_GltfOStreamWriter>
+ myWriter; //!< JSON writer
+ RWGltf_GltfBufferView myBuffViewPos; //!< current buffer view with nodes positions
+ RWGltf_GltfBufferView myBuffViewNorm; //!< current buffer view with nodes normals
+ RWGltf_GltfBufferView myBuffViewTextCoord; //!< current buffer view with nodes UV coordinates
+ RWGltf_GltfBufferView myBuffViewInd; //!< current buffer view with triangulation indexes
+ NCollection_DataMap<TopoDS_Shape, RWGltf_GltfFace,
+ TopTools_ShapeMapHasher> myBinDataMap; //!< map for TopoDS_Face to glTF face (merging duplicates)
+ int64_t myBinDataLen64; //!< length of binary file
+
+};
+
+#endif // _RWGltf_CafWriter_HeaderFiler
}
return true;
#else
- Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support.", Message_Fail);
+ Message::DefaultMessenger()->Send ("Error: glTF reader is unavailable - OCCT has been built without RapidJSON support [HAVE_RAPIDJSON undefined].", Message_Fail);
return false;
#endif
}
--- /dev/null
+// 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_GltfMaterialMap.hxx>
+
+#include <RWGltf_GltfRootElement.hxx>
+
+#ifdef HAVE_RAPIDJSON
+ #include <RWGltf_GltfOStreamWriter.hxx>
+#endif
+
+// =======================================================================
+// function : baseColorTexture
+// purpose :
+// =======================================================================
+const Handle(Image_Texture)& RWGltf_GltfMaterialMap::baseColorTexture (const Handle(XCAFDoc_VisMaterial)& theMat)
+{
+ static const Handle(Image_Texture) THE_NULL_TEXTURE;
+ if (theMat.IsNull())
+ {
+ return THE_NULL_TEXTURE;
+ }
+ else if (theMat->HasPbrMaterial()
+ && !theMat->PbrMaterial().BaseColorTexture.IsNull())
+ {
+ return theMat->PbrMaterial().BaseColorTexture;
+ }
+ else if (theMat->HasCommonMaterial()
+ && !theMat->CommonMaterial().DiffuseTexture.IsNull())
+ {
+ return theMat->CommonMaterial().DiffuseTexture;
+ }
+ return THE_NULL_TEXTURE;
+}
+
+// =======================================================================
+// function : RWGltf_GltfMaterialMap
+// purpose :
+// =======================================================================
+RWGltf_GltfMaterialMap::RWGltf_GltfMaterialMap (const TCollection_AsciiString& theFile,
+ const Standard_Integer theDefSamplerId)
+: RWMesh_MaterialMap (theFile),
+ myWriter (NULL),
+ myDefSamplerId (theDefSamplerId),
+ myNbImages (0)
+{
+ myMatNameAsKey = false;
+}
+
+// =======================================================================
+// function : ~RWGltf_GltfMaterialMap
+// purpose :
+// =======================================================================
+RWGltf_GltfMaterialMap::~RWGltf_GltfMaterialMap()
+{
+ //
+}
+
+// =======================================================================
+// function : AddImages
+// purpose :
+// =======================================================================
+void RWGltf_GltfMaterialMap::AddImages (RWGltf_GltfOStreamWriter* theWriter,
+ const XCAFPrs_Style& theStyle,
+ Standard_Boolean& theIsStarted)
+{
+ if (theWriter == NULL
+ || theStyle.Material().IsNull()
+ || theStyle.Material()->IsEmpty())
+ {
+ return;
+ }
+
+ addImage (theWriter, baseColorTexture (theStyle.Material()), theIsStarted);
+ addImage (theWriter, theStyle.Material()->PbrMaterial().MetallicRoughnessTexture, theIsStarted);
+ addImage (theWriter, theStyle.Material()->PbrMaterial().NormalTexture, theIsStarted);
+ addImage (theWriter, theStyle.Material()->PbrMaterial().EmissiveTexture, theIsStarted);
+ addImage (theWriter, theStyle.Material()->PbrMaterial().OcclusionTexture, theIsStarted);
+}
+
+// =======================================================================
+// function : addImage
+// purpose :
+// =======================================================================
+void RWGltf_GltfMaterialMap::addImage (RWGltf_GltfOStreamWriter* theWriter,
+ const Handle(Image_Texture)& theTexture,
+ Standard_Boolean& theIsStarted)
+{
+#ifdef HAVE_RAPIDJSON
+ if (theTexture.IsNull()
+ || myImageMap.IsBound1 (theTexture)
+ || myImageFailMap.Contains (theTexture))
+ {
+ return;
+ }
+
+ TCollection_AsciiString aGltfImgKey = myNbImages;
+ ++myNbImages;
+ for (; myImageMap.IsBound2 (aGltfImgKey); ++myNbImages)
+ {
+ aGltfImgKey = myNbImages;
+ }
+
+ TCollection_AsciiString aTextureUri;
+ if (!CopyTexture (aTextureUri, theTexture, aGltfImgKey))
+ {
+ myImageFailMap.Add (theTexture);
+ return;
+ }
+
+ myImageMap.Bind (theTexture, aGltfImgKey);
+
+ if (!theIsStarted)
+ {
+ theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Images));
+ theWriter->StartArray();
+ theIsStarted = true;
+ }
+
+ theWriter->StartObject();
+ {
+ theWriter->Key ("uri");
+ theWriter->String (aTextureUri.ToCString());
+ }
+ theWriter->EndObject();
+#else
+ (void )theWriter;
+ (void )theTexture;
+ (void )theIsStarted;
+#endif
+}
+
+// =======================================================================
+// function : AddMaterial
+// purpose :
+// =======================================================================
+void RWGltf_GltfMaterialMap::AddMaterial (RWGltf_GltfOStreamWriter* theWriter,
+ const XCAFPrs_Style& theStyle,
+ Standard_Boolean& theIsStarted)
+{
+#ifdef HAVE_RAPIDJSON
+ if (theWriter == NULL
+ || ((theStyle.Material().IsNull() || theStyle.Material()->IsEmpty())
+ && !theStyle.IsSetColorSurf()))
+ {
+ return;
+ }
+
+ if (!theIsStarted)
+ {
+ theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Materials));
+ theWriter->StartArray();
+ theIsStarted = true;
+ }
+ myWriter = theWriter;
+ AddMaterial (theStyle);
+ myWriter = NULL;
+#else
+ (void )theWriter;
+ (void )theStyle;
+ (void )theIsStarted;
+#endif
+}
+
+// =======================================================================
+// function : AddTextures
+// purpose :
+// =======================================================================
+void RWGltf_GltfMaterialMap::AddTextures (RWGltf_GltfOStreamWriter* theWriter,
+ const XCAFPrs_Style& theStyle,
+ Standard_Boolean& theIsStarted)
+{
+ if (theWriter == NULL
+ || theStyle.Material().IsNull()
+ || theStyle.Material()->IsEmpty())
+ {
+ return;
+ }
+
+ addTexture (theWriter, baseColorTexture (theStyle.Material()), theIsStarted);
+ addTexture (theWriter, theStyle.Material()->PbrMaterial().MetallicRoughnessTexture, theIsStarted);
+ addTexture (theWriter, theStyle.Material()->PbrMaterial().NormalTexture, theIsStarted);
+ addTexture (theWriter, theStyle.Material()->PbrMaterial().EmissiveTexture, theIsStarted);
+ addTexture (theWriter, theStyle.Material()->PbrMaterial().OcclusionTexture, theIsStarted);
+}
+
+// =======================================================================
+// function : addTexture
+// purpose :
+// =======================================================================
+void RWGltf_GltfMaterialMap::addTexture (RWGltf_GltfOStreamWriter* theWriter,
+ const Handle(Image_Texture)& theTexture,
+ Standard_Boolean& theIsStarted)
+{
+#ifdef HAVE_RAPIDJSON
+ if (theTexture.IsNull()
+ || myTextureMap.Contains (theTexture)
+ || !myImageMap .IsBound1 (theTexture))
+ {
+ return;
+ }
+
+ const TCollection_AsciiString anImgKey = myImageMap.Find1 (theTexture);
+ myTextureMap.Add (theTexture);
+ if (anImgKey.IsEmpty())
+ {
+ return;
+ }
+
+ if (!theIsStarted)
+ {
+ theWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Textures));
+ theWriter->StartArray();
+ theIsStarted = true;
+ }
+
+ theWriter->StartObject();
+ {
+ theWriter->Key ("sampler");
+ theWriter->Int (myDefSamplerId); // mandatory field by specs
+ theWriter->Key ("source");
+ theWriter->Int (anImgKey.IntegerValue());
+ }
+ theWriter->EndObject();
+#else
+ (void )theWriter;
+ (void )theTexture;
+ (void )theIsStarted;
+#endif
+}
+
+// =======================================================================
+// function : AddMaterial
+// purpose :
+// =======================================================================
+TCollection_AsciiString RWGltf_GltfMaterialMap::AddMaterial (const XCAFPrs_Style& theStyle)
+{
+ return RWMesh_MaterialMap::AddMaterial (theStyle);
+}
+
+// =======================================================================
+// function : DefineMaterial
+// purpose :
+// =======================================================================
+void RWGltf_GltfMaterialMap::DefineMaterial (const XCAFPrs_Style& theStyle,
+ const TCollection_AsciiString& /*theKey*/,
+ const TCollection_AsciiString& theName)
+{
+#ifdef HAVE_RAPIDJSON
+ if (myWriter == NULL)
+ {
+ Standard_ProgramError::Raise ("RWGltf_GltfMaterialMap::DefineMaterial() should be called with JSON Writer");
+ return;
+ }
+
+ XCAFDoc_VisMaterialPBR aPbrMat;
+ const bool hasMaterial = !theStyle.Material().IsNull()
+ && !theStyle.Material()->IsEmpty();
+ if (hasMaterial)
+ {
+ aPbrMat = theStyle.Material()->ConvertToPbrMaterial();
+ }
+ else if (!myDefaultStyle.Material().IsNull()
+ && myDefaultStyle.Material()->HasPbrMaterial())
+ {
+ aPbrMat = myDefaultStyle.Material()->PbrMaterial();
+ }
+ if (theStyle.IsSetColorSurf())
+ {
+ aPbrMat.BaseColor.SetRGB (theStyle.GetColorSurf());
+ if (theStyle.GetColorSurfRGBA().Alpha() < 1.0f)
+ {
+ aPbrMat.BaseColor.SetAlpha (theStyle.GetColorSurfRGBA().Alpha());
+ }
+ }
+ myWriter->StartObject();
+ {
+ myWriter->Key ("name");
+ myWriter->String (theName.ToCString());
+
+ myWriter->Key ("pbrMetallicRoughness");
+ myWriter->StartObject();
+ {
+ myWriter->Key ("baseColorFactor");
+ myWriter->StartArray();
+ {
+ myWriter->Double (aPbrMat.BaseColor.GetRGB().Red());
+ myWriter->Double (aPbrMat.BaseColor.GetRGB().Green());
+ myWriter->Double (aPbrMat.BaseColor.GetRGB().Blue());
+ myWriter->Double (aPbrMat.BaseColor.Alpha());
+ }
+ myWriter->EndArray();
+
+ if (const Handle(Image_Texture)& aBaseTexture = baseColorTexture (theStyle.Material()))
+ {
+ if (myImageMap.IsBound1 (aBaseTexture))
+ {
+ myWriter->Key ("baseColorTexture");
+ myWriter->StartObject();
+ {
+ myWriter->Key ("index");
+ const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aBaseTexture);
+ if (!anImageIdx.IsEmpty())
+ {
+ myWriter->Int (anImageIdx.IntegerValue());
+ }
+ }
+ myWriter->EndObject();
+ }
+ }
+
+ if (hasMaterial
+ || aPbrMat.Metallic != 1.0f)
+ {
+ myWriter->Key ("metallicFactor");
+ myWriter->Double (aPbrMat.Metallic);
+ }
+
+ if (!aPbrMat.MetallicRoughnessTexture.IsNull()
+ && myImageMap.IsBound1 (aPbrMat.MetallicRoughnessTexture))
+ {
+ myWriter->Key ("metallicRoughnessTexture");
+ myWriter->StartObject();
+ {
+ myWriter->Key ("index");
+ const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.MetallicRoughnessTexture);
+ if (!anImageIdx.IsEmpty())
+ {
+ myWriter->Int (anImageIdx.IntegerValue());
+ }
+ }
+ myWriter->EndObject();
+ }
+
+ if (hasMaterial
+ || aPbrMat.Roughness != 1.0f)
+ {
+ myWriter->Key ("roughnessFactor");
+ myWriter->Double (aPbrMat.Roughness);
+ }
+ }
+ myWriter->EndObject();
+
+ if (theStyle.Material().IsNull()
+ || theStyle.Material()->IsDoubleSided())
+ {
+ myWriter->Key ("doubleSided");
+ myWriter->Bool (true);
+ }
+
+ const Graphic3d_AlphaMode anAlphaMode = !theStyle.Material().IsNull() ? theStyle.Material()->AlphaMode() : Graphic3d_AlphaMode_BlendAuto;
+ switch (anAlphaMode)
+ {
+ case Graphic3d_AlphaMode_BlendAuto:
+ {
+ if (aPbrMat.BaseColor.Alpha() < 1.0f)
+ {
+ myWriter->Key ("alphaMode");
+ myWriter->String ("BLEND");
+ }
+ break;
+ }
+ case Graphic3d_AlphaMode_Opaque:
+ {
+ break;
+ }
+ case Graphic3d_AlphaMode_Mask:
+ {
+ myWriter->Key ("alphaMode");
+ myWriter->String ("MASK");
+ break;
+ }
+ case Graphic3d_AlphaMode_Blend:
+ {
+ myWriter->Key ("alphaMode");
+ myWriter->String ("BLEND");
+ break;
+ }
+ }
+ if (!theStyle.Material().IsNull()
+ && theStyle.Material()->AlphaCutOff() != 0.5f)
+ {
+ myWriter->Key ("alphaCutoff");
+ myWriter->Double (theStyle.Material()->AlphaCutOff());
+ }
+
+ if (aPbrMat.EmissiveFactor != Graphic3d_Vec3 (0.0f, 0.0f, 0.0f))
+ {
+ myWriter->Key ("emissiveFactor");
+ myWriter->StartArray();
+ {
+ myWriter->Double (aPbrMat.EmissiveFactor.r());
+ myWriter->Double (aPbrMat.EmissiveFactor.g());
+ myWriter->Double (aPbrMat.EmissiveFactor.b());
+ }
+ myWriter->EndArray();
+ }
+ if (!aPbrMat.EmissiveTexture.IsNull()
+ && myImageMap.IsBound1 (aPbrMat.EmissiveTexture))
+ {
+ myWriter->Key ("emissiveTexture");
+ myWriter->StartObject();
+ {
+ myWriter->Key ("index");
+ const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.EmissiveTexture);
+ if (!anImageIdx.IsEmpty())
+ {
+ myWriter->Int (anImageIdx.IntegerValue());
+ }
+ }
+ myWriter->EndObject();
+ }
+
+ if (!aPbrMat.NormalTexture.IsNull()
+ && myImageMap.IsBound1 (aPbrMat.NormalTexture))
+ {
+ myWriter->Key ("normalTexture");
+ myWriter->StartObject();
+ {
+ myWriter->Key ("index");
+ const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.NormalTexture);
+ if (!anImageIdx.IsEmpty())
+ {
+ myWriter->Int (anImageIdx.IntegerValue());
+ }
+ }
+ myWriter->EndObject();
+ }
+
+ if (!aPbrMat.OcclusionTexture.IsNull()
+ && myImageMap.IsBound1 (aPbrMat.OcclusionTexture))
+ {
+ myWriter->Key ("occlusionTexture");
+ myWriter->StartObject();
+ {
+ myWriter->Key ("index");
+ const TCollection_AsciiString& anImageIdx = myImageMap.Find1 (aPbrMat.OcclusionTexture);
+ if (!anImageIdx.IsEmpty())
+ {
+ myWriter->Int (anImageIdx.IntegerValue());
+ }
+ }
+ myWriter->EndObject();
+ }
+ }
+ myWriter->EndObject();
+#else
+ (void )theStyle;
+ (void )theName;
+#endif
+}
--- /dev/null
+// 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.
+
+#ifndef _RWGltf_GltfMaterialMap_HeaderFile
+#define _RWGltf_GltfMaterialMap_HeaderFile
+
+#include <RWMesh_MaterialMap.hxx>
+
+class RWGltf_GltfOStreamWriter;
+
+//! Material manager for exporting into glTF format.
+class RWGltf_GltfMaterialMap : public RWMesh_MaterialMap
+{
+public:
+
+ //! Main constructor.
+ Standard_EXPORT RWGltf_GltfMaterialMap (const TCollection_AsciiString& theFile,
+ const Standard_Integer theDefSamplerId);
+
+ //! Destructor.
+ Standard_EXPORT virtual ~RWGltf_GltfMaterialMap();
+
+ //! Add material images.
+ Standard_EXPORT void AddImages (RWGltf_GltfOStreamWriter* theWriter,
+ const XCAFPrs_Style& theStyle,
+ Standard_Boolean& theIsStarted);
+
+ //! Add material.
+ Standard_EXPORT void AddMaterial (RWGltf_GltfOStreamWriter* theWriter,
+ const XCAFPrs_Style& theStyle,
+ Standard_Boolean& theIsStarted);
+ //! Add material textures.
+ Standard_EXPORT void AddTextures (RWGltf_GltfOStreamWriter* theWriter,
+ const XCAFPrs_Style& theStyle,
+ Standard_Boolean& theIsStarted);
+
+ //! Return extent of images map.
+ Standard_Integer NbImages() const { return myImageMap.Extent(); }
+
+ //! Return extent of textures map.
+ Standard_Integer NbTextures() const { return myTextureMap.Extent(); }
+
+public:
+
+ //! Return base color texture.
+ Standard_EXPORT static const Handle(Image_Texture)& baseColorTexture (const Handle(XCAFDoc_VisMaterial)& theMat);
+
+protected:
+
+ //! Add texture image.
+ Standard_EXPORT void addImage (RWGltf_GltfOStreamWriter* theWriter,
+ const Handle(Image_Texture)& theTexture,
+ Standard_Boolean& theIsStarted);
+
+ //! Add texture.
+ Standard_EXPORT void addTexture (RWGltf_GltfOStreamWriter* theWriter,
+ const Handle(Image_Texture)& theTexture,
+ Standard_Boolean& theIsStarted);
+
+ //! Add material
+ Standard_EXPORT virtual TCollection_AsciiString AddMaterial (const XCAFPrs_Style& theStyle) Standard_OVERRIDE;
+
+ //! Virtual method actually defining the material (e.g. export to the file).
+ Standard_EXPORT virtual void DefineMaterial (const XCAFPrs_Style& theStyle,
+ const TCollection_AsciiString& theKey,
+ const TCollection_AsciiString& theName) Standard_OVERRIDE;
+
+protected:
+
+ RWGltf_GltfOStreamWriter* myWriter;
+ NCollection_DoubleMap<Handle(Image_Texture), TCollection_AsciiString, Image_Texture, TCollection_AsciiString> myImageMap;
+ NCollection_Map<Handle(Image_Texture), Image_Texture> myTextureMap;
+ Standard_Integer myDefSamplerId;
+ Standard_Integer myNbImages;
+
+};
+
+#endif // _RWGltf_GltfMaterialMap_HeaderFile
--- /dev/null
+// Copyright (c) 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.
+
+#ifndef _RWGltf_GltfOStreamWriter_HeaderFile
+#define _RWGltf_GltfOStreamWriter_HeaderFile
+
+#include <rapidjson/prettywriter.h>
+#include <rapidjson/ostreamwrapper.h>
+
+//! rapidjson::Writer wrapper for forward declaration.
+class RWGltf_GltfOStreamWriter : public rapidjson::Writer<rapidjson::OStreamWrapper>
+{
+public:
+ //! Main constructor.
+ RWGltf_GltfOStreamWriter (rapidjson::OStreamWrapper& theOStream)
+ : rapidjson::Writer<rapidjson::OStreamWrapper> (theOStream) {}
+};
+
+#endif // _RWGltf_GltfOStreamWriter_HeaderFile
--- /dev/null
+// Copyright (c) 2018-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.
+
+#ifndef _RWGltf_GltfSceneNodeMap_HeaderFile
+#define _RWGltf_GltfSceneNodeMap_HeaderFile
+
+#include <NCollection_IndexedMap.hxx>
+#include <XCAFPrs_DocumentExplorer.hxx>
+
+//! Indexed map of scene nodes with custom search algorithm.
+class RWGltf_GltfSceneNodeMap : public NCollection_IndexedMap<XCAFPrs_DocumentNode, XCAFPrs_DocumentNode>
+{
+public:
+
+ //! Empty constructor.
+ RWGltf_GltfSceneNodeMap() {}
+
+ //! Find index from document node string identifier.
+ Standard_Integer FindIndex (const TCollection_AsciiString& theNodeId) const
+ {
+ if (IsEmpty())
+ {
+ return 0;
+ }
+
+ for (IndexedMapNode* aNode1Iter = (IndexedMapNode* )myData1[::HashCode (theNodeId, NbBuckets())]; aNode1Iter != NULL; aNode1Iter = (IndexedMapNode* )aNode1Iter->Next())
+ {
+ if (::IsEqual (aNode1Iter->Key1().Id, theNodeId))
+ {
+ return aNode1Iter->Index();
+ }
+ }
+ return 0;
+ }
+
+};
+
+#endif // _RWGltf_GltfSceneNodeMap_HeaderFile
--- /dev/null
+// 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.
+
+#ifndef _RWGltf_WriterTrsfFormat_HeaderFile
+#define _RWGltf_WriterTrsfFormat_HeaderFile
+
+//! Transformation format.
+enum RWGltf_WriterTrsfFormat
+{
+ RWGltf_WriterTrsfFormat_Compact = 0, //!< automatically choose most compact representation between Mat4 and TRS
+ RWGltf_WriterTrsfFormat_Mat4 = 1, //!< 4x4 transformation Matrix
+ RWGltf_WriterTrsfFormat_TRS = 2, //!< transformation decomposed into Translation vector, Rotation quaternion and Scale factor (T * R * S)
+};
+enum { RWGltf_WriterTrsfFormat_LOWER = 0, RWGltf_WriterTrsfFormat_UPPER = RWGltf_WriterTrsfFormat_TRS }; // aliases
+
+#endif // _RWGltf_WriterTrsfFormat_HeaderFile
RWMesh_CoordinateSystemConverter.hxx
RWMesh_CafReader.cxx
RWMesh_CafReader.hxx
+RWMesh_FaceIterator.cxx
+RWMesh_FaceIterator.hxx
+RWMesh_MaterialMap.cxx
+RWMesh_MaterialMap.hxx
RWMesh_NodeAttributes.hxx
--- /dev/null
+// 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 <RWMesh_FaceIterator.hxx>
+
+#include <BRepAdaptor_Surface.hxx>
+#include <BRep_Tool.hxx>
+#include <TopExp.hxx>
+#include <TopoDS.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs.hxx>
+
+// =======================================================================
+// function : RWMesh_FaceIterator
+// purpose :
+// =======================================================================
+RWMesh_FaceIterator::RWMesh_FaceIterator (const TDF_Label& theLabel,
+ const TopLoc_Location& theLocation,
+ const Standard_Boolean theToMapColors,
+ const XCAFPrs_Style& theStyle)
+: myDefStyle (theStyle),
+ myToMapColors (theToMapColors),
+ mySLTool (1, 1e-12),
+ myNodes (NULL),
+ myNormals (NULL),
+ myNodeUVs (NULL),
+ myHasNormals (false),
+ myIsMirrored (false),
+ myHasFaceColor (false)
+{
+ TopoDS_Shape aShape;
+ if (!XCAFDoc_ShapeTool::GetShape (theLabel, aShape)
+ || aShape.IsNull())
+ {
+ return;
+ }
+
+ aShape.Location (theLocation);
+ myFaceIter.Init (aShape, TopAbs_FACE);
+
+ if (theToMapColors)
+ {
+ dispatchStyles (theLabel, theLocation, theStyle);
+ }
+
+ Next();
+}
+
+// =======================================================================
+// function : dispatchStyles
+// purpose :
+// =======================================================================
+void RWMesh_FaceIterator::dispatchStyles (const TDF_Label& theLabel,
+ const TopLoc_Location& theLocation,
+ const XCAFPrs_Style& theStyle)
+{
+ TopLoc_Location aDummyLoc;
+ XCAFPrs_IndexedDataMapOfShapeStyle aStyles;
+ XCAFPrs::CollectStyleSettings (theLabel, aDummyLoc, aStyles);
+
+ Standard_Integer aNbTypes[TopAbs_SHAPE] = {};
+ for (Standard_Integer aTypeIter = TopAbs_FACE; aTypeIter >= TopAbs_COMPOUND; --aTypeIter)
+ {
+ if (aTypeIter != TopAbs_FACE
+ && aNbTypes[aTypeIter] == 0)
+ {
+ continue;
+ }
+
+ for (XCAFPrs_IndexedDataMapOfShapeStyle::Iterator aStyleIter (aStyles); aStyleIter.More(); aStyleIter.Next())
+ {
+ const TopoDS_Shape& aKeyShape = aStyleIter.Key();
+ const TopAbs_ShapeEnum aKeyShapeType = aKeyShape.ShapeType();
+ if (aTypeIter == TopAbs_FACE)
+ {
+ ++aNbTypes[aKeyShapeType];
+ }
+ if (aTypeIter != aKeyShapeType)
+ {
+ continue;
+ }
+
+ XCAFPrs_Style aCafStyle = aStyleIter.Value();
+ if (!aCafStyle.IsSetColorCurv()
+ && theStyle.IsSetColorCurv())
+ {
+ aCafStyle.SetColorCurv (theStyle.GetColorCurv());
+ }
+ if (!aCafStyle.IsSetColorSurf()
+ && theStyle.IsSetColorSurf())
+ {
+ aCafStyle.SetColorSurf (theStyle.GetColorSurfRGBA());
+ }
+ if (aCafStyle.Material().IsNull()
+ && !theStyle.Material().IsNull())
+ {
+ aCafStyle.SetMaterial (theStyle.Material());
+ }
+
+ TopoDS_Shape aKeyShapeLocated = aKeyShape.Located (theLocation);
+ if (aKeyShapeType == TopAbs_FACE)
+ {
+ myStyles.Bind (aKeyShapeLocated, aCafStyle);
+ }
+ else
+ {
+ for (TopExp_Explorer aFaceIter (aKeyShapeLocated, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
+ {
+ if (!myStyles.IsBound (aFaceIter.Current()))
+ {
+ myStyles.Bind (aFaceIter.Current(), aCafStyle);
+ }
+ }
+ }
+ }
+ }
+}
+
+// =======================================================================
+// function : normal
+// purpose :
+// =======================================================================
+gp_Dir RWMesh_FaceIterator::normal (Standard_Integer theNode)
+{
+ gp_Dir aNormal (gp::DZ());
+ if (myNormals != NULL)
+ {
+ const Standard_Integer aNodeIndex = theNode - myNodes->Lower();
+ const Graphic3d_Vec3 aNormVec3 (myNormals->Value (myNormals->Lower() + aNodeIndex * 3),
+ myNormals->Value (myNormals->Lower() + aNodeIndex * 3 + 1),
+ myNormals->Value (myNormals->Lower() + aNodeIndex * 3 + 2));
+ if (aNormVec3.Modulus() != 0.0f)
+ {
+ aNormal.SetCoord (aNormVec3.x(), aNormVec3.y(), aNormVec3.z());
+ }
+ }
+ else if (myHasNormals
+ && myNodeUVs != NULL)
+ {
+ const gp_XY& anUV = myNodeUVs->Value (theNode).XY();
+ mySLTool.SetParameters (anUV.X(), anUV.Y());
+ if (mySLTool.IsNormalDefined())
+ {
+ aNormal = mySLTool.Normal();
+ }
+ }
+ return aNormal;
+}
+
+// =======================================================================
+// function : Next
+// purpose :
+// =======================================================================
+void RWMesh_FaceIterator::Next()
+{
+ for (; myFaceIter.More(); myFaceIter.Next())
+ {
+ myFace = TopoDS::Face (myFaceIter.Current());
+ myPolyTriang = BRep_Tool::Triangulation (myFace, myFaceLocation);
+ myTrsf = myFaceLocation.Transformation();
+ if (myPolyTriang.IsNull()
+ || myPolyTriang->Triangles().Length() == 0)
+ {
+ resetFace();
+ continue;
+ }
+
+ initFace();
+ myFaceIter.Next();
+ return;
+ }
+
+ resetFace();
+}
+
+// =======================================================================
+// function : initFace
+// purpose :
+// =======================================================================
+void RWMesh_FaceIterator::initFace()
+{
+ myHasNormals = false;
+ myHasFaceColor = false;
+ myIsMirrored = myTrsf.VectorialPart().Determinant() < 0.0;
+ myNormals = NULL;
+ myNodeUVs = NULL;
+
+ myNodes = &myPolyTriang->Nodes();
+ if (myPolyTriang->HasNormals())
+ {
+ myNormals = &myPolyTriang->Normals();
+ myHasNormals = true;
+ }
+ if (myPolyTriang->HasUVNodes())
+ {
+ myNodeUVs = &myPolyTriang->UVNodes();
+ if (!myHasNormals)
+ {
+ TopoDS_Face aFaceFwd = TopoDS::Face (myFace.Oriented (TopAbs_FORWARD));
+ aFaceFwd.Location (TopLoc_Location());
+ TopLoc_Location aLoc;
+ if (!BRep_Tool::Surface (aFaceFwd, aLoc).IsNull())
+ {
+ myFaceAdaptor.Initialize (aFaceFwd, false);
+ mySLTool.SetSurface (myFaceAdaptor);
+ myHasNormals = true;
+ }
+ }
+ }
+ if (!myToMapColors)
+ {
+ return;
+ }
+
+ if (!myStyles.Find (myFace, myFaceStyle))
+ {
+ myFaceStyle = myDefStyle;
+ }
+
+ if (!myFaceStyle.Material().IsNull())
+ {
+ myHasFaceColor = true;
+ myFaceColor = myFaceStyle.Material()->BaseColor();
+ }
+ else if (myFaceStyle.IsSetColorSurf())
+ {
+ myHasFaceColor = true;
+ myFaceColor = myFaceStyle.GetColorSurfRGBA();
+ }
+}
--- /dev/null
+// 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.
+
+#ifndef _RWMesh_FaceIterator_HeaderFile
+#define _RWMesh_FaceIterator_HeaderFile
+
+#include <BRepLProp_SLProps.hxx>
+#include <gp_Trsf.hxx>
+#include <NCollection_DataMap.hxx>
+#include <Poly_Array1OfTriangle.hxx>
+#include <Poly_Triangulation.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS_Face.hxx>
+#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
+#include <XCAFPrs_Style.hxx>
+
+#include <algorithm>
+
+class TDF_Label;
+
+//! Auxiliary class to iterate through triangulated faces.
+class RWMesh_FaceIterator
+{
+public:
+
+ //! Main constructor.
+ Standard_EXPORT RWMesh_FaceIterator (const TDF_Label& theLabel,
+ const TopLoc_Location& theLocation,
+ const Standard_Boolean theToMapColors = false,
+ const XCAFPrs_Style& theStyle = XCAFPrs_Style());
+
+ //! Return true if iterator points to the valid triangulation.
+ bool More() const { return !myPolyTriang.IsNull(); }
+
+ //! Find next value.
+ Standard_EXPORT void Next();
+
+ //! Return current face.
+ const TopoDS_Face& Face() const { return myFace; }
+
+ //! Return current face triangulation.
+ const Handle(Poly_Triangulation)& Triangulation() const { return myPolyTriang; }
+
+ //! Return true if mesh data is defined.
+ bool IsEmptyMesh() const
+ {
+ return myPolyTriang.IsNull()
+ || (myPolyTriang->NbNodes() < 1 && myPolyTriang->NbTriangles() < 1);
+ }
+
+public:
+
+ //! Return face material.
+ const XCAFPrs_Style& FaceStyle() const { return myFaceStyle; }
+
+ //! Return TRUE if face color is set.
+ bool HasFaceColor() const { return myHasFaceColor; }
+
+ //! Return face color.
+ const Quantity_ColorRGBA& FaceColor() const { return myFaceColor; }
+
+public:
+
+ //! Return number of elements of specific type for the current face.
+ Standard_Integer NbTriangles() const { return myPolyTriang->NbTriangles(); }
+
+ //! Lower element index in current triangulation.
+ Standard_Integer ElemLower() const { return myPolyTriang->Triangles().Lower(); }
+
+ //! Upper element index in current triangulation.
+ Standard_Integer ElemUpper() const { return myPolyTriang->Triangles().Upper(); }
+
+ //! Return triangle with specified index with applied Face orientation.
+ Poly_Triangle TriangleOriented (Standard_Integer theElemIndex) const
+ {
+ Poly_Triangle aTri = triangle (theElemIndex);
+ if ((myFace.Orientation() == TopAbs_REVERSED) ^ myIsMirrored)
+ {
+ return Poly_Triangle (aTri.Value (1), aTri.Value (3), aTri.Value (2));
+ }
+ return aTri;
+ }
+
+public:
+
+ //! Return true if triangulation has defined normals.
+ bool HasNormals() const { return myHasNormals; }
+
+ //! Return true if triangulation has defined normals.
+ bool HasTexCoords() const { return myNodeUVs != NULL; }
+
+ //! Return normal at specified node index with face transformation applied and face orientation applied.
+ gp_Dir NormalTransformed (Standard_Integer theNode)
+ {
+ gp_Dir aNorm = normal (theNode);
+ if (myTrsf.Form() != gp_Identity)
+ {
+ aNorm.Transform (myTrsf);
+ }
+ if (myFace.Orientation() == TopAbs_REVERSED)
+ {
+ aNorm.Reverse();
+ }
+ return aNorm;
+ }
+
+ //! Return number of nodes for the current face.
+ Standard_Integer NbNodes() const
+ {
+ return !myPolyTriang.IsNull()
+ ? myPolyTriang->Nodes().Length()
+ : 0;
+ }
+
+ //! Lower node index in current triangulation.
+ Standard_Integer NodeLower() const { return myPolyTriang->Nodes().Lower(); }
+
+ //! Upper node index in current triangulation.
+ Standard_Integer NodeUpper() const { return myPolyTriang->Nodes().Upper(); }
+
+ //! Return the node with specified index with applied transformation.
+ gp_Pnt NodeTransformed (const Standard_Integer theNode) const
+ {
+ gp_Pnt aNode = node (theNode);
+ aNode.Transform (myTrsf);
+ return aNode;
+ }
+
+ //! Return texture coordinates for the node.
+ gp_Pnt2d NodeTexCoord (const Standard_Integer theNode) const
+ {
+ return myNodeUVs != NULL ? myNodeUVs->Value (theNode) : gp_Pnt2d();
+ }
+
+public:
+
+ //! Return the node with specified index with applied transformation.
+ gp_Pnt node (const Standard_Integer theNode) const { return myPolyTriang->Nodes().Value (theNode); }
+
+ //! Return normal at specified node index without face transformation applied.
+ Standard_EXPORT gp_Dir normal (Standard_Integer theNode);
+
+ //! Return triangle with specified index.
+ Poly_Triangle triangle (Standard_Integer theElemIndex) const { return myPolyTriang->Triangles().Value (theElemIndex); }
+
+private:
+
+ //! Dispatch face styles.
+ void dispatchStyles (const TDF_Label& theLabel,
+ const TopLoc_Location& theLocation,
+ const XCAFPrs_Style& theStyle);
+
+ //! Reset information for current face.
+ void resetFace()
+ {
+ myPolyTriang.Nullify();
+ myFace.Nullify();
+ myNodes = NULL;
+ myNormals = NULL;
+ myNodeUVs = NULL;
+ myHasNormals = false;
+ myHasFaceColor = false;
+ myFaceColor = Quantity_ColorRGBA();
+ myFaceStyle = XCAFPrs_Style();
+ }
+
+ //! Initialize face properties.
+ void initFace();
+
+private:
+
+ NCollection_DataMap<TopoDS_Shape, XCAFPrs_Style, TopTools_ShapeMapHasher>
+ myStyles; //!< Face -> Style map
+ XCAFPrs_Style myDefStyle; //!< default style for faces without dedicated style
+ Standard_Boolean myToMapColors; //!< flag to dispatch styles
+
+ TopExp_Explorer myFaceIter; //!< face explorer
+ TopoDS_Face myFace; //!< current face
+ Handle(Poly_Triangulation) myPolyTriang; //!< triangulation of current face
+ TopLoc_Location myFaceLocation; //!< current face location
+ BRepLProp_SLProps mySLTool; //!< auxiliary tool for fetching normals from surface
+ BRepAdaptor_Surface myFaceAdaptor; //!< surface adaptor for fetching normals from surface
+ const TColgp_Array1OfPnt* myNodes; //!< node positions of current face
+ const TShort_Array1OfShortReal* myNormals; //!< node normals of current face
+ const TColgp_Array1OfPnt2d* myNodeUVs; //!< node UV coordinates of current face
+ Standard_Boolean myHasNormals; //!< flag indicating that current face has normals
+ gp_Trsf myTrsf; //!< current face transformation
+ Standard_Boolean myIsMirrored; //!< flag indicating that face triangles should be mirrored
+ XCAFPrs_Style myFaceStyle; //!< current face style
+ Quantity_ColorRGBA myFaceColor; //!< current face color
+ Standard_Boolean myHasFaceColor; //!< flag indicating that current face has assigned color
+
+};
+
+#endif // _RWMesh_FaceIterator_HeaderFile
--- /dev/null
+// 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 <RWMesh_MaterialMap.hxx>
+
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <OSD_Directory.hxx>
+#include <OSD_File.hxx>
+#include <OSD_Path.hxx>
+#include <OSD_Protection.hxx>
+#include <TDataStd_Name.hxx>
+#include <TDF_Label.hxx>
+
+// =======================================================================
+// function : RWMesh_MaterialMap
+// purpose :
+// =======================================================================
+RWMesh_MaterialMap::RWMesh_MaterialMap (const TCollection_AsciiString& theFile)
+: myFileName (theFile),
+ myKeyPrefix ("mat_"),
+ myNbMaterials (0),
+ myIsFailed (false),
+ myMatNameAsKey (true)
+{
+ TCollection_AsciiString aFileName, aFileExt;
+ OSD_Path::FolderAndFileFromPath (theFile, myFolder, aFileName);
+ OSD_Path::FileNameAndExtension (aFileName, myShortFileNameBase, aFileExt);
+}
+
+// =======================================================================
+// function : ~RWMesh_MaterialMap
+// purpose :
+// =======================================================================
+RWMesh_MaterialMap::~RWMesh_MaterialMap()
+{
+ //
+}
+
+// =======================================================================
+// function : AddMaterial
+// purpose :
+// =======================================================================
+TCollection_AsciiString RWMesh_MaterialMap::AddMaterial (const XCAFPrs_Style& theStyle)
+{
+ if (myStyles.IsBound1 (theStyle))
+ {
+ return myStyles.Find1 (theStyle);
+ }
+
+ TCollection_AsciiString aMatKey, aMatName, aMatNameSuffix;
+ int aCounter = 0;
+ int* aCounterPtr = &myNbMaterials;
+ if (myMatNameAsKey)
+ {
+ if (!theStyle.Material().IsNull()
+ && !theStyle.Material()->IsEmpty())
+ {
+ aCounterPtr = &aCounter;
+ Handle(TDataStd_Name) aNodeName;
+ if (!theStyle.Material()->Label().IsNull()
+ && theStyle.Material()->Label().FindAttribute (TDataStd_Name::GetID(), aNodeName))
+ {
+ aMatName = aNodeName->Get();
+ }
+ else
+ {
+ aMatName = "mat";
+ }
+ aMatNameSuffix = aMatName;
+ }
+ else
+ {
+ ++myNbMaterials;
+ aMatNameSuffix = myKeyPrefix;
+ aMatName = aMatNameSuffix + myNbMaterials;
+ }
+ aMatKey = aMatName;
+ }
+ else
+ {
+ aMatKey = myNbMaterials++; // starts from 0
+ aMatNameSuffix = myKeyPrefix;
+ aMatName = aMatNameSuffix + aMatKey;
+ }
+
+ for (;; ++(*aCounterPtr))
+ {
+ if (myStyles.IsBound2 (aMatKey))
+ {
+ if (myMatNameAsKey)
+ {
+ aMatName = aMatNameSuffix + (*aCounterPtr);
+ aMatKey = aMatName;
+ }
+ else
+ {
+ aMatKey = *aCounterPtr;
+ aMatName = aMatNameSuffix + aMatKey;
+ }
+ continue;
+ }
+ break;
+ }
+
+ myStyles.Bind (theStyle, aMatKey);
+ DefineMaterial (theStyle, aMatKey, aMatName);
+ return aMatKey;
+}
+
+// =======================================================================
+// function : copyFileTo
+// purpose :
+// =======================================================================
+bool RWMesh_MaterialMap::copyFileTo (const TCollection_AsciiString& theFileSrc,
+ const TCollection_AsciiString& theFileDst)
+{
+ if (theFileSrc.IsEmpty()
+ || theFileDst.IsEmpty())
+ {
+ return false;
+ }
+ else if (theFileSrc == theFileDst)
+ {
+ return true;
+ }
+
+ try
+ {
+ OSD_Path aSrcPath (theFileSrc);
+ OSD_Path aDstPath (theFileDst);
+ OSD_File aFileSrc (aSrcPath);
+ if (!aFileSrc.Exists())
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Failed to copy file - source file '")
+ + theFileSrc + "' does not exist\n", Message_Fail);
+ return false;
+ }
+ aFileSrc.Copy (aDstPath);
+ return !aFileSrc.Failed();
+ }
+ catch (Standard_Failure const& theException)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString("Failed to copy file\n") +
+ theException.GetMessageString(), Message_Fail);
+ return false;
+ }
+}
+
+// =======================================================================
+// function : CopyTexture
+// purpose :
+// =======================================================================
+bool RWMesh_MaterialMap::CopyTexture (TCollection_AsciiString& theResTexture,
+ const Handle(Image_Texture)& theTexture,
+ const TCollection_AsciiString& theKey)
+{
+ CreateTextureFolder();
+
+ TCollection_AsciiString aTexFileName;
+ TCollection_AsciiString aTextureSrc = theTexture->FilePath();
+ if (!aTextureSrc.IsEmpty())
+ {
+ TCollection_AsciiString aSrcTexFolder;
+ OSD_Path::FolderAndFileFromPath (aTextureSrc, aSrcTexFolder, aTexFileName);
+ const TCollection_AsciiString aResTexFile = myTexFolder + aTexFileName;
+ theResTexture = myTexFolderShort + aTexFileName;
+ return copyFileTo (aTextureSrc, aResTexFile);
+ }
+
+ TCollection_AsciiString anExt = theTexture->ProbeImageFileFormat();
+ if (anExt.IsEmpty())
+ {
+ anExt = "bin";
+ }
+ aTexFileName = theKey + "." + anExt;
+
+ const TCollection_AsciiString aResTexFile = myTexFolder + aTexFileName;
+ theResTexture = myTexFolderShort + aTexFileName;
+ return theTexture->WriteImage (aResTexFile);
+}
+
+// =======================================================================
+// function : CreateTextureFolder
+// purpose :
+// =======================================================================
+bool RWMesh_MaterialMap::CreateTextureFolder()
+{
+ if (!myTexFolder.IsEmpty())
+ {
+ return true;
+ }
+
+ myTexFolderShort = myShortFileNameBase + "_textures/";
+ myTexFolder = myFolder + "/" + myTexFolderShort;
+ OSD_Path aTexFolderPath (myTexFolder);
+ OSD_Directory aTexDir (aTexFolderPath);
+ if (aTexDir.Exists())
+ {
+ return true;
+ }
+
+ OSD_Path aResFolderPath (myFolder);
+ OSD_Directory aResDir (aResFolderPath);
+ if (!aResDir.Exists())
+ {
+ return false;
+ }
+ const OSD_Protection aParentProt = aResDir.Protection();
+ OSD_Protection aProt = aParentProt;
+ if (aProt.User() == OSD_None)
+ {
+ aProt.SetUser (OSD_RWXD);
+ }
+ if (aProt.System() == OSD_None)
+ {
+ aProt.SetSystem (OSD_RWXD);
+ }
+
+ aTexDir.Build (aProt);
+ if (aTexDir.Failed())
+ {
+ // fallback to the same folder as output model file
+ myTexFolder = myFolder;
+ myTexFolderShort.Clear();
+ return true;
+ }
+ return true;
+}
--- /dev/null
+// 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.
+
+#ifndef _RWMesh_MaterialMap_HeaderFile
+#define _RWMesh_MaterialMap_HeaderFile
+
+#include <NCollection_DoubleMap.hxx>
+#include <NCollection_Map.hxx>
+#include <XCAFPrs_Style.hxx>
+
+//! Material manager.
+//! Provides an interface for collecting all materials within the document before writing it into file,
+//! and for copying associated image files (textures) into sub-folder near by exported model.
+class RWMesh_MaterialMap
+{
+public:
+
+ //! Main constructor.
+ Standard_EXPORT RWMesh_MaterialMap (const TCollection_AsciiString& theFile);
+
+ //! Destructor.
+ Standard_EXPORT virtual ~RWMesh_MaterialMap();
+
+ //! Return default material definition to be used for nodes with only color defined.
+ const XCAFPrs_Style& DefaultStyle() const { return myDefaultStyle; }
+
+ //! Set default material definition to be used for nodes with only color defined.
+ void SetDefaultStyle (const XCAFPrs_Style& theStyle) { myDefaultStyle = theStyle; }
+
+ //! Find already registered material
+ TCollection_AsciiString FindMaterial (const XCAFPrs_Style& theStyle) const
+ {
+ if (myStyles.IsBound1 (theStyle))
+ {
+ return myStyles.Find1 (theStyle);
+ }
+ return TCollection_AsciiString();
+ }
+
+ //! Register material and return its name identifier.
+ Standard_EXPORT virtual TCollection_AsciiString AddMaterial (const XCAFPrs_Style& theStyle);
+
+ //! Create texture folder "modelName/textures"; for example:
+ //! MODEL: Path/ModelName.gltf
+ //! IMAGES: Path/ModelName/textures/
+ //! Warning! Output folder is NOT cleared.
+ Standard_EXPORT virtual bool CreateTextureFolder();
+
+ //! Copy and rename texture file to the new location.
+ //! @param theResTexture [out] result texture file path (relative to the model)
+ //! @param theTexture [in] original texture
+ //! @param theKey [in] material key
+ Standard_EXPORT virtual bool CopyTexture (TCollection_AsciiString& theResTexture,
+ const Handle(Image_Texture)& theTexture,
+ const TCollection_AsciiString& theKey);
+
+ //! Virtual method actually defining the material (e.g. export to the file).
+ virtual void DefineMaterial (const XCAFPrs_Style& theStyle,
+ const TCollection_AsciiString& theKey,
+ const TCollection_AsciiString& theName) = 0;
+
+ //! Return failed flag.
+ bool IsFailed() const { return myIsFailed; }
+
+protected:
+
+ //! Copy file to another place.
+ Standard_EXPORT static bool copyFileTo (const TCollection_AsciiString& theFileSrc,
+ const TCollection_AsciiString& theFileDst);
+
+protected:
+
+ TCollection_AsciiString myFolder; //!< output folder for glTF file
+ TCollection_AsciiString myTexFolder; //!< output folder for images (full path)
+ TCollection_AsciiString myTexFolderShort; //!< output folder for images (short path)
+ TCollection_AsciiString myFileName; //!< output glTF file path
+ TCollection_AsciiString myShortFileNameBase; //!< output glTF file name without extension
+ TCollection_AsciiString myKeyPrefix; //!< prefix for generated keys
+ NCollection_DoubleMap<XCAFPrs_Style, TCollection_AsciiString,
+ XCAFPrs_Style, TCollection_AsciiString>
+ myStyles; //!< map of processed styles
+ NCollection_Map<Handle(Image_Texture), Image_Texture>
+ myImageFailMap; //!< map of images failed to be copied
+ XCAFPrs_Style myDefaultStyle; //!< default material definition to be used for nodes with only color defined
+ Standard_Integer myNbMaterials; //!< number of registered materials
+ Standard_Boolean myIsFailed; //!< flag indicating failure
+ Standard_Boolean myMatNameAsKey; //!< flag indicating usage of material name as key
+
+};
+
+#endif // _RWMesh_MaterialMap_HeaderFile
TKVRML
TKLCAF
TKDCAF
+TKXCAF
TKRWMesh
Standard_Boolean IsAssembly; //!< flag indicating that this label is assembly
XCAFPrs_DocumentNode() : IsAssembly (Standard_False) {}
+
+public: // Methods for hash map
+
+ //! Return hash code based on node string identifier.
+ static Standard_Integer HashCode (const XCAFPrs_DocumentNode& theNode,
+ const Standard_Integer theN)
+ {
+ return ::HashCode (theNode.Id, theN);
+ }
+
+ //! Return TRUE if two document nodes has the same string identifier.
+ static Standard_Boolean IsEqual (const XCAFPrs_DocumentNode& theNode1,
+ const XCAFPrs_DocumentNode& theNode2)
+ {
+ return theNode1.Id == theNode2.Id;
+ }
+
};
#endif // _XCAFPrs_DocumentNode_HeaderFile
#include <Quantity_HArray1OfColor.hxx>
#include <Quantity_NameOfColor.hxx>
#include <RWGltf_CafReader.hxx>
+#include <RWGltf_CafWriter.hxx>
#include <RWStl.hxx>
#include <RWObj.hxx>
#include <RWObj_CafReader.hxx>
#include <VrmlData_DataMapOfShapeAppearance.hxx>
#include <VrmlData_Scene.hxx>
#include <VrmlData_ShapeConvert.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
#include <XSDRAW.hxx>
#include <XSDRAWIGES.hxx>
#include <XSDRAWSTEP.hxx>
return 0;
}
+//=============================================================================
+//function : WriteGltf
+//purpose : Writes glTF file
+//=============================================================================
+static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
+ Standard_Integer theNbArgs,
+ const char** theArgVec)
+{
+ TCollection_AsciiString aGltfFilePath;
+ Handle(TDocStd_Document) aDoc;
+ Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
+ TColStd_IndexedDataMapOfStringString aFileInfo;
+ RWGltf_WriterTrsfFormat aTrsfFormat = RWGltf_WriterTrsfFormat_Compact;
+ bool toForceUVExport = false;
+ for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+ {
+ TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
+ anArgCase.LowerCase();
+ if (anArgCase == "-comments"
+ && anArgIter + 1 < theNbArgs)
+ {
+ aFileInfo.Add ("Comments", theArgVec[++anArgIter]);
+ }
+ else if (anArgCase == "-author"
+ && anArgIter + 1 < theNbArgs)
+ {
+ aFileInfo.Add ("Author", theArgVec[++anArgIter]);
+ }
+ else if (anArgCase == "-forceuvexport"
+ || anArgCase == "-forceuv")
+ {
+ toForceUVExport = true;
+ if (anArgIter + 1 < theNbArgs
+ && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], toForceUVExport))
+ {
+ ++anArgIter;
+ }
+ }
+ else if (anArgCase == "-trsfformat"
+ && anArgIter + 1 < theNbArgs)
+ {
+ TCollection_AsciiString aTrsfStr (theArgVec[++anArgIter]);
+ aTrsfStr.LowerCase();
+ if (aTrsfStr == "compact")
+ {
+ aTrsfFormat = RWGltf_WriterTrsfFormat_Compact;
+ }
+ else if (aTrsfStr == "mat4")
+ {
+ aTrsfFormat = RWGltf_WriterTrsfFormat_Mat4;
+ }
+ else if (aTrsfStr == "trs")
+ {
+ aTrsfFormat = RWGltf_WriterTrsfFormat_TRS;
+ }
+ else
+ {
+ std::cout << "Syntax error at '" << anArgCase << "'\n";
+ return 1;
+ }
+ }
+ else if (aDoc.IsNull())
+ {
+ Standard_CString aNameVar = theArgVec[anArgIter];
+ DDocStd::GetDocument (aNameVar, aDoc, false);
+ if (aDoc.IsNull())
+ {
+ TopoDS_Shape aShape = DBRep::Get (aNameVar);
+ if (aShape.IsNull())
+ {
+ std::cout << "Syntax error: '" << aNameVar << "' is not a shape nor document\n";
+ return 1;
+ }
+
+ anApp->NewDocument (TCollection_ExtendedString ("BinXCAF"), aDoc);
+ Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (aDoc->Main());
+ aShapeTool->AddShape (aShape);
+ }
+ }
+ else if (aGltfFilePath.IsEmpty())
+ {
+ aGltfFilePath = theArgVec[anArgIter];
+ }
+ else
+ {
+ std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+ return 1;
+ }
+ }
+ if (aGltfFilePath.IsEmpty())
+ {
+ std::cout << "Syntax error: wrong number of arguments\n";
+ return 1;
+ }
+
+ Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (theDI, 1);
+
+ TCollection_AsciiString anExt = aGltfFilePath;
+ anExt.LowerCase();
+
+ const Standard_Real aSystemUnitFactor = UnitsMethods::GetCasCadeLengthUnit() * 0.001;
+
+ RWGltf_CafWriter aWriter (aGltfFilePath, anExt.EndsWith (".glb"));
+ aWriter.SetTransformationFormat (aTrsfFormat);
+ aWriter.SetForcedUVExport (toForceUVExport);
+ aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aSystemUnitFactor);
+ aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (RWMesh_CoordinateSystem_Zup);
+ aWriter.Perform (aDoc, aFileInfo, aProgress);
+ return 0;
+}
+
static Standard_Integer writestl
(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
{
"readgltf shape file"
"\n\t\t: Same as ReadGltf but reads glTF file into a shape instead of a document.",
__FILE__, ReadGltf, g);
+ theCommands.Add ("WriteGltf",
+ "WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}=compact] [-comments Text] [-author Name] [-forceUVExport]"
+ "\n\t\t: Write XDE document into glTF file."
+ "\n\t\t: -trsfFormat preferred transformation format"
+ "\n\t\t: -forceUVExport always export UV coordinates",
+ __FILE__, WriteGltf, g);
+ theCommands.Add ("writegltf",
+ "writegltf shape file",
+ __FILE__, WriteGltf, g);
theCommands.Add ("writevrml", "shape file [version VRML#1.0/VRML#2.0 (1/2): 2 by default] [representation shaded/wireframe/both (0/1/2): 1 by default]",__FILE__,writevrml,g);
theCommands.Add ("writestl", "shape file [ascii/binary (0/1) : 1 by default] [InParallel (0/1) : 0 by default]",__FILE__,writestl,g);
theCommands.Add ("readstl",
+if { [info exists occ_tmp_files] } {
+ foreach aTmpFileIter $occ_tmp_files {
+ if {[file exists "$aTmpFileIter"] == 1} {
+ puts "Deleting temporary file $aTmpFileIter"
+ file delete -force "$aTmpFileIter"
+ }
+ }
+ set occ_tmp_files ""
+}
+
puts ""
puts "TEST COMPLETED"
puts ""
--- /dev/null
+puts "========"
+puts "0030953: Data Exchange - implement export of mesh data into glTF 2.0 format"
+puts "Test case exporting BRep model into glb (binary glTF) file."
+puts "========"
+
+restore [locate_data_file Ball.brep] b
+incmesh b 0.1
+set aTmpGltf "${imagedir}/${casename}_tmp.glb"
+writegltf b "$aTmpGltf"
+
+ReadGltf D "$aTmpGltf"
+XGetOneShape s D
+checknbshapes s -face 17 -compound 3
--- /dev/null
+pload XDE OCAF MODELING VISUALIZATION
+catch { Close D }
--- /dev/null
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+
+vrenderparams -shadingModel PHONG
+vlight -change 0 -intensity 2.5
+vlight -change 1 -intensity 0.3
+vcamera -orthographic
+vdump ${imagedir}/${casename}.png
+vdump ${imagedir}/${casename}_ortho_phong.png
+vcamera -persp
+vdump ${imagedir}/${casename}_persp_phong.png
+
+vrenderparams -shadingModel PBR
+vcamera -orthographic
+vdump ${imagedir}/${casename}_ortho_pbr.png
+vcamera -persp
+vdump ${imagedir}/${casename}_persp_pbr.png
--- /dev/null
+puts "========"
+puts "0030953: Data Exchange - implement export of mesh data into glTF 2.0 format"
+puts "Test case exporting glTF model into glTF file."
+puts "========"
+
+catch { Close D1 }
+ReadGltf D1 [locate_data_file bug30691_DamagedHelmet.gltf]
+
+set aTmpGltfBase "${imagedir}/${casename}_tmp"
+set aTmpGltf "${aTmpGltfBase}.gltf"
+lappend occ_tmp_files $aTmpGltf
+lappend occ_tmp_files "${aTmpGltfBase}.bin"
+lappend occ_tmp_files "${aTmpGltfBase}_textures"
+
+WriteGltf D1 "$aTmpGltf"
+
+ReadGltf D "$aTmpGltf"
+XGetOneShape s D
+checknbshapes s -face 1 -compound 0
+checktrinfo s -tri 15452 -nod 14556
002 shape_write_stl
003 gltf_read
004 obj_read
+005 gltf_write
set aPrefix "g_";
set aColor "CCB11D"
}
+ set aColShapes {}
for { set m 0 } { $m <= $THE_UPPER } { incr m } {
+ set aRowShapes {}
for { set r 0 } { $r <= $THE_UPPER } { incr r } {
set aName ${aPrefix}m${m}r${r}
copy s $aName
+ lappend aRowShapes $aName
ttranslate $aName ${r} ${i} ${m}
- set aLab [XAddShape D $aName]
- SetName D $aLab $aName
+ }
+ set aName ${aPrefix}m${m}
+ compound {*}$aRowShapes $aName
+ lappend aColShapes $aName
+ }
+ set aName ${aPrefix}spheres
+ compound {*}$aColShapes $aName
+ set aLabName "Gray Spheres"
+ if { $i != 0 } { set aLabName "Golden Spheres" }
+ set aLabComp [XAddShape D $aName 0]
+ SetName D $aLabComp $aLabName
+
+ for { set m 0 } { $m <= $THE_UPPER } { incr m } {
+ set aMet [expr 100 * ${m}/$THE_UPPER]
+ set aName ${aPrefix}m${m}
+ XAddComponent D $aLabComp $aName
+ set aLabCompCol [XFindShape D $aName]
+ SetName D $aLabCompCol "${aPrefix}m${aMet}%"
+ SetName D {*}[XFindComponent D $aName] "${aPrefix}m${aMet}%"
+ for { set r 0 } { $r <= $THE_UPPER } { incr r } {
+ set aRoug [expr 100 * ${r}/$THE_UPPER]
+ set aName ${aPrefix}m${m}r${r}
+ XAddComponent D $aLabCompCol $aName
+ set aLab [XFindComponent D $aName]
+ SetName D {*}$aLab "${aPrefix}m${aMet}%_r${aRoug}%"
XAddVisMaterial D $aName -baseColor $aColor -metallic ${m}/$THE_UPPER -roughness ${r}/$THE_UPPER
- XSetVisMaterial D $aLab $aName
+ XSetVisMaterial D {*}$aLab $aName
}
}
}
+set aLab [XFindShape D s]
+SetName D {*}$aLab "Sphere"
+
XGetAllVisMaterials D
# labels
text2brep tnm "Non-metal" -plane 0 -1 0 0 0 -1 -height 0.5 -pos -0.5 0 -0.5 -halign right -valign top -font monospace
text2brep ts "Smooth" -plane 0 -1 0 1 0 0 -height 0.5 -pos -0.5 0 -0.5 -halign left -valign top -font monospace
text2brep tr "Rough" -plane 0 -1 0 1 0 0 -height 0.5 -pos 6.5 0 -0.5 -halign right -valign top -font monospace
-set aLab [XAddShape D tm]
-SetName D $aLab "Metal"
-set aLab [XAddShape D tnm]
-SetName D $aLab "Non-metal"
-set aLab [XAddShape D ts]
-SetName D $aLab "Smooth"
-set aLab [XAddShape D tr]
-SetName D $aLab "Rough"
+compound tm tnm ts tr labs
+set aLab [XAddShape D labs 0]
+SetName D $aLab "Labels"
+XAddComponent D $aLab tm
+XAddComponent D $aLab tnm
+XAddComponent D $aLab ts
+XAddComponent D $aLab tr
+SetName D {*}[XFindComponent D tm] "Metal"
+SetName D [XFindShape D tm] "Metal"
+SetName D {*}[XFindComponent D tnm] "Non-metal"
+SetName D [XFindShape D tnm] "Non-metal"
+SetName D {*}[XFindComponent D ts] "Smooth"
+SetName D [XFindShape D ts] "Smooth"
+SetName D {*}[XFindComponent D tr] "Rough"
+SetName D [XFindShape D tr] "Rough"
vclear
vinit View1 -width 768 -height 768