mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-08-09 13:22:24 +03:00
0029296: Data Exchange - implement import of mesh data from files in OBJ format
RWObj_Reader and RWObj_CafReader - added new classes reading triangulation from OBJ file.
This commit is contained in:
@@ -442,3 +442,4 @@ n XCAFNoteObjects
|
|||||||
t TKRWMesh
|
t TKRWMesh
|
||||||
n RWGltf
|
n RWGltf
|
||||||
n RWMesh
|
n RWMesh
|
||||||
|
n RWObj
|
||||||
|
14
src/RWObj/FILES
Normal file
14
src/RWObj/FILES
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
RWObj.cxx
|
||||||
|
RWObj.hxx
|
||||||
|
RWObj_CafReader.cxx
|
||||||
|
RWObj_CafReader.hxx
|
||||||
|
RWObj_Material.hxx
|
||||||
|
RWObj_MtlReader.cxx
|
||||||
|
RWObj_MtlReader.hxx
|
||||||
|
RWObj_Reader.cxx
|
||||||
|
RWObj_Reader.hxx
|
||||||
|
RWObj_SubMesh.hxx
|
||||||
|
RWObj_SubMeshReason.hxx
|
||||||
|
RWObj_Tools.hxx
|
||||||
|
RWObj_TriangulationReader.cxx
|
||||||
|
RWObj_TriangulationReader.hxx
|
32
src/RWObj/RWObj.cxx
Normal file
32
src/RWObj/RWObj.cxx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2017-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#include <RWObj.hxx>
|
||||||
|
|
||||||
|
#include <RWObj_TriangulationReader.hxx>
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//function : Read
|
||||||
|
//purpose :
|
||||||
|
//=============================================================================
|
||||||
|
Handle(Poly_Triangulation) RWObj::ReadFile (const Standard_CString theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress)
|
||||||
|
{
|
||||||
|
RWObj_TriangulationReader aReader;
|
||||||
|
aReader.SetCreateShapes (Standard_False);
|
||||||
|
aReader.Read (theFile, theProgress);
|
||||||
|
// note that returned bool value is ignored intentionally -- even if something went wrong,
|
||||||
|
// but some data have been read, we at least will return these data
|
||||||
|
return aReader.GetTriangulation();
|
||||||
|
}
|
35
src/RWObj/RWObj.hxx
Normal file
35
src/RWObj/RWObj.hxx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2017-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_HeaderFile
|
||||||
|
#define _RWObj_HeaderFile
|
||||||
|
|
||||||
|
#include <Message_ProgressIndicator.hxx>
|
||||||
|
#include <OSD_Path.hxx>
|
||||||
|
#include <Poly_Triangulation.hxx>
|
||||||
|
#include <Standard_Macro.hxx>
|
||||||
|
|
||||||
|
//! This class provides methods to read and write triangulation from / to the OBJ files.
|
||||||
|
class RWObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Read specified OBJ file and returns its content as triangulation.
|
||||||
|
//! In case of error, returns Null handle.
|
||||||
|
Standard_EXPORT static Handle(Poly_Triangulation) ReadFile (const Standard_CString theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& aProgInd = NULL);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_HeaderFile
|
104
src/RWObj/RWObj_CafReader.cxx
Normal file
104
src/RWObj/RWObj_CafReader.cxx
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#include <RWObj_CafReader.hxx>
|
||||||
|
|
||||||
|
#include <Message_ProgressSentry.hxx>
|
||||||
|
|
||||||
|
IMPLEMENT_STANDARD_RTTIEXT(RWObj_CafReader, RWMesh_CafReader)
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : Constructor
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
RWObj_CafReader::RWObj_CafReader()
|
||||||
|
: myIsSinglePrecision (Standard_False)
|
||||||
|
{
|
||||||
|
//myCoordSysConverter.SetInputLengthUnit (-1.0); // length units are undefined within OBJ file
|
||||||
|
// OBJ format does not define coordinate system (apart from mentioning that it is right-handed),
|
||||||
|
// however most files are stored Y-up
|
||||||
|
myCoordSysConverter.SetInputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : BindNamedShape
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
void RWObj_CafReader::BindNamedShape (const TopoDS_Shape& theShape,
|
||||||
|
const TCollection_AsciiString& theName,
|
||||||
|
const RWObj_Material* theMaterial,
|
||||||
|
const Standard_Boolean theIsRootShape)
|
||||||
|
{
|
||||||
|
if (theShape.IsNull())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWMesh_NodeAttributes aShapeAttribs;
|
||||||
|
aShapeAttribs.Name = theName;
|
||||||
|
if (theMaterial != NULL)
|
||||||
|
{
|
||||||
|
aShapeAttribs.Style.SetColorSurf (Quantity_ColorRGBA (theMaterial->DiffuseColor, 1.0f - theMaterial->Transparency));
|
||||||
|
}
|
||||||
|
myAttribMap.Bind (theShape, aShapeAttribs);
|
||||||
|
|
||||||
|
if (theIsRootShape)
|
||||||
|
{
|
||||||
|
myRootShapes.Append (theShape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : createReaderContext
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
Handle(RWObj_TriangulationReader) RWObj_CafReader::createReaderContext()
|
||||||
|
{
|
||||||
|
Handle(RWObj_TriangulationReader) aReader = new RWObj_TriangulationReader();
|
||||||
|
return aReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : performMesh
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
Standard_Boolean RWObj_CafReader::performMesh (const TCollection_AsciiString& theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress,
|
||||||
|
const Standard_Boolean theToProbe)
|
||||||
|
{
|
||||||
|
Handle(RWObj_TriangulationReader) aCtx = createReaderContext();
|
||||||
|
aCtx->SetSinglePrecision (myIsSinglePrecision);
|
||||||
|
aCtx->SetCreateShapes (Standard_True);
|
||||||
|
aCtx->SetShapeReceiver (this);
|
||||||
|
aCtx->SetTransformation (myCoordSysConverter);
|
||||||
|
aCtx->SetMemoryLimit (myMemoryLimitMiB == -1 ? Standard_Size(-1) : Standard_Size(myMemoryLimitMiB * 1024 * 1024));
|
||||||
|
Standard_Boolean isDone = Standard_False;
|
||||||
|
if (theToProbe)
|
||||||
|
{
|
||||||
|
isDone = aCtx->Probe (theFile.ToCString(), theProgress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isDone = aCtx->Read (theFile.ToCString(), theProgress);
|
||||||
|
}
|
||||||
|
if (!aCtx->FileComments().IsEmpty())
|
||||||
|
{
|
||||||
|
myMetadata.Add ("Comments", aCtx->FileComments());
|
||||||
|
}
|
||||||
|
for (NCollection_IndexedMap<TCollection_AsciiString>::Iterator aFileIter (aCtx->ExternalFiles()); aFileIter.More(); aFileIter.Next())
|
||||||
|
{
|
||||||
|
myExternalFiles.Add (aFileIter.Value());
|
||||||
|
}
|
||||||
|
return isDone;
|
||||||
|
}
|
63
src/RWObj/RWObj_CafReader.hxx
Normal file
63
src/RWObj/RWObj_CafReader.hxx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_CafReader_HeaderFile
|
||||||
|
#define _RWObj_CafReader_HeaderFile
|
||||||
|
|
||||||
|
#include <RWMesh_CafReader.hxx>
|
||||||
|
#include <RWObj_TriangulationReader.hxx>
|
||||||
|
|
||||||
|
//! The OBJ mesh reader into XDE document.
|
||||||
|
class RWObj_CafReader : public RWMesh_CafReader, protected RWObj_IShapeReceiver
|
||||||
|
{
|
||||||
|
DEFINE_STANDARD_RTTIEXT(RWObj_CafReader, RWMesh_CafReader)
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Empty constructor.
|
||||||
|
Standard_EXPORT RWObj_CafReader();
|
||||||
|
|
||||||
|
//! Return single precision flag for reading vertex data (coordinates); FALSE by default.
|
||||||
|
Standard_Boolean IsSinglePrecision() const { return myIsSinglePrecision; }
|
||||||
|
|
||||||
|
//! Setup single/double precision flag for reading vertex data (coordinates).
|
||||||
|
void SetSinglePrecision (Standard_Boolean theIsSinglePrecision) { myIsSinglePrecision = theIsSinglePrecision; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Read the mesh from specified file.
|
||||||
|
Standard_EXPORT virtual Standard_Boolean performMesh (const TCollection_AsciiString& theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress,
|
||||||
|
const Standard_Boolean theToProbe) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Create reader context.
|
||||||
|
//! Can be overridden by sub-class to read triangulation into application-specific data structures instead of Poly_Triangulation.
|
||||||
|
virtual Handle(RWObj_TriangulationReader) createReaderContext();
|
||||||
|
|
||||||
|
//! @param theShape shape to register
|
||||||
|
//! @param theName shape name
|
||||||
|
//! @param theMaterial shape material
|
||||||
|
//! @param theIsRootShape indicates that this is a root object (free shape)
|
||||||
|
virtual void BindNamedShape (const TopoDS_Shape& theShape,
|
||||||
|
const TCollection_AsciiString& theName,
|
||||||
|
const RWObj_Material* theMaterial,
|
||||||
|
const Standard_Boolean theIsRootShape) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Standard_Boolean myIsSinglePrecision; //!< flag for reading vertex data with single or double floating point precision
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_CafReader_HeaderFile
|
43
src/RWObj/RWObj_Material.hxx
Normal file
43
src/RWObj/RWObj_Material.hxx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2015-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_Material_HeaderFile
|
||||||
|
#define _RWObj_Material_HeaderFile
|
||||||
|
|
||||||
|
#include <Quantity_Color.hxx>
|
||||||
|
#include <TCollection_AsciiString.hxx>
|
||||||
|
|
||||||
|
//! Material definition for OBJ file format.
|
||||||
|
struct RWObj_Material
|
||||||
|
{
|
||||||
|
TCollection_AsciiString Name; //!< material name (identifier) as defined in MTL file
|
||||||
|
TCollection_AsciiString DiffuseTexture; //!< path to the texture image file defining diffuse color
|
||||||
|
TCollection_AsciiString SpecularTexture; //!< path to the texture image file defining specular color
|
||||||
|
TCollection_AsciiString BumpTexture; //!< path to the texture image file defining normal map
|
||||||
|
Quantity_Color AmbientColor;
|
||||||
|
Quantity_Color DiffuseColor;
|
||||||
|
Quantity_Color SpecularColor;
|
||||||
|
Standard_ShortReal Shininess;
|
||||||
|
Standard_ShortReal Transparency;
|
||||||
|
|
||||||
|
RWObj_Material()
|
||||||
|
: AmbientColor (0.1, 0.1, 0.1, Quantity_TOC_RGB),
|
||||||
|
DiffuseColor (0.8, 0.8, 0.8, Quantity_TOC_RGB),
|
||||||
|
SpecularColor(0.2, 0.2, 0.2, Quantity_TOC_RGB),
|
||||||
|
Shininess (1.0f),
|
||||||
|
Transparency (0.0f) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_Material_HeaderFile
|
351
src/RWObj/RWObj_MtlReader.cxx
Normal file
351
src/RWObj/RWObj_MtlReader.cxx
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2017-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#include <RWObj_MtlReader.hxx>
|
||||||
|
|
||||||
|
#include <RWObj_Tools.hxx>
|
||||||
|
|
||||||
|
#include <Message.hxx>
|
||||||
|
#include <Message_Messenger.hxx>
|
||||||
|
#include <OSD_File.hxx>
|
||||||
|
#include <OSD_OpenFile.hxx>
|
||||||
|
#include <OSD_Path.hxx>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
//! Try to find a new location of the file relative to specified folder from absolute path.
|
||||||
|
//! @param theAbsolutePath original absolute file path
|
||||||
|
//! @param theNewFoler the new folder to look for the file
|
||||||
|
//! @param theRelativePath result file path relative to theNewFoler
|
||||||
|
//! @return true if relative file has been found
|
||||||
|
static bool findRelativePath (const TCollection_AsciiString& theAbsolutePath,
|
||||||
|
const TCollection_AsciiString& theNewFoler,
|
||||||
|
TCollection_AsciiString& theRelativePath)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aNewFoler = (theNewFoler.EndsWith ("\\") || theNewFoler.EndsWith ("/"))
|
||||||
|
? theNewFoler
|
||||||
|
: (theNewFoler + "/");
|
||||||
|
|
||||||
|
TCollection_AsciiString aRelPath;
|
||||||
|
TCollection_AsciiString aPath = theAbsolutePath;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aFolder, aFileName;
|
||||||
|
OSD_Path::FolderAndFileFromPath (aPath, aFolder, aFileName);
|
||||||
|
if (aFolder.IsEmpty()
|
||||||
|
|| aFileName.IsEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aRelPath.IsEmpty())
|
||||||
|
{
|
||||||
|
aRelPath = aFileName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aRelPath = aFileName + "/" + aRelPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OSD_File (aNewFoler + aRelPath).Exists())
|
||||||
|
{
|
||||||
|
theRelativePath = aRelPath;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
aPath = aFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : RWObj_MtlReader
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
RWObj_MtlReader::RWObj_MtlReader (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>& theMaterials)
|
||||||
|
: myFile (NULL),
|
||||||
|
myMaterials (&theMaterials),
|
||||||
|
myNbLines (0)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : ~RWObj_MtlReader
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
RWObj_MtlReader::~RWObj_MtlReader()
|
||||||
|
{
|
||||||
|
if (myFile != NULL)
|
||||||
|
{
|
||||||
|
::fclose (myFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : Read
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
bool RWObj_MtlReader::Read (const TCollection_AsciiString& theFolder,
|
||||||
|
const TCollection_AsciiString& theFile)
|
||||||
|
{
|
||||||
|
myPath = theFolder + theFile;
|
||||||
|
myFile = OSD_OpenFile (myPath.ToCString(), "rb");
|
||||||
|
if (myFile == NULL)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString ("OBJ material file '") + myPath + "' is not found!", Message_Warning);
|
||||||
|
return Standard_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
char aLine[256] = {};
|
||||||
|
TCollection_AsciiString aMatName;
|
||||||
|
RWObj_Material aMat;
|
||||||
|
const Standard_Integer aNbMatOld = myMaterials->Extent();
|
||||||
|
bool hasAspect = false;
|
||||||
|
for (; ::feof (myFile) == 0 && ::fgets (aLine, 255, myFile) != NULL; )
|
||||||
|
{
|
||||||
|
++myNbLines;
|
||||||
|
|
||||||
|
const char* aPos = aLine;
|
||||||
|
|
||||||
|
// skip spaces
|
||||||
|
for (; IsSpace(*aPos);)
|
||||||
|
{
|
||||||
|
++aPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*aPos == '#'
|
||||||
|
|| *aPos == '\n'
|
||||||
|
|| *aPos == '\0')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::memcmp (aPos, "newmtl", 6) == 0)
|
||||||
|
{
|
||||||
|
aPos += 7;
|
||||||
|
if (!aMatName.IsEmpty())
|
||||||
|
{
|
||||||
|
if (hasAspect)
|
||||||
|
{
|
||||||
|
aMat.Name = aMatName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reset incomplete material definition
|
||||||
|
aMat = RWObj_Material();
|
||||||
|
}
|
||||||
|
myMaterials->Bind (aMatName, aMat);
|
||||||
|
hasAspect = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aMatName = TCollection_AsciiString(aPos);
|
||||||
|
aMat = RWObj_Material();
|
||||||
|
if (!RWObj_Tools::ReadName (aPos, aMatName))
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Empty OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "Ka", 2) == 0
|
||||||
|
&& IsSpace (aPos[2]))
|
||||||
|
{
|
||||||
|
aPos += 3;
|
||||||
|
char* aNext = NULL;
|
||||||
|
Graphic3d_Vec3 aColor;
|
||||||
|
RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
|
||||||
|
aPos = aNext;
|
||||||
|
if (validateColor (aColor))
|
||||||
|
{
|
||||||
|
aMat.AmbientColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB);
|
||||||
|
hasAspect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "Kd", 2) == 0
|
||||||
|
&& IsSpace (aPos[2]))
|
||||||
|
{
|
||||||
|
aPos += 3;
|
||||||
|
char* aNext = NULL;
|
||||||
|
Graphic3d_Vec3 aColor;
|
||||||
|
RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
|
||||||
|
aPos = aNext;
|
||||||
|
if (validateColor (aColor))
|
||||||
|
{
|
||||||
|
aMat.DiffuseColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB);
|
||||||
|
hasAspect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "Ks", 2) == 0
|
||||||
|
&& IsSpace (aPos[2]))
|
||||||
|
{
|
||||||
|
aPos += 3;
|
||||||
|
char* aNext = NULL;
|
||||||
|
Graphic3d_Vec3 aColor;
|
||||||
|
RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
|
||||||
|
aPos = aNext;
|
||||||
|
if (validateColor (aColor))
|
||||||
|
{
|
||||||
|
aMat.SpecularColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "Ns", 2) == 0
|
||||||
|
&& IsSpace (aPos[2]))
|
||||||
|
{
|
||||||
|
aPos += 3;
|
||||||
|
char* aNext = NULL;
|
||||||
|
double aSpecular = Strtod (aPos, &aNext);
|
||||||
|
aPos = aNext;
|
||||||
|
if (aSpecular >= 0.0)
|
||||||
|
{
|
||||||
|
aMat.Shininess = (float )Min (aSpecular / 1000.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "Tr", 2) == 0
|
||||||
|
&& IsSpace (aPos[2]))
|
||||||
|
{
|
||||||
|
aPos += 3;
|
||||||
|
char* aNext = NULL;
|
||||||
|
double aTransp = Strtod (aPos, &aNext);
|
||||||
|
aPos = aNext;
|
||||||
|
if (validateScalar (aTransp)
|
||||||
|
&& aTransp <= 0.99)
|
||||||
|
{
|
||||||
|
aMat.Transparency = (float )aTransp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*aPos == 'd' && IsSpace (aPos[1]))
|
||||||
|
{
|
||||||
|
// dissolve
|
||||||
|
aPos += 2;
|
||||||
|
char* aNext = NULL;
|
||||||
|
double anAlpha = Strtod (aPos, &aNext);
|
||||||
|
aPos = aNext;
|
||||||
|
if (validateScalar (anAlpha)
|
||||||
|
&& anAlpha >= 0.01)
|
||||||
|
{
|
||||||
|
aMat.Transparency = float(1.0 - anAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "map_Kd", 6) == 0
|
||||||
|
&& IsSpace (aPos[6]))
|
||||||
|
{
|
||||||
|
aPos += 7;
|
||||||
|
if (RWObj_Tools::ReadName (aPos, aMat.DiffuseTexture))
|
||||||
|
{
|
||||||
|
processTexturePath (aMat.DiffuseTexture, theFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "map_Ks", 6) == 0
|
||||||
|
&& IsSpace (aPos[6]))
|
||||||
|
{
|
||||||
|
aPos += 7;
|
||||||
|
if (RWObj_Tools::ReadName (aPos, aMat.SpecularTexture))
|
||||||
|
{
|
||||||
|
processTexturePath (aMat.SpecularTexture, theFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (::memcmp (aPos, "map_Bump", 8) == 0
|
||||||
|
&& IsSpace (aPos[8]))
|
||||||
|
{
|
||||||
|
aPos += 9;
|
||||||
|
if (RWObj_Tools::ReadName (aPos, aMat.BumpTexture))
|
||||||
|
{
|
||||||
|
processTexturePath (aMat.BumpTexture, theFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*else if (::memcmp (aPos, "illum", 5) == 0)
|
||||||
|
{
|
||||||
|
aPos += 6;
|
||||||
|
char* aNext = NULL;
|
||||||
|
const int aModel = strtol (aPos, &aNext, 10);
|
||||||
|
aPos = aNext;
|
||||||
|
if (aModel < 0 || aModel > 10)
|
||||||
|
{
|
||||||
|
// unknown model
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aMatName.IsEmpty())
|
||||||
|
{
|
||||||
|
if (hasAspect)
|
||||||
|
{
|
||||||
|
aMat.Name = aMatName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reset incomplete material definition
|
||||||
|
aMat = RWObj_Material();
|
||||||
|
}
|
||||||
|
myMaterials->Bind (aMatName, aMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return myMaterials->Extent() != aNbMatOld;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : processTexturePath
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_MtlReader::processTexturePath (TCollection_AsciiString& theTexturePath,
|
||||||
|
const TCollection_AsciiString& theFolder)
|
||||||
|
{
|
||||||
|
if (OSD_Path::IsAbsolutePath (theTexturePath.ToCString()))
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("OBJ file specifies absolute path to the texture image file which may be inaccessible on another device\n")
|
||||||
|
+ theTexturePath, Message_Warning);
|
||||||
|
if (!OSD_File (theTexturePath).Exists())
|
||||||
|
{
|
||||||
|
// workaround absolute filenames - try to find the same file at the OBJ file location
|
||||||
|
TCollection_AsciiString aRelativePath;
|
||||||
|
if (findRelativePath (theTexturePath, theFolder, aRelativePath))
|
||||||
|
{
|
||||||
|
theTexturePath = theFolder + aRelativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
theTexturePath = theFolder + theTexturePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : validateScalar
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
bool RWObj_MtlReader::validateScalar (const Standard_Real theValue)
|
||||||
|
{
|
||||||
|
if (theValue < 0.0
|
||||||
|
|| theValue > 1.0)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid scalar in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : validateColor
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
bool RWObj_MtlReader::validateColor (const Graphic3d_Vec3& theVec)
|
||||||
|
{
|
||||||
|
if (theVec.r() < 0.0f || theVec.r() > 1.0f
|
||||||
|
|| theVec.g() < 0.0f || theVec.g() > 1.0f
|
||||||
|
|| theVec.b() < 0.0f || theVec.b() > 1.0f)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid color in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
58
src/RWObj/RWObj_MtlReader.hxx
Normal file
58
src/RWObj/RWObj_MtlReader.hxx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2015-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_MtlReader_HeaderFile
|
||||||
|
#define _RWObj_MtlReader_HeaderFile
|
||||||
|
|
||||||
|
#include <Graphic3d_Vec3.hxx>
|
||||||
|
#include <RWObj_Material.hxx>
|
||||||
|
#include <NCollection_DataMap.hxx>
|
||||||
|
|
||||||
|
//! Reader of mtl files.
|
||||||
|
class RWObj_MtlReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Main constructor.
|
||||||
|
RWObj_MtlReader (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>& theMaterials);
|
||||||
|
|
||||||
|
//! Destructor.
|
||||||
|
~RWObj_MtlReader();
|
||||||
|
|
||||||
|
//! Read the file.
|
||||||
|
bool Read (const TCollection_AsciiString& theFolder,
|
||||||
|
const TCollection_AsciiString& theFile);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//! Validate scalar value
|
||||||
|
bool validateScalar (const Standard_Real theValue);
|
||||||
|
|
||||||
|
//! Validate RGB color
|
||||||
|
bool validateColor (const Graphic3d_Vec3& theVec);
|
||||||
|
|
||||||
|
//! Process texture path.
|
||||||
|
void processTexturePath (TCollection_AsciiString& theTexturePath,
|
||||||
|
const TCollection_AsciiString& theFolder);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
FILE* myFile;
|
||||||
|
TCollection_AsciiString myPath;
|
||||||
|
NCollection_DataMap<TCollection_AsciiString, RWObj_Material>* myMaterials;
|
||||||
|
int myNbLines;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_MtlReader_HeaderFile
|
829
src/RWObj/RWObj_Reader.cxx
Normal file
829
src/RWObj/RWObj_Reader.cxx
Normal file
@@ -0,0 +1,829 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2017-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#include <RWObj_Reader.hxx>
|
||||||
|
|
||||||
|
#include <RWObj_MtlReader.hxx>
|
||||||
|
|
||||||
|
#include <BRepMesh_DataStructureOfDelaun.hxx>
|
||||||
|
#include <BRepMesh_Delaun.hxx>
|
||||||
|
#include <gp_XY.hxx>
|
||||||
|
#include <Message.hxx>
|
||||||
|
#include <Message_Messenger.hxx>
|
||||||
|
#include <Message_ProgressSentry.hxx>
|
||||||
|
#include <NCollection_IncAllocator.hxx>
|
||||||
|
#include <OSD_OpenFile.hxx>
|
||||||
|
#include <OSD_Path.hxx>
|
||||||
|
#include <OSD_Timer.hxx>
|
||||||
|
#include <Precision.hxx>
|
||||||
|
#include <Standard_CLocaleSentry.hxx>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define ftell64(a) _ftelli64(a)
|
||||||
|
#define fseek64(a,b,c) _fseeki64(a,b,c)
|
||||||
|
#else
|
||||||
|
#define ftell64(a) ftello(a)
|
||||||
|
#define fseek64(a,b,c) fseeko(a,b,c)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IMPLEMENT_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
//! Simple wrapper.
|
||||||
|
struct RWObj_ReaderFile
|
||||||
|
{
|
||||||
|
FILE* File;
|
||||||
|
NCollection_Array1<char> Line;
|
||||||
|
Standard_Integer LineBuffLen;
|
||||||
|
Standard_Integer MaxLineLen;
|
||||||
|
int64_t Position;
|
||||||
|
int64_t FileLen;
|
||||||
|
|
||||||
|
//! Constructor opening the file.
|
||||||
|
RWObj_ReaderFile (const TCollection_AsciiString& theFile,
|
||||||
|
const Standard_Integer theMaxLineLen = 256)
|
||||||
|
: File (OSD_OpenFile (theFile.ToCString(), "rb")),
|
||||||
|
Line (0, theMaxLineLen - 1),
|
||||||
|
LineBuffLen (theMaxLineLen),
|
||||||
|
MaxLineLen (theMaxLineLen),
|
||||||
|
Position (0),
|
||||||
|
FileLen (0)
|
||||||
|
{
|
||||||
|
if (this->File != NULL)
|
||||||
|
{
|
||||||
|
// determine length of file
|
||||||
|
::fseek64 (this->File, 0, SEEK_END);
|
||||||
|
FileLen = ::ftell64 (this->File);
|
||||||
|
::fseek64 (this->File, 0, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructor closing the file.
|
||||||
|
~RWObj_ReaderFile()
|
||||||
|
{
|
||||||
|
if (File != NULL)
|
||||||
|
{
|
||||||
|
::fclose (File);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Read line, also considers multi-line syntax (when last line symbol is slash).
|
||||||
|
bool ReadLine()
|
||||||
|
{
|
||||||
|
int64_t aPosPrev = this->Position;
|
||||||
|
char* aLine = &Line.ChangeFirst();
|
||||||
|
for (; ::feof (this->File) == 0 && ::fgets (aLine, MaxLineLen - 1, this->File) != NULL; )
|
||||||
|
{
|
||||||
|
const int64_t aPosNew = ::ftell64 (this->File);
|
||||||
|
if (aLine[0] == '#')
|
||||||
|
{
|
||||||
|
Position = aPosNew;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Standard_Integer aNbRead = Standard_Integer(aPosNew - aPosPrev);
|
||||||
|
bool toReadMore = false;
|
||||||
|
for (int aTailIter = aNbRead - 1; aTailIter >= 0; --aTailIter)
|
||||||
|
{
|
||||||
|
if (aLine[aTailIter] != '\n'
|
||||||
|
&& aLine[aTailIter] != '\r'
|
||||||
|
&& aLine[aTailIter] != '\0')
|
||||||
|
{
|
||||||
|
if (aLine[aTailIter] == '\\')
|
||||||
|
{
|
||||||
|
// multi-line syntax
|
||||||
|
aLine[aTailIter] = ' ';
|
||||||
|
const ptrdiff_t aFullLen = aLine + aTailIter + 1 - &this->Line.First();
|
||||||
|
if (LineBuffLen < aNbRead + MaxLineLen)
|
||||||
|
{
|
||||||
|
LineBuffLen += MaxLineLen;
|
||||||
|
this->Line.Resize (0, LineBuffLen - 1, true);
|
||||||
|
}
|
||||||
|
aLine = &this->Line.ChangeFirst() + aFullLen;
|
||||||
|
toReadMore = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toReadMore)
|
||||||
|
{
|
||||||
|
aPosPrev = aPosNew;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position = aPosNew;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Return TRUE if given polygon has clockwise node order.
|
||||||
|
static bool isClockwisePolygon (const Handle(BRepMesh_DataStructureOfDelaun)& theMesh,
|
||||||
|
const IMeshData::VectorOfInteger& theIndexes)
|
||||||
|
{
|
||||||
|
double aPtSum = 0;
|
||||||
|
const int aNbElemNodes = theIndexes.Size();
|
||||||
|
for (int aNodeIter = theIndexes.Lower(); aNodeIter <= theIndexes.Upper(); ++aNodeIter)
|
||||||
|
{
|
||||||
|
int aNodeNext = theIndexes.Lower() + ((aNodeIter + 1) % aNbElemNodes);
|
||||||
|
const BRepMesh_Vertex& aVert1 = theMesh->GetNode (theIndexes.Value (aNodeIter));
|
||||||
|
const BRepMesh_Vertex& aVert2 = theMesh->GetNode (theIndexes.Value (aNodeNext));
|
||||||
|
aPtSum += (aVert2.Coord().X() - aVert1.Coord().X())
|
||||||
|
* (aVert2.Coord().Y() + aVert1.Coord().Y());
|
||||||
|
}
|
||||||
|
return aPtSum < 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// Function : Read
|
||||||
|
// Purpose :
|
||||||
|
// ================================================================
|
||||||
|
RWObj_Reader::RWObj_Reader()
|
||||||
|
: myMemLimitBytes (Standard_Size(-1)),
|
||||||
|
myMemEstim (0),
|
||||||
|
myNbLines (0),
|
||||||
|
myNbProbeNodes (0),
|
||||||
|
myNbProbeElems (0),
|
||||||
|
myNbElemsBig (0),
|
||||||
|
myToAbort (false)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// Function : read
|
||||||
|
// Purpose :
|
||||||
|
// ================================================================
|
||||||
|
Standard_Boolean RWObj_Reader::read (const TCollection_AsciiString& theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress,
|
||||||
|
const Standard_Boolean theToProbe)
|
||||||
|
{
|
||||||
|
myMemEstim = 0;
|
||||||
|
myNbLines = 0;
|
||||||
|
myNbProbeNodes = 0;
|
||||||
|
myNbProbeElems = 0;
|
||||||
|
myNbElemsBig = 0;
|
||||||
|
myToAbort = false;
|
||||||
|
myObjVerts.Reset();
|
||||||
|
myObjVertsUV.Clear();
|
||||||
|
myObjNorms.Clear();
|
||||||
|
myPackedIndices.Clear();
|
||||||
|
myMaterials.Clear();
|
||||||
|
myFileComments.Clear();
|
||||||
|
myExternalFiles.Clear();
|
||||||
|
myActiveSubMesh = RWObj_SubMesh();
|
||||||
|
|
||||||
|
// determine file location to load associated files
|
||||||
|
TCollection_AsciiString aFileName;
|
||||||
|
OSD_Path::FolderAndFileFromPath (theFile, myFolder, aFileName);
|
||||||
|
myCurrElem.resize (1024, -1);
|
||||||
|
|
||||||
|
Standard_CLocaleSentry aLocaleSentry;
|
||||||
|
RWObj_ReaderFile aFile (theFile);
|
||||||
|
if (aFile.File == NULL)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: file '") + theFile + "' is not found!", Message_Fail);
|
||||||
|
return Standard_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine length of file
|
||||||
|
const int64_t aFileLen = aFile.FileLen;
|
||||||
|
if (aFileLen <= 0L)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: file '") + theFile + "' is empty!", Message_Fail);
|
||||||
|
return Standard_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Standard_Integer aNbMiBTotal = Standard_Integer(aFileLen / (1024 * 1024));
|
||||||
|
Standard_Integer aNbMiBPassed = 0;
|
||||||
|
Message_ProgressSentry aPSentry (theProgress, "Reading text OBJ file", 0, aNbMiBTotal, 1);
|
||||||
|
OSD_Timer aTimer;
|
||||||
|
aTimer.Start();
|
||||||
|
|
||||||
|
bool isStart = true;
|
||||||
|
for (; aFile.ReadLine(); )
|
||||||
|
{
|
||||||
|
++myNbLines;
|
||||||
|
const char* aLine = &aFile.Line.First();
|
||||||
|
if (aTimer.ElapsedTime() > 1.0)
|
||||||
|
{
|
||||||
|
if (!aPSentry.More())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Standard_Integer aNbMiBRead = Standard_Integer(aFile.Position / (1024 * 1024));
|
||||||
|
for (; aNbMiBPassed < aNbMiBRead; ++aNbMiBPassed) { aPSentry.Next(); }
|
||||||
|
aTimer.Reset();
|
||||||
|
aTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*aLine == '#')
|
||||||
|
{
|
||||||
|
if (isStart)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aComment (aLine + 1);
|
||||||
|
aComment.LeftAdjust();
|
||||||
|
aComment.RightAdjust();
|
||||||
|
if (!aComment.IsEmpty())
|
||||||
|
{
|
||||||
|
if (!myFileComments.IsEmpty())
|
||||||
|
{
|
||||||
|
myFileComments += "\n";
|
||||||
|
}
|
||||||
|
myFileComments += aComment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (*aLine == '\n'
|
||||||
|
|| *aLine == '\0')
|
||||||
|
{
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
isStart = false;
|
||||||
|
|
||||||
|
if (theToProbe)
|
||||||
|
{
|
||||||
|
if (::memcmp (aLine, "mtllib", 6) == 0)
|
||||||
|
{
|
||||||
|
readMaterialLib (aLine + 7);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'v' && RWObj_Tools::isSpaceChar (aLine[1]))
|
||||||
|
{
|
||||||
|
++myNbProbeNodes;
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'f' && RWObj_Tools::isSpaceChar (aLine[1]))
|
||||||
|
{
|
||||||
|
++myNbProbeElems;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aLine[0] == 'v' && RWObj_Tools::isSpaceChar (aLine[1]))
|
||||||
|
{
|
||||||
|
++myNbProbeNodes;
|
||||||
|
pushVertex (aLine + 2);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'v'
|
||||||
|
&& aLine[1] == 'n'
|
||||||
|
&& RWObj_Tools::isSpaceChar (aLine[2]))
|
||||||
|
{
|
||||||
|
pushNormal (aLine + 3);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'v'
|
||||||
|
&& aLine[1] == 't'
|
||||||
|
&& RWObj_Tools::isSpaceChar (aLine[2]))
|
||||||
|
{
|
||||||
|
pushTexel (aLine + 3);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'f' && RWObj_Tools::isSpaceChar (aLine[1]))
|
||||||
|
{
|
||||||
|
++myNbProbeElems;
|
||||||
|
pushIndices (aLine + 2);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'g' && IsSpace (aLine[1]))
|
||||||
|
{
|
||||||
|
pushGroup (aLine + 2);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 's' && IsSpace (aLine[1]))
|
||||||
|
{
|
||||||
|
pushSmoothGroup (aLine + 2);
|
||||||
|
}
|
||||||
|
else if (aLine[0] == 'o' && IsSpace (aLine[1]))
|
||||||
|
{
|
||||||
|
pushObject (aLine + 2);
|
||||||
|
}
|
||||||
|
else if (::memcmp (aLine, "mtllib", 6) == 0)
|
||||||
|
{
|
||||||
|
readMaterialLib (aLine + 7);
|
||||||
|
}
|
||||||
|
else if (::memcmp (aLine, "usemtl", 6) == 0)
|
||||||
|
{
|
||||||
|
pushMaterial (aLine + 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkMemory())
|
||||||
|
{
|
||||||
|
addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewObject);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect external references
|
||||||
|
for (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>::Iterator aMatIter (myMaterials); aMatIter.More(); aMatIter.Next())
|
||||||
|
{
|
||||||
|
const RWObj_Material& aMat = aMatIter.Value();
|
||||||
|
if (!aMat.DiffuseTexture.IsEmpty())
|
||||||
|
{
|
||||||
|
myExternalFiles.Add (aMat.DiffuseTexture);
|
||||||
|
}
|
||||||
|
if (!aMat.SpecularTexture.IsEmpty())
|
||||||
|
{
|
||||||
|
myExternalFiles.Add (aMat.SpecularTexture);
|
||||||
|
}
|
||||||
|
if (!aMat.BumpTexture.IsEmpty())
|
||||||
|
{
|
||||||
|
myExternalFiles.Add (aMat.BumpTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush the last group
|
||||||
|
if (!theToProbe)
|
||||||
|
{
|
||||||
|
addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewObject);
|
||||||
|
}
|
||||||
|
if (myNbElemsBig != 0)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: OBJ reader, ") + myNbElemsBig
|
||||||
|
+ " polygon(s) have been split into triangles.", Message_Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; aNbMiBPassed < aNbMiBTotal; ++aNbMiBPassed) { aPSentry.Next(); }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : pushIndices
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_Reader::pushIndices (const char* thePos)
|
||||||
|
{
|
||||||
|
char* aNext = NULL;
|
||||||
|
|
||||||
|
Standard_Integer aNbElemNodes = 0;
|
||||||
|
for (Standard_Integer aNode = 0;; ++aNode)
|
||||||
|
{
|
||||||
|
Graphic3d_Vec3i a3Indices (-1, -1, -1);
|
||||||
|
a3Indices[0] = strtol (thePos, &aNext, 10) - 1;
|
||||||
|
if (aNext == thePos)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse UV index
|
||||||
|
thePos = aNext;
|
||||||
|
if (*thePos == '/')
|
||||||
|
{
|
||||||
|
++thePos;
|
||||||
|
a3Indices[1] = strtol (thePos, &aNext, 10) - 1;
|
||||||
|
thePos = aNext;
|
||||||
|
|
||||||
|
// parse Normal index
|
||||||
|
if (*thePos == '/')
|
||||||
|
{
|
||||||
|
++thePos;
|
||||||
|
a3Indices[2] = strtol (thePos, &aNext, 10) - 1;
|
||||||
|
thePos = aNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle negative indices
|
||||||
|
if (a3Indices[0] < -1)
|
||||||
|
{
|
||||||
|
a3Indices[0] += myObjVerts.Upper() + 2;
|
||||||
|
}
|
||||||
|
if (a3Indices[1] < -1)
|
||||||
|
{
|
||||||
|
a3Indices[1] += myObjVertsUV.Upper() + 2;
|
||||||
|
}
|
||||||
|
if (a3Indices[2] < -1)
|
||||||
|
{
|
||||||
|
a3Indices[2] += myObjNorms.Upper() + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Standard_Integer anIndex = -1;
|
||||||
|
if (!myPackedIndices.Find (a3Indices, anIndex))
|
||||||
|
{
|
||||||
|
if (a3Indices[0] >= 0)
|
||||||
|
{
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec3);
|
||||||
|
}
|
||||||
|
if (a3Indices[1] >= 0)
|
||||||
|
{
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec2);
|
||||||
|
}
|
||||||
|
if (a3Indices[2] >= 0)
|
||||||
|
{
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec3);
|
||||||
|
}
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec4i) + sizeof(Standard_Integer); // naive map
|
||||||
|
if (a3Indices[0] < myObjVerts.Lower() || a3Indices[0] > myObjVerts.Upper())
|
||||||
|
{
|
||||||
|
myToAbort = true;
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: invalid OBJ syntax at line ") + myNbLines
|
||||||
|
+ ": vertex index is out of range.", Message_Fail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
anIndex = addNode (myObjVerts.Value (a3Indices[0]));
|
||||||
|
myPackedIndices.Bind (a3Indices, anIndex);
|
||||||
|
if (a3Indices[1] >= 0)
|
||||||
|
{
|
||||||
|
if (myObjVertsUV.IsEmpty())
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
|
||||||
|
+ ": UV index is specified but no UV nodes are defined.", Message_Warning);
|
||||||
|
}
|
||||||
|
else if (a3Indices[1] < myObjVertsUV.Lower() || a3Indices[1] > myObjVertsUV.Upper())
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
|
||||||
|
+ ": UV index is out of range.", Message_Warning);
|
||||||
|
setNodeUV (anIndex,Graphic3d_Vec2 (0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setNodeUV (anIndex, myObjVertsUV.Value (a3Indices[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a3Indices[2] >= 0)
|
||||||
|
{
|
||||||
|
if (myObjNorms.IsEmpty())
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
|
||||||
|
+ ": Normal index is specified but no Normals nodes are defined.", Message_Warning);
|
||||||
|
}
|
||||||
|
else if (a3Indices[2] < myObjNorms.Lower() || a3Indices[2] > myObjNorms.Upper())
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ") + myNbLines
|
||||||
|
+ ": Normal index is out of range.", Message_Warning);
|
||||||
|
setNodeNormal (anIndex, Graphic3d_Vec3 (0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setNodeNormal (anIndex, myObjNorms.Value (a3Indices[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myCurrElem.size() < size_t(aNode))
|
||||||
|
{
|
||||||
|
myCurrElem.resize (aNode * 2, -1);
|
||||||
|
}
|
||||||
|
myCurrElem[aNode] = anIndex;
|
||||||
|
aNbElemNodes = aNode + 1;
|
||||||
|
|
||||||
|
if (*thePos == '\n'
|
||||||
|
|| *thePos == '\0')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*thePos != ' ')
|
||||||
|
{
|
||||||
|
++thePos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myCurrElem[0] < 0
|
||||||
|
|| myCurrElem[1] < 0
|
||||||
|
|| myCurrElem[2] < 0
|
||||||
|
|| aNbElemNodes < 3)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aNbElemNodes == 3)
|
||||||
|
{
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec4i);
|
||||||
|
addElement (myCurrElem[0], myCurrElem[1], myCurrElem[2], -1);
|
||||||
|
}
|
||||||
|
else if (aNbElemNodes == 4)
|
||||||
|
{
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec4i);
|
||||||
|
addElement (myCurrElem[0], myCurrElem[1], myCurrElem[2], myCurrElem[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const NCollection_Array1<Standard_Integer> aCurrElemArray1 (myCurrElem[0], 1, aNbElemNodes);
|
||||||
|
const Standard_Integer aNbAdded = triangulatePolygon (aCurrElemArray1);
|
||||||
|
if (aNbAdded < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
++myNbElemsBig;
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec4i) * aNbAdded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : triangulatePolygonFan
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
Standard_Integer RWObj_Reader::triangulatePolygonFan (const NCollection_Array1<Standard_Integer>& theIndices)
|
||||||
|
{
|
||||||
|
const Standard_Integer aNbElemNodes = theIndices.Size();
|
||||||
|
for (Standard_Integer aNodeIter = 0; aNodeIter < aNbElemNodes - 2; ++aNodeIter)
|
||||||
|
{
|
||||||
|
Graphic3d_Vec4i aTriNodes (-1, -1, -1, -1);
|
||||||
|
for (Standard_Integer aNodeInSubTriIter = 0; aNodeInSubTriIter < 3; ++aNodeInSubTriIter)
|
||||||
|
{
|
||||||
|
const Standard_Integer aCurrNodeIndex = (aNodeInSubTriIter == 0) ? 0 : (aNodeIter + aNodeInSubTriIter);
|
||||||
|
aTriNodes[aNodeInSubTriIter] = theIndices.Value (theIndices.Lower() + aCurrNodeIndex);
|
||||||
|
}
|
||||||
|
addElement (aTriNodes[0], aTriNodes[1], aTriNodes[2], -1);
|
||||||
|
}
|
||||||
|
return aNbElemNodes - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : polygonCenter
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
gp_XYZ RWObj_Reader::polygonCenter (const NCollection_Array1<Standard_Integer>& theIndices)
|
||||||
|
{
|
||||||
|
if (theIndices.Size() < 3)
|
||||||
|
{
|
||||||
|
return gp_XYZ (0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
else if (theIndices.Size() == 4)
|
||||||
|
{
|
||||||
|
gp_XYZ aCenter = getNode (theIndices.Value (theIndices.Lower() + 0)).XYZ()
|
||||||
|
+ getNode (theIndices.Value (theIndices.Lower() + 2)).XYZ();
|
||||||
|
aCenter /= 2.0;
|
||||||
|
return aCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
gp_XYZ aCenter (0, 0, 0);
|
||||||
|
for (NCollection_Array1<Standard_Integer>::Iterator aPntIter (theIndices); aPntIter.More(); aPntIter.Next())
|
||||||
|
{
|
||||||
|
aCenter += getNode (aPntIter.Value()).XYZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
aCenter /= (Standard_Real )theIndices.Size();
|
||||||
|
return aCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : polygonNormal
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
gp_XYZ RWObj_Reader::polygonNormal (const NCollection_Array1<Standard_Integer>& theIndices)
|
||||||
|
{
|
||||||
|
const gp_XYZ aCenter = polygonCenter (theIndices);
|
||||||
|
gp_XYZ aMaxDir = getNode (theIndices.First()).XYZ() - aCenter;
|
||||||
|
gp_XYZ aNormal = (getNode (theIndices.Last()).XYZ() - aCenter).Crossed (aMaxDir);
|
||||||
|
for (int aPntIter = theIndices.Lower(); aPntIter < theIndices.Upper(); ++aPntIter)
|
||||||
|
{
|
||||||
|
const gp_XYZ aTmpDir2 = getNode (theIndices.Value (aPntIter + 1)).XYZ() - aCenter;
|
||||||
|
if (aTmpDir2.SquareModulus() > aMaxDir.SquareModulus())
|
||||||
|
{
|
||||||
|
aMaxDir = aTmpDir2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gp_XYZ aTmpDir1 = getNode (theIndices.Value (aPntIter)).XYZ() - aCenter;
|
||||||
|
gp_XYZ aDelta = aTmpDir1.Crossed (aTmpDir2);
|
||||||
|
if (aNormal.Dot (aDelta) < 0.0)
|
||||||
|
{
|
||||||
|
aDelta *= -1.0;
|
||||||
|
}
|
||||||
|
aNormal += aDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Standard_Real aMod = aNormal.Modulus();
|
||||||
|
if (aMod > gp::Resolution())
|
||||||
|
{
|
||||||
|
aNormal /= aMod;
|
||||||
|
}
|
||||||
|
return aNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : triangulatePolygon
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
Standard_Integer RWObj_Reader::triangulatePolygon (const NCollection_Array1<Standard_Integer>& theIndices)
|
||||||
|
{
|
||||||
|
const Standard_Integer aNbElemNodes = theIndices.Size();
|
||||||
|
if (aNbElemNodes < 3)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gp_XYZ aPolygonNorm = polygonNormal (theIndices);
|
||||||
|
|
||||||
|
// map polygon onto plane
|
||||||
|
gp_XYZ aXDir;
|
||||||
|
{
|
||||||
|
const double aAbsXYZ[] = { Abs(aPolygonNorm.X()), Abs(aPolygonNorm.Y()), Abs(aPolygonNorm.Z()) };
|
||||||
|
Standard_Integer aMinI = (aAbsXYZ[0] < aAbsXYZ[1]) ? 0 : 1;
|
||||||
|
aMinI = (aAbsXYZ[aMinI] < aAbsXYZ[2]) ? aMinI : 2;
|
||||||
|
const Standard_Integer aI1 = (aMinI + 1) % 3 + 1;
|
||||||
|
const Standard_Integer aI2 = (aMinI + 2) % 3 + 1;
|
||||||
|
aXDir.ChangeCoord (aMinI + 1) = 0;
|
||||||
|
aXDir.ChangeCoord (aI1) = aPolygonNorm.Coord (aI2);
|
||||||
|
aXDir.ChangeCoord (aI2) = -aPolygonNorm.Coord (aI1);
|
||||||
|
}
|
||||||
|
const gp_XYZ aYDir = aPolygonNorm ^ aXDir;
|
||||||
|
|
||||||
|
Handle(NCollection_IncAllocator) anAllocator = new NCollection_IncAllocator();
|
||||||
|
Handle(BRepMesh_DataStructureOfDelaun) aMeshStructure = new BRepMesh_DataStructureOfDelaun (anAllocator);
|
||||||
|
IMeshData::VectorOfInteger anIndexes (aNbElemNodes, anAllocator);
|
||||||
|
for (Standard_Integer aNodeIter = 0; aNodeIter < aNbElemNodes; ++aNodeIter)
|
||||||
|
{
|
||||||
|
const Standard_Integer aNodeIndex = theIndices.Value (theIndices.Lower() + aNodeIter);
|
||||||
|
const gp_XYZ aPnt3d = getNode (aNodeIndex).XYZ();
|
||||||
|
gp_XY aPnt2d (aXDir * aPnt3d, aYDir * aPnt3d);
|
||||||
|
BRepMesh_Vertex aVertex (aPnt2d, aNodeIndex, BRepMesh_Frontier);
|
||||||
|
anIndexes.Append (aMeshStructure->AddNode (aVertex));
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isClockwiseOrdered = isClockwisePolygon (aMeshStructure, anIndexes);
|
||||||
|
for (Standard_Integer aIdx = anIndexes.Lower(); aIdx <= anIndexes.Upper(); ++aIdx)
|
||||||
|
{
|
||||||
|
const Standard_Integer aPtIdx = isClockwiseOrdered ? aIdx : (aIdx + 1) % anIndexes.Length();
|
||||||
|
const Standard_Integer aNextPtIdx = isClockwiseOrdered ? (aIdx + 1) % anIndexes.Length() : aIdx;
|
||||||
|
BRepMesh_Edge anEdge (anIndexes.Value (aPtIdx),
|
||||||
|
anIndexes.Value (aNextPtIdx),
|
||||||
|
BRepMesh_Frontier);
|
||||||
|
aMeshStructure->AddLink (anEdge);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BRepMesh_Delaun aTriangulation (aMeshStructure, anIndexes);
|
||||||
|
const IMeshData::MapOfInteger& aTriangles = aMeshStructure->ElementsOfDomain();
|
||||||
|
if (aTriangles.Extent() < 1)
|
||||||
|
{
|
||||||
|
return triangulatePolygonFan (theIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
Standard_Integer aNbTrisAdded = 0;
|
||||||
|
for (IMeshData::MapOfInteger::Iterator aTriIter (aTriangles); aTriIter.More(); aTriIter.Next())
|
||||||
|
{
|
||||||
|
const Standard_Integer aTriangleId = aTriIter.Key();
|
||||||
|
const BRepMesh_Triangle& aTriangle = aMeshStructure->GetElement (aTriangleId);
|
||||||
|
if (aTriangle.Movability() == BRepMesh_Deleted)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aTri2d[3];
|
||||||
|
aMeshStructure->ElementNodes (aTriangle, aTri2d);
|
||||||
|
if (!isClockwiseOrdered)
|
||||||
|
{
|
||||||
|
std::swap (aTri2d[1], aTri2d[2]);
|
||||||
|
}
|
||||||
|
const BRepMesh_Vertex& aVertex1 = aMeshStructure->GetNode (aTri2d[0]);
|
||||||
|
const BRepMesh_Vertex& aVertex2 = aMeshStructure->GetNode (aTri2d[1]);
|
||||||
|
const BRepMesh_Vertex& aVertex3 = aMeshStructure->GetNode (aTri2d[2]);
|
||||||
|
addElement (aVertex1.Location3d(), aVertex2.Location3d(), aVertex3.Location3d(), -1);
|
||||||
|
++aNbTrisAdded;
|
||||||
|
}
|
||||||
|
return aNbTrisAdded;
|
||||||
|
}
|
||||||
|
catch (Standard_Failure const& theFailure)
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: exception raised during polygon split\n[")
|
||||||
|
+ theFailure.GetMessageString() + "]", Message_Warning);
|
||||||
|
}
|
||||||
|
return triangulatePolygonFan (theIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : pushObject
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_Reader::pushObject (const char* theObjectName)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aNewObject;
|
||||||
|
if (!RWObj_Tools::ReadName (theObjectName, aNewObject))
|
||||||
|
{
|
||||||
|
// empty group name is OK
|
||||||
|
}
|
||||||
|
if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewObject))
|
||||||
|
{
|
||||||
|
myPackedIndices.Clear(); // vertices might be duplicated after this point...
|
||||||
|
}
|
||||||
|
myActiveSubMesh.Object = aNewObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : pushGroup
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_Reader::pushGroup (const char* theGroupName)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aNewGroup;
|
||||||
|
if (!RWObj_Tools::ReadName (theGroupName, aNewGroup))
|
||||||
|
{
|
||||||
|
// empty group name is OK
|
||||||
|
}
|
||||||
|
if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewGroup))
|
||||||
|
{
|
||||||
|
myPackedIndices.Clear(); // vertices might be duplicated after this point...
|
||||||
|
}
|
||||||
|
myActiveSubMesh.Group = aNewGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : pushSmoothGroup
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_Reader::pushSmoothGroup (const char* theSmoothGroupIndex)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aNewSmoothGroup;
|
||||||
|
RWObj_Tools::ReadName (theSmoothGroupIndex, aNewSmoothGroup);
|
||||||
|
if (aNewSmoothGroup == "off"
|
||||||
|
|| aNewSmoothGroup == "0")
|
||||||
|
{
|
||||||
|
aNewSmoothGroup.Clear();
|
||||||
|
}
|
||||||
|
if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewSmoothGroup))
|
||||||
|
{
|
||||||
|
myPackedIndices.Clear(); // vertices might be duplicated after this point...
|
||||||
|
}
|
||||||
|
myActiveSubMesh.SmoothGroup = aNewSmoothGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : pushMaterial
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_Reader::pushMaterial (const char* theMaterialName)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aNewMat;
|
||||||
|
if (!RWObj_Tools::ReadName (theMaterialName, aNewMat))
|
||||||
|
{
|
||||||
|
// empty material name is allowed by specs
|
||||||
|
}
|
||||||
|
else if (!myMaterials.IsBound (aNewMat))
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: use of undefined OBJ material at line ")
|
||||||
|
+ myNbLines, Message_Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (myActiveSubMesh.Material.IsEqual (aNewMat))
|
||||||
|
{
|
||||||
|
return; // ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicitly create a new group to split materials
|
||||||
|
if (addMesh (myActiveSubMesh, RWObj_SubMeshReason_NewMaterial))
|
||||||
|
{
|
||||||
|
myPackedIndices.Clear(); // vertices might be duplicated after this point...
|
||||||
|
}
|
||||||
|
myActiveSubMesh.Material = aNewMat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : readMaterialLib
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void RWObj_Reader::readMaterialLib (const char* theFileName)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aMatPath;
|
||||||
|
if (!RWObj_Tools::ReadName (theFileName, aMatPath))
|
||||||
|
{
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Warning: invalid OBJ syntax at line ")
|
||||||
|
+ myNbLines, Message_Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RWObj_MtlReader aMatReader (myMaterials);
|
||||||
|
if (aMatReader.Read (myFolder, aMatPath))
|
||||||
|
{
|
||||||
|
myExternalFiles.Add (myFolder + aMatPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : checkMemory
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
bool RWObj_Reader::checkMemory()
|
||||||
|
{
|
||||||
|
if (myMemEstim < myMemLimitBytes
|
||||||
|
|| myToAbort)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::DefaultMessenger()->Send (TCollection_AsciiString("Error: OBJ file content does not fit into ")
|
||||||
|
+ Standard_Integer(myMemLimitBytes / (1024 * 1024)) + " MiB limit."
|
||||||
|
+ "\nMesh data will be truncated.", Message_Fail);
|
||||||
|
myToAbort = true;
|
||||||
|
return false;
|
||||||
|
}
|
364
src/RWObj/RWObj_Reader.hxx
Normal file
364
src/RWObj/RWObj_Reader.hxx
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2015-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_Reader_HeaderFile
|
||||||
|
#define _RWObj_Reader_HeaderFile
|
||||||
|
|
||||||
|
#include <gp_XYZ.hxx>
|
||||||
|
#include <Graphic3d_Vec2.hxx>
|
||||||
|
#include <Graphic3d_Vec4.hxx>
|
||||||
|
#include <Message.hxx>
|
||||||
|
#include <Message_Messenger.hxx>
|
||||||
|
#include <Message_ProgressIndicator.hxx>
|
||||||
|
#include <NCollection_Array1.hxx>
|
||||||
|
#include <NCollection_DataMap.hxx>
|
||||||
|
#include <NCollection_IndexedMap.hxx>
|
||||||
|
#include <NCollection_Vector.hxx>
|
||||||
|
#include <NCollection_Shared.hxx>
|
||||||
|
|
||||||
|
#include <RWMesh_CoordinateSystemConverter.hxx>
|
||||||
|
#include <RWObj_Material.hxx>
|
||||||
|
#include <RWObj_SubMesh.hxx>
|
||||||
|
#include <RWObj_SubMeshReason.hxx>
|
||||||
|
#include <RWObj_Tools.hxx>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//! An abstract class implementing procedure to read OBJ file.
|
||||||
|
//!
|
||||||
|
//! This class is not bound to particular data structure
|
||||||
|
//! and can be used to read the file directly into arbitrary data model.
|
||||||
|
//! To use it, create descendant class and implement interface methods.
|
||||||
|
//!
|
||||||
|
//! Call method Read() to read the file.
|
||||||
|
class RWObj_Reader : public Standard_Transient
|
||||||
|
{
|
||||||
|
DEFINE_STANDARD_RTTIEXT(RWObj_Reader, Standard_Transient)
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Empty constructor.
|
||||||
|
Standard_EXPORT RWObj_Reader();
|
||||||
|
|
||||||
|
//! Reads data from OBJ file.
|
||||||
|
//! Unicode paths can be given in UTF-8 encoding.
|
||||||
|
//! Returns true if success, false on error or user break.
|
||||||
|
Standard_Boolean Read (const TCollection_AsciiString& theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress)
|
||||||
|
{
|
||||||
|
return read (theFile, theProgress, Standard_False);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Probe data from OBJ file (comments, external references) without actually reading mesh data.
|
||||||
|
//! Although mesh data will not be collected, the full file content will be parsed, due to OBJ format limitations.
|
||||||
|
//! @param theFile path to the file
|
||||||
|
//! @param theProgress progress indicator
|
||||||
|
//! @return TRUE if success, FALSE on error or user break.
|
||||||
|
//! @sa FileComments(), ExternalFiles(), NbProbeNodes(), NbProbeElems().
|
||||||
|
Standard_Boolean Probe (const TCollection_AsciiString& theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress)
|
||||||
|
{
|
||||||
|
return read (theFile, theProgress, Standard_True);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns file comments (lines starting with # at the beginning of file).
|
||||||
|
const TCollection_AsciiString& FileComments() const { return myFileComments; }
|
||||||
|
|
||||||
|
//! Return the list of external file references.
|
||||||
|
const NCollection_IndexedMap<TCollection_AsciiString>& ExternalFiles() const { return myExternalFiles; }
|
||||||
|
|
||||||
|
//! Number of probed nodes.
|
||||||
|
Standard_Integer NbProbeNodes() const { return myNbProbeNodes; }
|
||||||
|
|
||||||
|
//!< number of probed polygon elements (of unknown size).
|
||||||
|
Standard_Integer NbProbeElems() const { return myNbProbeElems; }
|
||||||
|
|
||||||
|
//! Returns memory limit in bytes; -1 (no limit) by default.
|
||||||
|
Standard_Size MemoryLimit() const { return myMemLimitBytes; }
|
||||||
|
|
||||||
|
//! Specify memory limit in bytes, so that import will be aborted
|
||||||
|
//! by specified limit before memory allocation error occurs.
|
||||||
|
void SetMemoryLimit (Standard_Size theMemLimit) { myMemLimitBytes = theMemLimit; }
|
||||||
|
|
||||||
|
//! Return transformation from one coordinate system to another; no transformation by default.
|
||||||
|
const RWMesh_CoordinateSystemConverter& Transformation() const { return myCSTrsf; }
|
||||||
|
|
||||||
|
//! Setup transformation from one coordinate system to another.
|
||||||
|
//! OBJ file might be exported following various coordinate system conventions,
|
||||||
|
//! so that it might be useful automatically transform data during file reading.
|
||||||
|
void SetTransformation (const RWMesh_CoordinateSystemConverter& theCSConverter) { myCSTrsf = theCSConverter; }
|
||||||
|
|
||||||
|
//! Return single precision flag for reading vertex data (coordinates); FALSE by default.
|
||||||
|
Standard_Boolean IsSinglePrecision() const { return myObjVerts.IsSinglePrecision(); }
|
||||||
|
|
||||||
|
//! Setup single/double precision flag for reading vertex data (coordinates).
|
||||||
|
void SetSinglePrecision (Standard_Boolean theIsSinglePrecision) { myObjVerts.SetSinglePrecision (theIsSinglePrecision); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Reads data from OBJ file.
|
||||||
|
//! Unicode paths can be given in UTF-8 encoding.
|
||||||
|
//! Returns true if success, false on error or user break.
|
||||||
|
Standard_EXPORT Standard_Boolean read (const TCollection_AsciiString& theFile,
|
||||||
|
const Handle(Message_ProgressIndicator)& theProgress,
|
||||||
|
const Standard_Boolean theToProbe);
|
||||||
|
|
||||||
|
//! @name interface methods which should be implemented by sub-class
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Add new sub-mesh.
|
||||||
|
//! Basically, this method will be called multiple times for the same group with different reason,
|
||||||
|
//! so that implementation should decide if previously allocated sub-mesh should be used or new one to be allocated.
|
||||||
|
//! Sub-mesh command can be skipped if previous sub-mesh is empty,
|
||||||
|
//! or if the reason is out of interest for particular reader
|
||||||
|
//! (e.g. if materials are ignored, reader may ignore RWObj_SubMeshReason_NewMaterial reason).
|
||||||
|
//! @param theMesh mesh definition
|
||||||
|
//! @param theReason reason to create new sub-mesh
|
||||||
|
//! @return TRUE if new sub-mesh should be started since this point
|
||||||
|
virtual Standard_Boolean addMesh (const RWObj_SubMesh& theMesh,
|
||||||
|
const RWObj_SubMeshReason theReason) = 0;
|
||||||
|
|
||||||
|
//! Retrieve sub-mesh node position, added by addNode().
|
||||||
|
virtual gp_Pnt getNode (Standard_Integer theIndex) const = 0;
|
||||||
|
|
||||||
|
//! Callback function to be implemented in descendant.
|
||||||
|
//! Should create new node with specified coordinates in the target model, and return its ID as integer.
|
||||||
|
virtual Standard_Integer addNode (const gp_Pnt& thePnt) = 0;
|
||||||
|
|
||||||
|
//! Callback function to be implemented in descendant.
|
||||||
|
//! Should set normal coordinates for specified node.
|
||||||
|
//! @param theIndex node ID as returned by addNode()
|
||||||
|
//! @param theNorm normal vector
|
||||||
|
virtual void setNodeNormal (const Standard_Integer theIndex,
|
||||||
|
const Graphic3d_Vec3& theNorm) = 0;
|
||||||
|
|
||||||
|
//! Callback function to be implemented in descendant.
|
||||||
|
//! Should set texture coordinates for specified node.
|
||||||
|
//! @param theIndex node ID as returned by addNode()
|
||||||
|
//! @param theUV UV texture coordinates
|
||||||
|
virtual void setNodeUV (const Standard_Integer theIndex,
|
||||||
|
const Graphic3d_Vec2& theUV) = 0;
|
||||||
|
|
||||||
|
//! Callback function to be implemented in descendant.
|
||||||
|
//! Should create new element (triangle or quad if 4th index is != -1) built on specified nodes in the target model.
|
||||||
|
virtual void addElement (Standard_Integer theN1,
|
||||||
|
Standard_Integer theN2,
|
||||||
|
Standard_Integer theN3,
|
||||||
|
Standard_Integer theN4) = 0;
|
||||||
|
|
||||||
|
//! @name implementation details
|
||||||
|
private:
|
||||||
|
|
||||||
|
//! Handle "v X Y Z".
|
||||||
|
void pushVertex (const char* theXYZ)
|
||||||
|
{
|
||||||
|
char* aNext = NULL;
|
||||||
|
gp_Pnt anXYZ;
|
||||||
|
RWObj_Tools::ReadVec3 (theXYZ, aNext, anXYZ.ChangeCoord());
|
||||||
|
myCSTrsf.TransformPosition (anXYZ.ChangeCoord());
|
||||||
|
|
||||||
|
myMemEstim += myObjVerts.IsSinglePrecision() ? sizeof(Graphic3d_Vec3) : sizeof(gp_Pnt);
|
||||||
|
myObjVerts.Append (anXYZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Handle "vn NX NY NZ".
|
||||||
|
void pushNormal (const char* theXYZ)
|
||||||
|
{
|
||||||
|
char* aNext = NULL;
|
||||||
|
Graphic3d_Vec3 aNorm;
|
||||||
|
RWObj_Tools::ReadVec3 (theXYZ, aNext, aNorm);
|
||||||
|
myCSTrsf.TransformNormal (aNorm);
|
||||||
|
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec3);
|
||||||
|
myObjNorms.Append (aNorm);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Handle "vt U V".
|
||||||
|
void pushTexel (const char* theUV)
|
||||||
|
{
|
||||||
|
char* aNext = NULL;
|
||||||
|
Graphic3d_Vec2 anUV;
|
||||||
|
anUV.x() = (float )Strtod (theUV, &aNext);
|
||||||
|
theUV = aNext;
|
||||||
|
anUV.y() = (float )Strtod (theUV, &aNext);
|
||||||
|
|
||||||
|
myMemEstim += sizeof(Graphic3d_Vec2);
|
||||||
|
myObjVertsUV.Append (anUV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Handle "f indices".
|
||||||
|
void pushIndices (const char* thePos);
|
||||||
|
|
||||||
|
//! Compute the center of planar polygon.
|
||||||
|
//! @param theIndices polygon indices
|
||||||
|
//! @return center of polygon
|
||||||
|
gp_XYZ polygonCenter (const NCollection_Array1<Standard_Integer>& theIndices);
|
||||||
|
|
||||||
|
//! Compute the normal to planar polygon.
|
||||||
|
//! The logic is similar to ShapeAnalysis_Curve::IsPlanar().
|
||||||
|
//! @param theIndices polygon indices
|
||||||
|
//! @return polygon normal
|
||||||
|
gp_XYZ polygonNormal (const NCollection_Array1<Standard_Integer>& theIndices);
|
||||||
|
|
||||||
|
//! Create triangle fan from specified polygon.
|
||||||
|
//! @param theIndices polygon nodes
|
||||||
|
//! @return number of added triangles
|
||||||
|
Standard_Integer triangulatePolygonFan (const NCollection_Array1<Standard_Integer>& theIndices);
|
||||||
|
|
||||||
|
//! Triangulate specified polygon.
|
||||||
|
//! @param theIndices polygon nodes
|
||||||
|
//! @return number of added triangles
|
||||||
|
Standard_Integer triangulatePolygon (const NCollection_Array1<Standard_Integer>& theIndices);
|
||||||
|
|
||||||
|
//! Handle "o ObjectName".
|
||||||
|
void pushObject (const char* theObjectName);
|
||||||
|
|
||||||
|
//! Handle "g GroupName".
|
||||||
|
void pushGroup (const char* theGroupName);
|
||||||
|
|
||||||
|
//! Handle "s SmoothGroupIndex".
|
||||||
|
void pushSmoothGroup (const char* theSmoothGroupIndex);
|
||||||
|
|
||||||
|
//! Handle "usemtl MaterialName".
|
||||||
|
void pushMaterial (const char* theMaterialName);
|
||||||
|
|
||||||
|
//! Handle "mtllib FileName".
|
||||||
|
void readMaterialLib (const char* theFileName);
|
||||||
|
|
||||||
|
//! Check memory limits.
|
||||||
|
//! @return FALSE on out of memory
|
||||||
|
bool checkMemory();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Hasher for 3 ordered integers.
|
||||||
|
struct ObjVec3iHasher
|
||||||
|
{
|
||||||
|
static Standard_Integer HashCode (const Graphic3d_Vec3i& theKey,
|
||||||
|
const Standard_Integer theUpper)
|
||||||
|
{
|
||||||
|
return ::HashCode (::HashCodes ((Standard_CString )&theKey, sizeof(Graphic3d_Vec3i)), theUpper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Standard_Boolean IsEqual (const Graphic3d_Vec3i& theKey1,
|
||||||
|
const Graphic3d_Vec3i& theKey2)
|
||||||
|
{
|
||||||
|
return theKey1[0] == theKey2[0]
|
||||||
|
&& theKey1[1] == theKey2[1]
|
||||||
|
&& theKey1[2] == theKey2[2];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Auxiliary structure holding vertex data either with single or double floating point precision.
|
||||||
|
class VectorOfVertices
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Empty constructor.
|
||||||
|
VectorOfVertices() : myIsSinglePrecision (Standard_False) {}
|
||||||
|
|
||||||
|
//! Return single precision flag; FALSE by default.
|
||||||
|
bool IsSinglePrecision() const { return myIsSinglePrecision; }
|
||||||
|
|
||||||
|
//! Setup single/double precision flag.
|
||||||
|
void SetSinglePrecision (Standard_Boolean theIsSinglePrecision)
|
||||||
|
{
|
||||||
|
myIsSinglePrecision = theIsSinglePrecision;
|
||||||
|
myPntVec.Nullify();
|
||||||
|
myVec3Vec.Nullify();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Reset and (re)allocate buffer.
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
if (myIsSinglePrecision)
|
||||||
|
{
|
||||||
|
myVec3Vec = new NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
myPntVec = new NCollection_Shared<NCollection_Vector<gp_Pnt> >();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Return vector lower index.
|
||||||
|
Standard_Integer Lower() const { return 0; }
|
||||||
|
|
||||||
|
//! Return vector upper index.
|
||||||
|
Standard_Integer Upper() const { return myIsSinglePrecision ? myVec3Vec->Upper() : myPntVec->Upper(); }
|
||||||
|
|
||||||
|
//! Return point with the given index.
|
||||||
|
gp_Pnt Value (Standard_Integer theIndex) const
|
||||||
|
{
|
||||||
|
if (myIsSinglePrecision)
|
||||||
|
{
|
||||||
|
const Graphic3d_Vec3& aPnt = myVec3Vec->Value (theIndex);
|
||||||
|
return gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return myPntVec->Value (theIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Append new point.
|
||||||
|
void Append (const gp_Pnt& thePnt)
|
||||||
|
{
|
||||||
|
if (myIsSinglePrecision)
|
||||||
|
{
|
||||||
|
myVec3Vec->Append (Graphic3d_Vec3 ((float )thePnt.X(), (float )thePnt.Y(), (float )thePnt.Z()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
myPntVec->Append (thePnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Handle(NCollection_Shared<NCollection_Vector<gp_Pnt> >) myPntVec;
|
||||||
|
Handle(NCollection_Shared<NCollection_Vector<Graphic3d_Vec3> >) myVec3Vec;
|
||||||
|
Standard_Boolean myIsSinglePrecision;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
NCollection_IndexedMap<TCollection_AsciiString>
|
||||||
|
myExternalFiles; //!< list of external file references
|
||||||
|
TCollection_AsciiString myFileComments; //!< file header comments
|
||||||
|
TCollection_AsciiString myFolder; //!< folder containing the OBJ file
|
||||||
|
RWMesh_CoordinateSystemConverter myCSTrsf; //!< coordinate system flipper
|
||||||
|
Standard_Size myMemLimitBytes; //!< memory limit in bytes
|
||||||
|
Standard_Size myMemEstim; //!< estimated memory occupation in bytes
|
||||||
|
Standard_Integer myNbLines; //!< number of parsed lines (e.g. current line)
|
||||||
|
Standard_Integer myNbProbeNodes; //!< number of probed nodes
|
||||||
|
Standard_Integer myNbProbeElems; //!< number of probed elements
|
||||||
|
Standard_Integer myNbElemsBig; //!< number of big elements (polygons with 5+ nodes)
|
||||||
|
Standard_Boolean myToAbort; //!< flag indicating abort state (e.g. syntax error)
|
||||||
|
|
||||||
|
// Each node in the Element specifies independent indices of Vertex position, Texture coordinates and Normal.
|
||||||
|
// This scheme does not match natural definition of Primitive Array
|
||||||
|
// where each unique set of nodal properties defines Vertex
|
||||||
|
// (thus node at the same location but with different normal should be duplicated).
|
||||||
|
// The following code converts OBJ definition of nodal properties to Primitive Array definition.
|
||||||
|
VectorOfVertices myObjVerts; //!< temporary vector of vertices
|
||||||
|
NCollection_Vector<Graphic3d_Vec2> myObjVertsUV; //!< temporary vector of UV parameters
|
||||||
|
NCollection_Vector<Graphic3d_Vec3> myObjNorms; //!< temporary vector of normals
|
||||||
|
NCollection_DataMap<Graphic3d_Vec3i, Standard_Integer, ObjVec3iHasher>
|
||||||
|
myPackedIndices;
|
||||||
|
NCollection_DataMap<TCollection_AsciiString, RWObj_Material>
|
||||||
|
myMaterials; //!< map of known materials
|
||||||
|
|
||||||
|
RWObj_SubMesh myActiveSubMesh; //!< active sub-mesh definition
|
||||||
|
std::vector<Standard_Integer> myCurrElem; //!< indices for the current element
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_Reader_HeaderFile
|
30
src/RWObj/RWObj_SubMesh.hxx
Normal file
30
src/RWObj/RWObj_SubMesh.hxx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2015-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_SubMesh_HeaderFile
|
||||||
|
#define _RWObj_SubMesh_HeaderFile
|
||||||
|
|
||||||
|
#include <Quantity_Color.hxx>
|
||||||
|
#include <TCollection_AsciiString.hxx>
|
||||||
|
|
||||||
|
//! Sub-mesh definition for OBJ reader.
|
||||||
|
struct RWObj_SubMesh
|
||||||
|
{
|
||||||
|
TCollection_AsciiString Object; //!< name of active object
|
||||||
|
TCollection_AsciiString Group; //!< name of active group
|
||||||
|
TCollection_AsciiString SmoothGroup; //!< name of active smoothing group
|
||||||
|
TCollection_AsciiString Material; //!< name of active material
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_SubMesh_HeaderFile
|
27
src/RWObj/RWObj_SubMeshReason.hxx
Normal file
27
src/RWObj/RWObj_SubMeshReason.hxx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2017-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_SubMeshReason_HeaderFile
|
||||||
|
#define _RWObj_SubMeshReason_HeaderFile
|
||||||
|
|
||||||
|
//! Reason for creating a new group within OBJ reader.
|
||||||
|
enum RWObj_SubMeshReason
|
||||||
|
{
|
||||||
|
RWObj_SubMeshReason_NewObject, //!< new object, should occur only ones in valid OBJ file (at the very beginning)
|
||||||
|
RWObj_SubMeshReason_NewGroup, //!< new group (g item)
|
||||||
|
RWObj_SubMeshReason_NewMaterial, //!< new material (usemtl item)
|
||||||
|
RWObj_SubMeshReason_NewSmoothGroup //!< new smoothing group (s item)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_SubMeshReason_HeaderFile
|
81
src/RWObj/RWObj_Tools.hxx
Normal file
81
src/RWObj/RWObj_Tools.hxx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2017-2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_Tools_HeaderFile
|
||||||
|
#define _RWObj_Tools_HeaderFile
|
||||||
|
|
||||||
|
#include <gp_XYZ.hxx>
|
||||||
|
#include <Graphic3d_Vec3.hxx>
|
||||||
|
#include <TCollection_AsciiString.hxx>
|
||||||
|
|
||||||
|
//! Auxiliary tools for OBJ format parser.
|
||||||
|
namespace RWObj_Tools
|
||||||
|
{
|
||||||
|
//! Read 3 float values.
|
||||||
|
inline bool ReadVec3 (const char* thePos,
|
||||||
|
char*& theNext,
|
||||||
|
Graphic3d_Vec3& theVec)
|
||||||
|
{
|
||||||
|
const char* aPos = thePos;
|
||||||
|
theVec.x() = (float )Strtod (aPos, &theNext);
|
||||||
|
aPos = theNext;
|
||||||
|
theVec.y() = (float )Strtod (aPos, &theNext);
|
||||||
|
aPos = theNext;
|
||||||
|
theVec.z() = (float )Strtod (aPos, &theNext);
|
||||||
|
return aPos != theNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Read 3 double values.
|
||||||
|
inline bool ReadVec3 (const char* thePos,
|
||||||
|
char*& theNext,
|
||||||
|
gp_XYZ& theVec)
|
||||||
|
{
|
||||||
|
const char* aPos = thePos;
|
||||||
|
theVec.SetX (Strtod (aPos, &theNext));
|
||||||
|
aPos = theNext;
|
||||||
|
theVec.SetY (Strtod (aPos, &theNext));
|
||||||
|
aPos = theNext;
|
||||||
|
theVec.SetZ (Strtod (aPos, &theNext));
|
||||||
|
return aPos != theNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Read string.
|
||||||
|
inline bool ReadName (const char* thePos,
|
||||||
|
TCollection_AsciiString& theName)
|
||||||
|
{
|
||||||
|
Standard_Integer aFrom = 0;
|
||||||
|
Standard_Integer aTail = (Standard_Integer )std::strlen (thePos) - 1;
|
||||||
|
if (aTail >= 0 && thePos[aTail] == '\n') { --aTail; }
|
||||||
|
if (aTail >= 0 && thePos[aTail] == '\r') { --aTail; }
|
||||||
|
for (; aTail >= 0 && IsSpace (thePos[aTail]); --aTail) {} // RightAdjust
|
||||||
|
for (; aFrom < aTail && IsSpace (thePos[aFrom]); ++aFrom) {} // LeftAdjust
|
||||||
|
if (aFrom > aTail)
|
||||||
|
{
|
||||||
|
theName.Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
theName = TCollection_AsciiString (thePos + aFrom, aTail - aFrom + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Return true if specified char is a white space.
|
||||||
|
inline bool isSpaceChar (const char theChar)
|
||||||
|
{
|
||||||
|
return theChar == ' '
|
||||||
|
|| theChar == '\t';
|
||||||
|
//return IsSpace (theChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _RWObj_Tools_HeaderFile
|
223
src/RWObj/RWObj_TriangulationReader.cxx
Normal file
223
src/RWObj/RWObj_TriangulationReader.cxx
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#include <RWObj_TriangulationReader.hxx>
|
||||||
|
|
||||||
|
#include <BRep_Builder.hxx>
|
||||||
|
#include <TopoDS.hxx>
|
||||||
|
#include <TopoDS_Iterator.hxx>
|
||||||
|
|
||||||
|
IMPLEMENT_STANDARD_RTTIEXT(RWObj_TriangulationReader, RWObj_Reader)
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : addMesh
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
Standard_Boolean RWObj_TriangulationReader::addMesh (const RWObj_SubMesh& theMesh,
|
||||||
|
const RWObj_SubMeshReason theReason)
|
||||||
|
{
|
||||||
|
if (!myToCreateShapes)
|
||||||
|
{
|
||||||
|
return Standard_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RWObj_Material* aMaterial = myMaterials.Seek (theMesh.Material);
|
||||||
|
if (Handle(Poly_Triangulation) aTris = GetTriangulation())
|
||||||
|
{
|
||||||
|
myNodes.Clear();
|
||||||
|
myNodesUV.Clear();
|
||||||
|
myNormals.Clear();
|
||||||
|
myTriangles.Clear();
|
||||||
|
if (theMesh.Group != myLastGroupName)
|
||||||
|
{
|
||||||
|
// flush previous group and start a new one
|
||||||
|
if (addSubShape (myLastObjectShape, myLastGroupShape, Standard_False))
|
||||||
|
{
|
||||||
|
if (myShapeReceiver != NULL)
|
||||||
|
{
|
||||||
|
myShapeReceiver->BindNamedShape (myLastGroupShape, theMesh.Group, myLastGroupShape.ShapeType() == TopAbs_FACE ? aMaterial : NULL, Standard_False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myLastGroupShape = TopoDS_Shape();
|
||||||
|
myLastGroupName = theMesh.Group;
|
||||||
|
}
|
||||||
|
|
||||||
|
TopoDS_Face aNewFace;
|
||||||
|
BRep_Builder aBuilder;
|
||||||
|
aBuilder.MakeFace (aNewFace, aTris);
|
||||||
|
addSubShape (myLastGroupShape, aNewFace, Standard_True);
|
||||||
|
if (myShapeReceiver != NULL)
|
||||||
|
{
|
||||||
|
myShapeReceiver->BindNamedShape (aNewFace, "", aMaterial, Standard_False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theReason == RWObj_SubMeshReason_NewObject)
|
||||||
|
{
|
||||||
|
// forced flush at the end of the object
|
||||||
|
if (addSubShape (myLastObjectShape, myLastGroupShape, Standard_False))
|
||||||
|
{
|
||||||
|
if (myShapeReceiver != NULL)
|
||||||
|
{
|
||||||
|
myShapeReceiver->BindNamedShape (myLastGroupShape, theMesh.Group, myLastGroupShape.ShapeType() == TopAbs_FACE ? aMaterial : NULL, Standard_False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myLastGroupShape = TopoDS_Shape();
|
||||||
|
myLastGroupName.Clear();
|
||||||
|
|
||||||
|
if (addSubShape (myResultShape, myLastObjectShape, Standard_False))
|
||||||
|
{
|
||||||
|
if (myShapeReceiver != NULL)
|
||||||
|
{
|
||||||
|
myShapeReceiver->BindNamedShape (myLastObjectShape, theMesh.Object, NULL, Standard_True);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myLastObjectShape = TopoDS_Compound();
|
||||||
|
}
|
||||||
|
return Standard_True;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : addSubShape
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_Boolean RWObj_TriangulationReader::addSubShape (TopoDS_Shape& theParent,
|
||||||
|
const TopoDS_Shape& theSubShape,
|
||||||
|
const Standard_Boolean theToExpandCompound)
|
||||||
|
{
|
||||||
|
if (theSubShape.IsNull())
|
||||||
|
{
|
||||||
|
return Standard_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
BRep_Builder aBuilder;
|
||||||
|
if (theParent.IsNull()
|
||||||
|
&& theToExpandCompound)
|
||||||
|
{
|
||||||
|
theParent = theSubShape;
|
||||||
|
return Standard_True;
|
||||||
|
}
|
||||||
|
|
||||||
|
TopoDS_Compound aComp;
|
||||||
|
if (!theParent.IsNull()
|
||||||
|
&& theParent.ShapeType() == TopAbs_COMPOUND)
|
||||||
|
{
|
||||||
|
aComp = TopoDS::Compound (theParent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aBuilder.MakeCompound (aComp);
|
||||||
|
if (!theParent.IsNull())
|
||||||
|
{
|
||||||
|
aBuilder.Add (aComp, theParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aBuilder.Add (aComp, theSubShape);
|
||||||
|
theParent = aComp;
|
||||||
|
return Standard_True;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//function : GetTriangulation
|
||||||
|
//purpose :
|
||||||
|
//=============================================================================
|
||||||
|
Handle(Poly_Triangulation) RWObj_TriangulationReader::GetTriangulation()
|
||||||
|
{
|
||||||
|
if (myTriangles.IsEmpty())
|
||||||
|
{
|
||||||
|
return Handle(Poly_Triangulation)();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Standard_Boolean hasNormals = myNodes.Length() == myNormals.Length();
|
||||||
|
const Standard_Boolean hasUV = myNodes.Length() == myNodesUV.Length();
|
||||||
|
|
||||||
|
Handle(Poly_Triangulation) aPoly = new Poly_Triangulation (myNodes.Length(), myTriangles.Length(), hasUV);
|
||||||
|
for (Standard_Integer aNodeIter = 0; aNodeIter < myNodes.Size(); ++aNodeIter)
|
||||||
|
{
|
||||||
|
const gp_Pnt& aNode = myNodes.Value (aNodeIter);
|
||||||
|
aPoly->ChangeNode (aNodeIter + 1) = aNode;
|
||||||
|
}
|
||||||
|
if (hasUV)
|
||||||
|
{
|
||||||
|
for (Standard_Integer aNodeIter = 0; aNodeIter < myNodes.Size(); ++aNodeIter)
|
||||||
|
{
|
||||||
|
const Graphic3d_Vec2& aNode = myNodesUV.Value (aNodeIter);
|
||||||
|
aPoly->ChangeUVNode (aNodeIter + 1).SetCoord (aNode.x(), aNode.y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasNormals)
|
||||||
|
{
|
||||||
|
const Handle(TShort_HArray1OfShortReal) aNormals = new TShort_HArray1OfShortReal (1, myNodes.Length() * 3);
|
||||||
|
Standard_ShortReal* aNormArr = &aNormals->ChangeFirst();
|
||||||
|
Standard_Integer aNbInvalid = 0;
|
||||||
|
for (Standard_Integer aNodeIter = 0; aNodeIter < myNodes.Size(); ++aNodeIter)
|
||||||
|
{
|
||||||
|
const Graphic3d_Vec3& aNorm = myNormals.Value (aNodeIter);
|
||||||
|
const float aMod2 = aNorm.SquareModulus();
|
||||||
|
if (aMod2 > 0.001f)
|
||||||
|
{
|
||||||
|
aNormArr[aNodeIter * 3 + 0] = aNorm.x();
|
||||||
|
aNormArr[aNodeIter * 3 + 1] = aNorm.y();
|
||||||
|
aNormArr[aNodeIter * 3 + 2] = aNorm.z();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++aNbInvalid;
|
||||||
|
aNormArr[aNodeIter * 3 + 0] = 0.0f;
|
||||||
|
aNormArr[aNodeIter * 3 + 1] = 0.0f;
|
||||||
|
aNormArr[aNodeIter * 3 + 2] = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aNbInvalid != myNodes.Length())
|
||||||
|
{
|
||||||
|
aPoly->SetNormals (aNormals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Standard_Integer aTriIter = 0; aTriIter < myTriangles.Size(); ++aTriIter)
|
||||||
|
{
|
||||||
|
aPoly->ChangeTriangle (aTriIter + 1) = myTriangles (aTriIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return aPoly;
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Function : ResultShape
|
||||||
|
// Purpose :
|
||||||
|
//================================================================
|
||||||
|
TopoDS_Shape RWObj_TriangulationReader::ResultShape()
|
||||||
|
{
|
||||||
|
if (!myToCreateShapes)
|
||||||
|
{
|
||||||
|
if (Handle(Poly_Triangulation) aTris = GetTriangulation())
|
||||||
|
{
|
||||||
|
TopoDS_Face aFace;
|
||||||
|
BRep_Builder aBuilder;
|
||||||
|
aBuilder.MakeFace (aFace, aTris);
|
||||||
|
return aFace;
|
||||||
|
}
|
||||||
|
return TopoDS_Shape();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myResultShape.IsNull()
|
||||||
|
&& myResultShape.ShapeType() == TopAbs_COMPOUND
|
||||||
|
&& myResultShape.NbChildren() == 1
|
||||||
|
&& myActiveSubMesh.Object.IsEmpty())
|
||||||
|
{
|
||||||
|
TopoDS_Iterator aChildIter (myResultShape);
|
||||||
|
return aChildIter.Value();
|
||||||
|
}
|
||||||
|
return myResultShape;
|
||||||
|
}
|
124
src/RWObj/RWObj_TriangulationReader.hxx
Normal file
124
src/RWObj/RWObj_TriangulationReader.hxx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Author: Kirill Gavrilov
|
||||||
|
// Copyright (c) 2019 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _RWObj_TriangulationReader_HeaderFile
|
||||||
|
#define _RWObj_TriangulationReader_HeaderFile
|
||||||
|
|
||||||
|
#include <RWObj_Reader.hxx>
|
||||||
|
|
||||||
|
#include <Poly_Triangulation.hxx>
|
||||||
|
#include <TopoDS_Compound.hxx>
|
||||||
|
|
||||||
|
//! Interface to store shape attributes into document.
|
||||||
|
class RWObj_IShapeReceiver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! @param theShape shape to register
|
||||||
|
//! @param theName shape name
|
||||||
|
//! @param theMaterial shape material
|
||||||
|
//! @param theIsRootShape indicates that this is a root object (free shape)
|
||||||
|
virtual void BindNamedShape (const TopoDS_Shape& theShape,
|
||||||
|
const TCollection_AsciiString& theName,
|
||||||
|
const RWObj_Material* theMaterial,
|
||||||
|
const Standard_Boolean theIsRootShape) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! RWObj_Reader implementation dumping OBJ file into Poly_Triangulation.
|
||||||
|
class RWObj_TriangulationReader : public RWObj_Reader
|
||||||
|
{
|
||||||
|
DEFINE_STANDARD_RTTIEXT(RWObj_TriangulationReader, RWObj_Reader)
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Constructor.
|
||||||
|
RWObj_TriangulationReader() : myShapeReceiver (NULL), myToCreateShapes (Standard_True) {}
|
||||||
|
|
||||||
|
//! Set flag to create shapes.
|
||||||
|
void SetCreateShapes (Standard_Boolean theToCreateShapes) { myToCreateShapes = theToCreateShapes; }
|
||||||
|
|
||||||
|
//! Set shape receiver callback.
|
||||||
|
void SetShapeReceiver (RWObj_IShapeReceiver* theReceiver) { myShapeReceiver = theReceiver; }
|
||||||
|
|
||||||
|
//! Create Poly_Triangulation from collected data
|
||||||
|
Standard_EXPORT virtual Handle(Poly_Triangulation) GetTriangulation();
|
||||||
|
|
||||||
|
//! Return result shape.
|
||||||
|
Standard_EXPORT TopoDS_Shape ResultShape();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Flush active sub-mesh.
|
||||||
|
Standard_EXPORT virtual Standard_Boolean addMesh (const RWObj_SubMesh& theMesh,
|
||||||
|
const RWObj_SubMeshReason theReason) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
//! Retrieve sub-mesh node position.
|
||||||
|
virtual gp_Pnt getNode (Standard_Integer theIndex) const Standard_OVERRIDE
|
||||||
|
{
|
||||||
|
return myNodes.Value (theIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Add new node.
|
||||||
|
virtual Standard_Integer addNode (const gp_Pnt& thePnt) Standard_OVERRIDE
|
||||||
|
{
|
||||||
|
myNodes.Append (thePnt);
|
||||||
|
return myNodes.Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Ignore normal.
|
||||||
|
virtual void setNodeNormal (const Standard_Integer theIndex,
|
||||||
|
const Graphic3d_Vec3& theNormal) Standard_OVERRIDE
|
||||||
|
{
|
||||||
|
myNormals.SetValue (theIndex - 1, theNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Ignore texture coordinates.
|
||||||
|
virtual void setNodeUV (const Standard_Integer theIndex,
|
||||||
|
const Graphic3d_Vec2& theUV) Standard_OVERRIDE
|
||||||
|
{
|
||||||
|
myNodesUV.SetValue (theIndex - 1, theUV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Add element.
|
||||||
|
virtual void addElement (Standard_Integer theN1, Standard_Integer theN2, Standard_Integer theN3, Standard_Integer theN4) Standard_OVERRIDE
|
||||||
|
{
|
||||||
|
myTriangles.Append (Poly_Triangle (theN1, theN2, theN3));
|
||||||
|
if (theN4 != -1)
|
||||||
|
{
|
||||||
|
myTriangles.Append (Poly_Triangle (theN1, theN3, theN4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Add sub-shape into specified shape
|
||||||
|
Standard_EXPORT Standard_Boolean addSubShape (TopoDS_Shape& theParent,
|
||||||
|
const TopoDS_Shape& theSubShape,
|
||||||
|
const Standard_Boolean theToExpandCompound);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
NCollection_Vector<gp_Pnt> myNodes; //!< nodes of currently filled triangulation
|
||||||
|
NCollection_Vector<Graphic3d_Vec3> myNormals; //!< normals of currently filled triangulation
|
||||||
|
NCollection_Vector<Graphic3d_Vec2> myNodesUV; //!< UVs of currently filled triangulation
|
||||||
|
NCollection_Vector<Poly_Triangle> myTriangles; //!< indexes of currently filled triangulation
|
||||||
|
|
||||||
|
RWObj_IShapeReceiver* myShapeReceiver; //!< optional shape receiver
|
||||||
|
TopoDS_Compound myResultShape; //!< result shape as Compound of objects
|
||||||
|
TopoDS_Compound myLastObjectShape; //!< Compound containing current object groups
|
||||||
|
TopoDS_Shape myLastGroupShape; //!< current group shape - either a single Face or Compound of Faces
|
||||||
|
TCollection_AsciiString myLastGroupName; //!< current group name
|
||||||
|
Standard_Boolean myToCreateShapes; //!< create a single triangulation
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RWObj_TriangulationReader_HeaderFile
|
@@ -1,2 +1,3 @@
|
|||||||
RWGltf
|
RWGltf
|
||||||
RWMesh
|
RWMesh
|
||||||
|
RWObj
|
||||||
|
@@ -44,6 +44,8 @@
|
|||||||
#include <Quantity_NameOfColor.hxx>
|
#include <Quantity_NameOfColor.hxx>
|
||||||
#include <RWGltf_CafReader.hxx>
|
#include <RWGltf_CafReader.hxx>
|
||||||
#include <RWStl.hxx>
|
#include <RWStl.hxx>
|
||||||
|
#include <RWObj.hxx>
|
||||||
|
#include <RWObj_CafReader.hxx>
|
||||||
#include <SelectMgr_SelectionManager.hxx>
|
#include <SelectMgr_SelectionManager.hxx>
|
||||||
#include <Standard_ErrorHandler.hxx>
|
#include <Standard_ErrorHandler.hxx>
|
||||||
#include <StdSelect_ViewerSelector3d.hxx>
|
#include <StdSelect_ViewerSelector3d.hxx>
|
||||||
@@ -290,6 +292,210 @@ static Standard_Integer readstl(Draw_Interpretor& theDI,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Parse RWMesh_CoordinateSystem enumeration.
|
||||||
|
static Standard_Boolean parseCoordinateSystem (const char* theArg,
|
||||||
|
RWMesh_CoordinateSystem& theSystem)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aCSStr (theArg);
|
||||||
|
aCSStr.LowerCase();
|
||||||
|
if (aCSStr == "zup")
|
||||||
|
{
|
||||||
|
theSystem = RWMesh_CoordinateSystem_Zup;
|
||||||
|
}
|
||||||
|
else if (aCSStr == "yup")
|
||||||
|
{
|
||||||
|
theSystem = RWMesh_CoordinateSystem_Yup;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Standard_False;
|
||||||
|
}
|
||||||
|
return Standard_True;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//function : ReadObj
|
||||||
|
//purpose : Reads OBJ file
|
||||||
|
//=============================================================================
|
||||||
|
static Standard_Integer ReadObj (Draw_Interpretor& theDI,
|
||||||
|
Standard_Integer theNbArgs,
|
||||||
|
const char** theArgVec)
|
||||||
|
{
|
||||||
|
TCollection_AsciiString aDestName, aFilePath;
|
||||||
|
Standard_Boolean toUseExistingDoc = Standard_False;
|
||||||
|
Standard_Real aFileUnitFactor = -1.0;
|
||||||
|
RWMesh_CoordinateSystem aResultCoordSys = RWMesh_CoordinateSystem_Zup, aFileCoordSys = RWMesh_CoordinateSystem_Yup;
|
||||||
|
Standard_Boolean toListExternalFiles = Standard_False, isSingleFace = Standard_False, isSinglePrecision = Standard_False;
|
||||||
|
Standard_Boolean isNoDoc = (TCollection_AsciiString(theArgVec[0]) == "readobj");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
std::cout << "Syntax error: wrong length unit '" << aUnitStr << "'\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (anArgIter + 1 < theNbArgs
|
||||||
|
&& (anArgCase == "-filecoordinatesystem"
|
||||||
|
|| anArgCase == "-filecoordsystem"
|
||||||
|
|| anArgCase == "-filecoordsys"))
|
||||||
|
{
|
||||||
|
if (!parseCoordinateSystem (theArgVec[++anArgIter], aFileCoordSys))
|
||||||
|
{
|
||||||
|
std::cout << "Syntax error: unknown coordinate system '" << theArgVec[anArgIter] << "'\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (anArgIter + 1 < theNbArgs
|
||||||
|
&& (anArgCase == "-resultcoordinatesystem"
|
||||||
|
|| anArgCase == "-resultcoordsystem"
|
||||||
|
|| anArgCase == "-resultcoordsys"
|
||||||
|
|| anArgCase == "-rescoordsys"))
|
||||||
|
{
|
||||||
|
if (!parseCoordinateSystem (theArgVec[++anArgIter], aResultCoordSys))
|
||||||
|
{
|
||||||
|
std::cout << "Syntax error: unknown coordinate system '" << theArgVec[anArgIter] << "'\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (anArgCase == "-singleprecision"
|
||||||
|
|| anArgCase == "-singleprec")
|
||||||
|
{
|
||||||
|
isSinglePrecision = Standard_True;
|
||||||
|
if (anArgIter + 1 < theNbArgs
|
||||||
|
&& ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], isSinglePrecision))
|
||||||
|
{
|
||||||
|
++anArgIter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isNoDoc
|
||||||
|
&& (anArgCase == "-singleface"
|
||||||
|
|| anArgCase == "-singletriangulation"))
|
||||||
|
{
|
||||||
|
isSingleFace = Standard_True;
|
||||||
|
}
|
||||||
|
else if (!isNoDoc
|
||||||
|
&& (anArgCase == "-nocreate"
|
||||||
|
|| anArgCase == "-nocreatedoc"))
|
||||||
|
{
|
||||||
|
toUseExistingDoc = Standard_True;
|
||||||
|
if (anArgIter + 1 < theNbArgs
|
||||||
|
&& ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], toUseExistingDoc))
|
||||||
|
{
|
||||||
|
++anArgIter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (anArgCase == "-listexternalfiles"
|
||||||
|
|| anArgCase == "-listexternals"
|
||||||
|
|| anArgCase == "-listexternal"
|
||||||
|
|| anArgCase == "-external"
|
||||||
|
|| anArgCase == "-externalfiles")
|
||||||
|
{
|
||||||
|
toListExternalFiles = Standard_True;
|
||||||
|
}
|
||||||
|
else if (aDestName.IsEmpty())
|
||||||
|
{
|
||||||
|
aDestName = theArgVec[anArgIter];
|
||||||
|
}
|
||||||
|
else if (aFilePath.IsEmpty())
|
||||||
|
{
|
||||||
|
aFilePath = theArgVec[anArgIter];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aFilePath.IsEmpty())
|
||||||
|
{
|
||||||
|
std::cout << "Syntax error: wrong number of arguments\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle(Draw_ProgressIndicator) aProgress = new Draw_ProgressIndicator (theDI, 1);
|
||||||
|
Handle(TDocStd_Document) aDoc;
|
||||||
|
if (!isNoDoc
|
||||||
|
&& !toListExternalFiles)
|
||||||
|
{
|
||||||
|
Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
|
||||||
|
Standard_CString aNameVar = aDestName.ToCString();
|
||||||
|
DDocStd::GetDocument (aNameVar, aDoc, Standard_False);
|
||||||
|
if (aDoc.IsNull())
|
||||||
|
{
|
||||||
|
if (toUseExistingDoc)
|
||||||
|
{
|
||||||
|
std::cout << "Error: document with name " << aDestName << " does not exist\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
anApp->NewDocument (TCollection_ExtendedString ("BinXCAF"), aDoc);
|
||||||
|
}
|
||||||
|
else if (!toUseExistingDoc)
|
||||||
|
{
|
||||||
|
std::cout << "Error: document with name " << aDestName << " already exists\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RWObj_CafReader aReader;
|
||||||
|
aReader.SetSinglePrecision (isSinglePrecision);
|
||||||
|
aReader.SetSystemLengthUnit (UnitsMethods::GetCasCadeLengthUnit() * 0.001);
|
||||||
|
aReader.SetSystemCoordinateSystem (aResultCoordSys);
|
||||||
|
aReader.SetFileLengthUnit (aFileUnitFactor);
|
||||||
|
aReader.SetFileCoordinateSystem (aFileCoordSys);
|
||||||
|
aReader.SetDocument (aDoc);
|
||||||
|
if (isSingleFace)
|
||||||
|
{
|
||||||
|
RWObj_TriangulationReader aSimpleReader;
|
||||||
|
aSimpleReader.SetSinglePrecision (isSinglePrecision);
|
||||||
|
aSimpleReader.SetCreateShapes (Standard_False);
|
||||||
|
aSimpleReader.SetTransformation (aReader.CoordinateSystemConverter());
|
||||||
|
aSimpleReader.Read (aFilePath.ToCString(), aProgress);
|
||||||
|
|
||||||
|
Handle(Poly_Triangulation) aTriangulation = aSimpleReader.GetTriangulation();
|
||||||
|
TopoDS_Face aFace;
|
||||||
|
BRep_Builder aBuiler;
|
||||||
|
aBuiler.MakeFace (aFace);
|
||||||
|
aBuiler.UpdateFace (aFace, aTriangulation);
|
||||||
|
DBRep::Set (aDestName.ToCString(), aFace);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toListExternalFiles)
|
||||||
|
{
|
||||||
|
aReader.ProbeHeader (aFilePath);
|
||||||
|
for (NCollection_IndexedMap<TCollection_AsciiString>::Iterator aFileIter (aReader.ExternalFiles()); aFileIter.More(); aFileIter.Next())
|
||||||
|
{
|
||||||
|
theDI << "\"" << aFileIter.Value() << "\" ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aReader.Perform (aFilePath, aProgress);
|
||||||
|
if (isNoDoc)
|
||||||
|
{
|
||||||
|
DBRep::Set (aDestName.ToCString(), aReader.SingleShape());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Handle(DDocStd_DrawDocument) aDrawDoc = new DDocStd_DrawDocument (aDoc);
|
||||||
|
TDataStd_Name::Set (aDoc->GetData()->Root(), aDestName.ToCString());
|
||||||
|
Draw::Set (aDestName.ToCString(), aDrawDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static Standard_Integer writevrml
|
static Standard_Integer writevrml
|
||||||
(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
|
(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
|
||||||
{
|
{
|
||||||
@@ -1404,6 +1610,25 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands)
|
|||||||
"\n\t\t: Single triangulation-only Face is created otherwise (default).",
|
"\n\t\t: Single triangulation-only Face is created otherwise (default).",
|
||||||
__FILE__, readstl, g);
|
__FILE__, readstl, g);
|
||||||
theCommands.Add ("loadvrml" , "shape file",__FILE__,loadvrml,g);
|
theCommands.Add ("loadvrml" , "shape file",__FILE__,loadvrml,g);
|
||||||
|
theCommands.Add ("ReadObj",
|
||||||
|
"ReadObj Doc file [-fileCoordSys {Zup|Yup}] [-fileUnit Unit]"
|
||||||
|
"\n\t\t: [-resultCoordSys {Zup|Yup}] [-singlePrecision]"
|
||||||
|
"\n\t\t: [-listExternalFiles] [-noCreateDoc]"
|
||||||
|
"\n\t\t: Read OBJ file into XDE document."
|
||||||
|
"\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: -resultCoordSys result coordinate system; Zup when not specified."
|
||||||
|
"\n\t\t: -singlePrecision truncate vertex data to single precision during read; FALSE by default."
|
||||||
|
"\n\t\t: -listExternalFiles do not read mesh and only list external files."
|
||||||
|
"\n\t\t: -noCreateDoc read into existing XDE document.",
|
||||||
|
__FILE__, ReadObj, g);
|
||||||
|
theCommands.Add ("readobj",
|
||||||
|
"readobj shape file [-fileCoordSys {Zup|Yup}] [-fileUnit Unit]"
|
||||||
|
"\n\t\t: [-resultCoordSys {Zup|Yup}] [-singlePrecision]"
|
||||||
|
"\n\t\t: [-singleFace]"
|
||||||
|
"\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 ("meshfromstl", "creates MeshVS_Mesh from STL file", __FILE__, createmesh, 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 );
|
theCommands.Add ("mesh3delem", "creates 3d element mesh to test", __FILE__, create3d, g );
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
001 stl_read
|
001 stl_read
|
||||||
002 shape_write_stl
|
002 shape_write_stl
|
||||||
003 gltf_read
|
003 gltf_read
|
||||||
|
004 obj_read
|
||||||
|
2
tests/de_mesh/obj_read/begin
Normal file
2
tests/de_mesh/obj_read/begin
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pload XDE OCAF MODELING VISUALIZATION
|
||||||
|
catch { Close D }
|
6
tests/de_mesh/obj_read/end
Normal file
6
tests/de_mesh/obj_read/end
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
vclear
|
||||||
|
vinit View1
|
||||||
|
XDisplay -dispMode 1 D -explore
|
||||||
|
vaxo
|
||||||
|
vfit
|
||||||
|
vdump ${imagedir}/${casename}.png
|
9
tests/de_mesh/obj_read/mustang
Normal file
9
tests/de_mesh/obj_read/mustang
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
puts "========"
|
||||||
|
puts "0029296: Data Exchange - implement import of mesh data from files in OBJ format"
|
||||||
|
puts "Small textured airplane model"
|
||||||
|
puts "========"
|
||||||
|
|
||||||
|
ReadObj D [locate_data_file "P-51 Mustang.obj"]
|
||||||
|
XGetOneShape s D
|
||||||
|
checknbshapes s -face 14 -compound 1
|
||||||
|
checktrinfo s -tri 4309 -nod 4727
|
73
tests/de_mesh/obj_read/prism
Normal file
73
tests/de_mesh/obj_read/prism
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
puts "========"
|
||||||
|
puts "0029296: Data Exchange - implement import of mesh data from files in OBJ format"
|
||||||
|
puts "Reading small generated file."
|
||||||
|
puts "========"
|
||||||
|
|
||||||
|
set material_mtl {newmtl ObjMat1
|
||||||
|
Ns 96.07
|
||||||
|
Ka 0.00 0.00 0.00
|
||||||
|
Kd 0.64 0.64 0.64
|
||||||
|
Ks 0.50 0.50 0.50
|
||||||
|
Ni 1.00
|
||||||
|
d 1.00}
|
||||||
|
|
||||||
|
set minimal_ascii_obj {mtllib A1_material.mtl
|
||||||
|
g Group1
|
||||||
|
usemtl ObjMat1
|
||||||
|
v 0 0 0
|
||||||
|
v 2 0 0
|
||||||
|
v 2 1 0
|
||||||
|
v 1 2 0
|
||||||
|
v 0 1 0
|
||||||
|
v 0 0 2
|
||||||
|
v 2 0 2
|
||||||
|
v 2 1 2
|
||||||
|
v 1 2 2
|
||||||
|
v 0 1 2
|
||||||
|
f 5 4 3 2 1
|
||||||
|
f 7 8 9 10 6
|
||||||
|
f 10 9 4 5
|
||||||
|
f 9 8 3 4
|
||||||
|
f 6 10 5 1
|
||||||
|
f 2 3 8 7
|
||||||
|
f 1 2 7 6}
|
||||||
|
|
||||||
|
# Ascii MTL file, CRLF
|
||||||
|
set fd [open ${imagedir}/${casename}_material.mtl w]
|
||||||
|
fconfigure $fd -translation crlf
|
||||||
|
puts $fd $material_mtl
|
||||||
|
close $fd
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts "#======================================================================"
|
||||||
|
puts "# Ascii file, CRLF"
|
||||||
|
puts "#======================================================================"
|
||||||
|
set fd [open ${imagedir}/${casename}_one_ascii_dos.obj w]
|
||||||
|
fconfigure $fd -translation crlf
|
||||||
|
puts $fd $minimal_ascii_obj
|
||||||
|
close $fd
|
||||||
|
readobj mcrlf ${imagedir}/${casename}_one_ascii_dos.obj -singleFace
|
||||||
|
checknbshapes mcrlf -face 1
|
||||||
|
checktrinfo mcrlf -tri 16 -nod 10
|
||||||
|
|
||||||
|
puts ""
|
||||||
|
puts "#======================================================================"
|
||||||
|
puts "# Ascii file with single facet, LF"
|
||||||
|
puts "#======================================================================"
|
||||||
|
set fd [open ${imagedir}/${casename}_one_ascii_unix.obj w]
|
||||||
|
fconfigure $fd -translation lf
|
||||||
|
puts $fd $minimal_ascii_obj
|
||||||
|
close $fd
|
||||||
|
readobj mlf ${imagedir}/${casename}_one_ascii_unix.obj -singleFace
|
||||||
|
checknbshapes mlf -face 1
|
||||||
|
checktrinfo mlf -tri 16 -nod 10
|
||||||
|
|
||||||
|
vclear
|
||||||
|
vinit View1
|
||||||
|
vdisplay -dispMode 1 mlf
|
||||||
|
vaxo
|
||||||
|
vfit
|
||||||
|
vdump ${imagedir}/${casename}_raw.png
|
||||||
|
|
||||||
|
# read OBJ into document
|
||||||
|
ReadObj D ${imagedir}/${casename}_one_ascii_unix.obj
|
9
tests/de_mesh/obj_read/ship_boat
Normal file
9
tests/de_mesh/obj_read/ship_boat
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
puts "========"
|
||||||
|
puts "0029296: Data Exchange - implement import of mesh data from files in OBJ format"
|
||||||
|
puts "Ship model with transparent windows"
|
||||||
|
puts "========"
|
||||||
|
|
||||||
|
ReadObj D [locate_data_file ship_boat.obj]
|
||||||
|
XGetOneShape s D
|
||||||
|
checknbshapes s -face 2359 -compound 2
|
||||||
|
checktrinfo s -tri 27297 -nod 40496
|
Reference in New Issue
Block a user