First pass at blendshapes
[opengl.git] / model.cpp
1 #include "model.hpp"
2 #include <iostream>
3 #include <assimp/quaternion.h>
4 #include <glm/gtc/type_ptr.hpp>
5 #include <glm/gtx/closest_point.hpp>
6 #include "util.hpp"
7
8 Model::Mesh::Mesh(const aiMesh *aiMesh, GLuint progId) : progId(progId), ai(*aiMesh) {
9         std::vector<glm::vec3> vertices, normals, tangents, bitangents;
10         std::vector<glm::vec2> texCoords;
11
12         for (int i = 0; i < aiMesh->mNumVertices; i++) {
13                 if (aiMesh->HasPositions()) {
14                         aiVector3D v = aiMesh->mVertices[i];
15                         vertices.push_back(glm::vec3(v.x, v.y, v.z));
16                 }
17                 if (aiMesh->HasNormals()) {
18                         aiVector3D v = aiMesh->mNormals[i];
19                         normals.push_back(glm::vec3(v.x, v.y, v.z));
20                 } else {
21                         std::cerr << "Missing normals" << std::endl;
22                         abort();
23                 }
24                 // check for texture coord set 0
25                 if (aiMesh->HasTextureCoords(0)) {
26                         const aiVector3D v = aiMesh->mTextureCoords[0][i];
27                         texCoords.push_back(glm::vec2(v.x, v.y));
28                 } else {
29                         texCoords.push_back(glm::vec2(0));
30                 }
31                 materialIndex = aiMesh->mMaterialIndex;
32         }
33
34         std::vector<GLuint> indices;
35
36         for (int i = 0; i < aiMesh->mNumFaces; i++) {
37                 const aiFace &face = aiMesh->mFaces[i];
38                 if(face.mNumIndices == 3) {
39                         indices.push_back(face.mIndices[0]);
40                         indices.push_back(face.mIndices[1]);
41                         indices.push_back(face.mIndices[2]);
42                 }
43         } 
44         
45         numIndices = indices.size();
46         
47         glGenVertexArrays(1, &vao);
48         glBindVertexArray(vao);
49         
50         glGenBuffers(6, vbos);
51         GLuint posVbo = vbos[0], normalVbo = vbos[1], texCoordVbo = vbos[2], indicesVbo = vbos[3];
52         GLuint boneVbo = vbos[4];
53         
54         GLuint posLoc = glGetAttribLocation(progId, "pos");
55         glBindBuffer(GL_ARRAY_BUFFER, posVbo);
56         glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
57         glEnableVertexAttribArray(posLoc);
58         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
59         
60         GLuint normalLoc = glGetAttribLocation(progId, "unscaledNormal");
61         glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
62         glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);
63         glEnableVertexAttribArray(normalLoc);
64         glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
65
66         GLuint texCoordLoc = glGetAttribLocation(progId, "vTexCoord");
67         glBindBuffer(GL_ARRAY_BUFFER, texCoordVbo);
68         glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(glm::vec2), &texCoords[0], GL_STATIC_DRAW);
69         glEnableVertexAttribArray(texCoordLoc);
70         glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
71
72         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesVbo);
73         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
74
75         // bones
76         std::vector<VertBones> vertBones(aiMesh->mNumVertices);
77
78         std::map<unsigned int, std::vector<std::pair<unsigned int, float>>> boneWeightMap;
79
80         for (unsigned int i = 0; i < aiMesh->mNumBones; i++) {
81                 aiBone *aiBone = aiMesh->mBones[i];
82
83                 boneMap[std::string(aiBone->mName.C_Str())] = std::pair(i + 1, aiBone);
84
85                 for (int j = 0; j < aiBone->mNumWeights; j++) {
86                         aiVertexWeight vw = aiBone->mWeights[j];
87
88                         if (!boneWeightMap.count(vw.mVertexId))
89                                 boneWeightMap[vw.mVertexId] = std::vector<std::pair<unsigned int, float>>();
90                         boneWeightMap[vw.mVertexId].push_back(std::pair(i + 1, vw.mWeight));
91                 }
92         }
93
94         for (auto pair: boneWeightMap) {
95                 unsigned int vertexId = pair.first;
96                 for (int i = 0; i < pair.second.size() && i < 4; i++) {
97                         unsigned int boneId = pair.second[i].first;
98                         float weight = pair.second[i].second;
99                         vertBones[vertexId].ids[i] = boneId;
100                         vertBones[vertexId].weights[i] = weight;
101                 }
102         }
103         
104         glBindBuffer(GL_ARRAY_BUFFER, boneVbo);
105         glBufferData(GL_ARRAY_BUFFER, sizeof(VertBones) * vertBones.size(), &vertBones[0], GL_STATIC_DRAW);
106
107         GLuint boneIdLoc = glGetAttribLocation(progId, "boneIds");
108         glEnableVertexAttribArray(boneIdLoc);
109         glVertexAttribIPointer(boneIdLoc, 4, GL_INT, sizeof(VertBones), 0);
110
111         GLuint boneWeightLoc = glGetAttribLocation(progId, "boneWeights");
112         glEnableVertexAttribArray(boneWeightLoc);
113         glVertexAttribPointer(boneWeightLoc, 4, GL_FLOAT, GL_FALSE, sizeof(VertBones), (const GLvoid *)sizeof(VertBones::ids));
114 }
115
116 void Model::Mesh::updatePosBuffer() const {
117         GLuint posLoc = glGetAttribLocation(progId, "pos");
118         GLuint posVbo = vbos[0];
119         glBindBuffer(GL_ARRAY_BUFFER, posVbo);
120         glBufferData(GL_ARRAY_BUFFER, ai.mNumVertices * sizeof(aiVector3D), ai.mVertices, GL_STATIC_DRAW);
121         glEnableVertexAttribArray(posLoc);
122         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
123 }
124
125 Model::Node::Node(aiNode &node, GLuint progId, AnimMap *am, std::set<std::string> allBones, Node *p): ai(node), parent(p), progId(progId), animMap(am), isBone(allBones.count(std::string(node.mName.C_Str())) > 0) {
126         for (int i = 0; i < node.mNumMeshes; i++) {
127                 meshIndices.push_back(node.mMeshes[i]);
128         }
129         for (int i = 0; i < node.mNumChildren; i++) {
130                 aiNode *child = node.mChildren[i];
131                 children.push_back(new Node(*child, progId, am, allBones, this));
132         }
133 }
134
135 glm::mat4 lerpPosition(const aiNodeAnim *anim, const float tick) {
136         if (anim->mNumPositionKeys == 0) return glm::mat4(1.f);
137
138         int yIndex = -1;
139         for (int i = 0; i < anim->mNumPositionKeys; i++) {
140                 aiVectorKey vk = anim->mPositionKeys[i];
141                 if (vk.mTime > tick) {
142                         yIndex = i;
143                         break;
144                 }
145         }
146         aiVector3D lerpPos;
147         if (yIndex == 0) {
148                 lerpPos = anim->mPositionKeys[0].mValue;
149         } else if (yIndex == -1) {
150                 lerpPos = anim->mPositionKeys[anim->mNumPositionKeys - 1].mValue;
151         } else {
152                 auto X = anim->mPositionKeys[yIndex - 1];
153                 auto Y = anim->mPositionKeys[yIndex];
154
155                 lerpPos = (X.mValue * (float)(Y.mTime - tick) + Y.mValue * (float)(tick - X.mTime)) / (float)(Y.mTime - X.mTime);
156         }
157         aiMatrix4x4 result;
158         aiMatrix4x4::Translation(lerpPos, result);
159         return aiMatrixToMat4(result);
160 }
161
162 glm::mat4 lerpRotation(const aiNodeAnim *anim, const float tick) {
163         int yIndex = -1;
164         for (int i = 0; i < anim->mNumRotationKeys; i++) {
165                 aiQuatKey vk = anim->mRotationKeys[i];
166                 if (vk.mTime > tick) {
167                         yIndex = i;
168                         break;
169                 }
170         }
171
172         aiQuaternion result;
173         if (yIndex < 1) {
174                 result = anim->mRotationKeys[0].mValue;
175         } else if (yIndex == -1) {
176                 result = anim->mRotationKeys[anim->mNumRotationKeys - 1].mValue;
177         } else {
178
179                 auto X = anim->mRotationKeys[yIndex - 1];
180                 auto Y = anim->mRotationKeys[yIndex];
181
182                 float mix = (tick - X.mTime) / (Y.mTime - X.mTime);
183
184                 aiQuaternion::Interpolate(result, X.mValue, Y.mValue, mix);
185
186         }
187         return aiMatrixToMat4(aiMatrix4x4(result.GetMatrix()));
188 }
189
190 glm::mat4 lerpScaling(const aiNodeAnim *anim, const float tick) {
191         int yIndex = -1;
192         for (int i = 0; i < anim->mNumScalingKeys; i++) {
193                 aiVectorKey vk = anim->mScalingKeys[i];
194                 if (vk.mTime > tick) {
195                         yIndex = i;
196                         break;
197                 }
198         }
199
200         aiVector3D lerpPos;
201         if (yIndex < 1) {
202                 lerpPos = anim->mScalingKeys[0].mValue;
203         } else {
204                 auto X = anim->mScalingKeys[yIndex - 1];
205                 auto Y = anim->mScalingKeys[yIndex];
206
207                 lerpPos = (X.mValue * (float)(Y.mTime - tick) + Y.mValue * (float)(tick - X.mTime)) / (float)(Y.mTime - X.mTime);
208         }
209         aiMatrix4x4 result;
210         aiMatrix4x4::Scaling(lerpPos, result);
211         return aiMatrixToMat4(result);
212 }
213
214 glm::mat4 Model::Node::totalTrans(const glm::mat4 parentTrans, const float tick) const {
215         glm::mat4 aiTrans = aiMatrixToMat4(ai.mTransformation);
216         if (animMap->count(std::string(ai.mName.C_Str()))) {
217                 for (const Animation anim: animMap->at(std::string(ai.mName.C_Str()))) {
218                         // animations are *absolute*
219                         // they replace aiNode.mTransformation!!
220                         aiTrans = glm::mat4(1);
221                         float t = fmod(tick, anim.duration);
222                         for (const aiNodeAnim *nodeAnim: anim.nodeAnims) {
223                                 aiTrans *= lerpPosition(nodeAnim, t);
224                                 aiTrans *= lerpRotation(nodeAnim, t);
225                                 aiTrans *= lerpScaling(nodeAnim, t);
226                         }
227                 }
228         }
229
230         glm::mat4 m = parentTrans * aiTrans * transform;
231         return m;
232 }
233
234 const Model::Node &Model::Node::getRoot() const {
235         const Model::Node *rootPtr = this;
236         while (rootPtr->parent != nullptr)
237                 rootPtr = rootPtr->parent;
238         const Model::Node &root = *rootPtr;
239         return root;
240 }
241
242 void Model::Node::draw( const std::vector<Mesh> &meshes,
243                                                 const std::vector<Material> &materials,
244                                                 const Skybox skybox,
245                                                 const float tick,
246                                                 const BoneTransforms &boneTransforms,
247                                                 glm::mat4 parentTrans = glm::mat4(1)) const {
248
249         GLuint modelLoc = glGetUniformLocation(progId, "model");
250         glm::mat4 m = totalTrans(parentTrans, tick);
251
252 #ifdef DEBUG_NODES
253         if (isBone)
254                 drawDebugNode(m, {0, 0.5, 1, 1});
255         else
256                 drawDebugNode(m);
257 #endif
258
259         for (unsigned int i: meshIndices) {
260                 const Mesh &mesh = meshes[i];
261                 glBindVertexArray(mesh.vao);
262
263                 // bones
264                 std::vector<glm::mat4> idBones(17, glm::mat4(1.f));
265                 glUniformMatrix4fv(glGetUniformLocation(progId, "bones"), 17, GL_FALSE, glm::value_ptr(idBones[0]));
266
267                 // bonemap: map from bone nodes to bone ids and aiBones
268                 for (auto pair: mesh.boneMap) {
269
270                         std::string boneName = pair.first;
271
272                         unsigned int boneId = pair.second.first;
273                         aiBone *bone = pair.second.second;
274                         // This is actually an inverse-bind matrix
275                         // i.e. transforms bone space -> mesh space
276                         // so no need to inverse again!
277                         // https://github.com/assimp/assimp/pull/1803/files
278                         glm::mat4 boneOffset = aiMatrixToMat4(bone->mOffsetMatrix);
279
280                         if (!boneTransforms.count(boneName)) abort();
281                         glm::mat4 boneTrans = boneTransforms.at(boneName);
282
283                         boneTrans = boneTrans * boneOffset;
284
285                         std::string boneLocStr = "bones[" + std::to_string(boneId) + "]";
286                         GLuint boneLoc = glGetUniformLocation(progId, boneLocStr.c_str());
287                         glUniformMatrix4fv(boneLoc, 1, GL_FALSE, glm::value_ptr(boneTrans));
288                 }
289
290                 Material material = materials[mesh.materialIndex];
291                 material.bind();
292
293                 glUniform1i(glGetUniformLocation(progId, "irradianceMap"), 4);
294                 glActiveTexture(GL_TEXTURE4);
295                 glBindTexture(GL_TEXTURE_CUBE_MAP, skybox.getIrradianceMap());
296
297                 glUniform1i(glGetUniformLocation(progId, "prefilterMap"), 5);
298                 glActiveTexture(GL_TEXTURE5);
299                 glBindTexture(GL_TEXTURE_CUBE_MAP, skybox.getPrefilterMap());
300
301                 glUniform1i(glGetUniformLocation(progId, "brdfMap"), 6);
302                 glActiveTexture(GL_TEXTURE6);
303                 glBindTexture(GL_TEXTURE_2D, skybox.getBRDFMap());
304                 
305                 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(m));
306
307                 glDrawElements(GL_TRIANGLES, mesh.numIndices, GL_UNSIGNED_INT, 0);
308         }
309         for (Node *child: children) child->draw(meshes, materials, skybox, tick, boneTransforms, m);
310 }
311
312 void printHierarchy(aiNode *n, int indent = 0) {
313         for (int i = 0; i < indent; i++)
314                 fprintf(stderr, "\t");
315         fprintf(stderr,"%s\n", n->mName.C_Str());
316         printMatrix4x4(n->mTransformation);
317         for (int i = 0; i < n->mNumChildren; i++)
318                 printHierarchy(n->mChildren[i], indent + 1);
319 }
320
321 Model::Model(const aiScene *scene, Program p): program(p) {
322         glUseProgram(p.progId);
323
324         std::set<std::string> allBones;
325         for (int i = 0; i < scene->mNumMeshes; i++) {
326                 const aiMesh *mesh = scene->mMeshes[i];
327                 meshes.push_back(Mesh(mesh, p.progId));
328                 for (int j = 0; j < mesh->mNumBones; j++)
329                         allBones.insert(std::string(mesh->mBones[j]->mName.C_Str()));
330         }
331
332         for (unsigned int i = 0; i < scene->mNumMaterials; i++) {
333                 const aiMaterial &material = *scene->mMaterials[i];
334                 materials.push_back(Material(material, *scene, p.progId));
335         }
336
337         for (int i = 0; i < scene->mNumAnimations; i++) {
338                 const aiAnimation *aiAnim = scene->mAnimations[i];
339
340                 std::map<std::string, std::vector<const aiNodeAnim*>> nodeAnims;
341
342                 for (int j = 0; j < aiAnim->mNumChannels; j++) {
343                         const aiNodeAnim *nodeAnim = aiAnim->mChannels[j];
344                         std::string nodeName = std::string(nodeAnim->mNodeName.C_Str());
345                         
346                         if (!nodeAnims.count(nodeName)) nodeAnims[nodeName] = std::vector<const aiNodeAnim*>();
347
348                         nodeAnims[nodeName].push_back(nodeAnim);
349                 }
350
351                 for (std::pair<std::string, std::vector<const aiNodeAnim*>> pair: nodeAnims) {
352                         std::string nodeName = pair.first;
353
354                         if (!animMap.count(nodeName)) animMap[nodeName] = std::vector<const Animation>();
355                         animMap[nodeName].push_back({ aiAnim->mDuration, pair.second });
356                 }
357         }
358
359         root = new Node(*(scene->mRootNode), p.progId, &animMap, allBones, nullptr);
360 }
361
362
363 std::map<std::string, glm::mat4> Model::calcBoneTransforms(const Node &n, const float tick, const std::set<std::string> bones, const glm::mat4 parentTrans = glm::mat4(1)) const {
364         std::string name = std::string(n.ai.mName.C_Str());
365
366         glm::mat4 m = n.totalTrans(parentTrans, tick);
367
368         BoneTransforms res;
369         if (bones.count(name) > 0)
370                 res[std::string(n.ai.mName.C_Str())] = m; // take part in hierarchy
371         else
372                 m = glm::mat4(1); // ignore this node transformation
373         for (const auto child: n.getChildren())
374                 res.merge(calcBoneTransforms(*child, tick, bones, m));
375         return res;
376 }
377
378 void Model::draw(Skybox skybox, const float tick) const {
379         glUseProgram(program.progId);
380
381         std::set<std::string> bones;
382         for (auto m: this->meshes) {
383                 for (auto b: m.boneMap) {
384                         bones.insert(b.first);
385                 }
386         }
387         auto boneTransforms = calcBoneTransforms(*root, tick, bones);
388
389         root->draw(meshes, materials, skybox, tick, boneTransforms);
390 }
391
392 Model::Node* Model::find(const std::string &name) const {
393         return find(aiString(name));
394 }
395
396 Model::Node* Model::find(const aiString name) const {
397         const aiNode *node = root->ai.FindNode(name);
398         Model::Node* res = root->findNode(*node);
399         return res;
400 }
401
402 Model::Node* Model::Node::findNode(const aiNode &aiNode) {
403         if (&ai == &aiNode) return this;
404         for (Model::Node *child: children) {
405                 Model::Node *res = child->findNode(aiNode);
406                 if (res) return res;
407         }
408         return nullptr;
409 }
410
411 bool Model::Node::operator==(const Model::Node &rhs) const {
412         return &ai == &rhs.ai;
413 }
414
415 // Returns closest vertex in world space and distance
416 // a and b define the line in 3d space
417 std::pair<glm::vec3, float> Model::closestVertex(Model::Node &n, glm::vec3 a, glm::vec3 b, glm::mat4 parentTrans) const {
418         float closestDist = FLT_MAX;
419         glm::vec3 closestVert;
420
421         for (int i = 0; i < n.ai.mNumMeshes; i++) {
422                 int meshIdx = n.ai.mMeshes[i];
423                 const aiMesh &mesh = meshes[meshIdx].ai;
424
425                 for (int j = 0; j < mesh.mNumVertices; j++) {
426                         if (mesh.HasNormals()) {
427                                 auto n = aiVector3DToVec3(mesh.mNormals[j]);
428                                 if (glm::dot(n, glm::normalize(b - a)) > 0)
429                                         continue;
430                         }
431                         glm::vec4 vPos = glm::vec4(aiVector3DToVec3(mesh.mVertices[j]), 1);
432                         // Move from model space -> world space
433                         vPos = parentTrans * aiMatrixToMat4(n.ai.mTransformation) * vPos;
434                         float dist = glm::distance(glm::vec3(vPos),
435                                                         glm::closestPointOnLine(glm::vec3(vPos), a, b));
436                         if (dist < closestDist) {
437                                 closestVert = glm::vec3(vPos);
438                                 closestDist = dist;
439                         }
440                 }
441         }
442         
443         for (auto child: n.getChildren()) {
444                 auto childRes = closestVertex(*child, a, b, parentTrans * aiMatrixToMat4(n.ai.mTransformation));
445                 if (childRes.second < closestDist) {
446                         closestVert = childRes.first;
447                         closestDist = childRes.second;
448                 }
449         }
450
451         return { closestVert, closestDist };
452 }