mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-04-10 18:51:21 +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:
parent
0a419c51ed
commit
4151c94d20
@ -442,3 +442,4 @@ n XCAFNoteObjects
|
||||
t TKRWMesh
|
||||
n RWGltf
|
||||
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
|
||||
RWMesh
|
||||
RWObj
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include <Quantity_NameOfColor.hxx>
|
||||
#include <RWGltf_CafReader.hxx>
|
||||
#include <RWStl.hxx>
|
||||
#include <RWObj.hxx>
|
||||
#include <RWObj_CafReader.hxx>
|
||||
#include <SelectMgr_SelectionManager.hxx>
|
||||
#include <Standard_ErrorHandler.hxx>
|
||||
#include <StdSelect_ViewerSelector3d.hxx>
|
||||
@ -290,6 +292,210 @@ static Standard_Integer readstl(Draw_Interpretor& theDI,
|
||||
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
|
||||
(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).",
|
||||
__FILE__, readstl, 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 ("mesh3delem", "creates 3d element mesh to test", __FILE__, create3d, g );
|
||||
|
@ -1,3 +1,4 @@
|
||||
001 stl_read
|
||||
002 shape_write_stl
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user