skeleton.h 4.79 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2
3

#pragma once

TheNumbat's avatar
TheNumbat committed
4
#include <functional>
TheNumbat's avatar
TheNumbat committed
5
6
7
#include <set>
#include <unordered_map>
#include <unordered_set>
TheNumbat's avatar
TheNumbat committed
8
#include <vector>
TheNumbat's avatar
TheNumbat committed
9
10

#include "../geometry/spline.h"
TheNumbat's avatar
TheNumbat committed
11
#include "../lib/mathlib.h"
TheNumbat's avatar
TheNumbat committed
12
13
14
15
16
#include "../platform/gl.h"

class Joint {
public:
    Joint(unsigned int id) : _id(id) {}
TheNumbat's avatar
TheNumbat committed
17
18
19
20
21
    Joint(unsigned int id, Joint *parent, Vec3 extent) : _id(id), parent(parent), extent(extent) {}
    ~Joint() {
        for (Joint *j : children)
            delete j;
    }
TheNumbat's avatar
TheNumbat committed
22

TheNumbat's avatar
TheNumbat committed
23
24
    Joint(const Joint &src) = delete;
    Joint(Joint &&src) = default;
TheNumbat's avatar
TheNumbat committed
25

TheNumbat's avatar
TheNumbat committed
26
27
    void operator=(const Joint &src) = delete;
    Joint &operator=(Joint &&src) = default;
TheNumbat's avatar
TheNumbat committed
28
29

    unsigned int id() const { return _id; }
TheNumbat's avatar
TheNumbat committed
30

TheNumbat's avatar
TheNumbat committed
31
32
33
34
35
    // Checks if this joint is a root node
    bool is_root() const;

    // Euler angles representing the current joint rotation
    Vec3 pose;
TheNumbat's avatar
TheNumbat committed
36

TheNumbat's avatar
TheNumbat committed
37
    // The vector representing the direction and length of the bone.
TheNumbat's avatar
TheNumbat committed
38
    // This is specified in Joint space, and defines the origin of child bones.
TheNumbat's avatar
TheNumbat committed
39
    Vec3 extent = Vec3(0.0f, 1.0f, 0.0f);
TheNumbat's avatar
TheNumbat committed
40

TheNumbat's avatar
TheNumbat committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    // The distance at which the joint segment should stop effecting vertices
    float radius = 0.25f;

private:
    // Builds the transformation matrix that takes a point in joint space to the mesh
    // space (in bind position). "Bind" position implies that the rotation of all joints
    // should be zero. Also note that this does not include the Skeleton's base_pos,
    // but it should include the transformations of the joint heirachy up to this point.
    Mat4 joint_to_bind() const;

    // Similarly, builds the transformation that takes a point in the space of this joint
    // into mesh space - taking into account the poses of the joint heirarchy. This also does
    // not include the Skeleton's base_pos.
    Mat4 joint_to_posed() const;

    // Pointer to parent joint in the joint heirarchy
TheNumbat's avatar
TheNumbat committed
57
    Joint *parent = nullptr;
TheNumbat's avatar
TheNumbat committed
58
59

    // Set of child joints - owned by this joint (could be shared_ptr and everything else weak_ptr)
TheNumbat's avatar
TheNumbat committed
60
    std::unordered_set<Joint *> children;
TheNumbat's avatar
TheNumbat committed
61

TheNumbat's avatar
TheNumbat committed
62
63
64
65
66
67
68
    // Current angle gradient for IK
    Vec3 angle_gradient;

    // Computes the gradient of IK energy for this joint and, recursively,
    // upward in the heirarchy, storing the result in angle_gradient.
    void compute_gradient(Vec3 target, Vec3 current);

TheNumbat's avatar
TheNumbat committed
69
    void for_joints(std::function<void(Joint *)> func);
TheNumbat's avatar
TheNumbat committed
70
71
72
73
74
75
76
77
78
79

    unsigned int _id = 0;
    Spline<Quat> anim;

    friend class Skeleton;
    friend class Scene;
};

class Skeleton {
public:
TheNumbat's avatar
TheNumbat committed
80
81
82
83
84
85
86
    struct IK_Handle {
        Vec3 target;
        Joint* joint = nullptr;
        bool enabled = false;
        unsigned int _id = 0;
    };

TheNumbat's avatar
TheNumbat committed
87
88
89
90
    Skeleton();
    Skeleton(unsigned int obj_id);
    ~Skeleton();

TheNumbat's avatar
TheNumbat committed
91
92
    Skeleton(const Skeleton &src) = delete;
    Skeleton(Skeleton &&src) = default;
TheNumbat's avatar
TheNumbat committed
93

TheNumbat's avatar
TheNumbat committed
94
95
    void operator=(const Skeleton &src) = delete;
    Skeleton &operator=(Skeleton &&src) = default;
TheNumbat's avatar
TheNumbat committed
96

TheNumbat's avatar
TheNumbat committed
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    ////////////////////////////////////////////
    // You will implement these functions

    Vec3 end_of(Joint *j);
    Vec3 posed_end_of(Joint *j);

    void step_ik(std::vector<IK_Handle*> active_handles);

    Mat4 joint_to_bind(const Joint *j) const;
    Mat4 joint_to_posed(const Joint *j) const;

    void find_joints(const GL::Mesh &src,
                     std::unordered_map<unsigned int, std::vector<Joint *>> &map);
    void skin(const GL::Mesh &input, GL::Mesh &output,
              const std::unordered_map<unsigned int, std::vector<Joint *>> &map);

    ////////////////////////////////////////////

TheNumbat's avatar
TheNumbat committed
115
    Vec3 &base();
TheNumbat's avatar
TheNumbat committed
116
117
    bool has_bones() const;
    unsigned int n_bones();
TheNumbat's avatar
TheNumbat committed
118
    unsigned int n_handles();
TheNumbat's avatar
TheNumbat committed
119

TheNumbat's avatar
TheNumbat committed
120
121
122
123
124
125
126
127
128
    Joint *parent(Joint *j);
    Joint *get_joint(unsigned int id);
    void erase(Joint *j);
    void restore(Joint *j);
    Vec3 base_of(Joint *j);
    Vec3 posed_base_of(Joint *j);

    void for_joints(std::function<void(Joint *)> func);

TheNumbat's avatar
TheNumbat committed
129
130
131
132
133
    void erase(IK_Handle* handle);
    void restore(IK_Handle* handle);
    IK_Handle *get_handle(unsigned int id);
    IK_Handle *add_handle(Vec3 pos, Joint* j);
    bool do_ik();
TheNumbat's avatar
TheNumbat committed
134
135
136

    Joint *add_root(Vec3 extent);
    Joint *add_child(Joint *j, Vec3 extent);
TheNumbat's avatar
TheNumbat committed
137
    bool is_root_id(unsigned int id);
TheNumbat's avatar
TheNumbat committed
138

TheNumbat's avatar
TheNumbat committed
139
    bool set_time(float time);
TheNumbat's avatar
TheNumbat committed
140
141
    void render(const Mat4 &view, Joint *jselect, IK_Handle *hselect, bool root, bool posed, unsigned int offset = 0);
    void outline(const Mat4 &view, bool root, bool posed, BBox &box, unsigned int offset = 0);
TheNumbat's avatar
TheNumbat committed
142
143
144
145
146
147
148
149

    void set(float t);
    void crop(float t);
    void erase(float t);
    bool has_keyframes();
    std::set<float> keys();
    std::unordered_map<unsigned int, Vec3> now();
    std::unordered_map<unsigned int, Vec3> at(float t);
TheNumbat's avatar
TheNumbat committed
150
    void set(float t, const std::unordered_map<unsigned int, Vec3> &data);
TheNumbat's avatar
TheNumbat committed
151
152
153
154

private:
    Vec3 base_pos;
    unsigned int root_id, next_id;
TheNumbat's avatar
TheNumbat committed
155
156
157
    std::unordered_set<Joint *> roots;
    std::unordered_map<Joint*,std::vector<IK_Handle*>> erased;
    std::unordered_set<IK_Handle*> handles, erased_handles;
TheNumbat's avatar
TheNumbat committed
158
159
    friend class Scene;
};