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