From c55509132b8fdafc2a7e5a96e4b778081b1bbc41 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Mon, 9 May 2011 18:00:55 +0000 Subject: [PATCH] - 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 --- code/BlenderLoader.cpp | 28 +---- code/BlenderLoader.h | 14 +-- code/CMakeLists.txt | 2 + code/IFCLoader.cpp | 193 ++++++++++++++++++++++++++++------- code/IFCLoader.h | 18 +--- code/LogAux.h | 126 +++++++++++++++++++++++ workspaces/vc9/assimp.vcproj | 4 + 7 files changed, 297 insertions(+), 88 deletions(-) create mode 100644 code/LogAux.h diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 67c218915..fc72412e9 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BlenderModifier.h" #include "StreamReader.h" -#include "TinyFormatter.h" #include "MemoryIOWrapper.h" // zlib is needed for compressed blend files @@ -68,7 +67,7 @@ using namespace Assimp; using namespace Assimp::Blender; using namespace Assimp::Formatter; - +const std::string LogFunctions::log_prefix = "BLEND: "; static const aiLoaderDesc blenderDesc = { "Blender 3D Importer \nhttp://www.blender3d.org", "Assimp Team", @@ -976,30 +975,5 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers 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 diff --git a/code/BlenderLoader.h b/code/BlenderLoader.h index 677ebf69c..001e4200f 100644 --- a/code/BlenderLoader.h +++ b/code/BlenderLoader.h @@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_AI_BLEND_LOADER_H #include "BaseImporter.h" +#include "LogAux.h" + namespace Assimp { // TinyFormatter.h @@ -116,7 +118,7 @@ struct aiLoaderDesc * call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the * conversion from intermediate format to aiScene. */ // ------------------------------------------------------------------------------------------- -class BlenderImporter : public BaseImporter +class BlenderImporter : public BaseImporter, public LogFunctions { friend class Importer; @@ -240,16 +242,6 @@ private: // static stuff, mostly logging and error reporting. 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: diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index c9a1515af..3b15aaee7 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -128,6 +128,7 @@ SOURCE_GROUP( Common FILES LineSplitter.h TinyFormatter.h Profiler.h + LogAux.h ) SOURCE_GROUP( 3DS FILES @@ -746,6 +747,7 @@ ADD_LIBRARY( assimp SHARED BlenderModifier.h BlenderModifier.cpp Profiler.h + LogAux.h NDOLoader.cpp NDOLoader.h DeboneProcess.cpp diff --git a/code/IFCLoader.cpp b/code/IFCLoader.cpp index 4971d91a3..8e9a357da 100644 --- a/code/IFCLoader.cpp +++ b/code/IFCLoader.cpp @@ -50,14 +50,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "IFCReaderGen.h" #include "StreamReader.h" -#include "TinyFormatter.h" #include "MemoryIOWrapper.h" +#include "ProcessHelper.h" + +#include using namespace Assimp; using namespace Assimp::Formatter; - namespace EXPRESS = STEP::EXPRESS; +const std::string LogFunctions::log_prefix = "IFC: "; + /* DO NOT REMOVE this comment block. The genentitylist.sh script * 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) { aiVector3D tmp; ConvertCartesianPoint(tmp,c); @@ -574,17 +577,34 @@ void ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, Conversion meshout.verts.push_back(tmp); ++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) { + TempMesh meshout; + + size_t ob = face.Bounds.size(), cnt = 0; BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) { - if(const IFC::IfcPolyLoop* polyloop = bound.Bound->ToPtr()) { - ProcessPolyloop(*polyloop, meshout, conv); + if(const IFC::IfcPolyLoop* const polyloop = bound.Bound->ToPtr()) { + if(ProcessPolyloop(*polyloop, meshout, conv)) { + if(bound.ToPtr()) { + ob = cnt; + } + ++cnt; + } } else { 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)) { - unsigned int cnt = 0; + size_t c = 0; 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::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(*iit)); + } + std::vector 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::quiet_NaN(); +#endif + ++cnt; + } - std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + i); - cnt += i; + aiVector3D nor; + 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 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 -// ------------------------------------------------------------------------------------------------ -/*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 diff --git a/code/IFCLoader.h b/code/IFCLoader.h index 6afac72a0..ddbe04da6 100644 --- a/code/IFCLoader.h +++ b/code/IFCLoader.h @@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_AI_IFC_LOADER_H #include "BaseImporter.h" +#include "LogAux.h" + namespace Assimp { // TinyFormatter.h @@ -65,7 +67,7 @@ namespace Assimp { See http://en.wikipedia.org/wiki/Industry_Foundation_Classes */ // ------------------------------------------------------------------------------------------- -class IFCImporter : public BaseImporter +class IFCImporter : public BaseImporter, public LogFunctions { friend class Importer; @@ -117,20 +119,8 @@ public: bool skipSpaceRepresentations; 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: Settings settings; diff --git a/code/LogAux.h b/code/LogAux.h new file mode 100644 index 000000000..df56b0f48 --- /dev/null +++ b/code/LogAux.h @@ -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 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 diff --git a/workspaces/vc9/assimp.vcproj b/workspaces/vc9/assimp.vcproj index 67b33057c..844219bb9 100644 --- a/workspaces/vc9/assimp.vcproj +++ b/workspaces/vc9/assimp.vcproj @@ -2406,6 +2406,10 @@ RelativePath="..\..\code\LineSplitter.h" > + +