| /* |
| Open Asset Import Library (assimp) |
| ---------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the |
| following conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| ---------------------------------------------------------------------- |
| */ |
| |
| /** @file SkeletonMeshBuilder.cpp |
| * @brief Implementation of a little class to construct a dummy mesh for a skeleton |
| */ |
| |
| #include <assimp/scene.h> |
| #include "SkeletonMeshBuilder.h" |
| |
| using namespace Assimp; |
| |
| // ------------------------------------------------------------------------------------------------ |
| // The constructor processes the given scene and adds a mesh there. |
| SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly) |
| { |
| // nothing to do if there's mesh data already present at the scene |
| if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL) |
| return; |
| |
| if (!root) |
| root = pScene->mRootNode; |
| |
| mKnobsOnly = bKnobsOnly; |
| |
| // build some faces around each node |
| CreateGeometry( root ); |
| |
| // create a mesh to hold all the generated faces |
| pScene->mNumMeshes = 1; |
| pScene->mMeshes = new aiMesh*[1]; |
| pScene->mMeshes[0] = CreateMesh(); |
| // and install it at the root node |
| root->mNumMeshes = 1; |
| root->mMeshes = new unsigned int[1]; |
| root->mMeshes[0] = 0; |
| |
| // create a dummy material for the mesh |
| if(pScene->mNumMaterials==0){ |
| pScene->mNumMaterials = 1; |
| pScene->mMaterials = new aiMaterial*[1]; |
| pScene->mMaterials[0] = CreateMaterial(); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Recursively builds a simple mesh representation for the given node |
| void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode) |
| { |
| // add a joint entry for the node. |
| const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size()); |
| |
| // now build the geometry. |
| if( pNode->mNumChildren > 0 && !mKnobsOnly) |
| { |
| // If the node has children, we build little pointers to each of them |
| for( unsigned int a = 0; a < pNode->mNumChildren; a++) |
| { |
| // find a suitable coordinate system |
| const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation; |
| aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4); |
| ai_real distanceToChild = childpos.Length(); |
| if( distanceToChild < 0.0001) |
| continue; |
| aiVector3D up = aiVector3D( childpos).Normalize(); |
| |
| aiVector3D orth( 1.0, 0.0, 0.0); |
| if( std::fabs( orth * up) > 0.99) |
| orth.Set( 0.0, 1.0, 0.0); |
| |
| aiVector3D front = (up ^ orth).Normalize(); |
| aiVector3D side = (front ^ up).Normalize(); |
| |
| unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size()); |
| mVertices.push_back( -front * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( childpos); |
| mVertices.push_back( -side * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( -side * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( childpos); |
| mVertices.push_back( front * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( front * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( childpos); |
| mVertices.push_back( side * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( side * distanceToChild * (ai_real)0.1); |
| mVertices.push_back( childpos); |
| mVertices.push_back( -front * distanceToChild * (ai_real)0.1); |
| |
| mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2)); |
| mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5)); |
| mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8)); |
| mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11)); |
| } |
| } |
| else |
| { |
| // if the node has no children, it's an end node. Put a little knob there instead |
| aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4); |
| ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 ); |
| |
| mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); |
| mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); |
| mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); |
| mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate)); |
| |
| mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); |
| mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); |
| mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); |
| mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0)); |
| mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate)); |
| mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0)); |
| |
| mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2)); |
| mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5)); |
| mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8)); |
| mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11)); |
| mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14)); |
| mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17)); |
| mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20)); |
| mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23)); |
| } |
| |
| unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex); |
| if( numVertices > 0) |
| { |
| // create a bone affecting all the newly created vertices |
| aiBone* bone = new aiBone; |
| mBones.push_back( bone); |
| bone->mName = pNode->mName; |
| |
| // calculate the bone offset matrix by concatenating the inverse transformations of all parents |
| bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse(); |
| for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent) |
| bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix; |
| |
| // add all the vertices to the bone's influences |
| bone->mNumWeights = numVertices; |
| bone->mWeights = new aiVertexWeight[numVertices]; |
| for( unsigned int a = 0; a < numVertices; a++) |
| bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0); |
| |
| // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding |
| // them to the array, but I'm tired now and I'm annoyed. |
| aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse(); |
| for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++) |
| mVertices[a] = boneToMeshTransform * mVertices[a]; |
| } |
| |
| // and finally recurse into the children list |
| for( unsigned int a = 0; a < pNode->mNumChildren; a++) |
| CreateGeometry( pNode->mChildren[a]); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Creates the mesh from the internally accumulated stuff and returns it. |
| aiMesh* SkeletonMeshBuilder::CreateMesh() |
| { |
| aiMesh* mesh = new aiMesh(); |
| |
| // add points |
| mesh->mNumVertices = static_cast<unsigned int>(mVertices.size()); |
| mesh->mVertices = new aiVector3D[mesh->mNumVertices]; |
| std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices); |
| |
| mesh->mNormals = new aiVector3D[mesh->mNumVertices]; |
| |
| // add faces |
| mesh->mNumFaces = static_cast<unsigned int>(mFaces.size()); |
| mesh->mFaces = new aiFace[mesh->mNumFaces]; |
| for( unsigned int a = 0; a < mesh->mNumFaces; a++) |
| { |
| const Face& inface = mFaces[a]; |
| aiFace& outface = mesh->mFaces[a]; |
| outface.mNumIndices = 3; |
| outface.mIndices = new unsigned int[3]; |
| outface.mIndices[0] = inface.mIndices[0]; |
| outface.mIndices[1] = inface.mIndices[1]; |
| outface.mIndices[2] = inface.mIndices[2]; |
| |
| // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize |
| // the skeleton, so it's good if there's a visual difference to the rest of the geometry |
| aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ |
| (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); |
| |
| if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/ |
| nor = aiVector3D(1.0,0.0,0.0); |
| |
| for (unsigned int n = 0; n < 3; ++n) |
| mesh->mNormals[inface.mIndices[n]] = nor; |
| } |
| |
| // add the bones |
| mesh->mNumBones = static_cast<unsigned int>(mBones.size()); |
| mesh->mBones = new aiBone*[mesh->mNumBones]; |
| std::copy( mBones.begin(), mBones.end(), mesh->mBones); |
| |
| // default |
| mesh->mMaterialIndex = 0; |
| |
| return mesh; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Creates a dummy material and returns it. |
| aiMaterial* SkeletonMeshBuilder::CreateMaterial() |
| { |
| aiMaterial* matHelper = new aiMaterial; |
| |
| // Name |
| aiString matName( std::string( "SkeletonMaterial")); |
| matHelper->AddProperty( &matName, AI_MATKEY_NAME); |
| |
| // Prevent backface culling |
| const int no_cull = 1; |
| matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED); |
| |
| return matHelper; |
| } |