From 84a9514d4b41849fcdfffbe706ab70200852740a Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 12:14:39 +0200 Subject: [PATCH 01/17] Fix warning about possibly uninitialized variable It might be better to move FindEmptyUVChannel call earlier --- code/ComputeUVMappingProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ComputeUVMappingProcess.cpp b/code/ComputeUVMappingProcess.cpp index 16b9bbfb0..2f037edb1 100644 --- a/code/ComputeUVMappingProcess.cpp +++ b/code/ComputeUVMappingProcess.cpp @@ -454,7 +454,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene) for (unsigned int m = 0; m < pScene->mNumMeshes;++m) { aiMesh* mesh = pScene->mMeshes[m]; - unsigned int outIdx; + unsigned int outIdx = 0; if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX || !mesh->mNumVertices) { From f708d0b122bde569482f3dceb9e0774adcecf2a8 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 12:21:59 +0200 Subject: [PATCH 02/17] Silence some uninitialized variable warnings These are false positives but the compiler isn't smart enough to see that. Also it might make more sense to use switch here. --- code/TextureTransform.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/TextureTransform.cpp b/code/TextureTransform.cpp index 1c27863e3..ca4f4ce73 100644 --- a/code/TextureTransform.cpp +++ b/code/TextureTransform.cpp @@ -120,7 +120,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) * offset 2 and 3) */ if ((rounded = (int)info.mTranslation.x)) { - float out; + float out = 0.0f; szTemp[0] = 0; if (aiTextureMapMode_Wrap == info.mapU) { // Wrap - simple take the fraction of the field @@ -153,7 +153,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) * offset 2 and 3) */ if ((rounded = (int)info.mTranslation.y)) { - float out; + float out = 0.0f; szTemp[0] = 0; if (aiTextureMapMode_Wrap == info.mapV) { // Wrap - simple take the fraction of the field From 570d02ff6e93e30b5d45d48231e7f7cba5f85162 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 12:37:40 +0200 Subject: [PATCH 03/17] Silence more uninitialized variable warnings These are false positives since aiMaterial::Get will have written to output if it returns AI_SUCCESS but the compiler can't see that. --- code/3DSExporter.cpp | 2 +- code/ColladaExporter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 8ad367058..1b870181c 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -294,7 +294,7 @@ void Discreet3DSExporter::WriteMaterials() WriteColor(color); } - aiShadingMode shading_mode; + aiShadingMode shading_mode = aiShadingMode_Flat; if (mat.Get(AI_MATKEY_SHADING_MODEL, shading_mode) == AI_SUCCESS) { ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING); diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index d2e85a015..db76e38a9 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -452,7 +452,7 @@ void ColladaExporter::WriteMaterials() } } - aiShadingMode shading; + aiShadingMode shading = aiShadingMode_Flat; materials[a].shading_model = "phong"; if(mat->Get( AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) { if(shading == aiShadingMode_Phong) { From b71ded1ad0f2eb7be9627a3a0b7b8b235e7e6caf Mon Sep 17 00:00:00 2001 From: ulf Date: Mon, 23 Feb 2015 14:23:28 +0100 Subject: [PATCH 04/17] - manually merged C4D importer code from acgessler branch - manually merged IFC bugfixes and improvements from schrompf branch --- CMakeLists.txt | 46 +++ code/C4DImporter.cpp | 641 ++++++++++++++++++++++++++++++++ code/C4DImporter.h | 120 ++++++ code/CMakeLists.txt | 19 + code/IFCBoolean.cpp | 759 +++++++++++++++++++++----------------- code/IFCGeometry.cpp | 49 ++- code/IFCLoader.cpp | 13 +- code/IFCMaterial.cpp | 69 ++-- code/IFCOpenings.cpp | 38 +- code/IFCUtil.cpp | 170 +++++++-- code/IFCUtil.h | 24 +- code/ImporterRegistry.cpp | 8 + 12 files changed, 1532 insertions(+), 424 deletions(-) create mode 100644 code/C4DImporter.cpp create mode 100644 code/C4DImporter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index be712c7fb..c8df63410 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,52 @@ ENDIF ( ASSIMP_BUILD_COMPILER STREQUAL "") MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER ) + +SET ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER OFF CACHE BOOL + "Build the C4D importer, which relies on the non-free Melange SDK." +) + +IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + IF ( MSVC ) + SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/includes") + + # pick the correct prebuilt library + IF(MSVC11) + SET(C4D_LIB_POSTFIX "_2012md") + ELSEIF(MSVC10) + SET(C4D_LIB_POSTFIX "_2010md") + ELSEIF(MSVC90) + SET(C4D_LIB_POSTFIX "_2008md") + ELSE() + MESSAGE( FATAL_ERROR + "C4D is currently only supported with MSVC 9, 10, 11" + ) + ENDIF() + + IF(CMAKE_CL_64) + SET(C4D_LIB_ARCH_POSTFIX "_x64") + ELSE() + SET(C4D_LIB_ARCH_POSTFIX "") + ENDIF() + + SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/lib/WIN") + + SET(C4D_DEBUG_LIBRARY "${C4D_LIB_BASE_PATH}/debug/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib") + SET(C4D_RELEASE_LIBRARY "${C4D_LIB_BASE_PATH}/release/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib") + + # winsock and winmm are necessary dependencies of melange (this is undocumented, but true.) + SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib) + ELSE () + MESSAGE( FATAL_ERROR + "C4D is currently only available on Windows with melange SDK installed in contrib/Melange" + ) + ENDIF ( MSVC ) +else (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER ) +ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + + + ADD_SUBDIRECTORY( code/ ) option ( ASSIMP_BUILD_ASSIMP_TOOLS "If the supplementary tools for Assimp are built in addition to the library." diff --git a/code/C4DImporter.cpp b/code/C4DImporter.cpp new file mode 100644 index 000000000..167e6800c --- /dev/null +++ b/code/C4DImporter.cpp @@ -0,0 +1,641 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp 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 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 C4DImporter.cpp + * @brief Implementation of the Cinema4D importer class. + */ +#include "AssimpPCH.h" + +// no #ifdefing here, Cinema4D support is carried out in a branch of assimp +// where it is turned on in the CMake settings. + +#ifndef _MSC_VER +# error C4D support is currently MSVC only +#endif + +#include "C4DImporter.h" +#include "TinyFormatter.h" + +#if defined(_M_X64) || defined(__amd64__) +# define __C4D_64BIT +#endif + +#define __PC +#include "c4d_file.h" +#include "default_alien_overloads.h" + +using namespace _melange_; + +// overload this function and fill in your own unique data +void GetWriterInfo(LONG &id, String &appname) +{ + id = 2424226; + appname = "Open Asset Import Library"; +} + +using namespace Assimp; +using namespace Assimp::Formatter; + +namespace Assimp { + template<> const std::string LogFunctions::log_prefix = "C4D: "; +} + +static const aiImporterDesc desc = { + "Cinema4D Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "c4d" +}; + + +// ------------------------------------------------------------------------------------------------ +C4DImporter::C4DImporter() +{} + +// ------------------------------------------------------------------------------------------------ +C4DImporter::~C4DImporter() +{} + +// ------------------------------------------------------------------------------------------------ +bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + const std::string& extension = GetExtension(pFile); + if (extension == "c4d") { + return true; + } + + else if ((!extension.length() || checkSig) && pIOHandler) { + // TODO + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc* C4DImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +void C4DImporter::SetupProperties(const Importer* /*pImp*/) +{ + // nothing to be done for the moment +} + + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void C4DImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile)); + + if( file.get() == NULL) { + ThrowException("failed to open file " + pFile); + } + + const size_t file_size = file->FileSize(); + + std::vector mBuffer(file_size); + file->Read(&mBuffer[0], 1, file_size); + + Filename f; + f.SetMemoryReadMode(&mBuffer[0], file_size); + + // open document first + BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS); + if(doc == NULL) { + ThrowException("failed to read document " + pFile); + } + + pScene->mRootNode = new aiNode(""); + + // first convert all materials + ReadMaterials(doc->GetFirstMaterial()); + + // process C4D scenegraph recursively + try { + RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode); + } + catch(...) { + BOOST_FOREACH(aiMesh* mesh, meshes) { + delete mesh; + } + BaseDocument::Free(doc); + throw; + } + BaseDocument::Free(doc); + + // copy meshes over + pScene->mNumMeshes = static_cast(meshes.size()); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes](); + std::copy(meshes.begin(), meshes.end(), pScene->mMeshes); + + // copy materials over, adding a default material if necessary + unsigned int mat_count = static_cast(materials.size()); + BOOST_FOREACH(aiMesh* mesh, meshes) { + ai_assert(mesh->mMaterialIndex <= mat_count); + if(mesh->mMaterialIndex >= mat_count) { + ++mat_count; + + ScopeGuard def_material(new aiMaterial()); + const aiString name(AI_DEFAULT_MATERIAL_NAME); + def_material->AddProperty(&name, AI_MATKEY_NAME); + + materials.push_back(def_material.dismiss()); + break; + } + } + + pScene->mNumMaterials = static_cast(materials.size()); + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials](); + std::copy(materials.begin(), materials.end(), pScene->mMaterials); +} + + +// ------------------------------------------------------------------------------------------------ +bool C4DImporter::ReadShader(aiMaterial* out, _melange_::BaseShader* shader) +{ + // based on Melange sample code (C4DImportExport.cpp) + while(shader) { + if(shader->GetType() == Xlayer) { + BaseContainer* container = shader->GetDataInstance(); + GeData blend = container->GetData(SLA_LAYER_BLEND); + iBlendDataType* blend_list = reinterpret_cast(blend.GetCustomDataType(CUSTOMDATA_BLEND_LIST)); + if (!blend_list) + { + LogWarn("ignoring XLayer shader: no blend list given"); + continue; + } + + LayerShaderLayer *lsl = dynamic_cast(blend_list->m_BlendLayers.GetObject(0)); + + // Ignore the actual layer blending - models for real-time rendering should not + // use them in a non-trivial way. Just try to find textures that we can apply + // to the model. + while (lsl) + { + if (lsl->GetType() == TypeFolder) + { + BlendFolder* const folder = dynamic_cast(lsl); + LayerShaderLayer *subLsl = dynamic_cast(folder->m_Children.GetObject(0)); + + while (subLsl) + { + if (subLsl->GetType() == TypeShader) { + BlendShader* const shader = dynamic_cast(subLsl); + if(ReadShader(out, static_cast(shader->m_pLink->GetLink()))) { + return true; + } + } + + subLsl = subLsl->GetNext(); + } + } + else if (lsl->GetType() == TypeShader) { + BlendShader* const shader = dynamic_cast(lsl); + if(ReadShader(out, static_cast(shader->m_pLink->GetLink()))) { + return true; + } + } + + lsl = lsl->GetNext(); + } + } + else if ( shader->GetType() == Xbitmap ) + { + aiString path; + shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1); + path.length = ::strlen(path.data); + out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0)); + return true; + } + else { + LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType()))); + } + shader = shader->GetNext(); + } + return false; +} + + +// ------------------------------------------------------------------------------------------------ +void C4DImporter::ReadMaterials(_melange_::BaseMaterial* mat) +{ + // based on Melange sample code + while (mat) + { + const String& name = mat->GetName(); + if (mat->GetType() == Mmaterial) + { + aiMaterial* out = new aiMaterial(); + material_mapping[mat] = static_cast(materials.size()); + materials.push_back(out); + + aiString ai_name; + name.GetCString(ai_name.data, MAXLEN-1); + ai_name.length = ::strlen(ai_name.data); + out->AddProperty(&ai_name, AI_MATKEY_NAME); + + Material& m = dynamic_cast(*mat); + + if (m.GetChannelState(CHANNEL_COLOR)) + { + GeData data; + mat->GetParameter(MATERIAL_COLOR_COLOR, data); + Vector color = data.GetVector(); + mat->GetParameter(MATERIAL_COLOR_BRIGHTNESS, data); + const Real brightness = data.GetReal(); + + color *= brightness; + + aiVector3D v; + v.x = color.x; + v.y = color.y; + v.z = color.z; + out->AddProperty(&v, 1, AI_MATKEY_COLOR_DIFFUSE); + } + + BaseShader* const shader = m.GetShader(MATERIAL_COLOR_SHADER); + if(shader) { + ReadShader(out, shader); + } + } + else + { + LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType()))); + } + mat = mat->GetNext(); + } +} + +// ------------------------------------------------------------------------------------------------ +void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) +{ + ai_assert(parent != NULL); + std::vector nodes; + + // based on Melange sample code + while (object) + { + const String& name = object->GetName(); + const LONG type = object->GetType(); + const Matrix& ml = object->GetMl(); + + aiString string; + name.GetCString(string.data, MAXLEN-1); + string.length = ::strlen(string.data); + aiNode* const nd = new aiNode(); + + nd->mParent = parent; + nd->mName = string; + + nd->mTransformation.a1 = ml.v1.x; + nd->mTransformation.b1 = ml.v1.y; + nd->mTransformation.c1 = ml.v1.z; + + nd->mTransformation.a2 = ml.v2.x; + nd->mTransformation.b2 = ml.v2.y; + nd->mTransformation.c2 = ml.v2.z; + + nd->mTransformation.a3 = ml.v3.x; + nd->mTransformation.b3 = ml.v3.y; + nd->mTransformation.c3 = ml.v3.z; + + nd->mTransformation.a4 = ml.off.x; + nd->mTransformation.b4 = ml.off.y; + nd->mTransformation.c4 = ml.off.z; + + nodes.push_back(nd); + + GeData data; + if (type == Ocamera) + { + object->GetParameter(CAMERAOBJECT_FOV, data); + // TODO: read camera + } + else if (type == Olight) + { + // TODO: read light + } + else if (type == Opolygon) + { + aiMesh* const mesh = ReadMesh(object); + if(mesh != NULL) { + nd->mNumMeshes = 1; + nd->mMeshes = new unsigned int[1]; + nd->mMeshes[0] = static_cast(meshes.size()); + meshes.push_back(mesh); + } + } + else { + LogWarn("ignoring object: " + std::string(GetObjectTypeName(type))); + } + + RecurseHierarchy(object->GetDown(), nd); + object = object->GetNext(); + } + + // copy nodes over to parent + parent->mNumChildren = static_cast(nodes.size()); + parent->mChildren = new aiNode*[parent->mNumChildren](); + std::copy(nodes.begin(), nodes.end(), parent->mChildren); +} + + +// ------------------------------------------------------------------------------------------------ +aiMesh* C4DImporter::ReadMesh(BaseObject* object) +{ + assert(object != NULL && object->GetType() == Opolygon); + + // based on Melange sample code + PolygonObject* const polyObject = dynamic_cast(object); + ai_assert(polyObject != NULL); + + const LONG pointCount = polyObject->GetPointCount(); + const LONG polyCount = polyObject->GetPolygonCount(); + if(!polyObject || !pointCount) { + LogWarn("ignoring mesh with zero vertices or faces"); + return NULL; + } + + const Vector* points = polyObject->GetPointR(); + ai_assert(points != NULL); + + const CPolygon* polys = polyObject->GetPolygonR(); + ai_assert(polys != NULL); + + ScopeGuard mesh(new aiMesh()); + mesh->mNumFaces = static_cast(polyCount); + aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces](); + + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + mesh->mMaterialIndex = 0; + + unsigned int vcount = 0; + + // first count vertices + for (LONG i = 0; i < polyCount; i++) + { + vcount += 3; + + // TODO: do we also need to handle lines or points with similar checks? + if (polys[i].c != polys[i].d) + { + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + ++vcount; + } + } + + ai_assert(vcount > 0); + + mesh->mNumVertices = vcount; + aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + aiVector3D* normals, *uvs, *tangents, *bitangents; + unsigned int n = 0; + + // check if there are normals, tangents or UVW coordinates + BaseTag* tag = object->GetTag(Tnormal); + NormalTag* normals_src = NULL; + if(tag) { + normals_src = dynamic_cast(tag); + normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices](); + } + + tag = object->GetTag(Ttangent); + TangentTag* tangents_src = NULL; + if(tag) { + tangents_src = dynamic_cast(tag); + tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices](); + bitangents = mesh->mBitangents = new aiVector3D[mesh->mNumVertices](); + } + + tag = object->GetTag(Tuvw); + UVWTag* uvs_src = NULL; + if(tag) { + uvs_src = dynamic_cast(tag); + uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices](); + } + + // copy vertices and extra channels over and populate faces + for (LONG i = 0; i < polyCount; ++i, ++face) + { + ai_assert(polys[i].a < pointCount && polys[i].a >= 0); + const Vector& pointA = points[polys[i].a]; + verts->x = pointA.x; + verts->y = pointA.y; + verts->z = pointA.z; + ++verts; + + ai_assert(polys[i].b < pointCount && polys[i].b >= 0); + const Vector& pointB = points[polys[i].b]; + verts->x = pointB.x; + verts->y = pointB.y; + verts->z = pointB.z; + ++verts; + + ai_assert(polys[i].c < pointCount && polys[i].c >= 0); + const Vector& pointC = points[polys[i].c]; + verts->x = pointC.x; + verts->y = pointC.y; + verts->z = pointC.z; + ++verts; + + // TODO: do we also need to handle lines or points with similar checks? + if (polys[i].c != polys[i].d) + { + ai_assert(polys[i].d < pointCount && polys[i].d >= 0); + + face->mNumIndices = 4; + mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + const Vector& pointD = points[polys[i].d]; + verts->x = pointD.x; + verts->y = pointD.y; + verts->z = pointD.z; + ++verts; + } + else { + face->mNumIndices = 3; + } + face->mIndices = new unsigned int[face->mNumIndices]; + for(unsigned int j = 0; j < face->mNumIndices; ++j) { + face->mIndices[j] = n++; + } + + // copy normals + if (normals_src) { + if(i >= normals_src->GetNormalCount()) { + LogError("unexpected number of normals, ignoring"); + } + else { + const NormalStruct& nor = normals_src->GetNormals(i); + normals->x = nor.a.x; + normals->y = nor.a.y; + normals->z = nor.a.z; + ++normals; + + normals->x = nor.b.x; + normals->y = nor.b.y; + normals->z = nor.b.z; + ++normals; + + normals->x = nor.c.x; + normals->y = nor.c.y; + normals->z = nor.c.z; + ++normals; + + if(face->mNumIndices == 4) { + normals->x = nor.d.x; + normals->y = nor.d.y; + normals->z = nor.d.z; + ++normals; + } + } + } + + // copy tangents and bitangents + if (tangents_src) { + + for(unsigned int k = 0; k < face->mNumIndices; ++k) { + LONG l; + switch(k) { + case 0: + l = polys[i].a; + break; + case 1: + l = polys[i].b; + break; + case 2: + l = polys[i].c; + break; + case 3: + l = polys[i].d; + break; + default: + ai_assert(false); + } + if(l >= tangents_src->GetDataCount()) { + LogError("unexpected number of tangents, ignoring"); + break; + } + + Tangent tan = tangents_src->GetDataR()[l]; + tangents->x = tan.vl.x; + tangents->y = tan.vl.y; + tangents->z = tan.vl.z; + ++tangents; + + bitangents->x = tan.vr.x; + bitangents->y = tan.vr.y; + bitangents->z = tan.vr.z; + ++bitangents; + } + } + + // copy UVs + if (uvs_src) { + if(i >= uvs_src->GetDataCount()) { + LogError("unexpected number of UV coordinates, ignoring"); + } + else { + UVWStruct uvw; + uvs_src->Get(uvs_src->GetDataAddressR(),i,uvw); + + uvs->x = uvw.a.x; + uvs->y = 1.0f-uvw.a.y; + uvs->z = uvw.a.z; + ++uvs; + + uvs->x = uvw.b.x; + uvs->y = 1.0f-uvw.b.y; + uvs->z = uvw.b.z; + ++uvs; + + uvs->x = uvw.c.x; + uvs->y = 1.0f-uvw.c.y; + uvs->z = uvw.c.z; + ++uvs; + + if(face->mNumIndices == 4) { + uvs->x = uvw.d.x; + uvs->y = 1.0f-uvw.d.y; + uvs->z = uvw.d.z; + ++uvs; + } + } + } + } + + mesh->mMaterialIndex = ResolveMaterial(polyObject); + return mesh.dismiss(); +} + + +// ------------------------------------------------------------------------------------------------ +unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) +{ + ai_assert(obj != NULL); + + const unsigned int mat_count = static_cast(materials.size()); + + BaseTag* tag = obj->GetTag(Ttexture); + if(tag == NULL) { + return mat_count; + } + + TextureTag& ttag = dynamic_cast(*tag); + + BaseMaterial* const mat = ttag.GetMaterial(); + assert(mat != NULL); + + const MaterialMap::const_iterator it = material_mapping.find(mat); + if(it == material_mapping.end()) { + return mat_count; + } + + ai_assert((*it).second < mat_count); + return (*it).second; +} diff --git a/code/C4DImporter.h b/code/C4DImporter.h new file mode 100644 index 000000000..3c8526eb2 --- /dev/null +++ b/code/C4DImporter.h @@ -0,0 +1,120 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp 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 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 C4DImporter.h + * @brief Declaration of the Cinema4D (*.c4d) importer class. + */ +#ifndef INCLUDED_AI_CINEMA_4D_LOADER_H +#define INCLUDED_AI_CINEMA_4D_LOADER_H + +#include "BaseImporter.h" +#include "LogAux.h" + +#include + +struct aiImporterDesc; + +namespace _melange_ { + class BaseObject; // c4d_file.h + class PolygonObject; + class BaseMaterial; + class BaseShader; +} + +namespace Assimp { + + // TinyFormatter.h + namespace Formatter { + template class basic_formatter; + typedef class basic_formatter< char, std::char_traits, std::allocator > format; + } + +// ------------------------------------------------------------------------------------------- +/** Importer class to load Cinema4D files using the Melange library to be obtained from + * www.plugincafe.com + * + * Note that Melange is not free software. */ +// ------------------------------------------------------------------------------------------- +class C4DImporter : public BaseImporter, public LogFunctions +{ +public: + + C4DImporter(); + ~C4DImporter(); + + +public: + + // -------------------- + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + +protected: + + // -------------------- + const aiImporterDesc* GetInfo () const; + + // -------------------- + void SetupProperties(const Importer* pImp); + + // -------------------- + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + +private: + + void ReadMaterials(_melange_::BaseMaterial* mat); + void RecurseHierarchy(_melange_::BaseObject* object, aiNode* parent); + aiMesh* ReadMesh(_melange_::BaseObject* object); + unsigned int ResolveMaterial(_melange_::PolygonObject* obj); + + bool ReadShader(aiMaterial* out, _melange_::BaseShader* shader); + + std::vector meshes; + std::vector materials; + + typedef std::map<_melange_::BaseMaterial*, unsigned int> MaterialMap; + MaterialMap material_mapping; + +}; // !class C4DImporter + +} // end of namespace Assimp +#endif // INCLUDED_AI_CINEMA_4D_LOADER_H + diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 5811bc103..e571789a2 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -148,6 +148,14 @@ SET( Common_SRCS ) SOURCE_GROUP(Common FILES ${Common_SRCS}) +IF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER ) + SET( C4D_SRCS + C4DImporter.cpp + C4DImporter.h + ) + SOURCE_GROUP( C4D FILES ${C4D_SRCS}) +ENDIF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER ) + SET( 3DS_SRCS 3DSConverter.cpp 3DSHelper.h @@ -718,6 +726,11 @@ SET( assimp_src AssimpPCH.cpp ) +IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + SET( assimp_src ${assimp_src} ${C4D_SRCS}) + INCLUDE_DIRECTORIES(${C4D_INCLUDES}) +ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + #ADD_MSVC_PRECOMPILED_HEADER("AssimpPCH.h" "AssimpPCH.cpp" assimp_src) ADD_LIBRARY( assimp ${assimp_src} ) @@ -730,6 +743,12 @@ if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM) target_link_libraries(assimp android_jniiosystem) endif(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM) +IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + TARGET_LINK_LIBRARIES(assimp optimized ${C4D_RELEASE_LIBRARY}) + TARGET_LINK_LIBRARIES(assimp debug ${C4D_DEBUG_LIBRARY}) + TARGET_LINK_LIBRARIES(assimp ${C4D_EXTRA_LIBRARIES}) +ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) + if( MSVC ) # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix if( MSVC70 OR MSVC71 ) diff --git a/code/IFCBoolean.cpp b/code/IFCBoolean.cpp index 731deca9f..cfa788a24 100644 --- a/code/IFCBoolean.cpp +++ b/code/IFCBoolean.cpp @@ -50,42 +50,97 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ProcessHelper.h" #include +#include + namespace Assimp { namespace IFC { // ------------------------------------------------------------------------------------------------ -enum Intersect { - Intersect_No, - Intersect_LiesOnPlane, - Intersect_Yes -}; - -// ------------------------------------------------------------------------------------------------ -Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0, - const IfcVector3& e1, - IfcVector3& out) +// Calculates intersection between line segment and plane. To catch corner cases, specify which side you prefer. +// The function then generates a hit only if the end is beyond a certain margin in that direction, filtering out +// "very close to plane" ghost hits as long as start and end stay directly on or within the given plane side. +bool IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0, + const IfcVector3& e1, bool assumeStartOnWhiteSide, IfcVector3& out) { - const IfcVector3 pdelta = e0 - p, seg = e1-e0; + const IfcVector3 pdelta = e0 - p, seg = e1 - e0; const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta); - if (std::fabs(dotOne) < 1e-6) { - return std::fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No; + // if segment ends on plane, do not report a hit. We stay on that side until a following segment starting at this + // point leaves the plane through the other side + if( std::abs(dotOne + dotTwo) < 1e-6 ) + return false; + + // if segment starts on the plane, report a hit only if the end lies on the *other* side + if( std::abs(dotTwo) < 1e-6 ) + { + if( (assumeStartOnWhiteSide && dotOne + dotTwo < 1e-6) || (!assumeStartOnWhiteSide && dotOne + dotTwo > -1e-6) ) + { + out = e0; + return true; + } + else + { + return false; + } } - const IfcFloat t = dotTwo/dotOne; + // ignore if segment is parallel to plane and far away from it on either side + // Warning: if there's a few thousand of such segments which slowly accumulate beyond the epsilon, no hit would be registered + if( std::abs(dotOne) < 1e-6 ) + return false; + // t must be in [0..1] if the intersection point is within the given segment - if (t > 1.f || t < 0.f) { - return Intersect_No; - } - out = e0+t*seg; - return Intersect_Yes; + const IfcFloat t = dotTwo / dotOne; + if( t > 1.0 || t < 0.0 ) + return false; + + out = e0 + t*seg; + return true; } // ------------------------------------------------------------------------------------------------ -void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result, - const TempMesh& first_operand, - ConversionData& /*conv*/) +void FilterPolygon(std::vector& resultpoly) +{ + if( resultpoly.size() < 3 ) + { + resultpoly.clear(); + return; + } + + IfcVector3 vmin, vmax; + ArrayBounds(resultpoly.data(), resultpoly.size(), vmin, vmax); + + // filter our IfcFloat points - those may happen if a point lies + // directly on the intersection line or directly on the clipping plane + const IfcFloat epsilon = (vmax - vmin).SquareLength() / 1e6f; + FuzzyVectorCompare fz(epsilon); + std::vector::iterator e = std::unique(resultpoly.begin(), resultpoly.end(), fz); + + if( e != resultpoly.end() ) + resultpoly.erase(e, resultpoly.end()); + + if( !resultpoly.empty() && fz(resultpoly.front(), resultpoly.back()) ) + resultpoly.pop_back(); +} + +// ------------------------------------------------------------------------------------------------ +void WritePolygon(std::vector& resultpoly, TempMesh& result) +{ + FilterPolygon(resultpoly); + + if( resultpoly.size() > 2 ) + { + result.verts.insert(result.verts.end(), resultpoly.begin(), resultpoly.end()); + result.vertcnt.push_back(resultpoly.size()); + } +} + + +// ------------------------------------------------------------------------------------------------ +void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result, + const TempMesh& first_operand, + ConversionData& /*conv*/) { ai_assert(hs != NULL); @@ -120,20 +175,14 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re for(iit = begin; iit != end; vidx += *iit++) { unsigned int newcount = 0; - for(unsigned int i = 0; i < *iit; ++i) { - const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit]; + bool isAtWhiteSide = (in[vidx] - p) * n > -1e-6; + for( unsigned int i = 0; i < *iit; ++i ) { + const IfcVector3& e0 = in[vidx + i], e1 = in[vidx + (i + 1) % *iit]; // does the next segment intersect the plane? IfcVector3 isectpos; - const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos); - if (isect == Intersect_No || isect == Intersect_LiesOnPlane) { - if ( (e0-p).Normalize()*n > 0 ) { - outvert.push_back(e0); - ++newcount; - } - } - else if (isect == Intersect_Yes) { - if ( (e0-p).Normalize()*n > 0 ) { + if( IntersectSegmentPlane(p, n, e0, e1, isAtWhiteSide, isectpos) ) { + if( isAtWhiteSide ) { // e0 is on the right side, so keep it outvert.push_back(e0); outvert.push_back(isectpos); @@ -144,8 +193,16 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re outvert.push_back(isectpos); ++newcount; } + isAtWhiteSide = !isAtWhiteSide; } - } + else + { + if( isAtWhiteSide ) { + outvert.push_back(e0); + ++newcount; + } + } + } if (!newcount) { continue; @@ -185,76 +242,114 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re // ------------------------------------------------------------------------------------------------ // Check if e0-e1 intersects a sub-segment of the given boundary line. // note: this functions works on 3D vectors, but performs its intersection checks solely in xy. -bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, const std::vector& boundary, - std::vector& intersected_boundary_segments, - std::vector& intersected_boundary_points, - bool half_open = false, - bool* e0_hits_border = NULL) +// New version takes the supposed inside/outside state as a parameter and treats corner cases as if +// the line stays on that side. This should make corner cases more stable. +// Two million assumptions! Boundary should have all z at 0.0, will be treated as closed, should not have +// segments with length <1e-6, self-intersecting might break the corner case handling... just don't go there, ok? +bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const std::vector& boundary, + const bool isStartAssumedInside, std::vector >& intersect_results, + const bool halfOpen = false) { - ai_assert(intersected_boundary_segments.empty()); - ai_assert(intersected_boundary_points.empty()); + ai_assert(intersect_results.empty()); - if(e0_hits_border) { - *e0_hits_border = false; + // determine winding order - necessary to detect segments going "inwards" or "outwards" from a point directly on the border + // positive sum of angles means clockwise order when looking down the -Z axis + IfcFloat windingOrder = 0.0; + for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) { + IfcVector3 b01 = boundary[(i + 1) % bcount] - boundary[i]; + IfcVector3 b12 = boundary[(i + 2) % bcount] - boundary[(i + 1) % bcount]; + IfcVector3 b1_side = IfcVector3(b01.y, -b01.x, 0.0); // rotated 90° clockwise in Z plane + // Warning: rough estimate only. A concave poly with lots of small segments each featuring a small counter rotation + // could fool the accumulation. Correct implementation would be sum( acos( b01 * b2) * sign( b12 * b1_side)) + windingOrder += (b1_side.x*b12.x + b1_side.y*b12.y); } + windingOrder = windingOrder > 0.0 ? 1.0 : -1.0; - const IfcVector3& e = e1 - e0; + const IfcVector3 e = e1 - e0; - for (size_t i = 0, bcount = boundary.size(); i < bcount; ++i) { + for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) { // boundary segment i: b0-b1 const IfcVector3& b0 = boundary[i]; - const IfcVector3& b1 = boundary[(i+1) % bcount]; - - const IfcVector3& b = b1 - b0; + const IfcVector3& b1 = boundary[(i + 1) % bcount]; + IfcVector3 b = b1 - b0; + IfcFloat b_sqlen_inv = 1.0 / b.SquareLength(); // segment-segment intersection // solve b0 + b*s = e0 + e*t for (s,t) const IfcFloat det = (-b.x * e.y + e.x * b.y); - if(std::fabs(det) < 1e-6) { + if( std::abs(det) < 1e-6 ) { // no solutions (parallel lines) continue; } const IfcFloat x = b0.x - e0.x; const IfcFloat y = b0.y - e0.y; - - const IfcFloat s = (x*e.y - e.x*y)/det; - const IfcFloat t = (x*b.y - b.x*y)/det; - + const IfcFloat s = (x*e.y - e.x*y) / det; // scale along boundary edge + const IfcFloat t = (x*b.y - b.x*y) / det; // scale along given segment + const IfcVector3 p = e0 + e*t; #ifdef ASSIMP_BUILD_DEBUG - const IfcVector3 check = b0 + b*s - (e0 + e*t); - ai_assert((IfcVector2(check.x,check.y)).SquareLength() < 1e-5); + const IfcVector3 check = b0 + b*s - p; + ai_assert((IfcVector2(check.x, check.y)).SquareLength() < 1e-5); #endif - // for a valid intersection, s-t should be in range [0,1]. - // note that for t (i.e. the segment point) we only use a - // half-sided epsilon because the next segment should catch - // this case. - const IfcFloat epsilon = 1e-6; - if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) { + // also calculate the distance of e0 and e1 to the segment. We need to detect the "starts directly on segment" + // and "ends directly at segment" cases + bool startsAtSegment, endsAtSegment; + { + // calculate closest point to each end on the segment, clamp that point to the segment's length, then check + // distance to that point. This approach is like testing if e0 is inside a capped cylinder. + IfcFloat et0 = (b.x*(e0.x - b0.x) + b.y*(e0.y - b0.y)) * b_sqlen_inv; + IfcVector3 closestPosToE0OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et0)) * b; + startsAtSegment = (closestPosToE0OnBoundary - IfcVector3(e0.x, e0.y, 0.0)).SquareLength() < 1e-12; + IfcFloat et1 = (b.x*(e1.x - b0.x) + b.y*(e1.y - b0.y)) * b_sqlen_inv; + IfcVector3 closestPosToE1OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et1)) * b; + endsAtSegment = (closestPosToE1OnBoundary - IfcVector3(e1.x, e1.y, 0.0)).SquareLength() < 1e-12; + } - if (e0_hits_border && !*e0_hits_border) { - *e0_hits_border = std::fabs(t) < 1e-5f; - } - - const IfcVector3& p = e0 + e*t; + // Line segment ends at boundary -> ignore any hit, it will be handled by possibly following segments + if( endsAtSegment && !halfOpen ) + continue; - // only insert the point into the list if it is sufficiently - // far away from the previous intersection point. This way, - // we avoid duplicate detection if the intersection is - // directly on the vertex between two segments. - if (!intersected_boundary_points.empty() && intersected_boundary_segments.back()==i-1 ) { - const IfcVector3 diff = intersected_boundary_points.back() - p; - if(IfcVector2(diff.x, diff.y).SquareLength() < 1e-7) { + // Line segment starts at boundary -> generate a hit only if following that line would change the INSIDE/OUTSIDE + // state. This should catch the case where a connected set of segments has a point directly on the boundary, + // one segment not hitting it because it ends there and the next segment not hitting it because it starts there + // Should NOT generate a hit if the segment only touches the boundary but turns around and stays inside. + if( startsAtSegment ) + { + IfcVector3 inside_dir = IfcVector3(b.y, -b.x, 0.0) * windingOrder; + bool isGoingInside = (inside_dir * e) > 0.0; + if( isGoingInside == isStartAssumedInside ) + continue; + + // only insert the point into the list if it is sufficiently far away from the previous intersection point. + // This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments. + if( !intersect_results.empty() && intersect_results.back().first == i - 1 ) + { + const IfcVector3 diff = intersect_results.back().second - e0; + if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 ) continue; - } } - intersected_boundary_segments.push_back(i); - intersected_boundary_points.push_back(p); + intersect_results.push_back(std::make_pair(i, e0)); + continue; + } + + // for a valid intersection, s and t should be in range [0,1]. Including a bit of epsilon on s, potential double + // hits on two consecutive boundary segments are filtered + if( s >= -1e-6 * b_sqlen_inv && s <= 1.0 + 1e-6*b_sqlen_inv && t >= 0.0 && (t <= 1.0 || halfOpen) ) + { + // only insert the point into the list if it is sufficiently far away from the previous intersection point. + // This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments. + if( !intersect_results.empty() && intersect_results.back().first == i - 1 ) + { + const IfcVector3 diff = intersect_results.back().second - p; + if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 ) + continue; + } + intersect_results.push_back(std::make_pair(i, p)); } } - return !intersected_boundary_segments.empty(); + return !intersect_results.empty(); } @@ -272,47 +367,21 @@ bool PointInPoly(const IfcVector3& p, const std::vector& boundary) // the border of the polygon. If any of our attempts produces this result, // we return false immediately. - std::vector intersected_boundary_segments; - std::vector intersected_boundary_points; + std::vector > intersected_boundary; size_t votes = 0; - bool is_border; - IntersectsBoundaryProfile(p, p + IfcVector3(1.0,0,0), boundary, - intersected_boundary_segments, - intersected_boundary_points, true, &is_border); + IntersectsBoundaryProfile(p, p + IfcVector3(1.0, 0, 0), boundary, true, intersected_boundary, true); + votes += intersected_boundary.size() % 2; - if(is_border) { - return false; - } + intersected_boundary.clear(); + IntersectsBoundaryProfile(p, p + IfcVector3(0, 1.0, 0), boundary, true, intersected_boundary, true); + votes += intersected_boundary.size() % 2; - votes += intersected_boundary_segments.size() % 2; + intersected_boundary.clear(); + IntersectsBoundaryProfile(p, p + IfcVector3(0.6, -0.6, 0.0), boundary, true, intersected_boundary, true); + votes += intersected_boundary.size() % 2; - intersected_boundary_segments.clear(); - intersected_boundary_points.clear(); - - IntersectsBoundaryProfile(p, p + IfcVector3(0,1.0,0), boundary, - intersected_boundary_segments, - intersected_boundary_points, true, &is_border); - - if(is_border) { - return false; - } - - votes += intersected_boundary_segments.size() % 2; - - intersected_boundary_segments.clear(); - intersected_boundary_points.clear(); - - IntersectsBoundaryProfile(p, p + IfcVector3(0.6,-0.6,0.0), boundary, - intersected_boundary_segments, - intersected_boundary_points, true, &is_border); - - if(is_border) { - return false; - } - - votes += intersected_boundary_segments.size() % 2; - //ai_assert(votes == 3 || votes == 0); + ai_assert(votes == 3 || votes == 0); return votes > 1; } @@ -350,6 +419,9 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded return; } + // determine winding order by calculating the normal. + IfcVector3 profileNormal = TempMesh::ComputePolygonNormal(profile->verts.data(), profile->verts.size()); + IfcMatrix4 proj_inv; ConvertAxisPlacement(proj_inv,hs->Position); @@ -361,256 +433,283 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded // clip the current contents of `meshout` against the plane we obtained from the second operand const std::vector& in = first_operand.verts; std::vector& outvert = result.verts; - - std::vector::const_iterator begin = first_operand.vertcnt.begin(), - end = first_operand.vertcnt.end(), iit; + std::vector& outvertcnt = result.vertcnt; outvert.reserve(in.size()); - result.vertcnt.reserve(first_operand.vertcnt.size()); + outvertcnt.reserve(first_operand.vertcnt.size()); - std::vector intersected_boundary_segments; - std::vector intersected_boundary_points; - - // TODO: the following algorithm doesn't handle all cases. unsigned int vidx = 0; - for(iit = begin; iit != end; vidx += *iit++) { - if (!*iit) { - continue; + std::vector::const_iterator begin = first_operand.vertcnt.begin(); + std::vector::const_iterator end = first_operand.vertcnt.end(); + std::vector::const_iterator iit; + for( iit = begin; iit != end; vidx += *iit++ ) + { + // Our new approach: we cut the poly along the plane, then we intersect the part on the black side of the plane + // against the bounding polygon. All the white parts, and the black part outside the boundary polygon, are kept. + std::vector whiteside, blackside; + + { + const IfcVector3* srcVertices = &in[vidx]; + const size_t srcVtxCount = *iit; + if( srcVtxCount == 0 ) + continue; + + IfcVector3 polyNormal = TempMesh::ComputePolygonNormal(srcVertices, srcVtxCount, true); + polyNormal = IfcMatrix3(proj) * polyNormal; + + // if the poly is parallel to the plane, put it completely on the black or white side + if( std::abs(polyNormal * n) > 0.9999 ) + { + bool isOnWhiteSide = ((proj * srcVertices[0]) - p) * n > -1e-6; + std::vector& targetSide = isOnWhiteSide ? whiteside : blackside; + targetSide.insert(targetSide.end(), srcVertices, srcVertices + srcVtxCount); + } + else + { + // otherwise start building one polygon for each side. Whenever the current line segment intersects the plane + // we put a point there as an end of the current segment. Then we switch to the other side, put a point there, too, + // as a beginning of the current segment, and simply continue accumulating vertices. + bool isCurrentlyOnWhiteSide = ((proj * srcVertices[0]) - p) * n > -1e-6; + for( size_t a = 0; a < srcVtxCount; ++a ) + { + IfcVector3 e0 = proj * srcVertices[a]; + IfcVector3 e1 = proj * srcVertices[(a + 1) % srcVtxCount]; + IfcVector3 ei; + + // put starting point to the current mesh + std::vector& trgt = isCurrentlyOnWhiteSide ? whiteside : blackside; + trgt.push_back(srcVertices[a]); + + // if there's an intersection, put an end vertex there, switch to the other side's mesh, + // and add a starting vertex there, too + bool isPlaneHit = IntersectSegmentPlane(p, n, e0, e1, isCurrentlyOnWhiteSide, ei); + if( isPlaneHit ) + { + IfcVector3 global_ei = proj_inv * ei; + if( trgt.empty() || (trgt.back() - global_ei).SquareLength() > 1e-12 ) + trgt.push_back(global_ei); + isCurrentlyOnWhiteSide = !isCurrentlyOnWhiteSide; + std::vector& newtrgt = isCurrentlyOnWhiteSide ? whiteside : blackside; + newtrgt.push_back(global_ei); + } + } + } } - unsigned int newcount = 0; - bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts); + // the part on the white side can be written into the target mesh right away + WritePolygon(whiteside, result); - // used any more? - //size_t last_intersected_boundary_segment; - IfcVector3 last_intersected_boundary_point; + // The black part is the piece we need to get rid of, but only the part of it within the boundary polygon. + // So we now need to construct all the polygons that result from BlackSidePoly minus BoundaryPoly. + FilterPolygon(blackside); - bool extra_point_flag = false; - IfcVector3 extra_point; + // Complicated, II. We run along the polygon. a) When we're inside the boundary, we run on until we hit an + // intersection, which means we're leaving it. We then start a new out poly there. b) When we're outside the + // boundary, we start collecting vertices until we hit an intersection, then we run along the boundary until we hit + // an intersection, then we switch back to the poly and run on on this one again, and so on until we got a closed + // loop. Then we continue with the path we left to catch potential additional polys on the other side of the + // boundary as described in a) + if( !blackside.empty() ) + { + // poly edge index, intersection point, edge index in boundary poly + std::vector > intersections; + bool startedInside = PointInPoly(proj * blackside.front(), profile->verts); + bool isCurrentlyInside = startedInside; - IfcVector3 enter_volume; - bool entered_volume_flag = false; + std::vector > intersected_boundary; - for(unsigned int i = 0; i < *iit; ++i) { - // current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set - const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i]; - const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit]; + for( size_t a = 0; a < blackside.size(); ++a ) + { + const IfcVector3 e0 = proj * blackside[a]; + const IfcVector3 e1 = proj * blackside[(a + 1) % blackside.size()]; - // does the current segment intersect the polygonal boundary? - const IfcVector3& e0_plane = proj * e0; - const IfcVector3& e1_plane = proj * e1; - - intersected_boundary_segments.clear(); - intersected_boundary_points.clear(); - - const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts); - const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary; - - IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts, - intersected_boundary_segments, - intersected_boundary_points); - - ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty()); - - // does the current segment intersect the plane? - // (no extra check if this is an extra point) - IfcVector3 isectpos; - const Intersect isect = extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos); - -#ifdef ASSIMP_BUILD_DEBUG - if (isect == Intersect_Yes) { - const IfcFloat f = std::fabs((isectpos - p)*n); - ai_assert(f < 1e-5); - } -#endif - - const bool is_white_side = (e0-p)*n >= -1e-6; - - // e0 on good side of plane? (i.e. we should keep all geometry on this side) - if (is_white_side) { - // but is there an intersection in e0-e1 and is e1 in the clipping - // boundary? In this case, generate a line that only goes to the - // intersection point. - if (isect == Intersect_Yes && !is_outside_boundary) { - outvert.push_back(e0); - ++newcount; - - outvert.push_back(isectpos); - ++newcount; - - /* - // this is, however, only a line that goes to the plane, but not - // necessarily to the point where the bounding volume on the - // black side of the plane is hit. So basically, we need another - // check for [isectpos-e1], which should yield an intersection - // point. - extra_point_flag = true; - extra_point = isectpos; - - was_outside_boundary = true; - continue; */ - - // [isectpos, enter_volume] potentially needs extra points. - // For this, we determine the intersection point with the - // bounding volume and project it onto the plane. - /* - const IfcVector3& enter_volume_proj = proj * enter_volume; - const IfcVector3& enter_isectpos = proj * isectpos; - - intersected_boundary_segments.clear(); - intersected_boundary_points.clear(); - - IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts, - intersected_boundary_segments, - intersected_boundary_points); - - if(!intersected_boundary_segments.empty()) { - - vec = vec + ((p - vec) * n) * n; + intersected_boundary.clear(); + IntersectsBoundaryProfile(e0, e1, profile->verts, isCurrentlyInside, intersected_boundary); + // sort the hits by distance from e0 to get the correct in/out/in sequence. Manually :-( I miss you, C++11. + if( intersected_boundary.size() > 1 ) + { + bool keepSorting = true; + while( keepSorting ) + { + keepSorting = false; + for( size_t b = 0; b < intersected_boundary.size() - 1; ++b ) + { + if( (intersected_boundary[b + 1].second - e0).SquareLength() < (intersected_boundary[b].second - e0).SquareLength() ) + { + keepSorting = true; + std::swap(intersected_boundary[b + 1], intersected_boundary[b]); + } + } } - */ - - //entered_volume_flag = true; } - else { - outvert.push_back(e0); - ++newcount; + // now add them to the list of intersections + for( size_t b = 0; b < intersected_boundary.size(); ++b ) + intersections.push_back(boost::make_tuple(a, proj_inv * intersected_boundary[b].second, intersected_boundary[b].first)); + + // and calculate our new inside/outside state + if( intersected_boundary.size() & 1 ) + isCurrentlyInside = !isCurrentlyInside; + } + + // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or + // we're fucked. + if( (intersections.size() & 1) != 0 ) + { + IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check."); + continue; + } + + if( intersections.size() > 1 ) + { + // If we started outside, the first intersection is a out->in intersection. Cycle them so that it + // starts with an intersection leaving the boundary + if( !startedInside ) + for( size_t b = 0; b < intersections.size() - 1; ++b ) + std::swap(intersections[b], intersections[(b + intersections.size() - 1) % intersections.size()]); + + // Filter pairs of out->in->out that lie too close to each other. + for( size_t a = 0; intersections.size() > 0 && a < intersections.size() - 1; /**/ ) + { + if( (intersections[a].get<1>() - intersections[(a + 1) % intersections.size()].get<1>()).SquareLength() < 1e-10 ) + intersections.erase(intersections.begin() + a, intersections.begin() + a + 2); + else + a++; + } + if( intersections.size() > 1 && (intersections.back().get<1>() - intersections.front().get<1>()).SquareLength() < 1e-10 ) + { + intersections.pop_back(); intersections.erase(intersections.begin()); } } - // e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side, - // but only if it is within the bounding volume). - else if (isect == Intersect_Yes) { - // is e0 within the clipping volume? Insert the intersection point - // of [e0,e1] and the plane instead of e0. - if(was_outside_boundary) { - outvert.push_back(e0); - } - else { - if(entered_volume_flag) { - const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n; - outvert.push_back(fix_point); - ++newcount; - } - outvert.push_back(isectpos); + + // no intersections at all: either completely inside the boundary, so everything gets discarded, or completely outside. + // in the latter case we're implementional lost. I'm simply going to ignore this, so a large poly will not get any + // holes if the boundary is smaller and does not touch it anywhere. + if( intersections.empty() ) + { + // starting point was outside -> everything is outside the boundary -> nothing is clipped -> add black side + // to result mesh unchanged + if( !startedInside ) + { + outvertcnt.push_back(blackside.size()); + outvert.insert(outvert.end(), blackside.begin(), blackside.end()); + continue; + } + else + { + // starting point was inside the boundary -> everything is inside the boundary -> nothing is spared from the + // clipping -> nothing left to add to the result mesh + continue; } - entered_volume_flag = false; - ++newcount; } - else { // no intersection with plane or parallel; e0,e1 are on the bad side - - // did we just pass the boundary line to the poly bounding? - if (is_boundary_intersection) { - // and are now outside the clipping boundary? - if (is_outside_boundary) { - // in this case, get the point where the clipping boundary - // was entered first. Then, get the point where the clipping - // boundary volume was left! These two points with the plane - // normal form another plane that intersects the clipping - // volume. There are two ways to get from the first to the - // second point along the intersection curve, try to pick the - // one that lies within the current polygon. + // determine the direction in which we're marching along the boundary polygon. If the src poly is faced upwards + // and the boundary is also winded this way, we need to march *backwards* on the boundary. + const IfcVector3 polyNormal = IfcMatrix3(proj) * TempMesh::ComputePolygonNormal(blackside.data(), blackside.size()); + bool marchBackwardsOnBoundary = (profileNormal * polyNormal) >= 0.0; - // TODO this approach doesn't handle all cases + // Build closed loops from these intersections. Starting from an intersection leaving the boundary we + // walk along the polygon to the next intersection (which should be an IS entering the boundary poly). + // From there we walk along the boundary until we hit another intersection leaving the boundary, + // walk along the poly to the next IS and so on until we're back at the starting point. + // We remove every intersection we "used up", so any remaining intersection is the start of a new loop. + while( !intersections.empty() ) + { + std::vector resultpoly; + size_t currentIntersecIdx = 0; - // ... + while( true ) + { + ai_assert(intersections.size() > currentIntersecIdx + 1); + boost::tuple currintsec = intersections[currentIntersecIdx + 0]; + boost::tuple nextintsec = intersections[currentIntersecIdx + 1]; + intersections.erase(intersections.begin() + currentIntersecIdx, intersections.begin() + currentIntersecIdx + 2); - IfcFloat d = 1e20; - IfcVector3 vclosest; - BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) { - const IfcFloat dn = (v-e1_plane).SquareLength(); - if (dn < d) { - d = dn; - vclosest = v; + // we start with an in->out intersection + resultpoly.push_back(currintsec.get<1>()); + // climb along the polygon to the next intersection, which should be an out->in + size_t numPolyPoints = (currintsec.get<0>() > nextintsec.get<0>() ? blackside.size() : 0) + + nextintsec.get<0>() - currintsec.get<0>(); + for( size_t a = 1; a <= numPolyPoints; ++a ) + resultpoly.push_back(blackside[(currintsec.get<0>() + a) % blackside.size()]); + // put the out->in intersection + resultpoly.push_back(nextintsec.get<1>()); + + // generate segments along the boundary polygon that lie in the poly's plane until we hit another intersection + IfcVector3 startingPoint = proj * nextintsec.get<1>(); + size_t currentBoundaryEdgeIdx = (nextintsec.get<2>() + (marchBackwardsOnBoundary ? 1 : 0)) % profile->verts.size(); + size_t nextIntsecIdx = SIZE_MAX; + while( nextIntsecIdx == SIZE_MAX ) + { + IfcFloat t = 1e10; + + size_t nextBoundaryEdgeIdx = marchBackwardsOnBoundary ? (currentBoundaryEdgeIdx + profile->verts.size() - 1) : currentBoundaryEdgeIdx + 1; + nextBoundaryEdgeIdx %= profile->verts.size(); + // vertices of the current boundary segments + IfcVector3 currBoundaryPoint = profile->verts[currentBoundaryEdgeIdx]; + IfcVector3 nextBoundaryPoint = profile->verts[nextBoundaryEdgeIdx]; + + // build a direction that goes along the boundary border but lies in the poly plane + IfcVector3 boundaryPlaneNormal = ((nextBoundaryPoint - currBoundaryPoint) ^ profileNormal).Normalize(); + IfcVector3 dirAtPolyPlane = (boundaryPlaneNormal ^ polyNormal).Normalize() * (marchBackwardsOnBoundary ? -1.0 : 1.0); + // if we can project the direction to the plane, we can calculate a maximum marching distance along that dir + // until we finish that boundary segment and continue on the next + if( std::abs(polyNormal.z) > 1e-5 ) + { + t = std::min(t, (nextBoundaryPoint - startingPoint).Length() / std::abs(polyNormal.z)); + } + + // check if the direction hits the loop start - if yes, we got a poly to output + IfcVector3 dirToThatPoint = proj * resultpoly.front() - startingPoint; + IfcFloat tpt = dirToThatPoint * dirAtPolyPlane; + if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 ) + { + nextIntsecIdx = intersections.size(); // dirty hack to end marching along the boundary and signal the end of the loop + t = tpt; + } + + // also check if the direction hits any in->out intersections earlier. If we hit one, we can switch back + // to marching along the poly border from that intersection point + for( size_t a = 0; a < intersections.size(); a += 2 ) + { + dirToThatPoint = proj * intersections[a].get<1>() - startingPoint; + tpt = dirToThatPoint * dirAtPolyPlane; + if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 ) + { + nextIntsecIdx = a; // switch back to poly and march on from this in->out intersection + t = tpt; } } - vclosest = proj_inv * vclosest; - if(entered_volume_flag) { - const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n; - outvert.push_back(fix_point); - ++newcount; - - entered_volume_flag = false; + // if we keep marching on the boundary, put the segment end point to the result poly and well... keep marching + if( nextIntsecIdx == SIZE_MAX ) + { + resultpoly.push_back(proj_inv * nextBoundaryPoint); + currentBoundaryEdgeIdx = nextBoundaryEdgeIdx; + startingPoint = nextBoundaryPoint; } - outvert.push_back(vclosest); - ++newcount; - - //outvert.push_back(e1); - //++newcount; - } - else { - entered_volume_flag = true; - - // we just entered the clipping boundary. Record the point - // and the segment where we entered and also generate this point. - //last_intersected_boundary_segment = intersected_boundary_segments.front(); - //last_intersected_boundary_point = intersected_boundary_points.front(); - - outvert.push_back(e0); - ++newcount; - - IfcFloat d = 1e20; - IfcVector3 vclosest; - BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) { - const IfcFloat dn = (v-e0_plane).SquareLength(); - if (dn < d) { - d = dn; - vclosest = v; - } + // quick endless loop check + if( resultpoly.size() > blackside.size() + profile->verts.size() ) + { + IFCImporter::LogError("Encountered endless loop while clipping polygon against poly-bounded half space."); + break; } - - enter_volume = proj_inv * vclosest; - outvert.push_back(enter_volume); - ++newcount; } - } - // if not, we just keep the vertex - else if (is_outside_boundary) { - outvert.push_back(e0); - ++newcount; - entered_volume_flag = false; + // we're back on the poly - if this is the intersection we started from, we got a closed loop. + if( nextIntsecIdx >= intersections.size() ) + { + break; + } + + // otherwise it's another intersection. Continue marching from there. + currentIntersecIdx = nextIntsecIdx; } + + WritePolygon(resultpoly, result); } - - was_outside_boundary = is_outside_boundary; - extra_point_flag = false; } - - if (!newcount) { - continue; - } - - IfcVector3 vmin,vmax; - ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax); - - // filter our IfcFloat points - those may happen if a point lies - // directly on the intersection line. However, due to IfcFloat - // precision a bitwise comparison is not feasible to detect - // this case. - const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f; - FuzzyVectorCompare fz(epsilon); - - std::vector::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz ); - - if (e != outvert.end()) { - newcount -= static_cast(std::distance(e,outvert.end())); - outvert.erase(e,outvert.end()); - } - if (fz(*( outvert.end()-newcount),outvert.back())) { - outvert.pop_back(); - --newcount; - } - if(newcount > 2) { - result.vertcnt.push_back(newcount); - } - else while(newcount-->0) { - result.verts.pop_back(); - } - } IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)"); } diff --git a/code/IFCGeometry.cpp b/code/IFCGeometry.cpp index e991f8e57..d0a55ad5b 100644 --- a/code/IFCGeometry.cpp +++ b/code/IFCGeometry.cpp @@ -579,6 +579,11 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul IfcVector3 min = in[0]; dir *= IfcMatrix3(trafo); + // reverse profile polygon if it's winded in the wrong direction in relation to the extrusion direction + IfcVector3 profileNormal = TempMesh::ComputePolygonNormal( in.data(), in.size()); + if( profileNormal * dir < 0.0 ) + std::reverse( in.begin(), in.end()); + std::vector nors; const bool openings = !!conv.apply_openings && conv.apply_openings->size(); @@ -619,9 +624,9 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul curmesh.vertcnt.push_back(4); out.push_back(in[i]); - out.push_back(in[i]+dir); - out.push_back(in[next]+dir); out.push_back(in[next]); + out.push_back(in[next]+dir); + out.push_back(in[i]+dir); if(openings) { if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) { @@ -646,8 +651,12 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul if(has_area) { for(size_t n = 0; n < 2; ++n) { - for(size_t i = size; i--; ) { - out.push_back(in[i]+(n?dir:IfcVector3())); + if( n > 0 ) { + for(size_t i = 0; i < size; ++i ) + out.push_back(in[i]+dir); + } else { + for(size_t i = size; i--; ) + out.push_back(in[i]); } curmesh.vertcnt.push_back(size); @@ -699,10 +708,10 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, } // ------------------------------------------------------------------------------------------------ -bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector& mesh_indices, +bool ProcessGeometricItem(const IfcRepresentationItem& geo, unsigned int matid, std::vector& mesh_indices, ConversionData& conv) { - bool fix_orientation = true; + bool fix_orientation = false; boost::shared_ptr< TempMesh > meshtmp = boost::make_shared(); if(const IfcShellBasedSurfaceModel* shellmod = geo.ToPtr()) { BOOST_FOREACH(boost::shared_ptr shell,shellmod->SbsmBoundary) { @@ -716,24 +725,27 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector()) { ProcessConnectedFaceSet(*fset,*meshtmp.get(),conv); + fix_orientation = true; } else if(const IfcSweptAreaSolid* swept = geo.ToPtr()) { ProcessSweptAreaSolid(*swept,*meshtmp.get(),conv); } else if(const IfcSweptDiskSolid* disk = geo.ToPtr()) { ProcessSweptDiskSolid(*disk,*meshtmp.get(),conv); - fix_orientation = false; } else if(const IfcManifoldSolidBrep* brep = geo.ToPtr()) { ProcessConnectedFaceSet(brep->Outer,*meshtmp.get(),conv); + fix_orientation = true; } else if(const IfcFaceBasedSurfaceModel* surf = geo.ToPtr()) { BOOST_FOREACH(const IfcConnectedFaceSet& fc, surf->FbsmFaces) { ProcessConnectedFaceSet(fc,*meshtmp.get(),conv); } + fix_orientation = true; } else if(const IfcBooleanResult* boolean = geo.ToPtr()) { ProcessBoolean(*boolean,*meshtmp.get(),conv); @@ -777,7 +789,7 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vectorToMesh(); if(mesh) { - mesh->mMaterialIndex = ProcessMaterials(geo,conv); + mesh->mMaterialIndex = matid; mesh_indices.push_back(conv.meshes.size()); conv.meshes.push_back(mesh); return true; @@ -807,10 +819,11 @@ void AssignAddedMeshes(std::vector& mesh_indices,aiNode* nd, // ------------------------------------------------------------------------------------------------ bool TryQueryMeshCache(const IfcRepresentationItem& item, - std::vector& mesh_indices, + std::vector& mesh_indices, unsigned int mat_index, ConversionData& conv) { - ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(&item); + ConversionData::MeshCacheIndex idx(&item, mat_index); + ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(idx); if (it != conv.cached_meshes.end()) { std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(mesh_indices)); return true; @@ -820,21 +833,25 @@ bool TryQueryMeshCache(const IfcRepresentationItem& item, // ------------------------------------------------------------------------------------------------ void PopulateMeshCache(const IfcRepresentationItem& item, - const std::vector& mesh_indices, + const std::vector& mesh_indices, unsigned int mat_index, ConversionData& conv) { - conv.cached_meshes[&item] = mesh_indices; + ConversionData::MeshCacheIndex idx(&item, mat_index); + conv.cached_meshes[idx] = mesh_indices; } // ------------------------------------------------------------------------------------------------ -bool ProcessRepresentationItem(const IfcRepresentationItem& item, +bool ProcessRepresentationItem(const IfcRepresentationItem& item, unsigned int matid, std::vector& mesh_indices, ConversionData& conv) { - if (!TryQueryMeshCache(item,mesh_indices,conv)) { - if(ProcessGeometricItem(item,mesh_indices,conv)) { + // determine material + unsigned int localmatid = ProcessMaterials(item.GetID(), matid, conv, true); + + if (!TryQueryMeshCache(item,mesh_indices,localmatid,conv)) { + if(ProcessGeometricItem(item,localmatid,mesh_indices,conv)) { if(mesh_indices.size()) { - PopulateMeshCache(item,mesh_indices,conv); + PopulateMeshCache(item,mesh_indices,localmatid,conv); } } else return false; diff --git a/code/IFCLoader.cpp b/code/IFCLoader.cpp index f6eedc6cb..3ae88a74e 100644 --- a/code/IFCLoader.cpp +++ b/code/IFCLoader.cpp @@ -428,7 +428,7 @@ void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv) } // ------------------------------------------------------------------------------------------------ -bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv) +bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, unsigned int matid, ConversionData& conv) { // insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix std::auto_ptr nd(new aiNode()); @@ -453,11 +453,12 @@ bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< } } + unsigned int localmatid = ProcessMaterials(mapped.GetID(),matid,conv,false); const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation; bool got = false; BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) { - if(!ProcessRepresentationItem(item,meshes,conv)) { + if(!ProcessRepresentationItem(item,localmatid,meshes,conv)) { IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated"); } else got = true; @@ -557,7 +558,11 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< if(!el.Representation) { return; } + + // extract Color from metadata, if present + unsigned int matid = ProcessMaterials( el.GetID(), UINT32_MAX, conv, false); std::vector meshes; + // we want only one representation type, so bring them in a suitable order (i.e try those // that look as if we could read them quickly at first). This way of reading // representation is relatively generic and allows the concrete implementations @@ -571,10 +576,10 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< bool res = false; BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) { if(const IfcMappedItem* const geo = item.ToPtr()) { - res = ProcessMappedItem(*geo,nd,subnodes,conv) || res; + res = ProcessMappedItem(*geo,nd,subnodes,matid,conv) || res; } else { - res = ProcessRepresentationItem(item,meshes,conv) || res; + res = ProcessRepresentationItem(item,matid,meshes,conv) || res; } } // if we got something meaningful at this point, skip any further representations diff --git a/code/IFCMaterial.cpp b/code/IFCMaterial.cpp index 081dd7e19..030603837 100644 --- a/code/IFCMaterial.cpp +++ b/code/IFCMaterial.cpp @@ -132,45 +132,70 @@ void FillMaterial(aiMaterial* mat,const IFC::IfcSurfaceStyle* surf,ConversionDat } // ------------------------------------------------------------------------------------------------ -unsigned int ProcessMaterials(const IFC::IfcRepresentationItem& item, ConversionData& conv) +unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat) { - if (conv.materials.empty()) { - aiString name; - std::auto_ptr mat(new aiMaterial()); - - name.Set(""); - mat->AddProperty(&name,AI_MATKEY_NAME); - - const aiColor4D col = aiColor4D(0.6f,0.6f,0.6f,1.0f); - mat->AddProperty(&col,1, AI_MATKEY_COLOR_DIFFUSE); - - conv.materials.push_back(mat.release()); - } - - STEP::DB::RefMapRange range = conv.db.GetRefs().equal_range(item.GetID()); + STEP::DB::RefMapRange range = conv.db.GetRefs().equal_range(id); for(;range.first != range.second; ++range.first) { if(const IFC::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr()) { BOOST_FOREACH(const IFC::IfcPresentationStyleAssignment& as, styled->Styles) { BOOST_FOREACH(boost::shared_ptr sel, as.Styles) { - if (const IFC::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr(conv.db)) { + if( const IFC::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr(conv.db) ) { + // try to satisfy from cache + ConversionData::MaterialCache::iterator mit = conv.cached_materials.find(surf); + if( mit != conv.cached_materials.end() ) + return mit->second; + + // not found, create new material const std::string side = static_cast(surf->Side); - if (side != "BOTH") { + if( side != "BOTH" ) { IFCImporter::LogWarn("ignoring surface side marker on IFC::IfcSurfaceStyle: " + side); } std::auto_ptr mat(new aiMaterial()); - FillMaterial(mat.get(),surf,conv); + FillMaterial(mat.get(), surf, conv); conv.materials.push_back(mat.release()); - return conv.materials.size()-1; - } - } + unsigned int matindex = conv.materials.size() - 1; + conv.cached_materials[surf] = matindex; + return matindex; } } } - return 0; + } + } + + // no local material defined. If there's global one, use that instead + if( prevMatId != UINT32_MAX ) + return prevMatId; + + // we're still here - create an default material if required, or simply fail otherwise + if( !forceDefaultMat ) + return UINT32_MAX; + + aiString name; + name.Set(""); + // ConvertColorToString( color, name); + + // look if there's already a default material with this base color + for( size_t a = 0; a < conv.materials.size(); ++a ) + { + aiString mname; + conv.materials[a]->Get(AI_MATKEY_NAME, mname); + if( name == mname ) + return (unsigned int)a; + } + + // we're here, yet - no default material with suitable color available. Generate one + std::auto_ptr mat(new aiMaterial()); + mat->AddProperty(&name,AI_MATKEY_NAME); + + const aiColor4D col = aiColor4D( 0.6f, 0.6f, 0.6f, 1.0f); // aiColor4D( color.r, color.g, color.b, 1.0f); + mat->AddProperty(&col,1, AI_MATKEY_COLOR_DIFFUSE); + + conv.materials.push_back(mat.release()); + return (unsigned int) conv.materials.size() - 1; } } // ! IFC diff --git a/code/IFCOpenings.cpp b/code/IFCOpenings.cpp index fb5d432fd..2bb7646b2 100644 --- a/code/IFCOpenings.cpp +++ b/code/IFCOpenings.cpp @@ -902,6 +902,14 @@ size_t CloseWindows(ContourVector& contours, curmesh.verts.reserve(curmesh.verts.size() + (*it).contour.size() * 4); curmesh.vertcnt.reserve(curmesh.vertcnt.size() + (*it).contour.size()); + // compare base poly normal and contour normal to detect if we need to reverse the face winding + IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal( curmesh.verts.data(), curmesh.vertcnt.front()); + std::vector worldSpaceContourVtx( it->contour.size()); + for( size_t a = 0; a < it->contour.size(); ++a ) + worldSpaceContourVtx[a] = minv * IfcVector3( it->contour[a].x, it->contour[a].y, 0.0); + IfcVector3 contourNormal = TempMesh::ComputePolygonNormal( worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); + bool reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; + // XXX this algorithm is really a bit inefficient - both in terms // of constant factor and of asymptotic runtime. std::vector::const_iterator skipit = skipbegin; @@ -909,9 +917,6 @@ size_t CloseWindows(ContourVector& contours, IfcVector3 start0; IfcVector3 start1; - IfcVector2 last_proj; - //const IfcVector2& first_proj; - const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end(); bool drop_this_edge = false; @@ -923,18 +928,8 @@ size_t CloseWindows(ContourVector& contours, IfcFloat best = static_cast(1e10); IfcVector3 bestv; - /* debug code to check for unwanted diagonal lines in window contours - if (cit != cbegin) { - const IfcVector2& vdelta = proj_point - last_proj; - if (std::fabs(vdelta.x-vdelta.y) < 0.5 * std::max(vdelta.x, vdelta.y)) { - //continue; - } - } */ - const IfcVector3& world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f); - last_proj = proj_point; - BOOST_FOREACH(const TempOpening* opening, refs) { BOOST_FOREACH(const IfcVector3& other, opening->wallPoints) { const IfcFloat sqdist = (world_point - other).SquareLength(); @@ -956,8 +951,8 @@ size_t CloseWindows(ContourVector& contours, curmesh.verts.pop_back(); } else { - curmesh.verts.push_back(cit == cbegin ? world_point : bestv); - curmesh.verts.push_back(cit == cbegin ? bestv : world_point); + curmesh.verts.push_back(((cit == cbegin) != reverseCountourFaces) ? world_point : bestv); + curmesh.verts.push_back(((cit == cbegin) != reverseCountourFaces) ? bestv : world_point); curmesh.vertcnt.push_back(4); ++closed; @@ -969,8 +964,8 @@ size_t CloseWindows(ContourVector& contours, continue; } - curmesh.verts.push_back(world_point); - curmesh.verts.push_back(bestv); + curmesh.verts.push_back(reverseCountourFaces ? bestv : world_point); + curmesh.verts.push_back(reverseCountourFaces ? world_point : bestv); if (cit == cend - 1) { drop_this_edge = *skipit; @@ -984,16 +979,11 @@ size_t CloseWindows(ContourVector& contours, curmesh.verts.pop_back(); } else { - curmesh.verts.push_back(start1); - curmesh.verts.push_back(start0); + curmesh.verts.push_back(reverseCountourFaces ? start0 : start1); + curmesh.verts.push_back(reverseCountourFaces ? start1 : start0); } } } - /* - BOOST_FOREACH(TempOpening* opening, refs) { - //opening->wallPoints.clear(); - }*/ - } else { diff --git a/code/IFCUtil.cpp b/code/IFCUtil.cpp index 38fd400e0..90489ef91 100644 --- a/code/IFCUtil.cpp +++ b/code/IFCUtil.cpp @@ -166,6 +166,23 @@ void TempMesh::RemoveDegenerates() } } +// ------------------------------------------------------------------------------------------------ +IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize) +{ + std::vector temp((cnt+2)*3); + for( size_t vofs = 0, i = 0; vofs < cnt; ++vofs ) + { + const IfcVector3& v = vtcs[vofs]; + temp[i++] = v.x; + temp[i++] = v.y; + temp[i++] = v.z; + } + + IfcVector3 nor; + NewellNormal<3, 3, 3>(nor, cnt, &temp[0], &temp[1], &temp[2]); + return normalize ? nor.Normalize() : nor; +} + // ------------------------------------------------------------------------------------------------ void TempMesh::ComputePolygonNormals(std::vector& normals, bool normalize, @@ -214,37 +231,148 @@ void TempMesh::ComputePolygonNormals(std::vector& normals, // Compute the normal of the last polygon in the given mesh IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const { - size_t total = vertcnt.back(), vidx = verts.size() - total; - std::vector temp((total+2)*3); - for(size_t vofs = 0, cnt = 0; vofs < total; ++vofs) { - const IfcVector3& v = verts[vidx+vofs]; - temp[cnt++] = v.x; - temp[cnt++] = v.y; - temp[cnt++] = v.z; - } - IfcVector3 nor; - NewellNormal<3,3,3>(nor,total,&temp[0],&temp[1],&temp[2]); - return normalize ? nor.Normalize() : nor; + return ComputePolygonNormal(&verts[verts.size() - vertcnt.back()], vertcnt.back(), normalize); } +struct CompareVector +{ + bool operator () (const IfcVector3& a, const IfcVector3& b) + { + IfcVector3 d = a - b; + IfcFloat eps = 1e-6; + return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps); + } +}; +struct FindVector +{ + IfcVector3 v; + FindVector(const IfcVector3& p) : v(p) { } + bool operator () (const IfcVector3& p) { return FuzzyVectorCompare(1e-6)(p, v); } +}; + // ------------------------------------------------------------------------------------------------ void TempMesh::FixupFaceOrientation() { const IfcVector3 vavg = Center(); - std::vector normals; - ComputePolygonNormals(normals); + // create a list of start indices for all faces to allow random access to faces + std::vector faceStartIndices(vertcnt.size()); + for( size_t i = 0, a = 0; a < vertcnt.size(); i += vertcnt[a], ++a ) + faceStartIndices[a] = i; - size_t c = 0, ofs = 0; - BOOST_FOREACH(unsigned int cnt, vertcnt) { - if (cnt>2){ - const IfcVector3& thisvert = verts[c]; - if (normals[ofs]*(thisvert-vavg) < 0) { - std::reverse(verts.begin()+c,verts.begin()+cnt+c); + // list all faces on a vertex + std::map, CompareVector> facesByVertex; + for( size_t a = 0; a < vertcnt.size(); ++a ) + { + for( size_t b = 0; b < vertcnt[a]; ++b ) + facesByVertex[verts[faceStartIndices[a] + b]].push_back(a); + } + // determine neighbourhood for all polys + std::vector neighbour(verts.size(), SIZE_MAX); + std::vector tempIntersect(10); + for( size_t a = 0; a < vertcnt.size(); ++a ) + { + for( size_t b = 0; b < vertcnt[a]; ++b ) + { + size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % vertcnt[a]; + const std::vector& facesOnB = facesByVertex[verts[ib]]; + const std::vector& facesOnNB = facesByVertex[verts[nib]]; + // there should be exactly one or two faces which appear in both lists. Our face and the other side + std::vector::iterator sectstart = tempIntersect.begin(); + std::vector::iterator sectend = std::set_intersection( + facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart); + + if( std::distance(sectstart, sectend) != 2 ) + continue; + if( *sectstart == a ) + ++sectstart; + neighbour[ib] = *sectstart; + } + } + + // now we're getting started. We take the face which is the farthest away from the center. This face is most probably + // facing outwards. So we reverse this face to point outwards in relation to the center. Then we adapt neighbouring + // faces to have the same winding until all faces have been tested. + std::vector faceDone(vertcnt.size(), false); + while( std::count(faceDone.begin(), faceDone.end(), false) != 0 ) + { + // find the farthest of the remaining faces + size_t farthestIndex = SIZE_MAX; + IfcFloat farthestDistance = -1.0; + for( size_t a = 0; a < vertcnt.size(); ++a ) + { + if( faceDone[a] ) + continue; + IfcVector3 faceCenter = std::accumulate(verts.begin() + faceStartIndices[a], + verts.begin() + faceStartIndices[a] + vertcnt[a], IfcVector3(0.0)) / IfcFloat(vertcnt[a]); + IfcFloat dst = (faceCenter - vavg).SquareLength(); + if( dst > farthestDistance ) { farthestDistance = dst; farthestIndex = a; } + } + + // calculate its normal and reverse the poly if its facing towards the mesh center + IfcVector3 farthestNormal = ComputePolygonNormal(verts.data() + faceStartIndices[farthestIndex], vertcnt[farthestIndex]); + IfcVector3 farthestCenter = std::accumulate(verts.begin() + faceStartIndices[farthestIndex], + verts.begin() + faceStartIndices[farthestIndex] + vertcnt[farthestIndex], IfcVector3(0.0)) + / IfcFloat(vertcnt[farthestIndex]); + // We accapt a bit of negative orientation without reversing. In case of doubt, prefer the orientation given in + // the file. + if( (farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4 ) + { + size_t fsi = faceStartIndices[farthestIndex], fvc = vertcnt[farthestIndex]; + std::reverse(verts.begin() + fsi, verts.begin() + fsi + fvc); + std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc); + // because of the neighbour index belonging to the edge starting with the point at the same index, we need to + // cycle the neighbours through to match the edges again. + // Before: points A - B - C - D with edge neighbour p - q - r - s + // After: points D - C - B - A, reversed neighbours are s - r - q - p, but the should be + // r q p s + for( size_t a = 0; a < fvc - 1; ++a ) + std::swap(neighbour[fsi + a], neighbour[fsi + a + 1]); + } + faceDone[farthestIndex] = true; + std::vector todo; + todo.push_back(farthestIndex); + + // go over its neighbour faces recursively and adapt their winding order to match the farthest face + while( !todo.empty() ) + { + size_t tdf = todo.back(); + size_t vsi = faceStartIndices[tdf], vc = vertcnt[tdf]; + todo.pop_back(); + + // check its neighbours + for( size_t a = 0; a < vc; ++a ) + { + // ignore neighbours if we already checked them + size_t nbi = neighbour[vsi + a]; + if( nbi == SIZE_MAX || faceDone[nbi] ) + continue; + + const IfcVector3& vp = verts[vsi + a]; + size_t nbvsi = faceStartIndices[nbi], nbvc = vertcnt[nbi]; + std::vector::iterator it = std::find_if(verts.begin() + nbvsi, verts.begin() + nbvsi + nbvc, FindVector(vp)); + ai_assert(it != verts.begin() + nbvsi + nbvc); + size_t nb_vidx = std::distance(verts.begin() + nbvsi, it); + // two faces winded in the same direction should have a crossed edge, where one face has p0->p1 and the other + // has p1'->p0'. If the next point on the neighbouring face is also the next on the current face, we need + // to reverse the neighbour + nb_vidx = (nb_vidx + 1) % nbvc; + size_t oursideidx = (a + 1) % vc; + if( FuzzyVectorCompare(1e-6)(verts[vsi + oursideidx], verts[nbvsi + nb_vidx]) ) + { + std::reverse(verts.begin() + nbvsi, verts.begin() + nbvsi + nbvc); + std::reverse(neighbour.begin() + nbvsi, neighbour.begin() + nbvsi + nbvc); + for( size_t a = 0; a < nbvc - 1; ++a ) + std::swap(neighbour[nbvsi + a], neighbour[nbvsi + a + 1]); + } + + // either way we're done with the neighbour. Mark it as done and continue checking from there recursively + faceDone[nbi] = true; + todo.push_back(nbi); } } - c += cnt; - ++ofs; + + // no more faces reachable from this part of the surface, start over with a disjunct part and its farthest face } } diff --git a/code/IFCUtil.h b/code/IFCUtil.h index 20826f121..8d329c5d6 100644 --- a/code/IFCUtil.h +++ b/code/IFCUtil.h @@ -97,10 +97,10 @@ struct TempMesh void RemoveDegenerates(); void FixupFaceOrientation(); + + static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true); IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const; - void ComputePolygonNormals(std::vector& normals, - bool normalize = true, - size_t ofs = 0) const; + void ComputePolygonNormals(std::vector& normals, bool normalize = true, size_t ofs = 0) const; void Swap(TempMesh& other); }; @@ -195,9 +195,19 @@ struct ConversionData std::vector meshes; std::vector materials; - typedef std::map > MeshCache; + struct MeshCacheIndex { + const IFC::IfcRepresentationItem* item; unsigned int matindex; + MeshCacheIndex() : item(NULL), matindex(0) { } + MeshCacheIndex(const IFC::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { } + bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; } + bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); } + }; + typedef std::map > MeshCache; MeshCache cached_meshes; + typedef std::map MaterialCache; + MaterialCache cached_materials; + const IFCImporter::Settings& settings; // Intermediate arrays used to resolve openings in walls: only one of them @@ -220,7 +230,7 @@ struct FuzzyVectorCompare { FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {} bool operator()(const IfcVector3& a, const IfcVector3& b) { - return std::fabs((a-b).SquareLength()) < epsilon; + return std::abs((a-b).SquareLength()) < epsilon; } const IfcFloat epsilon; @@ -263,11 +273,11 @@ IfcFloat ConvertSIPrefix(const std::string& prefix); bool ProcessProfile(const IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv); // IFCMaterial.cpp -unsigned int ProcessMaterials(const IFC::IfcRepresentationItem& item, ConversionData& conv); +unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat); // IFCGeometry.cpp IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut); -bool ProcessRepresentationItem(const IfcRepresentationItem& item, std::vector& mesh_indices, ConversionData& conv); +bool ProcessRepresentationItem(const IfcRepresentationItem& item, unsigned int matid, std::vector& mesh_indices, ConversionData& conv); void AssignAddedMeshes(std::vector& mesh_indices,aiNode* nd,ConversionData& /*conv*/); void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index dff195854..42b684a5a 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -170,6 +170,10 @@ corresponding preprocessor flag to selectively disable formats. # include "AssbinLoader.h" #endif +#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER +# include "C4DImporter.h" +#endif + namespace Assimp { // ------------------------------------------------------------------------------------------------ @@ -297,6 +301,10 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out) #if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER ) out.push_back( new AssbinImporter() ); #endif + +#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER + out.push_back( new C4DImporter() ); +#endif } } From e8bcad0840d84899d3c9e323dbe1efcaa4423d0b Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 13:17:47 +0200 Subject: [PATCH 05/17] Silence a warning and add an assertion Another false positive uninitialized variable. Actually it's only false if ToOutputVertexIndex does't return NULL which should only happen if index is out of bounds. Add assertion to make sure of that. --- code/FBXConverter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 9ad6a2f16..7bd15736f 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -1256,8 +1256,11 @@ private: // taking notes so we don't need to do it twice. BOOST_FOREACH(WeightIndexArray::value_type index, indices) { - unsigned int count; + unsigned int count = 0; const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); + // ToOutputVertexIndex only returns NULL if index is out of bounds + // which should never happen + ai_assert(out_idx != NULL); index_out_indices.push_back(no_index_sentinel); count_out_indices.push_back(0); From 308884f144b0f6648e7ae21e75427cf666befcff Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 13:54:06 +0200 Subject: [PATCH 06/17] Remove unnecessary semicolons after function definition Picked up by clang -Wextra-semi --- code/AssbinExporter.cpp | 10 +++++----- code/irrXMLWrapper.h | 2 +- include/assimp/Importer.hpp | 4 ++-- include/assimp/ProgressHandler.hpp | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index b1999d2b1..6b4da3e30 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -273,13 +273,13 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size) if (buffer) delete[] buffer; } - void * GetBufferPointer() { return buffer; }; + void * GetBufferPointer() { return buffer; } // ------------------------------------------------------------------- - virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) { return 0; }; - virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) { return aiReturn_FAILURE; }; - virtual size_t Tell() const { return cursor; }; - virtual void Flush() { }; + virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) { return 0; } + virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) { return aiReturn_FAILURE; } + virtual size_t Tell() const { return cursor; } + virtual void Flush() { } virtual size_t FileSize() const { diff --git a/code/irrXMLWrapper.h b/code/irrXMLWrapper.h index 569347e2b..134c2f8dd 100644 --- a/code/irrXMLWrapper.h +++ b/code/irrXMLWrapper.h @@ -102,7 +102,7 @@ public: // ---------------------------------------------------------------------------------- //! Virtual destructor - virtual ~CIrrXML_IOStreamReader() {}; + virtual ~CIrrXML_IOStreamReader() {} // ---------------------------------------------------------------------------------- //! Reads an amount of bytes from the file. diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 5e7797b99..7138ddc7d 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -624,8 +624,8 @@ public: // ------------------------------------------------------------------- /** Private, do not use. */ - ImporterPimpl* Pimpl() { return pimpl; }; - const ImporterPimpl* Pimpl() const { return pimpl; }; + ImporterPimpl* Pimpl() { return pimpl; } + const ImporterPimpl* Pimpl() const { return pimpl; } protected: diff --git a/include/assimp/ProgressHandler.hpp b/include/assimp/ProgressHandler.hpp index 3ab1e489b..be1dbd75e 100644 --- a/include/assimp/ProgressHandler.hpp +++ b/include/assimp/ProgressHandler.hpp @@ -99,7 +99,7 @@ public: virtual void UpdateFileRead(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; Update( f * 0.5f ); - }; + } // ------------------------------------------------------------------- /** @brief Progress callback for post-processing steps @@ -113,7 +113,7 @@ public: virtual void UpdatePostProcess(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; Update( f * 0.5f + 0.5f ); - }; + } }; // !class ProgressHandler // ------------------------------------------------------------------------------------ From dfd171be6d5e2c13bef14cd6752a6ef344210d48 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 13:54:43 +0200 Subject: [PATCH 07/17] Remove unnecessary semicolons after FBXDocument property declarations --- code/FBXDocument.h | 240 ++++++++++++++++++++++----------------------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/code/FBXDocument.h b/code/FBXDocument.h index 8cf8202a1..b4099550b 100644 --- a/code/FBXDocument.h +++ b/code/FBXDocument.h @@ -242,20 +242,20 @@ public: public: - fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0)); - fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0)); - fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0)); + fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0)) + fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0)) + fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0)) - fbx_simple_property(AspectWidth, float, 1.0f); - fbx_simple_property(AspectHeight, float, 1.0f); - fbx_simple_property(FilmWidth, float, 1.0f); - fbx_simple_property(FilmHeight, float, 1.0f); + fbx_simple_property(AspectWidth, float, 1.0f) + fbx_simple_property(AspectHeight, float, 1.0f) + fbx_simple_property(FilmWidth, float, 1.0f) + fbx_simple_property(FilmHeight, float, 1.0f) - fbx_simple_property(FilmAspectRatio, float, 1.0f); - fbx_simple_property(ApertureMode, int, 0); + fbx_simple_property(FilmAspectRatio, float, 1.0f) + fbx_simple_property(ApertureMode, int, 0) - fbx_simple_property(FieldOfView, float, 1.0f); - fbx_simple_property(FocalLength, float, 1.0f); + fbx_simple_property(FieldOfView, float, 1.0f) + fbx_simple_property(FocalLength, float, 1.0f) private: }; @@ -314,37 +314,37 @@ public: public: - fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1)); - fbx_simple_enum_property(LightType, Type, 0); - fbx_simple_property(CastLightOnObject, bool, false); - fbx_simple_property(DrawVolumetricLight, bool, true); - fbx_simple_property(DrawGroundProjection, bool, true); - fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false); - fbx_simple_property(Intensity, float, 1.0f); - fbx_simple_property(InnerAngle, float, 0.0f); - fbx_simple_property(OuterAngle, float, 45.0f); - fbx_simple_property(Fog, int, 50); - fbx_simple_enum_property(DecayType, Decay, 0); - fbx_simple_property(DecayStart, int, 0); - fbx_simple_property(FileName, std::string, ""); + fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1)) + fbx_simple_enum_property(LightType, Type, 0) + fbx_simple_property(CastLightOnObject, bool, false) + fbx_simple_property(DrawVolumetricLight, bool, true) + fbx_simple_property(DrawGroundProjection, bool, true) + fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false) + fbx_simple_property(Intensity, float, 1.0f) + fbx_simple_property(InnerAngle, float, 0.0f) + fbx_simple_property(OuterAngle, float, 45.0f) + fbx_simple_property(Fog, int, 50) + fbx_simple_enum_property(DecayType, Decay, 0) + fbx_simple_property(DecayStart, int, 0) + fbx_simple_property(FileName, std::string, "") - fbx_simple_property(EnableNearAttenuation, bool, false); - fbx_simple_property(NearAttenuationStart, float, 0.0f); - fbx_simple_property(NearAttenuationEnd, float, 0.0f); - fbx_simple_property(EnableFarAttenuation, bool, false); - fbx_simple_property(FarAttenuationStart, float, 0.0f); - fbx_simple_property(FarAttenuationEnd, float, 0.0f); + fbx_simple_property(EnableNearAttenuation, bool, false) + fbx_simple_property(NearAttenuationStart, float, 0.0f) + fbx_simple_property(NearAttenuationEnd, float, 0.0f) + fbx_simple_property(EnableFarAttenuation, bool, false) + fbx_simple_property(FarAttenuationStart, float, 0.0f) + fbx_simple_property(FarAttenuationEnd, float, 0.0f) - fbx_simple_property(CastShadows, bool, true); - fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0)); + fbx_simple_property(CastShadows, bool, true) + fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0)) - fbx_simple_property(AreaLightShape, int, 0); + fbx_simple_property(AreaLightShape, int, 0) - fbx_simple_property(LeftBarnDoor, float, 20.0f); - fbx_simple_property(RightBarnDoor, float, 20.0f); - fbx_simple_property(TopBarnDoor, float, 20.0f); - fbx_simple_property(BottomBarnDoor, float, 20.0f); - fbx_simple_property(EnableBarnDoor, bool, true); + fbx_simple_property(LeftBarnDoor, float, 20.0f) + fbx_simple_property(RightBarnDoor, float, 20.0f) + fbx_simple_property(TopBarnDoor, float, 20.0f) + fbx_simple_property(BottomBarnDoor, float, 20.0f) + fbx_simple_property(EnableBarnDoor, bool, true) private: @@ -387,81 +387,81 @@ public: public: - fbx_simple_property(QuaternionInterpolate, int, 0); + fbx_simple_property(QuaternionInterpolate, int, 0) - fbx_simple_property(RotationOffset, aiVector3D, aiVector3D()); - fbx_simple_property(RotationPivot, aiVector3D, aiVector3D()); - fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D()); - fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D()); - fbx_simple_property(TranslationActive, bool, false); + fbx_simple_property(RotationOffset, aiVector3D, aiVector3D()) + fbx_simple_property(RotationPivot, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D()) + fbx_simple_property(TranslationActive, bool, false) - fbx_simple_property(TranslationMin, aiVector3D, aiVector3D()); - fbx_simple_property(TranslationMax, aiVector3D, aiVector3D()); + fbx_simple_property(TranslationMin, aiVector3D, aiVector3D()) + fbx_simple_property(TranslationMax, aiVector3D, aiVector3D()) - fbx_simple_property(TranslationMinX, bool, false); - fbx_simple_property(TranslationMaxX, bool, false); - fbx_simple_property(TranslationMinY, bool, false); - fbx_simple_property(TranslationMaxY, bool, false); - fbx_simple_property(TranslationMinZ, bool, false); - fbx_simple_property(TranslationMaxZ, bool, false); + fbx_simple_property(TranslationMinX, bool, false) + fbx_simple_property(TranslationMaxX, bool, false) + fbx_simple_property(TranslationMinY, bool, false) + fbx_simple_property(TranslationMaxY, bool, false) + fbx_simple_property(TranslationMinZ, bool, false) + fbx_simple_property(TranslationMaxZ, bool, false) - fbx_simple_enum_property(RotationOrder, RotOrder, 0); - fbx_simple_property(RotationSpaceForLimitOnly, bool, false); - fbx_simple_property(RotationStiffnessX, float, 0.0f); - fbx_simple_property(RotationStiffnessY, float, 0.0f); - fbx_simple_property(RotationStiffnessZ, float, 0.0f); - fbx_simple_property(AxisLen, float, 0.0f); + fbx_simple_enum_property(RotationOrder, RotOrder, 0) + fbx_simple_property(RotationSpaceForLimitOnly, bool, false) + fbx_simple_property(RotationStiffnessX, float, 0.0f) + fbx_simple_property(RotationStiffnessY, float, 0.0f) + fbx_simple_property(RotationStiffnessZ, float, 0.0f) + fbx_simple_property(AxisLen, float, 0.0f) - fbx_simple_property(PreRotation, aiVector3D, aiVector3D()); - fbx_simple_property(PostRotation, aiVector3D, aiVector3D()); - fbx_simple_property(RotationActive, bool, false); + fbx_simple_property(PreRotation, aiVector3D, aiVector3D()) + fbx_simple_property(PostRotation, aiVector3D, aiVector3D()) + fbx_simple_property(RotationActive, bool, false) - fbx_simple_property(RotationMin, aiVector3D, aiVector3D()); - fbx_simple_property(RotationMax, aiVector3D, aiVector3D()); + fbx_simple_property(RotationMin, aiVector3D, aiVector3D()) + fbx_simple_property(RotationMax, aiVector3D, aiVector3D()) - fbx_simple_property(RotationMinX, bool, false); - fbx_simple_property(RotationMaxX, bool, false); - fbx_simple_property(RotationMinY, bool, false); - fbx_simple_property(RotationMaxY, bool, false); - fbx_simple_property(RotationMinZ, bool, false); - fbx_simple_property(RotationMaxZ, bool, false); - fbx_simple_enum_property(InheritType, TransformInheritance, 0); + fbx_simple_property(RotationMinX, bool, false) + fbx_simple_property(RotationMaxX, bool, false) + fbx_simple_property(RotationMinY, bool, false) + fbx_simple_property(RotationMaxY, bool, false) + fbx_simple_property(RotationMinZ, bool, false) + fbx_simple_property(RotationMaxZ, bool, false) + fbx_simple_enum_property(InheritType, TransformInheritance, 0) - fbx_simple_property(ScalingActive, bool, false); - fbx_simple_property(ScalingMin, aiVector3D, aiVector3D()); - fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f)); - fbx_simple_property(ScalingMinX, bool, false); - fbx_simple_property(ScalingMaxX, bool, false); - fbx_simple_property(ScalingMinY, bool, false); - fbx_simple_property(ScalingMaxY, bool, false); - fbx_simple_property(ScalingMinZ, bool, false); - fbx_simple_property(ScalingMaxZ, bool, false); + fbx_simple_property(ScalingActive, bool, false) + fbx_simple_property(ScalingMin, aiVector3D, aiVector3D()) + fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f)) + fbx_simple_property(ScalingMinX, bool, false) + fbx_simple_property(ScalingMaxX, bool, false) + fbx_simple_property(ScalingMinY, bool, false) + fbx_simple_property(ScalingMaxY, bool, false) + fbx_simple_property(ScalingMinZ, bool, false) + fbx_simple_property(ScalingMaxZ, bool, false) - fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D()); - fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D()); - fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f)); + fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D()) + fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D()) + fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f)) - fbx_simple_property(MinDampRangeX, float, 0.0f); - fbx_simple_property(MinDampRangeY, float, 0.0f); - fbx_simple_property(MinDampRangeZ, float, 0.0f); - fbx_simple_property(MaxDampRangeX, float, 0.0f); - fbx_simple_property(MaxDampRangeY, float, 0.0f); - fbx_simple_property(MaxDampRangeZ, float, 0.0f); + fbx_simple_property(MinDampRangeX, float, 0.0f) + fbx_simple_property(MinDampRangeY, float, 0.0f) + fbx_simple_property(MinDampRangeZ, float, 0.0f) + fbx_simple_property(MaxDampRangeX, float, 0.0f) + fbx_simple_property(MaxDampRangeY, float, 0.0f) + fbx_simple_property(MaxDampRangeZ, float, 0.0f) - fbx_simple_property(MinDampStrengthX, float, 0.0f); - fbx_simple_property(MinDampStrengthY, float, 0.0f); - fbx_simple_property(MinDampStrengthZ, float, 0.0f); - fbx_simple_property(MaxDampStrengthX, float, 0.0f); - fbx_simple_property(MaxDampStrengthY, float, 0.0f); - fbx_simple_property(MaxDampStrengthZ, float, 0.0f); + fbx_simple_property(MinDampStrengthX, float, 0.0f) + fbx_simple_property(MinDampStrengthY, float, 0.0f) + fbx_simple_property(MinDampStrengthZ, float, 0.0f) + fbx_simple_property(MaxDampStrengthX, float, 0.0f) + fbx_simple_property(MaxDampStrengthY, float, 0.0f) + fbx_simple_property(MaxDampStrengthZ, float, 0.0f) - fbx_simple_property(PreferredAngleX, float, 0.0f); - fbx_simple_property(PreferredAngleY, float, 0.0f); - fbx_simple_property(PreferredAngleZ, float, 0.0f); + fbx_simple_property(PreferredAngleX, float, 0.0f) + fbx_simple_property(PreferredAngleY, float, 0.0f) + fbx_simple_property(PreferredAngleZ, float, 0.0f) - fbx_simple_property(Show, bool, true); - fbx_simple_property(LODBox, bool, false); - fbx_simple_property(Freeze, bool, false); + fbx_simple_property(Show, bool, true) + fbx_simple_property(LODBox, bool, false) + fbx_simple_property(Freeze, bool, false) public: @@ -1015,10 +1015,10 @@ public: public: - fbx_simple_property(LocalStart, uint64_t, 0L); - fbx_simple_property(LocalStop, uint64_t, 0L); - fbx_simple_property(ReferenceStart, uint64_t, 0L); - fbx_simple_property(ReferenceStop, uint64_t, 0L); + fbx_simple_property(LocalStart, uint64_t, 0L) + fbx_simple_property(LocalStop, uint64_t, 0L) + fbx_simple_property(ReferenceStart, uint64_t, 0L) + fbx_simple_property(ReferenceStop, uint64_t, 0L) @@ -1227,18 +1227,18 @@ public: } - fbx_simple_property(UpAxis, int, 1); - fbx_simple_property(UpAxisSign, int, 1); - fbx_simple_property(FrontAxis, int, 2); - fbx_simple_property(FrontAxisSign, int, 1); - fbx_simple_property(CoordAxis, int, 0); - fbx_simple_property(CoordAxisSign, int, 1); - fbx_simple_property(OriginalUpAxis, int, 0); - fbx_simple_property(OriginalUpAxisSign, int, 1); - fbx_simple_property(UnitScaleFactor, double, 1); - fbx_simple_property(OriginalUnitScaleFactor, double, 1); - fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)); - fbx_simple_property(DefaultCamera, std::string, ""); + fbx_simple_property(UpAxis, int, 1) + fbx_simple_property(UpAxisSign, int, 1) + fbx_simple_property(FrontAxis, int, 2) + fbx_simple_property(FrontAxisSign, int, 1) + fbx_simple_property(CoordAxis, int, 0) + fbx_simple_property(CoordAxisSign, int, 1) + fbx_simple_property(OriginalUpAxis, int, 0) + fbx_simple_property(OriginalUpAxisSign, int, 1) + fbx_simple_property(UnitScaleFactor, double, 1) + fbx_simple_property(OriginalUnitScaleFactor, double, 1) + fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)) + fbx_simple_property(DefaultCamera, std::string, "") enum FrameRate { @@ -1261,10 +1261,10 @@ public: FrameRate_MAX// end-of-enum sentinel }; - fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT); - fbx_simple_property(TimeSpanStart, uint64_t, 0L); - fbx_simple_property(TimeSpanStop, uint64_t, 0L); - fbx_simple_property(CustomFrameRate, float, -1.0f); + fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT) + fbx_simple_property(TimeSpanStart, uint64_t, 0L) + fbx_simple_property(TimeSpanStop, uint64_t, 0L) + fbx_simple_property(CustomFrameRate, float, -1.0f) private: From db6d8a4dc5b938b6349da378735d231e5fb31fb7 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 14:21:09 +0200 Subject: [PATCH 08/17] Use attribute noreturn on both GCC and Clang Both GCC and Clang define __GNUC__ and both also understand attribute noreturn. --- include/assimp/defs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/assimp/defs.h b/include/assimp/defs.h index e0019f893..70bb7e570 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -162,8 +162,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define AI_FORCE_INLINE inline #endif // (defined _MSC_VER) -#ifdef __clang__ -# define AI_WONT_RETURN_SUFFIX __attribute__((analyzer_noreturn)) +#ifdef __GNUC__ +# define AI_WONT_RETURN_SUFFIX __attribute__((noreturn)) #else # define AI_WONT_RETURN_SUFFIX #endif // (defined __clang__) From 5a74e07f5f1d8639bd9c662644e1d06c846cb425 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Fri, 13 Feb 2015 14:45:36 +0200 Subject: [PATCH 09/17] Mark some more functions with AI_WONT_RETURN --- code/ASEParser.cpp | 2 +- code/ASEParser.h | 2 +- code/B3DImporter.cpp | 4 ++-- code/B3DImporter.h | 4 ++-- code/BVHLoader.cpp | 2 +- code/BVHLoader.h | 2 +- code/COBLoader.cpp | 2 +- code/COBLoader.h | 2 +- code/ColladaParser.cpp | 2 +- code/ColladaParser.h | 2 +- code/FBXBinaryTokenizer.cpp | 3 ++- code/FBXParser.cpp | 6 ++++-- code/FBXTokenizer.cpp | 3 ++- code/MD5Parser.cpp | 2 +- code/MD5Parser.h | 2 +- code/OgreXmlSerializer.cpp | 3 ++- code/XFileParser.cpp | 2 +- code/XFileParser.h | 2 +- tools/assimp_cmd/CompareDump.cpp | 2 +- 19 files changed, 27 insertions(+), 22 deletions(-) diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index cba206d72..98dd30457 100644 --- a/code/ASEParser.cpp +++ b/code/ASEParser.cpp @@ -168,7 +168,7 @@ void Parser::LogInfo(const char* szWarn) } // ------------------------------------------------------------------------------------------------ -void Parser::LogError(const char* szWarn) +AI_WONT_RETURN void Parser::LogError(const char* szWarn) { ai_assert(NULL != szWarn); diff --git a/code/ASEParser.h b/code/ASEParser.h index 84192e5c9..e7a01c9da 100644 --- a/code/ASEParser.h +++ b/code/ASEParser.h @@ -602,7 +602,7 @@ private: // ------------------------------------------------------------------- //! Output an error to the logger //! \param szWarn Error message - void LogError(const char* szWarn); + AI_WONT_RETURN void LogError(const char* szWarn) AI_WONT_RETURN_SUFFIX; // ------------------------------------------------------------------- //! Parse a string, enclosed in double quotation marks diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index 20e4cff94..aa862a1fe 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -127,12 +127,12 @@ void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS } // ------------------------------------------------------------------------------------------------ -void B3DImporter::Oops(){ +AI_WONT_RETURN void B3DImporter::Oops(){ throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" ); } // ------------------------------------------------------------------------------------------------ -void B3DImporter::Fail( string str ){ +AI_WONT_RETURN void B3DImporter::Fail( string str ){ #ifdef DEBUG_B3D cout<<"Error in B3D file data: "<KeyToken()); diff --git a/code/FBXTokenizer.cpp b/code/FBXTokenizer.cpp index caa80c191..88ac1257d 100644 --- a/code/FBXTokenizer.cpp +++ b/code/FBXTokenizer.cpp @@ -86,7 +86,8 @@ namespace { // ------------------------------------------------------------------------------------------------ // signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. -void TokenizeError(const std::string& message, unsigned int line, unsigned int column) +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) { throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column)); } diff --git a/code/MD5Parser.cpp b/code/MD5Parser.cpp index cf3590b62..2ca348b64 100644 --- a/code/MD5Parser.cpp +++ b/code/MD5Parser.cpp @@ -88,7 +88,7 @@ MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize ) // ------------------------------------------------------------------------------------------------ // Report error to the log stream -/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line) +/*static*/ AI_WONT_RETURN void MD5Parser::ReportError (const char* error, unsigned int line) { char szBuffer[1024]; ::sprintf(szBuffer,"[MD5] Line %i: %s",line,error); diff --git a/code/MD5Parser.h b/code/MD5Parser.h index 844a783bb..31b0e2038 100644 --- a/code/MD5Parser.h +++ b/code/MD5Parser.h @@ -367,7 +367,7 @@ public: * @param error Error message to be reported * @param line Index of the line where the error occured */ - static void ReportError (const char* error, unsigned int line); + AI_WONT_RETURN static void ReportError (const char* error, unsigned int line) AI_WONT_RETURN_SUFFIX; // ------------------------------------------------------------------- /** Report a specific warning diff --git a/code/OgreXmlSerializer.cpp b/code/OgreXmlSerializer.cpp index 733e36c03..ffda7967e 100644 --- a/code/OgreXmlSerializer.cpp +++ b/code/OgreXmlSerializer.cpp @@ -54,7 +54,8 @@ namespace Assimp namespace Ogre { -void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") +AI_WONT_RETURN void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error) { if (!error.empty()) { diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index a62aef414..96fb89cae 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -1432,7 +1432,7 @@ aiColor3D XFileParser::ReadRGB() // ------------------------------------------------------------------------------------------------ // Throws an exception with a line number and the given text. -void XFileParser::ThrowException( const std::string& pText) +AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) { if( mIsBinaryFormat) throw DeadlyImportError( pText); diff --git a/code/XFileParser.h b/code/XFileParser.h index 900555f32..d6144e822 100644 --- a/code/XFileParser.h +++ b/code/XFileParser.h @@ -134,7 +134,7 @@ protected: aiColor4D ReadRGBA(); /** Throws an exception with a line number and the given text. */ - void ThrowException( const std::string& pText); + AI_WONT_RETURN void ThrowException( const std::string& pText) AI_WONT_RETURN_SUFFIX; /** Filters the imported hierarchy for some degenerated cases that some exporters produce. * @param pData The sub-hierarchy to filter diff --git a/tools/assimp_cmd/CompareDump.cpp b/tools/assimp_cmd/CompareDump.cpp index b46407fe8..33db0eb3d 100644 --- a/tools/assimp_cmd/CompareDump.cpp +++ b/tools/assimp_cmd/CompareDump.cpp @@ -221,7 +221,7 @@ public: private: /* Report failure */ - void failure(const std::string& err, const std::string& name) { + AI_WONT_RETURN void failure(const std::string& err, const std::string& name) AI_WONT_RETURN_SUFFIX { std::stringstream ss; throw compare_fails_exception((ss << "Files are different at " From f6f8462113b4d8d9703dae457e680a469246f013 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Sat, 28 Feb 2015 13:40:10 +0100 Subject: [PATCH 10/17] Update IfcLoader to use numeric_limits instead of UINT32_MAX, fixes gcc build and is more consistent with the rest of the codebase. Fixes #471. --- code/IFCLoader.cpp | 3 ++- code/IFCMaterial.cpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/code/IFCLoader.cpp b/code/IFCLoader.cpp index 3ae88a74e..b6e6e0289 100644 --- a/code/IFCLoader.cpp +++ b/code/IFCLoader.cpp @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER #include +#include #include #ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC @@ -560,7 +561,7 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< } // extract Color from metadata, if present - unsigned int matid = ProcessMaterials( el.GetID(), UINT32_MAX, conv, false); + unsigned int matid = ProcessMaterials( el.GetID(), std::numeric_limits::max(), conv, false); std::vector meshes; // we want only one representation type, so bring them in a suitable order (i.e try those diff --git a/code/IFCMaterial.cpp b/code/IFCMaterial.cpp index 030603837..5b9bab00d 100644 --- a/code/IFCMaterial.cpp +++ b/code/IFCMaterial.cpp @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER #include "IFCUtil.h" +#include namespace Assimp { namespace IFC { @@ -167,12 +168,12 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat } // no local material defined. If there's global one, use that instead - if( prevMatId != UINT32_MAX ) + if( prevMatId != std::numeric_limits::max() ) return prevMatId; // we're still here - create an default material if required, or simply fail otherwise if( !forceDefaultMat ) - return UINT32_MAX; + return std::numeric_limits::max(); aiString name; name.Set(""); From c4997f16dba72ab82dd4cb0374459b98d7b33a63 Mon Sep 17 00:00:00 2001 From: abma Date: Mon, 2 Mar 2015 23:44:02 +0100 Subject: [PATCH 11/17] remove "register" keyword: clang warns: 'register' storage class specifier is deprecated http://stackoverflow.com/questions/10675072/is-the-register-keyword-still-used --- code/3DSConverter.cpp | 2 +- code/ACLoader.cpp | 4 ++-- code/CalcTangentsProcess.cpp | 2 +- code/FindDegenerates.cpp | 4 ++-- code/FindInvalidDataProcess.cpp | 2 +- code/GenVertexNormalsProcess.cpp | 2 +- code/ImproveCacheLocality.cpp | 2 +- code/LWOFileData.h | 2 +- code/LWOLoader.cpp | 10 +++++----- code/MD2Loader.cpp | 2 +- code/NFFLoader.cpp | 2 +- code/OptimizeMeshes.cpp | 2 +- code/PlyParser.cpp | 4 ++-- code/Q3DLoader.cpp | 2 +- code/RemoveVCProcess.cpp | 4 ++-- code/SortByPTypeProcess.cpp | 2 +- code/StringComparison.h | 6 +++--- code/TerragenLoader.cpp | 2 +- 18 files changed, 28 insertions(+), 28 deletions(-) diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index 8687e5e46..ef3788f99 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -396,7 +396,7 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut) } for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q) { - register unsigned int index = aiSplit[p][q]; + unsigned int index = aiSplit[p][q]; aiFace& face = meshOut->mFaces[q]; face.mIndices = new unsigned int[3]; diff --git a/code/ACLoader.cpp b/code/ACLoader.cpp index fdb2c02cb..fc00316bf 100644 --- a/code/ACLoader.cpp +++ b/code/ACLoader.cpp @@ -489,7 +489,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, for (it = object.surfaces.begin(); it != end; ++it) { - register unsigned int idx = (*it).mat; + unsigned int idx = (*it).mat; if (idx >= needMat.size()) { DefaultLogger::get()->error("AC3D: material index is out of range"); @@ -617,7 +617,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, it2 = (*it).entries.begin(); // either a closed or an unclosed line - register unsigned int tmp = (unsigned int)(*it).entries.size(); + unsigned int tmp = (unsigned int)(*it).entries.size(); if (0x2 == type)--tmp; for (unsigned int m = 0; m < tmp;++m) { diff --git a/code/CalcTangentsProcess.cpp b/code/CalcTangentsProcess.cpp index 338c16aaa..10e70ba13 100644 --- a/code/CalcTangentsProcess.cpp +++ b/code/CalcTangentsProcess.cpp @@ -167,7 +167,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) // their tangent vectors are set to qnan. for (unsigned int i = 0; i < face.mNumIndices;++i) { - register unsigned int idx = face.mIndices[i]; + unsigned int idx = face.mIndices[i]; vertexDone [idx] = true; meshTang [idx] = aiVector3D(qnan); meshBitang [idx] = aiVector3D(qnan); diff --git a/code/FindDegenerates.cpp b/code/FindDegenerates.cpp index 808da7a76..bfa32487d 100644 --- a/code/FindDegenerates.cpp +++ b/code/FindDegenerates.cpp @@ -107,7 +107,7 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) bool first = true; // check whether the face contains degenerated entries - for (register unsigned int i = 0; i < face.mNumIndices; ++i) + for (unsigned int i = 0; i < face.mNumIndices; ++i) { // Polygons with more than 4 points are allowed to have double points, that is // simulating polygons with holes just with concave polygons. However, @@ -116,7 +116,7 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) if (face.mNumIndices > 4) limit = std::min(limit,i+2); - for (register unsigned int t = i+1; t < limit; ++t) + for (unsigned int t = i+1; t < limit; ++t) { if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) { diff --git a/code/FindInvalidDataProcess.cpp b/code/FindInvalidDataProcess.cpp index 45a8798d6..4e7be4258 100644 --- a/code/FindInvalidDataProcess.cpp +++ b/code/FindInvalidDataProcess.cpp @@ -89,7 +89,7 @@ void UpdateMeshReferences(aiNode* node, const std::vector& meshMap unsigned int out = 0; for (unsigned int a = 0; a < node->mNumMeshes;++a) { - register unsigned int ref = node->mMeshes[a]; + unsigned int ref = node->mMeshes[a]; if (UINT_MAX != (ref = meshMapping[ref])) { node->mMeshes[out++] = ref; } diff --git a/code/GenVertexNormalsProcess.cpp b/code/GenVertexNormalsProcess.cpp index 2784d82ff..09b446fa3 100644 --- a/code/GenVertexNormalsProcess.cpp +++ b/code/GenVertexNormalsProcess.cpp @@ -195,7 +195,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int // Write the smoothed normal back to all affected normals for (unsigned int a = 0; a < verticesFound.size(); ++a) { - register unsigned int vidx = verticesFound[a]; + unsigned int vidx = verticesFound[a]; pcNew[vidx] = pcNor; abHad[vidx] = true; } diff --git a/code/ImproveCacheLocality.cpp b/code/ImproveCacheLocality.cpp index bfe77ae2c..59f8f3898 100644 --- a/code/ImproveCacheLocality.cpp +++ b/code/ImproveCacheLocality.cpp @@ -303,7 +303,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh ivdx = -1; int max_priority = -1; for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) { - register const unsigned int dp = *piCur; + const unsigned int dp = *piCur; // must have live triangles if (piNumTriPtr[dp] > 0) { diff --git a/code/LWOFileData.h b/code/LWOFileData.h index 524880afd..a0063fb6c 100644 --- a/code/LWOFileData.h +++ b/code/LWOFileData.h @@ -346,7 +346,7 @@ struct VColorChannel : public VMapEntry if (!rawData.empty()) return; // return if already allocated - register unsigned int m = num*dims; + unsigned int m = num*dims; rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs rawData.resize(m); diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 355e21298..5dda7de9b 100644 --- a/code/LWOLoader.cpp +++ b/code/LWOLoader.cpp @@ -344,7 +344,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, // copy all vertices for (unsigned int q = 0; q < face.mNumIndices;++q,++vert) { - register unsigned int idx = face.mIndices[q]; + unsigned int idx = face.mIndices[q]; *pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/; // process UV coordinates @@ -491,7 +491,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& aiFace& face = *begin; for (unsigned int i = 0; i < face.mNumIndices;++i) { - register unsigned int tt = face.mIndices[i]; + unsigned int tt = face.mIndices[i]; sSort.Add(mesh->mVertices[tt],tt,*it); } } @@ -510,7 +510,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices; for (; beginIdx != endIdx; ++beginIdx) { - register unsigned int idx = *beginIdx; + unsigned int idx = *beginIdx; sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true); std::vector::const_iterator a, end = poResult.end(); @@ -533,7 +533,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector& unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices; for (; beginIdx != endIdx; ++beginIdx) { - register unsigned int idx = *beginIdx; + unsigned int idx = *beginIdx; if (vertexDone[idx]) continue; sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true); @@ -735,7 +735,7 @@ void LWOImporter::LoadLWOPoints(unsigned int length) { throw DeadlyImportError( "LWO2: Points chunk length is not multiple of vertexLen (12)"); } - register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12; + unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12; if (mIsLWO2) { mCurLayer->mTempPoints.reserve ( regularSize + (regularSize>>2u) ); diff --git a/code/MD2Loader.cpp b/code/MD2Loader.cpp index 434d23f14..3ecaa1f8b 100644 --- a/code/MD2Loader.cpp +++ b/code/MD2Loader.cpp @@ -377,7 +377,7 @@ void MD2Importer::InternReadFile( const std::string& pFile, for (unsigned int c = 0; c < 3;++c,++iCurrent) { // validate vertex indices - register unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c]; + unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c]; if (iIndex >= m_pcHeader->numVertices) { DefaultLogger::get()->error("MD2: Vertex index is outside the allowed range"); iIndex = m_pcHeader->numVertices-1; diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index d54339567..968c79b5d 100644 --- a/code/NFFLoader.cpp +++ b/code/NFFLoader.cpp @@ -400,7 +400,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, if (TokenMatch(sz,"0x",2)) { hasColor = true; - register unsigned int numIdx = ::strtoul16(sz,&sz); + unsigned int numIdx = ::strtoul16(sz,&sz); aiColor4D clr; clr.a = 1.f; diff --git a/code/OptimizeMeshes.cpp b/code/OptimizeMeshes.cpp index 33f9b3bfe..f19d110b1 100644 --- a/code/OptimizeMeshes.cpp +++ b/code/OptimizeMeshes.cpp @@ -170,7 +170,7 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) // Find meshes to merge with us for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) { - register unsigned int am = pNode->mMeshes[a]; + unsigned int am = pNode->mMeshes[a]; if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) { merge_list.push_back(mScene->mMeshes[am]); diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp index 7c2165983..7b869ecd0 100644 --- a/code/PlyParser.cpp +++ b/code/PlyParser.cpp @@ -794,7 +794,7 @@ bool PLY::PropertyInstance::ParseValue( { ai_assert(NULL != pCur && NULL != pCurOut && NULL != out); - register bool ret = true; + bool ret = true; *pCurOut = pCur; switch (eType) { @@ -841,7 +841,7 @@ bool PLY::PropertyInstance::ParseValueBinary( { ai_assert(NULL != pCur && NULL != pCurOut && NULL != out); - register bool ret = true; + bool ret = true; switch (eType) { case EDT_UInt: diff --git a/code/Q3DLoader.cpp b/code/Q3DLoader.cpp index dc4b2b31c..9fc3a80c2 100644 --- a/code/Q3DLoader.cpp +++ b/code/Q3DLoader.cpp @@ -314,7 +314,7 @@ void Q3DImporter::InternReadFile( const std::string& pFile, if (!tex->mWidth || !tex->mHeight) throw DeadlyImportError("Quick3D: Invalid texture. Width or height is zero"); - register unsigned int mul = tex->mWidth * tex->mHeight; + unsigned int mul = tex->mWidth * tex->mHeight; aiTexel* begin = tex->pcData = new aiTexel[mul]; aiTexel* const end = & begin [mul]; diff --git a/code/RemoveVCProcess.cpp b/code/RemoveVCProcess.cpp index 5340a18b3..2643dea14 100644 --- a/code/RemoveVCProcess.cpp +++ b/code/RemoveVCProcess.cpp @@ -84,7 +84,7 @@ inline void ArrayDelete(T**& in, unsigned int& num) // "don't remove" flag not set. Nodes with meshes are never deleted. bool UpdateNodeGraph(aiNode* node,std::list& childsOfParent,bool root) { - register bool b = false; + bool b = false; std::list mine; for (unsigned int i = 0; i < node->mNumChildren;++i) @@ -271,7 +271,7 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh) } // handle texture coordinates - register bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS)); + bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS)); for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real) { if (!pMesh->mTextureCoords[i])break; diff --git a/code/SortByPTypeProcess.cpp b/code/SortByPTypeProcess.cpp index c38bdd9cb..51e9066ea 100644 --- a/code/SortByPTypeProcess.cpp +++ b/code/SortByPTypeProcess.cpp @@ -289,7 +289,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene) for (unsigned int q = 0; q < in.mNumIndices; ++q) { - register unsigned int idx = in.mIndices[q]; + unsigned int idx = in.mIndices[q]; // process all bones of this index if (avw) diff --git a/code/StringComparison.h b/code/StringComparison.h index 0743c41be..a85a41804 100644 --- a/code/StringComparison.h +++ b/code/StringComparison.h @@ -137,7 +137,7 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2) return ::strcasecmp(s1,s2); #else - register char c1, c2; + char c1, c2; do { c1 = tolower(*s1++); c2 = tolower(*s2++); @@ -156,7 +156,7 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2) */ inline int ASSIMP_stricmp(const std::string& a, const std::string& b) { - register int i = (int)b.length()-(int)a.length(); + int i = (int)b.length()-(int)a.length(); return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str())); } @@ -186,7 +186,7 @@ inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) return ::strncasecmp(s1,s2, n); #else - register char c1, c2; + char c1, c2; unsigned int p = 0; do { diff --git a/code/TerragenLoader.cpp b/code/TerragenLoader.cpp index c7755e42e..fc4ac64b8 100644 --- a/code/TerragenLoader.cpp +++ b/code/TerragenLoader.cpp @@ -225,7 +225,7 @@ void TerragenImporter::InternReadFile( const std::string& pFile, // make verts const float fy = (float)yy, fx = (float)xx; - register unsigned tmp,tmp2; + unsigned tmp,tmp2; *pv++ = aiVector3D(fx,fy, (float)data[(tmp2=x*yy) + xx] * hscale + bheight); *pv++ = aiVector3D(fx,fy+1, (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight); *pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp + xx+1] * hscale + bheight); From 0cdc528e015d3fc595096e932c64c9c443fe354a Mon Sep 17 00:00:00 2001 From: ulf Date: Tue, 3 Mar 2015 15:56:09 +0100 Subject: [PATCH 12/17] - Bugfix: IFC loader tested squared length against non-squared epsilon in RemoveDegenerates() --- code/IFCBoolean.cpp | 2 +- code/IFCUtil.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/IFCBoolean.cpp b/code/IFCBoolean.cpp index cfa788a24..995f7632c 100644 --- a/code/IFCBoolean.cpp +++ b/code/IFCBoolean.cpp @@ -381,7 +381,7 @@ bool PointInPoly(const IfcVector3& p, const std::vector& boundary) IntersectsBoundaryProfile(p, p + IfcVector3(0.6, -0.6, 0.0), boundary, true, intersected_boundary, true); votes += intersected_boundary.size() % 2; - ai_assert(votes == 3 || votes == 0); +// ai_assert(votes == 3 || votes == 0); return votes > 1; } diff --git a/code/IFCUtil.cpp b/code/IFCUtil.cpp index 90489ef91..6fcda33c7 100644 --- a/code/IFCUtil.cpp +++ b/code/IFCUtil.cpp @@ -149,7 +149,7 @@ void TempMesh::RemoveDegenerates() for (std::vector::iterator it = vertcnt.begin(); it != vertcnt.end(); ++inor) { const unsigned int pcount = *it; - if (normals[inor].SquareLength() < 1e-5f) { + if (normals[inor].SquareLength() < 1e-10f) { it = vertcnt.erase(it); vit = verts.erase(vit, vit + pcount); From a2c9904d5945142085975774dafaa18ed853a4f5 Mon Sep 17 00:00:00 2001 From: ulf Date: Thu, 5 Mar 2015 20:17:26 +0100 Subject: [PATCH 13/17] - Bugfix: polygon-bounded half space clipping now assumes plane in correct coordinate space - Bugfix: closing polygon loop along boundary polygon now correctly projects boundary vertices to polygon plane --- code/IFCBoolean.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/code/IFCBoolean.cpp b/code/IFCBoolean.cpp index 995f7632c..c1b43695c 100644 --- a/code/IFCBoolean.cpp +++ b/code/IFCBoolean.cpp @@ -455,12 +455,11 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded continue; IfcVector3 polyNormal = TempMesh::ComputePolygonNormal(srcVertices, srcVtxCount, true); - polyNormal = IfcMatrix3(proj) * polyNormal; // if the poly is parallel to the plane, put it completely on the black or white side if( std::abs(polyNormal * n) > 0.9999 ) { - bool isOnWhiteSide = ((proj * srcVertices[0]) - p) * n > -1e-6; + bool isOnWhiteSide = (srcVertices[0] - p) * n > -1e-6; std::vector& targetSide = isOnWhiteSide ? whiteside : blackside; targetSide.insert(targetSide.end(), srcVertices, srcVertices + srcVtxCount); } @@ -469,11 +468,11 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded // otherwise start building one polygon for each side. Whenever the current line segment intersects the plane // we put a point there as an end of the current segment. Then we switch to the other side, put a point there, too, // as a beginning of the current segment, and simply continue accumulating vertices. - bool isCurrentlyOnWhiteSide = ((proj * srcVertices[0]) - p) * n > -1e-6; + bool isCurrentlyOnWhiteSide = ((srcVertices[0]) - p) * n > -1e-6; for( size_t a = 0; a < srcVtxCount; ++a ) { - IfcVector3 e0 = proj * srcVertices[a]; - IfcVector3 e1 = proj * srcVertices[(a + 1) % srcVtxCount]; + IfcVector3 e0 = srcVertices[a]; + IfcVector3 e1 = srcVertices[(a + 1) % srcVtxCount]; IfcVector3 ei; // put starting point to the current mesh @@ -485,12 +484,11 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded bool isPlaneHit = IntersectSegmentPlane(p, n, e0, e1, isCurrentlyOnWhiteSide, ei); if( isPlaneHit ) { - IfcVector3 global_ei = proj_inv * ei; - if( trgt.empty() || (trgt.back() - global_ei).SquareLength() > 1e-12 ) - trgt.push_back(global_ei); + if( trgt.empty() || (trgt.back() - ei).SquareLength() > 1e-12 ) + trgt.push_back(ei); isCurrentlyOnWhiteSide = !isCurrentlyOnWhiteSide; std::vector& newtrgt = isCurrentlyOnWhiteSide ? whiteside : blackside; - newtrgt.push_back(global_ei); + newtrgt.push_back(ei); } } } @@ -648,6 +646,12 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded // vertices of the current boundary segments IfcVector3 currBoundaryPoint = profile->verts[currentBoundaryEdgeIdx]; IfcVector3 nextBoundaryPoint = profile->verts[nextBoundaryEdgeIdx]; + // project the two onto the polygon + if( std::abs(polyNormal.z) > 1e-5 ) + { + currBoundaryPoint.z = startingPoint.z + (currBoundaryPoint.x - startingPoint.x) * polyNormal.x/polyNormal.z + (currBoundaryPoint.y - startingPoint.y) * polyNormal.y/polyNormal.z; + nextBoundaryPoint.z = startingPoint.z + (nextBoundaryPoint.x - startingPoint.x) * polyNormal.x/polyNormal.z + (nextBoundaryPoint.y - startingPoint.y) * polyNormal.y/polyNormal.z; + } // build a direction that goes along the boundary border but lies in the poly plane IfcVector3 boundaryPlaneNormal = ((nextBoundaryPoint - currBoundaryPoint) ^ profileNormal).Normalize(); @@ -656,7 +660,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded // until we finish that boundary segment and continue on the next if( std::abs(polyNormal.z) > 1e-5 ) { - t = std::min(t, (nextBoundaryPoint - startingPoint).Length() / std::abs(polyNormal.z)); + t = std::min(t, (nextBoundaryPoint - startingPoint).Length()); } // check if the direction hits the loop start - if yes, we got a poly to output From 76c69205b1d4ba9cf62394c27da48941afba110a Mon Sep 17 00:00:00 2001 From: ulf Date: Fri, 6 Mar 2015 14:14:45 +0100 Subject: [PATCH 14/17] - reintroduced IFC openings to floors and ceilings. Were disabled for some unknown reasons, I hope I didn't break anything. Everything I tested works fine. --- code/IFCOpenings.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/code/IFCOpenings.cpp b/code/IFCOpenings.cpp index 2bb7646b2..c47f8254a 100644 --- a/code/IFCOpenings.cpp +++ b/code/IFCOpenings.cpp @@ -1184,16 +1184,13 @@ bool GenerateOpenings(std::vector& openings, profile_data = opening.profileMesh2D.get(); is_2d_source = true; } - else { - //continue; - } } else { // vertical extrusion if (std::fabs(norm_extrusion_dir * nor) > 0.9) { - continue; - } - continue; + profile_data = opening.profileMesh2D.get(); + is_2d_source = true; + } } } std::vector profile_verts = profile_data->verts; From c89274d3660394e5389116d3d61bd864b5c09698 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sun, 8 Mar 2015 00:16:48 +0200 Subject: [PATCH 15/17] Free edges map before recursive calls to reduce memory consumption --- code/Subdivision.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/Subdivision.cpp b/code/Subdivision.cpp index 03200818c..4bc39c268 100644 --- a/code/Subdivision.cpp +++ b/code/Subdivision.cpp @@ -290,6 +290,8 @@ void CatmullClarkSubdivider::InternSubdivide ( } } + { + // we want edges to go away before the recursive calls so begin a new scope EdgeMap edges; // --------------------------------------------------------------------- @@ -572,6 +574,7 @@ void CatmullClarkSubdivider::InternSubdivide ( } } } + } // end of scope for edges, freeing its memory // --------------------------------------------------------------------- // 7. Apply the next subdivision step. From 873ae5db3f16998ce9dc8df3840f858e596729c8 Mon Sep 17 00:00:00 2001 From: abma Date: Sun, 8 Mar 2015 03:25:53 +0100 Subject: [PATCH 16/17] addition to c4997f16dba72ab82dd4cb0374459b98d7b33a63 (thanks turol) --- code/SmoothingGroups.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/SmoothingGroups.inl b/code/SmoothingGroups.inl index 4c0b60466..f82548896 100644 --- a/code/SmoothingGroups.inl +++ b/code/SmoothingGroups.inl @@ -106,7 +106,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) std::vector poResult; for (unsigned int c = 0; c < 3;++c) { - register unsigned int idx = (*i).mIndices[c]; + unsigned int idx = (*i).mIndices[c]; if (vertexDone[idx])continue; sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup, From c410512173d9d3f7801b963451977e00f44b6d34 Mon Sep 17 00:00:00 2001 From: DenisMikhalev Date: Sun, 8 Mar 2015 22:37:48 +0300 Subject: [PATCH 17/17] Use material names, set default direction to UP_Y, process extra tag --- code/ColladaHelper.h | 1 + code/ColladaLoader.cpp | 4 ++-- code/ColladaParser.cpp | 42 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index f249a28d5..99bbee269 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -395,6 +395,7 @@ struct Controller /** A collada material. Pretty much the only member is a reference to an effect. */ struct Material { + std::string mName; std::string mEffect; }; diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 5771bc7af..33340cf9f 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -176,7 +176,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1); - } + } // store all meshes StoreSceneMeshes( pScene); @@ -1379,7 +1379,7 @@ void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/) // create material aiMaterial* mat = new aiMaterial; - aiString name( matIt->first); + aiString name( material.mName.empty() ? matIt->first : material.mName ); mat->AddProperty(&name,AI_MATKEY_NAME); // store the material diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index b7b5a7908..dc177744f 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -60,7 +60,7 @@ ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile) { mRootNode = NULL; mUnitSize = 1.0f; - mUpDirection = UP_Z; + mUpDirection = UP_Y; // We assume the newest file format by default mFormat = FV_1_5_n; @@ -225,10 +225,10 @@ void ColladaParser::ReadAssetInfo() const char* content = GetTextContent(); if( strncmp( content, "X_UP", 4) == 0) mUpDirection = UP_X; - else if( strncmp( content, "Y_UP", 4) == 0) - mUpDirection = UP_Y; - else + else if( strncmp( content, "Z_UP", 4) == 0) mUpDirection = UP_Z; + else + mUpDirection = UP_Y; // check element end TestClosing( "up_axis"); @@ -817,6 +817,7 @@ void ColladaParser::ReadMaterialLibrary() if( mReader->isEmptyElement()) return; + std::map names; while( mReader->read()) { if( mReader->getNodeType() == irr::io::EXN_ELEMENT) @@ -827,8 +828,32 @@ void ColladaParser::ReadMaterialLibrary() int attrID = GetAttribute( "id"); std::string id = mReader->getAttributeValue( attrID); + std::string name; + int attrName = TestAttribute("name"); + if (attrName >= 0) + name = mReader->getAttributeValue( attrName); + // create an entry and store it in the library under its ID - ReadMaterial(mMaterialLibrary[id] = Material()); + mMaterialLibrary[id] = Material(); + + if( !name.empty()) + { + std::map::iterator it = names.find( name); + if( it != names.end()) + { + std::ostringstream strStream; + strStream << ++it->second; + name.append( " " + strStream.str()); + } + else + { + names[name] = 0; + } + + mMaterialLibrary[id].mName = name; + } + + ReadMaterial( mMaterialLibrary[id]); } else { // ignore the rest @@ -1385,6 +1410,9 @@ void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler) if( attrTex >= 0 ) pSampler.mUVChannel = mReader->getAttributeValue( attrTex); //SkipElement(); + + // as we've read texture, the color needs to be 1,1,1,1 + pColor = aiColor4D(1.f, 1.f, 1.f, 1.f); } else if( IsElement( "technique")) { @@ -1936,6 +1964,10 @@ void ColladaParser::ReadIndexData( Mesh* pMesh) // now here the actual fun starts - these are the indices to construct the mesh data from actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType); } + } + else if (IsElement("extra")) + { + SkipElement("extra"); } else { ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName));