pax_global_header00006660000000000000000000000064121761617150014521gustar00rootroot0000000000000052 comment=78e7783a237b8cfc3491aaad04bda5fe9568106e .qmake.conf000066400000000000000000000000561217616171500131110ustar00rootroot00000000000000load(qt_build_config) MODULE_VERSION = 5.2.0 .tag000066400000000000000000000000511217616171500116350ustar00rootroot0000000000000078e7783a237b8cfc3491aaad04bda5fe9568106e 3rdparty/000077500000000000000000000000001217616171500126355ustar00rootroot000000000000003rdparty/assimp/000077500000000000000000000000001217616171500141315ustar00rootroot000000000000003rdparty/assimp/LICENSE000066400000000000000000000041151217616171500151370ustar00rootroot00000000000000Open Asset Import Library (Assimp) Copyright (c) 2006-2010, Assimp Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. AN EXCEPTION applies to all files in the ./test/models-nonbsd subfolder. These are 3d models for testing purposes, from various free sources on the internet. They are - unless otherwise stated - copyright of their respective creators, which may impose additional requirements on the use of their work. For any of these models, see .source.txt for more legal information. Contact us if you are a copyright holder and believe that we credited you inproperly or if you don't want your files to appear in the repository. 3rdparty/assimp/README000066400000000000000000000040151217616171500150110ustar00rootroot00000000000000 Open Asset Import Library (Assimp) Readme ----------------------------------------- Here you can find information regarding Open Asset Import Library: 1. Overview 1.1 Supported file formats 1.2 File structure 2. Build the Asset Import Library 3. Help 4. License 1. Overview 1.1 Supported file formats The Asset Import Library provides a lot of model formats: - 3DS - ASE - DXF - HMP - MD2 - MD3 - MD5 - MDC - MDL - NFF - PLY - STL - X - LWO - OBJ - SMD - Collada - LWO - Ogre XML - partly LWS - .. + many more, see http://assimp.sourceforge.net/main_features_formats.html for a full list. 1.2 Repository structure Open Asset Import Library is implemented in C++ and provides a C-interface. The directory structure is: /bin Binaries of the Asset Import Library. /code Code of the Asset Import Library. /contrib Third-party-libraries used by the Asset Import Library. /doc Documentation (doxygen generated and data structure as a xml file) /include Public headers. /lib Static library location for Windows. /obj Object file location for Windows. /port Ports to other languages. /test Unit- and regression tests, model test suite. /tools Tools (viewer, command line `assimp`). /workspaces Build enviroments for vc,xcode,... 2. Build the Asset Import Library Take a look into the INSTALL file. 3. Help For more help go to http://assimp.sourceforge.net/ (or, for germans, http://www.zfx.info). Or take a look into the doc-folder, which contains the doxygen-generated documentation in HTMl format (CHMs for Windows are as well contained in some distributions and located right here in the root folder). 4. License The license of the Asset Import Library is based on the BSD-License. It is contained in the LICENSE file. 3rdparty/assimp/assimp.pri000066400000000000000000000256211217616171500161470ustar00rootroot00000000000000# This file mostly g3nerated by "qmake -project" - use that to regenerate if large scale # changes occur to this directory # AssImp expects this to be defined on debug builds CONFIG(debug, debug|release) : DEFINES+=_DEBUG CONFIG += exceptions win32:DEFINES+=_CRT_SECURE_NO_WARNINGS VPATH += \ $$PWD \ $$PWD/code \ $$PWD/contrib/ConvertUTF \ $$PWD/contrib/zlib \ $$PWD/contrib/irrXML \ $$PWD/contrib/unzip INCLUDEPATH += \ $$PWD \ $$PWD/code \ $$PWD/code/BoostWorkaround \ $$PWD/include \ $$PWD/include/Compiler \ $$PWD/contrib/ConvertUTF \ $$PWD/contrib/zlib \ $$PWD/contrib/irrXML \ $$PWD/contrib/unzip # Input HEADERS += \ revision.h \ code/3DSHelper.h \ code/3DSLoader.h \ code/ACLoader.h \ code/ASELoader.h \ code/ASEParser.h \ code/assbin_chunks.h \ code/AssimpPCH.h \ code/B3DImporter.h \ code/BaseImporter.h \ code/BaseProcess.h \ code/BlenderDNA.h \ code/BlenderIntermediate.h \ code/BlenderLoader.h \ code/BlenderModifier.h \ code/BlenderScene.h \ code/BlenderSceneGen.h \ code/BVHLoader.h \ code/ByteSwap.h \ code/CalcTangentsProcess.h \ code/COBLoader.h \ code/COBScene.h \ code/ColladaHelper.h \ code/ColladaLoader.h \ code/ColladaParser.h \ code/ComputeUVMappingProcess.h \ code/ConvertToLHProcess.h \ code/CSMLoader.h \ code/DefaultIOStream.h \ code/DefaultIOSystem.h \ code/DefaultProgressHandler.h \ code/DXFLoader.h \ code/Exceptional.h \ code/fast_atof.h \ code/FileLogStream.h \ code/FileSystemFilter.h \ code/FindDegenerates.h \ code/FindInstancesProcess.h \ code/FindInvalidDataProcess.h \ code/FixNormalsStep.h \ code/GenericProperty.h \ code/GenFaceNormalsProcess.h \ code/GenVertexNormalsProcess.h \ code/HalfLifeFileData.h \ code/Hash.h \ code/HMPFileData.h \ code/HMPLoader.h \ code/IFF.h \ code/ImproveCacheLocality.h \ code/IRRLoader.h \ code/IRRMeshLoader.h \ code/IRRShared.h \ code/irrXMLWrapper.h \ code/JoinVerticesProcess.h \ code/LimitBoneWeightsProcess.h \ code/LineSplitter.h \ code/LWOAnimation.h \ code/LWOFileData.h \ code/LWOLoader.h \ code/LWSLoader.h \ code/MakeVerboseFormat.h \ code/MaterialSystem.h \ code/MD2FileData.h \ code/MD2Loader.h \ code/MD2NormalTable.h \ code/MD3FileData.h \ code/MD3Loader.h \ code/MD4FileData.h \ code/MD5Loader.h \ code/MD5Parser.h \ code/MDCFileData.h \ code/MDCLoader.h \ code/MDCNormalTable.h \ code/MDLDefaultColorMap.h \ code/MDLFileData.h \ code/MDLLoader.h \ code/MemoryIOWrapper.h \ code/MS3DLoader.h \ code/NDOLoader.h \ code/NFFLoader.h \ code/ObjFileData.h \ code/ObjFileImporter.h \ code/ObjFileMtlImporter.h \ code/ObjFileParser.h \ code/ObjTools.h \ code/OFFLoader.h \ code/OgreImporter.h \ code/OgreXmlHelper.h \ code/OptimizeGraph.h \ code/OptimizeMeshes.h \ code/ParsingUtils.h \ code/PlyLoader.h \ code/PlyParser.h \ code/PretransformVertices.h \ code/ProcessHelper.h \ code/Profiler.h \ code/pstdint.h \ code/Q3BSPFileData.h \ code/Q3BSPFileImporter.h \ code/Q3BSPFileParser.h \ code/Q3BSPZipArchive.h \ code/Q3DLoader.h \ code/qnan.h \ code/RawLoader.h \ code/RemoveComments.h \ code/RemoveRedundantMaterials.h \ code/RemoveVCProcess.h \ code/SceneCombiner.h \ code/ScenePreprocessor.h \ code/SGSpatialSort.h \ code/SkeletonMeshBuilder.h \ code/SMDLoader.h \ code/SmoothingGroups.h \ code/SortByPTypeProcess.h \ code/SpatialSort.h \ code/SplitLargeMeshes.h \ code/StandardShapes.h \ code/StdOStreamLogStream.h \ code/STLLoader.h \ code/StreamReader.h \ code/StringComparison.h \ code/Subdivision.h \ code/TargetAnimation.h \ code/TerragenLoader.h \ code/TextureTransform.h \ code/TinyFormatter.h \ code/TriangulateProcess.h \ code/UnrealLoader.h \ code/ValidateDataStructure.h \ code/Vertex.h \ code/VertexTriangleAdjacency.h \ code/Win32DebugLogStream.h \ code/XFileHelper.h \ code/XFileImporter.h \ code/XFileParser.h \ include/aiAnim.h \ include/aiAssert.h \ include/aiCamera.h \ include/aiColor4D.h \ include/aiConfig.h \ include/aiDefines.h \ include/aiFileIO.h \ include/aiLight.h \ include/aiMaterial.h \ include/aiMatrix3x3.h \ include/aiMatrix4x4.h \ include/aiMesh.h \ include/aiPostProcess.h \ include/aiQuaternion.h \ include/aiScene.h \ include/aiTexture.h \ include/aiTypes.h \ include/aiVector2D.h \ include/aiVector3D.h \ include/aiVersion.h \ include/assimp.h \ include/assimp.hpp \ include/DefaultLogger.h \ include/IOStream.h \ include/IOSystem.h \ include/Logger.h \ include/LogStream.h \ include/NullLogger.h \ include/ProgressHandler.h \ code/res/resource.h \ contrib/ConvertUTF/ConvertUTF.h \ contrib/irrXML/CXMLReaderImpl.h \ contrib/irrXML/heapsort.h \ contrib/irrXML/irrArray.h \ contrib/irrXML/irrString.h \ contrib/irrXML/irrTypes.h \ contrib/irrXML/irrXML.h \ contrib/unzip/crypt.h \ contrib/unzip/ioapi.h \ contrib/unzip/unzip.h \ contrib/zlib/crc32.h \ contrib/zlib/deflate.h \ contrib/zlib/inffast.h \ contrib/zlib/inffixed.h \ contrib/zlib/inflate.h \ contrib/zlib/inftrees.h \ contrib/zlib/trees.h \ contrib/zlib/zconf.h \ contrib/zlib/zconf.in.h \ contrib/zlib/zlib.h \ contrib/zlib/zutil.h \ include/Compiler/poppack1.h \ include/Compiler/pushpack1.h \ code/BoostWorkaround/boost/foreach.hpp \ code/BoostWorkaround/boost/format.hpp \ code/BoostWorkaround/boost/lexical_cast.hpp \ code/BoostWorkaround/boost/pointer_cast.hpp \ code/BoostWorkaround/boost/scoped_array.hpp \ code/BoostWorkaround/boost/scoped_ptr.hpp \ code/BoostWorkaround/boost/shared_array.hpp \ code/BoostWorkaround/boost/shared_ptr.hpp \ code/BoostWorkaround/boost/static_assert.hpp \ code/BoostWorkaround/boost/timer.hpp \ code/BoostWorkaround/boost/math/common_factor_rt.hpp \ code/BoostWorkaround/boost/tuple/tuple.hpp \ include/aiVector3D.inl \ include/aiColor4D.inl \ include/aiMatrix3x3.inl \ include/aiMatrix4x4.inl \ include/aiMaterial.inl \ code/SmoothingGroups.inl \ code/BlenderDNA.inl SOURCES += \ code/3DSConverter.cpp \ code/3DSLoader.cpp \ code/ACLoader.cpp \ code/aiAssert.cpp \ code/ASELoader.cpp \ code/ASEParser.cpp \ code/Assimp.cpp \ code/AssimpPCH.cpp \ code/B3DImporter.cpp \ code/BaseImporter.cpp \ code/BaseProcess.cpp \ code/BlenderDNA.cpp \ code/BlenderLoader.cpp \ code/BlenderModifier.cpp \ code/BlenderScene.cpp \ code/BVHLoader.cpp \ code/CalcTangentsProcess.cpp \ code/COBLoader.cpp \ code/ColladaLoader.cpp \ code/ColladaParser.cpp \ code/ComputeUVMappingProcess.cpp \ code/ConvertToLHProcess.cpp \ code/CSMLoader.cpp \ code/DefaultIOStream.cpp \ code/DefaultIOSystem.cpp \ code/DefaultLogger.cpp \ code/DXFLoader.cpp \ code/FindDegenerates.cpp \ code/FindInstancesProcess.cpp \ code/FindInvalidDataProcess.cpp \ code/FixNormalsStep.cpp \ code/GenFaceNormalsProcess.cpp \ code/GenVertexNormalsProcess.cpp \ code/HMPLoader.cpp \ code/Importer.cpp \ code/ImproveCacheLocality.cpp \ code/IRRLoader.cpp \ code/IRRMeshLoader.cpp \ code/IRRShared.cpp \ code/JoinVerticesProcess.cpp \ code/LimitBoneWeightsProcess.cpp \ code/LWOAnimation.cpp \ code/LWOBLoader.cpp \ code/LWOLoader.cpp \ code/LWOMaterial.cpp \ code/LWSLoader.cpp \ code/MakeVerboseFormat.cpp \ code/MaterialSystem.cpp \ code/MD2Loader.cpp \ code/MD3Loader.cpp \ code/MD5Loader.cpp \ code/MD5Parser.cpp \ code/MDCLoader.cpp \ code/MDLLoader.cpp \ code/MDLMaterialLoader.cpp \ code/MS3DLoader.cpp \ code/NDOLoader.cpp \ code/NFFLoader.cpp \ code/ObjFileImporter.cpp \ code/ObjFileMtlImporter.cpp \ code/ObjFileParser.cpp \ code/OFFLoader.cpp \ code/OgreImporter.cpp \ code/OgreImporterMaterial.cpp \ code/OptimizeGraph.cpp \ code/OptimizeMeshes.cpp \ code/PlyLoader.cpp \ code/PlyParser.cpp \ code/PretransformVertices.cpp \ code/Q3BSPFileImporter.cpp \ code/Q3BSPFileParser.cpp \ code/Q3BSPZipArchive.cpp \ code/Q3DLoader.cpp \ code/RawLoader.cpp \ code/RemoveComments.cpp \ code/RemoveRedundantMaterials.cpp \ code/RemoveVCProcess.cpp \ code/SceneCombiner.cpp \ code/ScenePreprocessor.cpp \ code/SGSpatialSort.cpp \ code/SkeletonMeshBuilder.cpp \ code/SMDLoader.cpp \ code/SortByPTypeProcess.cpp \ code/SpatialSort.cpp \ code/SplitLargeMeshes.cpp \ code/StandardShapes.cpp \ code/STLLoader.cpp \ code/Subdivision.cpp \ code/TargetAnimation.cpp \ code/TerragenLoader.cpp \ code/TextureTransform.cpp \ code/TriangulateProcess.cpp \ code/UnrealLoader.cpp \ code/ValidateDataStructure.cpp \ code/VertexTriangleAdjacency.cpp \ code/XFileImporter.cpp \ code/XFileParser.cpp \ contrib/ConvertUTF/ConvertUTF.c \ contrib/irrXML/irrXML.cpp \ contrib/unzip/ioapi.c \ contrib/unzip/unzip.c \ contrib/zlib/adler32.c \ contrib/zlib/compress.c \ contrib/zlib/crc32.c \ contrib/zlib/deflate.c \ contrib/zlib/inffast.c \ contrib/zlib/inflate.c \ contrib/zlib/inftrees.c \ contrib/zlib/trees.c \ contrib/zlib/zutil.c 3rdparty/assimp/code/000077500000000000000000000000001217616171500150435ustar00rootroot000000000000003rdparty/assimp/code/3DSConverter.cpp000066400000000000000000000765051217616171500200450ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file Implementation of the 3ds importer class */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER // internal headers #include "3DSLoader.h" #include "TargetAnimation.h" using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Setup final material indices, generae a default material if necessary void Discreet3DSImporter::ReplaceDefaultMaterial() { // Try to find an existing material that matches the // typical default material setting: // - no textures // - diffuse color (in grey!) // NOTE: This is here to workaround the fact that some // exporters are writing a default material, too. unsigned int idx = 0xcdcdcdcd; for (unsigned int i = 0; i < mScene->mMaterials.size();++i) { std::string s = mScene->mMaterials[i].mName; for (std::string::iterator it = s.begin(); it != s.end(); ++it) *it = ::tolower(*it); if (std::string::npos == s.find("default"))continue; if (mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.g || mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.b)continue; if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 ) { continue; } idx = i; } if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size(); // now iterate through all meshes and through all faces and // find all faces that are using the default material unsigned int cnt = 0; for (std::vector::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) { for (std::vector::iterator a = (*i).mFaceMaterials.begin(); a != (*i).mFaceMaterials.end();++a) { // NOTE: The additional check seems to be necessary, // some exporters seem to generate invalid data here if (0xcdcdcdcd == (*a)) { (*a) = idx; ++cnt; } else if ( (*a) >= mScene->mMaterials.size()) { (*a) = idx; DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material"); ++cnt; } } } if (cnt && idx == mScene->mMaterials.size()) { // We need to create our own default material D3DS::Material sMat; sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f); sMat.mName = "%%%DEFAULT"; mScene->mMaterials.push_back(sMat); DefaultLogger::get()->info("3DS: Generating default material"); } } // ------------------------------------------------------------------------------------------------ // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh) { for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i) { // check whether all indices are in range for (unsigned int a = 0; a < 3;++a) { if ((*i).mIndices[a] >= sMesh.mPositions.size()) { DefaultLogger::get()->warn("3DS: Vertex index overflow)"); (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1; } if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) { DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)"); (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1; } } } } // ------------------------------------------------------------------------------------------------ // Generate out unique verbose format representation void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh) { // TODO: really necessary? I don't think. Just a waste of memory and time // to do it now in a separate buffer. // Allocate output storage std::vector vNew (sMesh.mFaces.size() * 3); std::vector vNew2; if (sMesh.mTexCoords.size()) vNew2.resize(sMesh.mFaces.size() * 3); for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i) { D3DS::Face& face = sMesh.mFaces[i]; // Positions for (unsigned int a = 0; a < 3;++a,++base) { vNew[base] = sMesh.mPositions[face.mIndices[a]]; if (sMesh.mTexCoords.size()) vNew2[base] = sMesh.mTexCoords[face.mIndices[a]]; face.mIndices[a] = base; } } sMesh.mPositions = vNew; sMesh.mTexCoords = vNew2; } // ------------------------------------------------------------------------------------------------ // Convert a 3DS texture to texture keys in an aiMaterial void CopyTexture(MaterialHelper& mat, D3DS::Texture& texture, aiTextureType type) { // Setup the texture name aiString tex; tex.Set( texture.mMapName); mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); // Setup the texture blend factor if (is_not_qnan(texture.mTextureBlend)) mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); // Setup the texture mapping mode mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0)); mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0)); // Mirroring - double the scaling values // FIXME: this is not really correct ... if (texture.mMapMode == aiTextureMapMode_Mirror) { texture.mScaleU *= 2.f; texture.mScaleV *= 2.f; texture.mOffsetU /= 2.f; texture.mOffsetV /= 2.f; } // Setup texture UV transformations mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); } // ------------------------------------------------------------------------------------------------ // Convert a 3DS material to an aiMaterial void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat, MaterialHelper& mat) { // NOTE: Pass the background image to the viewer by bypassing the // material system. This is an evil hack, never do it again! if (0 != mBackgroundImage.length() && bHasBG) { aiString tex; tex.Set( mBackgroundImage); mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); // Be sure this is only done for the first material mBackgroundImage = std::string(""); } // At first add the base ambient color of the scene to the material oldMat.mAmbient.r += mClrAmbient.r; oldMat.mAmbient.g += mClrAmbient.g; oldMat.mAmbient.b += mClrAmbient.b; aiString name; name.Set( oldMat.mName); mat.AddProperty( &name, AI_MATKEY_NAME); // Material colors mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); // Phong shininess and shininess strength if (D3DS::Discreet3DS::Phong == oldMat.mShading || D3DS::Discreet3DS::Metal == oldMat.mShading) { if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) { oldMat.mShading = D3DS::Discreet3DS::Gouraud; } else { mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); } } // Opacity mat.AddProperty( &oldMat.mTransparency,1,AI_MATKEY_OPACITY); // Bump height scaling mat.AddProperty( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING); // Two sided rendering? if (oldMat.mTwoSided) { int i = 1; mat.AddProperty(&i,1,AI_MATKEY_TWOSIDED); } // Shading mode aiShadingMode eShading = aiShadingMode_NoShading; switch (oldMat.mShading) { case D3DS::Discreet3DS::Flat: eShading = aiShadingMode_Flat; break; // I don't know what "Wire" shading should be, // assume it is simple lambertian diffuse shading case D3DS::Discreet3DS::Wire: { // Set the wireframe flag unsigned int iWire = 1; mat.AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); } case D3DS::Discreet3DS::Gouraud: eShading = aiShadingMode_Gouraud; break; // assume cook-torrance shading for metals. case D3DS::Discreet3DS::Phong : eShading = aiShadingMode_Phong; break; case D3DS::Discreet3DS::Metal : eShading = aiShadingMode_CookTorrance; break; // FIX to workaround a warning with GCC 4 who complained // about a missing case Blinn: here - Blinn isn't a valid // value in the 3DS Loader, it is just needed for ASE case D3DS::Discreet3DS::Blinn : eShading = aiShadingMode_Blinn; break; } mat.AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); // DIFFUSE texture if ( oldMat.sTexDiffuse.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE); // SPECULAR texture if ( oldMat.sTexSpecular.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR); // OPACITY texture if ( oldMat.sTexOpacity.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY); // EMISSIVE texture if ( oldMat.sTexEmissive.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE); // BUMP texture if ( oldMat.sTexBump.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT); // SHININESS texture if ( oldMat.sTexShininess.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS); // REFLECTION texture if ( oldMat.sTexReflective.mMapName.length() > 0) CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION); // Store the name of the material itself, too if ( oldMat.mName.length()) { aiString tex; tex.Set( oldMat.mName); mat.AddProperty( &tex, AI_MATKEY_NAME); } } // ------------------------------------------------------------------------------------------------ // Split meshes by their materials and generate output aiMesh'es void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut) { std::vector avOutMeshes; avOutMeshes.reserve(mScene->mMeshes.size() * 2); unsigned int iFaceCnt = 0,num = 0; aiString name; // we need to split all meshes by their materials for (std::vector::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) { boost::scoped_array< std::vector > aiSplit(new std::vector[mScene->mMaterials.size()]); name.length = ASSIMP_itoa10(name.data,num++); unsigned int iNum = 0; for (std::vector::const_iterator a = (*i).mFaceMaterials.begin(); a != (*i).mFaceMaterials.end();++a,++iNum) { aiSplit[*a].push_back(iNum); } // now generate submeshes for (unsigned int p = 0; p < mScene->mMaterials.size();++p) { if (aiSplit[p].empty()) { continue; } aiMesh* meshOut = new aiMesh(); std::string name_parts((*i).mName); name_parts.append("::"); name_parts.append(mScene->mMaterials.at(p).mName); meshOut->mName.Set(name_parts); meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // be sure to setup the correct material index meshOut->mMaterialIndex = p; // use the color data as temporary storage meshOut->mColors[0] = (aiColor4D*)(&*i); avOutMeshes.push_back(meshOut); // convert vertices meshOut->mNumFaces = (unsigned int)aiSplit[p].size(); meshOut->mNumVertices = meshOut->mNumFaces*3; // allocate enough storage for faces meshOut->mFaces = new aiFace[meshOut->mNumFaces]; iFaceCnt += meshOut->mNumFaces; meshOut->mVertices = new aiVector3D[meshOut->mNumVertices]; meshOut->mNormals = new aiVector3D[meshOut->mNumVertices]; if ((*i).mTexCoords.size()) { meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices]; } for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q) { register unsigned int index = aiSplit[p][q]; aiFace& face = meshOut->mFaces[q]; face.mIndices = new unsigned int[3]; face.mNumIndices = 3; for (unsigned int a = 0; a < 3;++a,++base) { unsigned int idx = (*i).mFaces[index].mIndices[a]; meshOut->mVertices[base] = (*i).mPositions[idx]; meshOut->mNormals [base] = (*i).mNormals[idx]; if ((*i).mTexCoords.size()) meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx]; face.mIndices[a] = base; } } } } // Copy them to the output array pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) { pcOut->mMeshes[a] = avOutMeshes[a]; } // We should have at least one face here if (!iFaceCnt) { throw DeadlyImportError("No faces loaded. The mesh is empty"); } } // ------------------------------------------------------------------------------------------------ // Add a node to the scenegraph and setup its final transformation void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut, D3DS::Node* pcIn, aiMatrix4x4& /* absTrafo */) { std::vector iArray; iArray.reserve(3); aiMatrix4x4 abs; // Find all meshes with the same name as the node for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) { const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0]; ai_assert(NULL != pcMesh); if (pcIn->mName == pcMesh->mName) iArray.push_back(a); } if (!iArray.empty()) { // The matrix should be identical for all meshes with the // same name. It HAS to be identical for all meshes ..... D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]); // Compute the inverse of the transformation matrix to move the // vertices back to their relative and local space aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat; mInv.Inverse();mInvTransposed.Transpose(); aiVector3D pivot = pcIn->vPivot; pcOut->mNumMeshes = (unsigned int)iArray.size(); pcOut->mMeshes = new unsigned int[iArray.size()]; for (unsigned int i = 0;i < iArray.size();++i) { const unsigned int iIndex = iArray[i]; aiMesh* const mesh = pcSOut->mMeshes[iIndex]; // Transform the vertices back into their local space // fixme: consider computing normals after this, so we don't need to transform them const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices; aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals; for (;pvCurrent != pvEnd;++pvCurrent,++t2) { *pvCurrent = mInv * (*pvCurrent); *t2 = mInvTransposed * (*t2); } // Handle negative transformation matrix determinant -> invert vertex x if (imesh->mMat.Determinant() < 0.0f) { /* we *must* have normals */ for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) { pvCurrent->x *= -1.f; t2->x *= -1.f; } DefaultLogger::get()->info("3DS: Flipping mesh X-Axis"); } // Handle pivot point if (pivot.x || pivot.y || pivot.z) { for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) { *pvCurrent -= pivot; } } // Setup the mesh index pcOut->mMeshes[i] = iIndex; } } // Setup the name of the node pcOut->mName.Set(pcIn->mName); // Now build the transformation matrix of the node // ROTATION if (pcIn->aRotationKeys.size()){ // FIX to get to Assimp's quaternion conventions for (std::vector::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) { (*it).mValue.w *= -1.f; } pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() ); } else if (pcIn->aCameraRollKeys.size()) { aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue), pcOut->mTransformation); } // SCALING aiMatrix4x4& m = pcOut->mTransformation; if (pcIn->aScalingKeys.size()) { const aiVector3D& v = pcIn->aScalingKeys[0].mValue; m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x; m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y; m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z; } // TRANSLATION if (pcIn->aPositionKeys.size()) { const aiVector3D& v = pcIn->aPositionKeys[0].mValue; m.a4 += v.x; m.b4 += v.y; m.c4 += v.z; } // Generate animation channels for the node if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 || pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 || pcIn->aTargetPositionKeys.size() > 1) { aiAnimation* anim = pcSOut->mAnimations[0]; ai_assert(NULL != anim); if (pcIn->aCameraRollKeys.size() > 1) { DefaultLogger::get()->debug("3DS: Converting camera roll track ..."); // Camera roll keys - in fact they're just rotations // around the camera's z axis. The angles are given // in degrees (and they're clockwise). pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size()); for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i) { aiQuatKey& q = pcIn->aRotationKeys[i]; aiFloatKey& f = pcIn->aCameraRollKeys[i]; q.mTime = f.mTime; // FIX to get to Assimp quaternion conventions q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue)); } } #if 0 if (pcIn->aTargetPositionKeys.size() > 1) { DefaultLogger::get()->debug("3DS: Converting target track ..."); // Camera or spot light - need to convert the separate // target position channel to our representation TargetAnimationHelper helper; if (pcIn->aPositionKeys.empty()) { // We can just pass zero here ... helper.SetFixedMainAnimationChannel(aiVector3D()); } else helper.SetMainAnimationChannel(&pcIn->aPositionKeys); helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys); // Do the conversion std::vector distanceTrack; helper.Process(&distanceTrack); // Now add a new node as child, name it .Target // and assign the distance track to it. This is that the // information where the target is and how it moves is // not lost D3DS::Node* nd = new D3DS::Node(); pcIn->push_back(nd); nd->mName = pcIn->mName + ".Target"; aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); nda->mNodeName.Set(nd->mName); nda->mNumPositionKeys = (unsigned int)distanceTrack.size(); nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; ::memcpy(nda->mPositionKeys,&distanceTrack[0], sizeof(aiVectorKey)*nda->mNumPositionKeys); } #endif // Cameras or lights define their transformation in their parent node and in the // corresponding light or camera chunks. However, we read and process the latter // to to be able to return valid cameras/lights even if no scenegraph is given. for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) { if (pcSOut->mCameras[n]->mName == pcOut->mName) { pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f); } } for (unsigned int n = 0; n < pcSOut->mNumLights;++n) { if (pcSOut->mLights[n]->mName == pcOut->mName) { pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f); } } // Allocate a new node anim and setup its name aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); nda->mNodeName.Set(pcIn->mName); // POSITION keys if (pcIn->aPositionKeys.size() > 0) { nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size(); nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0], sizeof(aiVectorKey)*nda->mNumPositionKeys); } // ROTATION keys if (pcIn->aRotationKeys.size() > 0) { nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size(); nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys]; // Rotations are quaternion offsets aiQuaternion abs; for (unsigned int n = 0; n < nda->mNumRotationKeys;++n) { const aiQuatKey& q = pcIn->aRotationKeys[n]; abs = (n ? abs * q.mValue : q.mValue); nda->mRotationKeys[n].mTime = q.mTime; nda->mRotationKeys[n].mValue = abs.Normalize(); } } // SCALING keys if (pcIn->aScalingKeys.size() > 0) { nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size(); nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys]; ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0], sizeof(aiVectorKey)*nda->mNumScalingKeys); } } // Allocate storage for children pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size(); pcOut->mChildren = new aiNode*[pcIn->mChildren.size()]; // Recursively process all children const unsigned int size = pcIn->mChildren.size(); for (unsigned int i = 0; i < size;++i) { pcOut->mChildren[i] = new aiNode(); pcOut->mChildren[i]->mParent = pcOut; AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs); } } // ------------------------------------------------------------------------------------------------ // Find out how many node animation channels we'll have finally void CountTracks(D3DS::Node* node, unsigned int& cnt) { ////////////////////////////////////////////////////////////////////////////// // We will never generate more than one channel for a node, so // this is rather easy here. if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 || node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 || node->aTargetPositionKeys.size() > 1) { ++cnt; // account for the additional channel for the camera/spotlight target position if (node->aTargetPositionKeys.size() > 1)++cnt; } // Recursively process all children for (unsigned int i = 0; i < node->mChildren.size();++i) CountTracks(node->mChildren[i],cnt); } // ------------------------------------------------------------------------------------------------ // Generate the output node graph void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut) { pcOut->mRootNode = new aiNode(); if (0 == mRootNode->mChildren.size()) { ////////////////////////////////////////////////////////////////////////////// // It seems the file is so messed up that it has not even a hierarchy. // generate a flat hiearachy which looks like this: // // ROOT_NODE // | // ---------------------------------------- // | | | | | // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 .... // DefaultLogger::get()->warn("No hierarchy information has been found in the file. "); pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes + mScene->mCameras.size() + mScene->mLights.size(); pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ]; pcOut->mRootNode->mName.Set("<3DSDummyRoot>"); // Build dummy nodes for all meshes unsigned int a = 0; for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a) { aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); pcNode->mParent = pcOut->mRootNode; pcNode->mMeshes = new unsigned int[1]; pcNode->mMeshes[0] = i; pcNode->mNumMeshes = 1; // Build a name for the node pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%i",i); } // Build dummy nodes for all cameras for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a) { aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); pcNode->mParent = pcOut->mRootNode; // Build a name for the node pcNode->mName = mScene->mCameras[i]->mName; } // Build dummy nodes for all lights for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a) { aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); pcNode->mParent = pcOut->mRootNode; // Build a name for the node pcNode->mName = mScene->mLights[i]->mName; } } else { // First of all: find out how many scaling, rotation and translation // animation tracks we'll have afterwards unsigned int numChannel = 0; CountTracks(mRootNode,numChannel); if (numChannel) { // Allocate a primary animation channel pcOut->mNumAnimations = 1; pcOut->mAnimations = new aiAnimation*[1]; aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation(); anim->mName.Set("3DSMasterAnim"); // Allocate enough storage for all node animation channels, // but don't set the mNumChannels member - we'll use it to // index into the array anim->mChannels = new aiNodeAnim*[numChannel]; } aiMatrix4x4 m; AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m); } // We used the first vertex color set to store some emporary values so we need to cleanup here for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) pcOut->mMeshes[a]->mColors[0] = NULL; // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system pcOut->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation; // If the root node is unnamed name it "<3DSRoot>" if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) || (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') ) { pcOut->mRootNode->mName.Set("<3DSRoot>"); } } // ------------------------------------------------------------------------------------------------ // Convert all meshes in the scene and generate the final output scene. void Discreet3DSImporter::ConvertScene(aiScene* pcOut) { // Allocate enough storage for all output materials pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size(); pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials]; // ... and convert the 3DS materials to aiMaterial's for (unsigned int i = 0; i < pcOut->mNumMaterials;++i) { MaterialHelper* pcNew = new MaterialHelper(); ConvertMaterial(mScene->mMaterials[i],*pcNew); pcOut->mMaterials[i] = pcNew; } // Generate the output mesh list ConvertMeshes(pcOut); // Now copy all light sources to the output scene pcOut->mNumLights = (unsigned int)mScene->mLights.size(); if (pcOut->mNumLights) { pcOut->mLights = new aiLight*[pcOut->mNumLights]; ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights); } // Now copy all cameras to the output scene pcOut->mNumCameras = (unsigned int)mScene->mCameras.size(); if (pcOut->mNumCameras) { pcOut->mCameras = new aiCamera*[pcOut->mNumCameras]; ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras); } } #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER 3rdparty/assimp/code/3DSHelper.h000066400000000000000000000426411217616171500167540ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file Defines helper data structures for the import of 3DS files */ #ifndef AI_3DSFILEHELPER_H_INC #define AI_3DSFILEHELPER_H_INC #include "SpatialSort.h" #include "SmoothingGroups.h" #if defined(__QNXNTO__) #include #endif namespace Assimp { namespace D3DS { #include "./../include/Compiler/pushpack1.h" // --------------------------------------------------------------------------- /** Discreet3DS class: Helper class for loading 3ds files. Defines chunks * and data structures. */ class Discreet3DS { private: inline Discreet3DS() {} public: //! data structure for a single chunk in a .3ds file struct Chunk { uint16_t Flag; uint32_t Size; } PACK_STRUCT; //! Used for shading field in material3ds structure //! From AutoDesk 3ds SDK typedef enum { // translated to gouraud shading with wireframe active Wire = 0x0, // if this material is set, no vertex normals will // be calculated for the model. Face normals + gouraud Flat = 0x1, // standard gouraud shading Gouraud = 0x2, // phong shading Phong = 0x3, // cooktorrance or anistropic phong shading ... // the exact meaning is unknown, if you know it // feel free to tell me ;-) Metal = 0x4, // required by the ASE loader Blinn = 0x5 } shadetype3ds; // Flags for animated keys enum { KEY_USE_TENS = 0x1, KEY_USE_CONT = 0x2, KEY_USE_BIAS = 0x4, KEY_USE_EASE_TO = 0x8, KEY_USE_EASE_FROM = 0x10 } ; enum { // ******************************************************************** // Basic chunks which can be found everywhere in the file CHUNK_VERSION = 0x0002, CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B // Linear color values (gamma = 2.2?) CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B CHUNK_PERCENTW = 0x0030, // int2 percentage CHUNK_PERCENTF = 0x0031, // float4 percentage // ******************************************************************** // Prj master chunk CHUNK_PRJ = 0xC23D, // MDLI master chunk CHUNK_MLI = 0x3DAA, // Primary main chunk of the .3ds file CHUNK_MAIN = 0x4D4D, // Mesh main chunk CHUNK_OBJMESH = 0x3D3D, // Specifies the background color of the .3ds file // This is passed through the material system for // viewing purposes. CHUNK_BKGCOLOR = 0x1200, // Specifies the ambient base color of the scene. // This is added to all materials in the file CHUNK_AMBCOLOR = 0x2100, // Specifies the background image for the whole scene // This value is passed through the material system // to the viewer CHUNK_BIT_MAP = 0x1100, CHUNK_BIT_MAP_EXISTS = 0x1101, // ******************************************************************** // Viewport related stuff. Ignored CHUNK_DEFAULT_VIEW = 0x3000, CHUNK_VIEW_TOP = 0x3010, CHUNK_VIEW_BOTTOM = 0x3020, CHUNK_VIEW_LEFT = 0x3030, CHUNK_VIEW_RIGHT = 0x3040, CHUNK_VIEW_FRONT = 0x3050, CHUNK_VIEW_BACK = 0x3060, CHUNK_VIEW_USER = 0x3070, CHUNK_VIEW_CAMERA = 0x3080, // ******************************************************************** // Mesh chunks CHUNK_OBJBLOCK = 0x4000, CHUNK_TRIMESH = 0x4100, CHUNK_VERTLIST = 0x4110, CHUNK_VERTFLAGS = 0x4111, CHUNK_FACELIST = 0x4120, CHUNK_FACEMAT = 0x4130, CHUNK_MAPLIST = 0x4140, CHUNK_SMOOLIST = 0x4150, CHUNK_TRMATRIX = 0x4160, CHUNK_MESHCOLOR = 0x4165, CHUNK_TXTINFO = 0x4170, CHUNK_LIGHT = 0x4600, CHUNK_CAMERA = 0x4700, CHUNK_HIERARCHY = 0x4F00, // Specifies the global scaling factor. This is applied // to the root node's transformation matrix CHUNK_MASTER_SCALE = 0x0100, // ******************************************************************** // Material chunks CHUNK_MAT_MATERIAL = 0xAFFF, // asciiz containing the name of the material CHUNK_MAT_MATNAME = 0xA000, CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk // Specifies the shininess of the material // followed by percentage chunk CHUNK_MAT_SHININESS = 0xA040, CHUNK_MAT_SHININESS_PERCENT = 0xA041 , // Specifies the shading mode to be used // followed by a short CHUNK_MAT_SHADING = 0xA100, // NOTE: Emissive color (self illumination) seems not // to be a color but a single value, type is unknown. // Make the parser accept both of them. // followed by percentage chunk (?) CHUNK_MAT_SELF_ILLUM = 0xA080, // Always followed by percentage chunk (?) CHUNK_MAT_SELF_ILPCT = 0xA084, // Always followed by percentage chunk CHUNK_MAT_TRANSPARENCY = 0xA050, // Diffuse texture channel 0 CHUNK_MAT_TEXTURE = 0xA200, // Contains opacity information for each texel CHUNK_MAT_OPACMAP = 0xA210, // Contains a reflection map to be used to reflect // the environment. This is partially supported. CHUNK_MAT_REFLMAP = 0xA220, // Self Illumination map (emissive colors) CHUNK_MAT_SELFIMAP = 0xA33d, // Bumpmap. Not specified whether it is a heightmap // or a normal map. Assme it is a heightmap since // artist normally prefer this format. CHUNK_MAT_BUMPMAP = 0xA230, // Specular map. Seems to influence the specular color CHUNK_MAT_SPECMAP = 0xA204, // Holds shininess data. CHUNK_MAT_MAT_SHINMAP = 0xA33C, // Scaling in U/V direction. // (need to gen separate UV coordinate set // and do this by hand) CHUNK_MAT_MAP_USCALE = 0xA354, CHUNK_MAT_MAP_VSCALE = 0xA356, // Translation in U/V direction. // (need to gen separate UV coordinate set // and do this by hand) CHUNK_MAT_MAP_UOFFSET = 0xA358, CHUNK_MAT_MAP_VOFFSET = 0xA35a, // UV-coordinates rotation around the z-axis // Assumed to be in radians. CHUNK_MAT_MAP_ANG = 0xA35C, // Tiling flags for 3DS files CHUNK_MAT_MAP_TILING = 0xa351, // Specifies the file name of a texture CHUNK_MAPFILE = 0xA300, // Specifies whether a materail requires two-sided rendering CHUNK_MAT_TWO_SIDE = 0xA081, // ******************************************************************** // Main keyframer chunk. Contains translation/rotation/scaling data CHUNK_KEYFRAMER = 0xB000, // Supported sub chunks CHUNK_TRACKINFO = 0xB002, CHUNK_TRACKOBJNAME = 0xB010, CHUNK_TRACKDUMMYOBJNAME = 0xB011, CHUNK_TRACKPIVOT = 0xB013, CHUNK_TRACKPOS = 0xB020, CHUNK_TRACKROTATE = 0xB021, CHUNK_TRACKSCALE = 0xB022, // ******************************************************************** // Keyframes for various other stuff in the file // Partially ignored CHUNK_AMBIENTKEY = 0xB001, CHUNK_TRACKMORPH = 0xB026, CHUNK_TRACKHIDE = 0xB029, CHUNK_OBJNUMBER = 0xB030, CHUNK_TRACKCAMERA = 0xB003, CHUNK_TRACKFOV = 0xB023, CHUNK_TRACKROLL = 0xB024, CHUNK_TRACKCAMTGT = 0xB004, CHUNK_TRACKLIGHT = 0xB005, CHUNK_TRACKLIGTGT = 0xB006, CHUNK_TRACKSPOTL = 0xB007, CHUNK_FRAMES = 0xB008, // ******************************************************************** // light sub-chunks CHUNK_DL_OFF = 0x4620, CHUNK_DL_OUTER_RANGE = 0x465A, CHUNK_DL_INNER_RANGE = 0x4659, CHUNK_DL_MULTIPLIER = 0x465B, CHUNK_DL_EXCLUDE = 0x4654, CHUNK_DL_ATTENUATE = 0x4625, CHUNK_DL_SPOTLIGHT = 0x4610, // camera sub-chunks CHUNK_CAM_RANGES = 0x4720 }; }; // --------------------------------------------------------------------------- /** Helper structure representing a 3ds mesh face */ struct Face : public FaceWithSmoothingGroup { }; // --------------------------------------------------------------------------- /** Helper structure representing a texture */ struct Texture { //! Default constructor Texture() : mOffsetU (0.0f) , mOffsetV (0.0f) , mScaleU (1.0f) , mScaleV (1.0f) , mRotation (0.0f) , mMapMode (aiTextureMapMode_Wrap) , iUVSrc (0) { mTextureBlend = get_qnan(); } //! Specifies the blend factor for the texture float mTextureBlend; //! Specifies the filename of the texture std::string mMapName; //! Specifies texture coordinate offsets/scaling/rotations float mOffsetU; float mOffsetV; float mScaleU; float mScaleV; float mRotation; //! Specifies the mapping mode to be used for the texture aiTextureMapMode mMapMode; //! Used internally bool bPrivate; int iUVSrc; }; #include "./../include/Compiler/poppack1.h" // --------------------------------------------------------------------------- /** Helper structure representing a 3ds material */ struct Material { //! Default constructor. Builds a default name for the material Material() : mDiffuse (0.6f,0.6f,0.6f), // FIX ... we won't want object to be black mSpecularExponent (0.0f), mShininessStrength (1.0f), mShading(Discreet3DS::Gouraud), mTransparency (1.0f), mBumpHeight (1.0f), mTwoSided (false) { static int iCnt = 0; char szTemp[128]; sprintf(szTemp,"UNNAMED_%i",iCnt++); mName = szTemp; } //! Name of the material std::string mName; //! Diffuse color of the material aiColor3D mDiffuse; //! Specular exponent float mSpecularExponent; //! Shininess strength, in percent float mShininessStrength; //! Specular color of the material aiColor3D mSpecular; //! Ambient color of the material aiColor3D mAmbient; //! Shading type to be used Discreet3DS::shadetype3ds mShading; //! Opacity of the material float mTransparency; //! Diffuse texture channel Texture sTexDiffuse; //! Opacity texture channel Texture sTexOpacity; //! Specular texture channel Texture sTexSpecular; //! Reflective texture channel Texture sTexReflective; //! Bump texture channel Texture sTexBump; //! Emissive texture channel Texture sTexEmissive; //! Shininess texture channel Texture sTexShininess; //! Scaling factor for the bump values float mBumpHeight; //! Emissive color aiColor3D mEmissive; //! Ambient texture channel //! (used by the ASE format) Texture sTexAmbient; //! True if the material must be rendered from two sides bool mTwoSided; }; // --------------------------------------------------------------------------- /** Helper structure to represent a 3ds file mesh */ struct Mesh : public MeshWithSmoothingGroups { //! Default constructor Mesh() { static int iCnt = 0; // Generate a default name for the mesh char szTemp[128]; ::sprintf(szTemp,"UNNAMED_%i",iCnt++); mName = szTemp; } //! Name of the mesh std::string mName; //! Texture coordinates std::vector mTexCoords; //! Face materials std::vector mFaceMaterials; //! Local transformation matrix aiMatrix4x4 mMat; }; // --------------------------------------------------------------------------- /** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the C-API, so it would be difficult to make them a template. */ struct aiFloatKey { double mTime; ///< The time of this key float mValue; ///< The value of this key #ifdef __cplusplus // time is not compared bool operator == (const aiFloatKey& o) const {return o.mValue == this->mValue;} bool operator != (const aiFloatKey& o) const {return o.mValue != this->mValue;} // Only time is compared. This operator is defined // for use with std::sort bool operator < (const aiFloatKey& o) const {return mTime < o.mTime;} bool operator > (const aiFloatKey& o) const {return mTime < o.mTime;} #endif }; // --------------------------------------------------------------------------- /** Helper structure to represent a 3ds file node */ struct Node { Node() : mHierarchyPos (0) , mHierarchyIndex (0) { static int iCnt = 0; // Generate a default name for the node char szTemp[128]; ::sprintf(szTemp,"UNNAMED_%i",iCnt++); mName = szTemp; aRotationKeys.reserve (20); aPositionKeys.reserve (20); aScalingKeys.reserve (20); } ~Node() { for (unsigned int i = 0; i < mChildren.size();++i) delete mChildren[i]; } //! Pointer to the parent node Node* mParent; //! Holds all child nodes std::vector mChildren; //! Name of the node std::string mName; //! Dummy nodes: real name to be combined with the $$$DUMMY std::string mDummyName; //! Position of the node in the hierarchy (tree depth) int16_t mHierarchyPos; //! Index of the node int16_t mHierarchyIndex; //! Rotation keys loaded from the file std::vector aRotationKeys; //! Position keys loaded from the file std::vector aPositionKeys; //! Scaling keys loaded from the file std::vector aScalingKeys; // For target lights (spot lights and directional lights): // The position of the target std::vector< aiVectorKey > aTargetPositionKeys; // For cameras: the camera roll angle std::vector< aiFloatKey > aCameraRollKeys; //! Pivot position loaded from the file aiVector3D vPivot; //! Add a child node, setup the right parent node for it //! \param pc Node to be 'adopted' inline Node& push_back(Node* pc) { mChildren.push_back(pc); pc->mParent = this; return *this; } }; // --------------------------------------------------------------------------- /** Helper structure analogue to aiScene */ struct Scene { //! List of all materials loaded //! NOTE: 3ds references materials globally std::vector mMaterials; //! List of all meshes loaded std::vector mMeshes; //! List of all cameras loaded std::vector mCameras; //! List of all lights loaded std::vector mLights; //! Pointer to the root node of the scene // --- moved to main class // Node* pcRootNode; }; } // end of namespace D3DS } // end of namespace Assimp #endif // AI_XFILEHELPER_H_INC 3rdparty/assimp/code/3DSLoader.cpp000066400000000000000000001321341217616171500172730ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file 3DSLoader.cpp * @brief Implementation of the 3ds importer class * * http://www.the-labs.com/Blender/3DS-details.html */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER // internal headers #include "3DSLoader.h" using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Begins a new parsing block // - Reads the current chunk and validates it // - computes its length #define ASSIMP_3DS_BEGIN_CHUNK() \ while (true) { \ if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \ return; \ } \ Discreet3DS::Chunk chunk; \ ReadChunk(&chunk); \ int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \ const int oldReadLimit = stream->GetReadLimit(); \ stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \ // ------------------------------------------------------------------------------------------------ // End a parsing block // Must follow at the end of each parsing block, reset chunk end marker to previous value #define ASSIMP_3DS_END_CHUNK() \ stream->SkipToReadLimit(); \ stream->SetReadLimit(oldReadLimit); \ if (stream->GetRemainingSizeToLimit() == 0) \ return; \ } // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer Discreet3DSImporter::Discreet3DSImporter() {} // ------------------------------------------------------------------------------------------------ // Destructor, private as well Discreet3DSImporter::~Discreet3DSImporter() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); if (extension == "3ds" || extension == "prj" ) { return true; } if (!extension.length() || checkSig) { uint16_t token[3]; token[0] = 0x4d4d; token[1] = 0x3dc2; //token[2] = 0x3daa; return CheckMagicToken(pIOHandler,pFile,token,2,0,2); } return false; } // ------------------------------------------------------------------------------------------------ // Get list of all extension supported by this loader void Discreet3DSImporter::GetExtensionList(std::set& extensions) { extensions.insert("3ds"); extensions.insert("prj"); } // ------------------------------------------------------------------------------------------------ // Setup configuration properties void Discreet3DSImporter::SetupProperties(const Importer*) { // nothing to be done for the moment } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void Discreet3DSImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); this->stream = &stream; // We should have at least one chunk if (stream.GetRemainingSize() < 16) { throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile); } // Allocate our temporary 3DS representation mScene = new D3DS::Scene(); // Initialize members mLastNodeIndex = -1; mCurrentNode = new D3DS::Node(); mRootNode = mCurrentNode; mRootNode->mHierarchyPos = -1; mRootNode->mHierarchyIndex = -1; mRootNode->mParent = NULL; mMasterScale = 1.0f; mBackgroundImage = ""; bHasBG = false; bIsPrj = false; // Parse the file ParseMainChunk(); // Process all meshes in the file. First check whether all // face indices haev valid values. The generate our // internal verbose representation. Finally compute normal // vectors from the smoothing groups we read from the // file. for (std::vector::iterator i = mScene->mMeshes.begin(), end = mScene->mMeshes.end(); i != end;++i) { CheckIndices(*i); MakeUnique (*i); ComputeNormalsWithSmoothingsGroups(*i); } // Replace all occurences of the default material with a // valid material. Generate it if no material containing // DEFAULT in its name has been found in the file ReplaceDefaultMaterial(); // Convert the scene from our internal representation to an // aiScene object. This involves copying all meshes, lights // and cameras to the scene ConvertScene(pScene); // Generate the node graph for the scene. This is a little bit // tricky since we'll need to split some meshes into submeshes GenerateNodeGraph(pScene); // Now apply the master scaling factor to the scene ApplyMasterScale(pScene); // Delete our internal scene representation and the root // node, so the whole hierarchy will follow delete mRootNode; delete mScene; AI_DEBUG_INVALIDATE_PTR(mRootNode); AI_DEBUG_INVALIDATE_PTR(mScene); AI_DEBUG_INVALIDATE_PTR(this->stream); } // ------------------------------------------------------------------------------------------------ // Applies a master-scaling factor to the imported scene void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene) { // There are some 3DS files with a zero scaling factor if (!mMasterScale)mMasterScale = 1.0f; else mMasterScale = 1.0f / mMasterScale; // Construct an uniform scaling matrix and multiply with it pScene->mRootNode->mTransformation *= aiMatrix4x4( mMasterScale,0.0f, 0.0f, 0.0f, 0.0f, mMasterScale,0.0f, 0.0f, 0.0f, 0.0f, mMasterScale,0.0f, 0.0f, 0.0f, 0.0f, 1.0f); // Check whether a scaling track is assigned to the root node. } // ------------------------------------------------------------------------------------------------ // Reads a new chunk from the file void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut) { ai_assert(pcOut != NULL); pcOut->Flag = stream->GetI2(); pcOut->Size = stream->GetI4(); if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) throw DeadlyImportError("Chunk is too large"); if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) DefaultLogger::get()->error("3DS: Chunk overflow"); } // ------------------------------------------------------------------------------------------------ // Skip a chunk void Discreet3DSImporter::SkipChunk() { Discreet3DS::Chunk psChunk; ReadChunk(&psChunk); stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk)); return; } // ------------------------------------------------------------------------------------------------ // Process the primary chunk of the file void Discreet3DSImporter::ParseMainChunk() { ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_PRJ: bIsPrj = true; case Discreet3DS::CHUNK_MAIN: ParseEditorChunk(); break; }; ASSIMP_3DS_END_CHUNK(); // recursively continue processing this hierarchy level return ParseMainChunk(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseEditorChunk() { ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_OBJMESH: ParseObjectChunk(); break; // NOTE: In several documentations in the internet this // chunk appears at different locations case Discreet3DS::CHUNK_KEYFRAMER: ParseKeyframeChunk(); break; case Discreet3DS::CHUNK_VERSION: { // print the version number char buff[10]; ASSIMP_itoa10(buff,stream->GetI2()); DefaultLogger::get()->info(std::string("3DS file format version: ") + buff); } break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseObjectChunk() { ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_OBJBLOCK: { unsigned int cnt = 0; const char* sz = (const char*)stream->GetPtr(); // Get the name of the geometry object while (stream->GetI1())++cnt; ParseChunk(sz,cnt); } break; case Discreet3DS::CHUNK_MAT_MATERIAL: // Add a new material to the list mScene->mMaterials.push_back(D3DS::Material()); ParseMaterialChunk(); break; case Discreet3DS::CHUNK_AMBCOLOR: // This is the ambient base color of the scene. // We add it to the ambient color of all materials ParseColorChunk(&mClrAmbient,true); if (is_qnan(mClrAmbient.r)) { // We failed to read the ambient base color. DefaultLogger::get()->error("3DS: Failed to read ambient base color"); mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f; } break; case Discreet3DS::CHUNK_BIT_MAP: { // Specifies the background image. The string should already be // properly 0 terminated but we need to be sure unsigned int cnt = 0; const char* sz = (const char*)stream->GetPtr(); while (stream->GetI1())++cnt; mBackgroundImage = std::string(sz,cnt); } break; case Discreet3DS::CHUNK_BIT_MAP_EXISTS: bHasBG = true; break; case Discreet3DS::CHUNK_MASTER_SCALE: // Scene master scaling factor mMasterScale = stream->GetF4(); break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num) { ASSIMP_3DS_BEGIN_CHUNK(); // IMPLEMENTATION NOTE; // Cameras or lights define their transformation in their parent node and in the // corresponding light or camera chunks. However, we read and process the latter // to to be able to return valid cameras/lights even if no scenegraph is given. // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_TRIMESH: { // this starts a new triangle mesh mScene->mMeshes.push_back(D3DS::Mesh()); D3DS::Mesh& m = mScene->mMeshes.back(); // Setup the name of the mesh m.mName = std::string(name, num); // Read mesh chunks ParseMeshChunk(); } break; case Discreet3DS::CHUNK_LIGHT: { // This starts a new light aiLight* light = new aiLight(); mScene->mLights.push_back(light); light->mName.Set(std::string(name, num)); // First read the position of the light light->mPosition.x = stream->GetF4(); light->mPosition.y = stream->GetF4(); light->mPosition.z = stream->GetF4(); light->mColorDiffuse = aiColor3D(1.f,1.f,1.f); // Now check for further subchunks if (!bIsPrj) /* fixme */ ParseLightChunk(); // The specular light color is identical the the diffuse light color. The ambient light color // is equal to the ambient base color of the whole scene. light->mColorSpecular = light->mColorDiffuse; light->mColorAmbient = mClrAmbient; if (light->mType == aiLightSource_UNDEFINED) { // It must be a point light light->mType = aiLightSource_POINT; }} break; case Discreet3DS::CHUNK_CAMERA: { // This starts a new camera aiCamera* camera = new aiCamera(); mScene->mCameras.push_back(camera); camera->mName.Set(std::string(name, num)); // First read the position of the camera camera->mPosition.x = stream->GetF4(); camera->mPosition.y = stream->GetF4(); camera->mPosition.z = stream->GetF4(); // Then the camera target camera->mLookAt.x = stream->GetF4() - camera->mPosition.x; camera->mLookAt.y = stream->GetF4() - camera->mPosition.y; camera->mLookAt.z = stream->GetF4() - camera->mPosition.z; float len = camera->mLookAt.Length(); if (len < 1e-5f) { // There are some files with lookat == position. Don't know why or whether it's ok or not. DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector"); camera->mLookAt = aiVector3D(0.f,1.f,0.f); } else camera->mLookAt /= len; // And finally - the camera rotation angle, in counter clockwise direction const float angle = AI_DEG_TO_RAD( stream->GetF4() ); aiQuaternion quat(camera->mLookAt,angle); camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f); // Read the lense angle camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() ); if (camera->mHorizontalFOV < 0.001f) { camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); } // Now check for further subchunks if (!bIsPrj) /* fixme */ { ParseCameraChunk(); }} break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseLightChunk() { ASSIMP_3DS_BEGIN_CHUNK(); aiLight* light = mScene->mLights.back(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_DL_SPOTLIGHT: // Now we can be sure that the light is a spot light light->mType = aiLightSource_SPOT; // We wouldn't need to normalize here, but we do it light->mDirection.x = stream->GetF4() - light->mPosition.x; light->mDirection.y = stream->GetF4() - light->mPosition.y; light->mDirection.z = stream->GetF4() - light->mPosition.z; light->mDirection.Normalize(); // Now the hotspot and falloff angles - in degrees light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() ); // FIX: the falloff angle is just an offset light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() ); break; // intensity multiplier case Discreet3DS::CHUNK_DL_MULTIPLIER: light->mColorDiffuse = light->mColorDiffuse * stream->GetF4(); break; // light color case Discreet3DS::CHUNK_RGBF: case Discreet3DS::CHUNK_LINRGBF: light->mColorDiffuse.r *= stream->GetF4(); light->mColorDiffuse.g *= stream->GetF4(); light->mColorDiffuse.b *= stream->GetF4(); break; // light attenuation case Discreet3DS::CHUNK_DL_ATTENUATE: light->mAttenuationLinear = stream->GetF4(); break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseCameraChunk() { ASSIMP_3DS_BEGIN_CHUNK(); aiCamera* camera = mScene->mCameras.back(); // get chunk type switch (chunk.Flag) { // near and far clip plane case Discreet3DS::CHUNK_CAM_RANGES: camera->mClipPlaneNear = stream->GetF4(); camera->mClipPlaneFar = stream->GetF4(); break; } ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseKeyframeChunk() { ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_TRACKCAMTGT: case Discreet3DS::CHUNK_TRACKSPOTL: case Discreet3DS::CHUNK_TRACKCAMERA: case Discreet3DS::CHUNK_TRACKINFO: case Discreet3DS::CHUNK_TRACKLIGHT: case Discreet3DS::CHUNK_TRACKLIGTGT: // this starts a new mesh hierarchy chunk ParseHierarchyChunk(chunk.Flag); break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ // Little helper function for ParseHierarchyChunk void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent) { if (!pcCurrent) { mRootNode->push_back(pcNode); return; } if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) { if (pcCurrent->mParent) { pcCurrent->mParent->push_back(pcNode); } else pcCurrent->push_back(pcNode); return; } return InverseNodeSearch(pcNode,pcCurrent->mParent); } // ------------------------------------------------------------------------------------------------ // Find a node with a specific name in the import hierarchy D3DS::Node* FindNode(D3DS::Node* root, const std::string& name) { if (root->mName == name) return root; for (std::vector::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) { D3DS::Node* nd; if (( nd = FindNode(*it,name))) return nd; } return NULL; } // ------------------------------------------------------------------------------------------------ // Binary predicate for std::unique() template bool KeyUniqueCompare(const T& first, const T& second) { return first.mTime == second.mTime; } // ------------------------------------------------------------------------------------------------ // Skip some additional import data. void Discreet3DSImporter::SkipTCBInfo() { unsigned int flags = stream->GetI2(); if (!flags) { // Currently we can't do anything with these values. They occur // quite rare, so it wouldn't be worth the effort implementing // them. 3DS ist not really suitable for complex animations, // so full support is not required. DefaultLogger::get()->warn("3DS: Skipping TCB animation info"); } if (flags & Discreet3DS::KEY_USE_TENS) { stream->IncPtr(4); } if (flags & Discreet3DS::KEY_USE_BIAS) { stream->IncPtr(4); } if (flags & Discreet3DS::KEY_USE_CONT) { stream->IncPtr(4); } if (flags & Discreet3DS::KEY_USE_EASE_FROM) { stream->IncPtr(4); } if (flags & Discreet3DS::KEY_USE_EASE_TO) { stream->IncPtr(4); } } // ------------------------------------------------------------------------------------------------ // Read hierarchy and keyframe info void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) { ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_TRACKOBJNAME: // This is the name of the object to which the track applies. The chunk also // defines the position of this object in the hierarchy. { // First of all: get the name of the object unsigned int cnt = 0; const char* sz = (const char*)stream->GetPtr(); while (stream->GetI1())++cnt; std::string name = std::string(sz,cnt); // Now find out whether we have this node already (target animation channels // are stored with a separate object ID) D3DS::Node* pcNode = FindNode(mRootNode,name); if (pcNode) { // Make this node the current node mCurrentNode = pcNode; break; } pcNode = new D3DS::Node(); pcNode->mName = name; // There are two unknown values which we can safely ignore stream->IncPtr(4); // Now read the hierarchy position of the object uint16_t hierarchy = stream->GetI2() + 1; pcNode->mHierarchyPos = hierarchy; pcNode->mHierarchyIndex = mLastNodeIndex; // And find a proper position in the graph for it if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) { // add to the parent of the last touched node mCurrentNode->mParent->push_back(pcNode); mLastNodeIndex++; } else if (hierarchy >= mLastNodeIndex) { // place it at the current position in the hierarchy mCurrentNode->push_back(pcNode); mLastNodeIndex = hierarchy; } else { // need to go back to the specified position in the hierarchy. InverseNodeSearch(pcNode,mCurrentNode); mLastNodeIndex++; } // Make this node the current node mCurrentNode = pcNode; } break; case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME: // This is the "real" name of a $$$DUMMY object { const char* sz = (const char*) stream->GetPtr(); while (stream->GetI1()) {} // If object name is DUMMY, take this one instead if (mCurrentNode->mName == "$$$DUMMY") { //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); mCurrentNode->mName = std::string(sz); break; } } break; case Discreet3DS::CHUNK_TRACKPIVOT: if ( Discreet3DS::CHUNK_TRACKINFO != parent) { DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object"); break; } // Pivot = origin of rotation and scaling mCurrentNode->vPivot.x = stream->GetF4(); mCurrentNode->vPivot.y = stream->GetF4(); mCurrentNode->vPivot.z = stream->GetF4(); break; // //////////////////////////////////////////////////////////////////// // POSITION KEYFRAME case Discreet3DS::CHUNK_TRACKPOS: { stream->IncPtr(10); const unsigned int numFrames = stream->GetI4(); bool sortKeys = false; // This could also be meant as the target position for // (targeted) lights and cameras std::vector* l; if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) { l = & mCurrentNode->aTargetPositionKeys; } else l = & mCurrentNode->aPositionKeys; l->reserve(numFrames); for (unsigned int i = 0; i < numFrames;++i) { const unsigned int fidx = stream->GetI4(); // Setup a new position key aiVectorKey v; v.mTime = (double)fidx; SkipTCBInfo(); v.mValue.x = stream->GetF4(); v.mValue.y = stream->GetF4(); v.mValue.z = stream->GetF4(); // check whether we'll need to sort the keys if (!l->empty() && v.mTime <= l->back().mTime) sortKeys = true; // Add the new keyframe to the list l->push_back(v); } // Sort all keys with ascending time values and remove duplicates? if (sortKeys) { std::stable_sort(l->begin(),l->end()); l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); }} break; // //////////////////////////////////////////////////////////////////// // CAMERA ROLL KEYFRAME case Discreet3DS::CHUNK_TRACKROLL: { // roll keys are accepted for cameras only if (parent != Discreet3DS::CHUNK_TRACKCAMERA) { DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object"); break; } bool sortKeys = false; std::vector* l = &mCurrentNode->aCameraRollKeys; stream->IncPtr(10); const unsigned int numFrames = stream->GetI4(); l->reserve(numFrames); for (unsigned int i = 0; i < numFrames;++i) { const unsigned int fidx = stream->GetI4(); // Setup a new position key aiFloatKey v; v.mTime = (double)fidx; // This is just a single float SkipTCBInfo(); v.mValue = stream->GetF4(); // Check whether we'll need to sort the keys if (!l->empty() && v.mTime <= l->back().mTime) sortKeys = true; // Add the new keyframe to the list l->push_back(v); } // Sort all keys with ascending time values and remove duplicates? if (sortKeys) { std::stable_sort(l->begin(),l->end()); l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); }} break; // //////////////////////////////////////////////////////////////////// // CAMERA FOV KEYFRAME case Discreet3DS::CHUNK_TRACKFOV: { DefaultLogger::get()->error("3DS: Skipping FOV animation track. " "This is not supported"); } break; // //////////////////////////////////////////////////////////////////// // ROTATION KEYFRAME case Discreet3DS::CHUNK_TRACKROTATE: { stream->IncPtr(10); const unsigned int numFrames = stream->GetI4(); bool sortKeys = false; std::vector* l = &mCurrentNode->aRotationKeys; l->reserve(numFrames); for (unsigned int i = 0; i < numFrames;++i) { const unsigned int fidx = stream->GetI4(); SkipTCBInfo(); aiQuatKey v; v.mTime = (double)fidx; // The rotation keyframe is given as an axis-angle pair const float rad = stream->GetF4(); aiVector3D axis; axis.x = stream->GetF4(); axis.y = stream->GetF4(); axis.z = stream->GetF4(); if (!axis.x && !axis.y && !axis.z) axis.y = 1.f; // Construct a rotation quaternion from the axis-angle pair v.mValue = aiQuaternion(axis,rad); // Check whether we'll need to sort the keys if (!l->empty() && v.mTime <= l->back().mTime) sortKeys = true; // add the new keyframe to the list l->push_back(v); } // Sort all keys with ascending time values and remove duplicates? if (sortKeys) { std::stable_sort(l->begin(),l->end()); l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); }} break; // //////////////////////////////////////////////////////////////////// // SCALING KEYFRAME case Discreet3DS::CHUNK_TRACKSCALE: { stream->IncPtr(10); const unsigned int numFrames = stream->GetI2(); stream->IncPtr(2); bool sortKeys = false; std::vector* l = &mCurrentNode->aScalingKeys; l->reserve(numFrames); for (unsigned int i = 0; i < numFrames;++i) { const unsigned int fidx = stream->GetI4(); SkipTCBInfo(); // Setup a new key aiVectorKey v; v.mTime = (double)fidx; // ... and read its value v.mValue.x = stream->GetF4(); v.mValue.y = stream->GetF4(); v.mValue.z = stream->GetF4(); // check whether we'll need to sort the keys if (!l->empty() && v.mTime <= l->back().mTime) sortKeys = true; // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files if (!v.mValue.x) v.mValue.x = 1.f; if (!v.mValue.y) v.mValue.y = 1.f; if (!v.mValue.z) v.mValue.z = 1.f; l->push_back(v); } // Sort all keys with ascending time values and remove duplicates? if (sortKeys) { std::stable_sort(l->begin(),l->end()); l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); }} break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ // Read a face chunk - it contains smoothing groups and material assignments void Discreet3DSImporter::ParseFaceChunk() { ASSIMP_3DS_BEGIN_CHUNK(); // Get the mesh we're currently working on D3DS::Mesh& mMesh = mScene->mMeshes.back(); // Get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_SMOOLIST: { // This is the list of smoothing groups - a bitfield for every face. // Up to 32 smoothing groups assigned to a single face. unsigned int num = chunkSize/4, m = 0; for (std::vector::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) { // nth bit is set for nth smoothing group (*i).iSmoothGroup = stream->GetI4(); }} break; case Discreet3DS::CHUNK_FACEMAT: { // at fist an asciiz with the material name const char* sz = (const char*)stream->GetPtr(); while (stream->GetI1()) {} // find the index of the material unsigned int idx = 0xcdcdcdcd, cnt = 0; for (std::vector::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) { // use case independent comparisons. hopefully it will work. if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) { idx = cnt; break; } } if (0xcdcdcdcd == idx) { DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz); } // Now continue and read all material indices cnt = (uint16_t)stream->GetI2(); for (unsigned int i = 0; i < cnt;++i) { unsigned int fidx = (uint16_t)stream->GetI2(); // check range if (fidx >= mMesh.mFaceMaterials.size()) { DefaultLogger::get()->error("3DS: Invalid face index in face material list"); } else mMesh.mFaceMaterials[fidx] = idx; }} break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ // Read a mesh chunk. Here's the actual mesh data void Discreet3DSImporter::ParseMeshChunk() { ASSIMP_3DS_BEGIN_CHUNK(); // Get the mesh we're currently working on D3DS::Mesh& mMesh = mScene->mMeshes.back(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_VERTLIST: { // This is the list of all vertices in the current mesh int num = (int)(uint16_t)stream->GetI2(); mMesh.mPositions.reserve(num); while (num-- > 0) { aiVector3D v; v.x = stream->GetF4(); v.y = stream->GetF4(); v.z = stream->GetF4(); mMesh.mPositions.push_back(v); }} break; case Discreet3DS::CHUNK_TRMATRIX: { // This is the RLEATIVE transformation matrix of the current mesh. Vertices are // pretransformed by this matrix wonder. mMesh.mMat.a1 = stream->GetF4(); mMesh.mMat.b1 = stream->GetF4(); mMesh.mMat.c1 = stream->GetF4(); mMesh.mMat.a2 = stream->GetF4(); mMesh.mMat.b2 = stream->GetF4(); mMesh.mMat.c2 = stream->GetF4(); mMesh.mMat.a3 = stream->GetF4(); mMesh.mMat.b3 = stream->GetF4(); mMesh.mMat.c3 = stream->GetF4(); mMesh.mMat.a4 = stream->GetF4(); mMesh.mMat.b4 = stream->GetF4(); mMesh.mMat.c4 = stream->GetF4(); } break; case Discreet3DS::CHUNK_MAPLIST: { // This is the list of all UV coords in the current mesh int num = (int)(uint16_t)stream->GetI2(); mMesh.mTexCoords.reserve(num); while (num-- > 0) { aiVector3D v; v.x = stream->GetF4(); v.y = stream->GetF4(); mMesh.mTexCoords.push_back(v); }} break; case Discreet3DS::CHUNK_FACELIST: { // This is the list of all faces in the current mesh int num = (int)(uint16_t)stream->GetI2(); mMesh.mFaces.reserve(num); while (num-- > 0) { // 3DS faces are ALWAYS triangles mMesh.mFaces.push_back(D3DS::Face()); D3DS::Face& sFace = mMesh.mFaces.back(); sFace.mIndices[0] = (uint16_t)stream->GetI2(); sFace.mIndices[1] = (uint16_t)stream->GetI2(); sFace.mIndices[2] = (uint16_t)stream->GetI2(); stream->IncPtr(2); // skip edge visibility flag } // Resize the material array (0xcdcdcdcd marks the default material; so if a face is // not referenced by a material, $$DEFAULT will be assigned to it) mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd); // Larger 3DS files could have multiple FACE chunks here chunkSize = stream->GetRemainingSizeToLimit(); if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) ) ParseFaceChunk(); } break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ // Read a 3DS material chunk void Discreet3DSImporter::ParseMaterialChunk() { ASSIMP_3DS_BEGIN_CHUNK(); switch (chunk.Flag) { case Discreet3DS::CHUNK_MAT_MATNAME: { // The material name string is already zero-terminated, but we need to be sure ... const char* sz = (const char*)stream->GetPtr(); unsigned int cnt = 0; while (stream->GetI1()) ++cnt; if (!cnt) { // This may not be, we use the default name instead DefaultLogger::get()->error("3DS: Empty material name"); } else mScene->mMaterials.back().mName = std::string(sz,cnt); } break; case Discreet3DS::CHUNK_MAT_DIFFUSE: { // This is the diffuse material color aiColor3D* pc = &mScene->mMaterials.back().mDiffuse; ParseColorChunk(pc); if (is_qnan(pc->r)) { // color chunk is invalid. Simply ignore it DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk"); pc->r = pc->g = pc->b = 1.0f; }} break; case Discreet3DS::CHUNK_MAT_SPECULAR: { // This is the specular material color aiColor3D* pc = &mScene->mMaterials.back().mSpecular; ParseColorChunk(pc); if (is_qnan(pc->r)) { // color chunk is invalid. Simply ignore it DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk"); pc->r = pc->g = pc->b = 1.0f; }} break; case Discreet3DS::CHUNK_MAT_AMBIENT: { // This is the ambient material color aiColor3D* pc = &mScene->mMaterials.back().mAmbient; ParseColorChunk(pc); if (is_qnan(pc->r)) { // color chunk is invalid. Simply ignore it DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk"); pc->r = pc->g = pc->b = 0.0f; }} break; case Discreet3DS::CHUNK_MAT_SELF_ILLUM: { // This is the emissive material color aiColor3D* pc = &mScene->mMaterials.back().mEmissive; ParseColorChunk(pc); if (is_qnan(pc->r)) { // color chunk is invalid. Simply ignore it DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk"); pc->r = pc->g = pc->b = 0.0f; }} break; case Discreet3DS::CHUNK_MAT_TRANSPARENCY: { // This is the material's transparency float* pcf = &mScene->mMaterials.back().mTransparency; *pcf = ParsePercentageChunk(); // NOTE: transparency, not opacity if (is_qnan(*pcf)) *pcf = 1.0f; else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; } break; case Discreet3DS::CHUNK_MAT_SHADING: // This is the material shading mode mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2(); break; case Discreet3DS::CHUNK_MAT_TWO_SIDE: // This is the two-sided flag mScene->mMaterials.back().mTwoSided = true; break; case Discreet3DS::CHUNK_MAT_SHININESS: { // This is the shininess of the material float* pcf = &mScene->mMaterials.back().mSpecularExponent; *pcf = ParsePercentageChunk(); if (is_qnan(*pcf)) *pcf = 0.0f; else *pcf *= (float)0xFFFF; } break; case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: { // This is the shininess strength of the material float* pcf = &mScene->mMaterials.back().mShininessStrength; *pcf = ParsePercentageChunk(); if (is_qnan(*pcf)) *pcf = 0.0f; else *pcf *= (float)0xffff / 100.0f; } break; case Discreet3DS::CHUNK_MAT_SELF_ILPCT: { // This is the self illumination strength of the material float f = ParsePercentageChunk(); if (is_qnan(f)) f = 0.0f; else f *= (float)0xFFFF / 100.0f; mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f); } break; // Parse texture chunks case Discreet3DS::CHUNK_MAT_TEXTURE: // Diffuse texture ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse); break; case Discreet3DS::CHUNK_MAT_BUMPMAP: // Height map ParseTextureChunk(&mScene->mMaterials.back().sTexBump); break; case Discreet3DS::CHUNK_MAT_OPACMAP: // Opacity texture ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity); break; case Discreet3DS::CHUNK_MAT_MAT_SHINMAP: // Shininess map ParseTextureChunk(&mScene->mMaterials.back().sTexShininess); break; case Discreet3DS::CHUNK_MAT_SPECMAP: // Specular map ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular); break; case Discreet3DS::CHUNK_MAT_SELFIMAP: // Self-illumination (emissive) map ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive); break; case Discreet3DS::CHUNK_MAT_REFLMAP: // Reflection map ParseTextureChunk(&mScene->mMaterials.back().sTexReflective); break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut) { ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type switch (chunk.Flag) { case Discreet3DS::CHUNK_MAPFILE: { // The material name string is already zero-terminated, but we need to be sure ... const char* sz = (const char*)stream->GetPtr(); unsigned int cnt = 0; while (stream->GetI1()) ++cnt; pcOut->mMapName = std::string(sz,cnt); } break; case Discreet3DS::CHUNK_PERCENTF: // Manually parse the blend factor pcOut->mTextureBlend = stream->GetF4(); break; case Discreet3DS::CHUNK_PERCENTW: // Manually parse the blend factor pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f; break; case Discreet3DS::CHUNK_MAT_MAP_USCALE: // Texture coordinate scaling in the U direction pcOut->mScaleU = stream->GetF4(); if (0.0f == pcOut->mScaleU) { DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1."); pcOut->mScaleU = 1.0f; } break; case Discreet3DS::CHUNK_MAT_MAP_VSCALE: // Texture coordinate scaling in the V direction pcOut->mScaleV = stream->GetF4(); if (0.0f == pcOut->mScaleV) { DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1."); pcOut->mScaleV = 1.0f; } break; case Discreet3DS::CHUNK_MAT_MAP_UOFFSET: // Texture coordinate offset in the U direction pcOut->mOffsetU = -stream->GetF4(); break; case Discreet3DS::CHUNK_MAT_MAP_VOFFSET: // Texture coordinate offset in the V direction pcOut->mOffsetV = stream->GetF4(); break; case Discreet3DS::CHUNK_MAT_MAP_ANG: // Texture coordinate rotation, CCW in DEGREES pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() ); break; case Discreet3DS::CHUNK_MAT_MAP_TILING: { const uint16_t iFlags = stream->GetI2(); // Get the mapping mode (for both axes) if (iFlags & 0x2u) pcOut->mMapMode = aiTextureMapMode_Mirror; else if (iFlags & 0x10u) pcOut->mMapMode = aiTextureMapMode_Decal; // wrapping in all remaining cases else pcOut->mMapMode = aiTextureMapMode_Wrap; } break; }; ASSIMP_3DS_END_CHUNK(); } // ------------------------------------------------------------------------------------------------ // Read a percentage chunk float Discreet3DSImporter::ParsePercentageChunk() { Discreet3DS::Chunk chunk; ReadChunk(&chunk); if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) return stream->GetF4(); else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF; return get_qnan(); } // ------------------------------------------------------------------------------------------------ // Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color void Discreet3DSImporter::ParseColorChunk(aiColor3D* out, bool acceptPercent) { ai_assert(out != NULL); // error return value const float qnan = get_qnan(); static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan); Discreet3DS::Chunk chunk; ReadChunk(&chunk); const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk); // Get the type of the chunk switch(chunk.Flag) { case Discreet3DS::CHUNK_LINRGBF: case Discreet3DS::CHUNK_RGBF: if (sizeof(float) * 3 > diff) { *out = clrError; return; } out->r = stream->GetF4(); out->g = stream->GetF4(); out->b = stream->GetF4(); break; case Discreet3DS::CHUNK_LINRGBB: case Discreet3DS::CHUNK_RGBB: if (sizeof(char) * 3 > diff) { *out = clrError; return; } out->r = (float)(uint8_t)stream->GetI1() / 255.0f; out->g = (float)(uint8_t)stream->GetI1() / 255.0f; out->b = (float)(uint8_t)stream->GetI1() / 255.0f; break; // Percentage chunks are accepted, too. case Discreet3DS::CHUNK_PERCENTF: if (acceptPercent && 4 <= diff) { out->g = out->b = out->r = stream->GetF4(); break; } *out = clrError; return; case Discreet3DS::CHUNK_PERCENTW: if (acceptPercent && 1 <= diff) { out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f; break; } *out = clrError; return; default: stream->IncPtr(diff); // Skip unknown chunks, hope this won't cause any problems. return ParseColorChunk(out,acceptPercent); }; } #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER 3rdparty/assimp/code/3DSLoader.h000066400000000000000000000216361217616171500167440ustar00rootroot00000000000000 /* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file 3DSLoader.h * @brief 3DS File format loader */ #ifndef AI_3DSIMPORTER_H_INC #define AI_3DSIMPORTER_H_INC #include "BaseImporter.h" #include "../include/aiTypes.h" struct aiNode; #include "3DSHelper.h" namespace Assimp { class MaterialHelper; using namespace D3DS; // --------------------------------------------------------------------------------- /** Importer class for 3D Studio r3 and r4 3DS files */ class Discreet3DSImporter : public BaseImporter { friend class Importer; protected: /** Constructor to be privately used by Importer */ Discreet3DSImporter(); /** Destructor, private as well */ ~Discreet3DSImporter(); public: // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. */ void SetupProperties(const Importer* pImp); protected: // ------------------------------------------------------------------- /** Called by Importer::GetExtensionList() for each loaded importer. * See BaseImporter::GetExtensionList() for details */ void GetExtensionList(std::set& extensions); // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); // ------------------------------------------------------------------- /** Converts a temporary material to the outer representation */ void ConvertMaterial(D3DS::Material& p_cMat, MaterialHelper& p_pcOut); // ------------------------------------------------------------------- /** Read a chunk * * @param pcOut Receives the current chunk */ void ReadChunk(Discreet3DS::Chunk* pcOut); // ------------------------------------------------------------------- /** Parse a percentage chunk. mCurrent will point to the next * chunk behind afterwards. If no percentage chunk is found * QNAN is returned. */ float ParsePercentageChunk(); // ------------------------------------------------------------------- /** Parse a color chunk. mCurrent will point to the next * chunk behind afterwards. If no color chunk is found * QNAN is returned in all members. */ void ParseColorChunk(aiColor3D* p_pcOut, bool p_bAcceptPercent = true); // ------------------------------------------------------------------- /** Skip a chunk in the file */ void SkipChunk(); // ------------------------------------------------------------------- /** Generate the nodegraph */ void GenerateNodeGraph(aiScene* pcOut); // ------------------------------------------------------------------- /** Parse a main top-level chunk in the file */ void ParseMainChunk(); // ------------------------------------------------------------------- /** Parse a top-level chunk in the file */ void ParseChunk(const char* name, unsigned int num); // ------------------------------------------------------------------- /** Parse a top-level editor chunk in the file */ void ParseEditorChunk(); // ------------------------------------------------------------------- /** Parse a top-level object chunk in the file */ void ParseObjectChunk(); // ------------------------------------------------------------------- /** Parse a material chunk in the file */ void ParseMaterialChunk(); // ------------------------------------------------------------------- /** Parse a mesh chunk in the file */ void ParseMeshChunk(); // ------------------------------------------------------------------- /** Parse a light chunk in the file */ void ParseLightChunk(); // ------------------------------------------------------------------- /** Parse a camera chunk in the file */ void ParseCameraChunk(); // ------------------------------------------------------------------- /** Parse a face list chunk in the file */ void ParseFaceChunk(); // ------------------------------------------------------------------- /** Parse a keyframe chunk in the file */ void ParseKeyframeChunk(); // ------------------------------------------------------------------- /** Parse a hierarchy chunk in the file */ void ParseHierarchyChunk(uint16_t parent); // ------------------------------------------------------------------- /** Parse a texture chunk in the file */ void ParseTextureChunk(D3DS::Texture* pcOut); // ------------------------------------------------------------------- /** Convert the meshes in the file */ void ConvertMeshes(aiScene* pcOut); // ------------------------------------------------------------------- /** Replace the default material in the scene */ void ReplaceDefaultMaterial(); // ------------------------------------------------------------------- /** Convert the whole scene */ void ConvertScene(aiScene* pcOut); // ------------------------------------------------------------------- /** generate unique vertices for a mesh */ void MakeUnique(D3DS::Mesh& sMesh); // ------------------------------------------------------------------- /** Add a node to the node graph */ void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn, aiMatrix4x4& absTrafo); // ------------------------------------------------------------------- /** Search for a node in the graph. * Called recursively */ void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent); // ------------------------------------------------------------------- /** Apply the master scaling factor to the mesh */ void ApplyMasterScale(aiScene* pScene); // ------------------------------------------------------------------- /** Clamp all indices in the file to a valid range */ void CheckIndices(D3DS::Mesh& sMesh); // ------------------------------------------------------------------- /** Skip the TCB info in a track key */ void SkipTCBInfo(); protected: /** Stream to read from */ StreamReaderLE* stream; /** Last touched node index */ short mLastNodeIndex; /** Current node, root node */ D3DS::Node* mCurrentNode, *mRootNode; /** Scene under construction */ D3DS::Scene* mScene; /** Ambient base color of the scene */ aiColor3D mClrAmbient; /** Master scaling factor of the scene */ float mMasterScale; /** Path to the background image of the scene */ std::string mBackgroundImage; bool bHasBG; /** true if PRJ file */ bool bIsPrj; }; } // end of namespace Assimp #endif // AI_3DSIMPORTER_H_INC 3rdparty/assimp/code/ACLoader.cpp000066400000000000000000000764731217616171500172020ustar00rootroot00000000000000 /* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file Implementation of the AC3D importer class */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_AC_IMPORTER // internal headers #include "ACLoader.h" #include "ParsingUtils.h" #include "fast_atof.h" #include "Subdivision.h" #if defined(__QNXNTO__) #include #endif using namespace Assimp; // ------------------------------------------------------------------------------------------------ // skip to the next token #define AI_AC_SKIP_TO_NEXT_TOKEN() \ if (!SkipSpaces(&buffer)) \ { \ DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \ continue; \ } // ------------------------------------------------------------------------------------------------ // read a string (may be enclosed in double quotation marks). buffer must point to " #define AI_AC_GET_STRING(out) \ ++buffer; \ const char* sz = buffer; \ while ('\"' != *buffer) \ { \ if (IsLineEnd( *buffer )) \ { \ DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \ out = "ERROR"; \ break; \ } \ ++buffer; \ } \ if (IsLineEnd( *buffer ))continue; \ out = std::string(sz,(unsigned int)(buffer-sz)); \ ++buffer; // ------------------------------------------------------------------------------------------------ // read 1 to n floats prefixed with an optional predefined identifier #define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \ AI_AC_SKIP_TO_NEXT_TOKEN(); \ if (name_length) \ { \ if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \ { \ DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \ continue; \ } \ buffer += name_length+1; \ } \ for (unsigned int i = 0; i < num;++i) \ { \ AI_AC_SKIP_TO_NEXT_TOKEN(); \ buffer = fast_atof_move(buffer,((float*)out)[i]); \ } // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer AC3DImporter::AC3DImporter() : buffer(0), configSplitBFCull(false), configEvalSubdivision(false), mNumMeshes(0), lights(0), groups(0), polys(0), worlds(0) { // nothing to be done here } // ------------------------------------------------------------------------------------------------ // Destructor, private as well AC3DImporter::~AC3DImporter() { // nothing to be done here } // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); // fixme: are acc and ac3d *really* used? Some sources say they are if (extension == "ac" || extension == "ac3d" || extension == "acc") { return true; } if (!extension.length() || checkSig) { uint32_t token = AI_MAKE_MAGIC("AC3D"); return CheckMagicToken(pIOHandler,pFile,&token,1,0); } return false; } // ------------------------------------------------------------------------------------------------ // Get list of file extensions handled by this loader void AC3DImporter::GetExtensionList(std::set& extensions) { extensions.insert("ac"); extensions.insert("acc"); extensions.insert("ac3d"); } // ------------------------------------------------------------------------------------------------ // Get a pointer to the next line from the file bool AC3DImporter::GetNextLine( ) { SkipLine(&buffer); return SkipSpaces(&buffer); } // ------------------------------------------------------------------------------------------------ // Parse an object section in an AC file void AC3DImporter::LoadObjectSection(std::vector& objects) { if (!TokenMatch(buffer,"OBJECT",6)) return; SkipSpaces(&buffer); ++mNumMeshes; objects.push_back(Object()); Object& obj = objects.back(); aiLight* light = NULL; if (!ASSIMP_strincmp(buffer,"light",5)) { // This is a light source. Add it to the list mLights->push_back(light = new aiLight()); // Return a point light with no attenuation light->mType = aiLightSource_POINT; light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f); light->mAttenuationConstant = 1.f; // Generate a default name for both the light source and the node // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast(mLights->size())-1); obj.name = std::string( light->mName.data ); DefaultLogger::get()->debug("AC3D: Light source encountered"); obj.type = Object::Light; } else if (!ASSIMP_strincmp(buffer,"group",5)) { obj.type = Object::Group; } else if (!ASSIMP_strincmp(buffer,"world",5)) { obj.type = Object::World; } else obj.type = Object::Poly; while (GetNextLine()) { if (TokenMatch(buffer,"kids",4)) { SkipSpaces(&buffer); unsigned int num = strtol10(buffer,&buffer); GetNextLine(); if (num) { // load the children of this object recursively obj.children.reserve(num); for (unsigned int i = 0; i < num; ++i) LoadObjectSection(obj.children); } return; } else if (TokenMatch(buffer,"name",4)) { SkipSpaces(&buffer); AI_AC_GET_STRING(obj.name); // If this is a light source, we'll also need to store // the name of the node in it. if (light) { light->mName.Set(obj.name); } } else if (TokenMatch(buffer,"texture",7)) { SkipSpaces(&buffer); AI_AC_GET_STRING(obj.texture); } else if (TokenMatch(buffer,"texrep",6)) { SkipSpaces(&buffer); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat); if (!obj.texRepeat.x || !obj.texRepeat.y) obj.texRepeat = aiVector2D (1.f,1.f); } else if (TokenMatch(buffer,"texoff",6)) { SkipSpaces(&buffer); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset); } else if (TokenMatch(buffer,"rot",3)) { SkipSpaces(&buffer); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation); } else if (TokenMatch(buffer,"loc",3)) { SkipSpaces(&buffer); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation); } else if (TokenMatch(buffer,"subdiv",6)) { SkipSpaces(&buffer); obj.subDiv = strtol10(buffer,&buffer); } else if (TokenMatch(buffer,"crease",6)) { SkipSpaces(&buffer); obj.crease = fast_atof(buffer); } else if (TokenMatch(buffer,"numvert",7)) { SkipSpaces(&buffer); unsigned int t = strtol10(buffer,&buffer); obj.vertices.reserve(t); for (unsigned int i = 0; i < t;++i) { if (!GetNextLine()) { DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet"); break; } else if (!IsNumeric(*buffer)) { DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet"); --buffer; // make sure the line is processed a second time break; } obj.vertices.push_back(aiVector3D()); aiVector3D& v = obj.vertices.back(); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x); } } else if (TokenMatch(buffer,"numsurf",7)) { SkipSpaces(&buffer); bool Q3DWorkAround = false; const unsigned int t = strtol10(buffer,&buffer); obj.surfaces.reserve(t); for (unsigned int i = 0; i < t;++i) { GetNextLine(); if (!TokenMatch(buffer,"SURF",4)) { // FIX: this can occur for some files - Quick 3D for // example writes no surf chunks if (!Q3DWorkAround) { DefaultLogger::get()->warn("AC3D: SURF token was expected"); DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled"); } --buffer; // make sure the line is processed a second time // break; --- see fix notes above Q3DWorkAround = true; } SkipSpaces(&buffer); obj.surfaces.push_back(Surface()); Surface& surf = obj.surfaces.back(); surf.flags = strtol_cppstyle(buffer); while (1) { if (!GetNextLine()) { DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete"); break; } if (TokenMatch(buffer,"mat",3)) { SkipSpaces(&buffer); surf.mat = strtol10(buffer); } else if (TokenMatch(buffer,"refs",4)) { // --- see fix notes above if (Q3DWorkAround) { if (!surf.entries.empty()) { buffer -= 6; break; } } SkipSpaces(&buffer); const unsigned int m = strtol10(buffer); surf.entries.reserve(m); obj.numRefs += m; for (unsigned int k = 0; k < m; ++k) { if (!GetNextLine()) { DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete"); break; } surf.entries.push_back(Surface::SurfaceEntry()); Surface::SurfaceEntry& entry = surf.entries.back(); entry.first = strtol10(buffer,&buffer); SkipSpaces(&buffer); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second); } } else { --buffer; // make sure the line is processed a second time break; } } } } } DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected"); } // ------------------------------------------------------------------------------------------------ // Convert a material from AC3DImporter::Material to aiMaterial void AC3DImporter::ConvertMaterial(const Object& object, const Material& matSrc, MaterialHelper& matDest) { aiString s; if (matSrc.name.length()) { s.Set(matSrc.name); matDest.AddProperty(&s,AI_MATKEY_NAME); } if (object.texture.length()) { s.Set(object.texture); matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); // UV transformation if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || object.texOffset.x || object.texOffset.y) { aiUVTransform transform; transform.mScaling = object.texRepeat; transform.mTranslation = object.texOffset; matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); } } matDest.AddProperty(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE); matDest.AddProperty(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT); matDest.AddProperty(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE); matDest.AddProperty(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR); int n; if (matSrc.shin) { n = aiShadingMode_Phong; matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); } else n = aiShadingMode_Gouraud; matDest.AddProperty(&n,1,AI_MATKEY_SHADING_MODEL); float f = 1.f - matSrc.trans; matDest.AddProperty(&f,1,AI_MATKEY_OPACITY); } // ------------------------------------------------------------------------------------------------ // Converts the loaded data to the internal verbose representation aiNode* AC3DImporter::ConvertObjectSection(Object& object, std::vector& meshes, std::vector& outMaterials, const std::vector& materials, aiNode* parent) { aiNode* node = new aiNode(); node->mParent = parent; if (object.vertices.size()) { if (!object.surfaces.size() || !object.numRefs) { /* " An object with 7 vertices (no surfaces, no materials defined). This is a good way of getting point data into AC3D. The Vertex->create convex-surface/object can be used on these vertices to 'wrap' a 3d shape around them " (http://www.opencity.info/html/ac3dfileformat.html) therefore: if no surfaces are defined return point data only */ DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, " "a point list is returned"); meshes.push_back(new aiMesh()); aiMesh* mesh = meshes.back(); mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts) { *verts = object.vertices[i]; faces->mNumIndices = 1; faces->mIndices = new unsigned int[1]; faces->mIndices[0] = i; } // use the primary material in this case. this should be the // default material if all objects of the file contain points // and no faces. mesh->mMaterialIndex = 0; outMaterials.push_back(new MaterialHelper()); ConvertMaterial(object, materials[0], *outMaterials.back()); } else { // need to generate one or more meshes for this object. // find out how many different materials we have typedef std::pair< unsigned int, unsigned int > IntPair; typedef std::vector< IntPair > MatTable; MatTable needMat(materials.size(),IntPair(0,0)); std::vector::iterator it,end = object.surfaces.end(); std::vector::iterator it2,end2; for (it = object.surfaces.begin(); it != end; ++it) { register unsigned int idx = (*it).mat; if (idx >= needMat.size()) { DefaultLogger::get()->error("AC3D: material index is out of range"); idx = 0; } if ((*it).entries.empty()) { DefaultLogger::get()->warn("AC3D: surface her zero vertex references"); } // validate all vertex indices to make sure we won't crash here for (it2 = (*it).entries.begin(), end2 = (*it).entries.end(); it2 != end2; ++it2) { if ((*it2).first >= object.vertices.size()) { DefaultLogger::get()->warn("AC3D: Invalid vertex reference"); (*it2).first = 0; } } if (!needMat[idx].first)++node->mNumMeshes; switch ((*it).flags & 0xf) { // closed line case 0x1: needMat[idx].first += (unsigned int)(*it).entries.size(); needMat[idx].second += (unsigned int)(*it).entries.size()<<1u; break; // unclosed line case 0x2: needMat[idx].first += (unsigned int)(*it).entries.size()-1; needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u; break; // 0 == polygon, else unknown default: if ((*it).flags & 0xf) { DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown"); (*it).flags &= ~(0xf); } // the number of faces increments by one, the number // of vertices by surface.numref. needMat[idx].first++; needMat[idx].second += (unsigned int)(*it).entries.size(); }; } unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes]; unsigned int mat = 0; const size_t oldm = meshes.size(); for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); cit != cend; ++cit, ++mat) { if (!(*cit).first)continue; // allocate a new aiMesh object *pip++ = (unsigned int)meshes.size(); aiMesh* mesh = new aiMesh(); meshes.push_back(mesh); mesh->mMaterialIndex = (unsigned int)outMaterials.size(); outMaterials.push_back(new MaterialHelper()); ConvertMaterial(object, materials[mat], *outMaterials.back()); // allocate storage for vertices and normals mesh->mNumFaces = (*cit).first; aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; mesh->mNumVertices = (*cit).second; aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; unsigned int cur = 0; // allocate UV coordinates, but only if the texture name for the // surface is not empty aiVector3D* uv = NULL; if (object.texture.length()) { uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; mesh->mNumUVComponents[0] = 2; } for (it = object.surfaces.begin(); it != end; ++it) { if (mat == (*it).mat) { const Surface& src = *it; // closed polygon unsigned int type = (*it).flags & 0xf; if (!type) { aiFace& face = *faces++; if ((face.mNumIndices = (unsigned int)src.entries.size())) { face.mIndices = new unsigned int[face.mNumIndices]; for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices) { const Surface::SurfaceEntry& entry = src.entries[i]; face.mIndices[i] = cur++; // copy vertex positions *vertices = object.vertices[entry.first] + object.translation; // copy texture coordinates if (uv) { uv->x = entry.second.x; uv->y = entry.second.y; ++uv; } } } } else { it2 = (*it).entries.begin(); // either a closed or an unclosed line register unsigned int tmp = (unsigned int)(*it).entries.size(); if (0x2 == type)--tmp; for (unsigned int m = 0; m < tmp;++m) { aiFace& face = *faces++; face.mNumIndices = 2; face.mIndices = new unsigned int[2]; face.mIndices[0] = cur++; face.mIndices[1] = cur++; // copy vertex positions *vertices++ = object.vertices[(*it2).first]; // copy texture coordinates if (uv) { uv->x = (*it2).second.x; uv->y = (*it2).second.y; ++uv; } if (0x1 == type && tmp-1 == m) { // if this is a closed line repeat its beginning now it2 = (*it).entries.begin(); } else ++it2; // second point *vertices++ = object.vertices[(*it2).first]; if (uv) { uv->x = (*it2).second.x; uv->y = (*it2).second.y; ++uv; } } } } } } // Now apply catmull clark subdivision if necessary. We split meshes into // materials which is not done by AC3D during smoothing, so we need to // collect all meshes using the same material group. if (object.subDiv) { if (configEvalSubdivision) { boost::scoped_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name); std::vector cpy(meshes.size()-oldm,NULL); div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true); std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm); // previous meshes are deleted vy Subdivide(). } else { DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: " +object.name); } } } } if (object.name.length()) node->mName.Set(object.name); else { // generate a name depending on the type of the node switch (object.type) { case Object::Group: node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++); break; case Object::Poly: node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++); break; case Object::Light: node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++); break; // there shouldn't be more than one world, but we don't care case Object::World: node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++); break; } } // setup the local transformation matrix of the object // compute the transformation offset to the parent node node->mTransformation = aiMatrix4x4 ( object.rotation ); if (object.type == Object::Group || !object.numRefs) { node->mTransformation.a4 = object.translation.x; node->mTransformation.b4 = object.translation.y; node->mTransformation.c4 = object.translation.z; } // add children to the object if (object.children.size()) { node->mNumChildren = (unsigned int)object.children.size(); node->mChildren = new aiNode*[node->mNumChildren]; for (unsigned int i = 0; i < node->mNumChildren;++i) { node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node); } } return node; } // ------------------------------------------------------------------------------------------------ void AC3DImporter::SetupProperties(const Importer* pImp) { configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false; configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void AC3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file if ( file.get() == NULL) throw DeadlyImportError( "Failed to open AC3D file " + pFile + "."); // allocate storage and copy the contents of the file to a memory buffer std::vector mBuffer2; TextFileToBuffer(file.get(),mBuffer2); buffer = &mBuffer2[0]; mNumMeshes = 0; lights = polys = worlds = groups = 0; if (::strncmp(buffer,"AC3D",4)) { throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); } // print the file format version to the console unsigned int version = HexDigitToDecimal( buffer[4] ); char msg[3]; ASSIMP_itoa10(msg,3,version); DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg); std::vector materials; materials.reserve(5); std::vector rootObjects; rootObjects.reserve(5); std::vector lights; mLights = & lights; while (GetNextLine()) { if (TokenMatch(buffer,"MATERIAL",8)) { materials.push_back(Material()); Material& mat = materials.back(); // manually parse the material ... sscanf would use the buldin atof ... // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f AI_AC_SKIP_TO_NEXT_TOKEN(); if ('\"' == *buffer) { AI_AC_GET_STRING(mat.name); AI_AC_SKIP_TO_NEXT_TOKEN(); } AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin); AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans); } LoadObjectSection(rootObjects); } if (rootObjects.empty() || !mNumMeshes) { throw DeadlyImportError("AC3D: No meshes have been loaded"); } if (materials.empty()) { DefaultLogger::get()->warn("AC3D: No material has been found"); materials.push_back(Material()); } mNumMeshes += (mNumMeshes>>2u) + 1; std::vector meshes; meshes.reserve(mNumMeshes); std::vector omaterials; materials.reserve(mNumMeshes); // generate a dummy root if there are multiple objects on the top layer Object* root; if (1 == rootObjects.size()) root = &rootObjects[0]; else { root = new Object(); } // now convert the imported stuff to our output data structure pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials); if (1 != rootObjects.size())delete root; if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4)) pScene->mRootNode->mName.Set(""); // copy meshes if (meshes.empty()) { throw DeadlyImportError("An unknown error occured during converting"); } pScene->mNumMeshes = (unsigned int)meshes.size(); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*)); // copy materials pScene->mNumMaterials = (unsigned int)omaterials.size(); pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*)); // copy lights pScene->mNumLights = (unsigned int)lights.size(); if (lights.size()) { pScene->mLights = new aiLight*[lights.size()]; ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*)); } } #endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER 3rdparty/assimp/code/ACLoader.h000066400000000000000000000201531217616171500166270ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file ACLoader.h * @brief Declaration of the .ac importer class. */ #ifndef AI_AC3DLOADER_H_INCLUDED #define AI_AC3DLOADER_H_INCLUDED #include #include "BaseImporter.h" #include "../include/aiTypes.h" namespace Assimp { // --------------------------------------------------------------------------- /** AC3D (*.ac) importer class */ class AC3DImporter : public BaseImporter { friend class Importer; protected: /** Constructor to be privately used by Importer */ AC3DImporter(); /** Destructor, private as well */ ~AC3DImporter(); // Represents an AC3D material struct Material { Material() : rgb (0.6f,0.6f,0.6f) , spec (1.f,1.f,1.f) , shin (0.f) , trans (0.f) {} // base color of the material aiColor3D rgb; // ambient color of the material aiColor3D amb; // emissive color of the material aiColor3D emis; // specular color of the material aiColor3D spec; // shininess exponent float shin; // transparency. 0 == opaque float trans; // name of the material. optional. std::string name; }; // Represents an AC3D surface struct Surface { Surface() : mat (0) , flags (0) {} unsigned int mat,flags; typedef std::pair SurfaceEntry; std::vector< SurfaceEntry > entries; }; // Represents an AC3D object struct Object { Object() : type (World) , name( "" ) , children() , texture( "" ) , texRepeat( 1.f, 1.f ) , texOffset( 0.0f, 0.0f ) , rotation() , translation() , vertices() , surfaces() , numRefs (0) , subDiv (0) , crease(0.0f) {} // Type description enum Type { World = 0x0, Poly = 0x1, Group = 0x2, Light = 0x4 } type; // name of the object std::string name; // object children std::vector children; // texture to be assigned to all surfaces of the object std::string texture; // texture repat factors (scaling for all coordinates) aiVector2D texRepeat, texOffset; // rotation matrix aiMatrix3x3 rotation; // translation vector aiVector3D translation; // vertices std::vector vertices; // surfaces std::vector surfaces; // number of indices (= num verts in verbose format) unsigned int numRefs; // number of subdivisions to be performed on the // imported data unsigned int subDiv; // max angle limit for smoothing float crease; }; public: // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; protected: // ------------------------------------------------------------------- /** Called by Importer::GetExtensionList() for each loaded importer. * See BaseImporter::GetExtensionList() for details */ void GetExtensionList(std::set& extensions); // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details*/ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list.*/ void SetupProperties(const Importer* pImp); private: // ------------------------------------------------------------------- /** Get the next line from the file. * @return false if the end of the file was reached*/ bool GetNextLine(); // ------------------------------------------------------------------- /** Load the object section. This method is called recursively to * load subobjects, the method returns after a 'kids 0' was * encountered. * @objects List of output objects*/ void LoadObjectSection(std::vector& objects); // ------------------------------------------------------------------- /** Convert all objects into meshes and nodes. * @param object Current object to work on * @param meshes Pointer to the list of output meshes * @param outMaterials List of output materials * @param materials Material list * @param Scenegraph node for the object */ aiNode* ConvertObjectSection(Object& object, std::vector& meshes, std::vector& outMaterials, const std::vector& materials, aiNode* parent = NULL); // ------------------------------------------------------------------- /** Convert a material * @param object Current object * @param matSrc Source material description * @param matDest Destination material to be filled */ void ConvertMaterial(const Object& object, const Material& matSrc, MaterialHelper& matDest); private: // points to the next data line const char* buffer; // Configuration option: if enabled, up to two meshes // are generated per material: those faces who have // their bf cull flags set are separated. bool configSplitBFCull; // Configuration switch: subdivision surfaces are only // evaluated if the value is true. bool configEvalSubdivision; // counts how many objects we have in the tree. // basing on this information we can find a // good estimate how many meshes we'll have in the final scene. unsigned int mNumMeshes; // current list of light sources std::vector* mLights; // name counters unsigned int lights, groups, polys, worlds; }; } // end of namespace Assimp #endif // AI_AC3DIMPORTER_H_INC 3rdparty/assimp/code/ASELoader.cpp000066400000000000000000001512071217616171500173140ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file ASELoader.cpp * @brief Implementation of the ASE importer class */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER // internal headers #include "ASELoader.h" #include "MaterialSystem.h" #include "StringComparison.h" #include "SkeletonMeshBuilder.h" #include "TargetAnimation.h" // utilities #include "fast_atof.h" using namespace Assimp; using namespace Assimp::ASE; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ASEImporter::ASEImporter() : mParser(0), mBuffer(0), pcScene(0), configRecomputeNormals(false) {} // ------------------------------------------------------------------------------------------------ // Destructor, private as well ASEImporter::~ASEImporter() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const { // check file extension const std::string extension = GetExtension(pFile); if ( extension == "ase" || extension == "ask") return true; if ((!extension.length() || cs) && pIOHandler) { const char* tokens[] = {"*3dsmax_asciiexport"}; return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); } return false; } // ------------------------------------------------------------------------------------------------ void ASEImporter::GetExtensionList(std::set& extensions) { extensions.insert("ase"); extensions.insert("ask"); } // ------------------------------------------------------------------------------------------------ // Setup configuration options void ASEImporter::SetupProperties(const Importer* pImp) { configRecomputeNormals = (pImp->GetPropertyInteger( AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false); } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void ASEImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file if ( file.get() == NULL) { throw DeadlyImportError( "Failed to open ASE file " + pFile + "."); } // Allocate storage and copy the contents of the file to a memory buffer std::vector mBuffer2; TextFileToBuffer(file.get(),mBuffer2); this->mBuffer = &mBuffer2[0]; this->pcScene = pScene; // ------------------------------------------------------------------ // Guess the file format by looking at the extension // ASC is considered to be the older format 110, // ASE is the actual version 200 (that is currently written by max) // ------------------------------------------------------------------ unsigned int defaultFormat; std::string::size_type s = pFile.length()-1; switch (pFile.c_str()[s]) { case 'C': case 'c': defaultFormat = AI_ASE_OLD_FILE_FORMAT; break; default: defaultFormat = AI_ASE_NEW_FILE_FORMAT; }; // Construct an ASE parser and parse the file ASE::Parser parser(mBuffer,defaultFormat); mParser = &parser; mParser->Parse(); //------------------------------------------------------------------ // Check whether we god at least one mesh. If we did - generate // materials and copy meshes. // ------------------------------------------------------------------ if ( !mParser->m_vMeshes.empty()) { // If absolutely no material has been loaded from the file // we need to generate a default material GenerateDefaultMaterial(); // process all meshes bool tookNormals = false; std::vector avOutMeshes; avOutMeshes.reserve(mParser->m_vMeshes.size()*2); for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { if ((*i).bSkip) { continue; } BuildUniqueRepresentation(*i); // Need to generate proper vertex normals if necessary if (GenerateNormals(*i)) { tookNormals = true; } // Convert all meshes to aiMesh objects ConvertMeshes(*i,avOutMeshes); } if (tookNormals) { DefaultLogger::get()->debug("ASE: Taking normals from the file. Use " "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you " "experience problems"); } // Now build the output mesh list. Remove dummies pScene->mNumMeshes = (unsigned int)avOutMeshes.size(); aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; for (std::vector::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) { if (!(*i)->mNumFaces) { continue; } *pp++ = *i; } pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes); // Build final material indices (remove submaterials and setup // the final list) BuildMaterialIndices(); } // ------------------------------------------------------------------ // Copy all scene graph nodes - lights, cameras, dummies and meshes // into one huge list. //------------------------------------------------------------------ std::vector nodes; nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size() + mParser->m_vCameras.size() + mParser->m_vDummies.size()); // Lights for (std::vector::iterator it = mParser->m_vLights.begin(), end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it)); // Cameras for (std::vector::iterator it = mParser->m_vCameras.begin(), end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it)); // Meshes for (std::vector::iterator it = mParser->m_vMeshes.begin(), end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it)); // Dummies for (std::vector::iterator it = mParser->m_vDummies.begin(), end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it)); // build the final node graph BuildNodes(nodes); // build output animations BuildAnimations(nodes); // build output cameras BuildCameras(); // build output lights BuildLights(); // ------------------------------------------------------------------ // If we have no meshes use the SkeletonMeshBuilder helper class // to build a mesh for the animation skeleton // FIXME: very strange results // ------------------------------------------------------------------ if (!pScene->mNumMeshes) { pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; SkeletonMeshBuilder skeleton(pScene); } } // ------------------------------------------------------------------------------------------------ void ASEImporter::GenerateDefaultMaterial() { ai_assert(NULL != mParser); bool bHas = false; for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { if ((*i).bSkip)continue; if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) { (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size(); bHas = true; } } if (bHas || mParser->m_vMaterials.empty()) { // add a simple material without submaterials to the parser's list mParser->m_vMaterials.push_back ( ASE::Material() ); ASE::Material& mat = mParser->m_vMaterials.back(); mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f); mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f); mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f); mat.mShading = Discreet3DS::Gouraud; mat.mName = AI_DEFAULT_MATERIAL_NAME; } } // ------------------------------------------------------------------------------------------------ void ASEImporter::BuildAnimations(const std::vector& nodes) { // check whether we have at least one mesh which has animations std::vector::const_iterator i = nodes.begin(); unsigned int iNum = 0; for (;i != nodes.end();++i) { // TODO: Implement Bezier & TCB support if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) { DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " "This is not supported."); } if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) { DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. " "This is not supported."); } if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) { DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " "This is not supported."); } // We compare against 1 here - firstly one key is not // really an animation and secondly MAX writes dummies // that represent the node transformation. if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){ ++iNum; } if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) { ++iNum; } } if (iNum) { // Generate a new animation channel and setup everything for it pcScene->mNumAnimations = 1; pcScene->mAnimations = new aiAnimation*[1]; aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation(); pcAnim->mNumChannels = iNum; pcAnim->mChannels = new aiNodeAnim*[iNum]; pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame; iNum = 0; // Now iterate through all meshes and collect all data we can find for (i = nodes.begin();i != nodes.end();++i) { ASE::BaseNode* me = *i; if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) { // Generate an extra channel for the camera/light target. // BuildNodes() does also generate an extra node, named // .Target. aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); nd->mNodeName.Set(me->mName + ".Target"); // If there is no input position channel we will need // to supply the default position from the node's // local transformation matrix. /*TargetAnimationHelper helper; if (me->mAnim.akeyPositions.empty()) { aiMatrix4x4& mat = (*i)->mTransform; helper.SetFixedMainAnimationChannel(aiVector3D( mat.a4, mat.b4, mat.c4)); } else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions); helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions); helper.Process(&me->mTargetAnim.akeyPositions);*/ // Allocate the key array and fill it nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size(); nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0], nd->mNumPositionKeys * sizeof(aiVectorKey)); } if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) { // Begin a new node animation channel for this node aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); nd->mNodeName.Set(me->mName); // copy position keys if (me->mAnim.akeyPositions.size() > 1 ) { // Allocate the key array and fill it nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size(); nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0], nd->mNumPositionKeys * sizeof(aiVectorKey)); } // copy rotation keys if (me->mAnim.akeyRotations.size() > 1 ) { // Allocate the key array and fill it nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size(); nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; // -------------------------------------------------------------------- // Rotation keys are offsets to the previous keys. // We have the quaternion representations of all // of them, so we just need to concatenate all // (unit-length) quaternions to get the absolute // rotations. // Rotation keys are ABSOLUTE for older files // -------------------------------------------------------------------- aiQuaternion cur; for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { aiQuatKey q = me->mAnim.akeyRotations[a]; if (mParser->iFileFormat > 110) { cur = (a ? cur*q.mValue : q.mValue); q.mValue = cur.Normalize(); } nd->mRotationKeys[a] = q; // need this to get to Assimp quaternion conventions nd->mRotationKeys[a].mValue.w *= -1.f; } } // copy scaling keys if (me->mAnim.akeyScaling.size() > 1 ) { // Allocate the key array and fill it nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size(); nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0], nd->mNumScalingKeys * sizeof(aiVectorKey)); } } } } } // ------------------------------------------------------------------------------------------------ // Build output cameras void ASEImporter::BuildCameras() { if (!mParser->m_vCameras.empty()) { pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size(); pcScene->mCameras = new aiCamera*[pcScene->mNumCameras]; for (unsigned int i = 0; i < pcScene->mNumCameras;++i) { aiCamera* out = pcScene->mCameras[i] = new aiCamera(); ASE::Camera& in = mParser->m_vCameras[i]; // copy members out->mClipPlaneFar = in.mFar; out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f); out->mHorizontalFOV = in.mFOV; out->mName.Set(in.mName); } } } // ------------------------------------------------------------------------------------------------ // Build output lights void ASEImporter::BuildLights() { if (!mParser->m_vLights.empty()) { pcScene->mNumLights = (unsigned int)mParser->m_vLights.size(); pcScene->mLights = new aiLight*[pcScene->mNumLights]; for (unsigned int i = 0; i < pcScene->mNumLights;++i) { aiLight* out = pcScene->mLights[i] = new aiLight(); ASE::Light& in = mParser->m_vLights[i]; // The direction is encoded in the transformation matrix of the node. // In 3DS MAX the light source points into negative Z direction if // the node transformation is the identity. out->mDirection = aiVector3D(0.f,0.f,-1.f); out->mName.Set(in.mName); switch (in.mLightType) { case ASE::Light::TARGET: out->mType = aiLightSource_SPOT; out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle); out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone); break; case ASE::Light::DIRECTIONAL: out->mType = aiLightSource_DIRECTIONAL; break; default: //case ASE::Light::OMNI: out->mType = aiLightSource_POINT; break; }; out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity; } } } // ------------------------------------------------------------------------------------------------ void ASEImporter::AddNodes(const std::vector& nodes, aiNode* pcParent,const char* szName) { aiMatrix4x4 m; AddNodes(nodes,pcParent,szName,m); } // ------------------------------------------------------------------------------------------------ // Add meshes to a given node void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node) { for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) { // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color) const aiMesh* pcMesh = pcScene->mMeshes[i]; const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; if (mesh == snode) { ++node->mNumMeshes; } } if (node->mNumMeshes) { node->mMeshes = new unsigned int[node->mNumMeshes]; for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) { const aiMesh* pcMesh = pcScene->mMeshes[i]; const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; if (mesh == snode) { node->mMeshes[p++] = i; // Transform all vertices of the mesh back into their local space -> // at the moment they are pretransformed aiMatrix4x4 m = mesh->mTransform; m.Inverse(); aiVector3D* pvCurPtr = pcMesh->mVertices; const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices; while (pvCurPtr != pvEndPtr) { *pvCurPtr = m * (*pvCurPtr); pvCurPtr++; } // Do the same for the normal vectors, if we have them. // As always, inverse transpose. if (pcMesh->mNormals) { aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform ); m3.Transpose(); pvCurPtr = pcMesh->mNormals; pvEndPtr = pvCurPtr + pcMesh->mNumVertices; while (pvCurPtr != pvEndPtr) { *pvCurPtr = m3 * (*pvCurPtr); pvCurPtr++; } } } } } } // ------------------------------------------------------------------------------------------------ // Add child nodes to a given parent node void ASEImporter::AddNodes (const std::vector& nodes, aiNode* pcParent, const char* szName, const aiMatrix4x4& mat) { const size_t len = szName ? ::strlen(szName) : 0; ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); // Receives child nodes for the pcParent node std::vector apcNodes; // Now iterate through all nodes in the scene and search for one // which has *us* as parent. for (std::vector::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { const BaseNode* snode = *it; if (szName) { if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str())) continue; } else if (snode->mParent.length()) continue; (*it)->mProcessed = true; // Allocate a new node and add it to the output data structure apcNodes.push_back(new aiNode()); aiNode* node = apcNodes.back(); node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node")); node->mParent = pcParent; // Setup the transformation matrix of the node aiMatrix4x4 mParentAdjust = mat; mParentAdjust.Inverse(); node->mTransformation = mParentAdjust*snode->mTransform; // Add sub nodes - prevent stack overflow due to recursive parenting if (node->mName != node->mParent->mName) { AddNodes(nodes,node,node->mName.data,snode->mTransform); } // Further processing depends on the type of the node if (snode->mType == ASE::BaseNode::Mesh) { // If the type of this node is "Mesh" we need to search // the list of output meshes in the data structure for // all those that belonged to this node once. This is // slightly inconvinient here and a better solution should // be used when this code is refactored next. AddMeshes(snode,node); } else if (is_not_qnan( snode->mTargetPosition.x )) { // If this is a target camera or light we generate a small // child node which marks the position of the camera // target (the direction information is contained in *this* // node's animation track but the exact target position // would be lost otherwise) if (!node->mNumChildren) { node->mChildren = new aiNode*[1]; } aiNode* nd = new aiNode(); nd->mName.Set ( snode->mName + ".Target" ); nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4; nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4; nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4; nd->mParent = node; // The .Target node is always the first child node for (unsigned int m = 0; m < node->mNumChildren;++m) node->mChildren[m+1] = node->mChildren[m]; node->mChildren[0] = nd; node->mNumChildren++; // What we did is so great, it is at least worth a debug message DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")"); } } // Allocate enough space for the child nodes // We allocate one slot more in case this is a target camera/light pcParent->mNumChildren = (unsigned int)apcNodes.size(); if (pcParent->mNumChildren) { pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */]; // now build all nodes for our nice new children for (unsigned int p = 0; p < apcNodes.size();++p) pcParent->mChildren[p] = apcNodes[p]; } return; } // ------------------------------------------------------------------------------------------------ // Build the output node graph void ASEImporter::BuildNodes(std::vector& nodes) { ai_assert(NULL != pcScene); // allocate the one and only root node aiNode* root = pcScene->mRootNode = new aiNode(); root->mName.Set(""); // Setup the coordinate system transformation pcScene->mRootNode->mNumChildren = 1; pcScene->mRootNode->mChildren = new aiNode*[1]; aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode(); ch->mParent = root; // Change the transformation matrix of all nodes for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { aiMatrix4x4& m = (*it)->mTransform; m.Transpose(); // row-order vs column-order } // add all nodes AddNodes(nodes,ch,NULL); // now iterate through al nodes and find those that have not yet // been added to the nodegraph (= their parent could not be recognized) std::vector aiList; for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { if ((*it)->mProcessed) { continue; } // check whether our parent is known bool bKnowParent = false; // search the list another time, starting *here* and try to find out whether // there is a node that references *us* as a parent for (std::vector::const_iterator it2 = nodes.begin();it2 != end; ++it2) { if (it2 == it) { continue; } if ((*it2)->mName == (*it)->mParent) { bKnowParent = true; break; } } if (!bKnowParent) { aiList.push_back(*it); } } // Are there ane orphaned nodes? if (!aiList.empty()) { std::vector apcNodes; apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren); for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i) apcNodes.push_back(pcScene->mRootNode->mChildren[i]); delete[] pcScene->mRootNode->mChildren; for (std::vector::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) { const ASE::BaseNode* src = *i; // The parent is not known, so we can assume that we must add // this node to the root node of the whole scene aiNode* pcNode = new aiNode(); pcNode->mParent = pcScene->mRootNode; pcNode->mName.Set(src->mName); AddMeshes(src,pcNode); AddNodes(nodes,pcNode,pcNode->mName.data); apcNodes.push_back(pcNode); } // Regenerate our output array pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()]; for (unsigned int i = 0; i < apcNodes.size();++i) pcScene->mRootNode->mChildren[i] = apcNodes[i]; pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size(); } // Reset the third color set to NULL - we used this field to store a temporary pointer for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) pcScene->mMeshes[i]->mColors[2] = NULL; // The root node should not have at least one child or the file is valid if (!pcScene->mRootNode->mNumChildren) { throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt"); } // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); } // ------------------------------------------------------------------------------------------------ // Convert the imported data to the internal verbose representation void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { // allocate output storage std::vector mPositions; std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; std::vector mVertexColors; std::vector mNormals; std::vector mBoneVertices; unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3; mPositions.resize(iSize); // optional texture coordinates for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { if (!mesh.amTexCoords[i].empty()) { amTexCoords[i].resize(iSize); } } // optional vertex colors if (!mesh.mVertexColors.empty()) { mVertexColors.resize(iSize); } // optional vertex normals (vertex normals can simply be copied) if (!mesh.mNormals.empty()) { mNormals.resize(iSize); } // bone vertices. There is no need to change the bone list if (!mesh.mBoneVertices.empty()) { mBoneVertices.resize(iSize); } // iterate through all faces in the mesh unsigned int iCurrent = 0, fi = 0; for (std::vector::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) { for (unsigned int n = 0; n < 3;++n,++iCurrent) { mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]]; // add texture coordinates for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { if (mesh.amTexCoords[c].empty())break; amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]]; } // add vertex colors if (!mesh.mVertexColors.empty()) { mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]]; } // add normal vectors if (!mesh.mNormals.empty()) { mNormals[iCurrent] = mesh.mNormals[fi*3+n]; mNormals[iCurrent].Normalize(); } // handle bone vertices if ((*i).mIndices[n] < mesh.mBoneVertices.size()) { // (sometimes this will cause bone verts to be duplicated // however, I' quite sure Schrompf' JoinVerticesStep // will fix that again ...) mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; } (*i).mIndices[n] = iCurrent; } } // replace the old arrays mesh.mNormals = mNormals; mesh.mPositions = mPositions; mesh.mVertexColors = mVertexColors; for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) mesh.amTexCoords[c] = amTexCoords[c]; } // ------------------------------------------------------------------------------------------------ // Copy a texture from the ASE structs to the output material void CopyASETexture(MaterialHelper& mat, ASE::Texture& texture, aiTextureType type) { // Setup the texture name aiString tex; tex.Set( texture.mMapName); mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); // Setup the texture blend factor if (is_not_qnan(texture.mTextureBlend)) mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); // Setup texture UV transformations mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); } // ------------------------------------------------------------------------------------------------ // Convert from ASE material to output material void ASEImporter::ConvertMaterial(ASE::Material& mat) { // LARGE TODO: Much code her is copied from 3DS ... join them maybe? // Allocate the output material mat.pcInstance = new MaterialHelper(); // At first add the base ambient color of the // scene to the material mat.mAmbient.r += mParser->m_clrAmbient.r; mat.mAmbient.g += mParser->m_clrAmbient.g; mat.mAmbient.b += mParser->m_clrAmbient.b; aiString name; name.Set( mat.mName); mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME); // material colors mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); // shininess if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) { mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); } // If there is no shininess, we can disable phong lighting else if (D3DS::Discreet3DS::Metal == mat.mShading || D3DS::Discreet3DS::Phong == mat.mShading || D3DS::Discreet3DS::Blinn == mat.mShading) { mat.mShading = D3DS::Discreet3DS::Gouraud; } // opacity mat.pcInstance->AddProperty( &mat.mTransparency,1,AI_MATKEY_OPACITY); // Two sided rendering? if (mat.mTwoSided) { int i = 1; mat.pcInstance->AddProperty(&i,1,AI_MATKEY_TWOSIDED); } // shading mode aiShadingMode eShading = aiShadingMode_NoShading; switch (mat.mShading) { case D3DS::Discreet3DS::Flat: eShading = aiShadingMode_Flat; break; case D3DS::Discreet3DS::Phong : eShading = aiShadingMode_Phong; break; case D3DS::Discreet3DS::Blinn : eShading = aiShadingMode_Blinn; break; // I don't know what "Wire" shading should be, // assume it is simple lambertian diffuse (L dot N) shading case D3DS::Discreet3DS::Wire: { // set the wireframe flag unsigned int iWire = 1; mat.pcInstance->AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); } case D3DS::Discreet3DS::Gouraud: eShading = aiShadingMode_Gouraud; break; case D3DS::Discreet3DS::Metal : eShading = aiShadingMode_CookTorrance; break; } mat.pcInstance->AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); // DIFFUSE texture if ( mat.sTexDiffuse.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE); // SPECULAR texture if ( mat.sTexSpecular.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR); // AMBIENT texture if ( mat.sTexAmbient.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT); // OPACITY texture if ( mat.sTexOpacity.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY); // EMISSIVE texture if ( mat.sTexEmissive.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE); // BUMP texture if ( mat.sTexBump.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT); // SHININESS texture if ( mat.sTexShininess.mMapName.length() > 0) CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS); // store the name of the material itself, too if ( mat.mName.length() > 0) { aiString tex;tex.Set( mat.mName); mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME); } return; } // ------------------------------------------------------------------------------------------------ // Build output meshes void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector& avOutMeshes) { // validate the material index of the mesh if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) { mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1; DefaultLogger::get()->warn("Material index is out of range"); } // If the material the mesh is assigned to is consisting of submeshes, split it if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) { std::vector vSubMaterials = mParser-> m_vMaterials[mesh.iMaterialIndex].avSubMaterials; std::vector* aiSplit = new std::vector[vSubMaterials.size()]; // build a list of all faces per submaterial for (unsigned int i = 0; i < mesh.mFaces.size();++i) { // check range if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) { DefaultLogger::get()->warn("Submaterial index is out of range"); // use the last material instead aiSplit[vSubMaterials.size()-1].push_back(i); } else aiSplit[mesh.mFaces[i].iMaterial].push_back(i); } // now generate submeshes for (unsigned int p = 0; p < vSubMaterials.size();++p) { if (!aiSplit[p].empty()) { aiMesh* p_pcOut = new aiMesh(); p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // let the sub material index p_pcOut->mMaterialIndex = p; // we will need this material mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true; // store the real index here ... color channel 3 p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; // store a pointer to the mesh in color channel 2 p_pcOut->mColors[2] = (aiColor4D*) &mesh; avOutMeshes.push_back(p_pcOut); // convert vertices p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3; p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size(); // receive output vertex weights std::vector > *avOutputBones = NULL; if (!mesh.mBones.empty()) { avOutputBones = new std::vector >[mesh.mBones.size()]; } // allocate enough storage for faces p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; unsigned int iBase = 0,iIndex; if (p_pcOut->mNumVertices) { p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; for (unsigned int q = 0; q < aiSplit[p].size();++q) { iIndex = aiSplit[p][q]; p_pcOut->mFaces[q].mIndices = new unsigned int[3]; p_pcOut->mFaces[q].mNumIndices = 3; for (unsigned int t = 0; t < 3;++t, ++iBase) { const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2]; p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2]; // convert bones, if existing if (!mesh.mBones.empty()) { // check whether there is a vertex weight for this vertex index if (iIndex2 < mesh.mBoneVertices.size()) { for (std::vector >::const_iterator blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) { // NOTE: illegal cases have already been filtered out avOutputBones[(*blubb).first].push_back(std::pair( iBase,(*blubb).second)); } } } p_pcOut->mFaces[q].mIndices[t] = iBase; } } } // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported) for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { if (!mesh.amTexCoords[c].empty()) { p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices]; iBase = 0; for (unsigned int q = 0; q < aiSplit[p].size();++q) { iIndex = aiSplit[p][q]; for (unsigned int t = 0; t < 3;++t) { p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]]; } } // Setup the number of valid vertex components p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; } } // Convert vertex colors (only one set supported) if (!mesh.mVertexColors.empty()){ p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices]; iBase = 0; for (unsigned int q = 0; q < aiSplit[p].size();++q) { iIndex = aiSplit[p][q]; for (unsigned int t = 0; t < 3;++t) { p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]]; } } } // Copy bones if (!mesh.mBones.empty()) { p_pcOut->mNumBones = 0; for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; aiBone** pcBone = p_pcOut->mBones; for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) { if (!avOutputBones[mrspock].empty()) { // we will need this bone. add it to the output mesh and // add all per-vertex weights aiBone* pc = *pcBone = new aiBone(); pc->mName.Set(mesh.mBones[mrspock].mName); pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size(); pc->mWeights = new aiVertexWeight[pc->mNumWeights]; for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk) { const std::pair& ref = avOutputBones[mrspock][captainkirk]; pc->mWeights[captainkirk].mVertexId = ref.first; pc->mWeights[captainkirk].mWeight = ref.second; } ++pcBone; } } // delete allocated storage delete[] avOutputBones; } } } // delete storage delete[] aiSplit; } else { // Otherwise we can simply copy the data to one output mesh // This codepath needs less memory and uses fast memcpy()s // to do the actual copying. So I think it is worth the // effort here. aiMesh* p_pcOut = new aiMesh(); p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // set an empty sub material index p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX; mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true; // store the real index here ... in color channel 3 p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; // store a pointer to the mesh in color channel 2 p_pcOut->mColors[2] = (aiColor4D*) &mesh; avOutMeshes.push_back(p_pcOut); // If the mesh hasn't faces or vertices, there are two cases // possible: 1. the model is invalid. 2. This is a dummy // helper object which we are going to remove later ... if (mesh.mFaces.empty() || mesh.mPositions.empty()) { return; } // convert vertices p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size(); p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size(); // allocate enough storage for faces p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; // copy vertices p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()]; memcpy(p_pcOut->mVertices,&mesh.mPositions[0], mesh.mPositions.size() * sizeof(aiVector3D)); // copy normals p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()]; memcpy(p_pcOut->mNormals,&mesh.mNormals[0], mesh.mNormals.size() * sizeof(aiVector3D)); // copy texture coordinates for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { if (!mesh.amTexCoords[c].empty()) { p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()]; memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0], mesh.amTexCoords[c].size() * sizeof(aiVector3D)); // setup the number of valid vertex components p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; } } // copy vertex colors if (!mesh.mVertexColors.empty()) { p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()]; memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0], mesh.mVertexColors.size() * sizeof(aiColor4D)); } // copy faces for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) { p_pcOut->mFaces[iFace].mNumIndices = 3; p_pcOut->mFaces[iFace].mIndices = new unsigned int[3]; // copy indices p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0]; p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; } // copy vertex bones if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) { std::vector* avBonesOut = new std::vector[mesh.mBones.size()]; // find all vertex weights for this bone unsigned int quak = 0; for (std::vector::const_iterator harrypotter = mesh.mBoneVertices.begin(); harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) { for (std::vector >::const_iterator ronaldweasley = (*harrypotter).mBoneWeights.begin(); ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) { aiVertexWeight weight; weight.mVertexId = quak; weight.mWeight = (*ronaldweasley).second; avBonesOut[(*ronaldweasley).first].push_back(weight); } } // now build a final bone list p_pcOut->mNumBones = 0; for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; aiBone** pcBone = p_pcOut->mBones; for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) { if (!avBonesOut[jfkennedy].empty()) { aiBone* pc = *pcBone = new aiBone(); pc->mName.Set(mesh.mBones[jfkennedy].mName); pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size(); pc->mWeights = new aiVertexWeight[pc->mNumWeights]; ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0], sizeof(aiVertexWeight) * pc->mNumWeights); ++pcBone; } } // delete allocated storage delete[] avBonesOut; } } } // ------------------------------------------------------------------------------------------------ // Setup proper material indices and build output materials void ASEImporter::BuildMaterialIndices() { ai_assert(NULL != pcScene); // iterate through all materials and check whether we need them for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { ASE::Material& mat = mParser->m_vMaterials[iMat]; if (mat.bNeed) { // Convert it to the aiMaterial layout ConvertMaterial(mat); ++pcScene->mNumMaterials; } for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { ASE::Material& submat = mat.avSubMaterials[iSubMat]; if (submat.bNeed) { // Convert it to the aiMaterial layout ConvertMaterial(submat); ++pcScene->mNumMaterials; } } } // allocate the output material array pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials]; unsigned int iNum = 0; for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { ASE::Material& mat = mParser->m_vMaterials[iMat]; if (mat.bNeed) { ai_assert(NULL != mat.pcInstance); pcScene->mMaterials[iNum] = mat.pcInstance; // Store the internal material, too pcIntMaterials[iNum] = &mat; // Iterate through all meshes and search for one which is using // this top-level material index for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { aiMesh* mesh = pcScene->mMeshes[iMesh]; if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { mesh->mMaterialIndex = iNum; mesh->mColors[3] = NULL; } } iNum++; } for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { ASE::Material& submat = mat.avSubMaterials[iSubMat]; if (submat.bNeed) { ai_assert(NULL != submat.pcInstance); pcScene->mMaterials[iNum] = submat.pcInstance; // Store the internal material, too pcIntMaterials[iNum] = &submat; // Iterate through all meshes and search for one which is using // this sub-level material index for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { aiMesh* mesh = pcScene->mMeshes[iMesh]; if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { mesh->mMaterialIndex = iNum; mesh->mColors[3] = NULL; } } iNum++; } } } // Dekete our temporary array delete[] pcIntMaterials; } // ------------------------------------------------------------------------------------------------ // Generate normal vectors basing on smoothing groups bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) { if (!mesh.mNormals.empty() && !configRecomputeNormals) { // Check whether there are only uninitialized normals. If there are // some, skip all normals from the file and compute them on our own for (std::vector::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) { if ((*qq).x || (*qq).y || (*qq).z) { return true; } } } // The array is reused. ComputeNormalsWithSmoothingsGroups(mesh); return false; } #endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER 3rdparty/assimp/code/ASELoader.h000066400000000000000000000151441217616171500167600ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file ASELoader.h * @brief Definition of the .ASE importer class. */ #ifndef AI_ASELOADER_H_INCLUDED #define AI_ASELOADER_H_INCLUDED #include "BaseImporter.h" #include "../include/aiTypes.h" struct aiNode; #include "ASEParser.h" namespace Assimp { class MaterialHelper; // -------------------------------------------------------------------------------- /** Importer class for the 3DS ASE ASCII format. * */ class ASEImporter : public BaseImporter { friend class Importer; protected: /** Constructor to be privately used by Importer */ ASEImporter(); /** Destructor, private as well */ ~ASEImporter(); public: // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; protected: // ------------------------------------------------------------------- /** Called by Importer::GetExtensionList() for each loaded importer. * See BaseImporter::GetExtensionList() for details */ void GetExtensionList(std::set& extensions); // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. */ void SetupProperties(const Importer* pImp); private: // ------------------------------------------------------------------- /** Generate normal vectors basing on smoothing groups * (in some cases the normal are already contained in the file) * \param mesh Mesh to work on * \return false if the normals have been recomputed */ bool GenerateNormals(ASE::Mesh& mesh); // ------------------------------------------------------------------- /** Create valid vertex/normal/UV/color/face lists. * All elements are unique, faces have only one set of indices * after this step occurs. * \param mesh Mesh to work on */ void BuildUniqueRepresentation(ASE::Mesh& mesh); /** Create one-material-per-mesh meshes ;-) * \param mesh Mesh to work with * \param Receives the list of all created meshes */ void ConvertMeshes(ASE::Mesh& mesh, std::vector& avOut); // ------------------------------------------------------------------- /** Convert a material to a MaterialHelper object * \param mat Input material */ void ConvertMaterial(ASE::Material& mat); // ------------------------------------------------------------------- /** Setup the final material indices for each mesh */ void BuildMaterialIndices(); // ------------------------------------------------------------------- /** Build the node graph */ void BuildNodes(std::vector& nodes); // ------------------------------------------------------------------- /** Build output cameras */ void BuildCameras(); // ------------------------------------------------------------------- /** Build output lights */ void BuildLights(); // ------------------------------------------------------------------- /** Build output animations */ void BuildAnimations(const std::vector& nodes); // ------------------------------------------------------------------- /** Add sub nodes to a node * \param pcParent parent node to be filled * \param szName Name of the parent node * \param matrix Current transform */ void AddNodes(const std::vector& nodes, aiNode* pcParent,const char* szName); void AddNodes(const std::vector& nodes, aiNode* pcParent,const char* szName, const aiMatrix4x4& matrix); void AddMeshes(const ASE::BaseNode* snode,aiNode* node); // ------------------------------------------------------------------- /** Generate a default material and add it to the parser's list * Called if no material has been found in the file (rare for ASE, * but not impossible) */ void GenerateDefaultMaterial(); protected: /** Parser instance */ ASE::Parser* mParser; /** Buffer to hold the loaded file */ char* mBuffer; /** Scene to be filled */ aiScene* pcScene; /** Config options: Recompute the normals in every case - WA for 3DS Max broken ASE normal export */ bool configRecomputeNormals; }; } // end of namespace Assimp #endif // AI_3DSIMPORTER_H_INC 3rdparty/assimp/code/ASEParser.cpp000066400000000000000000002115351217616171500173430ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file ASEParser.cpp * @brief Implementation of the ASE parser class */ #include "AssimpPCH.h" // internal headers #include "TextureTransform.h" #include "ASELoader.h" #include "MaterialSystem.h" #include "fast_atof.h" using namespace Assimp; using namespace Assimp::ASE; // ------------------------------------------------------------------------------------------------ // Begin an ASE parsing function #define AI_ASE_PARSER_INIT() \ int iDepth = 0; // ------------------------------------------------------------------------------------------------ // Handle a "top-level" section in the file. EOF is no error in this case. #define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ else if ('{' == *filePtr)iDepth++; \ else if ('}' == *filePtr) \ { \ if (0 == --iDepth) \ { \ ++filePtr; \ SkipToNextToken(); \ return; \ } \ } \ else if ('\0' == *filePtr) \ { \ return; \ } \ if (IsLineEnd(*filePtr) && !bLastWasEndLine) \ { \ ++iLineNumber; \ bLastWasEndLine = true; \ } else bLastWasEndLine = false; \ ++filePtr; // ------------------------------------------------------------------------------------------------ // Handle a nested section in the file. EOF is an error in this case // @param level "Depth" of the section // @param msg Full name of the section (including the asterisk) #define AI_ASE_HANDLE_SECTION(level, msg) \ if ('{' == *filePtr)iDepth++; \ else if ('}' == *filePtr) \ { \ if (0 == --iDepth) \ { \ ++filePtr; \ SkipToNextToken(); \ return; \ } \ } \ else if ('\0' == *filePtr) \ { \ LogError("Encountered unexpected EOL while parsing a " msg \ " chunk (Level " level ")"); \ } \ if (IsLineEnd(*filePtr) && !bLastWasEndLine) \ { \ ++iLineNumber; \ bLastWasEndLine = true; \ } else bLastWasEndLine = false; \ ++filePtr; // ------------------------------------------------------------------------------------------------ Parser::Parser (const char* szFile, unsigned int fileFormatDefault) { ai_assert(NULL != szFile); filePtr = szFile; iFileFormat = fileFormatDefault; // make sure that the color values are invalid m_clrBackground.r = get_qnan(); m_clrAmbient.r = get_qnan(); // setup some default values iLineNumber = 0; iFirstFrame = 0; iLastFrame = 0; iFrameSpeed = 30; // use 30 as default value for this property iTicksPerFrame = 1; // use 1 as default value for this property bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping } // ------------------------------------------------------------------------------------------------ void Parser::LogWarning(const char* szWarn) { ai_assert(NULL != szWarn); char szTemp[1024]; #if _MSC_VER >= 1400 sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn); #else snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn); #endif // output the warning to the logger ... DefaultLogger::get()->warn(szTemp); } // ------------------------------------------------------------------------------------------------ void Parser::LogInfo(const char* szWarn) { ai_assert(NULL != szWarn); char szTemp[1024]; #if _MSC_VER >= 1400 sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn); #else snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn); #endif // output the information to the logger ... DefaultLogger::get()->info(szTemp); } // ------------------------------------------------------------------------------------------------ void Parser::LogError(const char* szWarn) { ai_assert(NULL != szWarn); char szTemp[1024]; #if _MSC_VER >= 1400 sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn); #else snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn); #endif // throw an exception throw DeadlyImportError(szTemp); } // ------------------------------------------------------------------------------------------------ bool Parser::SkipToNextToken() { while (true) { char me = *filePtr; // increase the line number counter if necessary if (IsLineEnd(me) && !bLastWasEndLine) { ++iLineNumber; bLastWasEndLine = true; } else bLastWasEndLine = false; if ('*' == me || '}' == me || '{' == me)return true; if ('\0' == me)return false; ++filePtr; } } // ------------------------------------------------------------------------------------------------ bool Parser::SkipSection() { // must handle subsections ... int iCnt = 0; while (true) { if ('}' == *filePtr) { --iCnt; if (0 == iCnt) { // go to the next valid token ... ++filePtr; SkipToNextToken(); return true; } } else if ('{' == *filePtr) { ++iCnt; } else if ('\0' == *filePtr) { LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); return false; } else if (IsLineEnd(*filePtr))++iLineNumber; ++filePtr; } } // ------------------------------------------------------------------------------------------------ void Parser::Parse() { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; // Version should be 200. Validate this ... if (TokenMatch(filePtr,"3DSMAX_ASCIIEXPORT",18)) { unsigned int fmt; ParseLV4MeshLong(fmt); if (fmt > 200) { LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \ be <= 200"); } // ************************************************************* // - fmt will be 0 if we're unable to read the version number // there are some faulty files without a version number ... // in this case we'll guess the exact file format by looking // at the file extension (ASE, ASK, ASC) // ************************************************************* if (fmt)iFileFormat = fmt; continue; } // main scene information if (TokenMatch(filePtr,"SCENE",5)) { ParseLV1SceneBlock(); continue; } // "group" - no implementation yet, in facte // we're just ignoring them for the moment if (TokenMatch(filePtr,"GROUP",5)) { Parse(); continue; } // material list if (TokenMatch(filePtr,"MATERIAL_LIST",13)) { ParseLV1MaterialListBlock(); continue; } // geometric object (mesh) if (TokenMatch(filePtr,"GEOMOBJECT",10)) { m_vMeshes.push_back(Mesh()); ParseLV1ObjectBlock(m_vMeshes.back()); continue; } // helper object = dummy in the hierarchy if (TokenMatch(filePtr,"HELPEROBJECT",12)) { m_vDummies.push_back(Dummy()); ParseLV1ObjectBlock(m_vDummies.back()); continue; } // light object if (TokenMatch(filePtr,"LIGHTOBJECT",11)) { m_vLights.push_back(Light()); ParseLV1ObjectBlock(m_vLights.back()); continue; } // camera object if (TokenMatch(filePtr,"CAMERAOBJECT",12)) { m_vCameras.push_back(Camera()); ParseLV1ObjectBlock(m_vCameras.back()); continue; } // comment - print it on the console if (TokenMatch(filePtr,"COMMENT",7)) { std::string out = ""; ParseString(out,"*COMMENT"); LogInfo(("Comment: " + out).c_str()); continue; } // ASC bone weights if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr,"MESH_SOFTSKINVERTS",18)) { ParseLV1SoftSkinBlock(); } } AI_ASE_HANDLE_TOP_LEVEL_SECTION(); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV1SoftSkinBlock() { // TODO: fix line counting here // ************************************************************** // The soft skin block is formatted differently. There are no // nested sections supported and the single elements aren't // marked by keywords starting with an asterisk. /** FORMAT BEGIN *MESH_SOFTSKINVERTS { [for times:] [for times:] } FORMAT END */ // ************************************************************** while (true) { if (*filePtr == '}' ) {++filePtr;return;} else if (*filePtr == '\0') return; else if (*filePtr == '{' ) ++filePtr; else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) { ASE::Mesh* curMesh = NULL; unsigned int numVerts = 0; const char* sz = filePtr; while (!IsSpaceOrNewLine(*filePtr))++filePtr; const unsigned int diff = (unsigned int)(filePtr-sz); if (diff) { std::string name = std::string(sz,diff); for (std::vector::iterator it = m_vMeshes.begin(); it != m_vMeshes.end(); ++it) { if ((*it).mName == name) { curMesh = & (*it); break; } } if (!curMesh) { LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section"); // Skip the mesh data - until we find a new mesh // or the end of the *MESH_SOFTSKINVERTS section while (true) { SkipSpacesAndLineEnd(&filePtr); if (*filePtr == '}') {++filePtr;return;} else if (!IsNumeric(*filePtr)) break; SkipLine(&filePtr); } } else { SkipSpacesAndLineEnd(&filePtr); ParseLV4MeshLong(numVerts); // Reserve enough storage curMesh->mBoneVertices.reserve(numVerts); for (unsigned int i = 0; i < numVerts;++i) { SkipSpacesAndLineEnd(&filePtr); unsigned int numWeights; ParseLV4MeshLong(numWeights); curMesh->mBoneVertices.push_back(ASE::BoneVertex()); ASE::BoneVertex& vert = curMesh->mBoneVertices.back(); // Reserve enough storage vert.mBoneWeights.reserve(numWeights); for (unsigned int w = 0; w < numWeights;++w) { std::string bone; ParseString(bone,"*MESH_SOFTSKINVERTS.Bone"); // Find the bone in the mesh's list std::pair me; me.first = -1; for (unsigned int n = 0; n < curMesh->mBones.size();++n) { if (curMesh->mBones[n].mName == bone) { me.first = n; break; } } if (-1 == me.first) { // We don't have this bone yet, so add it to the list me.first = (int)curMesh->mBones.size(); curMesh->mBones.push_back(ASE::Bone(bone)); } ParseLV4MeshFloat( me.second ); // Add the new bone weight to list vert.mBoneWeights.push_back(me); } } } } } ++filePtr; SkipSpacesAndLineEnd(&filePtr); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV1SceneBlock() { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; if (TokenMatch(filePtr,"SCENE_BACKGROUND_STATIC",23)) { // parse a color triple and assume it is really the bg color ParseLV4MeshFloatTriple( &m_clrBackground.r ); continue; } if (TokenMatch(filePtr,"SCENE_AMBIENT_STATIC",20)) { // parse a color triple and assume it is really the bg color ParseLV4MeshFloatTriple( &m_clrAmbient.r ); continue; } if (TokenMatch(filePtr,"SCENE_FIRSTFRAME",16)) { ParseLV4MeshLong(iFirstFrame); continue; } if (TokenMatch(filePtr,"SCENE_LASTFRAME",15)) { ParseLV4MeshLong(iLastFrame); continue; } if (TokenMatch(filePtr,"SCENE_FRAMESPEED",16)) { ParseLV4MeshLong(iFrameSpeed); continue; } if (TokenMatch(filePtr,"SCENE_TICKSPERFRAME",19)) { ParseLV4MeshLong(iTicksPerFrame); continue; } } AI_ASE_HANDLE_TOP_LEVEL_SECTION(); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV1MaterialListBlock() { AI_ASE_PARSER_INIT(); unsigned int iMaterialCount = 0; unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); while (true) { if ('*' == *filePtr) { ++filePtr; if (TokenMatch(filePtr,"MATERIAL_COUNT",14)) { ParseLV4MeshLong(iMaterialCount); // now allocate enough storage to hold all materials m_vMaterials.resize(iOldMaterialCount+iMaterialCount); continue; } if (TokenMatch(filePtr,"MATERIAL",8)) { unsigned int iIndex = 0; ParseLV4MeshLong(iIndex); if (iIndex >= iMaterialCount) { LogWarning("Out of range: material index is too large"); iIndex = iMaterialCount-1; } // get a reference to the material Material& sMat = m_vMaterials[iIndex+iOldMaterialCount]; // parse the material block ParseLV2MaterialBlock(sMat); continue; } } AI_ASE_HANDLE_TOP_LEVEL_SECTION(); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV2MaterialBlock(ASE::Material& mat) { AI_ASE_PARSER_INIT(); unsigned int iNumSubMaterials = 0; while (true) { if ('*' == *filePtr) { ++filePtr; if (TokenMatch(filePtr,"MATERIAL_NAME",13)) { if (!ParseString(mat.mName,"*MATERIAL_NAME")) SkipToNextToken(); continue; } // ambient material color if (TokenMatch(filePtr,"MATERIAL_AMBIENT",16)) { ParseLV4MeshFloatTriple(&mat.mAmbient.r); continue; } // diffuse material color if (TokenMatch(filePtr,"MATERIAL_DIFFUSE",16) ) { ParseLV4MeshFloatTriple(&mat.mDiffuse.r); continue; } // specular material color if (TokenMatch(filePtr,"MATERIAL_SPECULAR",17)) { ParseLV4MeshFloatTriple(&mat.mSpecular.r); continue; } // material shading type if (TokenMatch(filePtr,"MATERIAL_SHADING",16)) { if (TokenMatch(filePtr,"Blinn",5)) { mat.mShading = Discreet3DS::Blinn; } else if (TokenMatch(filePtr,"Phong",5)) { mat.mShading = Discreet3DS::Phong; } else if (TokenMatch(filePtr,"Flat",4)) { mat.mShading = Discreet3DS::Flat; } else if (TokenMatch(filePtr,"Wire",4)) { mat.mShading = Discreet3DS::Wire; } else { // assume gouraud shading mat.mShading = Discreet3DS::Gouraud; SkipToNextToken(); } continue; } // material transparency if (TokenMatch(filePtr,"MATERIAL_TRANSPARENCY",21)) { ParseLV4MeshFloat(mat.mTransparency); mat.mTransparency = 1.0f - mat.mTransparency;continue; } // material self illumination if (TokenMatch(filePtr,"MATERIAL_SELFILLUM",18)) { float f = 0.0f; ParseLV4MeshFloat(f); mat.mEmissive.r = f; mat.mEmissive.g = f; mat.mEmissive.b = f; continue; } // material shininess if (TokenMatch(filePtr,"MATERIAL_SHINE",14) ) { ParseLV4MeshFloat(mat.mSpecularExponent); mat.mSpecularExponent *= 15; continue; } // two-sided material if (TokenMatch(filePtr,"MATERIAL_TWOSIDED",17) ) { mat.mTwoSided = true; continue; } // material shininess strength if (TokenMatch(filePtr,"MATERIAL_SHINESTRENGTH",22)) { ParseLV4MeshFloat(mat.mShininessStrength); continue; } // diffuse color map if (TokenMatch(filePtr,"MAP_DIFFUSE",11)) { // parse the texture block ParseLV3MapBlock(mat.sTexDiffuse); continue; } // ambient color map if (TokenMatch(filePtr,"MAP_AMBIENT",11)) { // parse the texture block ParseLV3MapBlock(mat.sTexAmbient); continue; } // specular color map if (TokenMatch(filePtr,"MAP_SPECULAR",12)) { // parse the texture block ParseLV3MapBlock(mat.sTexSpecular); continue; } // opacity map if (TokenMatch(filePtr,"MAP_OPACITY",11)) { // parse the texture block ParseLV3MapBlock(mat.sTexOpacity); continue; } // emissive map if (TokenMatch(filePtr,"MAP_SELFILLUM",13)) { // parse the texture block ParseLV3MapBlock(mat.sTexEmissive); continue; } // bump map if (TokenMatch(filePtr,"MAP_BUMP",8)) { // parse the texture block ParseLV3MapBlock(mat.sTexBump); } // specular/shininess map if (TokenMatch(filePtr,"MAP_SHINESTRENGTH",17)) { // parse the texture block ParseLV3MapBlock(mat.sTexShininess); continue; } // number of submaterials if (TokenMatch(filePtr,"NUMSUBMTLS",10)) { ParseLV4MeshLong(iNumSubMaterials); // allocate enough storage mat.avSubMaterials.resize(iNumSubMaterials); } // submaterial chunks if (TokenMatch(filePtr,"SUBMATERIAL",11)) { unsigned int iIndex = 0; ParseLV4MeshLong(iIndex); if (iIndex >= iNumSubMaterials) { LogWarning("Out of range: submaterial index is too large"); iIndex = iNumSubMaterials-1; } // get a reference to the material Material& sMat = mat.avSubMaterials[iIndex]; // parse the material block ParseLV2MaterialBlock(sMat); continue; } } AI_ASE_HANDLE_SECTION("2","*MATERIAL"); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MapBlock(Texture& map) { AI_ASE_PARSER_INIT(); // *********************************************************** // *BITMAP should not be there if *MAP_CLASS is not BITMAP, // but we need to expect that case ... if the path is // empty the texture won't be used later. // *********************************************************** bool parsePath = true; while (true) { if ('*' == *filePtr) { ++filePtr; // type of map if (TokenMatch(filePtr,"MAP_CLASS" ,9)) { std::string temp; if (!ParseString(temp,"*MAP_CLASS")) SkipToNextToken(); if (temp != "Bitmap") { DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp); parsePath = false; } continue; } // path to the texture if (parsePath && TokenMatch(filePtr,"BITMAP" ,6)) { if (!ParseString(map.mMapName,"*BITMAP")) SkipToNextToken(); if (map.mMapName == "None") { // Files with 'None' as map name are produced by // an Maja to ASE exporter which name I forgot .. DefaultLogger::get()->warn("ASE: Skipping invalid map entry"); map.mMapName = ""; } continue; } // offset on the u axis if (TokenMatch(filePtr,"UVW_U_OFFSET" ,12)) { ParseLV4MeshFloat(map.mOffsetU); continue; } // offset on the v axis if (TokenMatch(filePtr,"UVW_V_OFFSET" ,12)) { ParseLV4MeshFloat(map.mOffsetV); continue; } // tiling on the u axis if (TokenMatch(filePtr,"UVW_U_TILING" ,12)) { ParseLV4MeshFloat(map.mScaleU); continue; } // tiling on the v axis if (TokenMatch(filePtr,"UVW_V_TILING" ,12)) { ParseLV4MeshFloat(map.mScaleV); continue; } // rotation around the z-axis if (TokenMatch(filePtr,"UVW_ANGLE" ,9)) { ParseLV4MeshFloat(map.mRotation); continue; } // map blending factor if (TokenMatch(filePtr,"MAP_AMOUNT" ,10)) { ParseLV4MeshFloat(map.mTextureBlend); continue; } } AI_ASE_HANDLE_SECTION("3","*MAP_XXXXXX"); } return; } // ------------------------------------------------------------------------------------------------ bool Parser::ParseString(std::string& out,const char* szName) { char szBuffer[1024]; if (!SkipSpaces(&filePtr)) { sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName); LogWarning(szBuffer); return false; } // there must be '"' if ('\"' != *filePtr) { sprintf(szBuffer,"Unable to parse %s block: Strings are expected " "to be enclosed in double quotation marks",szName); LogWarning(szBuffer); return false; } ++filePtr; const char* sz = filePtr; while (true) { if ('\"' == *sz)break; else if ('\0' == *sz) { sprintf(szBuffer,"Unable to parse %s block: Strings are expected to " "be enclosed in double quotation marks but EOF was reached before " "a closing quotation mark was encountered",szName); LogWarning(szBuffer); return false; } sz++; } out = std::string(filePtr,(uintptr_t)sz-(uintptr_t)filePtr); filePtr = sz+1; return true; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV1ObjectBlock(ASE::BaseNode& node) { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; // first process common tokens such as node name and transform // name of the mesh/node if (TokenMatch(filePtr,"NODE_NAME" ,9)) { if (!ParseString(node.mName,"*NODE_NAME")) SkipToNextToken(); continue; } // name of the parent of the node if (TokenMatch(filePtr,"NODE_PARENT" ,11) ) { if (!ParseString(node.mParent,"*NODE_PARENT")) SkipToNextToken(); continue; } // transformation matrix of the node if (TokenMatch(filePtr,"NODE_TM" ,7)) { ParseLV2NodeTransformBlock(node); continue; } // animation data of the node if (TokenMatch(filePtr,"TM_ANIMATION" ,12)) { ParseLV2AnimationBlock(node); continue; } if (node.mType == BaseNode::Light) { // light settings if (TokenMatch(filePtr,"LIGHT_SETTINGS" ,14)) { ParseLV2LightSettingsBlock((ASE::Light&)node); continue; } // type of the light source if (TokenMatch(filePtr,"LIGHT_TYPE" ,10)) { if (!ASSIMP_strincmp("omni",filePtr,4)) { ((ASE::Light&)node).mLightType = ASE::Light::OMNI; } else if (!ASSIMP_strincmp("target",filePtr,6)) { ((ASE::Light&)node).mLightType = ASE::Light::TARGET; } else if (!ASSIMP_strincmp("free",filePtr,4)) { ((ASE::Light&)node).mLightType = ASE::Light::FREE; } else if (!ASSIMP_strincmp("directional",filePtr,11)) { ((ASE::Light&)node).mLightType = ASE::Light::DIRECTIONAL; } else { LogWarning("Unknown kind of light source"); } continue; } } else if (node.mType == BaseNode::Camera) { // Camera settings if (TokenMatch(filePtr,"CAMERA_SETTINGS" ,15)) { ParseLV2CameraSettingsBlock((ASE::Camera&)node); continue; } else if (TokenMatch(filePtr,"CAMERA_TYPE" ,11)) { if (!ASSIMP_strincmp("target",filePtr,6)) { ((ASE::Camera&)node).mCameraType = ASE::Camera::TARGET; } else if (!ASSIMP_strincmp("free",filePtr,4)) { ((ASE::Camera&)node).mCameraType = ASE::Camera::FREE; } else { LogWarning("Unknown kind of camera"); } continue; } } else if (node.mType == BaseNode::Mesh) { // mesh data // FIX: Older files use MESH_SOFTSKIN if (TokenMatch(filePtr,"MESH" ,4) || TokenMatch(filePtr,"MESH_SOFTSKIN",13)) { ParseLV2MeshBlock((ASE::Mesh&)node); continue; } // mesh material index if (TokenMatch(filePtr,"MATERIAL_REF" ,12)) { ParseLV4MeshLong(((ASE::Mesh&)node).iMaterialIndex); continue; } } } AI_ASE_HANDLE_TOP_LEVEL_SECTION(); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV2CameraSettingsBlock(ASE::Camera& camera) { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; if (TokenMatch(filePtr,"CAMERA_NEAR" ,11)) { ParseLV4MeshFloat(camera.mNear); continue; } if (TokenMatch(filePtr,"CAMERA_FAR" ,10)) { ParseLV4MeshFloat(camera.mFar); continue; } if (TokenMatch(filePtr,"CAMERA_FOV" ,10)) { ParseLV4MeshFloat(camera.mFOV); continue; } } AI_ASE_HANDLE_SECTION("2","CAMERA_SETTINGS"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV2LightSettingsBlock(ASE::Light& light) { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; if (TokenMatch(filePtr,"LIGHT_COLOR" ,11)) { ParseLV4MeshFloatTriple(&light.mColor.r); continue; } if (TokenMatch(filePtr,"LIGHT_INTENS" ,12)) { ParseLV4MeshFloat(light.mIntensity); continue; } if (TokenMatch(filePtr,"LIGHT_HOTSPOT" ,13)) { ParseLV4MeshFloat(light.mAngle); continue; } if (TokenMatch(filePtr,"LIGHT_FALLOFF" ,13)) { ParseLV4MeshFloat(light.mFalloff); continue; } } AI_ASE_HANDLE_SECTION("2","LIGHT_SETTINGS"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh) { AI_ASE_PARSER_INIT(); ASE::Animation* anim = &mesh.mAnim; while (true) { if ('*' == *filePtr) { ++filePtr; if (TokenMatch(filePtr,"NODE_NAME" ,9)) { std::string temp; if (!ParseString(temp,"*NODE_NAME")) SkipToNextToken(); // If the name of the node contains .target it // represents an animated camera or spot light // target. if (std::string::npos != temp.find(".Target")) { if ((mesh.mType != BaseNode::Camera || ((ASE::Camera&)mesh).mCameraType != ASE::Camera::TARGET) && ( mesh.mType != BaseNode::Light || ((ASE::Light&)mesh).mLightType != ASE::Light::TARGET)) { DefaultLogger::get()->error("ASE: Found target animation channel " "but the node is neither a camera nor a spot light"); anim = NULL; } else anim = &mesh.mTargetAnim; } continue; } // position keyframes if (TokenMatch(filePtr,"CONTROL_POS_TRACK" ,17) || TokenMatch(filePtr,"CONTROL_POS_BEZIER" ,18) || TokenMatch(filePtr,"CONTROL_POS_TCB" ,15)) { if (!anim)SkipSection(); else ParseLV3PosAnimationBlock(*anim); continue; } // scaling keyframes if (TokenMatch(filePtr,"CONTROL_SCALE_TRACK" ,19) || TokenMatch(filePtr,"CONTROL_SCALE_BEZIER" ,20) || TokenMatch(filePtr,"CONTROL_SCALE_TCB" ,17)) { if (!anim || anim == &mesh.mTargetAnim) { // Target animation channels may have no rotation channels DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation"); SkipSection(); } else ParseLV3ScaleAnimationBlock(*anim); continue; } // rotation keyframes if (TokenMatch(filePtr,"CONTROL_ROT_TRACK" ,17) || TokenMatch(filePtr,"CONTROL_ROT_BEZIER" ,18) || TokenMatch(filePtr,"CONTROL_ROT_TCB" ,15)) { if (!anim || anim == &mesh.mTargetAnim) { // Target animation channels may have no rotation channels DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation"); SkipSection(); } else ParseLV3RotAnimationBlock(*anim); continue; } } AI_ASE_HANDLE_SECTION("2","TM_ANIMATION"); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation& anim) { AI_ASE_PARSER_INIT(); unsigned int iIndex; while (true) { if ('*' == *filePtr) { ++filePtr; bool b = false; // For the moment we're just reading the three floats - // we ignore the dditional information for bezier's and TCBs // simple scaling keyframe if (TokenMatch(filePtr,"CONTROL_SCALE_SAMPLE" ,20)) { b = true; anim.mScalingType = ASE::Animation::TRACK; } // Bezier scaling keyframe if (TokenMatch(filePtr,"CONTROL_BEZIER_SCALE_KEY" ,24)) { b = true; anim.mScalingType = ASE::Animation::BEZIER; } // TCB scaling keyframe if (TokenMatch(filePtr,"CONTROL_TCB_SCALE_KEY" ,21)) { b = true; anim.mScalingType = ASE::Animation::TCB; } if (b) { anim.akeyScaling.push_back(aiVectorKey()); aiVectorKey& key = anim.akeyScaling.back(); ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); key.mTime = (double)iIndex; } } AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3PosAnimationBlock(ASE::Animation& anim) { AI_ASE_PARSER_INIT(); unsigned int iIndex; while (true) { if ('*' == *filePtr) { ++filePtr; bool b = false; // For the moment we're just reading the three floats - // we ignore the dditional information for bezier's and TCBs // simple scaling keyframe if (TokenMatch(filePtr,"CONTROL_POS_SAMPLE" ,18)) { b = true; anim.mPositionType = ASE::Animation::TRACK; } // Bezier scaling keyframe if (TokenMatch(filePtr,"CONTROL_BEZIER_POS_KEY" ,22)) { b = true; anim.mPositionType = ASE::Animation::BEZIER; } // TCB scaling keyframe if (TokenMatch(filePtr,"CONTROL_TCB_POS_KEY" ,19)) { b = true; anim.mPositionType = ASE::Animation::TCB; } if (b) { anim.akeyPositions.push_back(aiVectorKey()); aiVectorKey& key = anim.akeyPositions.back(); ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); key.mTime = (double)iIndex; } } AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3RotAnimationBlock(ASE::Animation& anim) { AI_ASE_PARSER_INIT(); unsigned int iIndex; while (true) { if ('*' == *filePtr) { ++filePtr; bool b = false; // For the moment we're just reading the floats - // we ignore the dditional information for bezier's and TCBs // simple scaling keyframe if (TokenMatch(filePtr,"CONTROL_ROT_SAMPLE" ,18)) { b = true; anim.mRotationType = ASE::Animation::TRACK; } // Bezier scaling keyframe if (TokenMatch(filePtr,"CONTROL_BEZIER_ROT_KEY" ,22)) { b = true; anim.mRotationType = ASE::Animation::BEZIER; } // TCB scaling keyframe if (TokenMatch(filePtr,"CONTROL_TCB_ROT_KEY" ,19)) { b = true; anim.mRotationType = ASE::Animation::TCB; } if (b) { anim.akeyRotations.push_back(aiQuatKey()); aiQuatKey& key = anim.akeyRotations.back(); aiVector3D v;float f; ParseLV4MeshFloatTriple(&v.x,iIndex); ParseLV4MeshFloat(f); key.mTime = (double)iIndex; key.mValue = aiQuaternion(v,f); } } AI_ASE_HANDLE_SECTION("3","*CONTROL_ROT_TRACK"); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh) { AI_ASE_PARSER_INIT(); int mode = 0; while (true) { if ('*' == *filePtr) { ++filePtr; // name of the node if (TokenMatch(filePtr,"NODE_NAME" ,9)) { std::string temp; if (!ParseString(temp,"*NODE_NAME")) SkipToNextToken(); std::string::size_type s; if (temp == mesh.mName) { mode = 1; } else if (std::string::npos != (s = temp.find(".Target")) && mesh.mName == temp.substr(0,s)) { // This should be either a target light or a target camera if ( (mesh.mType == BaseNode::Light && ((ASE::Light&)mesh) .mLightType == ASE::Light::TARGET) || (mesh.mType == BaseNode::Camera && ((ASE::Camera&)mesh).mCameraType == ASE::Camera::TARGET)) { mode = 2; } else DefaultLogger::get()->error("ASE: Ignoring target transform, " "this is no spot light or target camera"); } else { DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp); // mode = 0 } continue; } if (mode) { // fourth row of the transformation matrix - and also the // only information here that is interesting for targets if (TokenMatch(filePtr,"TM_ROW3" ,7)) { ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); continue; } if (mode == 1) { // first row of the transformation matrix if (TokenMatch(filePtr,"TM_ROW0" ,7)) { ParseLV4MeshFloatTriple(mesh.mTransform[0]); continue; } // second row of the transformation matrix if (TokenMatch(filePtr,"TM_ROW1" ,7)) { ParseLV4MeshFloatTriple(mesh.mTransform[1]); continue; } // third row of the transformation matrix if (TokenMatch(filePtr,"TM_ROW2" ,7)) { ParseLV4MeshFloatTriple(mesh.mTransform[2]); continue; } // inherited position axes if (TokenMatch(filePtr,"INHERIT_POS" ,11)) { unsigned int aiVal[3]; ParseLV4MeshLongTriple(aiVal); for (unsigned int i = 0; i < 3;++i) mesh.inherit.abInheritPosition[i] = aiVal[i] != 0; continue; } // inherited rotation axes if (TokenMatch(filePtr,"INHERIT_ROT" ,11)) { unsigned int aiVal[3]; ParseLV4MeshLongTriple(aiVal); for (unsigned int i = 0; i < 3;++i) mesh.inherit.abInheritRotation[i] = aiVal[i] != 0; continue; } // inherited scaling axes if (TokenMatch(filePtr,"INHERIT_SCL" ,11)) { unsigned int aiVal[3]; ParseLV4MeshLongTriple(aiVal); for (unsigned int i = 0; i < 3;++i) mesh.inherit.abInheritScaling[i] = aiVal[i] != 0; continue; } } } } AI_ASE_HANDLE_SECTION("2","*NODE_TM"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); unsigned int iNumVertices = 0; unsigned int iNumFaces = 0; unsigned int iNumTVertices = 0; unsigned int iNumTFaces = 0; unsigned int iNumCVertices = 0; unsigned int iNumCFaces = 0; while (true) { if ('*' == *filePtr) { ++filePtr; // Number of vertices in the mesh if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) { ParseLV4MeshLong(iNumVertices); continue; } // Number of texture coordinates in the mesh if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) { ParseLV4MeshLong(iNumTVertices); continue; } // Number of vertex colors in the mesh if (TokenMatch(filePtr,"MESH_NUMCVERTEX" ,15)) { ParseLV4MeshLong(iNumCVertices); continue; } // Number of regular faces in the mesh if (TokenMatch(filePtr,"MESH_NUMFACES" ,13)) { ParseLV4MeshLong(iNumFaces); continue; } // Number of UVWed faces in the mesh if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) { ParseLV4MeshLong(iNumTFaces); continue; } // Number of colored faces in the mesh if (TokenMatch(filePtr,"MESH_NUMCVFACES" ,15)) { ParseLV4MeshLong(iNumCFaces); continue; } // mesh vertex list block if (TokenMatch(filePtr,"MESH_VERTEX_LIST" ,16)) { ParseLV3MeshVertexListBlock(iNumVertices,mesh); continue; } // mesh face list block if (TokenMatch(filePtr,"MESH_FACE_LIST" ,14)) { ParseLV3MeshFaceListBlock(iNumFaces,mesh); continue; } // mesh texture vertex list block if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) { ParseLV3MeshTListBlock(iNumTVertices,mesh); continue; } // mesh texture face block if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) { ParseLV3MeshTFaceListBlock(iNumTFaces,mesh); continue; } // mesh color vertex list block if (TokenMatch(filePtr,"MESH_CVERTLIST" ,14)) { ParseLV3MeshCListBlock(iNumCVertices,mesh); continue; } // mesh color face block if (TokenMatch(filePtr,"MESH_CFACELIST" ,14)) { ParseLV3MeshCFaceListBlock(iNumCFaces,mesh); continue; } // mesh normals if (TokenMatch(filePtr,"MESH_NORMALS" ,12)) { ParseLV3MeshNormalListBlock(mesh); continue; } // another mesh UV channel ... if (TokenMatch(filePtr,"MESH_MAPPINGCHANNEL" ,19)) { unsigned int iIndex = 0; ParseLV4MeshLong(iIndex); if (iIndex < 2) { LogWarning("Mapping channel has an invalid index. Skipping UV channel"); // skip it ... SkipSection(); } if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) { LogWarning("Too many UV channels specified. Skipping channel .."); // skip it ... SkipSection(); } else { // parse the mapping channel ParseLV3MappingChannel(iIndex-1,mesh); } continue; } // mesh animation keyframe. Not supported if (TokenMatch(filePtr,"MESH_ANIMATION" ,14)) { LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " "Keyframe animation is not supported by Assimp, this element " "will be ignored"); //SkipSection(); continue; } if (TokenMatch(filePtr,"MESH_WEIGHTS" ,12)) { ParseLV3MeshWeightsBlock(mesh);continue; } } AI_ASE_HANDLE_SECTION("2","*MESH"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); unsigned int iNumVertices = 0, iNumBones = 0; while (true) { if ('*' == *filePtr) { ++filePtr; // Number of bone vertices ... if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) { ParseLV4MeshLong(iNumVertices); continue; } // Number of bones if (TokenMatch(filePtr,"MESH_NUMBONE" ,11)) { ParseLV4MeshLong(iNumBones); continue; } // parse the list of bones if (TokenMatch(filePtr,"MESH_BONE_LIST" ,14)) { ParseLV4MeshBones(iNumBones,mesh); continue; } // parse the list of bones vertices if (TokenMatch(filePtr,"MESH_BONE_VERTEX_LIST" ,21) ) { ParseLV4MeshBonesVertices(iNumVertices,mesh); continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_WEIGHTS"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); mesh.mBones.resize(iNumBones); while (true) { if ('*' == *filePtr) { ++filePtr; // Mesh bone with name ... if (TokenMatch(filePtr,"MESH_BONE_NAME" ,16)) { // parse an index ... if (SkipSpaces(&filePtr)) { unsigned int iIndex = strtol10(filePtr,&filePtr); if (iIndex >= iNumBones) { continue; LogWarning("Bone index is out of bounds"); } if (!ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME")) SkipToNextToken(); continue; } } } AI_ASE_HANDLE_SECTION("3","*MESH_BONE_LIST"); } } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); mesh.mBoneVertices.resize(iNumVertices); while (true) { if ('*' == *filePtr) { ++filePtr; // Mesh bone vertex if (TokenMatch(filePtr,"MESH_BONE_VERTEX" ,16)) { // read the vertex index unsigned int iIndex = strtol10(filePtr,&filePtr); if (iIndex >= mesh.mPositions.size()) { iIndex = (unsigned int)mesh.mPositions.size()-1; LogWarning("Bone vertex index is out of bounds. Using the largest valid " "bone vertex index instead"); } // --- ignored float afVert[3]; ParseLV4MeshFloatTriple(afVert); std::pair pairOut; while (true) { // first parse the bone index ... if (!SkipSpaces(&filePtr))break; pairOut.first = strtol10(filePtr,&filePtr); // then parse the vertex weight if (!SkipSpaces(&filePtr))break; filePtr = fast_atof_move(filePtr,pairOut.second); // -1 marks unused entries if (-1 != pairOut.first) { mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); } } continue; } } AI_ASE_HANDLE_SECTION("4","*MESH_BONE_VERTEX"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshVertexListBlock( unsigned int iNumVertices, ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); // allocate enough storage in the array mesh.mPositions.resize(iNumVertices); while (true) { if ('*' == *filePtr) { ++filePtr; // Vertex entry if (TokenMatch(filePtr,"MESH_VERTEX" ,11)) { aiVector3D vTemp; unsigned int iIndex; ParseLV4MeshFloatTriple(&vTemp.x,iIndex); if (iIndex >= iNumVertices) { LogWarning("Invalid vertex index. It will be ignored"); } else mesh.mPositions[iIndex] = vTemp; continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_VERTEX_LIST"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); // allocate enough storage in the face array mesh.mFaces.resize(iNumFaces); while (true) { if ('*' == *filePtr) { ++filePtr; // Face entry if (TokenMatch(filePtr,"MESH_FACE" ,9)) { ASE::Face mFace; ParseLV4MeshFace(mFace); if (mFace.iFace >= iNumFaces) { LogWarning("Face has an invalid index. It will be ignored"); } else mesh.mFaces[mFace.iFace] = mFace; continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_FACE_LIST"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, ASE::Mesh& mesh, unsigned int iChannel) { AI_ASE_PARSER_INIT(); // allocate enough storage in the array mesh.amTexCoords[iChannel].resize(iNumVertices); while (true) { if ('*' == *filePtr) { ++filePtr; // Vertex entry if (TokenMatch(filePtr,"MESH_TVERT" ,10)) { aiVector3D vTemp; unsigned int iIndex; ParseLV4MeshFloatTriple(&vTemp.x,iIndex); if (iIndex >= iNumVertices) { LogWarning("Tvertex has an invalid index. It will be ignored"); } else mesh.amTexCoords[iChannel][iIndex] = vTemp; if (0.0f != vTemp.z) { // we need 3 coordinate channels mesh.mNumUVComponents[iChannel] = 3; } continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_TVERT_LIST"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh, unsigned int iChannel) { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; // Face entry if (TokenMatch(filePtr,"MESH_TFACE" ,10)) { unsigned int aiValues[3]; unsigned int iIndex = 0; ParseLV4MeshLongTriple(aiValues,iIndex); if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) { LogWarning("UV-Face has an invalid index. It will be ignored"); } else { // copy UV indices mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0]; mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1]; mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2]; } continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_TFACE_LIST"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); unsigned int iNumTVertices = 0; unsigned int iNumTFaces = 0; while (true) { if ('*' == *filePtr) { ++filePtr; // Number of texture coordinates in the mesh if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) { ParseLV4MeshLong(iNumTVertices); continue; } // Number of UVWed faces in the mesh if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) { ParseLV4MeshLong(iNumTFaces); continue; } // mesh texture vertex list block if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) { ParseLV3MeshTListBlock(iNumTVertices,mesh,iChannel); continue; } // mesh texture face block if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) { ParseLV3MeshTFaceListBlock(iNumTFaces,mesh, iChannel); continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_MAPPING_CHANNEL"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); // allocate enough storage in the array mesh.mVertexColors.resize(iNumVertices); while (true) { if ('*' == *filePtr) { ++filePtr; // Vertex entry if (TokenMatch(filePtr,"MESH_VERTCOL" ,12)) { aiColor4D vTemp; vTemp.a = 1.0f; unsigned int iIndex; ParseLV4MeshFloatTriple(&vTemp.r,iIndex); if (iIndex >= iNumVertices) { LogWarning("Vertex color has an invalid index. It will be ignored"); } else mesh.mVertexColors[iIndex] = vTemp; continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_CVERTEX_LIST"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) { AI_ASE_PARSER_INIT(); while (true) { if ('*' == *filePtr) { ++filePtr; // Face entry if (TokenMatch(filePtr,"MESH_CFACE" ,11)) { unsigned int aiValues[3]; unsigned int iIndex = 0; ParseLV4MeshLongTriple(aiValues,iIndex); if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) { LogWarning("UV-Face has an invalid index. It will be ignored"); } else { // copy color indices mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0]; mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1]; mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2]; } continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_CFACE_LIST"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh) { AI_ASE_PARSER_INIT(); // Allocate enough storage for the normals sMesh.mNormals.resize(sMesh.mFaces.size()*3,aiVector3D( 0.f, 0.f, 0.f )); unsigned int index, faceIdx = 0xffffffff; // FIXME: rewrite this and find out how to interpret the normals // correctly. // Smooth the vertex and face normals together. The result // will be edgy then, but otherwise everything would be soft ... while (true) { if ('*' == *filePtr) { ++filePtr; if (faceIdx != 0xffffffff && TokenMatch(filePtr,"MESH_VERTEXNORMAL",17)) { aiVector3D vNormal; ParseLV4MeshFloatTriple(&vNormal.x,index); if (faceIdx >= sMesh.mFaces.size()) continue; // Make sure we assign it to the correct face const ASE::Face& face = sMesh.mFaces[faceIdx]; if (index == face.mIndices[0]) index = 0; else if (index == face.mIndices[1]) index = 1; else if (index == face.mIndices[2]) index = 2; else { DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section"); continue; } // We'll renormalize later sMesh.mNormals[faceIdx*3+index] += vNormal; continue; } if (TokenMatch(filePtr,"MESH_FACENORMAL",15)) { aiVector3D vNormal; ParseLV4MeshFloatTriple(&vNormal.x,faceIdx); if (faceIdx >= sMesh.mFaces.size()) { DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section"); continue; } // We'll renormalize later sMesh.mNormals[faceIdx*3] += vNormal; sMesh.mNormals[faceIdx*3+1] += vNormal; sMesh.mNormals[faceIdx*3+2] += vNormal; continue; } } AI_ASE_HANDLE_SECTION("3","*MESH_NORMALS"); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFace(ASE::Face& out) { // skip spaces and tabs if (!SkipSpaces(&filePtr)) { LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); SkipToNextToken(); return; } // parse the face index out.iFace = strtol10(filePtr,&filePtr); // next character should be ':' if (!SkipSpaces(&filePtr)) { // FIX: there are some ASE files which haven't got : here .... LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); SkipToNextToken(); return; } // FIX: There are some ASE files which haven't got ':' here if (':' == *filePtr)++filePtr; // Parse all mesh indices for (unsigned int i = 0; i < 3;++i) { unsigned int iIndex = 0; if (!SkipSpaces(&filePtr)) { LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); SkipToNextToken(); return; } switch (*filePtr) { case 'A': case 'a': break; case 'B': case 'b': iIndex = 1; break; case 'C': case 'c': iIndex = 2; break; default: LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " "A,B or C expected [#3]"); SkipToNextToken(); return; }; ++filePtr; // next character should be ':' if (!SkipSpaces(&filePtr) || ':' != *filePtr) { LogWarning("Unable to parse *MESH_FACE Element: " "Unexpected EOL. \':\' expected [#2]"); SkipToNextToken(); return; } ++filePtr; if (!SkipSpaces(&filePtr)) { LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " "Vertex index ecpected [#4]"); SkipToNextToken(); return; } out.mIndices[iIndex] = strtol10(filePtr,&filePtr); } // now we need to skip the AB, BC, CA blocks. while (true) { if ('*' == *filePtr)break; if (IsLineEnd(*filePtr)) { //iLineNumber++; return; } filePtr++; } // parse the smoothing group of the face if (TokenMatch(filePtr,"*MESH_SMOOTHING",15)) { if (!SkipSpaces(&filePtr)) { LogWarning("Unable to parse *MESH_SMOOTHING Element: " "Unexpected EOL. Smoothing group(s) expected [#5]"); SkipToNextToken(); return; } // Parse smoothing groups until we don't anymore see commas // FIX: There needn't always be a value, sad but true while (true) { if (*filePtr < '9' && *filePtr >= '0') { out.iSmoothGroup |= (1 << strtol10(filePtr,&filePtr)); } SkipSpaces(&filePtr); if (',' != *filePtr) { break; } ++filePtr; SkipSpaces(&filePtr); } } // *MESH_MTLID is optional, too while (true) { if ('*' == *filePtr)break; if (IsLineEnd(*filePtr)) { return; } filePtr++; } if (TokenMatch(filePtr,"*MESH_MTLID",11)) { if (!SkipSpaces(&filePtr)) { LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " "Material index expected [#6]"); SkipToNextToken(); return; } out.iMaterial = strtol10(filePtr,&filePtr); } return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut) { ai_assert(NULL != apOut); for (unsigned int i = 0; i < 3;++i) ParseLV4MeshLong(apOut[i]); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) { ai_assert(NULL != apOut); // parse the index ParseLV4MeshLong(rIndexOut); // parse the three others ParseLV4MeshLongTriple(apOut); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) { ai_assert(NULL != apOut); // parse the index ParseLV4MeshLong(rIndexOut); // parse the three others ParseLV4MeshFloatTriple(apOut); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFloatTriple(float* apOut) { ai_assert(NULL != apOut); for (unsigned int i = 0; i < 3;++i) ParseLV4MeshFloat(apOut[i]); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFloat(float& fOut) { // skip spaces and tabs if (!SkipSpaces(&filePtr)) { // LOG LogWarning("Unable to parse float: unexpected EOL [#1]"); fOut = 0.0f; ++iLineNumber; return; } // parse the first float filePtr = fast_atof_move(filePtr,fOut); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLong(unsigned int& iOut) { // Skip spaces and tabs if (!SkipSpaces(&filePtr)) { // LOG LogWarning("Unable to parse long: unexpected EOL [#1]"); iOut = 0; ++iLineNumber; return; } // parse the value iOut = strtol10(filePtr,&filePtr); } 3rdparty/assimp/code/ASEParser.h000066400000000000000000000535621217616171500170140ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file Defines the helper data structures for importing ASE files */ #ifndef AI_ASEFILEHELPER_H_INC #define AI_ASEFILEHELPER_H_INC // STL/CRT headers #include #include #include // public ASSIMP headers #include "../include/aiTypes.h" #include "../include/aiMesh.h" #include "../include/aiAnim.h" // for some helper routines like IsSpace() #include "ParsingUtils.h" #include "qnan.h" // ASE is quite similar to 3ds. We can reuse some structures #include "3DSLoader.h" namespace Assimp { namespace ASE { using namespace D3DS; // --------------------------------------------------------------------------- /** Helper structure representing an ASE material */ struct Material : public D3DS::Material { //! Default constructor Material() : pcInstance(NULL), bNeed (false) {} //! Contains all sub materials of this material std::vector avSubMaterials; //! MaterialHelper object MaterialHelper* pcInstance; //! Can we remove this material? bool bNeed; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file face */ struct Face : public FaceWithSmoothingGroup { //! Default constructor. Initializes everything with 0 Face() { mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0; for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0; } iMaterial = DEFAULT_MATINDEX; iFace = 0; } //! special value to indicate that no material index has //! been assigned to a face. The default material index //! will replace this value later. static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF; //! Indices into each list of texture coordinates unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3]; //! Index into the list of vertex colors unsigned int mColorIndices[3]; //! (Sub)Material index to be assigned to this face unsigned int iMaterial; //! Index of the face. It is not specified whether it is //! a requirement of the file format that all faces are //! written in sequential order, so we have to expect this case unsigned int iFace; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file bone */ struct Bone { //! Constructor Bone() { static int iCnt = 0; // Generate a default name for the bone char szTemp[128]; ::sprintf(szTemp,"UNNAMED_%i",iCnt++); mName = szTemp; } //! Construction from an existing name Bone( const std::string& name) : mName (name) {} //! Name of the bone std::string mName; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file bone vertex */ struct BoneVertex { //! Bone and corresponding vertex weight. //! -1 for unrequired bones .... std::vector > mBoneWeights; //! Position of the bone vertex. //! MUST be identical to the vertex position //aiVector3D mPosition; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file animation */ struct Animation { enum Type { TRACK = 0x0, BEZIER = 0x1, TCB = 0x2 } mRotationType, mScalingType, mPositionType; Animation() : mRotationType (TRACK) , mScalingType (TRACK) , mPositionType (TRACK) {} //! List of track rotation keyframes std::vector< aiQuatKey > akeyRotations; //! List of track position keyframes std::vector< aiVectorKey > akeyPositions; //! List of track scaling keyframes std::vector< aiVectorKey > akeyScaling; }; // --------------------------------------------------------------------------- /** Helper structure to represent the inheritance information of an ASE node */ struct InheritanceInfo { //! Default constructor InheritanceInfo() { // set the inheritance flag for all axes by default to true for (unsigned int i = 0; i < 3;++i) abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true; } //! Inherit the parent's position?, axis order is x,y,z bool abInheritPosition[3]; //! Inherit the parent's rotation?, axis order is x,y,z bool abInheritRotation[3]; //! Inherit the parent's scaling?, axis order is x,y,z bool abInheritScaling[3]; }; // --------------------------------------------------------------------------- /** Represents an ASE file node. Base class for mesh, light and cameras */ struct BaseNode { enum Type {Light, Camera, Mesh, Dummy} mType; //! Constructor. Creates a default name for the node BaseNode(Type _mType) : mType (_mType) , mProcessed (false) { // generate a default name for the node static int iCnt = 0; char szTemp[128]; // should be sufficiently large ::sprintf(szTemp,"UNNAMED_%i",iCnt++); mName = szTemp; // Set mTargetPosition to qnan const float qnan = get_qnan(); mTargetPosition.x = qnan; } //! Name of the mesh std::string mName; //! Name of the parent of the node //! "" if there is no parent ... std::string mParent; //! Transformation matrix of the node aiMatrix4x4 mTransform; //! Target position (target lights and cameras) aiVector3D mTargetPosition; //! Specifies which axes transformations a node inherits //! from its parent ... InheritanceInfo inherit; //! Animation channels for the node Animation mAnim; //! Needed for lights and cameras: target animation channel //! Should contain position keys only. Animation mTargetAnim; bool mProcessed; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file mesh */ struct Mesh : public MeshWithSmoothingGroups, public BaseNode { //! Constructor. Mesh() : BaseNode (BaseNode::Mesh) , bSkip (false) { // use 2 texture vertex components by default for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) this->mNumUVComponents[c] = 2; // setup the default material index by default iMaterialIndex = Face::DEFAULT_MATINDEX; } //! List of all texture coordinate sets std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; //! List of all vertex color sets. std::vector mVertexColors; //! List of all bone vertices std::vector mBoneVertices; //! List of all bones std::vector mBones; //! Material index of the mesh unsigned int iMaterialIndex; //! Number of vertex components for each UVW set unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; //! used internally bool bSkip; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE light source */ struct Light : public BaseNode { enum LightType { OMNI, TARGET, FREE, DIRECTIONAL }; //! Constructor. Light() : BaseNode (BaseNode::Light) , mLightType (OMNI) , mColor (1.f,1.f,1.f) , mIntensity (1.f) // light is white by default , mAngle (45.f) , mFalloff (0.f) { } LightType mLightType; aiColor3D mColor; float mIntensity; float mAngle; // in degrees float mFalloff; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE camera */ struct Camera : public BaseNode { enum CameraType { FREE, TARGET }; //! Constructor Camera() : BaseNode (BaseNode::Camera) , mFOV (0.75f) // in radians , mNear (0.1f) , mFar (1000.f) // could be zero , mCameraType (FREE) { } float mFOV, mNear, mFar; CameraType mCameraType; }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE helper object (dummy) */ struct Dummy : public BaseNode { //! Constructor Dummy() : BaseNode (BaseNode::Dummy) { } }; // Parameters to Parser::Parse() #define AI_ASE_NEW_FILE_FORMAT 200 #define AI_ASE_OLD_FILE_FORMAT 110 // Internally we're a little bit more tolerant #define AI_ASE_IS_NEW_FILE_FORMAT() (iFileFormat >= 200) #define AI_ASE_IS_OLD_FILE_FORMAT() (iFileFormat < 200) // ------------------------------------------------------------------------------- /** \brief Class to parse ASE files */ class Parser { private: Parser() {} public: // ------------------------------------------------------------------- //! Construct a parser from a given input file which is //! guaranted to be terminated with zero. //! @param szFile Input file //! @param fileFormatDefault Assumed file format version. If the //! file format is specified in the file the new value replaces //! the default value. Parser (const char* szFile, unsigned int fileFormatDefault); // ------------------------------------------------------------------- //! Parses the file into the parsers internal representation void Parse(); private: // ------------------------------------------------------------------- //! Parse the *SCENE block in a file void ParseLV1SceneBlock(); // ------------------------------------------------------------------- //! Parse the *MESH_SOFTSKINVERTS block in a file void ParseLV1SoftSkinBlock(); // ------------------------------------------------------------------- //! Parse the *MATERIAL_LIST block in a file void ParseLV1MaterialListBlock(); // ------------------------------------------------------------------- //! Parse a *OBJECT block in a file //! \param mesh Node to be filled void ParseLV1ObjectBlock(BaseNode& mesh); // ------------------------------------------------------------------- //! Parse a *MATERIAL blocks in a material list //! \param mat Material structure to be filled void ParseLV2MaterialBlock(Material& mat); // ------------------------------------------------------------------- //! Parse a *NODE_TM block in a file //! \param mesh Node (!) object to be filled void ParseLV2NodeTransformBlock(BaseNode& mesh); // ------------------------------------------------------------------- //! Parse a *TM_ANIMATION block in a file //! \param mesh Mesh object to be filled void ParseLV2AnimationBlock(BaseNode& mesh); void ParseLV3PosAnimationBlock(ASE::Animation& anim); void ParseLV3ScaleAnimationBlock(ASE::Animation& anim); void ParseLV3RotAnimationBlock(ASE::Animation& anim); // ------------------------------------------------------------------- //! Parse a *MESH block in a file //! \param mesh Mesh object to be filled void ParseLV2MeshBlock(Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *LIGHT_SETTINGS block in a file //! \param light Light object to be filled void ParseLV2LightSettingsBlock(Light& light); // ------------------------------------------------------------------- //! Parse a *CAMERA_SETTINGS block in a file //! \param cam Camera object to be filled void ParseLV2CameraSettingsBlock(Camera& cam); // ------------------------------------------------------------------- //! Parse the *MAP_XXXXXX blocks in a material //! \param map Texture structure to be filled void ParseLV3MapBlock(Texture& map); // ------------------------------------------------------------------- //! Parse a *MESH_VERTEX_LIST block in a file //! \param iNumVertices Value of *MESH_NUMVERTEX, if present. //! Otherwise zero. This is used to check the consistency of the file. //! A warning is sent to the logger if the validations fails. //! \param mesh Mesh object to be filled void ParseLV3MeshVertexListBlock( unsigned int iNumVertices,Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_FACE_LIST block in a file //! \param iNumFaces Value of *MESH_NUMFACES, if present. //! Otherwise zero. This is used to check the consistency of the file. //! A warning is sent to the logger if the validations fails. //! \param mesh Mesh object to be filled void ParseLV3MeshFaceListBlock( unsigned int iNumFaces,Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_TVERT_LIST block in a file //! \param iNumVertices Value of *MESH_NUMTVERTEX, if present. //! Otherwise zero. This is used to check the consistency of the file. //! A warning is sent to the logger if the validations fails. //! \param mesh Mesh object to be filled //! \param iChannel Output UVW channel void ParseLV3MeshTListBlock( unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0); // ------------------------------------------------------------------- //! Parse a *MESH_TFACELIST block in a file //! \param iNumFaces Value of *MESH_NUMTVFACES, if present. //! Otherwise zero. This is used to check the consistency of the file. //! A warning is sent to the logger if the validations fails. //! \param mesh Mesh object to be filled //! \param iChannel Output UVW channel void ParseLV3MeshTFaceListBlock( unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0); // ------------------------------------------------------------------- //! Parse an additional mapping channel //! (specified via *MESH_MAPPINGCHANNEL) //! \param iChannel Channel index to be filled //! \param mesh Mesh object to be filled void ParseLV3MappingChannel( unsigned int iChannel, Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_CVERTLIST block in a file //! \param iNumVertices Value of *MESH_NUMCVERTEX, if present. //! Otherwise zero. This is used to check the consistency of the file. //! A warning is sent to the logger if the validations fails. //! \param mesh Mesh object to be filled void ParseLV3MeshCListBlock( unsigned int iNumVertices, Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_CFACELIST block in a file //! \param iNumFaces Value of *MESH_NUMCVFACES, if present. //! Otherwise zero. This is used to check the consistency of the file. //! A warning is sent to the logger if the validations fails. //! \param mesh Mesh object to be filled void ParseLV3MeshCFaceListBlock( unsigned int iNumFaces, Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_NORMALS block in a file //! \param mesh Mesh object to be filled void ParseLV3MeshNormalListBlock(Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_WEIGHTSblock in a file //! \param mesh Mesh object to be filled void ParseLV3MeshWeightsBlock(Mesh& mesh); // ------------------------------------------------------------------- //! Parse the bone list of a file //! \param mesh Mesh object to be filled //! \param iNumBones Number of bones in the mesh void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh); // ------------------------------------------------------------------- //! Parse the bone vertices list of a file //! \param mesh Mesh object to be filled //! \param iNumVertices Number of vertices to be parsed void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh); // ------------------------------------------------------------------- //! Parse a *MESH_FACE block in a file //! \param out receive the face data void ParseLV4MeshFace(ASE::Face& out); // ------------------------------------------------------------------- //! Parse a *MESH_VERT block in a file //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) //! \param apOut Output buffer (3 floats) //! \param rIndexOut Output index void ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut); // ------------------------------------------------------------------- //! Parse a *MESH_VERT block in a file //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) //! \param apOut Output buffer (3 floats) void ParseLV4MeshFloatTriple(float* apOut); // ------------------------------------------------------------------- //! Parse a *MESH_TFACE block in a file //! (also works for MESH_CFACE) //! \param apOut Output buffer (3 ints) //! \param rIndexOut Output index void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut); // ------------------------------------------------------------------- //! Parse a *MESH_TFACE block in a file //! (also works for MESH_CFACE) //! \param apOut Output buffer (3 ints) void ParseLV4MeshLongTriple(unsigned int* apOut); // ------------------------------------------------------------------- //! Parse a single float element //! \param fOut Output float void ParseLV4MeshFloat(float& fOut); // ------------------------------------------------------------------- //! Parse a single int element //! \param iOut Output integer void ParseLV4MeshLong(unsigned int& iOut); // ------------------------------------------------------------------- //! Skip everything to the next: '*' or '\0' bool SkipToNextToken(); // ------------------------------------------------------------------- //! Skip the current section until the token after the closing }. //! This function handles embedded subsections correctly bool SkipSection(); // ------------------------------------------------------------------- //! Output a warning to the logger //! \param szWarn Warn message void LogWarning(const char* szWarn); // ------------------------------------------------------------------- //! Output a message to the logger //! \param szWarn Message void LogInfo(const char* szWarn); // ------------------------------------------------------------------- //! Output an error to the logger //! \param szWarn Error message void LogError(const char* szWarn); // ------------------------------------------------------------------- //! Parse a string, enclosed in double quotation marks //! \param out Output string //! \param szName Name of the enclosing element -> used in error //! messages. //! \return false if an error occured bool ParseString(std::string& out,const char* szName); public: //! Pointer to current data const char* filePtr; //! background color to be passed to the viewer //! QNAN if none was found aiColor3D m_clrBackground; //! Base ambient color to be passed to all materials //! QNAN if none was found aiColor3D m_clrAmbient; //! List of all materials found in the file std::vector m_vMaterials; //! List of all meshes found in the file std::vector m_vMeshes; //! List of all dummies found in the file std::vector m_vDummies; //! List of all lights found in the file std::vector m_vLights; //! List of all cameras found in the file std::vector m_vCameras; //! Current line in the file unsigned int iLineNumber; //! First frame unsigned int iFirstFrame; //! Last frame unsigned int iLastFrame; //! Frame speed - frames per second unsigned int iFrameSpeed; //! Ticks per frame unsigned int iTicksPerFrame; //! true if the last character read was an end-line character bool bLastWasEndLine; //! File format version unsigned int iFileFormat; }; } // Namespace ASE } // Namespace ASSIMP #endif // !! include guard 3rdparty/assimp/code/Assimp.cpp000066400000000000000000000551511217616171500170120ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file Assimp.cpp * @brief Implementation of the Plain-C API */ #include "AssimpPCH.h" #include "../include/assimp.h" #include "../include/aiFileIO.h" #include "GenericProperty.h" // ------------------------------------------------------------------------------------------------ #ifdef AI_C_THREADSAFE # include # include #endif // ------------------------------------------------------------------------------------------------ using namespace Assimp; namespace Assimp { /** Stores the importer objects for all active import processes */ typedef std::map ImporterMap; /** Stores the LogStream objects for all active C log streams */ struct mpred { bool operator () (const aiLogStream& s0, const aiLogStream& s1) const { return s0.callback LogStreamMap; /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ typedef std::list PredefLogStreamMap; /** Local storage of all active import processes */ static ImporterMap gActiveImports; /** Local storage of all active log streams */ static LogStreamMap gActiveLogStreams; /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */ static PredefLogStreamMap gPredefinedStreams; /** Error message of the last failed import process */ static std::string gLastErrorString; /** Verbose logging active or not? */ static aiBool gVerboseLogging = false; } /** Configuration properties */ static ImporterPimpl::IntPropertyMap gIntProperties; static ImporterPimpl::FloatPropertyMap gFloatProperties; static ImporterPimpl::StringPropertyMap gStringProperties; #ifdef AI_C_THREADSAFE /** Global mutex to manage the access to the importer map */ static boost::mutex gMutex; /** Global mutex to manage the access to the logstream map */ static boost::mutex gLogStreamMutex; #endif class CIOSystemWrapper; class CIOStreamWrapper; // ------------------------------------------------------------------------------------------------ // Custom IOStream implementation for the C-API class CIOStreamWrapper : public IOStream { friend class CIOSystemWrapper; public: CIOStreamWrapper(aiFile* pFile) : mFile(pFile) {} // ................................................................... size_t Read(void* pvBuffer, size_t pSize, size_t pCount ){ // need to typecast here as C has no void* return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); } // ................................................................... size_t Write(const void* pvBuffer, size_t pSize, size_t pCount ){ // need to typecast here as C has no void* return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); } // ................................................................... aiReturn Seek(size_t pOffset, aiOrigin pOrigin ){ return mFile->SeekProc(mFile,pOffset,pOrigin); } // ................................................................... size_t Tell(void) const { return mFile->TellProc(mFile); } // ................................................................... size_t FileSize() const { return mFile->FileSizeProc(mFile); } // ................................................................... void Flush () { return mFile->FlushProc(mFile); } private: aiFile* mFile; }; // ------------------------------------------------------------------------------------------------ // Custom IOStream implementation for the C-API class CIOSystemWrapper : public IOSystem { public: CIOSystemWrapper(aiFileIO* pFile) : mFileSystem(pFile) {} // ................................................................... bool Exists( const char* pFile) const { CIOSystemWrapper* pip = const_cast(this); IOStream* p = pip->Open(pFile); if (p){ pip->Close(p); return true; } return false; } // ................................................................... char getOsSeparator() const { #ifndef _WIN32 return '/'; #else return '\\'; #endif } // ................................................................... IOStream* Open(const char* pFile,const char* pMode = "rb") { aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode); if (!p) { return NULL; } return new CIOStreamWrapper(p); } // ................................................................... void Close( IOStream* pFile) { if (!pFile) { return; } mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile); delete pFile; } private: aiFileIO* mFileSystem; }; // ------------------------------------------------------------------------------------------------ // Custom LogStream implementation for the C-API class LogToCallbackRedirector : public LogStream { public: LogToCallbackRedirector(const aiLogStream& s) : stream (s) { ai_assert(NULL != s.callback); } ~LogToCallbackRedirector() { #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gLogStreamMutex); #endif // (HACK) Check whether the 'stream.user' pointer points to a // custom LogStream allocated by #aiGetPredefinedLogStream. // In this case, we need to delete it, too. Of course, this // might cause strange problems, but the chance is quite low. PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(), gPredefinedStreams.end(), (Assimp::LogStream*)stream.user); if (it != gPredefinedStreams.end()) { delete *it; gPredefinedStreams.erase(it); } } /** @copydoc LogStream::write */ void write(const char* message) { stream.callback(message,stream.user); } private: aiLogStream stream; }; // ------------------------------------------------------------------------------------------------ void ReportSceneNotFoundError() { DefaultLogger::get()->error("Unable to find the Assimp::Importer for this aiScene. " "Are you playing fools with us? Don't mix cpp and c API. Thanks."); assert(false); } // ------------------------------------------------------------------------------------------------ // Reads the given file and returns its content. const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) { return aiImportFileEx(pFile,pFlags,NULL); } // ------------------------------------------------------------------------------------------------ const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) { ai_assert(NULL != pFile); const aiScene* scene = NULL; ASSIMP_BEGIN_EXCEPTION_REGION(); // create an Importer for this file Assimp::Importer* imp = new Assimp::Importer(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif // copy the global property lists to the Importer instance imp->pimpl->mIntProperties = gIntProperties; imp->pimpl->mFloatProperties = gFloatProperties; imp->pimpl->mStringProperties = gStringProperties; #ifdef AI_C_THREADSAFE lock.unlock(); #endif // setup a custom IO system if necessary if (pFS) { imp->SetIOHandler( new CIOSystemWrapper (pFS) ); } // and have it read the file scene = imp->ReadFile( pFile, pFlags); // if succeeded, place it in the collection of active processes if ( scene) { #ifdef AI_C_THREADSAFE lock.lock(); #endif gActiveImports[scene] = imp; } else { // if failed, extract error code and destroy the import gLastErrorString = imp->GetErrorString(); delete imp; } // return imported data. If the import failed the pointer is NULL anyways ASSIMP_END_EXCEPTION_REGION(const aiScene*); return scene; } // ------------------------------------------------------------------------------------------------ const aiScene* aiImportFileFromMemory( const char* pBuffer, unsigned int pLength, unsigned int pFlags, const char* pHint) { ai_assert(NULL != pBuffer && 0 != pLength); const aiScene* scene = NULL; ASSIMP_BEGIN_EXCEPTION_REGION(); // create an Importer for this file Assimp::Importer* imp = new Assimp::Importer(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif // copy the global property lists to the Importer instance imp->pimpl->mIntProperties = gIntProperties; imp->pimpl->mFloatProperties = gFloatProperties; imp->pimpl->mStringProperties = gStringProperties; #ifdef AI_C_THREADSAFE lock.unlock(); #endif // and have it read the file from the memory buffer scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); // if succeeded, place it in the collection of active processes if ( scene) { #ifdef AI_C_THREADSAFE lock.lock(); #endif gActiveImports[scene] = imp; } else { // if failed, extract error code and destroy the import gLastErrorString = imp->GetErrorString(); delete imp; } // return imported data. If the import failed the pointer is NULL anyways ASSIMP_END_EXCEPTION_REGION(const aiScene*); return scene; } // ------------------------------------------------------------------------------------------------ // Releases all resources associated with the given import process. void aiReleaseImport( const aiScene* pScene) { if (!pScene) { return; } ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif // find the importer associated with this data ImporterMap::iterator it = gActiveImports.find( pScene); // it should be there... else the user is playing fools with us if ( it == gActiveImports.end()) { ReportSceneNotFoundError(); return; } // kill the importer, the data dies with it delete it->second; gActiveImports.erase( it); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene, unsigned int pFlags) { const aiScene* sc = NULL; ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif // find the importer associated with this data ImporterMap::iterator it = gActiveImports.find( pScene); // it should be there... else the user is playing fools with us if ( it == gActiveImports.end()) { ReportSceneNotFoundError(); return NULL; } #ifdef AI_C_THREADSAFE lock.unlock(); #endif sc = it->second->ApplyPostProcessing(pFlags); #ifdef AI_C_THREADSAFE lock.lock(); #endif if (!sc) { // kill the importer, the data dies with it delete it->second; gActiveImports.erase( it); return NULL; } ASSIMP_END_EXCEPTION_REGION(const aiScene*); return sc; } // ------------------------------------------------------------------------------------------------ void CallbackToLogRedirector (const char* msg, char* dt) { ai_assert(NULL != msg && NULL != dt); LogStream* s = (LogStream*)dt; s->write(msg); } // ------------------------------------------------------------------------------------------------ ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file) { aiLogStream sout; ASSIMP_BEGIN_EXCEPTION_REGION(); LogStream* stream = LogStream::createDefaultStream(pStream,file); if (!stream) { sout.callback = NULL; sout.user = NULL; } else { sout.callback = &CallbackToLogRedirector; sout.user = (char*)stream; } gPredefinedStreams.push_back(stream); ASSIMP_END_EXCEPTION_REGION(aiLogStream); return sout; } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiAttachLogStream( const aiLogStream* stream ) { ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gLogStreamMutex); #endif LogStream* lg = new LogToCallbackRedirector(*stream); gActiveLogStreams[*stream] = lg; if (DefaultLogger::isNullLogger()) { DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); } DefaultLogger::get()->attachStream(lg); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream) { ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gLogStreamMutex); #endif // find the logstream associated with this data LogStreamMap::iterator it = gActiveLogStreams.find( *stream); // it should be there... else the user is playing fools with us if ( it == gActiveLogStreams.end()) { return AI_FAILURE; } DefaultLogger::get()->detatchStream( it->second ); delete it->second; gActiveLogStreams.erase( it); if (gActiveLogStreams.empty()) { DefaultLogger::kill(); } ASSIMP_END_EXCEPTION_REGION(aiReturn); return AI_SUCCESS; } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiDetachAllLogStreams(void) { ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gLogStreamMutex); #endif for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) { DefaultLogger::get()->detatchStream( it->second ); delete it->second; } gActiveLogStreams.clear(); DefaultLogger::kill(); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiEnableVerboseLogging(aiBool d) { if (!DefaultLogger::isNullLogger()) { DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); } gVerboseLogging = d; } // ------------------------------------------------------------------------------------------------ // Returns the error text of the last failed import process. const char* aiGetErrorString() { return gLastErrorString.c_str(); } // ------------------------------------------------------------------------------------------------ // Returns the error text of the last failed import process. aiBool aiIsExtensionSupported(const char* szExtension) { ai_assert(NULL != szExtension); aiBool candoit=AI_FALSE; ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif if (!gActiveImports.empty()) { return ((*(gActiveImports.begin())).second->IsExtensionSupported( szExtension )) ? AI_TRUE : AI_FALSE; } // fixme: no need to create a temporary Importer instance just for that .. Assimp::Importer tmp; candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE; ASSIMP_END_EXCEPTION_REGION(aiBool); return candoit; } // ------------------------------------------------------------------------------------------------ // Get a list of all file extensions supported by ASSIMP void aiGetExtensionList(aiString* szOut) { ai_assert(NULL != szOut); ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif if (!gActiveImports.empty()) { (*(gActiveImports.begin())).second->GetExtensionList(*szOut); return; } // fixme: no need to create a temporary Importer instance just for that .. Assimp::Importer tmp; tmp.GetExtensionList(*szOut); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ // Get the memory requirements for a particular import. void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, C_STRUCT aiMemoryInfo* in) { ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif // find the importer associated with this data ImporterMap::iterator it = gActiveImports.find( pIn); // it should be there... else the user is playing fools with us if ( it == gActiveImports.end()) { ReportSceneNotFoundError(); return; } // get memory statistics #ifdef AI_C_THREADSAFE lock.unlock(); #endif it->second->GetMemoryRequirements(*in); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ // Importer::SetPropertyInteger ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value) { ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif SetGenericProperty(gIntProperties,szName,value,NULL); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ // Importer::SetPropertyFloat ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value) { ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif SetGenericProperty(gFloatProperties,szName,value,NULL); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ // Importer::SetPropertyString ASSIMP_API void aiSetImportPropertyString(const char* szName, const C_STRUCT aiString* st) { if (!st) { return; } ASSIMP_BEGIN_EXCEPTION_REGION(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif SetGenericProperty(gStringProperties,szName, std::string( st->data ),NULL); ASSIMP_END_EXCEPTION_REGION(void); } // ------------------------------------------------------------------------------------------------ // Rotation matrix to quaternion ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat) { ai_assert(NULL != quat && NULL != mat); *quat = aiQuaternion(*mat); } // ------------------------------------------------------------------------------------------------ // Matrix decomposition ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling, aiQuaternion* rotation, aiVector3D* position) { ai_assert(NULL != rotation && NULL != position && NULL != scaling && NULL != mat); mat->Decompose(*scaling,*rotation,*position); } // ------------------------------------------------------------------------------------------------ // Matrix transpose ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat) { ai_assert(NULL != mat); mat->Transpose(); } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat) { ai_assert(NULL != mat); mat->Transpose(); } // ------------------------------------------------------------------------------------------------ // Vector transformation ASSIMP_API void aiTransformVecByMatrix3(C_STRUCT aiVector3D* vec, const C_STRUCT aiMatrix3x3* mat) { ai_assert(NULL != mat && NULL != vec); *vec *= (*mat); } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiTransformVecByMatrix4(C_STRUCT aiVector3D* vec, const C_STRUCT aiMatrix4x4* mat) { ai_assert(NULL != mat && NULL != vec); *vec *= (*mat); } // ------------------------------------------------------------------------------------------------ // Matrix multiplication ASSIMP_API void aiMultiplyMatrix4( C_STRUCT aiMatrix4x4* dst, const C_STRUCT aiMatrix4x4* src) { ai_assert(NULL != dst && NULL != src); *dst = (*dst) * (*src); } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiMultiplyMatrix3( C_STRUCT aiMatrix3x3* dst, const C_STRUCT aiMatrix3x3* src) { ai_assert(NULL != dst && NULL != src); *dst = (*dst) * (*src); } // ------------------------------------------------------------------------------------------------ // Matrix identity ASSIMP_API void aiIdentityMatrix3( C_STRUCT aiMatrix3x3* mat) { ai_assert(NULL != mat); *mat = aiMatrix3x3(); } // ------------------------------------------------------------------------------------------------ ASSIMP_API void aiIdentityMatrix4( C_STRUCT aiMatrix4x4* mat) { ai_assert(NULL != mat); *mat = aiMatrix4x4(); } 3rdparty/assimp/code/AssimpPCH.cpp000066400000000000000000000040371217616171500173420ustar00rootroot00000000000000 // Actually just a dummy, used by the compiler to build the precompiled header. #include "AssimpPCH.h" #include "./../include/aiVersion.h" // -------------------------------------------------------------------------------- // Legal information string - dont't remove from image! static const char* LEGAL_INFORMATION = "Open Asset Import Library (Assimp).\n" "A free C/C++ library to import various 3D file formats into applications\n\n" "(c) 2008-2010, ASSIMP Development Team\n" "License under the terms and conditions of the 3-clause BSD license\n" "http://assimp.sourceforge.net\n" ; // ------------------------------------------------------------------------------------------------ // Get legal string ASSIMP_API const char* aiGetLegalString () { return LEGAL_INFORMATION; } // ------------------------------------------------------------------------------------------------ // Get Assimp minor version ASSIMP_API unsigned int aiGetVersionMinor () { return 0; } // ------------------------------------------------------------------------------------------------ // Get Assimp major version ASSIMP_API unsigned int aiGetVersionMajor () { return 2; } // ------------------------------------------------------------------------------------------------ // Get flags used for compilation ASSIMP_API unsigned int aiGetCompileFlags () { unsigned int flags = 0; #ifdef ASSIMP_BUILD_BOOST_WORKAROUND flags |= ASSIMP_CFLAGS_NOBOOST; #endif #ifdef ASSIMP_BUILD_SINGLETHREADED flags |= ASSIMP_CFLAGS_SINGLETHREADED; #endif #ifdef ASSIMP_BUILD_DEBUG flags |= ASSIMP_CFLAGS_DEBUG; #endif #ifdef ASSIMP_BUILD_DLL_EXPORT flags |= ASSIMP_CFLAGS_SHARED; #endif #ifdef _STLPORT_VERSION flags |= ASSIMP_CFLAGS_STLPORT; #endif return flags; } // include current build revision, which is even updated from time to time -- :-) #include "../revision.h" // ------------------------------------------------------------------------------------------------ ASSIMP_API unsigned int aiGetVersionRevision () { return SVNRevision; } 3rdparty/assimp/code/AssimpPCH.h000066400000000000000000000114221217616171500170030ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file AssimpPCH.h * PCH master include. Every unit in Assimp has to include it. */ #ifndef ASSIMP_PCH_INCLUDED #define ASSIMP_PCH_INCLUDED #define ASSIMP_INTERNAL_BUILD // ---------------------------------------------------------------------------------------- /* General compile config taken from aiDefines.h. It is important that the user compiles * using exactly the same settings in aiDefines.h. Settings in AssimpPCH.h may differ, * they won't affect the public API. */ #include "../include/aiDefines.h" /* Include our stdint.h replacement header for MSVC, take the global header for gcc/mingw */ #ifdef _MSC_VER # include "pstdint.h" #else # include #endif /* Undefine the min/max macros defined by some platform headers (namely Windows.h) to * avoid obvious conflicts with std::min() and std::max(). */ #undef min #undef max /* Concatenate two tokens after evaluating them */ #define _AI_CONCAT(a,b) a ## b #define AI_CONCAT(a,b) _AI_CONCAT(a,b) /* Helper macro to set a pointer to NULL in debug builds */ #if (defined _DEBUG) # define AI_DEBUG_INVALIDATE_PTR(x) x = NULL; #else # define AI_DEBUG_INVALIDATE_PTR(x) #endif /* Beginning with MSVC8 some C string manipulation functions are mapped to their _safe_ * counterparts (e.g. _itoa_s). This avoids a lot of trouble with deprecation warnings. */ #if _MSC_VER >= 1400 && !(defined _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) # define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 #endif /* size_t to unsigned int, possible loss of data. The compiler is right with his warning * but this loss of data won't be a problem for us. So shut up, little boy. */ #ifdef _MSC_VER # pragma warning (disable : 4267) #endif // ---------------------------------------------------------------------------------------- /* Actually that's not required for MSVC. It is included somewhere in the deeper parts of * the MSVC STL but it's necessary for proper build with STLport. */ #include // Runtime/STL headers #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Boost headers #include #include #include #include #include //#include #include #include #include #include // Public ASSIMP headers #include "../include/DefaultLogger.h" #include "../include/IOStream.h" #include "../include/IOSystem.h" #include "../include/aiScene.h" #include "../include/aiPostProcess.h" #include "../include/assimp.hpp" // Internal utility headers #include "BaseImporter.h" #include "MaterialSystem.h" #include "StringComparison.h" #include "StreamReader.h" #include "qnan.h" #endif // !! ASSIMP_PCH_INCLUDED 3rdparty/assimp/code/B3DImporter.cpp000066400000000000000000000466111217616171500176510ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file B3DImporter.cpp * @brief Implementation of the b3d importer class */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER // internal headers #include "B3DImporter.h" #include "TextureTransform.h" #include "ConvertToLHProcess.h" using namespace Assimp; using namespace std; // (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings #ifdef _MSC_VER # pragma warning (disable: 4018) #endif //#define DEBUG_B3D // ------------------------------------------------------------------------------------------------ bool B3DImporter::CanRead( const std::string& pFile, IOSystem*, bool) const{ size_t pos=pFile.find_last_of( '.' ); if ( pos==string::npos ) return false; string ext=pFile.substr( pos+1 ); if ( ext.size()!=3 ) return false; return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D'); } // ------------------------------------------------------------------------------------------------ void B3DImporter::GetExtensionList( std::set& extensions ){ extensions.insert("b3d"); } #ifdef DEBUG_B3D extern "C"{ void _stdcall AllocConsole(); } #endif // ------------------------------------------------------------------------------------------------ void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){ #ifdef DEBUG_B3D AllocConsole(); freopen( "conin$","r",stdin ); freopen( "conout$","w",stdout ); freopen( "conout$","w",stderr ); cout<<"Hello world from the B3DImporter!"< file( pIOHandler->Open( pFile)); // Check whether we can read from the file if ( file.get() == NULL) throw DeadlyImportError( "Failed to open B3D file " + pFile + "."); // check whether the .b3d file is large enough to contain // at least one chunk. size_t fileSize = file->FileSize(); if ( fileSize<8 ) throw DeadlyImportError( "B3D File is too small."); _pos=0; _buf.resize( fileSize ); file->Read( &_buf[0],1,fileSize ); _stack.clear(); ReadBB3D( pScene ); } // ------------------------------------------------------------------------------------------------ void B3DImporter::Oops(){ throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" ); } // ------------------------------------------------------------------------------------------------ void B3DImporter::Fail( string str ){ #ifdef DEBUG_B3D cout<<"Error in B3D file data: "< T *B3DImporter::to_array( const vector &v ){ if ( !v.size() ) return 0; T *p=new T[v.size()]; for ( size_t i=0;i8 ){ Fail( "Bad texture count" ); } while ( ChunkSize() ){ string name=ReadString(); aiVector3D color=ReadVec3(); float alpha=ReadFloat(); float shiny=ReadFloat(); /*int blend=**/ReadInt(); int fx=ReadInt(); MaterialHelper *mat=new MaterialHelper; _materials.push_back( mat ); // Name aiString ainame( name ); mat->AddProperty( &ainame,AI_MATKEY_NAME ); // Diffuse color mat->AddProperty( &color,1,AI_MATKEY_COLOR_DIFFUSE ); // Opacity mat->AddProperty( &alpha,1,AI_MATKEY_OPACITY ); // Specular color aiColor3D speccolor( shiny,shiny,shiny ); mat->AddProperty( &speccolor,1,AI_MATKEY_COLOR_SPECULAR ); // Specular power float specpow=shiny*128; mat->AddProperty( &specpow,1,AI_MATKEY_SHININESS ); // Double sided if ( fx & 0x10 ){ int i=1; mat->AddProperty( &i,1,AI_MATKEY_TWOSIDED ); } //Textures for ( int i=0;i=0 && texid>=static_cast(_textures.size())) ){ Fail( "Bad texture id" ); } if ( i==0 && texid>=0 ){ aiString texname( _textures[texid] ); mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) ); } } } } // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadVRTS(){ _vflags=ReadInt(); _tcsets=ReadInt(); _tcsize=ReadInt(); if ( _tcsets<0 || _tcsets>4 || _tcsize<0 || _tcsize>4 ){ Fail( "Bad texcoord data" ); } int sz=12+(_vflags&1?12:0)+(_vflags&2?16:0)+(_tcsets*_tcsize*4); int n_verts=ChunkSize()/sz; int v0=_vertices.size(); _vertices.resize( v0+n_verts ); for ( int i=0;i=(int)_materials.size() ){ #ifdef DEBUG_B3D cout<<"material id="<mMaterialIndex=matid; mesh->mNumFaces=0; mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE; int n_tris=ChunkSize()/12; aiFace *face=mesh->mFaces=new aiFace[n_tris]; for ( int i=0;i=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){ #ifdef DEBUG_B3D cout<<"Bad triangle index: i0="<mNumIndices=3; face->mIndices=new unsigned[3]; face->mIndices[0]=i0; face->mIndices[1]=i1; face->mIndices[2]=i2; ++mesh->mNumFaces; ++face; } } // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadMESH(){ /*int matid=*/ReadInt(); int v0=_vertices.size(); while ( ChunkSize() ){ string t=ReadChunk(); if ( t=="VRTS" ){ ReadVRTS(); }else if ( t=="TRIS" ){ ReadTRIS( v0 ); } ExitChunk(); } } // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadBONE( int id ){ while ( ChunkSize() ){ int vertex=ReadInt(); float weight=ReadFloat(); if ( vertex<0 || vertex>=(int)_vertices.size() ){ Fail( "Bad vertex index" ); } Vertex &v=_vertices[vertex]; int i; for ( i=0;i<4;++i ){ if ( !v.weights[i] ){ v.bones[i]=id; v.weights[i]=weight; break; } } #ifdef DEBUG_B3D if ( i==4 ){ cout<<"Too many bone weights"< trans,scale; vector rot; int flags=ReadInt(); while ( ChunkSize() ){ int frame=ReadInt(); if ( flags & 1 ){ trans.push_back( aiVectorKey( frame,ReadVec3() ) ); } if ( flags & 2 ){ scale.push_back( aiVectorKey( frame,ReadVec3() ) ); } if ( flags & 4 ){ rot.push_back( aiQuatKey( frame,ReadQuat() ) ); } } if ( flags & 1 ){ nodeAnim->mNumPositionKeys=trans.size(); nodeAnim->mPositionKeys=to_array( trans ); } if ( flags & 2 ){ nodeAnim->mNumScalingKeys=scale.size(); nodeAnim->mScalingKeys=to_array( scale ); } if ( flags & 4 ){ nodeAnim->mNumRotationKeys=rot.size(); nodeAnim->mRotationKeys=to_array( rot ); } } // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadANIM(){ /*int flags=*/ReadInt(); int frames=ReadInt(); float fps=ReadFloat(); aiAnimation *anim=new aiAnimation; _animations.push_back( anim ); anim->mDuration=frames; anim->mTicksPerSecond=fps; } // ------------------------------------------------------------------------------------------------ aiNode *B3DImporter::ReadNODE( aiNode *parent ){ string name=ReadString(); aiVector3D t=ReadVec3(); aiVector3D s=ReadVec3(); aiQuaternion r=ReadQuat(); aiMatrix4x4 trans,scale,rot; aiMatrix4x4::Translation( t,trans ); aiMatrix4x4::Scaling( s,scale ); rot=aiMatrix4x4( r.GetMatrix() ); aiMatrix4x4 tform=trans * rot * scale; int nodeid=_nodes.size(); aiNode *node=new aiNode( name ); _nodes.push_back( node ); node->mParent=parent; node->mTransformation=tform; aiNodeAnim *nodeAnim=0; vector meshes; vector children; while ( ChunkSize() ){ string t=ReadChunk(); if ( t=="MESH" ){ int n=_meshes.size(); ReadMESH(); for ( int i=n;i<(int)_meshes.size();++i ){ meshes.push_back( i ); } }else if ( t=="BONE" ){ ReadBONE( nodeid ); }else if ( t=="ANIM" ){ ReadANIM(); }else if ( t=="KEYS" ){ if ( !nodeAnim ){ nodeAnim=new aiNodeAnim; _nodeAnims.push_back( nodeAnim ); nodeAnim->mNodeName=node->mName; } ReadKEYS( nodeAnim ); }else if ( t=="NODE" ){ aiNode *child=ReadNODE( node ); children.push_back( child ); } ExitChunk(); } node->mNumMeshes=meshes.size(); node->mMeshes=to_array( meshes ); node->mNumChildren=children.size(); node->mChildren=to_array( children ); return node; } // ------------------------------------------------------------------------------------------------ void B3DImporter::ReadBB3D( aiScene *scene ){ _textures.clear(); _materials.size(); _vertices.clear(); _meshes.clear(); _nodes.clear(); _nodeAnims.clear(); _animations.clear(); string t=ReadChunk(); if ( t=="BB3D" ){ int version=ReadInt(); if (!DefaultLogger::isNullLogger()) { char dmp[128]; sprintf(dmp,"B3D file format version: %i",version); DefaultLogger::get()->info(dmp); } while ( ChunkSize() ){ string t=ReadChunk(); if ( t=="TEXS" ){ ReadTEXS(); }else if ( t=="BRUS" ){ ReadBRUS(); }else if ( t=="NODE" ){ ReadNODE( 0 ); } ExitChunk(); } } ExitChunk(); if ( !_nodes.size() ) Fail( "No nodes" ); if ( !_meshes.size() ) Fail( "No meshes" ); //Fix nodes/meshes/bones for (size_t i=0;i<_nodes.size();++i ){ aiNode *node=_nodes[i]; for ( size_t j=0;jmNumMeshes;++j ){ aiMesh *mesh=_meshes[node->mMeshes[j]]; int n_tris=mesh->mNumFaces; int n_verts=mesh->mNumVertices=n_tris * 3; aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0; if ( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ]; if ( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ]; aiFace *face=mesh->mFaces; vector< vector > vweights( _nodes.size() ); for ( int i=0;imIndices[j]]; *mv++=v.vertex; if ( mn ) *mn++=v.normal; if ( mc ) *mc++=v.texcoords; face->mIndices[j]=i+j; for ( int k=0;k<4;++k ){ if ( !v.weights[k] ) break; int bone=v.bones[k]; float weight=v.weights[k]; vweights[bone].push_back( aiVertexWeight(i+j,weight) ); } } ++face; } vector bones; for (size_t i=0;i &weights=vweights[i]; if ( !weights.size() ) continue; aiBone *bone=new aiBone; bones.push_back( bone ); aiNode *bnode=_nodes[i]; bone->mName=bnode->mName; bone->mNumWeights=weights.size(); bone->mWeights=to_array( weights ); aiMatrix4x4 mat=bnode->mTransformation; while ( bnode->mParent ){ bnode=bnode->mParent; mat=bnode->mTransformation * mat; } bone->mOffsetMatrix=mat.Inverse(); } mesh->mNumBones=bones.size(); mesh->mBones=to_array( bones ); } } //nodes scene->mRootNode=_nodes[0]; //material if ( !_materials.size() ){ _materials.push_back( new MaterialHelper ); } scene->mNumMaterials=_materials.size(); scene->mMaterials=to_array( _materials ); //meshes scene->mNumMeshes=_meshes.size(); scene->mMeshes=to_array( _meshes ); //animations if ( _animations.size()==1 && _nodeAnims.size() ){ aiAnimation *anim=_animations.back(); anim->mNumChannels=_nodeAnims.size(); anim->mChannels=to_array( _nodeAnims ); scene->mNumAnimations=_animations.size(); scene->mAnimations=to_array( _animations ); } // convert to RH MakeLeftHandedProcess makeleft; makeleft.Execute( scene ); FlipWindingOrderProcess flip; flip.Execute( scene ); } #endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER 3rdparty/assimp/code/B3DImporter.h000066400000000000000000000070251217616171500173120ustar00rootroot00000000000000 /* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file Definition of the .b3d importer class. */ #ifndef AI_B3DIMPORTER_H_INC #define AI_B3DIMPORTER_H_INC #include "../include/aiTypes.h" #include "../include/aiMesh.h" #include "../include/aiMaterial.h" #include #include namespace Assimp{ class B3DImporter : public BaseImporter{ public: virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; protected: virtual void GetExtensionList(std::set& extensions); virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); private: int ReadByte(); int ReadInt(); float ReadFloat(); aiVector2D ReadVec2(); aiVector3D ReadVec3(); aiQuaternion ReadQuat(); std::string ReadString(); std::string ReadChunk(); void ExitChunk(); unsigned ChunkSize(); template T *to_array( const std::vector &v ); struct Vertex{ aiVector3D vertex; aiVector3D normal; aiVector3D texcoords; unsigned char bones[4]; float weights[4]; }; void Oops(); void Fail( std::string str ); void ReadTEXS(); void ReadBRUS(); void ReadVRTS(); void ReadTRIS( int v0 ); void ReadMESH(); void ReadBONE( int id ); void ReadKEYS( aiNodeAnim *nodeAnim ); void ReadANIM(); aiNode *ReadNODE( aiNode *parent ); void ReadBB3D( aiScene *scene ); unsigned _pos; // unsigned _size; std::vector _buf; std::vector _stack; std::vector _textures; std::vector _materials; int _vflags,_tcsets,_tcsize; std::vector _vertices; std::vector _nodes; std::vector _meshes; std::vector _nodeAnims; std::vector _animations; }; } #endif 3rdparty/assimp/code/BVHLoader.cpp000066400000000000000000000452701217616171500173250ustar00rootroot00000000000000/** Implementation of the BVH loader */ /* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_BVH_IMPORTER #include "BVHLoader.h" #include "fast_atof.h" #include "SkeletonMeshBuilder.h" using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer BVHLoader::BVHLoader() : mLine(0), mAnimTickDuration(1.), mAnimNumFrames(0) {} // ------------------------------------------------------------------------------------------------ // Destructor, private as well BVHLoader::~BVHLoader() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const { // check file extension const std::string extension = GetExtension(pFile); if ( extension == "bvh") return true; if ((!extension.length() || cs) && pIOHandler) { const char* tokens[] = {"HIERARCHY"}; return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); } return false; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { mFileName = pFile; // read file into memory boost::scoped_ptr file( pIOHandler->Open( pFile)); if ( file.get() == NULL) throw DeadlyImportError( "Failed to open file " + pFile + "."); size_t fileSize = file->FileSize(); if ( fileSize == 0) throw DeadlyImportError( "File is too small."); mBuffer.resize( fileSize); file->Read( &mBuffer.front(), 1, fileSize); // start reading mReader = mBuffer.begin(); mLine = 1; ReadStructure( pScene); // build a dummy mesh for the skeleton so that we see something at least SkeletonMeshBuilder meshBuilder( pScene); // construct an animation from all the motion data we read CreateAnimation( pScene); } // ------------------------------------------------------------------------------------------------ // Reads the file void BVHLoader::ReadStructure( aiScene* pScene) { // first comes hierarchy std::string header = GetNextToken(); if ( header != "HIERARCHY") ThrowException( "Expected header string \"HIERARCHY\"."); ReadHierarchy( pScene); // then comes the motion data std::string motion = GetNextToken(); if ( motion != "MOTION") ThrowException( "Expected beginning of motion data \"MOTION\"."); ReadMotion( pScene); } // ------------------------------------------------------------------------------------------------ // Reads the hierarchy void BVHLoader::ReadHierarchy( aiScene* pScene) { std::string root = GetNextToken(); if ( root != "ROOT") ThrowException( "Expected root node \"ROOT\"."); // Go read the hierarchy from here pScene->mRootNode = ReadNode(); } // ------------------------------------------------------------------------------------------------ // Reads a node and recursively its childs and returns the created node; aiNode* BVHLoader::ReadNode() { // first token is name std::string nodeName = GetNextToken(); if ( nodeName.empty() || nodeName == "{") ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName)); // then an opening brace should follow std::string openBrace = GetNextToken(); if ( openBrace != "{") ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); // Create a node aiNode* node = new aiNode( nodeName); std::vector childNodes; // and create an bone entry for it mNodes.push_back( Node( node)); Node& internNode = mNodes.back(); // now read the node's contents while ( 1) { std::string token = GetNextToken(); // node offset to parent node if ( token == "OFFSET") ReadNodeOffset( node); else if ( token == "CHANNELS") ReadNodeChannels( internNode); else if ( token == "JOINT") { // child node follows aiNode* child = ReadNode(); child->mParent = node; childNodes.push_back( child); } else if ( token == "End") { // The real symbol is "End Site". Second part comes in a separate token std::string siteToken = GetNextToken(); if ( siteToken != "Site") ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken)); aiNode* child = ReadEndSite( nodeName); child->mParent = node; childNodes.push_back( child); } else if ( token == "}") { // we're done with that part of the hierarchy break; } else { // everything else is a parse error ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); } } // add the child nodes if there are any if ( childNodes.size() > 0) { node->mNumChildren = childNodes.size(); node->mChildren = new aiNode*[node->mNumChildren]; std::copy( childNodes.begin(), childNodes.end(), node->mChildren); } // and return the sub-hierarchy we built here return node; } // ------------------------------------------------------------------------------------------------ // Reads an end node and returns the created node. aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) { // check opening brace std::string openBrace = GetNextToken(); if ( openBrace != "{") ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); // Create a node aiNode* node = new aiNode( "EndSite_" + pParentName); // now read the node's contents. Only possible entry is "OFFSET" while ( 1) { std::string token = GetNextToken(); // end node's offset if ( token == "OFFSET") { ReadNodeOffset( node); } else if ( token == "}") { // we're done with the end node break; } else { // everything else is a parse error ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); } } // and return the sub-hierarchy we built here return node; } // ------------------------------------------------------------------------------------------------ // Reads a node offset for the given node void BVHLoader::ReadNodeOffset( aiNode* pNode) { // Offset consists of three floats to read aiVector3D offset; offset.x = GetNextTokenAsFloat(); offset.y = GetNextTokenAsFloat(); offset.z = GetNextTokenAsFloat(); // build a transformation matrix from it pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); } // ------------------------------------------------------------------------------------------------ // Reads the animation channels for the given node void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode) { // number of channels. Use the float reader because we're lazy float numChannelsFloat = GetNextTokenAsFloat(); unsigned int numChannels = (unsigned int) numChannelsFloat; for ( unsigned int a = 0; a < numChannels; a++) { std::string channelToken = GetNextToken(); if ( channelToken == "Xposition") pNode.mChannels.push_back( Channel_PositionX); else if ( channelToken == "Yposition") pNode.mChannels.push_back( Channel_PositionY); else if ( channelToken == "Zposition") pNode.mChannels.push_back( Channel_PositionZ); else if ( channelToken == "Xrotation") pNode.mChannels.push_back( Channel_RotationX); else if ( channelToken == "Yrotation") pNode.mChannels.push_back( Channel_RotationY); else if ( channelToken == "Zrotation") pNode.mChannels.push_back( Channel_RotationZ); else ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken)); } } // ------------------------------------------------------------------------------------------------ // Reads the motion data void BVHLoader::ReadMotion( aiScene*) { // Read number of frames std::string tokenFrames = GetNextToken(); if ( tokenFrames != "Frames:") ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames)); float numFramesFloat = GetNextTokenAsFloat(); mAnimNumFrames = (unsigned int) numFramesFloat; // Read frame duration std::string tokenDuration1 = GetNextToken(); std::string tokenDuration2 = GetNextToken(); if ( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2)); mAnimTickDuration = GetNextTokenAsFloat(); // resize value vectors for each node for ( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames); // now read all the data and store it in the corresponding node's value vector for ( unsigned int frame = 0; frame < mAnimNumFrames; ++frame) { // on each line read the values for all nodes for ( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) { // get as many values as the node has channels for ( unsigned int c = 0; c < it->mChannels.size(); ++c) it->mChannelValues.push_back( GetNextTokenAsFloat()); } // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it } } // ------------------------------------------------------------------------------------------------ // Retrieves the next token std::string BVHLoader::GetNextToken() { // skip any preceeding whitespace while ( mReader != mBuffer.end()) { if ( !isspace( *mReader)) break; // count lines if ( *mReader == '\n') mLine++; ++mReader; } // collect all chars till the next whitespace. BVH is easy in respect to that. std::string token; while ( mReader != mBuffer.end()) { if ( isspace( *mReader)) break; token.push_back( *mReader); ++mReader; // little extra logic to make sure braces are counted correctly if ( token == "{" || token == "}") break; } // empty token means end of file, which is just fine return token; } // ------------------------------------------------------------------------------------------------ // Reads the next token as a float float BVHLoader::GetNextTokenAsFloat() { std::string token = GetNextToken(); if ( token.empty()) ThrowException( "Unexpected end of file while trying to read a float"); // check if the float is valid by testing if the atof() function consumed every char of the token const char* ctoken = token.c_str(); float result = 0.0f; ctoken = fast_atof_move( ctoken, result); if ( ctoken != token.c_str() + token.length()) ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token)); return result; } // ------------------------------------------------------------------------------------------------ // Aborts the file reading with an exception void BVHLoader::ThrowException( const std::string& pError) { throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError)); } // ------------------------------------------------------------------------------------------------ // Constructs an animation for the motion data and stores it in the given scene void BVHLoader::CreateAnimation( aiScene* pScene) { // create the animation pScene->mNumAnimations = 1; pScene->mAnimations = new aiAnimation*[1]; aiAnimation* anim = new aiAnimation; pScene->mAnimations[0] = anim; // put down the basic parameters anim->mName.Set( "Motion"); anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration); anim->mDuration = double( mAnimNumFrames - 1); // now generate the tracks for all nodes anim->mNumChannels = mNodes.size(); anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown for (unsigned int i = 0; i < anim->mNumChannels;++i) anim->mChannels[i] = NULL; for ( unsigned int a = 0; a < anim->mNumChannels; a++) { const Node& node = mNodes[a]; const std::string nodeName = std::string( node.mNode->mName.data ); aiNodeAnim* nodeAnim = new aiNodeAnim; anim->mChannels[a] = nodeAnim; nodeAnim->mNodeName.Set( nodeName); // translational part, if given if ( node.mChannels.size() == 6) { nodeAnim->mNumPositionKeys = mAnimNumFrames; nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; aiVectorKey* poskey = nodeAnim->mPositionKeys; for ( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) { poskey->mTime = double( fr); // Now compute all translations in the right order for ( unsigned int channel = 0; channel < 3; ++channel) { switch( node.mChannels[channel]) { case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break; case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break; case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break; default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); } } ++poskey; } } else { // if no translation part is given, put a default sequence aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); nodeAnim->mNumPositionKeys = 1; nodeAnim->mPositionKeys = new aiVectorKey[1]; nodeAnim->mPositionKeys[0].mTime = 0.0; nodeAnim->mPositionKeys[0].mValue = nodePos; } // rotation part. Always present. First find value offsets { unsigned int rotOffset = 0; if ( node.mChannels.size() == 6) { // Offset all further calculations rotOffset = 3; } // Then create the number of rotation keys nodeAnim->mNumRotationKeys = mAnimNumFrames; nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; aiQuatKey* rotkey = nodeAnim->mRotationKeys; for ( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) { aiMatrix4x4 temp; aiMatrix3x3 rotMatrix; for ( unsigned int channel = 0; channel < 3; ++channel) { // translate ZXY euler angels into a quaternion const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f; // Compute rotation transformations in the right order switch (node.mChannels[rotOffset+channel]) { case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); } } rotkey->mTime = double( fr); rotkey->mValue = aiQuaternion( rotMatrix); ++rotkey; } } // scaling part. Always just a default track { nodeAnim->mNumScalingKeys = 1; nodeAnim->mScalingKeys = new aiVectorKey[1]; nodeAnim->mScalingKeys[0].mTime = 0.0; nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f); } } } #endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER 3rdparty/assimp/code/BVHLoader.h000066400000000000000000000127701217616171500167710ustar00rootroot00000000000000/** Defines the BHV motion capturing loader class */ /* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file BVHLoader.h * @brief Biovision BVH import */ #ifndef AI_BVHLOADER_H_INC #define AI_BVHLOADER_H_INC #include "BaseImporter.h" namespace Assimp { // -------------------------------------------------------------------------------- /** Loader class to read Motion Capturing data from a .bvh file. * * This format only contains a hierarchy of joints and a series of keyframes for * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh * inside the loader just to be able to see something. */ class BVHLoader : public BaseImporter { friend class Importer; /** Possible animation channels for which the motion data holds the values */ enum ChannelType { Channel_PositionX, Channel_PositionY, Channel_PositionZ, Channel_RotationX, Channel_RotationY, Channel_RotationZ }; /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */ struct Node { const aiNode* mNode; std::vector mChannels; std::vector mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames Node() { } Node( const aiNode* pNode) : mNode( pNode) { } }; protected: /** Constructor to be privately used by Importer */ BVHLoader(); /** Destructor, private as well */ ~BVHLoader(); public: /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const; protected: /** Called by Importer::GetExtensionList() for each loaded importer. * See BaseImporter::GetExtensionList() for details */ void GetExtensionList(std::set& extensions) { extensions.insert("bvh"); } /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); protected: /** Reads the file */ void ReadStructure( aiScene* pScene); /** Reads the hierarchy */ void ReadHierarchy( aiScene* pScene); /** Reads a node and recursively its childs and returns the created node. */ aiNode* ReadNode(); /** Reads an end node and returns the created node. */ aiNode* ReadEndSite( const std::string& pParentName); /** Reads a node offset for the given node */ void ReadNodeOffset( aiNode* pNode); /** Reads the animation channels into the given node */ void ReadNodeChannels( BVHLoader::Node& pNode); /** Reads the motion data */ void ReadMotion( aiScene* pScene); /** Retrieves the next token */ std::string GetNextToken(); /** Reads the next token as a float */ float GetNextTokenAsFloat(); /** Aborts the file reading with an exception */ void ThrowException( const std::string& pError); /** Constructs an animation for the motion data and stores it in the given scene */ void CreateAnimation( aiScene* pScene); protected: /** Filename, for a verbose error message */ std::string mFileName; /** Buffer to hold the loaded file */ std::vector mBuffer; /** Next char to read from the buffer */ std::vector::const_iterator mReader; /** Current line, for error messages */ unsigned int mLine; /** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index. * Also contain the motion data for the node's channels */ std::vector mNodes; /** basic Animation parameters */ float mAnimTickDuration; unsigned int mAnimNumFrames; }; } // end of namespace Assimp #endif // AI_BVHLOADER_H_INC 3rdparty/assimp/code/BaseImporter.cpp000066400000000000000000000414551217616171500201540ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file BaseImporter.cpp * @brief Implementation of BaseImporter */ #include "AssimpPCH.h" #include "BaseImporter.h" #include "FileSystemFilter.h" using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer BaseImporter::BaseImporter() : progress() { // nothing to do here } // ------------------------------------------------------------------------------------------------ // Destructor, private as well BaseImporter::~BaseImporter() { // nothing to do here } // ------------------------------------------------------------------------------------------------ // Imports the given file and returns the imported data. aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { progress = pImp->GetProgressHandler(); ai_assert(progress); // Gather configuration properties for this run SetupProperties( pImp ); // Construct a file system filter to improve our success ratio at reading external files FileSystemFilter filter(pFile,pIOHandler); // create a scene object to hold the data ScopeGuard sc(new aiScene()); // dispatch importing try { InternReadFile( pFile, sc, &filter); } catch( const std::exception& err ) { // extract error description mErrorText = err.what(); DefaultLogger::get()->error(mErrorText); return NULL; } // return what we gathered from the import. sc.dismiss(); return sc; } // ------------------------------------------------------------------------------------------------ void BaseImporter::SetupProperties(const Importer*) { // the default implementation does nothing } // ------------------------------------------------------------------------------------------------ /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler, const std::string& pFile, const char** tokens, unsigned int numTokens, unsigned int searchBytes /* = 200 */) { ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes); if (!pIOHandler) return false; boost::scoped_ptr pStream (pIOHandler->Open(pFile)); if (pStream.get() ) { // read 200 characters from the file boost::scoped_array _buffer (new char[searchBytes+1 /* for the '\0' */]); char* buffer = _buffer.get(); const unsigned int read = pStream->Read(buffer,1,searchBytes); if (!read) return false; for (unsigned int i = 0; i < read;++i) buffer[i] = ::tolower(buffer[i]); // It is not a proper handling of unicode files here ... // ehm ... but it works in most cases. char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; while (cur != end) { if (*cur) *cur2++ = *cur; ++cur; } *cur2 = '\0'; for (unsigned int i = 0; i < numTokens;++i) { ai_assert(NULL != tokens[i]); if (::strstr(buffer,tokens[i])) { DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]); return true; } } } return false; } // ------------------------------------------------------------------------------------------------ // Simple check for file extension /*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, const char* ext0, const char* ext1, const char* ext2) { std::string::size_type pos = pFile.find_last_of('.'); // no file extension - can't read if ( pos == std::string::npos) return false; const char* ext_real = & pFile[ pos+1 ]; if ( !ASSIMP_stricmp(ext_real,ext0) ) return true; // check for other, optional, file extensions if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) return true; if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) return true; return false; } // ------------------------------------------------------------------------------------------------ // Get file extension from path /*static*/ std::string BaseImporter::GetExtension (const std::string& pFile) { std::string::size_type pos = pFile.find_last_of('.'); // no file extension at all if ( pos == std::string::npos) return ""; std::string ret = pFile.substr(pos+1); std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint return ret; } // ------------------------------------------------------------------------------------------------ // Check for magic bytes at the beginning of the file. /* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, const void* _magic, unsigned int num, unsigned int offset, unsigned int size) { ai_assert(size <= 16 && _magic); if (!pIOHandler) { return false; } union { const char* magic; const uint16_t* magic_u16; const uint32_t* magic_u32; }; magic = reinterpret_cast(_magic); boost::scoped_ptr pStream (pIOHandler->Open(pFile)); if (pStream.get() ) { // skip to offset pStream->Seek(offset,aiOrigin_SET); // read 'size' characters from the file union { char data[16]; uint16_t data_u16[8]; uint32_t data_u32[4]; }; if (size != pStream->Read(data,1,size)) { return false; } for (unsigned int i = 0; i < num; ++i) { // also check against big endian versions of tokens with size 2,4 // that's just for convinience, the chance that we cause conflicts // is quite low and it can save some lines and prevent nasty bugs if (2 == size) { uint16_t rev = *magic_u16; ByteSwap::Swap(&rev); if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { return true; } } else if (4 == size) { uint32_t rev = *magic_u32; ByteSwap::Swap(&rev); if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { return true; } } else { // any length ... just compare if (!memcmp(magic,data,size)) { return true; } } magic += size; } } return false; } #include "../contrib/ConvertUTF/ConvertUTF.h" // ------------------------------------------------------------------------------------------------ void ReportResult(ConversionResult res) { if (res == sourceExhausted) { DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails"); } else if (res == sourceIllegal) { DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails"); } } // ------------------------------------------------------------------------------------------------ // Convert to UTF8 data void BaseImporter::ConvertToUTF8(std::vector& data) { ConversionResult result; if (data.size() < 8) { throw DeadlyImportError("File is too small"); } // UTF 8 with BOM if ((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { DefaultLogger::get()->debug("Found UTF-8 BOM ..."); std::copy(data.begin()+3,data.end(),data.begin()); data.resize(data.size()-3); return; } // UTF 32 BE with BOM if (*((uint32_t*)&data.front()) == 0xFFFE0000) { // swap the endianess .. for (uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { AI_SWAP4P(p); } } // UTF 32 LE with BOM if (*((uint32_t*)&data.front()) == 0x0000FFFE) { DefaultLogger::get()->debug("Found UTF-32 BOM ..."); const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1; char* dstart,*dend; std::vector output; do { output.resize(output.size()?output.size()*3/2:data.size()/2); dstart = &output.front(),dend = &output.back()+1; result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); } while (result == targetExhausted); ReportResult(result); // copy to output buffer. const size_t outlen = (size_t)(dstart-&output.front()); data.assign(output.begin(),output.begin()+outlen); return; } // UTF 16 BE with BOM if (*((uint16_t*)&data.front()) == 0xFFFE) { // swap the endianess .. for (uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { ByteSwap::Swap2(p); } } // UTF 16 LE with BOM if (*((uint16_t*)&data.front()) == 0xFEFF) { DefaultLogger::get()->debug("Found UTF-16 BOM ..."); const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)&data.back()+1; char* dstart,*dend; std::vector output; do { output.resize(output.size()?output.size()*3/2:data.size()*3/4); dstart = &output.front(),dend = &output.back()+1; result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); } while (result == targetExhausted); ReportResult(result); // copy to output buffer. const size_t outlen = (size_t)(dstart-&output.front()); data.assign(output.begin(),output.begin()+outlen); return; } } // ------------------------------------------------------------------------------------------------ void BaseImporter::TextFileToBuffer(IOStream* stream, std::vector& data) { ai_assert(NULL != stream); const size_t fileSize = stream->FileSize(); if (!fileSize) { throw DeadlyImportError("File is empty"); } data.reserve(fileSize+1); data.resize(fileSize); if (fileSize != stream->Read( &data[0], 1, fileSize)) { throw DeadlyImportError("File read error"); } ConvertToUTF8(data); // append a binary zero to simplify string parsing data.push_back(0); } // ------------------------------------------------------------------------------------------------ namespace Assimp { // Represents an import request struct LoadRequest { LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id) { if (_map) map = *_map; } const std::string file; unsigned int flags; unsigned int refCnt; aiScene* scene; bool loaded; BatchLoader::PropertyMap map; unsigned int id; bool operator== (const std::string& f) { return file == f; } }; } // ------------------------------------------------------------------------------------------------ // BatchLoader::pimpl data structure struct Assimp::BatchData { BatchData() : pIOSystem(0) , pImporter(0) , next_id(0xffff) {} // IO system to be used for all imports IOSystem* pIOSystem; // Importer used to load all meshes Importer* pImporter; // List of all imports std::list requests; // Base path std::string pathBase; // Id for next item unsigned int next_id; }; // ------------------------------------------------------------------------------------------------ BatchLoader::BatchLoader(IOSystem* pIO) { ai_assert(NULL != pIO); data = new BatchData(); data->pIOSystem = pIO; data->pImporter = new Importer(); data->pImporter->SetIOHandler(data->pIOSystem); } // ------------------------------------------------------------------------------------------------ BatchLoader::~BatchLoader() { // delete all scenes wthat have not been polled by the user for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { delete (*it).scene; } data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */ delete data->pImporter; delete data; } // ------------------------------------------------------------------------------------------------ unsigned int BatchLoader::AddLoadRequest (const std::string& file, unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) { ai_assert(!file.empty()); // check whether we have this loading request already std::list::iterator it; for (it = data->requests.begin();it != data->requests.end(); ++it) { // Call IOSystem's path comparison function here if (data->pIOSystem->ComparePaths((*it).file,file)) { if (map) { if (!((*it).map == *map)) continue; } else if (!(*it).map.empty()) continue; (*it).refCnt++; return (*it).id; } } // no, we don't have it. So add it to the queue ... data->requests.push_back(LoadRequest(file,steps,map,data->next_id)); return data->next_id++; } // ------------------------------------------------------------------------------------------------ aiScene* BatchLoader::GetImport (unsigned int which) { for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { if ((*it).id == which && (*it).loaded) { aiScene* sc = (*it).scene; if (!(--(*it).refCnt)) { data->requests.erase(it); } return sc; } } return NULL; } // ------------------------------------------------------------------------------------------------ void BatchLoader::LoadAll() { // no threaded implementation for the moment for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { // force validation in debug builds unsigned int pp = (*it).flags; #ifdef _DEBUG pp |= aiProcess_ValidateDataStructure; #endif // setup config properties if necessary data->pImporter->pimpl->mFloatProperties = (*it).map.floats; data->pImporter->pimpl->mIntProperties = (*it).map.ints; data->pImporter->pimpl->mStringProperties = (*it).map.strings; if (!DefaultLogger::isNullLogger()) { DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%"); DefaultLogger::get()->info("File: " + (*it).file); } data->pImporter->ReadFile((*it).file,pp); (*it).scene = const_cast(data->pImporter->GetOrphanedScene()); (*it).loaded = true; DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%"); } } 3rdparty/assimp/code/BaseImporter.h000066400000000000000000000442251217616171500176170ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file Definition of the base class for all importer worker classes. */ #ifndef INCLUDED_AI_BASEIMPORTER_H #define INCLUDED_AI_BASEIMPORTER_H #include "Exceptional.h" #include #include #include #include "./../include/aiTypes.h" struct aiScene; namespace Assimp { class IOSystem; class Importer; class BaseImporter; class BaseProcess; class SharedPostProcessInfo; class IOStream; // utility to do char4 to uint32 in a portable manner #define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ (string[1] << 16) + (string[2] << 8) + string[3])) // --------------------------------------------------------------------------- template struct ScopeGuard { ScopeGuard(T* obj) : obj(obj), mdismiss() {} ~ScopeGuard () throw() { if (!mdismiss) { delete obj; } obj = NULL; } T* dismiss() { mdismiss=true; return obj; } operator T*() { return obj; } T* operator -> () { return obj; } private: T* obj; bool mdismiss; }; //! @cond never // --------------------------------------------------------------------------- /** @brief Internal PIMPL implementation for Assimp::Importer * * Using this idiom here allows us to drop the dependency from * std::vector and std::map in the public headers. Furthermore we are dropping * any STL interface problems caused by mismatching STL settings. All * size calculation are now done by us, not the app heap. */ class ASSIMP_API ImporterPimpl { public: // Data type to store the key hash typedef unsigned int KeyType; // typedefs for our three configuration maps. // We don't need more, so there is no need for a generic solution typedef std::map IntPropertyMap; typedef std::map FloatPropertyMap; typedef std::map StringPropertyMap; public: /** IO handler to use for all file accesses. */ IOSystem* mIOHandler; bool mIsDefaultHandler; /** Progress handler for feedback. */ ProgressHandler* mProgressHandler; bool mIsDefaultProgressHandler; /** Format-specific importer worker objects - one for each format we can read.*/ std::vector mImporter; /** Post processing steps we can apply at the imported data. */ std::vector mPostProcessingSteps; /** The imported data, if ReadFile() was successful, NULL otherwise. */ aiScene* mScene; /** The error description, if there was one. */ std::string mErrorString; /** List of integer properties */ IntPropertyMap mIntProperties; /** List of floating-point properties */ FloatPropertyMap mFloatProperties; /** List of string properties */ StringPropertyMap mStringProperties; /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step * to be executed before and after every single postprocess step */ bool bExtraVerbose; /** Used by post-process steps to share data */ SharedPostProcessInfo* mPPShared; }; //! @endcond // --------------------------------------------------------------------------- /** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface * for all importer worker classes. * * The interface defines two functions: CanRead() is used to check if the * importer can handle the format of the given file. If an implementation of * this function returns true, the importer then calls ReadFile() which * imports the given file. ReadFile is not overridable, it just calls * InternReadFile() and catches any ImportErrorException that might occur. */ class ASSIMP_API BaseImporter { friend class Importer; protected: /** Constructor to be privately used by #Importer */ BaseImporter(); /** Destructor, private as well */ virtual ~BaseImporter(); public: // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * * The implementation should be as quick as possible. A check for * the file extension is enough. If no suitable loader is found with * this strategy, CanRead() is called again, the 'checkSig' parameter * set to true this time. Now the implementation is expected to * perform a full check of the file structure, possibly searching the * first bytes of the file for magic identifiers or keywords. * * @param pFile Path and file name of the file to be examined. * @param pIOHandler The IO handler to use for accessing any file. * @param checkSig Set to true if this method is called a second time. * This time, the implementation may take more time to examine the * contents of the file to be loaded for magic bytes, keywords, etc * to be able to load files with unknown/not existent file extensions. * @return true if the class can read this file, false if not. */ virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const = 0; // ------------------------------------------------------------------- /** Imports the given file and returns the imported data. * If the import succeeds, ownership of the data is transferred to * the caller. If the import fails, NULL is returned. The function * takes care that any partially constructed data is destroyed * beforehand. * * @param pImp #Importer object hosting this loader. * @param pFile Path of the file to be imported. * @param pIOHandler IO-Handler used to open this and possible other files. * @return The imported data or NULL if failed. If it failed a * human-readable error description can be retrieved by calling * GetErrorText() * * @note This function is not intended to be overridden. Implement * InternReadFile() to do the import. If an exception is thrown somewhere * in InternReadFile(), this function will catch it and transform it into * a suitable response to the caller. */ aiScene* ReadFile( const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler ); // ------------------------------------------------------------------- /** Returns the error description of the last error that occured. * @return A description of the last error that occured. An empty * string if there was no error. */ const std::string& GetErrorText() const { return mErrorText; } // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. * @param pImp Importer instance */ virtual void SetupProperties( const Importer* pImp ); protected: // ------------------------------------------------------------------- /** Called by Importer::GetExtensionList() for each loaded importer. * Implementations are expected to insert() all file extensions * handled by them into the extension set. A loader capable of * reading certain files with the extension BLA would place the * string bla (lower-case!) in the output set. * @param extensions Output set. */ virtual void GetExtensionList( std::set& extensions ) = 0; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. The * function is expected to throw an ImportErrorException if there is * an error. If it terminates normally, the data in aiScene is * expected to be correct. Override this function to implement the * actual importing. *
* The output scene must meet the following requirements:
*
    *
  • At least a root node must be there, even if its only purpose * is to reference one mesh.
  • *
  • aiMesh::mPrimitiveTypes may be 0. The types of primitives * in the mesh are determined automatically in this case.
  • *
  • the vertex data is stored in a pseudo-indexed "verbose" format. * In fact this means that every vertex that is referenced by * a face is unique. Or the other way round: a vertex index may * not occur twice in a single aiMesh.
  • *
  • aiAnimation::mDuration may be -1. Assimp determines the length * of the animation automatically in this case as the length of * the longest animation channel.
  • *
  • aiMesh::mBitangents may be NULL if tangents and normals are * given. In this case bitangents are computed as the cross product * between normal and tangent.
  • *
  • There needn't be a material. If none is there a default material * is generated. However, it is recommended practice for loaders * to generate a default material for yourself that matches the * default material setting for the file format better than Assimp's * generic default material. Note that default materials *should* * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) * texture.
  • *
