1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42/** @file SkeletonMeshBuilder.cpp
43 * @brief Implementation of a little class to construct a dummy mesh for a skeleton
44 */
45
46#include <assimp/scene.h>
47#include "SkeletonMeshBuilder.h"
48
49using namespace Assimp;
50
51// ------------------------------------------------------------------------------------------------
52// The constructor processes the given scene and adds a mesh there.
53SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly)
54{
55 // nothing to do if there's mesh data already present at the scene
56 if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
57 return;
58
59 if (!root)
60 root = pScene->mRootNode;
61
62 mKnobsOnly = bKnobsOnly;
63
64 // build some faces around each node
65 CreateGeometry( root );
66
67 // create a mesh to hold all the generated faces
68 pScene->mNumMeshes = 1;
69 pScene->mMeshes = new aiMesh*[1];
70 pScene->mMeshes[0] = CreateMesh();
71 // and install it at the root node
72 root->mNumMeshes = 1;
73 root->mMeshes = new unsigned int[1];
74 root->mMeshes[0] = 0;
75
76 // create a dummy material for the mesh
77 if(pScene->mNumMaterials==0){
78 pScene->mNumMaterials = 1;
79 pScene->mMaterials = new aiMaterial*[1];
80 pScene->mMaterials[0] = CreateMaterial();
81 }
82}
83
84// ------------------------------------------------------------------------------------------------
85// Recursively builds a simple mesh representation for the given node
86void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
87{
88 // add a joint entry for the node.
89 const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size());
90
91 // now build the geometry.
92 if( pNode->mNumChildren > 0 && !mKnobsOnly)
93 {
94 // If the node has children, we build little pointers to each of them
95 for( unsigned int a = 0; a < pNode->mNumChildren; a++)
96 {
97 // find a suitable coordinate system
98 const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation;
99 aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4);
100 ai_real distanceToChild = childpos.Length();
101 if( distanceToChild < 0.0001)
102 continue;
103 aiVector3D up = aiVector3D( childpos).Normalize();
104
105 aiVector3D orth( 1.0, 0.0, 0.0);
106 if( std::fabs( orth * up) > 0.99)
107 orth.Set( 0.0, 1.0, 0.0);
108
109 aiVector3D front = (up ^ orth).Normalize();
110 aiVector3D side = (front ^ up).Normalize();
111
112 unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size());
113 mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
114 mVertices.push_back( childpos);
115 mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
116 mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
117 mVertices.push_back( childpos);
118 mVertices.push_back( front * distanceToChild * (ai_real)0.1);
119 mVertices.push_back( front * distanceToChild * (ai_real)0.1);
120 mVertices.push_back( childpos);
121 mVertices.push_back( side * distanceToChild * (ai_real)0.1);
122 mVertices.push_back( side * distanceToChild * (ai_real)0.1);
123 mVertices.push_back( childpos);
124 mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
125
126 mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
127 mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
128 mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
129 mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
130 }
131 }
132 else
133 {
134 // if the node has no children, it's an end node. Put a little knob there instead
135 aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
136 ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 );
137
138 mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
139 mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
140 mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
141 mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
142 mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
143 mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
144 mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
145 mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
146 mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
147 mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
148 mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
149 mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
150
151 mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
152 mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
153 mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
154 mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
155 mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
156 mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
157 mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
158 mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
159 mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
160 mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
161 mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
162 mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
163
164 mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
165 mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
166 mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
167 mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
168 mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
169 mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
170 mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
171 mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
172 }
173
174 unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex);
175 if( numVertices > 0)
176 {
177 // create a bone affecting all the newly created vertices
178 aiBone* bone = new aiBone;
179 mBones.push_back( bone);
180 bone->mName = pNode->mName;
181
182 // calculate the bone offset matrix by concatenating the inverse transformations of all parents
183 bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse();
184 for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent)
185 bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix;
186
187 // add all the vertices to the bone's influences
188 bone->mNumWeights = numVertices;
189 bone->mWeights = new aiVertexWeight[numVertices];
190 for( unsigned int a = 0; a < numVertices; a++)
191 bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0);
192
193 // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
194 // them to the array, but I'm tired now and I'm annoyed.
195 aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse();
196 for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
197 mVertices[a] = boneToMeshTransform * mVertices[a];
198 }
199
200 // and finally recurse into the children list
201 for( unsigned int a = 0; a < pNode->mNumChildren; a++)
202 CreateGeometry( pNode->mChildren[a]);
203}
204
205// ------------------------------------------------------------------------------------------------
206// Creates the mesh from the internally accumulated stuff and returns it.
207aiMesh* SkeletonMeshBuilder::CreateMesh()
208{
209 aiMesh* mesh = new aiMesh();
210
211 // add points
212 mesh->mNumVertices = static_cast<unsigned int>(mVertices.size());
213 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
214 std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
215
216 mesh->mNormals = new aiVector3D[mesh->mNumVertices];
217
218 // add faces
219 mesh->mNumFaces = static_cast<unsigned int>(mFaces.size());
220 mesh->mFaces = new aiFace[mesh->mNumFaces];
221 for( unsigned int a = 0; a < mesh->mNumFaces; a++)
222 {
223 const Face& inface = mFaces[a];
224 aiFace& outface = mesh->mFaces[a];
225 outface.mNumIndices = 3;
226 outface.mIndices = new unsigned int[3];
227 outface.mIndices[0] = inface.mIndices[0];
228 outface.mIndices[1] = inface.mIndices[1];
229 outface.mIndices[2] = inface.mIndices[2];
230
231 // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize
232 // the skeleton, so it's good if there's a visual difference to the rest of the geometry
233 aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^
234 (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));
235
236 if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/
237 nor = aiVector3D(1.0,0.0,0.0);
238
239 for (unsigned int n = 0; n < 3; ++n)
240 mesh->mNormals[inface.mIndices[n]] = nor;
241 }
242
243 // add the bones
244 mesh->mNumBones = static_cast<unsigned int>(mBones.size());
245 mesh->mBones = new aiBone*[mesh->mNumBones];
246 std::copy( mBones.begin(), mBones.end(), mesh->mBones);
247
248 // default
249 mesh->mMaterialIndex = 0;
250
251 return mesh;
252}
253
254// ------------------------------------------------------------------------------------------------
255// Creates a dummy material and returns it.
256aiMaterial* SkeletonMeshBuilder::CreateMaterial()
257{
258 aiMaterial* matHelper = new aiMaterial;
259
260 // Name
261 aiString matName( std::string( "SkeletonMaterial"));
262 matHelper->AddProperty( &matName, AI_MATKEY_NAME);
263
264 // Prevent backface culling
265 const int no_cull = 1;
266 matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED);
267
268 return matHelper;
269}
270