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/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/3DSExporter.cpp b/code/3DSExporter.cpp index de47bc064..c0d22a3e4 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/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/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/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/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: "< 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/COBLoader.cpp b/code/COBLoader.cpp index 47b6b0315..ce9ceeb7e 100644 --- a/code/COBLoader.cpp +++ b/code/COBLoader.cpp @@ -127,7 +127,7 @@ void COBImporter::SetupProperties(const Importer* /*pImp*/) } // ------------------------------------------------------------------------------------------------ -/*static*/ void COBImporter::ThrowException(const std::string& msg) +/*static*/ AI_WONT_RETURN void COBImporter::ThrowException(const std::string& msg) { throw DeadlyImportError("COB: "+msg); } diff --git a/code/COBLoader.h b/code/COBLoader.h index b6f0b045b..1982801c4 100644 --- a/code/COBLoader.h +++ b/code/COBLoader.h @@ -95,7 +95,7 @@ private: // ------------------------------------------------------------------- /** Prepend 'COB: ' and throw msg.*/ - static void ThrowException(const std::string& msg); + AI_WONT_RETURN static void ThrowException(const std::string& msg) AI_WONT_RETURN_SUFFIX; // ------------------------------------------------------------------- /** @brief Read from an ascii scene/object file 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/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) { 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 a11d94ec3..33340cf9f 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -73,7 +73,7 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ColladaLoader::ColladaLoader() - : noSkeletonMesh(), ignoreUpDirection(false), mNodeNameCounter() +: noSkeletonMesh(), ignoreUpDirection(false), mNodeNameCounter() {} // ------------------------------------------------------------------------------------------------ @@ -87,16 +87,16 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo { // check file extension std::string extension = GetExtension(pFile); - + if( extension == "dae") return true; // XML - too generic, we need to open the file and search for typical keywords if( extension == "xml" || !extension.length() || checkSig) { /* If CanRead() is called in order to check whether we - * support a specific file extension in general pIOHandler - * might be NULL and it's our duty to return true here. - */ + * support a specific file extension in general pIOHandler + * might be NULL and it's our duty to return true here. + */ if (!pIOHandler)return true; const char* tokens[] = {"collada"}; return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); @@ -157,26 +157,26 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // ... then fill the materials with the now adjusted settings FillMaterials(parser, pScene); - // Apply unitsize scale calculation - pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, - 0, parser.mUnitSize, 0, 0, - 0, 0, parser.mUnitSize, 0, - 0, 0, 0, 1); - if( !ignoreUpDirection ) { - // Convert to Y_UP, if different orientation + // Apply unitsize scale calculation + pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, + 0, parser.mUnitSize, 0, 0, + 0, 0, parser.mUnitSize, 0, + 0, 0, 0, 1); + if( !ignoreUpDirection ) { + // Convert to Y_UP, if different orientation if( parser.mUpDirection == ColladaParser::UP_X) pScene->mRootNode->mTransformation *= aiMatrix4x4( - 0, -1, 0, 0, - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); + 0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); else if( parser.mUpDirection == ColladaParser::UP_Z) pScene->mRootNode->mTransformation *= aiMatrix4x4( - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, -1, 0, 0, - 0, 0, 0, 1); - } + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, -1, 0, 0, + 0, 0, 0, 1); + } // store all meshes StoreSceneMeshes( pScene); @@ -195,7 +195,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I // If no meshes have been loaded, it's probably just an animated skeleton. if (!pScene->mNumMeshes) { - + if (!noSkeletonMesh) { SkeletonMeshBuilder hero(pScene); } @@ -251,14 +251,14 @@ aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Colla // ------------------------------------------------------------------------------------------------ // Resolve node instances void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode, - std::vector& resolved) + std::vector& resolved) { // reserve enough storage resolved.reserve(pNode->mNodeInstances.size()); // ... and iterate through all nodes to be instanced as children of pNode for (std::vector::const_iterator it = pNode->mNodeInstances.begin(), - end = pNode->mNodeInstances.end(); it != end; ++it) + end = pNode->mNodeInstances.end(); it != end; ++it) { // find the corresponding node in the library const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode); @@ -272,7 +272,7 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co } if (!nd) DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode); - + else { // attach this node to the list of children resolved.push_back(nd); @@ -283,7 +283,7 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co // ------------------------------------------------------------------------------------------------ // Resolve UV channels void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler, - const Collada::SemanticMappingTable& table) + const Collada::SemanticMappingTable& table) { std::map::const_iterator it = table.mMap.find(sampler.mUVChannel); if (it != table.mMap.end()) { @@ -308,7 +308,7 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll continue; } const Collada::Light* srcLight = &srcLightIt->second; - + // now fill our ai data structure aiLight* out = new aiLight(); out->mName = pTarget->mName; @@ -326,7 +326,7 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll // convert falloff angle and falloff exponent in our representation, if given if (out->mType == aiLightSource_SPOT) { - + out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle ); // ... some extension magic. @@ -387,7 +387,7 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col // ... but for the rest some values are optional // and we need to compute the others in any combination. - if (srcCamera->mAspect != 10e10f) + if (srcCamera->mAspect != 10e10f) out->mAspect = srcCamera->mAspect; if (srcCamera->mHorFov != 10e10f) { @@ -395,12 +395,12 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) { out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) / - tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); + tan(AI_DEG_TO_RAD(srcCamera->mVerFov)); } } else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) { out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect * - tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); + tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f))); } // Collada uses degrees, we use radians @@ -518,11 +518,11 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll // assign the material index dstMesh->mMaterialIndex = matIdx; - if(dstMesh->mName.length == 0) - { - dstMesh->mName = mid.mMeshOrController; - } - } + if(dstMesh->mName.length == 0) + { + dstMesh->mName = mid.mMeshOrController; + } + } } } @@ -538,11 +538,11 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll // ------------------------------------------------------------------------------------------------ // Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, - const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) + const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) { aiMesh* dstMesh = new aiMesh; - - dstMesh->mName = pSrcMesh->mName; + + dstMesh->mName = pSrcMesh->mName; // count the vertices addressed by its faces const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace, @@ -589,7 +589,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: dstMesh->mTextureCoords[real] = new aiVector3D[numVertices]; for( size_t b = 0; b < numVertices; ++b) dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b]; - + dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a]; ++real; } @@ -624,8 +624,8 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada:: if( pSrcController) { // refuse if the vertex count does not match - // if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) - // throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count"); +// if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) +// throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count"); // resolve references - joint names const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); @@ -937,7 +937,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // find the collada node corresponding to the aiNode const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName); - // ai_assert( srcNode != NULL); +// ai_assert( srcNode != NULL); if( !srcNode) continue; @@ -990,7 +990,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars { entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1); std::string subElement = srcChannel.mTarget.substr(bracketPos); - + if (subElement == "(0)(0)") entry.mSubElement = 0; else if (subElement == "(1)(0)") @@ -1058,154 +1058,134 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount) throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget)); - if( e.mTimeAccessor->mCount > 0 ) - { - // find bounding times - startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); - endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); - } + if( e.mTimeAccessor->mCount > 0 ) + { + // find bounding times + startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0)); + endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0)); + } } - std::vector resultTrafos; - if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) - { - // create a local transformation chain of the node's transforms - std::vector transforms = srcNode->mTransforms; + std::vector resultTrafos; + if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 ) + { + // create a local transformation chain of the node's transforms + std::vector transforms = srcNode->mTransforms; - // now for every unique point in time, find or interpolate the key values for that time - // and apply them to the transform chain. Then the node's present transformation can be calculated. - float time = startTime; - while( 1) - { - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) - { - Collada::ChannelEntry& e = *it; + // now for every unique point in time, find or interpolate the key values for that time + // and apply them to the transform chain. Then the node's present transformation can be calculated. + float time = startTime; + while( 1) + { + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + Collada::ChannelEntry& e = *it; - // find the keyframe behind the current point in time - size_t pos = 0; - float postTime = 0.f; - while( 1) - { - if( pos >= e.mTimeAccessor->mCount) - break; - postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); - if( postTime >= time) - break; - ++pos; - } + // find the keyframe behind the current point in time + size_t pos = 0; + float postTime = 0.f; + while( 1) + { + if( pos >= e.mTimeAccessor->mCount) + break; + postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); + if( postTime >= time) + break; + ++pos; + } - pos = std::min( pos, e.mTimeAccessor->mCount-1); + pos = std::min( pos, e.mTimeAccessor->mCount-1); - // read values from there - float temp[16]; - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) - temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); + // read values from there + float temp[16]; + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) + temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c); - // if not exactly at the key time, interpolate with previous value set - if( postTime > time && pos > 0) - { - float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); - float factor = (time - postTime) / (preTime - postTime); + // if not exactly at the key time, interpolate with previous value set + if( postTime > time && pos > 0) + { + float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0); + float factor = (time - postTime) / (preTime - postTime); - for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) - { - float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); - temp[c] += (v - temp[c]) * factor; - } - } + for( size_t c = 0; c < e.mValueAccessor->mSize; ++c) + { + float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c); + temp[c] += (v - temp[c]) * factor; + } + } - // Apply values to current transformation - std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); - } + // Apply values to current transformation + std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement); + } - // Calculate resulting transformation - aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); + // Calculate resulting transformation + aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms); - // out of lazyness: we store the time in matrix.d4 - mat.d4 = time; - resultTrafos.push_back( mat); + // out of lazyness: we store the time in matrix.d4 + mat.d4 = time; + resultTrafos.push_back( mat); - // find next point in time to evaluate. That's the closest frame larger than the current in any channel - float nextTime = 1e20f; - for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) - { - Collada::ChannelEntry& e = *it; + // find next point in time to evaluate. That's the closest frame larger than the current in any channel + float nextTime = 1e20f; + for( std::vector::iterator it = entries.begin(); it != entries.end(); ++it) + { + Collada::ChannelEntry& e = *it; - // find the next time value larger than the current - size_t pos = 0; - while( pos < e.mTimeAccessor->mCount) - { - float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); - if( t > time) - { - nextTime = std::min( nextTime, t); - break; - } - ++pos; - } + // find the next time value larger than the current + size_t pos = 0; + while( pos < e.mTimeAccessor->mCount) + { + float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); + if( t > time) + { + nextTime = std::min( nextTime, t); + break; + } + ++pos; + } + } - // https://github.com/assimp/assimp/issues/458 - // Sub-sample axis-angle channels if the delta between two consecutive - // key-frame angles is >= 180 degrees. - if (transforms[e.mTransformIndex].mType == Collada::TF_ROTATE && e.mSubElement == 3 && pos > 0 && pos < e.mTimeAccessor->mCount) { - float cur_key_angle = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, 0); - float last_key_angle = ReadFloat( *e.mValueAccessor, *e.mValueData, pos - 1, 0); - float cur_key_time = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0); - float last_key_time = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos - 1, 0); + // no more keys on any channel after the current time -> we're done + if( nextTime > 1e19) + break; - float last_eval_angle = last_key_angle + (cur_key_angle - last_key_angle) * (time - last_key_time) / (cur_key_time - last_key_time); - float delta = std::fabs(cur_key_angle-last_eval_angle); - if (delta >= 180.0f) { - int subSampleCount = static_cast(std::floorf(delta / 90.0f)); - if (cur_key_time != time) { - float nextSampleTime = time + (cur_key_time - time) / subSampleCount; - nextTime = std::min( nextTime, nextSampleTime); - } - } - } - } - - // no more keys on any channel after the current time -> we're done - if( nextTime > 1e19) - break; - - // else construct next keyframe at this following time point - time = nextTime; - } - } + // else construct next keyframe at this following time point + time = nextTime; + } + } // there should be some keyframes, but we aren't that fixated on valid input data - // ai_assert( resultTrafos.size() > 0); +// ai_assert( resultTrafos.size() > 0); // build an animation channel for the given node out of these trafo keys - if( !resultTrafos.empty() ) - { - aiNodeAnim* dstAnim = new aiNodeAnim; - dstAnim->mNodeName = nodeName; - dstAnim->mNumPositionKeys = resultTrafos.size(); - dstAnim->mNumRotationKeys= resultTrafos.size(); - dstAnim->mNumScalingKeys = resultTrafos.size(); - dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; - dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; - dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; + if( !resultTrafos.empty() ) + { + aiNodeAnim* dstAnim = new aiNodeAnim; + dstAnim->mNodeName = nodeName; + dstAnim->mNumPositionKeys = resultTrafos.size(); + dstAnim->mNumRotationKeys= resultTrafos.size(); + dstAnim->mNumScalingKeys = resultTrafos.size(); + dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()]; + dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()]; + dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()]; - for( size_t a = 0; a < resultTrafos.size(); ++a) - { - aiMatrix4x4 mat = resultTrafos[a]; - double time = double( mat.d4); // remember? time is stored in mat.d4 - mat.d4 = 1.0f; + for( size_t a = 0; a < resultTrafos.size(); ++a) + { + aiMatrix4x4 mat = resultTrafos[a]; + double time = double( mat.d4); // remember? time is stored in mat.d4 + mat.d4 = 1.0f; - dstAnim->mPositionKeys[a].mTime = time; - dstAnim->mRotationKeys[a].mTime = time; - dstAnim->mScalingKeys[a].mTime = time; - mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); - } + dstAnim->mPositionKeys[a].mTime = time; + dstAnim->mRotationKeys[a].mTime = time; + dstAnim->mScalingKeys[a].mTime = time; + mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue); + } - anims.push_back( dstAnim); - } else - { - DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); - } + anims.push_back( dstAnim); + } else + { + DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter."); + } } if( !anims.empty()) @@ -1230,9 +1210,9 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars // ------------------------------------------------------------------------------------------------ // Add a texture to a material structure void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser, - const Collada::Effect& effect, - const Collada::Sampler& sampler, - aiTextureType type, unsigned int idx) + const Collada::Effect& effect, + const Collada::Sampler& sampler, + aiTextureType type, unsigned int idx) { // first of all, basic file name const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName ); @@ -1359,8 +1339,8 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce // add textures, if given if( !effect.mTexAmbient.mName.empty()) - /* It is merely a lightmap */ - AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); + /* It is merely a lightmap */ + AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP); if( !effect.mTexEmissive.mName.empty()) AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE); @@ -1399,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 @@ -1431,7 +1411,7 @@ void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/) // ------------------------------------------------------------------------------------------------ // Resolves the texture name for the given effect texture entry aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser, - const Collada::Effect& pEffect, const std::string& pName) + const Collada::Effect& pEffect, const std::string& pName) { // recurse through the param references until we end up at an image std::string name = pName; @@ -1508,35 +1488,35 @@ void ColladaLoader::ConvertPath (aiString& ss) ss.data[ss.length] = '\0'; } - // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... - // I need to filter it without destroying linux paths starting with "/somewhere" - if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' ) - { - ss.length--; - memmove( ss.data, ss.data+1, ss.length); - ss.data[ss.length] = 0; - } + // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... + // I need to filter it without destroying linux paths starting with "/somewhere" + if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' ) + { + ss.length--; + memmove( ss.data, ss.data+1, ss.length); + ss.data[ss.length] = 0; + } - // find and convert all %xy special chars - char* out = ss.data; - for( const char* it = ss.data; it != ss.data + ss.length; /**/ ) - { - if( *it == '%' && (it + 3) < ss.data + ss.length ) - { - // separate the number to avoid dragging in chars from behind into the parsing - char mychar[3] = { it[1], it[2], 0 }; - size_t nbr = strtoul16( mychar); - it += 3; - *out++ = (char)(nbr & 0xFF); - } else - { - *out++ = *it++; - } - } + // find and convert all %xy special chars + char* out = ss.data; + for( const char* it = ss.data; it != ss.data + ss.length; /**/ ) + { + if( *it == '%' && (it + 3) < ss.data + ss.length ) + { + // separate the number to avoid dragging in chars from behind into the parsing + char mychar[3] = { it[1], it[2], 0 }; + size_t nbr = strtoul16( mychar); + it += 3; + *out++ = (char)(nbr & 0xFF); + } else + { + *out++ = *it++; + } + } - // adjust length and terminator of the shortened string - *out = 0; - ss.length = (ptrdiff_t) (out - ss.data); + // adjust length and terminator of the shortened string + *out = 0; + ss.length = (ptrdiff_t) (out - ss.data); } // ------------------------------------------------------------------------------------------------ @@ -1589,17 +1569,17 @@ const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const // Finds a node in the collada scene by the given SID const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const { - if( pNode->mSID == pSID) - return pNode; + if( pNode->mSID == pSID) + return pNode; - for( size_t a = 0; a < pNode->mChildren.size(); ++a) - { - const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID); - if( node) - return node; - } + for( size_t a = 0; a < pNode->mChildren.size(); ++a) + { + const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID); + if( node) + return node; + } - return NULL; + return NULL; } // ------------------------------------------------------------------------------------------------ @@ -1613,12 +1593,12 @@ std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) else if (!pNode->mID.empty()) return pNode->mID; else if (!pNode->mSID.empty()) - return pNode->mSID; - else + return pNode->mSID; + else { // No need to worry. Unnamed nodes are no problem at all, except // if cameras or lights need to be assigned to them. - return boost::str( boost::format( "$ColladaAutoName$_%d") % mNodeNameCounter++); + return boost::str( boost::format( "$ColladaAutoName$_%d") % mNodeNameCounter++); } } diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 353c50812..19d269682 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)); @@ -2661,7 +2693,7 @@ void ColladaParser::ReadScene() // ------------------------------------------------------------------------------------------------ // Aborts the file reading with an exception -void ColladaParser::ThrowException( const std::string& pError) const +AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const { throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError)); } diff --git a/code/ColladaParser.h b/code/ColladaParser.h index 561ff05b3..553f9caf4 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -212,7 +212,7 @@ protected: protected: /** Aborts the file reading with an exception */ - void ThrowException( const std::string& pError) const; + AI_WONT_RETURN void ThrowException( const std::string& pError) const AI_WONT_RETURN_SUFFIX; /** Skips all data until the end node of the current element */ void SkipElement(); 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) { diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index ba5db4625..9d5e6ac7e 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -78,7 +78,8 @@ namespace { // ------------------------------------------------------------------------------------------------ // signal tokenization error, this is always unrecoverable. Throws DeadlyImportError. -void TokenizeError(const std::string& message, unsigned int offset) +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX; +AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) { throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset)); } 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); 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: diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index bdeb40a09..62f8d9c0c 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -68,13 +68,15 @@ namespace { // ------------------------------------------------------------------------------------------------ // signal parse error, this is always unrecoverable. Throws DeadlyImportError. - void ParseError(const std::string& message, const Token& token) + AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) { throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token)); } // ------------------------------------------------------------------------------------------------ - void ParseError(const std::string& message, const Element* element = NULL) + AI_WONT_RETURN void ParseError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void ParseError(const std::string& message, const Element* element) { if(element) { ParseError(message,element->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/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/IFCBoolean.cpp b/code/IFCBoolean.cpp index 731deca9f..c1b43695c 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,287 @@ 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); + + // 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 = (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 = ((srcVertices[0]) - p) * n > -1e-6; + for( size_t a = 0; a < srcVtxCount; ++a ) + { + IfcVector3 e0 = srcVertices[a]; + IfcVector3 e1 = 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 ) + { + if( trgt.empty() || (trgt.back() - ei).SquareLength() > 1e-12 ) + trgt.push_back(ei); + isCurrentlyOnWhiteSide = !isCurrentlyOnWhiteSide; + std::vector& newtrgt = isCurrentlyOnWhiteSide ? whiteside : blackside; + newtrgt.push_back(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]; + // 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(); + 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()); + } + + // 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..24ed772e0 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 @@ -428,7 +429,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 +454,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 +559,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(), 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 // 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 +577,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 @@ -671,10 +677,11 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion const STEP::DB::RefMap& refs = conv.db.GetRefs(); // skip over space and annotation nodes - usually, these have no meaning in Assimp's context + bool skipGeometry = false; if(conv.settings.skipSpaceRepresentations) { if(const IfcSpace* const space = el.ToPtr()) { IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings"); - return NULL; + skipGeometry = true; } } @@ -844,8 +851,10 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion conv.apply_openings = &openings; } - ProcessProductRepresentation(el,nd.get(),subnodes,conv); - conv.apply_openings = conv.collect_openings = NULL; + if (!skipGeometry) { + ProcessProductRepresentation(el,nd.get(),subnodes,conv); + conv.apply_openings = conv.collect_openings = NULL; + } if (subnodes.size()) { nd->mChildren = new aiNode*[subnodes.size()](); diff --git a/code/IFCMaterial.cpp b/code/IFCMaterial.cpp index 081dd7e19..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 { @@ -132,45 +133,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 != std::numeric_limits::max() ) + return prevMatId; + + // we're still here - create an default material if required, or simply fail otherwise + if( !forceDefaultMat ) + return std::numeric_limits::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..c47f8254a 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 { @@ -1194,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; diff --git a/code/IFCUtil.cpp b/code/IFCUtil.cpp index 38fd400e0..800ffd3ed 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); @@ -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) const + { + 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 } } 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/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/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/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/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/ParsingUtils.h b/code/ParsingUtils.h index 25495fd51..93a44d0a6 100644 --- a/code/ParsingUtils.h +++ b/code/ParsingUtils.h @@ -201,7 +201,12 @@ template AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len) { if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { - in += len+1; + if (in[len] != '\0') { + in += len+1; + } else { + // If EOF after the token make sure we don't go past end of buffer + in += len; + } return true; } diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index d61c4e5a7..deb7da094 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -64,6 +64,24 @@ static const aiImporterDesc desc = { "ply" }; + +// ------------------------------------------------------------------------------------------------ +// Internal stuff +namespace +{ + // ------------------------------------------------------------------------------------------------ + // Checks that property index is within range + template + const T &GetProperty(const std::vector &props, int idx) + { + if (idx >= props.size()) + throw DeadlyImportError("Invalid .ply file: Property index is out of range."); + + return props[idx]; + } +} + + // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PLYImporter::PLYImporter() @@ -431,13 +449,13 @@ void PLYImporter::LoadTextureCoordinates(std::vector* pvOut) if (0xFFFFFFFF != aiPositions[0]) { vOut.x = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]); + GetProperty((*i).alProperties, aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF != aiPositions[1]) { vOut.y = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]); + GetProperty((*i).alProperties, aiPositions[1]).avList.front(),aiTypes[1]); } // and add them to our nice list pvOut->push_back(vOut); @@ -541,19 +559,19 @@ void PLYImporter::LoadVertices(std::vector* pvOut, bool p_bNormals) if (0xFFFFFFFF != aiPositions[0]) { vOut.x = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]); + GetProperty((*i).alProperties, aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF != aiPositions[1]) { vOut.y = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]); + GetProperty((*i).alProperties, aiPositions[1]).avList.front(),aiTypes[1]); } if (0xFFFFFFFF != aiPositions[2]) { vOut.z = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]); + GetProperty((*i).alProperties, aiPositions[2]).avList.front(),aiTypes[2]); } // and add them to our nice list @@ -659,28 +677,28 @@ void PLYImporter::LoadVertexColor(std::vector* pvOut) if (0xFFFFFFFF != aiPositions[0]) { - vOut.r = NormalizeColorValue((*i).alProperties[ - aiPositions[0]].avList.front(),aiTypes[0]); + vOut.r = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF != aiPositions[1]) { - vOut.g = NormalizeColorValue((*i).alProperties[ - aiPositions[1]].avList.front(),aiTypes[1]); + vOut.g = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[1]).avList.front(),aiTypes[1]); } if (0xFFFFFFFF != aiPositions[2]) { - vOut.b = NormalizeColorValue((*i).alProperties[ - aiPositions[2]].avList.front(),aiTypes[2]); + vOut.b = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[2]).avList.front(),aiTypes[2]); } // assume 1.0 for the alpha channel ifit is not set if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f; else { - vOut.a = NormalizeColorValue((*i).alProperties[ - aiPositions[3]].avList.front(),aiTypes[3]); + vOut.a = NormalizeColorValue(GetProperty((*i).alProperties, + aiPositions[3]).avList.front(),aiTypes[3]); } // and add them to our nice list @@ -773,11 +791,11 @@ void PLYImporter::LoadFaces(std::vector* pvOut) // parse the list of vertex indices if (0xFFFFFFFF != iProperty) { - const unsigned int iNum = (unsigned int)(*i).alProperties[iProperty].avList.size(); + const unsigned int iNum = (unsigned int)GetProperty((*i).alProperties, iProperty).avList.size(); sFace.mIndices.resize(iNum); std::vector::const_iterator p = - (*i).alProperties[iProperty].avList.begin(); + GetProperty((*i).alProperties, iProperty).avList.begin(); for (unsigned int a = 0; a < iNum;++a,++p) { @@ -789,7 +807,7 @@ void PLYImporter::LoadFaces(std::vector* pvOut) if (0xFFFFFFFF != iMaterialIndex) { sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo( - (*i).alProperties[iMaterialIndex].avList.front(),eType2); + GetProperty((*i).alProperties, iMaterialIndex).avList.front(),eType2); } pvOut->push_back(sFace); } @@ -800,7 +818,7 @@ void PLYImporter::LoadFaces(std::vector* pvOut) // a value of -1 indicates a restart of the strip bool flip = false; for (std::vector::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) { - const std::vector& quak = (*i).alProperties[iProperty].avList; + const std::vector& quak = GetProperty((*i).alProperties, iProperty).avList; pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u)); int aiTable[2] = {-1,-1}; @@ -851,30 +869,30 @@ void PLYImporter::GetMaterialColor(const std::vector& avL if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f; else { - clrOut->r = NormalizeColorValue(avList[ - aiPositions[0]].avList.front(),aiTypes[0]); + clrOut->r = NormalizeColorValue(GetProperty(avList, + aiPositions[0]).avList.front(),aiTypes[0]); } if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f; else { - clrOut->g = NormalizeColorValue(avList[ - aiPositions[1]].avList.front(),aiTypes[1]); + clrOut->g = NormalizeColorValue(GetProperty(avList, + aiPositions[1]).avList.front(),aiTypes[1]); } if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f; else { - clrOut->b = NormalizeColorValue(avList[ - aiPositions[2]].avList.front(),aiTypes[2]); + clrOut->b = NormalizeColorValue(GetProperty(avList, + aiPositions[2]).avList.front(),aiTypes[2]); } // assume 1.0 for the alpha channel ifit is not set if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f; else { - clrOut->a = NormalizeColorValue(avList[ - aiPositions[3]].avList.front(),aiTypes[3]); + clrOut->a = NormalizeColorValue(GetProperty(avList, + aiPositions[3]).avList.front(),aiTypes[3]); } } @@ -1025,7 +1043,7 @@ void PLYImporter::LoadMaterial(std::vector* pvOut) // handle phong power and shading mode int iMode; if (0xFFFFFFFF != iPhong) { - float fSpec = PLY::PropertyInstance::ConvertTo((*i).alProperties[iPhong].avList.front(),ePhong); + float fSpec = PLY::PropertyInstance::ConvertTo(GetProperty((*i).alProperties, iPhong).avList.front(),ePhong); // if shininess is 0 (and the pow() calculation would therefore always // become 1, not depending on the angle), use gouraud lighting @@ -1043,7 +1061,7 @@ void PLYImporter::LoadMaterial(std::vector* pvOut) // handle opacity if (0xFFFFFFFF != iOpacity) { - float fOpacity = PLY::PropertyInstance::ConvertTo((*i).alProperties[iPhong].avList.front(),eOpacity); + float fOpacity = PLY::PropertyInstance::ConvertTo(GetProperty((*i).alProperties, iPhong).avList.front(),eOpacity); pcHelper->AddProperty(&fOpacity, 1, AI_MATKEY_OPACITY); } 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/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, 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/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. 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); 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 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/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/DefaultLogger.hpp b/include/assimp/DefaultLogger.hpp index 7efd11750..771ab9d59 100644 --- a/include/assimp/DefaultLogger.hpp +++ b/include/assimp/DefaultLogger.hpp @@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file DefaultLogger.h +/** @file DefaultLogger.hpp */ #ifndef INCLUDED_AI_DEFAULTLOGGER diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index fbf1ce1d6..8ed98b3a2 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file export.hpp +/** @file Exporter.hpp * @brief Defines the CPP-API for the Assimp export interface */ #ifndef AI_EXPORT_HPP_INC @@ -181,7 +181,7 @@ public: * about the output data flow of the export process. * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL. * @param pPath Full target file name. Target must be accessible. - * @param pPreprocessing Accepts any choice of the #aiPostProcessing enumerated + * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated * flags, but in reality only a subset of them makes sense here. Specifying * 'preprocessing' flags is useful if the input scene does not conform to * Assimp's default conventions as specified in the @link data Data Structures Page @endlink. diff --git a/include/assimp/IOStream.hpp b/include/assimp/IOStream.hpp index 72f2e6611..4c1ba93a1 100644 --- a/include/assimp/IOStream.hpp +++ b/include/assimp/IOStream.hpp @@ -38,7 +38,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file IOStream.h +/** @file IOStream.hpp * @brief File I/O wrappers for C++. */ diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 197988f72..c04514feb 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file IOSystem.h +/** @file IOSystem.hpp * @brief File system wrapper for C++. Inherit this class to supply * custom file handling logic to the Import library. */ diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 5e7797b99..8aa9e5ae5 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file assimp.hpp +/** @file Importer.hpp * @brief Defines the C++-API to the Open Asset Import Library. */ #ifndef INCLUDED_AI_ASSIMP_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/LogStream.hpp b/include/assimp/LogStream.hpp index 25b1c65f5..a405e4b51 100644 --- a/include/assimp/LogStream.hpp +++ b/include/assimp/LogStream.hpp @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file LogStream.h +/** @file LogStream.hpp * @brief Abstract base class 'LogStream', representing an output log stream. */ #ifndef INCLUDED_AI_LOGSTREAM_H diff --git a/include/assimp/NullLogger.hpp b/include/assimp/NullLogger.hpp index 38472b0c2..7b1895069 100644 --- a/include/assimp/NullLogger.hpp +++ b/include/assimp/NullLogger.hpp @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file NullLogger.h +/** @file NullLogger.hpp * @brief Dummy logger */ diff --git a/include/assimp/ProgressHandler.hpp b/include/assimp/ProgressHandler.hpp index 3ab1e489b..ab96f2ac5 100644 --- a/include/assimp/ProgressHandler.hpp +++ b/include/assimp/ProgressHandler.hpp @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file ProgressHandler.h +/** @file ProgressHandler.hpp * @brief Abstract base class 'ProgressHandler'. */ #ifndef INCLUDED_AI_PROGRESSHANDLER_H @@ -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 // ------------------------------------------------------------------------------------ diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 1f946d6ec..202446009 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -1,4 +1,4 @@ -/** @file assert.h +/** @file ai_assert.h */ #ifndef AI_DEBUG_H_INC #define AI_DEBUG_H_INC diff --git a/include/assimp/cexport.h b/include/assimp/cexport.h index 9041621a1..d9cb12761 100644 --- a/include/assimp/cexport.h +++ b/include/assimp/cexport.h @@ -122,10 +122,7 @@ ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn); * @param pFormatId ID string to specify to which format you want to export to. Use * aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. * @param pFileName Output file to write -* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. -* If none is supplied, a default implementation using standard file IO is used. Note that -* #aiExportSceneToBlob is provided as convenience function to export to memory buffers. -* @param pPreprocessing Accepts any choice of the #aiPostProcessing enumerated +* @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated * flags, but in reality only a subset of them makes sense here. Specifying * 'preprocessing' flags is useful if the input scene does not conform to * Assimp's default conventions as specified in the @link data Data Structures Page @endlink. @@ -183,7 +180,7 @@ ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene, // -------------------------------------------------------------------------------- /** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an -* exported scene. The memory referred by this structure is owned by Assimp. Use #aiReleaseExportedFile() +* exported scene. The memory referred by this structure is owned by Assimp. * to free its resources. Don't try to free the memory on your side - it will crash for most build configurations * due to conflicting heaps. * diff --git a/include/assimp/cfileio.h b/include/assimp/cfileio.h index 221198d1c..e3b71e9b2 100644 --- a/include/assimp/cfileio.h +++ b/include/assimp/cfileio.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiFileIO.h +/** @file cfileio.h * @brief Defines generic C routines to access memory-mapped files */ #ifndef AI_FILEIO_H_INC diff --git a/include/assimp/cimport.h b/include/assimp/cimport.h index 9080c16b6..6e85882d1 100644 --- a/include/assimp/cimport.h +++ b/include/assimp/cimport.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file assimp.h +/** @file cimport.h * @brief Defines the C-API to the Open Asset Import Library. */ #ifndef AI_ASSIMP_H_INC @@ -139,7 +139,17 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( // -------------------------------------------------------------------------------- /** Same as #aiImportFileEx, but adds an extra parameter containing importer settings. * + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @param pFS aiFileIO structure. Will be used to open the model file itself + * and any other files the loader needs to open. Pass NULL to use the default + * implementation. * @param pProps #aiPropertyStore instance containing import settings. + * @return Pointer to the imported data or NULL if the import failed. + * @note Include for the definition of #aiFileIO. * @see aiImportFileEx */ ASSIMP_API const C_STRUCT aiScene* aiImportFileExWithProperties( @@ -188,7 +198,29 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemory( // -------------------------------------------------------------------------------- /** Same as #aiImportFileFromMemory, but adds an extra parameter containing importer settings. * + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #aiApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non empty string, + * the library looks for a loader to support the file extension specified by pHint + * and passes the file to the first matching loader. If this loader is unable to + * completely the request, the library continues and tries to determine the file + * format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... * @param pProps #aiPropertyStore instance containing import settings. + * @return A pointer to the imported data, NULL if the import failed. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular aiImportFileEx()/aiImportFileExWithProperties() API. * @see aiImportFileFromMemory */ ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties( @@ -210,7 +242,7 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties( * meaning this is still the same #aiScene which you passed for pScene. However, * _if_ post-processing failed, the scene could now be NULL. That's quite a rare * case, post processing steps are not really designed to 'fail'. To be exact, - * the #aiProcess_ValidateDS flag is currently the only post processing step + * the #aiProcess_ValidateDataStructure flag is currently the only post processing step * which can actually cause the scene to be reset to NULL. */ ASSIMP_API const C_STRUCT aiScene* aiApplyPostProcessing( @@ -266,7 +298,7 @@ ASSIMP_API void aiEnableVerboseLogging(aiBool d); // -------------------------------------------------------------------------------- /** Detach a custom log stream from the libraries' logging system. * - * This is the counterpart of #aiAttachPredefinedLogStream. If you attached a stream, + * This is the counterpart of #aiAttachLogStream. If you attached a stream, * don't forget to detach it again. * @param stream The log stream to be detached. * @return AI_SUCCESS if the log stream has been detached successfully. @@ -356,8 +388,9 @@ ASSIMP_API void aiReleasePropertyStore(C_STRUCT aiPropertyStore* p); * interface, properties are always shared by all imports. It is not possible to * specify them per import. * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. * @param szName Name of the configuration property to be set. All supported - * public properties are defined in the config.h header file (#AI_CONFIG_XXX). + * public properties are defined in the config.h header file (AI_CONFIG_XXX). * @param value New value for the property */ ASSIMP_API void aiSetImportPropertyInteger( @@ -372,8 +405,9 @@ ASSIMP_API void aiSetImportPropertyInteger( * interface, properties are always shared by all imports. It is not possible to * specify them per import. * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. * @param szName Name of the configuration property to be set. All supported - * public properties are defined in the config.h header file (#AI_CONFIG_XXX). + * public properties are defined in the config.h header file (AI_CONFIG_XXX). * @param value New value for the property */ ASSIMP_API void aiSetImportPropertyFloat( @@ -388,10 +422,10 @@ ASSIMP_API void aiSetImportPropertyFloat( * interface, properties are always shared by all imports. It is not possible to * specify them per import. * - * @param property store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. * @param szName Name of the configuration property to be set. All supported - * public properties are defined in the config.h header file (#AI_CONFIG_XXX). - * @param value New value for the property + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param st New value for the property */ ASSIMP_API void aiSetImportPropertyString( C_STRUCT aiPropertyStore* store, @@ -405,10 +439,10 @@ ASSIMP_API void aiSetImportPropertyString( * interface, properties are always shared by all imports. It is not possible to * specify them per import. * - * @param property store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. * @param szName Name of the configuration property to be set. All supported - * public properties are defined in the config.h header file (#AI_CONFIG_XXX). - * @param value New value for the property + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param mat New value for the property */ ASSIMP_API void aiSetImportPropertyMatrix( C_STRUCT aiPropertyStore* store, diff --git a/include/assimp/color4.h b/include/assimp/color4.h index bd8ff6fb4..4677858eb 100644 --- a/include/assimp/color4.h +++ b/include/assimp/color4.h @@ -38,7 +38,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiColor4D.h +/** @file color4.h * @brief RGBA color structure, including operators when compiling in C++ */ #ifndef AI_COLOR4D_H_INC diff --git a/include/assimp/color4.inl b/include/assimp/color4.inl index 2c83872d4..d47a60e23 100644 --- a/include/assimp/color4.inl +++ b/include/assimp/color4.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiColor4D.inl +/** @file color4.inl * @brief Inline implementation of aiColor4t operators */ #ifndef AI_COLOR4D_INL_INC diff --git a/include/assimp/config.h b/include/assimp/config.h index 56ef8f3b5..abe3f1102 100644 --- a/include/assimp/config.h +++ b/include/assimp/config.h @@ -209,7 +209,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. "PP_RRM_EXCLUDE_LIST" // --------------------------------------------------------------------------- -/** @brief Configures the #aiProcess_PretransformVertices step to +/** @brief Configures the #aiProcess_PreTransformVertices step to * keep the scene hierarchy. Meshes are moved to worldspace, but * no optimization is performed (read: meshes with equal materials are not * joined. The total number of meshes won't change). @@ -224,7 +224,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. "PP_PTV_KEEP_HIERARCHY" // --------------------------------------------------------------------------- -/** @brief Configures the #aiProcess_PretransformVertices step to normalize +/** @brief Configures the #aiProcess_PreTransformVertices step to normalize * all vertex components into the [-1,1] range. That is, a bounding box * for the whole scene is computed, the maximum component is taken and all * meshes are scaled appropriately (uniformly of course!). @@ -234,7 +234,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. "PP_PTV_NORMALIZE" // --------------------------------------------------------------------------- -/** @brief Configures the #aiProcess_PretransformVertices step to use +/** @brief Configures the #aiProcess_PreTransformVertices step to use * a users defined matrix as the scene root node transformation before * transforming vertices. * Property type: bool. Default value: false. @@ -243,7 +243,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. "PP_PTV_ADD_ROOT_TRANSFORMATION" // --------------------------------------------------------------------------- -/** @brief Configures the #aiProcess_PretransformVertices step to use +/** @brief Configures the #aiProcess_PreTransformVertices step to use * a users defined matrix as the scene root node transformation before * transforming vertices. This property correspond to the 'a1' component * of the transformation matrix. @@ -376,7 +376,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // --------------------------------------------------------------------------- /** @brief Enumerates components of the aiScene and aiMesh data structures - * that can be excluded from the import using the #aiPrpcess_RemoveComponent step. + * that can be excluded from the import using the #aiProcess_RemoveComponent step. * * See the documentation to #aiProcess_RemoveComponent for more details. */ @@ -715,7 +715,7 @@ enum aiComponent /** @brief Tells the MD3 loader which skin files to load. * * When loading MD3 files, Assimp checks whether a file - * _.skin is existing. These files are used by + * [md3_file_name]_[skin_name].skin is existing. These files are used by * Quake III to be able to assign different skins (e.g. red and blue team) * to models. 'default', 'red', 'blue' are typical skin names. * Property type: String. Default value: "default". @@ -728,14 +728,14 @@ enum aiComponent * MD3 file. This can also be a search path. * * By default Assimp's behaviour is as follows: If a MD3 file - * /models///.md3 is + * any_path/models/any_q3_subdir/model_name/file_name.md3 is * loaded, the library tries to locate the corresponding shader file in - * /scripts/.shader. This property overrides this + * any_path/scripts/model_name.shader. This property overrides this * behaviour. It can either specify a full path to the shader to be loaded * or alternatively the path (relative or absolute) to the directory where * the shaders for all MD3s to be loaded reside. Assimp attempts to open - * /.shader first, /.shader - * is the fallback file. Note that should have a terminal (back)slash. + * IMPORT_MD3_SHADER_SRC/model_name.shader first, IMPORT_MD3_SHADER_SRC/file_name.shader + * is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash. * Property type: String. Default value: n/a. */ #define AI_CONFIG_IMPORT_MD3_SHADER_SRC \ @@ -818,12 +818,13 @@ enum aiComponent /** @brief Ogre Importer detect the texture usage from its filename. * * Ogre material texture units do not define texture type, the textures usage - * depends on the used shader or Ogres fixed pipeline. If this config property + * depends on the used shader or Ogre's fixed pipeline. If this config property * is true Assimp will try to detect the type from the textures filename postfix: * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec, * _specular and _specularmap for specular map, _l, _light, _lightmap, _occ * and _occlusion for light map, _disp and _displacement for displacement map. - * The matching is case insensitive. Post fix is taken between last "_" and last ".". + * The matching is case insensitive. Post fix is taken between the last + * underscore and the last period. * Default behavior is to detect type from lower cased texture unit name by * matching against: normalmap, specularmap, lightmap and displacementmap. * For both cases if no match is found aiTextureType_DIFFUSE is used. diff --git a/include/assimp/defs.h b/include/assimp/defs.h index e0019f893..6b8ac3416 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiDefines.h +/** @file defs.h * @brief Assimp build configuration setup. See the notes in the comment * blocks to find out how to customize _your_ Assimp build. */ @@ -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__) diff --git a/include/assimp/importerdesc.h b/include/assimp/importerdesc.h index 5cc9ef302..e61112a17 100644 --- a/include/assimp/importerdesc.h +++ b/include/assimp/importerdesc.h @@ -98,8 +98,7 @@ struct aiImporterDesc /** Implementation comments, i.e. unimplemented features*/ const char* mComments; - /** Any combination of the #aiLoaderFlags enumerated values. - These flags indicate some characteristics common to many + /** These flags indicate some characteristics common to many importers. */ unsigned int mFlags; diff --git a/include/assimp/material.h b/include/assimp/material.h index 2c8ad1d5c..03e46b0be 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -95,14 +95,9 @@ enum aiTextureOp aiTextureOp_SignedAdd = 0x5, - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiTextureOp_Force32Bit = INT_MAX #endif - //! @endcond }; // --------------------------------------------------------------------------- @@ -131,14 +126,9 @@ enum aiTextureMapMode */ aiTextureMapMode_Mirror = 0x2, - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiTextureMapMode_Force32Bit = INT_MAX #endif - //! @endcond }; // --------------------------------------------------------------------------- @@ -176,14 +166,9 @@ enum aiTextureMapping aiTextureMapping_OTHER = 0x5, - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiTextureMapping_Force32Bit = INT_MAX #endif - //! @endcond }; // --------------------------------------------------------------------------- @@ -296,14 +281,9 @@ enum aiTextureType aiTextureType_UNKNOWN = 0xC, - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiTextureType_Force32Bit = INT_MAX #endif - //! @endcond }; #define AI_TEXTURE_TYPE_MAX aiTextureType_UNKNOWN @@ -374,14 +354,9 @@ enum aiShadingMode aiShadingMode_Fresnel = 0xa, - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif - //! @endcond }; @@ -420,14 +395,9 @@ enum aiTextureFlags */ aiTextureFlags_IgnoreAlpha = 0x4, - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiTextureFlags_Force32Bit = INT_MAX #endif - //! @endcond }; @@ -442,8 +412,8 @@ enum aiTextureFlags * @code * SourceColor * SourceBlend + DestColor * DestBlend * @endcode - * where is the previous color in the framebuffer at this - * position and is the material colro before the transparency + * where DestColor is the previous color in the framebuffer at this + * position and SourceColor is the material colro before the transparency * calculation.
* This corresponds to the #AI_MATKEY_BLEND_FUNC property. */ @@ -469,14 +439,9 @@ enum aiBlendMode // we don't need more for the moment, but we might need them // in future versions ... - /** @cond never - * This value is not used. It forces the compiler to use at least - * 32 Bit integers to represent this enum. - */ #ifndef SWIG _aiBlendMode_Force32Bit = INT_MAX #endif - //! @endcond }; @@ -862,7 +827,9 @@ public: /** @brief Remove a given key from the list. * * The function fails if the key isn't found - * @param pKey Key to be deleted */ + * @param pKey Key to be deleted + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ aiReturn RemoveProperty (const char* pKey, unsigned int type = 0, unsigned int index = 0); @@ -1330,6 +1297,8 @@ extern "C" { #define AI_MATKEY_TEXFLAGS_UNKNOWN(N) \ AI_MATKEY_TEXFLAGS(aiTextureType_UNKNOWN,N) +//! @endcond +//! // --------------------------------------------------------------------------- /** @brief Retrieve a material property with a specific key from the material * @@ -1537,6 +1506,7 @@ ASSIMP_API unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMa * Pass NULL if you're not interested in this information. Otherwise, * pass a pointer to an array of two aiTextureMapMode's (one for each * axis, UV order). + * @param[out] flags Receives the the texture flags. * @return AI_SUCCESS on success, otherwise something else. Have fun.*/ // --------------------------------------------------------------------------- #ifdef __cplusplus diff --git a/include/assimp/material.inl b/include/assimp/material.inl index 3c269cd1b..2658922bc 100644 --- a/include/assimp/material.inl +++ b/include/assimp/material.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiMaterial.inl +/** @file material.inl * @brief Defines the C++ getters for the material system */ diff --git a/include/assimp/matrix3x3.inl b/include/assimp/matrix3x3.inl index e8a34f9af..f10b45a83 100644 --- a/include/assimp/matrix3x3.inl +++ b/include/assimp/matrix3x3.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiMatrix3x3.inl +/** @file matrix3x3.inl * @brief Inline implementation of the 3x3 matrix operators */ #ifndef AI_MATRIX3x3_INL_INC diff --git a/include/assimp/matrix4x4.inl b/include/assimp/matrix4x4.inl index 4cae3afa7..5e4903e70 100644 --- a/include/assimp/matrix4x4.inl +++ b/include/assimp/matrix4x4.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiMatrix4x4t.inl +/** @file matrix4x4.inl * @brief Inline implementation of the 4x4 matrix operators */ #ifndef AI_MATRIX4x4_INL_INC @@ -52,7 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "quaternion.h" #include -#include +#include #include // ---------------------------------------------------------------------------------------- diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index e283e4105..44f3ebf47 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -668,8 +668,7 @@ struct aiMesh } //! Check whether the mesh contains positions. Provided no special - //! scene flags are set (such as #AI_SCENE_FLAGS_ANIM_SKELETON_ONLY), - //! this will always be true + //! scene flags are set, this will always be true bool HasPositions() const { return mVertices != NULL && mNumVertices > 0; } diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index f078fd692..3904718a2 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -282,7 +282,7 @@ enum aiPostProcessSteps /**
Searches for redundant/unreferenced materials and removes them. * * This is especially useful in combination with the - * #aiProcess_PretransformVertices and #aiProcess_OptimizeMeshes flags. + * #aiProcess_PreTransformVertices and #aiProcess_OptimizeMeshes flags. * Both join small meshes with equal characteristics, but they can't do * their work if two meshes have different materials. Because several * material settings are lost during Assimp's import filters, @@ -335,7 +335,7 @@ enum aiPostProcessSteps * To have the degenerate stuff not only detected and collapsed but * removed, try one of the following procedures: *
1. (if you support lines and points for rendering but don't - * want the degenerates)
+ * want the degenerates)
*
    *
  • Specify the #aiProcess_FindDegenerates flag. *
  • @@ -345,7 +345,7 @@ enum aiPostProcessSteps * pipeline steps. * *
