- IFC: implement automatic conversion from polygons with holes to polygons that consist of only one piece and are thus applicable to triangulation by ear-cutting. This solves many of the broken windows that would fall victim to z-fighting in earlier revisions.

- IFC: reduce logging overhead
- Move parts of IFC and BLENDs logging code to a shared implementation.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@994 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2011-05-09 18:00:55 +00:00
parent 6a9e0f57d4
commit c55509132b
7 changed files with 297 additions and 88 deletions

View File

@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "BlenderModifier.h" #include "BlenderModifier.h"
#include "StreamReader.h" #include "StreamReader.h"
#include "TinyFormatter.h"
#include "MemoryIOWrapper.h" #include "MemoryIOWrapper.h"
// zlib is needed for compressed blend files // zlib is needed for compressed blend files
@ -68,7 +67,7 @@ using namespace Assimp;
using namespace Assimp::Blender; using namespace Assimp::Blender;
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
const std::string LogFunctions<BlenderImporter>::log_prefix = "BLEND: ";
static const aiLoaderDesc blenderDesc = { static const aiLoaderDesc blenderDesc = {
"Blender 3D Importer \nhttp://www.blender3d.org", "Blender 3D Importer \nhttp://www.blender3d.org",
"Assimp Team", "Assimp Team",
@ -976,30 +975,5 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers
return node.dismiss(); return node.dismiss();
} }
// ------------------------------------------------------------------------------------------------
/*static*/ void BlenderImporter::ThrowException(const std::string& msg)
{
throw DeadlyImportError("BLEND: "+msg);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void BlenderImporter::LogWarn(const Formatter::format& message) {
DefaultLogger::get()->warn(std::string("BLEND: ")+=message);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void BlenderImporter::LogError(const Formatter::format& message) {
DefaultLogger::get()->error(std::string("BLEND: ")+=message);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void BlenderImporter::LogInfo(const Formatter::format& message) {
DefaultLogger::get()->info(std::string("BLEND: ")+=message);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void BlenderImporter::LogDebug(const Formatter::format& message) {
DefaultLogger::get()->debug(std::string("BLEND: ")+=message);
}
#endif #endif

View File

@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define INCLUDED_AI_BLEND_LOADER_H #define INCLUDED_AI_BLEND_LOADER_H
#include "BaseImporter.h" #include "BaseImporter.h"
#include "LogAux.h"
namespace Assimp { namespace Assimp {
// TinyFormatter.h // TinyFormatter.h
@ -116,7 +118,7 @@ struct aiLoaderDesc
* call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the * call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
* conversion from intermediate format to aiScene. */ * conversion from intermediate format to aiScene. */
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
class BlenderImporter : public BaseImporter class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter>
{ {
friend class Importer; friend class Importer;
@ -240,16 +242,6 @@ private: // static stuff, mostly logging and error reporting.
const char* type const char* type
); );
// -------------------------------------------------------------------
/** Prepend 'BLEND: ' and throw msg.*/
static void ThrowException(const std::string& msg);
// -------------------------------------------------------------------
/** @defgroup blog Prepend 'BLEND: ' and write @c message to log.*/
static void LogWarn (const Formatter::format& message); //! @ingroup blog
static void LogError (const Formatter::format& message); //! @ingroup blog
static void LogInfo (const Formatter::format& message); //! @ingroup blog
static void LogDebug (const Formatter::format& message); //! @ingroup blog
private: private:

View File

@ -128,6 +128,7 @@ SOURCE_GROUP( Common FILES
LineSplitter.h LineSplitter.h
TinyFormatter.h TinyFormatter.h
Profiler.h Profiler.h
LogAux.h
) )
SOURCE_GROUP( 3DS FILES SOURCE_GROUP( 3DS FILES
@ -746,6 +747,7 @@ ADD_LIBRARY( assimp SHARED
BlenderModifier.h BlenderModifier.h
BlenderModifier.cpp BlenderModifier.cpp
Profiler.h Profiler.h
LogAux.h
NDOLoader.cpp NDOLoader.cpp
NDOLoader.h NDOLoader.h
DeboneProcess.cpp DeboneProcess.cpp

View File

@ -50,14 +50,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "IFCReaderGen.h" #include "IFCReaderGen.h"
#include "StreamReader.h" #include "StreamReader.h"
#include "TinyFormatter.h"
#include "MemoryIOWrapper.h" #include "MemoryIOWrapper.h"
#include "ProcessHelper.h"
#include <boost/tuple/tuple.hpp>
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
namespace EXPRESS = STEP::EXPRESS; namespace EXPRESS = STEP::EXPRESS;
const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
/* DO NOT REMOVE this comment block. The genentitylist.sh script /* DO NOT REMOVE this comment block. The genentitylist.sh script
* just looks for names adhering to the IFC :: IfcSomething naming scheme * just looks for names adhering to the IFC :: IfcSomething naming scheme
@ -564,9 +567,9 @@ void ConvertTransformOperator(aiMatrix4x4& out, const IFC::IfcCartesianTransform
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, ConversionData& conv) bool ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, ConversionData& conv)
{ {
unsigned int cnt = 0; size_t cnt = 0;
BOOST_FOREACH(const IFC::IfcCartesianPoint& c, loop.Polygon) { BOOST_FOREACH(const IFC::IfcCartesianPoint& c, loop.Polygon) {
aiVector3D tmp; aiVector3D tmp;
ConvertCartesianPoint(tmp,c); ConvertCartesianPoint(tmp,c);
@ -574,17 +577,34 @@ void ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, Conversion
meshout.verts.push_back(tmp); meshout.verts.push_back(tmp);
++cnt; ++cnt;
} }
meshout.vertcnt.push_back(cnt); // zero- or one- vertex polyloops simply ignored
if (cnt >= 1) {
meshout.vertcnt.push_back(cnt);
return true;
}
if (cnt==1) {
meshout.vertcnt.pop_back();
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& meshout, ConversionData& conv) void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& result, ConversionData& conv)
{ {
BOOST_FOREACH(const IFC::IfcFace& face, fset.CfsFaces) { BOOST_FOREACH(const IFC::IfcFace& face, fset.CfsFaces) {
TempMesh meshout;
size_t ob = face.Bounds.size(), cnt = 0;
BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) { BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) {
if(const IFC::IfcPolyLoop* polyloop = bound.Bound->ToPtr<IFC::IfcPolyLoop>()) { if(const IFC::IfcPolyLoop* const polyloop = bound.Bound->ToPtr<IFC::IfcPolyLoop>()) {
ProcessPolyloop(*polyloop, meshout, conv); if(ProcessPolyloop(*polyloop, meshout, conv)) {
if(bound.ToPtr<IFC::IfcFaceOuterBound>()) {
ob = cnt;
}
++cnt;
}
} }
else { else {
IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is " + bound.Bound->GetClassName()); IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is " + bound.Bound->GetClassName());
@ -592,14 +612,140 @@ void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& mes
} }
if(!IsTrue(bound.Orientation)) { if(!IsTrue(bound.Orientation)) {
unsigned int cnt = 0; size_t c = 0;
BOOST_FOREACH(unsigned int& i, meshout.vertcnt) { BOOST_FOREACH(unsigned int& i, meshout.vertcnt) {
std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + c);
cnt += c;
}
}
}
result.vertcnt.reserve(meshout.vertcnt.size()+result.vertcnt.size());
if (meshout.vertcnt.size() <= 1) {
result.verts.reserve(meshout.verts.size()+result.verts.size());
std::copy(meshout.verts.begin(),meshout.verts.end(),std::back_inserter(result.verts));
std::copy(meshout.vertcnt.begin(),meshout.vertcnt.end(),std::back_inserter(result.vertcnt));
continue;
}
IFCImporter::LogDebug("fixing polygon with holes for triangulation via ear-cutting");
// each hole results in two extra vertices
result.verts.reserve(meshout.verts.size()+cnt*2+result.verts.size());
// handle polygons with holes. our built in triangulation won't handle them as is, but
// the ear cutting algorithm is solid enough to deal with them if we join the inner
// holes with the outer boundaries by dummy connections.
size_t outer_polygon_start = 0;
// see if one of the polygons is a IfcFaceOuterBound - treats this as the outer boundary.
// sadly we can't rely on it, the docs say 'At most one of the bounds shall be of the type IfcFaceOuterBound'
std::vector<unsigned int>::iterator outer_polygon = meshout.vertcnt.end(), begin=meshout.vertcnt.begin(), iit;
if (ob < face.Bounds.size()) {
outer_polygon = begin + ob;
outer_polygon_start = std::accumulate(begin,outer_polygon,0);
}
else {
float area_outer_polygon = 1e-10f;
// find the polygon with the largest area, it must be the outer bound.
size_t max_vcount = 0;
for(iit = begin; iit != meshout.vertcnt.end(); ++iit) {
ai_assert(*iit);
max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
}
std::vector<float> temp((max_vcount+2)*4);
size_t vidx = 0;
for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
for(size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
const aiVector3D& v = meshout.verts[vidx+vofs];
temp[cnt++] = v.x;
temp[cnt++] = v.y;
temp[cnt++] = v.z;
#ifdef _DEBUG
temp[cnt] = std::numeric_limits<float>::quiet_NaN();
#endif
++cnt;
}
std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + i); aiVector3D nor;
cnt += i; NewellNormal<4,4,4>(nor,*iit,&temp[0],&temp[1],&temp[2]);
const float area = nor.SquareLength();
if (area > area_outer_polygon) {
area_outer_polygon = area;
outer_polygon = iit;
outer_polygon_start = vidx;
} }
} }
} }
ai_assert(outer_polygon != meshout.vertcnt.end());
typedef boost::tuple<unsigned int, unsigned int, unsigned int> InsertionPoint;
std::vector< InsertionPoint > insertions(*outer_polygon,boost::make_tuple(0u,0u,0u));
// iterate through all other polyloops and find points in the outer polyloop that are close
size_t vidx = 0;
for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
if (iit == outer_polygon) {
continue;
}
size_t best_ofs,best_outer;
float best_dist = 1e10;
for(size_t vofs = 0; vofs < *iit; ++vofs) {
const aiVector3D& v = meshout.verts[vidx+vofs];
for(size_t outer = 0; outer < *outer_polygon; ++outer) {
if (insertions[outer].get<0>()) {
continue;
}
const aiVector3D& o = meshout.verts[outer_polygon_start+outer];
const float d = (o-v).SquareLength();
if (d < best_dist) {
best_dist = d;
best_ofs = vofs;
best_outer = outer;
}
}
}
// we will later insert a hidden connection line right after the closest point in the outer polygon
insertions[best_outer] = boost::make_tuple(*iit,vidx,best_ofs);
}
// now that we collected all vertex connections to be added, build the output polygon
cnt = *outer_polygon;
for(size_t outer = 0; outer < *outer_polygon; ++outer) {
const aiVector3D& o = meshout.verts[outer_polygon_start+outer];
result.verts.push_back(o);
const InsertionPoint& ins = insertions[outer];
if (!ins.get<0>()) {
continue;
}
for(size_t i = ins.get<2>(); i < ins.get<0>(); ++i) {
result.verts.push_back(meshout.verts[ins.get<1>() + i]);
}
for(size_t i = 0; i < ins.get<2>(); ++i) {
result.verts.push_back(meshout.verts[ins.get<1>() + i]);
}
// we need the first vertex of the inner polygon twice as we return to the
// outer loop through the very same connection through which we got there.
result.verts.push_back(meshout.verts[ins.get<1>() + ins.get<2>()]);
// also append a copy of the initial insertion point to be able to continue the outer polygon
result.verts.push_back(o);
cnt += ins.get<0>()+2;
}
result.vertcnt.push_back(cnt);
} }
} }
@ -1262,31 +1408,6 @@ void MakeTreeRelative(ConversionData& conv)
} // !anon } // !anon
// ------------------------------------------------------------------------------------------------
/*static*/ void IFCImporter::ThrowException(const std::string& msg)
{
throw DeadlyImportError("IFC: "+msg);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void IFCImporter::LogWarn(const Formatter::format& message) {
DefaultLogger::get()->warn(std::string("IFC: ")+=message);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void IFCImporter::LogError(const Formatter::format& message) {
DefaultLogger::get()->error(std::string("IFC: ")+=message);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void IFCImporter::LogInfo(const Formatter::format& message) {
DefaultLogger::get()->info(std::string("IFC: ")+=message);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void IFCImporter::LogDebug(const Formatter::format& message) {
DefaultLogger::get()->debug(std::string("IFC: ")+=message);
}
#endif #endif

View File

@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define INCLUDED_AI_IFC_LOADER_H #define INCLUDED_AI_IFC_LOADER_H
#include "BaseImporter.h" #include "BaseImporter.h"
#include "LogAux.h"
namespace Assimp { namespace Assimp {
// TinyFormatter.h // TinyFormatter.h
@ -65,7 +67,7 @@ namespace Assimp {
See http://en.wikipedia.org/wiki/Industry_Foundation_Classes See http://en.wikipedia.org/wiki/Industry_Foundation_Classes
*/ */
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
class IFCImporter : public BaseImporter class IFCImporter : public BaseImporter, public LogFunctions<IFCImporter>
{ {
friend class Importer; friend class Importer;
@ -117,20 +119,8 @@ public:
bool skipSpaceRepresentations; bool skipSpaceRepresentations;
bool skipCurveRepresentations; bool skipCurveRepresentations;
}; };
public:
// -------------------------------------------------------------------
/** Prepend 'IFC: ' and throw msg.*/
static void ThrowException(const std::string& msg);
// -------------------------------------------------------------------
/** @defgroup blog Prepend 'IFC: ' and write @c message to log.*/
static void LogWarn (const Formatter::format& message); //! @ingroup blog
static void LogError (const Formatter::format& message); //! @ingroup blog
static void LogInfo (const Formatter::format& message); //! @ingroup blog
static void LogDebug (const Formatter::format& message); //! @ingroup blog
private: private:
Settings settings; Settings settings;

126
code/LogAux.h 100644
View File

@ -0,0 +1,126 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2010, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file LogAux.h
* @brief Common logging usage patterns for importer implementations
*/
#ifndef INCLUDED_AI_LOGAUX_H
#define INCLUDED_AI_LOGAUX_H
#include "TinyFormatter.h"
namespace Assimp {
template <class TDeriving>
class LogFunctions
{
public:
// ------------------------------------------------------------------------------------------------
static void ThrowException(const std::string& msg)
{
throw DeadlyImportError(log_prefix+msg);
}
// ------------------------------------------------------------------------------------------------
static void LogWarn(const Formatter::format& message) {
if (!DefaultLogger::isNullLogger()) {
DefaultLogger::get()->warn(log_prefix+(std::string)message);
}
}
// ------------------------------------------------------------------------------------------------
static void LogError(const Formatter::format& message) {
if (!DefaultLogger::isNullLogger()) {
DefaultLogger::get()->error(log_prefix+(std::string)message);
}
}
// ------------------------------------------------------------------------------------------------
static void LogInfo(const Formatter::format& message) {
if (!DefaultLogger::isNullLogger()) {
DefaultLogger::get()->info(log_prefix+(std::string)message);
}
}
// ------------------------------------------------------------------------------------------------
static void LogDebug(const Formatter::format& message) {
if (!DefaultLogger::isNullLogger()) {
DefaultLogger::get()->debug(log_prefix+(std::string)message);
}
}
// ------------------------------------------------------------------------------------------------
static void LogWarn (const char* message) {
if (!DefaultLogger::isNullLogger()) {
LogWarn(Formatter::format(message));
}
}
// ------------------------------------------------------------------------------------------------
static void LogError (const char* message) {
if (!DefaultLogger::isNullLogger()) {
LogError(Formatter::format(message));
}
}
// ------------------------------------------------------------------------------------------------
static void LogInfo (const char* message) {
if (!DefaultLogger::isNullLogger()) {
LogInfo(Formatter::format(message));
}
}
// ------------------------------------------------------------------------------------------------
static void LogDebug (const char* message) {
if (!DefaultLogger::isNullLogger()) {
LogDebug(Formatter::format(message));
}
}
private:
static const std::string log_prefix;
};
} // ! Assimp
#endif

View File

@ -2406,6 +2406,10 @@
RelativePath="..\..\code\LineSplitter.h" RelativePath="..\..\code\LineSplitter.h"
> >
</File> </File>
<File
RelativePath="..\..\code\LogAux.h"
>
</File>
<File <File
RelativePath="..\..\code\MakeVerboseFormat.cpp" RelativePath="..\..\code\MakeVerboseFormat.cpp"
> >