Unstable test case v3d/memory/bug26538 has been adjusted.
Message_ExecStatus.hxx
Message_Gravity.hxx
Message_HArrayOfMsg.hxx
+Message_LazyProgressScope.hxx
Message_Level.cxx
Message_Level.hxx
Message_ListIteratorOfListOfMsg.hxx
--- /dev/null
+// Copyright (c) 2017-2021 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 _Message_LazyProgressScope_HeaderFiler
+#define _Message_LazyProgressScope_HeaderFiler
+
+#include <Message_ProgressScope.hxx>
+
+//! Progress scope with lazy updates and abort fetches.
+//!
+//! Although Message_ProgressIndicator implementation is encouraged to spare GUI updates,
+//! even optimized implementation might show a noticeable overhead on a very small update step (e.g. per triangle).
+//!
+//! The class splits initial (displayed) number of overall steps into larger chunks specified in constructor,
+//! so that displayed progress is updated at larger steps.
+class Message_LazyProgressScope : protected Message_ProgressScope
+{
+public:
+
+ //! Main constructor.
+ //! @param theRange [in] progress range to scope
+ //! @param theName [in] name of this scope
+ //! @param theMax [in] number of steps within this scope
+ //! @param thePatchStep [in] number of steps to update progress
+ //! @param theIsInf [in] infinite flag
+ Message_LazyProgressScope (const Message_ProgressRange& theRange,
+ const char* theName,
+ const Standard_Real theMax,
+ const Standard_Real thePatchStep,
+ const Standard_Boolean theIsInf = Standard_False)
+ : Message_ProgressScope (theRange, theName, theMax, theIsInf),
+ myPatchStep (thePatchStep),
+ myPatchProgress (0.0),
+ myIsLazyAborted (Standard_False) {}
+
+ //! Increment progress with 1.
+ void Next()
+ {
+ if (++myPatchProgress < myPatchStep)
+ {
+ return;
+ }
+
+ myPatchProgress = 0.0;
+ Message_ProgressScope::Next (myPatchStep);
+ IsAborted();
+ }
+
+ //! Return TRUE if progress has been aborted - return the cached state lazily updated.
+ Standard_Boolean More() const
+ {
+ return !myIsLazyAborted;
+ }
+
+ //! Return TRUE if progress has been aborted - fetches actual value from the Progress.
+ Standard_Boolean IsAborted()
+ {
+ myIsLazyAborted = myIsLazyAborted || !Message_ProgressScope::More();
+ return myIsLazyAborted;
+ }
+
+protected:
+
+ Standard_Real myPatchStep;
+ Standard_Real myPatchProgress;
+ Standard_Boolean myIsLazyAborted;
+
+};
+
+#endif // _Message_LazyProgressScope_HeaderFiler
myWriter->Key (RWGltf_GltfRootElementName (RWGltf_GltfRootElement_Asset));
myWriter->StartObject();
myWriter->Key ("generator");
- myWriter->String ("Open CASCADE Technology [www.opencascade.com]");
+ myWriter->String ("Open CASCADE Technology [dev.opencascade.org]");
myWriter->Key ("version");
myWriter->String ("2.0"); // glTF format version
// function : normal
// purpose :
// =======================================================================
-gp_Dir RWMesh_FaceIterator::normal (Standard_Integer theNode)
+gp_Dir RWMesh_FaceIterator::normal (Standard_Integer theNode) const
{
gp_Dir aNormal (gp::DZ());
if (myPolyTriang->HasNormals())
bool HasTexCoords() const { return !myPolyTriang.IsNull() && myPolyTriang->HasUVNodes(); }
//! Return normal at specified node index with face transformation applied and face orientation applied.
- gp_Dir NormalTransformed (Standard_Integer theNode)
+ gp_Dir NormalTransformed (Standard_Integer theNode) const
{
gp_Dir aNorm = normal (theNode);
if (myTrsf.Form() != gp_Identity)
gp_Pnt node (const Standard_Integer theNode) const { return myPolyTriang->Node (theNode); }
//! Return normal at specified node index without face transformation applied.
- Standard_EXPORT gp_Dir normal (Standard_Integer theNode);
+ Standard_EXPORT gp_Dir normal (Standard_Integer theNode) const;
//! Return triangle with specified index.
Poly_Triangle triangle (Standard_Integer theElemIndex) const { return myPolyTriang->Triangle (theElemIndex); }
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
+ mutable BRepLProp_SLProps mySLTool; //!< auxiliary tool for fetching normals from surface
BRepAdaptor_Surface myFaceAdaptor; //!< surface adaptor for fetching normals from surface
Standard_Boolean myHasNormals; //!< flag indicating that current face has normals
gp_Trsf myTrsf; //!< current face transformation
RWObj.hxx
RWObj_CafReader.cxx
RWObj_CafReader.hxx
+RWObj_CafWriter.cxx
+RWObj_CafWriter.hxx
RWObj_Material.hxx
RWObj_MtlReader.cxx
RWObj_MtlReader.hxx
+RWObj_ObjMaterialMap.cxx
+RWObj_ObjMaterialMap.hxx
+RWObj_ObjWriterContext.cxx
+RWObj_ObjWriterContext.hxx
RWObj_Reader.cxx
RWObj_Reader.hxx
RWObj_SubMesh.hxx
--- /dev/null
+// Copyright (c) 2015-2021 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 <RWObj_CafWriter.hxx>
+
+#include <Message.hxx>
+#include <Message_LazyProgressScope.hxx>
+#include <OSD_OpenFile.hxx>
+#include <OSD_Path.hxx>
+#include <RWMesh_FaceIterator.hxx>
+#include <RWMesh_MaterialMap.hxx>
+#include <RWObj_ObjMaterialMap.hxx>
+#include <RWObj_ObjWriterContext.hxx>
+#include <Standard_CLocaleSentry.hxx>
+#include <TDocStd_Document.hxx>
+#include <TDataStd_Name.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs_DocumentExplorer.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWObj_CafWriter, Standard_Transient)
+
+namespace
+{
+ //! Trivial cast.
+ inline Graphic3d_Vec3 objXyzToVec (const gp_XYZ& thePnt)
+ {
+ return Graphic3d_Vec3 ((float )thePnt.X(), (float )thePnt.Y(), (float )thePnt.Z());
+ }
+
+ //! Trivial cast.
+ inline Graphic3d_Vec2 objXyToVec (const gp_XY& thePnt)
+ {
+ return Graphic3d_Vec2 ((float )thePnt.X(), (float )thePnt.Y());
+ }
+
+ //! 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());
+ }
+}
+
+//================================================================
+// Function : Constructor
+// Purpose :
+//================================================================
+RWObj_CafWriter::RWObj_CafWriter (const TCollection_AsciiString& theFile)
+: myFile (theFile)
+{
+ // OBJ file format doesn't define length units;
+ // Y-up coordinate system is most commonly used (but also undefined)
+ //myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_negZfwd_posYup);
+}
+
+//================================================================
+// Function : Destructor
+// Purpose :
+//================================================================
+RWObj_CafWriter::~RWObj_CafWriter()
+{
+ //
+}
+
+//================================================================
+// Function : toSkipFaceMesh
+// Purpose :
+//================================================================
+Standard_Boolean RWObj_CafWriter::toSkipFaceMesh (const RWMesh_FaceIterator& theFaceIter)
+{
+ return theFaceIter.IsEmptyMesh();
+}
+
+// =======================================================================
+// function : Perform
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Message_ProgressRange& theProgress)
+{
+ TDF_LabelSequence aRoots;
+ Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (theDocument->Main());
+ aShapeTool->GetFreeShapes (aRoots);
+ return Perform (theDocument, aRoots, NULL, theFileInfo, theProgress);
+}
+
+// =======================================================================
+// function : Perform
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::Perform (const Handle(TDocStd_Document)& theDocument,
+ const TDF_LabelSequence& theRootLabels,
+ const TColStd_MapOfAsciiString* theLabelFilter,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo,
+ const Message_ProgressRange& theProgress)
+{
+ TCollection_AsciiString aFolder, aFileName, aFullFileNameBase, aShortFileNameBase, aFileExt;
+ OSD_Path::FolderAndFileFromPath (myFile, aFolder, aFileName);
+ OSD_Path::FileNameAndExtension (aFileName, aShortFileNameBase, aFileExt);
+
+ if (theRootLabels.IsEmpty()
+ || (theLabelFilter != NULL && theLabelFilter->IsEmpty()))
+ {
+ Message::SendFail ("Nothing to export into OBJ file");
+ return false;
+ }
+
+ Standard_Integer aNbNodesAll = 0, aNbElemsAll = 0;
+ Standard_Real aNbPEntities = 0; // steps for progress range
+ bool toCreateMatFile = false;
+ 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;
+ }
+
+ for (RWMesh_FaceIterator aFaceIter (aDocNode.RefLabel, aDocNode.Location, true, aDocNode.Style); aFaceIter.More(); aFaceIter.Next())
+ {
+ if (toSkipFaceMesh (aFaceIter))
+ {
+ continue;
+ }
+
+ addFaceInfo (aFaceIter, aNbNodesAll, aNbElemsAll, aNbPEntities, toCreateMatFile);
+ }
+ }
+ if (aNbNodesAll == 0
+ || aNbElemsAll == 0)
+ {
+ Message::SendFail ("No mesh data to save");
+ return false;
+ }
+
+ TCollection_AsciiString aMatFileNameShort = aShortFileNameBase + ".mtl";
+ const TCollection_AsciiString aMatFileNameFull = !aFolder.IsEmpty() ? aFolder + aMatFileNameShort : aMatFileNameShort;
+ if (!toCreateMatFile)
+ {
+ aMatFileNameShort.Clear();
+ }
+
+ Standard_CLocaleSentry aLocaleSentry;
+ RWObj_ObjWriterContext anObjFile(myFile);
+ RWObj_ObjMaterialMap aMatMgr (aMatFileNameFull);
+ aMatMgr.SetDefaultStyle (myDefaultStyle);
+ if (!anObjFile.IsOpened()
+ || !anObjFile.WriteHeader (aNbNodesAll, aNbElemsAll, aMatFileNameShort, theFileInfo))
+ {
+ return false;
+ }
+
+ int aRootDepth = 0;
+ if (theRootLabels.Size() == 1)
+ {
+ TDF_Label aRefLabel = theRootLabels.First();
+ XCAFDoc_ShapeTool::GetReferredShape (theRootLabels.First(), aRefLabel);
+ TCollection_AsciiString aRootName = readNameAttribute (aRefLabel);
+ if (aRootName.EndsWith (".obj"))
+ {
+ // workaround import/export of .obj file
+ aRootDepth = 1;
+ }
+ }
+
+ // simple global progress sentry - ignores size of node and index data
+ const Standard_Real aPatchStep = 2048.0; // about 100 KiB
+ Message_LazyProgressScope aPSentry (theProgress, "OBJ export", aNbPEntities, aPatchStep);
+
+ bool isDone = true;
+ for (XCAFPrs_DocumentExplorer aDocExplorer (theDocument, theRootLabels, XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes);
+ aDocExplorer.More() && !aPSentry.IsAborted(); aDocExplorer.Next())
+ {
+ const XCAFPrs_DocumentNode& aDocNode = aDocExplorer.Current();
+ if (theLabelFilter != NULL
+ && !theLabelFilter->Contains (aDocNode.Id))
+ {
+ continue;
+ }
+
+ TCollection_AsciiString aName = readNameAttribute (aDocNode.RefLabel);
+ for (int aParentIter = aDocExplorer.CurrentDepth() - 1; aParentIter >= aRootDepth; --aParentIter)
+ {
+ const TCollection_AsciiString aParentName = readNameAttribute (aDocExplorer.Current (aParentIter).RefLabel);
+ if (!aParentName.IsEmpty())
+ {
+ aName = aParentName + "/" + aName;
+ }
+ }
+
+ if (!writeShape (anObjFile, aMatMgr, aPSentry, aDocNode.RefLabel, aDocNode.Location, aDocNode.Style, aName))
+ {
+ isDone = false;
+ break;
+ }
+ }
+
+ const bool isClosed = anObjFile.Close();
+ if (isDone && !isClosed)
+ {
+ Message::SendFail (TCollection_AsciiString ("Failed to write OBJ file\n") + myFile);
+ return false;
+ }
+ return isDone && !aPSentry.IsAborted();
+}
+
+// =======================================================================
+// function : addFaceInfo
+// purpose :
+// =======================================================================
+void RWObj_CafWriter::addFaceInfo (const RWMesh_FaceIterator& theFace,
+ Standard_Integer& theNbNodes,
+ Standard_Integer& theNbElems,
+ Standard_Real& theNbProgressSteps,
+ Standard_Boolean& theToCreateMatFile)
+{
+ theNbNodes += theFace.NbNodes();
+ theNbElems += theFace.NbTriangles();
+
+ theNbProgressSteps += theFace.NbNodes();
+ theNbProgressSteps += theFace.NbTriangles();
+ if (theFace.HasNormals())
+ {
+ theNbProgressSteps += theFace.NbNodes();
+ }
+ if (theFace.HasTexCoords()) //&& !theFace.FaceStyle().Texture().IsEmpty()
+ {
+ theNbProgressSteps += theFace.NbNodes();
+ }
+
+ theToCreateMatFile = theToCreateMatFile
+ || theFace.HasFaceColor()
+ || (!theFace.FaceStyle().BaseColorTexture().IsNull() && theFace.HasTexCoords());
+}
+
+// =======================================================================
+// function : writeShape
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::writeShape (RWObj_ObjWriterContext& theWriter,
+ RWObj_ObjMaterialMap& theMatMgr,
+ Message_LazyProgressScope& thePSentry,
+ const TDF_Label& theLabel,
+ const TopLoc_Location& theParentTrsf,
+ const XCAFPrs_Style& theParentStyle,
+ const TCollection_AsciiString& theName)
+{
+ bool toCreateGroup = true;
+ for (RWMesh_FaceIterator aFaceIter (theLabel, theParentTrsf, true, theParentStyle); aFaceIter.More() && !thePSentry.IsAborted(); aFaceIter.Next())
+ {
+ if (toSkipFaceMesh (aFaceIter))
+ {
+ continue;
+ }
+
+ ++theWriter.NbFaces;
+ {
+ const bool hasNormals = aFaceIter.HasNormals();
+ const bool hasTexCoords = aFaceIter.HasTexCoords(); //&& !aFaceIter.FaceStyle().Texture().IsEmpty();
+ if (theWriter.NbFaces != 1)
+ {
+ toCreateGroup = toCreateGroup
+ || hasNormals != theWriter.HasNormals()
+ || hasTexCoords != theWriter.HasTexCoords();
+ }
+ theWriter.SetNormals (hasNormals);
+ theWriter.SetTexCoords(hasTexCoords);
+ }
+
+ if (toCreateGroup
+ && !theWriter.WriteGroup (theName))
+ {
+ return false;
+ }
+ toCreateGroup = false;
+
+ TCollection_AsciiString aMatName;
+ if (aFaceIter.HasFaceColor()
+ || !aFaceIter.FaceStyle().BaseColorTexture().IsNull())
+ {
+ aMatName = theMatMgr.AddMaterial (aFaceIter.FaceStyle());
+ }
+ if (aMatName != theWriter.ActiveMaterial())
+ {
+ theWriter.WriteActiveMaterial (aMatName);
+ }
+
+ // write nodes
+ if (!writePositions (theWriter, thePSentry, aFaceIter))
+ {
+ return false;
+ }
+
+ // write normals
+ if (theWriter.HasNormals()
+ && !writeNormals (theWriter, thePSentry, aFaceIter))
+ {
+ return false;
+ }
+
+ if (theWriter.HasTexCoords()
+ && !writeTextCoords (theWriter, thePSentry, aFaceIter))
+ {
+ return false;
+ }
+
+ if (!writeIndices (theWriter, thePSentry, aFaceIter))
+ {
+ return false;
+ }
+ theWriter.FlushFace (aFaceIter.NbNodes());
+ }
+ return true;
+}
+
+// =======================================================================
+// function : writePositions
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::writePositions (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace)
+{
+ const Standard_Integer aNodeUpper = theFace.NodeUpper();
+ for (Standard_Integer aNodeIter = theFace.NodeLower(); aNodeIter <= aNodeUpper && thePSentry.More(); ++aNodeIter, thePSentry.Next())
+ {
+ gp_XYZ aNode = theFace.NodeTransformed (aNodeIter).XYZ();
+ myCSTrsf.TransformPosition (aNode);
+ if (!theWriter.WriteVertex (objXyzToVec (aNode)))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// =======================================================================
+// function : writeNormals
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::writeNormals (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace)
+{
+ const Standard_Integer aNodeUpper = theFace.NodeUpper();
+ for (Standard_Integer aNodeIter = theFace.NodeLower(); aNodeIter <= aNodeUpper && thePSentry.More(); ++aNodeIter, thePSentry.Next())
+ {
+ const gp_Dir aNormal = theFace.NormalTransformed (aNodeIter);
+ Graphic3d_Vec3 aNormVec3 = objXyzToVec (aNormal.XYZ());
+ myCSTrsf.TransformNormal (aNormVec3);
+ if (!theWriter.WriteNormal (aNormVec3))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// =======================================================================
+// function : writeTextCoords
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::writeTextCoords (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace)
+{
+ const Standard_Integer aNodeUpper = theFace.NodeUpper();
+ for (Standard_Integer aNodeIter = theFace.NodeLower(); aNodeIter <= aNodeUpper && thePSentry.More(); ++aNodeIter, thePSentry.Next())
+ {
+ gp_Pnt2d aTexCoord = theFace.NodeTexCoord (aNodeIter);
+ if (!theWriter.WriteTexCoord (objXyToVec (aTexCoord.XY())))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// =======================================================================
+// function : writeIndices
+// purpose :
+// =======================================================================
+bool RWObj_CafWriter::writeIndices (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace)
+{
+ const Standard_Integer anElemLower = theFace.ElemLower();
+ const Standard_Integer anElemUpper = theFace.ElemUpper();
+ for (Standard_Integer anElemIter = anElemLower; anElemIter <= anElemUpper && thePSentry.More(); ++anElemIter, thePSentry.Next())
+ {
+ const Poly_Triangle aTri = theFace.TriangleOriented (anElemIter);
+ if (!theWriter.WriteTriangle (Graphic3d_Vec3i (aTri(1), aTri(2), aTri(3)) - Graphic3d_Vec3i (anElemLower)))
+ {
+ return false;
+ }
+ }
+ return true;
+}
--- /dev/null
+// Copyright (c) 2015-2021 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 _RWObj_CafWriter_HeaderFiler
+#define _RWObj_CafWriter_HeaderFiler
+
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+#include <TColStd_MapOfAsciiString.hxx>
+#include <TDF_LabelSequence.hxx>
+#include <TopTools_ShapeMapHasher.hxx>
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <XCAFPrs_Style.hxx>
+
+#include <memory>
+
+class Message_ProgressRange;
+class RWMesh_FaceIterator;
+class TDocStd_Document;
+
+class Message_LazyProgressScope;
+class RWObj_ObjWriterContext;
+class RWObj_ObjMaterialMap;
+
+//! OBJ writer context from XCAF document.
+class RWObj_CafWriter : public Standard_Transient
+{
+ DEFINE_STANDARD_RTTIEXT(RWObj_CafWriter, Standard_Transient)
+public:
+
+ //! Main constructor.
+ //! @param theFile [in] path to output OBJ file
+ Standard_EXPORT RWObj_CafWriter (const TCollection_AsciiString& theFile);
+
+ //! Destructor.
+ Standard_EXPORT virtual ~RWObj_CafWriter();
+
+ //! Return transformation from OCCT to OBJ coordinate system.
+ const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCSTrsf; }
+
+ //! Return transformation from OCCT to OBJ coordinate system.
+ RWMesh_CoordinateSystemConverter& ChangeCoordinateSystemConverter() { return myCSTrsf; }
+
+ //! Set transformation from OCCT to OBJ coordinate system.
+ void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCSTrsf = theConverter; }
+
+ //! 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 OBJ file and associated MTL material 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 OBJ 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 Message_ProgressRange& theProgress);
+
+ //! Write OBJ file and associated MTL material 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 Message_ProgressRange& 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);
+
+ //! Collect face triangulation info.
+ //! @param theFace [in] face to process
+ //! @param theNbNodes [in] [out] overall number of triangulation nodes (should be appended)
+ //! @param theNbElems [in] [out] overall number of triangulation elements (should be appended)
+ //! @param theNbProgressSteps [in] [out] overall number of progress steps (should be appended)
+ //! @param theToCreateMatFile [in] [out] flag to create material file or not (should be appended)
+ Standard_EXPORT virtual void addFaceInfo (const RWMesh_FaceIterator& theFace,
+ Standard_Integer& theNbNodes,
+ Standard_Integer& theNbElems,
+ Standard_Real& theNbProgressSteps,
+ Standard_Boolean& theToCreateMatFile);
+
+ //! Write the shape.
+ //! @param theWriter [in] OBJ writer context
+ //! @param theMatMgr [in] OBJ material map
+ //! @param thePSentry [in] progress sentry
+ //! @param theLabel [in] document label to process
+ //! @param theParentTrsf [in] parent node transformation
+ //! @param theParentStyle [in] parent node style
+ //! @param theName [in] node name
+ Standard_EXPORT virtual bool writeShape (RWObj_ObjWriterContext& theWriter,
+ RWObj_ObjMaterialMap& theMatMgr,
+ Message_LazyProgressScope& thePSentry,
+ const TDF_Label& theLabel,
+ const TopLoc_Location& theParentTrsf,
+ const XCAFPrs_Style& theParentStyle,
+ const TCollection_AsciiString& theName);
+
+ //! Write face triangle vertex positions.
+ //! @param theWriter [in] OBJ writer context
+ //! @param thePSentry [in] progress sentry
+ //! @param theFace [in] current face
+ //! @return FALSE on writing file error
+ Standard_EXPORT virtual bool writePositions (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace);
+
+ //! Write face triangle vertex normals.
+ //! @param theWriter [in] OBJ writer context
+ //! @param thePSentry [in] progress sentry
+ //! @param theFace [in] current face
+ //! @return FALSE on writing file error
+ Standard_EXPORT virtual bool writeNormals (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace);
+
+ //! Write face triangle vertex texture coordinates.
+ //! @param theWriter [in] OBJ writer context
+ //! @param thePSentry [in] progress sentry
+ //! @param theFace [in] current face
+ //! @return FALSE on writing file error
+ Standard_EXPORT virtual bool writeTextCoords (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace);
+
+ //! Write face triangles indices.
+ //! @param theWriter [in] OBJ writer context
+ //! @param thePSentry [in] progress sentry
+ //! @param theFace [in] current face
+ //! @return FALSE on writing file error
+ Standard_EXPORT virtual bool writeIndices (RWObj_ObjWriterContext& theWriter,
+ Message_LazyProgressScope& thePSentry,
+ const RWMesh_FaceIterator& theFace);
+
+
+protected:
+
+ TCollection_AsciiString myFile; //!< output OBJ file
+ RWMesh_CoordinateSystemConverter myCSTrsf; //!< transformation from OCCT to OBJ coordinate system
+ XCAFPrs_Style myDefaultStyle; //!< default material definition to be used for nodes with only color defined
+
+};
+
+#endif // _RWObj_CafWriter_HeaderFiler
--- /dev/null
+// Copyright (c) 2015-2021 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 <RWObj_ObjMaterialMap.hxx>
+
+#include <Message.hxx>
+#include <OSD_OpenFile.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWObj_ObjMaterialMap, RWMesh_MaterialMap)
+
+// ================================================================
+// Function : RWObj_ObjMaterialMap
+// Purpose :
+// ================================================================
+RWObj_ObjMaterialMap::RWObj_ObjMaterialMap (const TCollection_AsciiString& theFile)
+: RWMesh_MaterialMap (theFile),
+ myFile (NULL)
+{
+ //
+}
+
+// ================================================================
+// Function : ~RWObj_ObjMaterialMap
+// Purpose :
+// ================================================================
+RWObj_ObjMaterialMap::~RWObj_ObjMaterialMap()
+{
+ if (myFile != NULL)
+ {
+ if (::fclose (myFile) != 0)
+ {
+ myIsFailed = true;
+ }
+ }
+
+ if (myIsFailed)
+ {
+ Message::SendFail (TCollection_AsciiString ("File cannot be written\n") + myFileName);
+ }
+}
+
+// ================================================================
+// Function : AddMaterial
+// Purpose :
+// ================================================================
+TCollection_AsciiString RWObj_ObjMaterialMap::AddMaterial (const XCAFPrs_Style& theStyle)
+{
+ if (myFile == NULL
+ && !myIsFailed)
+ {
+ myFile = OSD_OpenFile (myFileName.ToCString(), "wb");
+ myIsFailed = myFile == NULL;
+ if (myFile != NULL)
+ {
+ Fprintf (myFile, "# Exported by Open CASCADE Technology [dev.opencascade.org]\n");
+ }
+ }
+ if (myFile == NULL)
+ {
+ return TCollection_AsciiString();
+ }
+
+ return RWMesh_MaterialMap::AddMaterial (theStyle);
+}
+
+// ================================================================
+// Function : DefineMaterial
+// Purpose :
+// ================================================================
+void RWObj_ObjMaterialMap::DefineMaterial (const XCAFPrs_Style& theStyle,
+ const TCollection_AsciiString& theKey,
+ const TCollection_AsciiString& theName)
+{
+ (void )theName;
+ Fprintf (myFile, "newmtl %s\n", theKey.ToCString());
+
+ bool hasMaterial = false;
+ const XCAFDoc_VisMaterialCommon aDefMat = !myDefaultStyle.Material().IsNull()
+ ? myDefaultStyle.Material()->ConvertToCommonMaterial()
+ : XCAFDoc_VisMaterialCommon();
+ Quantity_Color anAmbQ (aDefMat.AmbientColor), aDiffQ (aDefMat.DiffuseColor), aSpecQ (aDefMat.SpecularColor);
+ Standard_ShortReal aTransp = 0.0f;
+ Standard_ShortReal aSpecular = aDefMat.Shininess * 1000.0f;
+ if (!theStyle.Material().IsNull()
+ && !theStyle.Material()->IsEmpty())
+ {
+ hasMaterial = true;
+ const XCAFDoc_VisMaterialCommon aComMat = theStyle.Material()->ConvertToCommonMaterial();
+ anAmbQ = aComMat.AmbientColor;
+ aDiffQ = aComMat.DiffuseColor;
+ aSpecQ = aComMat.SpecularColor;
+ aTransp = aComMat.Transparency;
+ aSpecular = aComMat.Shininess * 1000.0f;
+ }
+ if (theStyle.IsSetColorSurf())
+ {
+ hasMaterial = true;
+ aDiffQ = theStyle.GetColorSurf();
+ anAmbQ = Quantity_Color ((Graphic3d_Vec3 )theStyle.GetColorSurf() * 0.25f);
+ if (theStyle.GetColorSurfRGBA().Alpha() < 1.0f)
+ {
+ aTransp = 1.0f - theStyle.GetColorSurfRGBA().Alpha();
+ }
+ }
+
+ if (hasMaterial)
+ {
+ Graphic3d_Vec3d anAmb, aDiff, aSpec;
+ anAmbQ.Values (anAmb.r(), anAmb.g(), anAmb.b(), Quantity_TOC_sRGB);
+ aDiffQ.Values (aDiff.r(), aDiff.g(), aDiff.b(), Quantity_TOC_sRGB);
+ aSpecQ.Values (aSpec.r(), aSpec.g(), aSpec.b(), Quantity_TOC_sRGB);
+
+ Fprintf (myFile, "Ka %f %f %f\n", anAmb.r(), anAmb.g(), anAmb.b());
+ Fprintf (myFile, "Kd %f %f %f\n", aDiff.r(), aDiff.g(), aDiff.b());
+ Fprintf (myFile, "Ks %f %f %f\n", aSpec.r(), aSpec.g(), aSpec.b());
+ Fprintf (myFile, "Ns %f\n", aSpecular);
+ if (aTransp >= 0.0001f)
+ {
+ Fprintf (myFile, "Tr %f\n", aTransp);
+ }
+ }
+
+ if (const Handle(Image_Texture)& aBaseTexture = theStyle.BaseColorTexture())
+ {
+ TCollection_AsciiString aTexture;
+ if (!myImageMap.Find (aBaseTexture, aTexture)
+ && !myImageFailMap.Contains (aBaseTexture))
+ {
+ if (CopyTexture (aTexture, aBaseTexture, TCollection_AsciiString (myImageMap.Extent() + 1)))
+ {
+ myImageMap.Bind (aBaseTexture, aTexture);
+ }
+ else
+ {
+ myImageFailMap.Add (aBaseTexture);
+ }
+ }
+ if (!aTexture.IsEmpty())
+ {
+ Fprintf (myFile, "map_Kd %s\n", aTexture.ToCString());
+ }
+ }
+}
--- /dev/null
+// Copyright (c) 2015-2021 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 _RWObj_ObjMaterialMap_HeaderFiler
+#define _RWObj_ObjMaterialMap_HeaderFiler
+
+#include <RWMesh_MaterialMap.hxx>
+
+//! Material MTL file writer for OBJ export.
+class RWObj_ObjMaterialMap : public RWMesh_MaterialMap
+{
+ DEFINE_STANDARD_RTTIEXT(RWObj_ObjMaterialMap, RWMesh_MaterialMap)
+public:
+
+ //! Main constructor.
+ Standard_EXPORT RWObj_ObjMaterialMap (const TCollection_AsciiString& theFile);
+
+ //! Destructor, will emit error message if file was not closed.
+ Standard_EXPORT virtual ~RWObj_ObjMaterialMap();
+
+ //! 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;
+
+private:
+
+ FILE* myFile;
+ NCollection_DataMap<Handle(Image_Texture), TCollection_AsciiString, Image_Texture> myImageMap;
+
+};
+
+#endif // _RWObj_ObjMaterialMap_HeaderFiler
--- /dev/null
+// Copyright (c) 2015-2021 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 <RWObj_ObjWriterContext.hxx>
+
+#include <Message.hxx>
+#include <NCollection_IndexedMap.hxx>
+#include <OSD_OpenFile.hxx>
+
+// =======================================================================
+// function : splitLines
+// purpose :
+// =======================================================================
+static void splitLines (const TCollection_AsciiString& theString,
+ NCollection_IndexedMap<TCollection_AsciiString>& theLines)
+{
+ if (theString.IsEmpty())
+ {
+ return;
+ }
+
+ Standard_Integer aLineFrom = 1;
+ for (Standard_Integer aCharIter = 1;; ++aCharIter)
+ {
+ const char aChar = theString.Value (aCharIter);
+ if (aChar != '\r'
+ && aChar != '\n'
+ && aCharIter != theString.Length())
+ {
+ continue;
+ }
+
+ if (aLineFrom != aCharIter)
+ {
+ TCollection_AsciiString aLine = theString.SubString (aLineFrom, aCharIter);
+ aLine.RightAdjust();
+ theLines.Add (aLine);
+ }
+
+ if (aCharIter == theString.Length())
+ {
+ break;
+ }
+ else if (aChar == '\r'
+ && theString.Value (aCharIter + 1) == '\n')
+ {
+ // CRLF
+ ++aCharIter;
+ }
+ aLineFrom = aCharIter + 1;
+ }
+}
+
+// ================================================================
+// Function : RWObj_ObjWriterContext
+// Purpose :
+// ================================================================
+RWObj_ObjWriterContext::RWObj_ObjWriterContext (const TCollection_AsciiString& theName)
+: NbFaces (0),
+ myFile (OSD_OpenFile (theName.ToCString(), "wb")),
+ myName (theName),
+ myElemPosFirst (1, 1, 1, 1),
+ myElemNormFirst(1, 1, 1, 1),
+ myElemUVFirst (1, 1, 1, 1),
+ myHasNormals (false),
+ myHasTexCoords (false)
+{
+ if (myFile == NULL)
+ {
+ Message::SendFail (TCollection_AsciiString ("File cannot be created\n") + theName);
+ return;
+ }
+}
+
+// ================================================================
+// Function : ~RWObj_ObjWriterContext
+// Purpose :
+// ================================================================
+RWObj_ObjWriterContext::~RWObj_ObjWriterContext()
+{
+ if (myFile != NULL)
+ {
+ ::fclose (myFile);
+ Message::SendFail (TCollection_AsciiString ("File cannot be written\n") + myName);
+ }
+}
+
+// ================================================================
+// Function : Close
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::Close()
+{
+ bool isOk = ::fclose (myFile) == 0;
+ myFile = NULL;
+ return isOk;
+}
+
+// ================================================================
+// Function : WriteHeader
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteHeader (const Standard_Integer theNbNodes,
+ const Standard_Integer theNbElems,
+ const TCollection_AsciiString& theMatLib,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo)
+{
+ bool isOk = ::Fprintf (myFile, "# Exported by Open CASCADE Technology [dev.opencascade.org]\n"
+ "# Vertices: %d\n"
+ "# Faces: %d\n", theNbNodes, theNbElems) != 0;
+ for (TColStd_IndexedDataMapOfStringString::Iterator aKeyValueIter (theFileInfo); aKeyValueIter.More(); aKeyValueIter.Next())
+ {
+ NCollection_IndexedMap<TCollection_AsciiString> aKeyLines, aValLines;
+ splitLines (aKeyValueIter.Key(), aKeyLines);
+ splitLines (aKeyValueIter.Value(), aValLines);
+ for (Standard_Integer aLineIter = 1; aLineIter <= aKeyLines.Extent(); ++aLineIter)
+ {
+ const TCollection_AsciiString& aLine = aKeyLines.FindKey (aLineIter);
+ isOk = isOk
+ && ::Fprintf (myFile,
+ aLineIter > 1 ? "\n# %s" : "# %s",
+ aLine.ToCString()) != 0;
+ }
+ isOk = isOk
+ && ::Fprintf (myFile, !aKeyLines.IsEmpty() ? ":" : "# ") != 0;
+ for (Standard_Integer aLineIter = 1; aLineIter <= aValLines.Extent(); ++aLineIter)
+ {
+ const TCollection_AsciiString& aLine = aValLines.FindKey (aLineIter);
+ isOk = isOk
+ && ::Fprintf (myFile,
+ aLineIter > 1 ? "\n# %s" : " %s",
+ aLine.ToCString()) != 0;
+ }
+ isOk = isOk
+ && ::Fprintf (myFile, "\n") != 0;
+ }
+
+ if (!theMatLib.IsEmpty())
+ {
+ isOk = isOk
+ && ::Fprintf (myFile, "mtllib %s\n", theMatLib.ToCString()) != 0;
+ }
+ return isOk;
+}
+
+// ================================================================
+// Function : WriteActiveMaterial
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteActiveMaterial (const TCollection_AsciiString& theMaterial)
+{
+ myActiveMaterial = theMaterial;
+ return !theMaterial.IsEmpty()
+ ? Fprintf (myFile, "usemtl %s\n", theMaterial.ToCString()) != 0
+ : Fprintf (myFile, "usemtl\n") != 0;
+}
+
+// ================================================================
+// Function : WriteTriangle
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteTriangle (const Graphic3d_Vec3i& theTri)
+{
+ const Graphic3d_Vec3i aTriPos = theTri + myElemPosFirst.xyz();
+ if (myHasNormals)
+ {
+ const Graphic3d_Vec3i aTriNorm = theTri + myElemNormFirst.xyz();
+ if (myHasTexCoords)
+ {
+ const Graphic3d_Vec3i aTriUv = theTri + myElemUVFirst.xyz();
+ return Fprintf (myFile, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
+ aTriPos[0], aTriUv[0], aTriNorm[0],
+ aTriPos[1], aTriUv[1], aTriNorm[1],
+ aTriPos[2], aTriUv[2], aTriNorm[2]) != 0;
+ }
+ else
+ {
+ return Fprintf (myFile, "f %d//%d %d//%d %d//%d\n",
+ aTriPos[0], aTriNorm[0],
+ aTriPos[1], aTriNorm[1],
+ aTriPos[2], aTriNorm[2]) != 0;
+ }
+ }
+ if (myHasTexCoords)
+ {
+ const Graphic3d_Vec3i aTriUv = theTri + myElemUVFirst.xyz();
+ return Fprintf (myFile, "f %d/%d %d/%d %d/%d\n",
+ aTriPos[0], aTriUv[0],
+ aTriPos[1], aTriUv[1],
+ aTriPos[2], aTriUv[2]) != 0;
+ }
+ else
+ {
+ return Fprintf (myFile, "f %d %d %d\n", aTriPos[0], aTriPos[1], aTriPos[2]) != 0;
+ }
+}
+
+// ================================================================
+// Function : WriteQuad
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteQuad (const Graphic3d_Vec4i& theQuad)
+{
+ const Graphic3d_Vec4i aQPos = theQuad + myElemPosFirst;
+ if (myHasNormals)
+ {
+ const Graphic3d_Vec4i aQNorm = theQuad + myElemNormFirst;
+ if (myHasTexCoords)
+ {
+ const Graphic3d_Vec4i aQTex = theQuad + myElemUVFirst;
+ return Fprintf (myFile, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d\n",
+ aQPos[0], aQTex[0], aQNorm[0],
+ aQPos[1], aQTex[1], aQNorm[1],
+ aQPos[2], aQTex[2], aQNorm[2],
+ aQPos[3], aQTex[3], aQNorm[3]) != 0;
+ }
+ else
+ {
+ return Fprintf (myFile, "f %d//%d %d//%d %d//%d %d//%d\n",
+ aQPos[0], aQNorm[0],
+ aQPos[1], aQNorm[1],
+ aQPos[2], aQNorm[2],
+ aQPos[3], aQNorm[3]) != 0;
+ }
+ }
+ if (myHasTexCoords)
+ {
+ const Graphic3d_Vec4i aQTex = theQuad + myElemUVFirst;
+ return Fprintf (myFile, "f %d/%d %d/%d %d/%d %d/%d\n",
+ aQPos[0], aQTex[0],
+ aQPos[1], aQTex[1],
+ aQPos[2], aQTex[2],
+ aQPos[3], aQTex[3]) != 0;
+ }
+ else
+ {
+ return Fprintf (myFile, "f %d %d %d %d\n", aQPos[0], aQPos[1], aQPos[2], aQPos[3]) != 0;
+ }
+}
+
+// ================================================================
+// Function : WriteVertex
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteVertex (const Graphic3d_Vec3& theValue)
+{
+ return Fprintf (myFile, "v %f %f %f\n", theValue.x(), theValue.y(), theValue.z()) != 0;
+}
+
+// ================================================================
+// Function : WriteNormal
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteNormal (const Graphic3d_Vec3& theValue)
+{
+ return Fprintf (myFile, "vn %f %f %f\n", theValue.x(), theValue.y(), theValue.z()) != 0;
+}
+
+// ================================================================
+// Function : WriteTexCoord
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteTexCoord (const Graphic3d_Vec2& theValue)
+{
+ return Fprintf (myFile, "vt %f %f\n", theValue.x(), theValue.y()) != 0;
+}
+
+// ================================================================
+// Function : WriteGroup
+// Purpose :
+// ================================================================
+bool RWObj_ObjWriterContext::WriteGroup (const TCollection_AsciiString& theValue)
+{
+ return !theValue.IsEmpty()
+ ? Fprintf (myFile, "g %s\n", theValue.ToCString()) != 0
+ : Fprintf (myFile, "g\n") != 0;
+}
+
+// ================================================================
+// Function : FlushFace
+// Purpose :
+// ================================================================
+void RWObj_ObjWriterContext::FlushFace (Standard_Integer theNbNodes)
+{
+ Graphic3d_Vec4i aShift (theNbNodes, theNbNodes, theNbNodes, theNbNodes);
+ myElemPosFirst += aShift;
+ if (myHasNormals)
+ {
+ myElemNormFirst += aShift;
+ }
+ if (myHasTexCoords)
+ {
+ myElemUVFirst += aShift;
+ }
+}
--- /dev/null
+// Copyright (c) 2015-2021 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 _RWObj_ObjWriterContext_HeaderFiler
+#define _RWObj_ObjWriterContext_HeaderFiler
+
+#include <Graphic3d_Vec.hxx>
+#include <TCollection_AsciiString.hxx>
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+
+//! Auxiliary low-level tool writing OBJ file.
+class RWObj_ObjWriterContext
+{
+public:
+
+ //! Main constructor.
+ Standard_EXPORT RWObj_ObjWriterContext (const TCollection_AsciiString& theName);
+
+ //! Destructor, will emit error message if file was not closed.
+ Standard_EXPORT ~RWObj_ObjWriterContext();
+
+ //! Return true if file has been opened.
+ bool IsOpened() const { return myFile != NULL; }
+
+ //! Correctly close the file.
+ Standard_EXPORT bool Close();
+
+ //! Return true if normals are defined.
+ bool HasNormals() const { return myHasNormals; }
+
+ //! Set if normals are defined.
+ void SetNormals (const bool theHasNormals) { myHasNormals = theHasNormals; }
+
+ //! Return true if normals are defined.
+ bool HasTexCoords() const { return myHasTexCoords; }
+
+ //! Set if normals are defined.
+ void SetTexCoords (const bool theHasTexCoords) { myHasTexCoords = theHasTexCoords; }
+
+ //! Write the header.
+ Standard_EXPORT bool WriteHeader (const Standard_Integer theNbNodes,
+ const Standard_Integer theNbElems,
+ const TCollection_AsciiString& theMatLib,
+ const TColStd_IndexedDataMapOfStringString& theFileInfo);
+
+ //! Return active material or empty string if not set.
+ const TCollection_AsciiString& ActiveMaterial() const { return myActiveMaterial; }
+
+ //! Set active material.
+ Standard_EXPORT bool WriteActiveMaterial (const TCollection_AsciiString& theMaterial);
+
+ //! Writing a triangle
+ Standard_EXPORT bool WriteTriangle (const Graphic3d_Vec3i& theTri);
+
+ //! Writing a quad
+ Standard_EXPORT bool WriteQuad (const Graphic3d_Vec4i& theQuad);
+
+ //! Writing a vector
+ Standard_EXPORT bool WriteVertex (const Graphic3d_Vec3& theValue);
+
+ //! Writing a vector
+ Standard_EXPORT bool WriteNormal (const Graphic3d_Vec3& theValue);
+
+ //! Writing a vector
+ Standard_EXPORT bool WriteTexCoord (const Graphic3d_Vec2& theValue);
+
+ //! Writing a group name
+ Standard_EXPORT bool WriteGroup (const TCollection_AsciiString& theValue);
+
+ //! Increment indices shift.
+ Standard_EXPORT void FlushFace (Standard_Integer theNbNodes);
+
+public:
+
+ Standard_Integer NbFaces;
+
+private:
+
+ FILE* myFile;
+ TCollection_AsciiString myName;
+ TCollection_AsciiString myActiveMaterial;
+ Graphic3d_Vec4i myElemPosFirst;
+ Graphic3d_Vec4i myElemNormFirst;
+ Graphic3d_Vec4i myElemUVFirst;
+ bool myHasNormals;
+ bool myHasTexCoords;
+
+};
+
+#endif // _RWObj_ObjWriterContext_HeaderFiler
//! Manage visibility.
Standard_Boolean IsVisible() const { return myIsVisible; }
+ //! Return base color texture.
+ const Handle(Image_Texture)& BaseColorTexture() const
+ {
+ static const Handle(Image_Texture) THE_NULL_TEXTURE;
+ if (myMaterial.IsNull())
+ {
+ return THE_NULL_TEXTURE;
+ }
+ else if (myMaterial->HasPbrMaterial()
+ && !myMaterial->PbrMaterial().BaseColorTexture.IsNull())
+ {
+ return myMaterial->PbrMaterial().BaseColorTexture;
+ }
+ else if (myMaterial->HasCommonMaterial()
+ && !myMaterial->CommonMaterial().DiffuseTexture.IsNull())
+ {
+ return myMaterial->CommonMaterial().DiffuseTexture;
+ }
+ return THE_NULL_TEXTURE;
+ }
+
//! Returns True if styles are the same
//! Methods for using Style as key in maps
Standard_Boolean IsEqual (const XCAFPrs_Style& theOther) const
#include <RWStl.hxx>
#include <RWObj.hxx>
#include <RWObj_CafReader.hxx>
+#include <RWObj_CafWriter.hxx>
#include <SelectMgr_SelectionManager.hxx>
#include <Standard_ErrorHandler.hxx>
#include <StdSelect_ViewerSelector3d.hxx>
return 0;
}
+//=============================================================================
+//function : WriteObj
+//purpose : Writes OBJ file
+//=============================================================================
+static Standard_Integer WriteObj (Draw_Interpretor& theDI,
+ Standard_Integer theNbArgs,
+ const char** theArgVec)
+{
+ TCollection_AsciiString anObjFilePath;
+ Handle(TDocStd_Document) aDoc;
+ Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
+ TColStd_IndexedDataMapOfStringString aFileInfo;
+ Standard_Real aFileUnitFactor = -1.0;
+ RWMesh_CoordinateSystem aSystemCoordSys = RWMesh_CoordinateSystem_Zup, aFileCoordSys = RWMesh_CoordinateSystem_Yup;
+ for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+ {
+ TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
+ anArgCase.LowerCase();
+ if (anArgIter + 1 < theNbArgs
+ && (anArgCase == "-unit"
+ || anArgCase == "-units"
+ || anArgCase == "-fileunit"
+ || anArgCase == "-fileunits"))
+ {
+ const TCollection_AsciiString aUnitStr (theArgVec[++anArgIter]);
+ aFileUnitFactor = UnitsAPI::AnyToSI (1.0, aUnitStr.ToCString());
+ if (aFileUnitFactor <= 0.0)
+ {
+ Message::SendFail() << "Syntax error: wrong length unit '" << aUnitStr << "'";
+ return 1;
+ }
+ }
+ else if (anArgIter + 1 < theNbArgs
+ && (anArgCase == "-filecoordinatesystem"
+ || anArgCase == "-filecoordsystem"
+ || anArgCase == "-filecoordsys"))
+ {
+ if (!parseCoordinateSystem (theArgVec[++anArgIter], aFileCoordSys))
+ {
+ Message::SendFail() << "Syntax error: unknown coordinate system '" << theArgVec[anArgIter] << "'";
+ return 1;
+ }
+ }
+ else if (anArgIter + 1 < theNbArgs
+ && (anArgCase == "-systemcoordinatesystem"
+ || anArgCase == "-systemcoordsystem"
+ || anArgCase == "-systemcoordsys"
+ || anArgCase == "-syscoordsys"))
+ {
+ if (!parseCoordinateSystem (theArgVec[++anArgIter], aSystemCoordSys))
+ {
+ Message::SendFail() << "Syntax error: unknown coordinate system '" << theArgVec[anArgIter] << "'";
+ return 1;
+ }
+ }
+ else 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 (aDoc.IsNull())
+ {
+ Standard_CString aNameVar = theArgVec[anArgIter];
+ DDocStd::GetDocument (aNameVar, aDoc, false);
+ if (aDoc.IsNull())
+ {
+ TopoDS_Shape aShape = DBRep::Get (aNameVar);
+ if (aShape.IsNull())
+ {
+ Message::SendFail() << "Syntax error: '" << aNameVar << "' is not a shape nor document";
+ return 1;
+ }
+
+ anApp->NewDocument (TCollection_ExtendedString ("BinXCAF"), aDoc);
+ Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (aDoc->Main());
+ aShapeTool->AddShape (aShape);
+ }
+ }
+ else if (anObjFilePath.IsEmpty())
+ {
+ anObjFilePath = theArgVec[anArgIter];
+ }
+ else
+ {
+ Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'";
+ return 1;
+ }
+ }
+ if (anObjFilePath.IsEmpty())
+ {
+ Message::SendFail() << "Syntax error: wrong number of arguments";
+ return 1;
+ }
+
+ Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (theDI, 1);
+
+ const Standard_Real aSystemUnitFactor = UnitsMethods::GetCasCadeLengthUnit() * 0.001;
+ RWObj_CafWriter aWriter (anObjFilePath);
+ aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aSystemUnitFactor);
+ aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (aSystemCoordSys);
+ aWriter.ChangeCoordinateSystemConverter().SetOutputLengthUnit (aFileUnitFactor);
+ aWriter.ChangeCoordinateSystemConverter().SetOutputCoordinateSystem (aFileCoordSys);
+ aWriter.Perform (aDoc, aFileInfo, aProgress->Start());
+ return 0;
+}
+
static Standard_Integer writevrml
(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
{
"\n\t\t: (false by default)"
"\n\t\t: -keepLate data is loaded into itself with preservation of information"
"\n\t\t: about deferred storage to load/unload this data later.",
- "\n\t\t: -toPrintDebugInfo print additional debug inforamtion during data reading"
+ "\n\t\t: -toPrintDebugInfo print additional debug information during data reading"
__FILE__, ReadGltf, g);
theCommands.Add ("readgltf",
"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] [-texturesSeparate]"
- "\n\t\t: Write XDE document into glTF file."
- "\n\t\t: -trsfFormat preferred transformation format"
- "\n\t\t: -forceUVExport always export UV coordinates"
- "\n\t\t: -texturesSeparate write textures to separate files",
+ "WriteGltf Doc file [-trsfFormat {compact|TRS|mat4}=compact]"
+ "\n\t\t: [-comments Text] [-author Name]"
+ "\n\t\t: [-forceUVExport] [-texturesSeparate]"
+ "\n\t\t: Write XDE document into glTF file."
+ "\n\t\t: -trsfFormat preferred transformation format"
+ "\n\t\t: -forceUVExport always export UV coordinates"
+ "\n\t\t: -texturesSeparate write textures to separate files",
__FILE__, WriteGltf, g);
theCommands.Add ("writegltf",
"writegltf shape file",
"\n\t\t: Same as ReadObj but reads OBJ file into a shape instead of a document."
"\n\t\t: -singleFace merge OBJ content into a single triangulation Face.",
__FILE__, ReadObj, g);
+ theCommands.Add ("WriteObj",
+ "WriteObj Doc file [-fileCoordSys {Zup|Yup}] [-fileUnit Unit]"
+ "\n\t\t: [-systemCoordSys {Zup|Yup}]"
+ "\n\t\t: [-comments Text] [-author Name]"
+ "\n\t\t: Write XDE document into OBJ file."
+ "\n\t\t: -fileUnit length unit of OBJ file content;"
+ "\n\t\t: -fileCoordSys coordinate system defined by OBJ file; Yup when not specified."
+ "\n\t\t: -systemCoordSys system coordinate system; Zup when not specified.",
+ __FILE__, WriteObj, g);
+ theCommands.Add ("writeobj",
+ "writeobj shape file",
+ __FILE__, WriteObj, g);
theCommands.Add ("meshfromstl", "creates MeshVS_Mesh from STL file", __FILE__, createmesh, g );
theCommands.Add ("mesh3delem", "creates 3d element mesh to test", __FILE__, create3d, g );
001 stl_read
002 shape_write_stl
003 gltf_read
-004 obj_read
-005 gltf_write
-006 gltf_lateload
+004 gltf_write
+005 gltf_lateload
+006 obj_read
+007 obj_write
--- /dev/null
+puts "========"
+puts "0029303: Data Exchange - add RWObj_CafWriter tool for wavefront OBJ file"
+puts "Write as1 STEP model into OBJ file"
+puts "========"
+
+pload XDE OCAF MODELING VISUALIZATION
+Close D -silent
+Close D1 -silent
+ReadStep D1 [locate_data_file as1-oc-214-mat.stp]
+XGetOneShape ss D1
+incmesh ss 1.0
+
+set aTmpObjBase "${imagedir}/${casename}_tmp"
+set aTmpObj "${aTmpObjBase}.obj"
+lappend occ_tmp_files $aTmpObj
+lappend occ_tmp_files "${aTmpObjBase}.mtl"
+lappend occ_tmp_files "${aTmpObjBase}_textures"
+
+WriteObj D1 "$aTmpObj"
+
+ReadObj D "$aTmpObj"
+XGetOneShape s D
+checknbshapes s -face 18 -compound 2
+
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
--- /dev/null
+puts "========"
+puts "0029303: Data Exchange - add RWObj_CafWriter tool for wavefront OBJ file"
+puts "Write B-Rep model into OBJ file"
+puts "========"
+
+pload XDE OCAF MODELING VISUALIZATION
+Close D -silent
+
+restore [locate_data_file Ball.brep] b
+incmesh b 0.1
+
+set aTmpObjBase "${imagedir}/${casename}_tmp"
+set aTmpObj "${aTmpObjBase}.obj"
+lappend occ_tmp_files $aTmpObj
+lappend occ_tmp_files "${aTmpObjBase}.mtl"
+
+writeobj b "$aTmpObj"
+
+ReadObj D "$aTmpObj"
+XGetOneShape s D
+checknbshapes s -face 2 -compound 2
+
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
--- /dev/null
+puts "========"
+puts "0029303: Data Exchange - add RWObj_CafWriter tool for wavefront OBJ file"
+puts "Write textured lantern glTF model into OBJ file"
+puts "========"
+
+pload XDE OCAF MODELING VISUALIZATION
+Close D -silent
+Close D1 -silent
+ReadGltf D1 [locate_data_file bug30691_Lantern.glb]
+
+set aTmpObjBase "${imagedir}/${casename}_tmp"
+set aTmpObj "${aTmpObjBase}.obj"
+lappend occ_tmp_files $aTmpObj
+lappend occ_tmp_files "${aTmpObjBase}.mtl"
+lappend occ_tmp_files "${aTmpObjBase}_textures"
+
+WriteObj D1 "$aTmpObj"
+
+ReadObj D "$aTmpObj"
+XGetOneShape s D
+checknbshapes s -face 3 -compound 1
+checktrinfo s -tri 5394 -nod 4145
+
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
--- /dev/null
+puts "========"
+puts "0029303: Data Exchange - add RWObj_CafWriter tool for wavefront OBJ file"
+puts "Write textured plane OBJ model into OBJ file"
+puts "========"
+
+pload XDE OCAF MODELING VISUALIZATION
+Close D -silent
+Close D1 -silent
+ReadObj D1 [locate_data_file "P-51 Mustang.obj"]
+
+set aTmpObjBase "${imagedir}/${casename}_tmp"
+set aTmpObj "${aTmpObjBase}.obj"
+lappend occ_tmp_files $aTmpObj
+lappend occ_tmp_files "${aTmpObjBase}.mtl"
+lappend occ_tmp_files "${aTmpObjBase}_textures"
+
+WriteObj D1 "$aTmpObj"
+
+ReadObj D "$aTmpObj"
+XGetOneShape s D
+checknbshapes s -face 14 -compound 1
+checktrinfo s -tri 4309 -nod 4727
+
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
--- /dev/null
+puts "========"
+puts "0029303: Data Exchange - add RWObj_CafWriter tool for wavefront OBJ file"
+puts "Write textured boat OBJ model into OBJ file"
+puts "========"
+
+pload XDE OCAF MODELING VISUALIZATION
+Close D -silent
+Close D1 -silent
+ReadObj D1 [locate_data_file ship_boat.obj]
+
+set aTmpObjBase "${imagedir}/${casename}_tmp"
+set aTmpObj "${aTmpObjBase}.obj"
+lappend occ_tmp_files $aTmpObj
+lappend occ_tmp_files "${aTmpObjBase}.mtl"
+lappend occ_tmp_files "${aTmpObjBase}_textures"
+
+WriteObj D1 "$aTmpObj"
+
+ReadObj D "$aTmpObj"
+XGetOneShape s D
+checknbshapes s -face 158 -compound 2
+checktrinfo s -tri 27297 -nod 40496
+
+vclear
+vinit View1
+XDisplay -dispMode 1 D
+vaxo
+vfit
+vdump ${imagedir}/${casename}.png
box b1 1 1 1
box b2 1 1 1
+vclear
vinit View1
-vdisplay b1
-vdisplay b2
+vdisplay b1 b2
vsetlocation b2 10 10 10
vfit
set listmem {}
-
-set i_max 3
-for {set i 1} {${i} <= ${i_max}} {incr i} {
- vfps 1000
+set aNbChecks 50
+for {set anIter 1} {$anIter <= $aNbChecks} {incr anIter} {
+ vfps 100
lappend listmem [meminfo h]
- checktrend $listmem 0 1 "Memory leak detected"
+ #checktrend $listmem 0 1 "Memory leak detected"
}
+puts $listmem
+checktrend $listmem 0 1 "Memory leak detected"
vdump ${imagedir}/${casename}.png