- *
2.(if you don't support lines and points at all)
+ *
2.(if you don't support lines and points at all)
*
    *
  • Specify the #aiProcess_FindDegenerates flag. *
  • @@ -550,7 +550,7 @@ enum aiPostProcessSteps // --------------------------------------------------------------------------------------- -/** @def aiProcessPreset_TargetRealtimeUse_Fast +/** @def aiProcessPreset_TargetRealtime_Fast * @brief Default postprocess configuration optimizing the data for real-time rendering. * * Applications would want to use this preset to load models on end-user PCs, diff --git a/include/assimp/quaternion.inl b/include/assimp/quaternion.inl index 181b6ffcc..6fae14017 100644 --- a/include/assimp/quaternion.inl +++ b/include/assimp/quaternion.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiQuaterniont.inl +/** @file quaternion.inl * @brief Inline implementation of aiQuaterniont operators */ #ifndef AI_QUATERNION_INL_INC diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 87f13152f..1096e18ac 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiScene.h +/** @file scene.h * @brief Defines the data structures in which the imported scene is returned. */ #ifndef __AI_SCENE_H_INC__ @@ -182,8 +182,6 @@ struct aiNode } - /** @override - */ inline const aiNode* FindNode(const char* name) const { if (!::strcmp( mName.data,name))return this; @@ -217,7 +215,7 @@ struct aiNode // ------------------------------------------------------------------------------- -/** @def AI_SCENE_FLAGS_INCOMPLETE +/** * Specifies that the scene data structure that was imported is not complete. * This flag bypasses some internal validations and allows the import * of animation skeletons, material libraries or camera animation paths @@ -225,14 +223,14 @@ struct aiNode */ #define AI_SCENE_FLAGS_INCOMPLETE 0x1 -/** @def AI_SCENE_FLAGS_VALIDATED +/** * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) * if the validation is successful. In a validated scene you can be sure that * any cross references in the data structure (e.g. vertex indices) are valid. */ #define AI_SCENE_FLAGS_VALIDATED 0x2 -/** @def AI_SCENE_FLAGS_VALIDATION_WARNING +/** * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) * if the validation is successful but some issues have been found. * This can for example mean that a texture that does not exist is referenced @@ -242,7 +240,7 @@ struct aiNode */ #define AI_SCENE_FLAGS_VALIDATION_WARNING 0x4 -/** @def AI_SCENE_FLAGS_NON_VERBOSE_FORMAT +/** * This flag is currently only set by the aiProcess_JoinIdenticalVertices step. * It indicates that the vertices of the output meshes aren't in the internal * verbose format anymore. In the verbose format all vertices are unique, @@ -250,7 +248,7 @@ struct aiNode */ #define AI_SCENE_FLAGS_NON_VERBOSE_FORMAT 0x8 - /** @def AI_SCENE_FLAGS_TERRAIN + /** * Denotes pure height-map terrain data. Pure terrains usually consist of quads, * sometimes triangles, in a regular grid. The x,y coordinates of all vertex * positions refer to the x,y coordinates on the terrain height map, the z-axis diff --git a/include/assimp/types.h b/include/assimp/types.h index 83767ad46..291e055cf 100644 --- a/include/assimp/types.h +++ b/include/assimp/types.h @@ -241,7 +241,7 @@ struct aiColor3D * For most applications, it will be absolutely sufficient to interpret the * aiString as ASCII data and work with it as one would work with a plain char*. * Windows users in need of proper support for i.e asian characters can use the - * #MultiByteToWideChar(), #WideCharToMultiByte() WinAPI functionality to convert the + * MultiByteToWideChar(), WideCharToMultiByte() WinAPI functionality to convert the * UTF-8 strings to their working character set (i.e. MBCS, WideChar). * * We use this representation instead of std::string to be C-compatible. The @@ -388,6 +388,8 @@ typedef enum aiReturn * Force 32-bit size enum */ _AI_ENFORCE_ENUM_SIZE = 0x7fffffff + + /// @endcond } aiReturn; // !enum aiReturn // just for backwards compatibility, don't use these constants anymore @@ -414,13 +416,14 @@ enum aiOrigin * Force 32-bit size enum */ _AI_ORIGIN_ENFORCE_ENUM_SIZE = 0x7fffffff + + /// @endcond }; // !enum aiOrigin // ---------------------------------------------------------------------------------- /** @brief Enumerates predefined log streaming destinations. * Logging to these streams can be enabled with a single call to - * #LogStream::createDefaultStream or #aiAttachPredefinedLogStream(), - * respectively. + * #LogStream::createDefaultStream. */ enum aiDefaultLogStream { @@ -442,6 +445,7 @@ enum aiDefaultLogStream * Force 32-bit size enum */ _AI_DLS_ENFORCE_ENUM_SIZE = 0x7fffffff + /// @endcond }; // !enum aiDefaultLogStream // just for backwards compatibility, don't use these constants anymore diff --git a/include/assimp/vector2.h b/include/assimp/vector2.h index d033e5a15..e43606123 100644 --- a/include/assimp/vector2.h +++ b/include/assimp/vector2.h @@ -38,18 +38,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiVector2t.h +/** @file vector2.h * @brief 2D vector structure, including operators when compiling in C++ */ #ifndef AI_VECTOR2D_H_INC #define AI_VECTOR2D_H_INC - + #ifdef __cplusplus # include #else # include #endif - + #include "./Compiler/pushpack1.h" // ---------------------------------------------------------------------------------- diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index f6cedea1d..5ff03be17 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiVector2D.inl +/** @file vector2.inl * @brief Inline implementation of aiVector2t operators */ #ifndef AI_VECTOR2D_INL_INC diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index a4c33b194..38716db56 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -38,18 +38,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiVector3D.h +/** @file vector3.h * @brief 3D vector structure, including operators when compiling in C++ */ #ifndef AI_VECTOR3D_H_INC #define AI_VECTOR3D_H_INC - + #ifdef __cplusplus # include #else # include #endif - + #include "./Compiler/pushpack1.h" #ifdef __cplusplus diff --git a/include/assimp/vector3.inl b/include/assimp/vector3.inl index 7ba92f6d0..bae22f616 100644 --- a/include/assimp/vector3.inl +++ b/include/assimp/vector3.inl @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiVector3D.inl +/** @file vector3.inl * @brief Inline implementation of aiVector3t operators */ #ifndef AI_VECTOR3D_INL_INC diff --git a/include/assimp/version.h b/include/assimp/version.h index 44e797a11..7d2bbdb31 100644 --- a/include/assimp/version.h +++ b/include/assimp/version.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file aiVersion.h +/** @file version.h * @brief Functions to query the version of the Assimp runtime, check * compile flags, ... */ diff --git a/test/models-nonbsd/PLY/ant-half.ply b/test/models-nonbsd/PLY/ant-half.ply new file mode 100644 index 000000000..47cf9a9a4 --- /dev/null +++ b/test/models-nonbsd/PLY/ant-half.ply @@ -0,0 +1,754 @@ +ply +format ascii 1.0 +element vertex 486 +property float32 x +property float32 y +property float32 z +element face 912 +property list uint8 int32 vertex_indices +end_header +-1.106 3.844 9.073 +-0.3523 0.4402 11.07 +1.028 3.76 9.209 +0.137 -0.5632 10.73 +2.01 4.503 5.887 +0.07813 5.232 6.794 +-0.7266 3.741 3.839 +-2.789 3.179 5.07 +-0.9185 2.402 3.279 +1.445 2.139 3.394 +-3.114 1.498 5.285 +-1.317 -1.269 8.385 +-0.854 -1.319 5.036 +-0.9351 5.347 0.6162 +-0.8184 2.622 3.235 +0.4875 5.529 -0.2241 +3.193 2.756 1.633 +3.213 1.145 -2.568 +-2.25 1.208 2.767 +0.3225 0.8339 3.323 +1.354 -1.725 1.105 +0.1887 1.133 -5.785 +-3.637 2.696 0.3325 +-3.706 0.8936 -0.5452 +-0.4326 -0.8737 -4.095 +-2.994 -0.4858 0.9023 +0.1252 -2.079 -1.51 +2.856 -0.7459 -1.187 +2.32 3.733 8.13 +2.031 3.721 9.095 +2.819 3.567 8.537 +1.994 2.581 9.711 +2.64 2.491 9.542 +3.187 2.528 8.375 +1.628 2.491 7.518 +1.62 3.721 8.274 +0.9868 2.471 8.914 +1.552 2.106 9.414 +2.657 1.774 8.918 +-1.805 3.733 8.13 +-2.094 3.721 9.095 +-1.306 3.567 8.537 +-2.131 2.581 9.711 +-1.485 2.491 9.542 +-0.938 2.528 8.375 +-2.497 2.491 7.518 +-2.505 3.721 8.274 +-3.138 2.471 8.914 +-2.573 2.106 9.414 +-1.469 1.774 8.918 +1.549 3.638 6.803 +1.355 3.638 6.7 +1.424 5.126 6.883 +1.615 5.083 6.981 +1.169 3.638 6.815 +1.237 5.115 6.997 +1.176 3.638 7.035 +1.241 5.061 7.21 +1.369 3.638 7.138 +1.433 5.018 7.308 +1.556 3.638 7.023 +1.62 5.029 7.194 +1.625 6.512 7.421 +1.811 6.429 7.504 +1.437 6.49 7.531 +1.434 6.386 7.724 +1.619 6.304 7.807 +1.808 6.326 7.697 +1.945 7.702 8.276 +2.121 7.585 8.335 +1.754 7.672 8.38 +1.74 7.525 8.542 +1.916 7.408 8.601 +2.107 7.439 8.497 +2.362 8.615 9.391 +2.526 8.473 9.417 +2.168 8.578 9.487 +2.138 8.398 9.608 +2.303 8.255 9.635 +2.497 8.293 9.539 +2.847 9.189 10.69 +2.998 9.03 10.68 +2.649 9.148 10.78 +2.603 8.947 10.85 +2.753 8.788 10.84 +2.95 8.83 10.75 +3.368 9.385 12.08 +3.503 9.22 12.03 +3.167 9.342 12.16 +3.101 9.134 12.18 +3.236 8.97 12.13 +3.438 9.013 12.06 +3.889 9.189 13.48 +4.01 9.03 13.39 +3.684 9.148 13.54 +3.599 8.947 13.51 +3.719 8.788 13.42 +3.925 8.83 13.36 +4.374 8.615 14.77 +4.481 8.473 14.65 +4.165 8.578 14.83 +4.063 8.398 14.76 +4.17 8.255 14.63 +4.379 8.293 14.57 +4.791 7.702 15.89 +4.886 7.585 15.73 +4.579 7.672 15.94 +4.462 7.525 15.82 +4.556 7.408 15.66 +4.769 7.439 15.62 +5.111 6.512 16.74 +5.196 6.429 16.56 +4.896 6.49 16.78 +4.768 6.386 16.64 +4.853 6.304 16.46 +5.068 6.326 16.42 +-1.141 5.126 6.883 +-1.072 3.638 6.7 +-1.266 3.638 6.803 +-1.332 5.083 6.981 +-0.9541 5.115 6.997 +-0.886 3.638 6.815 +-0.9585 5.061 7.21 +-0.8928 3.638 7.035 +-1.15 5.018 7.308 +-1.086 3.638 7.138 +-1.337 5.029 7.194 +-1.272 3.638 7.023 +-1.342 6.512 7.421 +-1.528 6.429 7.504 +-1.154 6.49 7.531 +-1.151 6.386 7.724 +-1.336 6.304 7.807 +-1.525 6.326 7.697 +-1.662 7.702 8.276 +-1.838 7.585 8.335 +-1.471 7.672 8.38 +-1.457 7.525 8.542 +-1.633 7.408 8.601 +-1.824 7.439 8.497 +-2.079 8.615 9.391 +-2.243 8.473 9.417 +-1.885 8.578 9.487 +-1.855 8.398 9.608 +-2.02 8.255 9.635 +-2.214 8.293 9.539 +-2.564 9.189 10.69 +-2.715 9.03 10.68 +-2.366 9.148 10.78 +-2.32 8.947 10.85 +-2.47 8.788 10.84 +-2.667 8.83 10.75 +-3.085 9.385 12.08 +-3.22 9.22 12.03 +-2.884 9.342 12.16 +-2.818 9.134 12.18 +-2.953 8.97 12.13 +-3.155 9.013 12.06 +-3.606 9.189 13.48 +-3.726 9.03 13.39 +-3.4 9.148 13.54 +-3.316 8.947 13.51 +-3.437 8.788 13.42 +-3.642 8.83 13.36 +-4.091 8.615 14.77 +-4.198 8.473 14.65 +-3.882 8.578 14.83 +-3.78 8.398 14.76 +-3.887 8.255 14.63 +-4.096 8.293 14.57 +-4.508 7.702 15.89 +-4.603 7.585 15.73 +-4.296 7.672 15.94 +-4.179 7.525 15.82 +-4.273 7.408 15.66 +-4.485 7.439 15.62 +-4.828 6.512 16.74 +-4.913 6.429 16.56 +-4.613 6.49 16.78 +-4.484 6.386 16.64 +-4.57 6.304 16.46 +-4.784 6.326 16.42 +-1.519 -2.27 2.989 +-1.987 -1.082 3.408 +-2.709 -1.989 3.536 +-1.513 -2.259 2.974 +-2.524 -1.536 2.471 +-4.318 -0.3933 4.187 +-4.692 -0.6204 3.123 +-6.274 0.9227 4.173 +-5.533 1.852 4.043 +-6.084 1.387 3.082 +-6.844 2.279 4.015 +-6.83 1.957 4.012 +-6.886 2.118 3.737 +-7.536 1.346 4.658 +-7.229 0.9683 4.298 +-8.053 1.724 3.821 +-7.747 1.346 3.46 +-9.085 -0.1982 5.078 +-9.347 -0.1982 3.597 +-8.702 -0.7393 4.247 +-10.91 -1.528 5.154 +-10.39 -2.11 4.847 +-11.08 -1.528 4.148 +-10.49 -2.11 4.26 +-11.58 -2.509 4.768 +-11.57 -2.523 4.76 +-11.59 -2.509 4.742 +-11.57 -2.523 4.744 +-12.39 -3.546 5.269 +-11.93 -3.852 5.034 +-12.52 -3.546 4.548 +-12.87 -4.803 5.622 +-12.58 -5.09 4.606 +-13.49 -4.516 4.767 +-13.11 -6.264 5.38 +-14.78 -6.803 5.643 +-14.49 -7.092 4.622 +-15.35 -8.042 5.637 +-14.73 -8.43 5.528 +-15.15 -8.236 4.95 +-15.78 -9.378 5.503 +-15.77 -9.385 5.497 +-15.78 -9.375 5.495 +-15.78 -9.378 5.486 +-15.77 -9.383 5.484 +-1.522 -2.262 -1.24 +-1.519 -2.258 -1.231 +-1.522 -2.262 -1.222 +-1.529 -2.27 -1.222 +-1.529 -2.27 -1.24 +-2.061 -1.082 -0.8906 +-2.428 -1.536 -1.907 +-3.082 -0.4963 -0.5522 +-3.082 -0.4963 -1.91 +-3.759 -1.333 -1.628 +-5.567 0.1257 -0.8323 +-5.665 1.852 -0.8818 +-6.417 0.9227 -0.8818 +-6.041 1.387 -1.924 +-6.95 2.279 -1.137 +-6.937 1.957 -1.326 +-6.943 2.118 -1.418 +-7.378 0.9683 -0.9246 +-8.108 1.724 -0.9246 +-7.743 1.346 -1.84 +-8.797 0.8371 -0.6052 +-9.793 0.269 -1.611 +-8.891 -0.6654 -1.611 +-10.59 -2.11 -0.9326 +-10.57 -0.523 -0.8679 +-11.3 -1.375 -0.9326 +-10.94 -1.742 -1.823 +-11.75 -2.506 -1.223 +-11.73 -2.523 -1.223 +-11.74 -2.514 -1.247 +-12.76 -3.466 -1.017 +-12.14 -3.852 -1.017 +-12.45 -3.659 -1.656 +-13.16 -4.803 -0.6001 +-12.7 -5.09 -1.55 +-13.62 -4.516 -1.55 +-13.36 -6.264 -0.8809 +-15.05 -6.803 -0.9114 +-14.58 -7.092 -1.866 +-15.6 -8.042 -1.016 +-14.98 -8.43 -1.016 +-15.29 -8.236 -1.658 +-16.01 -9.378 -1.222 +-15.99 -9.385 -1.226 +-16.01 -9.375 -1.231 +-16.01 -9.378 -1.24 +-16 -9.383 -1.24 +-1.522 -2.27 -4.918 +-2.105 -1.082 -4.684 +-2.827 -1.989 -4.812 +-1.513 -2.262 -4.935 +-2.29 -1.536 -5.749 +-4.562 -0.3933 -4.75 +-4.55 -0.6204 -5.878 +-6.395 0.9227 -5.432 +-5.655 1.852 -5.302 +-5.844 1.387 -6.393 +-6.877 2.279 -5.776 +-6.863 1.957 -5.773 +-6.821 2.118 -6.051 +-7.747 1.346 -5.408 +-7.335 0.9683 -5.641 +-7.947 1.724 -6.372 +-7.536 1.346 -6.605 +-9.347 -0.1982 -5.543 +-9.085 -0.1982 -7.025 +-8.702 -0.7393 -6.193 +-11.08 -1.528 -6.095 +-10.49 -2.11 -6.206 +-10.91 -1.528 -7.101 +-10.39 -2.11 -6.794 +-11.59 -2.509 -6.689 +-11.57 -2.523 -6.691 +-11.58 -2.509 -6.715 +-11.57 -2.523 -6.707 +-12.52 -3.546 -6.495 +-12.01 -3.852 -6.559 +-12.39 -3.546 -7.217 +-13.09 -4.803 -6.326 +-12.47 -5.09 -7.18 +-13.38 -4.516 -7.341 +-13.24 -6.264 -6.637 +-14.89 -6.803 -6.96 +-14.27 -7.092 -7.819 +-15.42 -8.042 -7.159 +-14.8 -8.43 -7.051 +-15 -8.236 -7.738 +-15.78 -9.378 -7.432 +-15.77 -9.385 -7.434 +-15.78 -9.375 -7.442 +-15.78 -9.378 -7.45 +-15.77 -9.383 -7.448 +1.522 -2.27 -4.918 +1.511 -2.258 -4.926 +1.513 -2.262 -4.935 +2.828 -1.989 -4.812 +2.105 -1.082 -4.684 +2.29 -1.536 -5.749 +4.55 -0.6204 -5.878 +4.562 -0.3933 -4.75 +6.395 0.9227 -5.432 +5.655 1.852 -5.302 +5.844 1.387 -6.393 +6.877 2.279 -5.776 +6.863 1.957 -5.773 +6.821 2.118 -6.051 +7.335 0.9683 -5.641 +7.747 1.346 -5.408 +7.947 1.724 -6.372 +7.536 1.346 -6.605 +9.347 -0.1982 -5.543 +9.086 -0.1982 -7.025 +8.702 -0.7393 -6.193 +11.08 -1.528 -6.095 +10.49 -2.11 -6.206 +10.91 -1.528 -7.101 +10.39 -2.11 -6.794 +11.59 -2.509 -6.689 +11.57 -2.523 -6.691 +11.58 -2.509 -6.715 +11.57 -2.523 -6.707 +12.52 -3.546 -6.495 +12.01 -3.852 -6.559 +12.39 -3.546 -7.217 +12.47 -5.09 -7.18 +13.09 -4.803 -6.326 +13.38 -4.516 -7.341 +13.24 -6.264 -6.637 +14.89 -6.803 -6.96 +14.27 -7.092 -7.819 +15.42 -8.042 -7.159 +14.8 -8.43 -7.051 +15 -8.236 -7.738 +15.78 -9.378 -7.432 +15.77 -9.385 -7.434 +15.78 -9.375 -7.442 +15.78 -9.378 -7.45 +15.77 -9.383 -7.448 +-0.9592 -0.9547 10.35 +0.007813 0.3928 11.6 +-0.7324 1.062 10.63 +0.007813 -1.855 11.37 +0.007813 -2.459 10.63 +0.007813 0.3928 9.662 +-0.3928 1.509 10.63 +0.7327 1.062 10.63 +-0.007813 0.3928 11.6 +0.9595 -0.9547 10.35 +-0.007813 -1.855 11.37 +-0.007813 -2.459 10.63 +-0.007813 0.3928 9.662 +0.3931 1.509 10.63 +1.513 -2.262 2.988 +1.511 -2.258 2.979 +1.516 -2.262 2.97 +1.522 -2.27 2.971 +1.52 -2.27 2.989 +1.987 -1.082 3.408 +2.524 -1.536 2.471 +2.933 -0.4963 3.919 +3.168 -0.4963 2.581 +3.786 -1.333 2.977 +5.429 0.1257 4.074 +5.534 1.852 4.042 +6.274 0.9227 4.173 +6.085 1.387 3.082 +6.863 1.957 3.827 +6.844 2.279 4.015 +6.886 2.118 3.737 +7.229 0.9683 4.298 +7.947 1.724 4.425 +7.747 1.346 3.46 +8.57 0.8371 4.859 +9.726 0.269 4.042 +8.838 -0.6654 3.885 +10.39 -2.11 4.847 +10.37 -0.523 4.909 +11.09 -1.375 4.97 +10.89 -1.742 4.032 +11.59 -2.506 4.763 +11.57 -2.523 4.76 +11.58 -2.514 4.739 +12.55 -3.466 5.142 +11.93 -3.852 5.034 +12.35 -3.659 4.459 +12.58 -5.09 4.606 +12.87 -4.803 5.622 +13.49 -4.516 4.767 +13.11 -6.264 5.38 +14.78 -6.803 5.643 +14.49 -7.092 4.622 +15.35 -8.042 5.637 +14.73 -8.43 5.528 +15.15 -8.236 4.95 +15.78 -9.378 5.503 +15.77 -9.385 5.497 +15.78 -9.375 5.495 +15.78 -9.378 5.486 +15.77 -9.383 5.484 +1.529 -2.27 -1.222 +1.519 -2.258 -1.231 +1.522 -2.262 -1.24 +2.795 -1.989 -0.8906 +2.062 -1.082 -0.8906 +2.428 -1.536 -1.907 +4.677 -0.6204 -1.642 +4.493 -0.3933 -0.5283 +6.417 0.9227 -0.8818 +5.665 1.852 -0.8818 +6.041 1.387 -1.924 +6.951 2.279 -1.137 +6.937 1.957 -1.137 +6.944 2.118 -1.418 +7.379 0.9683 -0.9246 +7.743 1.346 -0.623 +8.108 1.724 -1.538 +7.743 1.346 -1.84 +9.343 -0.1982 -0.4792 +9.343 -0.1982 -1.983 +8.82 -0.7393 -1.231 +11.15 -1.528 -0.7207 +10.59 -2.11 -0.9326 +11.15 -1.528 -1.742 +10.59 -2.11 -1.53 +11.75 -2.509 -1.218 +11.73 -2.523 -1.223 +11.75 -2.509 -1.245 +11.73 -2.523 -1.239 +12.63 -3.546 -0.8647 +12.14 -3.852 -1.017 +12.63 -3.546 -1.598 +12.7 -5.09 -1.55 +13.16 -4.803 -0.6001 +13.62 -4.516 -1.55 +13.36 -6.264 -0.8804 +15.05 -6.803 -0.9111 +14.58 -7.092 -1.866 +15.6 -8.042 -1.016 +14.98 -8.43 -1.016 +15.29 -8.236 -1.658 +16.01 -9.378 -1.222 +15.99 -9.385 -1.226 +16.01 -9.375 -1.231 +16.01 -9.378 -1.24 +16 -9.383 -1.24 +-0.7932 4.462 -7.734 +-0.3843 1.974 -5.495 +1.95 3.241 -7.348 +1.167 0.6428 -6.109 +3.808 0.1871 -9.572 +3.644 1.688 -11.88 +0.07617 4.961 -10.12 +3.28 -0.5704 -14.15 +0.2488 -1.848 -16.78 +-0.9744 -2.686 -16.57 +0.7307 -3.159 -16.47 +-2.678 2.963 -8.185 +-2.616 -2.779 -12.24 +-1.159 -1.533 -8.922 +3 0 1 2 +3 1 3 2 +3 2 4 5 +3 4 6 5 +3 5 6 7 +3 7 6 8 +3 2 3 9 +3 2 9 4 +3 4 9 6 +3 6 9 8 +3 5 0 2 +3 5 7 0 +3 8 10 7 +3 7 10 0 +3 0 10 1 +3 1 10 11 +3 1 11 3 +3 10 12 11 +3 11 12 3 +3 3 12 9 +3 10 8 12 +3 12 8 9 +3 13 14 15 +3 15 14 16 +3 17 15 16 +3 18 19 14 +3 14 19 16 +3 19 20 16 +3 15 17 21 +3 15 21 13 +3 14 13 18 +3 21 22 13 +3 13 22 18 +3 21 23 22 +3 21 24 23 +3 23 25 22 +3 22 25 18 +3 18 25 20 +3 18 20 19 +3 23 26 25 +3 25 26 20 +3 23 24 26 +3 26 24 27 +3 26 27 20 +3 20 27 16 +3 24 21 27 +3 27 21 17 +3 27 17 16 +3 28 29 30 +3 31 32 29 +3 29 32 30 +3 32 33 30 +3 30 33 28 +3 28 33 34 +3 28 34 35 +3 35 34 36 +3 28 35 29 +3 35 36 29 +3 29 36 31 +3 36 37 31 +3 31 37 32 +3 37 38 32 +3 32 38 33 +3 36 34 37 +3 37 34 38 +3 38 34 33 +3 39 40 41 +3 42 43 40 +3 40 43 41 +3 43 44 41 +3 41 44 39 +3 39 44 45 +3 39 45 46 +3 46 45 47 +3 39 46 40 +3 46 47 40 +3 40 47 42 +3 47 48 42 +3 42 48 43 +3 48 49 43 +3 43 49 44 +3 47 45 48 +3 48 45 49 +3 49 45 44 +3 50 51 52 +3 50 52 53 +3 51 54 55 +3 51 55 52 +3 54 56 57 +3 54 57 55 +3 56 58 59 +3 56 59 57 +3 58 60 61 +3 58 61 59 +3 60 50 53 +3 60 53 61 +3 53 52 62 +3 53 62 63 +3 52 55 64 +3 52 64 62 +3 55 57 65 +3 55 65 64 +3 57 59 66 +3 57 66 65 +3 59 61 67 +3 59 67 66 +3 61 53 63 +3 61 63 67 +3 63 62 68 +3 63 68 69 +3 62 64 70 +3 62 70 68 +3 64 65 71 +3 64 71 70 +3 65 66 72 +3 65 72 71 +3 66 67 73 +3 66 73 72 +3 67 63 69 +3 67 69 73 +3 69 68 74 +3 69 74 75 +3 68 70 76 +3 68 76 74 +3 70 71 77 +3 70 77 76 +3 71 72 78 +3 71 78 77 +3 72 73 79 +3 72 79 78 +3 73 69 75 +3 73 75 79 +3 75 74 80 +3 75 80 81 +3 74 76 82 +3 74 82 80 +3 76 77 83 +3 76 83 82 +3 77 78 84 +3 77 84 83 +3 78 79 85 +3 78 85 84 +3 79 75 81 +3 79 81 85 +3 81 80 86 +3 81 86 87 +3 80 82 88 +3 80 88 86 +3 82 83 89 +3 82 89 88 +3 83 84 90 +3 83 90 89 +3 84 85 91 +3 84 91 90 +3 85 81 87 +3 85 87 91 +3 87 86 92 +3 87 92 93 +3 86 88 94 +3 86 94 92 +3 88 89 95 +3 88 95 94 +3 89 90 96 +3 89 96 95 +3 90 91 97 +3 90 97 96 +3 91 87 93 +3 91 93 97 +3 93 92 98 +3 93 98 99 +3 92 94 100 +3 92 100 98 +3 94 95 101 +3 94 101 100 +3 95 96 102 +3 95 102 101 +3 96 97 103 +3 96 103 102 +3 97 93 99 +3 97 99 103 +3 99 98 104 +3 99 104 105 +3 98 100 106 +3 98 106 104 +3 100 101 107 +3 100 107 106 +3 101 102 108 +3 101 108 107 +3 102 103 109 +3 102 109 108 +3 103 99 105 +3 103 105 109 +3 105 104 110 +3 105 110 111 +3 104 106 112 +3 104 112 110 +3 106 107 113 +3 106 113 112 +3 107 108 114 +3 107 114 113 +3 108 109 115 +3 108 115 114 +3 109 105 111 +3 109 111 115 +3 54 51 50 +3 56 54 50 +3 58 56 50 +3 60 58 50 +3 112 113 110 +3 113 114 110 +3 114 115 110 +3 115 111 110 +3 116 117 118 +3 119 116 118 +3 120 121 117 +3 116 120 117 +3 122 123 121 +3 120 122 121 +3 124 125 123 +3 122 124 123 +3 126 127 125 +3 124 126 125 +3 119 118 127 +3 126 119 127 +3 128 116 119 +3 129 128 119 +3 130 120 116 +3 128 130 116 +3 131 122 120 +3 130 131 120 +3 132 124 122 +3 131 132 122 +3 133 126 124 +3 132 133 124 +3 129 119 126 +3 133 129 126 +3 134 128 129 +3 135 134 129 +3 136 130 128 +3 134 136 128 +3 137 131 130 +3 136 137 130 +3 138 132 131 +3 137 138 131 +3 139 133 132 +3 138 139 132 +3 135 129 133 +3 139 135 133 +3 140 134 135 +3 141 140 135 +3 142 136 134 +3 140 142 134 +3 143 137 136 +3 142 143 136 +3 144 138 137 +3 143 144 137 +3 145 139 138 +3 144 145 138 +3 141 135 139 diff --git a/test/models-nonbsd/PLY/ant-half.ply.license b/test/models-nonbsd/PLY/ant-half.ply.license new file mode 100644 index 000000000..cca7fc278 --- /dev/null +++ b/test/models-nonbsd/PLY/ant-half.ply.license @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. 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 "