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