nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team 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 nuclear@0: following 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: nuclear@0: /** @file SkeletonMeshBuilder.cpp nuclear@0: * @brief Implementation of a little class to construct a dummy mesh for a skeleton nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #include "assimp/scene.h" nuclear@0: #include "SkeletonMeshBuilder.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // The constructor processes the given scene and adds a mesh there. nuclear@0: SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly) nuclear@0: { nuclear@0: // nothing to do if there's mesh data already present at the scene nuclear@0: if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL) nuclear@0: return; nuclear@0: nuclear@0: if (!root) nuclear@0: root = pScene->mRootNode; nuclear@0: nuclear@0: mKnobsOnly = bKnobsOnly; nuclear@0: nuclear@0: // build some faces around each node nuclear@0: CreateGeometry( root ); nuclear@0: nuclear@0: // create a mesh to hold all the generated faces nuclear@0: pScene->mNumMeshes = 1; nuclear@0: pScene->mMeshes = new aiMesh*[1]; nuclear@0: pScene->mMeshes[0] = CreateMesh(); nuclear@0: // and install it at the root node nuclear@0: root->mNumMeshes = 1; nuclear@0: root->mMeshes = new unsigned int[1]; nuclear@0: root->mMeshes[0] = 0; nuclear@0: nuclear@0: // create a dummy material for the mesh nuclear@0: pScene->mNumMaterials = 1; nuclear@0: pScene->mMaterials = new aiMaterial*[1]; nuclear@0: pScene->mMaterials[0] = CreateMaterial(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Recursively builds a simple mesh representation for the given node nuclear@0: void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode) nuclear@0: { nuclear@0: // add a joint entry for the node. nuclear@0: const unsigned int vertexStartIndex = mVertices.size(); nuclear@0: nuclear@0: // now build the geometry. nuclear@0: if( pNode->mNumChildren > 0 && !mKnobsOnly) nuclear@0: { nuclear@0: // If the node has children, we build little pointers to each of them nuclear@0: for( unsigned int a = 0; a < pNode->mNumChildren; a++) nuclear@0: { nuclear@0: // find a suitable coordinate system nuclear@0: const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation; nuclear@0: aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4); nuclear@0: float distanceToChild = childpos.Length(); nuclear@0: if( distanceToChild < 0.0001f) nuclear@0: continue; nuclear@0: aiVector3D up = aiVector3D( childpos).Normalize(); nuclear@0: nuclear@0: aiVector3D orth( 1.0f, 0.0f, 0.0f); nuclear@0: if( fabs( orth * up) > 0.99f) nuclear@0: orth.Set( 0.0f, 1.0f, 0.0f); nuclear@0: nuclear@0: aiVector3D front = (up ^ orth).Normalize(); nuclear@0: aiVector3D side = (front ^ up).Normalize(); nuclear@0: nuclear@0: unsigned int localVertexStart = mVertices.size(); nuclear@0: mVertices.push_back( -front * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( childpos); nuclear@0: mVertices.push_back( -side * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( -side * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( childpos); nuclear@0: mVertices.push_back( front * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( front * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( childpos); nuclear@0: mVertices.push_back( side * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( side * distanceToChild * 0.1f); nuclear@0: mVertices.push_back( childpos); nuclear@0: mVertices.push_back( -front * distanceToChild * 0.1f); nuclear@0: nuclear@0: mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2)); nuclear@0: mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5)); nuclear@0: mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8)); nuclear@0: mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11)); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // if the node has no children, it's an end node. Put a little knob there instead nuclear@0: aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4); nuclear@0: float sizeEstimate = ownpos.Length() * 0.18f; nuclear@0: nuclear@0: mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate)); nuclear@0: nuclear@0: mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f)); nuclear@0: mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate)); nuclear@0: mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f)); nuclear@0: nuclear@0: mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20)); nuclear@0: mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23)); nuclear@0: } nuclear@0: nuclear@0: unsigned int numVertices = mVertices.size() - vertexStartIndex; nuclear@0: if( numVertices > 0) nuclear@0: { nuclear@0: // create a bone affecting all the newly created vertices nuclear@0: aiBone* bone = new aiBone; nuclear@0: mBones.push_back( bone); nuclear@0: bone->mName = pNode->mName; nuclear@0: nuclear@0: // calculate the bone offset matrix by concatenating the inverse transformations of all parents nuclear@0: bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse(); nuclear@0: for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent) nuclear@0: bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix; nuclear@0: nuclear@0: // add all the vertices to the bone's influences nuclear@0: bone->mNumWeights = numVertices; nuclear@0: bone->mWeights = new aiVertexWeight[numVertices]; nuclear@0: for( unsigned int a = 0; a < numVertices; a++) nuclear@0: bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0f); nuclear@0: nuclear@0: // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding nuclear@0: // them to the array, but I'm tired now and I'm annoyed. nuclear@0: aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse(); nuclear@0: for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++) nuclear@0: mVertices[a] = boneToMeshTransform * mVertices[a]; nuclear@0: } nuclear@0: nuclear@0: // and finally recurse into the children list nuclear@0: for( unsigned int a = 0; a < pNode->mNumChildren; a++) nuclear@0: CreateGeometry( pNode->mChildren[a]); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Creates the mesh from the internally accumulated stuff and returns it. nuclear@0: aiMesh* SkeletonMeshBuilder::CreateMesh() nuclear@0: { nuclear@0: aiMesh* mesh = new aiMesh(); nuclear@0: nuclear@0: // add points nuclear@0: mesh->mNumVertices = mVertices.size(); nuclear@0: mesh->mVertices = new aiVector3D[mesh->mNumVertices]; nuclear@0: std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices); nuclear@0: nuclear@0: mesh->mNormals = new aiVector3D[mesh->mNumVertices]; nuclear@0: nuclear@0: // add faces nuclear@0: mesh->mNumFaces = mFaces.size(); nuclear@0: mesh->mFaces = new aiFace[mesh->mNumFaces]; nuclear@0: for( unsigned int a = 0; a < mesh->mNumFaces; a++) nuclear@0: { nuclear@0: const Face& inface = mFaces[a]; nuclear@0: aiFace& outface = mesh->mFaces[a]; nuclear@0: outface.mNumIndices = 3; nuclear@0: outface.mIndices = new unsigned int[3]; nuclear@0: outface.mIndices[0] = inface.mIndices[0]; nuclear@0: outface.mIndices[1] = inface.mIndices[1]; nuclear@0: outface.mIndices[2] = inface.mIndices[2]; nuclear@0: nuclear@0: // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize nuclear@0: // the skeleton, so it's good if there's a visual difference to the rest of the geometry nuclear@0: aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ nuclear@0: (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); nuclear@0: nuclear@0: if (nor.Length() < 1e-5f) /* ensure that FindInvalidData won't remove us ...*/ nuclear@0: nor = aiVector3D(1.f,0.f,0.f); nuclear@0: nuclear@0: for (unsigned int n = 0; n < 3; ++n) nuclear@0: mesh->mNormals[inface.mIndices[n]] = nor; nuclear@0: } nuclear@0: nuclear@0: // add the bones nuclear@0: mesh->mNumBones = mBones.size(); nuclear@0: mesh->mBones = new aiBone*[mesh->mNumBones]; nuclear@0: std::copy( mBones.begin(), mBones.end(), mesh->mBones); nuclear@0: nuclear@0: // default nuclear@0: mesh->mMaterialIndex = 0; nuclear@0: nuclear@0: return mesh; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Creates a dummy material and returns it. nuclear@0: aiMaterial* SkeletonMeshBuilder::CreateMaterial() nuclear@0: { nuclear@0: aiMaterial* matHelper = new aiMaterial; nuclear@0: nuclear@0: // Name nuclear@0: aiString matName( std::string( "SkeletonMaterial")); nuclear@0: matHelper->AddProperty( &matName, AI_MATKEY_NAME); nuclear@0: nuclear@0: // Prevent backface culling nuclear@0: const int no_cull = 1; nuclear@0: matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED); nuclear@0: nuclear@0: return matHelper; nuclear@0: }