From: Luke Lau Date: Tue, 18 Feb 2020 00:57:59 +0000 (+0000) Subject: Tidy up and get IK working X-Git-Tag: cs7gv5-a2 X-Git-Url: https://git.lukelau.me/?p=opengl.git;a=commitdiff_plain;h=ad77a970429c9c5e9cf8d513d0469d03ad82e622 Tidy up and get IK working --- diff --git a/Makefile b/Makefile index 6126267..91abc4f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ all: bin/main -CXX_FLAGS := -g --std=c++17 -Wall -DDEBUG_NODES +CXX_FLAGS := -g --std=c++17 -march=native -Wall -DDEBUG_NODES -DCOWEDBOY_IK bin/main: model.o material.o image.o skybox.o program.o ik.o main.o util.o clang++ $(CXX_FLAGS) $^ \ diff --git a/ik.cpp b/ik.cpp index 3895efe..9598ddc 100644 --- a/ik.cpp +++ b/ik.cpp @@ -70,7 +70,7 @@ vec3 extractPos(mat4 trans) { return vec3(trans[3]); } -glm::mat4 absoluteToModelSpace(const Model::Node &root, const Model::Node &n, mat4 m) { +mat4 absoluteToModelSpace(const Model::Node &root, const Model::Node &n, mat4 m) { const Model::Node *parent = &n; glm::mat4 res = m; std::vector trans; @@ -96,18 +96,24 @@ mat4 getRotationToPoint(vec3 u, vec3 v, float dist) { return res; } +// Normalizes a transformation matrix to not have any scale factor +inline mat4 normalizeScale(mat4 m) { + for (int i = 0; i < 3; i++) + m[i] = normalize(m[i]); + return m; +} + float d = 0; // Target is world position -void inverseKinematic(Model::Node &start, Model::Node &end, vec3 target) { +void inverseKinematics(Model::Node &start, Model::Node &end, vec3 target) { std::vector chain = allNodesTo(start, end); assert(!chain.empty()); - const Model::Node *rootPtr = start.parent; - while (rootPtr->parent != nullptr) - rootPtr = rootPtr->parent; - const Model::Node &root = *rootPtr; + // Calculate the world root + const Model::Node &root = start.getRoot(); + // Work out the positions and distances std::vector positions(chain.size()); std::vector distances(chain.size() - 1); for (size_t i = 0; i < chain.size(); i++) { mat4 absTrans = getAbsTrans(root, chain[i]); @@ -116,10 +122,9 @@ void inverseKinematic(Model::Node &start, Model::Node &end, vec3 target) { distances[i - 1] = distance(positions[i], positions[i - 1]); } + // Do the actual IK part auto newPositions = fabrik(target, positions, distances); - - // Move all the nodes so that they are in the correct positions // Don't need to move the root node - it's already in place for (size_t i = 1; i < chain.size(); i++) { @@ -127,26 +132,19 @@ void inverseKinematic(Model::Node &start, Model::Node &end, vec3 target) { mat4 absTrans = getAbsTrans(root, node); absTrans[3] = vec4(newPositions[i], absTrans[3][3]); // update position in transform - /* vec3 oldRelPos = extractPos(aiMatrixToMat4(node.ai.mTransformation)); */ - /* vec3 newRelPos = extractPos(absoluteToModelSpace(root, *node.parent, absTrans)); */ - /* mat4 rot = getRotationToPoint(oldRelPos, newRelPos, distances[i - 1]); */ - /* node.ai.mTransformation = mat4ToaiMatrix(rot * aiMatrixToMat4(node.ai.mTransformation)); */ - - absTrans[3] = vec4(newPositions[i], absTrans[3][3]); // update position in transform - mat4 relTrans = absoluteToModelSpace(root, *node.parent, absTrans); node.ai.mTransformation = mat4ToaiMatrix(relTrans); } // Now rotate all the nodes so that they point towards each other // Don't need to rotate the last node - it has nothing to point towards - // FIXME: This is numerically unstable and the transformation scales over time!!! + // FIXME: Despite normalizeScale, this is still numerically unstable + // and the transformation scales over time!!! for (size_t i = 0; i < chain.size() - 1; i++) { auto node = chain[i]; auto nextNode = chain[i + 1]; mat4 oldTrans = aiMatrixToMat4(node.ai.mTransformation); vec3 nextNodePos = extractPos(aiMatrixToMat4(nextNode.ai.mTransformation)); - vec3 up = {0, 1, 0}; vec3 dir = -normalize(nextNodePos); @@ -156,11 +154,14 @@ void inverseKinematic(Model::Node &start, Model::Node &end, vec3 target) { -v[1], v[0], 0); mat4 rot = mat3(1) + sscpm + sscpm * sscpm * (1.f / 1.f + dot(up, dir)); - + // very important that we normalize the scale + // otherwise we end up with gradually growing models + // due to numerical instabaility + rot = normalizeScale(rot); node.ai.mTransformation = mat4ToaiMatrix(oldTrans * rot); for (auto child: node.getChildren()) { - child->ai.mTransformation = mat4ToaiMatrix(inverse(rot) * aiMatrixToMat4(child->ai.mTransformation)); + child->ai.mTransformation = mat4ToaiMatrix(normalizeScale(inverse(rot)) * aiMatrixToMat4(child->ai.mTransformation)); } } diff --git a/ik.hpp b/ik.hpp index 2786ffe..7c6ebb1 100644 --- a/ik.hpp +++ b/ik.hpp @@ -1,4 +1,4 @@ #include #include "model.hpp" -void inverseKinematic(Model::Node &end, Model::Node &root, glm::vec3 target); +void inverseKinematics(Model::Node &end, Model::Node &root, glm::vec3 target); diff --git a/main.cpp b/main.cpp index c65e8d2..e4cc81a 100644 --- a/main.cpp +++ b/main.cpp @@ -168,41 +168,19 @@ void display() { glUniform3fv(glGetUniformLocation(pbrProg->progId, "lightPositions"), numLights, glm::value_ptr(lightPositions[0])); glUniform3fv(glGetUniformLocation(pbrProg->progId, "lightColors"), numLights, glm::value_ptr(lightColors[0])); - glm::vec3 targetPos(sin(d) * 2 - 2, 2, 0); +#ifdef COWEDBOY_IK + { + glm::vec3 targetPos(sin(d) * 2 + 3, -2, 1); Light targetLight = { glm::translate(glm::mat4(1), targetPos), {0.5, 1, 1} }; drawLight(targetLight); - inverseKinematic(*sceneModel->find("Bottom Bone"), *sceneModel->find("Toppest Bone"), targetPos); + inverseKinematics(*sceneModel->find("Shoulder.L"), *sceneModel->find("Finger.L"), targetPos); - /* std::array jointPositions; std::array jointDistances; */ - - /* std::array jointNames = { "Bottom Bone", "Middle Bone", "Top Bone" }; */ - /* for (int i = 0; i < 3; i++) { */ - /* glm::mat4 trans; */ - /* findNodeTrans(&sceneModel->getRoot()->ai, aiString(jointNames[i]), &trans); */ - /* jointPositions[i] = glm::vec3(trans[3]); */ - - /* if (i > 0) */ - /* jointDistances[i - 1] = glm::distance(jointPositions[i], jointPositions[i - 1]); */ - /* } */ - - /* glm::vec3 targetPos(sin(d * 10.f), cos(d * 10.f), 0); */ - /* auto newPositions = fabrik(targetPos, jointPositions, jointDistances); */ - - /* for (int i = 0; i < 3; i++) { */ - /* glm::mat4 absTrans(1); */ - /* findNodeTrans(&sceneModel->getRoot()->ai, aiString(jointNames[i]), */ - /* &absTrans); */ - /* glm::mat4 newAbsTrans = absTrans; */ - /* newAbsTrans[3] = glm::vec4(newPositions[i], newAbsTrans[3][3]); */ - - /* auto node = sceneModel->getRoot()->ai.FindNode(jointNames[i].c_str()); */ - - /* auto newTrans = worldSpaceToModelSpace(node->mParent, newAbsTrans); */ - - /* node->mTransformation = mat4ToaiMatrix(newTrans); */ - /* } */ - /* sceneModel->find("Top Bone")->transform = glm::rotate(glm::mat4(1), d / 5.f, { 1, 0, 0}); */ - /* sceneModel->find("Bottom Bone")->transform = glm::rotate(glm::mat4(1), d / 3.f, { 1, 0, 0}); */ + targetPos = { sin(d * 2) * 2 - 5, 2.5, 0 }; + targetLight = { glm::translate(glm::mat4(1), targetPos), {1, 1, 0.5} }; + drawLight(targetLight); + inverseKinematics(*sceneModel->find("Shoulder.R"), *sceneModel->find("Finger.R"), targetPos); + } +#endif sceneModel->draw(skyboxes[activeSkybox], d * 1000); @@ -254,7 +232,7 @@ void init() { pbrProg = new Program("pbrvert.glsl", "pbrfrag.glsl"); glUseProgram(pbrProg->progId); - const std::string scenePath = "models/ik.glb"; + const std::string scenePath = "models/cowedboy.glb"; const aiScene *scene = importer.ReadFile(scenePath, aiProcess_Triangulate | aiProcess_CalcTangentSpace | aiProcess_GenNormals | aiProcess_FlipUVs); if (!scene) { std::cerr << importer.GetErrorString() << std::endl; @@ -312,7 +290,7 @@ void keyboardUp(unsigned char key, int x, int y) { keyStates[key] = false; } -#define ENABLE_MOVEMENT +/* #define ENABLE_MOVEMENT */ void timer(int _) { #ifdef ENABLE_MOVEMENT diff --git a/model.cpp b/model.cpp index 345309a..f4fc307 100644 --- a/model.cpp +++ b/model.cpp @@ -224,6 +224,14 @@ glm::mat4 Model::Node::totalTrans(const glm::mat4 parentTrans, const float tick) return m; } +const Model::Node &Model::Node::getRoot() const { + const Model::Node *rootPtr = this; + while (rootPtr->parent != nullptr) + rootPtr = rootPtr->parent; + const Model::Node &root = *rootPtr; + return root; +} + void Model::Node::draw( const std::vector &meshes, const std::vector &materials, const Skybox skybox, @@ -341,8 +349,6 @@ Model::Model(const aiScene *scene, Program p): program(p) { } } - printHierarchy(scene->mRootNode); - root = new Node(*(scene->mRootNode), p.progId, &animMap, allBones, nullptr); } diff --git a/model.hpp b/model.hpp index e74d657..a3a290c 100644 --- a/model.hpp +++ b/model.hpp @@ -73,6 +73,7 @@ class Model { glm::mat4 totalTrans(const glm::mat4 parentTrans, const float tick) const; const Node *parent; + const Node &getRoot() const; bool operator==(const Node &rhs) const; diff --git a/models/cowedboy.blend b/models/cowedboy.blend index 5fe3aa3..b70977f 100644 Binary files a/models/cowedboy.blend and b/models/cowedboy.blend differ diff --git a/models/cowedboy.glb b/models/cowedboy.glb new file mode 100644 index 0000000..ed08600 Binary files /dev/null and b/models/cowedboy.glb differ diff --git a/models/ik.blend b/models/ik.blend index 37c97a9..d597e6c 100644 Binary files a/models/ik.blend and b/models/ik.blend differ