nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file PretransformVertices.cpp nuclear@0: * @brief Implementation of the "PretransformVertices" post processing step nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #include "PretransformVertices.h" nuclear@0: #include "ProcessHelper.h" nuclear@0: #include "SceneCombiner.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: // some array offsets nuclear@0: #define AI_PTVS_VERTEX 0x0 nuclear@0: #define AI_PTVS_FACE 0x1 nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: PretransformVertices::PretransformVertices() nuclear@0: : configKeepHierarchy (false) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: PretransformVertices::~PretransformVertices() nuclear@0: { nuclear@0: // nothing to do here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the processing step is present in the given flag field. nuclear@0: bool PretransformVertices::IsActive( unsigned int pFlags) const nuclear@0: { nuclear@0: return (pFlags & aiProcess_PreTransformVertices) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup import configuration nuclear@0: void PretransformVertices::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY and AI_CONFIG_PP_PTV_NORMALIZE nuclear@0: configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0)); nuclear@0: configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Count the number of nodes nuclear@0: unsigned int PretransformVertices::CountNodes( aiNode* pcNode ) nuclear@0: { nuclear@0: unsigned int iRet = 1; nuclear@0: for (unsigned int i = 0;i < pcNode->mNumChildren;++i) nuclear@0: { nuclear@0: iRet += CountNodes(pcNode->mChildren[i]); nuclear@0: } nuclear@0: return iRet; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Get a bitwise combination identifying the vertex format of a mesh nuclear@0: unsigned int PretransformVertices::GetMeshVFormat(aiMesh* pcMesh) nuclear@0: { nuclear@0: // the vertex format is stored in aiMesh::mBones for later retrieval. nuclear@0: // there isn't a good reason to compute it a few hundred times nuclear@0: // from scratch. The pointer is unused as animations are lost nuclear@0: // during PretransformVertices. nuclear@0: if (pcMesh->mBones) nuclear@0: return (unsigned int)(uint64_t)pcMesh->mBones; nuclear@0: nuclear@0: nuclear@0: const unsigned int iRet = GetMeshVFormatUnique(pcMesh); nuclear@0: nuclear@0: // store the value for later use nuclear@0: pcMesh->mBones = (aiBone**)(uint64_t)iRet; nuclear@0: return iRet; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Count the number of vertices in the whole scene and a given nuclear@0: // material index nuclear@0: void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, nuclear@0: unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices) nuclear@0: { nuclear@0: for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) nuclear@0: { nuclear@0: aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; nuclear@0: if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) nuclear@0: { nuclear@0: *piVertices += pcMesh->mNumVertices; nuclear@0: *piFaces += pcMesh->mNumFaces; nuclear@0: } nuclear@0: } nuclear@0: for (unsigned int i = 0;i < pcNode->mNumChildren;++i) nuclear@0: { nuclear@0: CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat, nuclear@0: iVFormat,piFaces,piVertices); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Collect vertex/face data nuclear@0: void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, nuclear@0: unsigned int iVFormat, aiMesh* pcMeshOut, nuclear@0: unsigned int aiCurrent[2], unsigned int* num_refs) nuclear@0: { nuclear@0: // No need to multiply if there's no transformation nuclear@0: const bool identity = pcNode->mTransformation.IsIdentity(); nuclear@0: for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) nuclear@0: { nuclear@0: aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ]; nuclear@0: if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) nuclear@0: { nuclear@0: // Decrement mesh reference counter nuclear@0: unsigned int& num_ref = num_refs[pcNode->mMeshes[i]]; nuclear@0: ai_assert(0 != num_ref); nuclear@0: --num_ref; nuclear@0: nuclear@0: if (identity) { nuclear@0: // copy positions without modifying them nuclear@0: ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX], nuclear@0: pcMesh->mVertices, nuclear@0: pcMesh->mNumVertices * sizeof(aiVector3D)); nuclear@0: nuclear@0: if (iVFormat & 0x2) { nuclear@0: // copy normals without modifying them nuclear@0: ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX], nuclear@0: pcMesh->mNormals, nuclear@0: pcMesh->mNumVertices * sizeof(aiVector3D)); nuclear@0: } nuclear@0: if (iVFormat & 0x4) nuclear@0: { nuclear@0: // copy tangents without modifying them nuclear@0: ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX], nuclear@0: pcMesh->mTangents, nuclear@0: pcMesh->mNumVertices * sizeof(aiVector3D)); nuclear@0: // copy bitangents without modifying them nuclear@0: ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX], nuclear@0: pcMesh->mBitangents, nuclear@0: pcMesh->mNumVertices * sizeof(aiVector3D)); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // copy positions, transform them to worldspace nuclear@0: for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { nuclear@0: pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n]; nuclear@0: } nuclear@0: aiMatrix4x4 mWorldIT = pcNode->mTransformation; nuclear@0: mWorldIT.Inverse().Transpose(); nuclear@0: nuclear@0: // TODO: implement Inverse() for aiMatrix3x3 nuclear@0: aiMatrix3x3 m = aiMatrix3x3(mWorldIT); nuclear@0: nuclear@0: if (iVFormat & 0x2) nuclear@0: { nuclear@0: // copy normals, transform them to worldspace nuclear@0: for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { nuclear@0: pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = nuclear@0: (m * pcMesh->mNormals[n]).Normalize(); nuclear@0: } nuclear@0: } nuclear@0: if (iVFormat & 0x4) nuclear@0: { nuclear@0: // copy tangents and bitangents, transform them to worldspace nuclear@0: for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { nuclear@0: pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize(); nuclear@0: pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: unsigned int p = 0; nuclear@0: while (iVFormat & (0x100 << p)) nuclear@0: { nuclear@0: // copy texture coordinates nuclear@0: memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX], nuclear@0: pcMesh->mTextureCoords[p], nuclear@0: pcMesh->mNumVertices * sizeof(aiVector3D)); nuclear@0: ++p; nuclear@0: } nuclear@0: p = 0; nuclear@0: while (iVFormat & (0x1000000 << p)) nuclear@0: { nuclear@0: // copy vertex colors nuclear@0: memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX], nuclear@0: pcMesh->mColors[p], nuclear@0: pcMesh->mNumVertices * sizeof(aiColor4D)); nuclear@0: ++p; nuclear@0: } nuclear@0: // now we need to copy all faces. since we will delete the source mesh afterwards, nuclear@0: // we don't need to reallocate the array of indices except if this mesh is nuclear@0: // referenced multiple times. nuclear@0: for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck) nuclear@0: { nuclear@0: aiFace& f_src = pcMesh->mFaces[planck]; nuclear@0: aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck]; nuclear@0: nuclear@0: const unsigned int num_idx = f_src.mNumIndices; nuclear@0: nuclear@0: f_dst.mNumIndices = num_idx; nuclear@0: nuclear@0: unsigned int* pi; nuclear@0: if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */ nuclear@0: pi = f_dst.mIndices = f_src.mIndices; nuclear@0: nuclear@0: // offset all vertex indices nuclear@0: for (unsigned int hahn = 0; hahn < num_idx;++hahn){ nuclear@0: pi[hahn] += aiCurrent[AI_PTVS_VERTEX]; nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: pi = f_dst.mIndices = new unsigned int[num_idx]; nuclear@0: nuclear@0: // copy and offset all vertex indices nuclear@0: for (unsigned int hahn = 0; hahn < num_idx;++hahn){ nuclear@0: pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Update the mPrimitiveTypes member of the mesh nuclear@0: switch (pcMesh->mFaces[planck].mNumIndices) nuclear@0: { nuclear@0: case 0x1: nuclear@0: pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT; nuclear@0: break; nuclear@0: case 0x2: nuclear@0: pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE; nuclear@0: break; nuclear@0: case 0x3: nuclear@0: pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; nuclear@0: break; nuclear@0: default: nuclear@0: pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON; nuclear@0: break; nuclear@0: }; nuclear@0: } nuclear@0: aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices; nuclear@0: aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // append all children of us nuclear@0: for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { nuclear@0: CollectData(pcScene,pcNode->mChildren[i],iMat, nuclear@0: iVFormat,pcMeshOut,aiCurrent,num_refs); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Get a list of all vertex formats that occur for a given material index nuclear@0: // The output list contains duplicate elements nuclear@0: void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat, nuclear@0: std::list& aiOut) nuclear@0: { nuclear@0: for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) nuclear@0: { nuclear@0: aiMesh* pcMesh = pcScene->mMeshes[ i ]; nuclear@0: if (iMat == pcMesh->mMaterialIndex) { nuclear@0: aiOut.push_back(GetMeshVFormat(pcMesh)); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Compute the absolute transformation matrices of each node nuclear@0: void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode ) nuclear@0: { nuclear@0: if (pcNode->mParent) { nuclear@0: pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation; nuclear@0: } nuclear@0: nuclear@0: for (unsigned int i = 0;i < pcNode->mNumChildren;++i) { nuclear@0: ComputeAbsoluteTransform(pcNode->mChildren[i]); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Apply the node transformation to a mesh nuclear@0: void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat) nuclear@0: { nuclear@0: // Check whether we need to transform the coordinates at all nuclear@0: if (!mat.IsIdentity()) { nuclear@0: nuclear@0: if (mesh->HasPositions()) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: mesh->mVertices[i] = mat * mesh->mVertices[i]; nuclear@0: } nuclear@0: } nuclear@0: if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) { nuclear@0: aiMatrix4x4 mWorldIT = mat; nuclear@0: mWorldIT.Inverse().Transpose(); nuclear@0: nuclear@0: // TODO: implement Inverse() for aiMatrix3x3 nuclear@0: aiMatrix3x3 m = aiMatrix3x3(mWorldIT); nuclear@0: nuclear@0: if (mesh->HasNormals()) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize(); nuclear@0: } nuclear@0: } nuclear@0: if (mesh->HasTangentsAndBitangents()) { nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { nuclear@0: mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize(); nuclear@0: mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Simple routine to build meshes in worldspace, no further optimization nuclear@0: void PretransformVertices::BuildWCSMeshes(std::vector& out, aiMesh** in, nuclear@0: unsigned int numIn, aiNode* node) nuclear@0: { nuclear@0: // NOTE: nuclear@0: // aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy nuclear@0: // aiMesh::mBones store reference to abs. transform we multiplied with nuclear@0: nuclear@0: // process meshes nuclear@0: for (unsigned int i = 0; i < node->mNumMeshes;++i) { nuclear@0: aiMesh* mesh = in[node->mMeshes[i]]; nuclear@0: nuclear@0: // check whether we can operate on this mesh nuclear@0: if (!mesh->mBones || *reinterpret_cast(mesh->mBones) == node->mTransformation) { nuclear@0: // yes, we can. nuclear@0: mesh->mBones = reinterpret_cast (&node->mTransformation); nuclear@0: mesh->mNumBones = UINT_MAX; nuclear@0: } nuclear@0: else { nuclear@0: nuclear@0: // try to find us in the list of newly created meshes nuclear@0: for (unsigned int n = 0; n < out.size(); ++n) { nuclear@0: aiMesh* ctz = out[n]; nuclear@0: if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast(ctz->mBones) == node->mTransformation) { nuclear@0: nuclear@0: // ok, use this one. Update node mesh index nuclear@0: node->mMeshes[i] = numIn + n; nuclear@0: } nuclear@0: } nuclear@0: if (node->mMeshes[i] < numIn) { nuclear@0: // Worst case. Need to operate on a full copy of the mesh nuclear@0: DefaultLogger::get()->info("PretransformVertices: Copying mesh due to mismatching transforms"); nuclear@0: aiMesh* ntz; nuclear@0: nuclear@0: const unsigned int tmp = mesh->mNumBones; // nuclear@0: mesh->mNumBones = 0; nuclear@0: SceneCombiner::Copy(&ntz,mesh); nuclear@0: mesh->mNumBones = tmp; nuclear@0: nuclear@0: ntz->mNumBones = node->mMeshes[i]; nuclear@0: ntz->mBones = reinterpret_cast (&node->mTransformation); nuclear@0: nuclear@0: out.push_back(ntz); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // call children nuclear@0: for (unsigned int i = 0; i < node->mNumChildren;++i) nuclear@0: BuildWCSMeshes(out,in,numIn,node->mChildren[i]); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Reset transformation matrices to identity nuclear@0: void PretransformVertices::MakeIdentityTransform(aiNode* nd) nuclear@0: { nuclear@0: nd->mTransformation = aiMatrix4x4(); nuclear@0: nuclear@0: // call children nuclear@0: for (unsigned int i = 0; i < nd->mNumChildren;++i) nuclear@0: MakeIdentityTransform(nd->mChildren[i]); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build reference counters for all meshes nuclear@0: void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs) nuclear@0: { nuclear@0: for (unsigned int i = 0; i< nd->mNumMeshes;++i) nuclear@0: refs[nd->mMeshes[i]]++; nuclear@0: nuclear@0: // call children nuclear@0: for (unsigned int i = 0; i < nd->mNumChildren;++i) nuclear@0: BuildMeshRefCountArray(nd->mChildren[i],refs); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: void PretransformVertices::Execute( aiScene* pScene) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("PretransformVerticesProcess begin"); nuclear@0: nuclear@0: // Return immediately if we have no meshes nuclear@0: if (!pScene->mNumMeshes) nuclear@0: return; nuclear@0: nuclear@0: const unsigned int iOldMeshes = pScene->mNumMeshes; nuclear@0: const unsigned int iOldAnimationChannels = pScene->mNumAnimations; nuclear@0: const unsigned int iOldNodes = CountNodes(pScene->mRootNode); nuclear@0: nuclear@0: // first compute absolute transformation matrices for all nodes nuclear@0: ComputeAbsoluteTransform(pScene->mRootNode); nuclear@0: nuclear@0: // Delete aiMesh::mBones for all meshes. The bones are nuclear@0: // removed during this step and we need the pointer as nuclear@0: // temporary storage nuclear@0: for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { nuclear@0: aiMesh* mesh = pScene->mMeshes[i]; nuclear@0: nuclear@0: for (unsigned int a = 0; a < mesh->mNumBones;++a) nuclear@0: delete mesh->mBones[a]; nuclear@0: nuclear@0: delete[] mesh->mBones; nuclear@0: mesh->mBones = NULL; nuclear@0: } nuclear@0: nuclear@0: // now build a list of output meshes nuclear@0: std::vector apcOutMeshes; nuclear@0: nuclear@0: // Keep scene hierarchy? It's an easy job in this case ... nuclear@0: // we go on and transform all meshes, if one is referenced by nodes nuclear@0: // with different absolute transformations a depth copy of the mesh nuclear@0: // is required. nuclear@0: if( configKeepHierarchy ) { nuclear@0: nuclear@0: // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones nuclear@0: BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode); nuclear@0: nuclear@0: // ... if new meshes have been generated, append them to the end of the scene nuclear@0: if (apcOutMeshes.size() > 0) { nuclear@0: aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()]; nuclear@0: nuclear@0: memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes); nuclear@0: memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size()); nuclear@0: nuclear@0: pScene->mNumMeshes += apcOutMeshes.size(); nuclear@0: delete[] pScene->mMeshes; pScene->mMeshes = npp; nuclear@0: } nuclear@0: nuclear@0: // now iterate through all meshes and transform them to worldspace nuclear@0: for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { nuclear@0: ApplyTransform(pScene->mMeshes[i],*reinterpret_cast( pScene->mMeshes[i]->mBones )); nuclear@0: nuclear@0: // prevent improper destruction nuclear@0: pScene->mMeshes[i]->mBones = NULL; nuclear@0: pScene->mMeshes[i]->mNumBones = 0; nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: nuclear@0: apcOutMeshes.reserve(pScene->mNumMaterials<<1u); nuclear@0: std::list aiVFormats; nuclear@0: nuclear@0: std::vector s(pScene->mNumMeshes,0); nuclear@0: BuildMeshRefCountArray(pScene->mRootNode,&s[0]); nuclear@0: nuclear@0: for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { nuclear@0: // get the list of all vertex formats for this material nuclear@0: aiVFormats.clear(); nuclear@0: GetVFormatList(pScene,i,aiVFormats); nuclear@0: aiVFormats.sort(); nuclear@0: aiVFormats.unique(); nuclear@0: for (std::list::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) { nuclear@0: unsigned int iVertices = 0; nuclear@0: unsigned int iFaces = 0; nuclear@0: CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices); nuclear@0: if (0 != iFaces && 0 != iVertices) nuclear@0: { nuclear@0: apcOutMeshes.push_back(new aiMesh()); nuclear@0: aiMesh* pcMesh = apcOutMeshes.back(); nuclear@0: pcMesh->mNumFaces = iFaces; nuclear@0: pcMesh->mNumVertices = iVertices; nuclear@0: pcMesh->mFaces = new aiFace[iFaces]; nuclear@0: pcMesh->mVertices = new aiVector3D[iVertices]; nuclear@0: pcMesh->mMaterialIndex = i; nuclear@0: if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices]; nuclear@0: if ((*j) & 0x4) nuclear@0: { nuclear@0: pcMesh->mTangents = new aiVector3D[iVertices]; nuclear@0: pcMesh->mBitangents = new aiVector3D[iVertices]; nuclear@0: } nuclear@0: iFaces = 0; nuclear@0: while ((*j) & (0x100 << iFaces)) nuclear@0: { nuclear@0: pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; nuclear@0: if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; nuclear@0: else pcMesh->mNumUVComponents[iFaces] = 2; nuclear@0: iFaces++; nuclear@0: } nuclear@0: iFaces = 0; nuclear@0: while ((*j) & (0x1000000 << iFaces)) nuclear@0: pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; nuclear@0: nuclear@0: // fill the mesh ... nuclear@0: unsigned int aiTemp[2] = {0,0}; nuclear@0: CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // If no meshes are referenced in the node graph it is possible that we get no output meshes. nuclear@0: if (apcOutMeshes.empty()) { nuclear@0: throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes"); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // now delete all meshes in the scene and build a new mesh list nuclear@0: for (unsigned int i = 0; i < pScene->mNumMeshes;++i) nuclear@0: { nuclear@0: aiMesh* mesh = pScene->mMeshes[i]; nuclear@0: mesh->mNumBones = 0; nuclear@0: mesh->mBones = NULL; nuclear@0: nuclear@0: // we're reusing the face index arrays. avoid destruction nuclear@0: for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { nuclear@0: mesh->mFaces[a].mNumIndices = 0; nuclear@0: mesh->mFaces[a].mIndices = NULL; nuclear@0: } nuclear@0: nuclear@0: delete mesh; nuclear@0: nuclear@0: // Invalidate the contents of the old mesh array. We will most nuclear@0: // likely have less output meshes now, so the last entries of nuclear@0: // the mesh array are not overridden. We set them to NULL to nuclear@0: // make sure the developer gets notified when his application nuclear@0: // attempts to access these fields ... nuclear@0: mesh = NULL; nuclear@0: } nuclear@0: nuclear@0: // It is impossible that we have more output meshes than nuclear@0: // input meshes, so we can easily reuse the old mesh array nuclear@0: pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); nuclear@0: for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { nuclear@0: pScene->mMeshes[i] = apcOutMeshes[i]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // remove all animations from the scene nuclear@0: for (unsigned int i = 0; i < pScene->mNumAnimations;++i) nuclear@0: delete pScene->mAnimations[i]; nuclear@0: delete[] pScene->mAnimations; nuclear@0: nuclear@0: pScene->mAnimations = NULL; nuclear@0: pScene->mNumAnimations = 0; nuclear@0: nuclear@0: // --- we need to keep all cameras and lights nuclear@0: for (unsigned int i = 0; i < pScene->mNumCameras;++i) nuclear@0: { nuclear@0: aiCamera* cam = pScene->mCameras[i]; nuclear@0: const aiNode* nd = pScene->mRootNode->FindNode(cam->mName); nuclear@0: ai_assert(NULL != nd); nuclear@0: nuclear@0: // multiply all properties of the camera with the absolute nuclear@0: // transformation of the corresponding node nuclear@0: cam->mPosition = nd->mTransformation * cam->mPosition; nuclear@0: cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt; nuclear@0: cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp; nuclear@0: } nuclear@0: nuclear@0: for (unsigned int i = 0; i < pScene->mNumLights;++i) nuclear@0: { nuclear@0: aiLight* l = pScene->mLights[i]; nuclear@0: const aiNode* nd = pScene->mRootNode->FindNode(l->mName); nuclear@0: ai_assert(NULL != nd); nuclear@0: nuclear@0: // multiply all properties of the camera with the absolute nuclear@0: // transformation of the corresponding node nuclear@0: l->mPosition = nd->mTransformation * l->mPosition; nuclear@0: l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection; nuclear@0: } nuclear@0: nuclear@0: if( !configKeepHierarchy ) { nuclear@0: nuclear@0: // now delete all nodes in the scene and build a new nuclear@0: // flat node graph with a root node and some level 1 children nuclear@0: delete pScene->mRootNode; nuclear@0: pScene->mRootNode = new aiNode(); nuclear@0: pScene->mRootNode->mName.Set(""); nuclear@0: nuclear@0: if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) nuclear@0: { nuclear@0: pScene->mRootNode->mNumMeshes = 1; nuclear@0: pScene->mRootNode->mMeshes = new unsigned int[1]; nuclear@0: pScene->mRootNode->mMeshes[0] = 0; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras; nuclear@0: aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; nuclear@0: nuclear@0: // generate mesh nodes nuclear@0: for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) nuclear@0: { nuclear@0: aiNode* pcNode = *nodes = new aiNode(); nuclear@0: pcNode->mParent = pScene->mRootNode; nuclear@0: pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i); nuclear@0: nuclear@0: // setup mesh indices nuclear@0: pcNode->mNumMeshes = 1; nuclear@0: pcNode->mMeshes = new unsigned int[1]; nuclear@0: pcNode->mMeshes[0] = i; nuclear@0: } nuclear@0: // generate light nodes nuclear@0: for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) nuclear@0: { nuclear@0: aiNode* pcNode = *nodes = new aiNode(); nuclear@0: pcNode->mParent = pScene->mRootNode; nuclear@0: pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i); nuclear@0: pScene->mLights[i]->mName = pcNode->mName; nuclear@0: } nuclear@0: // generate camera nodes nuclear@0: for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) nuclear@0: { nuclear@0: aiNode* pcNode = *nodes = new aiNode(); nuclear@0: pcNode->mParent = pScene->mRootNode; nuclear@0: pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i); nuclear@0: pScene->mCameras[i]->mName = pcNode->mName; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: // ... and finally set the transformation matrix of all nodes to identity nuclear@0: MakeIdentityTransform(pScene->mRootNode); nuclear@0: } nuclear@0: nuclear@0: if (configNormalize) { nuclear@0: // compute the boundary of all meshes nuclear@0: aiVector3D min,max; nuclear@0: MinMaxChooser ()(min,max); nuclear@0: nuclear@0: for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { nuclear@0: aiMesh* m = pScene->mMeshes[a]; nuclear@0: for (unsigned int i = 0; i < m->mNumVertices;++i) { nuclear@0: min = std::min(m->mVertices[i],min); nuclear@0: max = std::max(m->mVertices[i],max); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // find the dominant axis nuclear@0: aiVector3D d = max-min; nuclear@0: const float div = std::max(d.x,std::max(d.y,d.z))*0.5f; nuclear@0: nuclear@0: d = min+d*0.5f; nuclear@0: for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) { nuclear@0: aiMesh* m = pScene->mMeshes[a]; nuclear@0: for (unsigned int i = 0; i < m->mNumVertices;++i) { nuclear@0: m->mVertices[i] = (m->mVertices[i]-d)/div; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // print statistics nuclear@0: if (!DefaultLogger::isNullLogger()) nuclear@0: { nuclear@0: char buffer[4096]; nuclear@0: nuclear@0: DefaultLogger::get()->debug("PretransformVerticesProcess finished"); nuclear@0: nuclear@0: sprintf(buffer,"Removed %i nodes and %i animation channels (%i output nodes)", nuclear@0: iOldNodes,iOldAnimationChannels,CountNodes(pScene->mRootNode)); nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: nuclear@0: sprintf(buffer,"Kept %i lights and %i cameras", nuclear@0: pScene->mNumLights,pScene->mNumCameras); nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: nuclear@0: sprintf(buffer,"Moved %i meshes to WCS (number of output meshes: %i)", nuclear@0: iOldMeshes,pScene->mNumMeshes); nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: } nuclear@0: } nuclear@0: