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) $^ \
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<mat4> trans;
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<Model::Node> 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<vec3> positions(chain.size()); std::vector<float> distances(chain.size() - 1);
for (size_t i = 0; i < chain.size(); i++) {
mat4 absTrans = getAbsTrans(root, chain[i]);
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++) {
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);
-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));
}
}
#include <glm/glm.hpp>
#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);
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<glm::vec3, 3> jointPositions; std::array<float, 2> jointDistances; */
-
- /* std::array<std::string, 3> 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);
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;
keyStates[key] = false;
}
-#define ENABLE_MOVEMENT
+/* #define ENABLE_MOVEMENT */
void timer(int _) {
#ifdef ENABLE_MOVEMENT
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<Mesh> &meshes,
const std::vector<Material> &materials,
const Skybox skybox,
}
}
- printHierarchy(scene->mRootNode);
-
root = new Node(*(scene->mRootNode), p.progId, &animMap, allBones, nullptr);
}
glm::mat4 totalTrans(const glm::mat4 parentTrans, const float tick) const;
const Node *parent;
+ const Node &getRoot() const;
bool operator==(const Node &rhs) const;