* If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:
    *
  • at least one mesh must be there
  • *
  • there may be no meshes with 0 vertices or faces
  • *
* This won't be checked (except by the validation step): Assimp will * crash if one of the conditions is not met! * * @param pFile Path of the file to be imported. * @param pScene The scene object to hold the imported data. * NULL is not a valid parameter. * @param pIOHandler The IO handler to use for any file access. * NULL is not a valid parameter. */ virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) = 0; public: // static utilities // ------------------------------------------------------------------- /** A utility for CanRead(). * * The function searches the header of a file for a specific token * and returns true if this token is found. This works for text * files only. There is a rudimentary handling of UNICODE files. * The comparison is case independent. * * @param pIOSystem IO System to work with * @param file File name of the file * @param tokens List of tokens to search for * @param numTokens Size of the token array * @param searchBytes Number of bytes to be searched for the tokens. */ static bool SearchFileHeaderForToken( IOSystem* pIOSystem, const std::string& file, const char** tokens, unsigned int numTokens, unsigned int searchBytes = 200); // ------------------------------------------------------------------- /** @brief Check whether a file has a specific file extension * @param pFile Input file * @param ext0 Extension to check for. Lowercase characters only, no dot! * @param ext1 Optional second extension * @param ext2 Optional third extension * @note Case-insensitive */ static bool SimpleExtensionCheck ( const std::string& pFile, const char* ext0, const char* ext1 = NULL, const char* ext2 = NULL); // ------------------------------------------------------------------- /** @brief Extract file extension from a string * @param pFile Input file * @return Extension without trailing dot, all lowercase */ static std::string GetExtension ( const std::string& pFile); // ------------------------------------------------------------------- /** @brief Check whether a file starts with one or more magic tokens * @param pFile Input file * @param pIOHandler IO system to be used * @param magic n magic tokens * @params num Size of magic * @param offset Offset from file start where tokens are located * @param Size of one token, in bytes. Maximally 16 bytes. * @return true if one of the given tokens was found * * @note For convinence, the check is also performed for the * byte-swapped variant of all tokens (big endian). Only for * tokens of size 2,4. */ static bool CheckMagicToken( IOSystem* pIOHandler, const std::string& pFile, const void* magic, unsigned int num, unsigned int offset = 0, unsigned int size = 4); // ------------------------------------------------------------------- /** An utility for all text file loaders. It converts a file to our * UTF8 character set. Errors are reported, but ignored. * * @param data File buffer to be converted to UTF8 data. The buffer * is resized as appropriate. */ static void ConvertToUTF8( std::vector& data); // ------------------------------------------------------------------- /** Utility for text file loaders which copies the contents of the * file into a memory buffer and converts it to our UTF8 * representation. * @param stream Stream to read from. * @param data Output buffer to be resized and filled with the * converted text file data. The buffer is terminated with * a binary 0. */ static void TextFileToBuffer( IOStream* stream, std::vector& data); protected: /** Error description in case there was one. */ std::string mErrorText; /** Currently set progress handler */ ProgressHandler* progress; }; struct BatchData; // --------------------------------------------------------------------------- /** FOR IMPORTER PLUGINS ONLY: A helper class for the pleasure of importers * which need to load many extern meshes recursively. * * The class uses several threads to load these meshes (or at least it * could, this has not yet been implemented at the moment). * * @note The class may not be used by more than one thread*/ class ASSIMP_API BatchLoader { // friend of Importer public: //! @cond never // ------------------------------------------------------------------- /** Wraps a full list of configuration properties for an importer. * Properties can be set using SetGenericProperty */ struct PropertyMap { ImporterPimpl::IntPropertyMap ints; ImporterPimpl::FloatPropertyMap floats; ImporterPimpl::StringPropertyMap strings; bool operator == (const PropertyMap& prop) const { // fixme: really isocpp? gcc complains return ints == prop.ints && floats == prop.floats && strings == prop.strings; } bool empty () const { return ints.empty() && floats.empty() && strings.empty(); } }; //! @endcond public: // ------------------------------------------------------------------- /** Construct a batch loader from a given IO system to be used * to acess external files */ BatchLoader(IOSystem* pIO); ~BatchLoader(); // ------------------------------------------------------------------- /** Add a new file to the list of files to be loaded. * @param file File to be loaded * @param steps Post-processing steps to be executed on the file * @param map Optional configuration properties * @return 'Load request channel' - an unique ID that can later * be used to access the imported file data. * @see GetImport */ unsigned int AddLoadRequest ( const std::string& file, unsigned int steps = 0, const PropertyMap* map = NULL ); // ------------------------------------------------------------------- /** Get an imported scene. * This polls the import from the internal request list. * If an import is requested several times, this function * can be called several times, too. * * @param which LRWC returned by AddLoadRequest(). * @return NULL if there is no scene with this file name * in the queue of the scene hasn't been loaded yet. */ aiScene* GetImport( unsigned int which ); // ------------------------------------------------------------------- /** Waits until all scenes have been loaded. This returns * immediately if no scenes are queued.*/ void LoadAll(); private: // No need to have that in the public API ... BatchData* data; }; } // end of namespace Assimp #endif // AI_BASEIMPORTER_H_INC 3rdparty/assimp/code/BaseProcess.cpp000066400000000000000000000063651217616171500177720ustar00rootroot00000000000000/* --------------------------------------------------------------------------- Open Asset Import Library (ASSIMP) --------------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ /** @file Implementation of BaseProcess */ #include "AssimpPCH.h" #include "BaseImporter.h" #include "BaseProcess.h" using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer BaseProcess::BaseProcess() : shared() , progress() { } // ------------------------------------------------------------------------------------------------ // Destructor, private as well BaseProcess::~BaseProcess() { // nothing to do here } // ------------------------------------------------------------------------------------------------ void BaseProcess::ExecuteOnScene( Importer* pImp) { ai_assert(NULL != pImp && NULL != pImp->pimpl->mScene); progress = pImp->GetProgressHandler(); ai_assert(progress); SetupProperties( pImp ); // catch exceptions thrown inside the PostProcess-Step try { Execute(pImp->pimpl->mScene); } catch( const std::exception& err ) { // extract error description pImp->pimpl->mErrorString = err.what(); DefaultLogger::get()->error(pImp->pimpl->mErrorString); // and kill the partially imported data delete pImp->pimpl->mScene; pImp->pimpl->mScene = NULL; } } // ------------------------------------------------------------------------------------------------ void BaseProcess::SetupProperties(const Importer*) { // the default implementation does nothing } 3rdparty/assimp/code/BaseProcess.h000066400000000000000000000206341217616171500174320ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file Base class of all import post processing steps */ #ifndef INCLUDED_AI_BASEPROCESS_H #define INCLUDED_AI_BASEPROCESS_H #include #include "../include/aiTypes.h" #include "GenericProperty.h" struct aiScene; namespace Assimp { class Importer; // --------------------------------------------------------------------------- /** Helper class to allow post-processing steps to interact with each other. * * The class maintains a simple property list that can be used by pp-steps * to provide additional information to other steps. This is primarily * intended for cross-step optimizations. */ class ASSIMP_API SharedPostProcessInfo { public: struct Base { virtual ~Base() {} }; //! Represents data that is allocated on the heap, thus needs to be deleted template struct THeapData : public Base { THeapData(T* in) : data (in) {} ~THeapData() { delete data; } T* data; }; //! Represents static, by-value data not allocated on the heap template struct TStaticData : public Base { TStaticData(T in) : data (in) {} ~TStaticData() {} T data; }; // some typedefs for cleaner code typedef unsigned int KeyType; typedef std::map PropertyMap; public: //! Destructor ~SharedPostProcessInfo() { Clean(); } //! Remove all stored properties from the table void Clean() { // invoke the virtual destructor for all stored properties for (PropertyMap::iterator it = pmap.begin(), end = pmap.end(); it != end; ++it) { delete (*it).second; } pmap.clear(); } //! Add a heap property to the list template void AddProperty( const char* name, T* in ){ AddProperty(name,(Base*)new THeapData(in)); } //! Add a static by-value property to the list template void AddProperty( const char* name, T in ){ AddProperty(name,(Base*)new TStaticData(in)); } //! Get a heap property template bool GetProperty( const char* name, T*& out ) const { THeapData* t = (THeapData*)GetPropertyInternal(name); if (!t) { out = NULL; return false; } out = t->data; return true; } //! Get a static, by-value property template bool GetProperty( const char* name, T& out ) const { TStaticData* t = (TStaticData*)GetPropertyInternal(name); if (!t)return false; out = t->data; return true; } //! Remove a property of a specific type void RemoveProperty( const char* name) { SetGenericPropertyPtr(pmap,name,NULL); } private: void AddProperty( const char* name, Base* data) { SetGenericPropertyPtr(pmap,name,data); } Base* GetPropertyInternal( const char* name) const { return GetGenericProperty(pmap,name,NULL); } private: //! Map of all stored properties PropertyMap pmap; }; #if 0 // --------------------------------------------------------------------------- /** @brief Represents a dependency table for a postprocessing steps. * * For future use. */ struct PPDependencyTable { unsigned int execute_me_before_these; unsigned int execute_me_after_these; unsigned int only_if_these_are_not_specified; unsigned int mutually_exclusive_with; }; #endif #define AI_SPP_SPATIAL_SORT "$Spat" // --------------------------------------------------------------------------- /** The BaseProcess defines a common interface for all post processing steps. * A post processing step is run after a successful import if the caller * specified the corresponding flag when calling ReadFile(). * Enum #aiPostProcessSteps defines which flags are available. * After a successful import the Importer iterates over its internal array * of processes and calls IsActive() on each process to evaluate if the step * should be executed. If the function returns true, the class' Execute() * function is called subsequently. */ class ASSIMP_API BaseProcess { friend class Importer; public: /** Constructor to be privately used by Importer */ BaseProcess(); /** Destructor, private as well */ virtual ~BaseProcess(); public: // ------------------------------------------------------------------- /** Returns whether the processing step is present in the given flag. * @param pFlags The processing flags the importer was called with. A * bitwise combination of #aiPostProcessSteps. * @return true if the process is present in this flag fields, * false if not. */ virtual bool IsActive( unsigned int pFlags) const = 0; // ------------------------------------------------------------------- /** Executes the post processing step on the given imported data. * The function deletes the scene if the postprocess step fails ( * the object pointer will be set to NULL). * @param pImp Importer instance (pImp->mScene must be valid) */ void ExecuteOnScene( Importer* pImp); // ------------------------------------------------------------------- /** Called prior to ExecuteOnScene(). * The function is a request to the process to update its configuration * basing on the Importer's configuration property list. */ virtual void SetupProperties(const Importer* pImp); // ------------------------------------------------------------------- /** Executes the post processing step on the given imported data. * A process should throw an ImportErrorException* if it fails. * This method must be implemented by deriving classes. * @param pScene The imported data to work at. */ virtual void Execute( aiScene* pScene) = 0; // ------------------------------------------------------------------- /** Assign a new SharedPostProcessInfo to the step. This object * allows multiple postprocess steps to share data. * @param sh May be NULL */ inline void SetSharedData(SharedPostProcessInfo* sh) { shared = sh; } // ------------------------------------------------------------------- /** Get the shared data that is assigned to the step. */ inline SharedPostProcessInfo* GetSharedData() { return shared; } protected: /** See the doc of #SharedPostProcessInfo for more details */ SharedPostProcessInfo* shared; /** Currently active progress handler */ ProgressHandler* progress; }; } // end of namespace Assimp #endif // AI_BASEPROCESS_H_INC 3rdparty/assimp/code/BlenderDNA.cpp000066400000000000000000000272311217616171500174520ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file BlenderDNA.cpp * @brief Implementation of the Blender `DNA`, that is its own * serialized set of data structures. */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER #include "BlenderDNA.h" #include "StreamReader.h" #include "fast_atof.h" using namespace Assimp; using namespace Assimp::Blender; using namespace Assimp::Formatter; #define for_each BOOST_FOREACH bool match4(StreamReaderAny& stream, const char* string) { char tmp[] = { (stream).GetI1(), (stream).GetI1(), (stream).GetI1(), (stream).GetI1() }; return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); } struct Type { size_t size; std::string name; }; // ------------------------------------------------------------------------------------------------ void DNAParser :: Parse () { StreamReaderAny& stream = *db.reader.get(); DNA& dna = db.dna; if (!match4(stream,"SDNA")) { throw DeadlyImportError("BlenderDNA: Expected SDNA chunk"); } // name dictionary if (!match4(stream,"NAME")) { throw DeadlyImportError("BlenderDNA: Expected NAME field"); } std::vector names (stream.GetI4()); for_each(std::string& s, names) { while (char c = stream.GetI1()) { s += c; } } // type dictionary for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {} if (!match4(stream,"TYPE")) { throw DeadlyImportError("BlenderDNA: Expected TYPE field"); } std::vector types (stream.GetI4()); for_each(Type& s, types) { while (char c = stream.GetI1()) { s.name += c; } } // type length dictionary for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {} if (!match4(stream,"TLEN")) { throw DeadlyImportError("BlenderDNA: Expected TLEN field"); } for_each(Type& s, types) { s.size = stream.GetI2(); } // structures dictionary for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {} if (!match4(stream,"STRC")) { throw DeadlyImportError("BlenderDNA: Expected STRC field"); } size_t end = stream.GetI4(), fields = 0; dna.structures.reserve(end); for (size_t i = 0; i != end; ++i) { uint16_t n = stream.GetI2(); if (n >= types.size()) { throw DeadlyImportError((format(), "BlenderDNA: Invalid type index in structure name" ,n, " (there are only ", types.size(), " entries)" )); } // maintain separate indexes dna.indices[types[n].name] = dna.structures.size(); dna.structures.push_back(Structure()); Structure& s = dna.structures.back(); s.name = types[n].name; //s.index = dna.structures.size()-1; n = stream.GetI2(); s.fields.reserve(n); size_t offset = 0; for (size_t m = 0; m < n; ++m, ++fields) { uint16_t j = stream.GetI2(); if (j >= types.size()) { throw DeadlyImportError((format(), "BlenderDNA: Invalid type index in structure field ", j, " (there are only ", types.size(), " entries)" )); } s.fields.push_back(Field()); Field& f = s.fields.back(); f.offset = offset; f.type = types[j].name; f.size = types[j].size; j = stream.GetI2(); if (j >= names.size()) { throw DeadlyImportError((format(), "BlenderDNA: Invalid name index in structure field ", j, " (there are only ", names.size(), " entries)" )); } f.name = names[j]; f.flags = 0u; // pointers always specify the size of the pointee instead of their own. // The pointer asterisk remains a property of the lookup name. if (f.name[0] == '*') { f.size = db.i64bit ? 8 : 4; f.flags |= FieldFlag_Pointer; } // arrays, however, specify the size of a single element so we // need to parse the (possibly multi-dimensional) array declaration // in order to obtain the actual size of the array in the file. // Also we need to alter the lookup name to include no array // brackets anymore or size fixup won't work (if our size does // not match the size read from the DNA). if (*f.name.rbegin() == ']') { const std::string::size_type rb = f.name.find('['); if (rb == std::string::npos) { throw DeadlyImportError((format(), "BlenderDNA: Encountered invalid array declaration ", f.name )); } f.flags |= FieldFlag_Array; DNA::ExtractArraySize(f.name,f.array_sizes); f.name = f.name.substr(0,rb); f.size *= f.array_sizes[0] * f.array_sizes[1]; } // maintain separate indexes s.indices[f.name] = s.fields.size()-1; offset += f.size; } s.size = offset; } DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(), " structures with totally ",fields," fields")); #ifdef ASSIMP_BUILD_BLENDER_DEBUG dna.DumpToFile(); #endif dna.AddPrimitiveStructures(); dna.RegisterConverters(); } #ifdef ASSIMP_BUILD_BLENDER_DEBUG #include // ------------------------------------------------------------------------------------------------ void DNA :: DumpToFile() { // we dont't bother using the VFS here for this is only for debugging. // (and all your bases are belong to us). std::ofstream f("dna.txt"); if (f.fail()) { DefaultLogger::get()->error("Could not dump dna to dna.txt"); return; } f << "Field format: type name offset size" << "\n"; f << "Structure format: name size" << "\n"; for_each(const Structure& s, structures) { f << s.name << " " << s.size << "\n\n"; for_each(const Field& ff, s.fields) { f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl; } f << std::endl; } DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt"); } #endif // ------------------------------------------------------------------------------------------------ /*static*/ void DNA :: ExtractArraySize( const std::string& out, size_t array_sizes[2] ) { array_sizes[0] = array_sizes[1] = 1; std::string::size_type pos = out.find('['); if (pos++ == std::string::npos) { return; } array_sizes[0] = strtol10(&out[pos]); pos = out.find('[',pos); if (pos++ == std::string::npos) { return; } array_sizes[1] = strtol10(&out[pos]); } // ------------------------------------------------------------------------------------------------ boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( const Structure& structure, const FileDatabase& db ) const { std::map::const_iterator it = converters.find(structure.name); if (it == converters.end()) { return boost::shared_ptr< ElemBase >(); } boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); (structure.*((*it).second.second))(ret,db); return ret; } // ------------------------------------------------------------------------------------------------ DNA::FactoryPair DNA :: GetBlobToStructureConverter( const Structure& structure, const FileDatabase& ) const { std::map::const_iterator it = converters.find(structure.name); return it == converters.end() ? FactoryPair() : (*it).second; } // basing on http://www.blender.org/development/architecture/notes-on-sdna/ // ------------------------------------------------------------------------------------------------ void DNA :: AddPrimitiveStructures() { // NOTE: these are just dummies. Their presence enforces // Structure::Convert to be called on these // empty structures. These converters are special // overloads which scan the name of the structure and // perform the required data type conversion if one // of these special names is found in the structure // in question. indices["int"] = structures.size(); structures.push_back( Structure() ); structures.back().name = "int"; structures.back().size = 4; indices["short"] = structures.size(); structures.push_back( Structure() ); structures.back().name = "short"; structures.back().size = 2; indices["char"] = structures.size(); structures.push_back( Structure() ); structures.back().name = "char"; structures.back().size = 1; indices["float"] = structures.size(); structures.push_back( Structure() ); structures.back().name = "float"; structures.back().size = 4; indices["double"] = structures.size(); structures.push_back( Structure() ); structures.back().name = "double"; structures.back().size = 8; // no long, seemingly. } // ------------------------------------------------------------------------------------------------ void SectionParser :: Next() { stream.SetCurrentPos(current.start + current.size); const char tmp[] = { stream.GetI1(), stream.GetI1(), stream.GetI1(), stream.GetI1() }; current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); current.size = stream.GetI4(); current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); current.dna_index = stream.GetI4(); current.num = stream.GetI4(); current.start = stream.GetCurrentPos(); if (stream.GetRemainingSizeToLimit() < current.size) { throw DeadlyImportError("BLEND: invalid size of file block"); } #ifdef ASSIMP_BUILD_BLENDER_DEBUG DefaultLogger::get()->debug(current.id); #endif } #endif 3rdparty/assimp/code/BlenderDNA.h000066400000000000000000000710251217616171500171170ustar00rootroot00000000000000/* Open Asset Import Library (ASSIMP) ---------------------------------------------------------------------- Copyright (c) 2006-2010, ASSIMP Development Team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the ASSIMP team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ /** @file BlenderDNA.h * @brief Blender `DNA` (file format specification embedded in * blend file itself) loader. */ #ifndef INCLUDED_AI_BLEND_DNA_H #define INCLUDED_AI_BLEND_DNA_H #include "BaseImporter.h" #include "TinyFormatter.h" // enable verbose log output. really verbose, so be careful. #ifdef _DEBUG # define ASSIMP_BUILD_BLENDER_DEBUG #endif // #define ASSIMP_BUILD_BLENDER_NO_STATS namespace Assimp { template class StreamReader; typedef StreamReader StreamReaderAny; namespace Blender { class FileDatabase; struct FileBlockHead; template