forked from orangeduck/Motion-Matching
-
Notifications
You must be signed in to change notification settings - Fork 0
/
contactcontroller.h
199 lines (171 loc) · 6.15 KB
/
contactcontroller.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#pragma once
#include "common.h"
#include "vec.h"
#include "quat.h"
#include "array.h"
#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <vector>
//--------------------------------------
void contact_reset(
bool& contact_state,
bool& contact_lock,
vec3& contact_position,
vec3& contact_velocity,
vec3& contact_point,
vec3& contact_target,
vec3& contact_offset_position,
vec3& contact_offset_velocity,
const vec3 input_contact_position,
const vec3 input_contact_velocity,
const bool input_contact_state)
{
contact_state = false;
contact_lock = false;
contact_position = input_contact_position;
contact_velocity = input_contact_velocity;
contact_point = input_contact_position;
contact_target = input_contact_position;
contact_offset_position = vec3();
contact_offset_velocity = vec3();
}
void contact_update(
bool& contact_state,
bool& contact_lock,
vec3& contact_position,
vec3& contact_velocity,
vec3& contact_point,
vec3& contact_target,
vec3& contact_offset_position,
vec3& contact_offset_velocity,
const vec3 input_contact_position,
const bool input_contact_state,
const float unlock_radius,
const float foot_height,
const float halflife,
const float dt,
const float eps=1e-8)
{
// First compute the input contact position velocity via finite difference
vec3 input_contact_velocity =
(input_contact_position - contact_target) / (dt + eps);
contact_target = input_contact_position;
// Update the inertializer to tick forward in time
inertialize_update(
contact_position,
contact_velocity,
contact_offset_position,
contact_offset_velocity,
// If locked we feed the contact point and zero velocity,
// otherwise we feed the input from the animation
contact_lock ? contact_point : input_contact_position,
contact_lock ? vec3() : input_contact_velocity,
halflife,
dt);
// If the contact point is too far from the current input position
// then we need to unlock the contact
bool unlock_contact = contact_lock && (
length(contact_point - input_contact_position) > unlock_radius);
// If the contact was previously inactive but is now active we
// need to transition to the locked contact state
if (!contact_state && input_contact_state)
{
// Contact point is given by the current position of
// the foot projected onto the ground plus foot height
contact_lock = true;
contact_point = contact_position;
contact_point.y = foot_height;
inertialize_transition(
contact_offset_position,
contact_offset_velocity,
input_contact_position,
input_contact_velocity,
contact_point,
vec3());
}
// Otherwise if we need to unlock or we were previously in
// contact but are no longer we transition to just taking
// the input position as-is
else if ((contact_lock && contact_state && !input_contact_state)
|| unlock_contact)
{
contact_lock = false;
inertialize_transition(
contact_offset_position,
contact_offset_velocity,
contact_point,
vec3(),
input_contact_position,
input_contact_velocity);
}
// Update contact state
contact_state = input_contact_state;
}
//--------------------------------------
// Rotate a joint to look toward some
// given target position
void ik_look_at(
quat& bone_rotation,
const quat global_parent_rotation,
const quat global_rotation,
const vec3 global_position,
const vec3 child_position,
const vec3 target_position,
const float eps = 1e-5f)
{
vec3 curr_dir = normalize(child_position - global_position);
vec3 targ_dir = normalize(target_position - global_position);
if (fabs(1.0f - dot(curr_dir, targ_dir) > eps))
{
bone_rotation = quat_inv_mul(global_parent_rotation,
quat_mul(quat_between(curr_dir, targ_dir), global_rotation));
}
}
// Basic two-joint IK in the style of https://theorangeduck.com/page/simple-two-joint
// Here I add a basic "forward vector" which acts like a kind of pole-vetor
// to control the bending direction
void ik_two_bone(
quat& bone_root_lr,
quat& bone_mid_lr,
const vec3 bone_root,
const vec3 bone_mid,
const vec3 bone_end,
const vec3 target,
const vec3 fwd,
const quat bone_root_gr,
const quat bone_mid_gr,
const quat bone_par_gr,
const float max_length_buffer) {
float max_extension =
length(bone_root - bone_mid) +
length(bone_mid - bone_end) -
max_length_buffer;
vec3 target_clamp = target;
if (length(target - bone_root) > max_extension)
{
target_clamp = bone_root + max_extension * normalize(target - bone_root);
}
vec3 axis_dwn = normalize(bone_end - bone_root);
vec3 axis_rot = normalize(cross(axis_dwn, fwd));
vec3 a = bone_root;
vec3 b = bone_mid;
vec3 c = bone_end;
vec3 t = target_clamp;
float lab = length(b - a);
float lcb = length(b - c);
float lat = length(t - a);
float ac_ab_0 = acosf(clampf(dot(normalize(c - a), normalize(b - a)), -1.0f, 1.0f));
float ba_bc_0 = acosf(clampf(dot(normalize(a - b), normalize(c - b)), -1.0f, 1.0f));
float ac_ab_1 = acosf(clampf((lab * lab + lat * lat - lcb * lcb) / (2.0f * lab * lat), -1.0f, 1.0f));
float ba_bc_1 = acosf(clampf((lab * lab + lcb * lcb - lat * lat) / (2.0f * lab * lcb), -1.0f, 1.0f));
quat r0 = quat_from_angle_axis(ac_ab_1 - ac_ab_0, axis_rot);
quat r1 = quat_from_angle_axis(ba_bc_1 - ba_bc_0, axis_rot);
vec3 c_a = normalize(bone_end - bone_root);
vec3 t_a = normalize(target_clamp - bone_root);
quat r2 = quat_from_angle_axis(
acosf(clampf(dot(c_a, t_a), -1.0f, 1.0f)),
normalize(cross(c_a, t_a)));
bone_root_lr = quat_inv_mul(bone_par_gr, quat_mul(r2, quat_mul(r0, bone_root_gr)));
bone_mid_lr = quat_inv_mul(bone_root_gr, quat_mul(r1, bone_mid_gr));
}