1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-04 18:06:22 +03:00

0029303: Data Exchange - add RWObj_CafWriter tool for wavefront OBJ file

Unstable test case v3d/memory/bug26538 has been adjusted.
This commit is contained in:
kgv 2021-05-31 20:14:03 +03:00 committed by bugmaster
parent ff1f0c9ae2
commit 1b6b8afcd0
21 changed files with 1589 additions and 21 deletions

View File

@ -21,6 +21,7 @@ Message_CompositeAlerts.hxx
Message_ExecStatus.hxx
Message_Gravity.hxx
Message_HArrayOfMsg.hxx
Message_LazyProgressScope.hxx
Message_Level.cxx
Message_Level.hxx
Message_ListIteratorOfListOfMsg.hxx

View File

@ -0,0 +1,80 @@
// 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

View File

@ -1068,7 +1068,7 @@ void RWGltf_CafWriter::writeAsset (const TColStd_IndexedDataMapOfStringString& t
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

View File

@ -127,7 +127,7 @@ void RWMesh_FaceIterator::dispatchStyles (const TDF_Label& theLabel,
// 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())

View File

@ -100,7 +100,7 @@ public:
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)
@ -148,7 +148,7 @@ public:
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); }
@ -185,7 +185,7 @@ private:
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

View File

@ -2,9 +2,15 @@ RWObj.cxx
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

View File

@ -0,0 +1,416 @@
// 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;
}

View File

@ -0,0 +1,167 @@
// 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

View File

@ -0,0 +1,153 @@
// 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());
}
}
}

View File

@ -0,0 +1,46 @@
// 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

View File

@ -0,0 +1,305 @@
// 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;
}
}

View File

@ -0,0 +1,100 @@
// 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

View File

@ -83,6 +83,27 @@ public:
//! 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

View File

@ -47,6 +47,7 @@
#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>
@ -669,6 +670,117 @@ static Standard_Integer ReadObj (Draw_Interpretor& theDI,
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)
{
@ -1782,18 +1894,20 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
"\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",
@ -1826,6 +1940,18 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
"\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 );

View File

@ -1,6 +1,7 @@
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

View File

@ -0,0 +1,30 @@
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

View File

@ -0,0 +1,28 @@
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

View File

@ -0,0 +1,29 @@
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

View File

@ -0,0 +1,29 @@
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

View File

@ -0,0 +1,29 @@
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

View File

@ -7,19 +7,20 @@ pload MODELING VISUALIZATION
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