1 #include "blendshapes.hpp"
3 #include <assimp/Importer.hpp>
4 #include <assimp/postprocess.h>
6 #include <eigen3/Eigen/Dense>
9 void createBlendshapes(std::string dir, std::vector<std::string> blends,
10 std::string neutral, Program p, BlendshapeModel *res) {
12 Assimp::Importer importer;
13 unsigned int ppFlags = aiProcess_Triangulate | aiProcess_CalcTangentSpace |
15 importer.ReadFile(dir + "/" + neutral, ppFlags);
16 const aiScene *scene = importer.GetOrphanedScene();
18 res->model = new Model(scene, p);
20 assert(scene->mNumMeshes == 1);
22 const aiMesh *neutralMesh = scene->mMeshes[0];
23 res->neutral = std::vector<glm::vec3>(neutralMesh->mNumVertices);
25 res->blendshapes = std::vector<Blendshape>(blends.size());
27 for (int i = 0; i < neutralMesh->mNumVertices; i++)
28 res->neutral[i] = aiVector3DToVec3(neutralMesh->mVertices[i]);
30 for (int i = 0; i < blends.size(); i++) {
31 auto fp = dir + "/" + blends[i];
32 const aiScene *blendshape = importer.ReadFile(fp, ppFlags);
33 if (blendshape->mNumMeshes != 1) {
34 std::cerr << "Too many or too little meshes for the blendshape "
38 aiMesh *mesh = blendshape->mMeshes[0];
39 std::vector<glm::vec3> meshDeltas(mesh->mNumVertices);
40 assert(mesh->mNumVertices == neutralMesh->mNumVertices);
41 for (int j = 0; j < mesh->mNumVertices; j++) {
42 glm::vec3 d = aiVector3DToVec3(mesh->mVertices[j]) -
43 aiVector3DToVec3(neutralMesh->mVertices[j]);
46 res->blendshapes[i] = {blends[i], meshDeltas, 0};
50 void loadBlendshapes(std::string dir, Program p, BlendshapeModel *bsModel) {
51 std::vector<std::string> names;
52 DIR *blendDir = opendir(dir.c_str());
53 while (dirent *e = readdir(blendDir)) {
54 if (e->d_type & DT_DIR)
56 const std::string name(e->d_name);
57 const std::string ext = ".obj";
58 if (name.compare(name.length() - ext.length(), ext.length(), ext) != 0)
60 if (name == "neutral.obj")
62 names.push_back(name);
66 // alphabetically sort blendshapes
67 std::sort(names.begin(), names.end());
70 std::ifstream animFile(dir + "/animation.txt");
71 if (animFile.good()) {
72 std::vector<float> weights(names.size());
74 while (animFile >> weights[i]) {
76 if (i >= names.size()) {
78 bsModel->animation.push_back(weights);
81 assert(i == 0); // should not end on an arbitrary number
84 createBlendshapes(dir, names, "neutral.obj", p, bsModel);
87 void interpolateBlendshapes(BlendshapeModel *b) {
88 const Model::Mesh mesh = b->model->meshes[0];
90 for (int i = 0; i < mesh.ai.mNumVertices; i++) {
91 glm::vec3 pos = b->neutral[i];
92 for (auto &blendshape : b->blendshapes) {
93 pos += blendshape.deltas[i] * blendshape.weight;
96 mesh.ai.mVertices[i] = vec3ToaiVector3D(pos);
99 mesh.updatePosBuffer();
102 void solveWeights(BlendshapeModel *b, std::map<VertIdx, glm::vec3> manips) {
104 int numBlends = b->blendshapes.size();
105 int numManips = manips.size();
107 Eigen::VectorXf manipOffset(3 * numManips); // 3 * numManips x 1
110 for (auto &m : manips) {
111 auto origPos = b->neutral[m.first.second];
112 auto manipOffsetV = m.second - origPos;
113 for (int j = 0; j < 3; j++)
114 manipOffset(i * 3 + j) = manipOffsetV[j];
119 Eigen::MatrixXf Bbar(3 * numManips, numBlends); // 3 * numManips x numBlends
120 for (int i = 0; i < numBlends; i++) {
122 for (auto &m : manips) {
123 for (int k = 0; k < 3; k++)
125 b->blendshapes[i].deltas[m.first.second][k];
130 Eigen::VectorXf prevWeights(numBlends); // numBlends x 1
131 for (int i = 0; i < numBlends; i++)
132 prevWeights(i) = b->blendshapes[i].weight;
134 float alpha = .1, mu = 1;
136 Eigen::MatrixXf LHS(3 * numManips + numBlends * 2, numBlends);
137 LHS << Bbar, alpha * Eigen::MatrixXf::Identity(numBlends, numBlends),
138 mu * Eigen::MatrixXf::Identity(numBlends, numBlends);
140 Eigen::VectorXf RHS(3 * numManips + numBlends * 2);
141 RHS << manipOffset, alpha * prevWeights, Eigen::VectorXf::Zero(numBlends);
144 LHS.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeFullV).solve(RHS);
146 for (int i = 0; i < numBlends; i++)
147 b->blendshapes[i].weight = w[i];
150 void stepBlendshapeAnim(BlendshapeModel *b) {
151 for (int i = 0; i < b->blendshapes.size(); i++) {
152 b->blendshapes[i].weight = b->animation[b->curFrame][i];
155 b->curFrame %= b->animation.size();