Files
armorpaint/base/sources/ts/anim_bone.ts
T

721 lines
22 KiB
TypeScript
Raw Normal View History

2024-11-10 22:11:29 +01:00
///if arm_skin
type anim_bone_t = {
base?: anim_raw_t;
object?: mesh_object_t;
data?: mesh_data_t;
skin_buffer?: f32_array_t;
skeleton_bones?: obj_t[];
skeleton_mats?: mat4_t[];
skeleton_bones_blend?: obj_t[];
skeleton_mats_blend?: mat4_t[];
abs_mats?: mat4_t[];
apply_parent?: bool[];
mats_fast?: mat4_t[];
mats_fast_sort?: i32[];
mats_fast_blend?: mat4_t[];
mats_fast_blend_sort?: i32[];
bone_children?: map_t<string, object_t[]>; // Parented to bone
// Do inverse kinematics here
on_updates?: (()=>void)[];
};
let _anim_bone_skin_max_bones: i32 = 128;
let _anim_bone_m: mat4_t = mat4_identity(); // Skinning matrix
let _anim_bone_m1: mat4_t = mat4_identity();
let _anim_bone_m2: mat4_t = mat4_identity();
let _anim_bone_bm: mat4_t = mat4_identity(); // Absolute bone matrix
let _anim_bone_wm: mat4_t = mat4_identity();
let _anim_bone_vpos: vec4_t = vec4_create();
let _anim_bone_vscale: vec4_t = vec4_create();
let _anim_bone_q1: quat_t = quat_create();
let _anim_bone_q2: quat_t = quat_create();
let _anim_bone_q3: quat_t = quat_create();
let _anim_bone_vpos2: vec4_t = vec4_create();
let _anim_bone_vscale2: vec4_t = vec4_create();
let _anim_bone_v1: vec4_t = vec4_create();
let _anim_bone_v2: vec4_t = vec4_create();
function anim_bone_create(armature_name: string = ""): anim_bone_t {
let raw: anim_bone_t = {};
raw.mats_fast = [];
raw.mats_fast_sort = [];
raw.mats_fast_blend = [];
raw.mats_fast_blend_sort = [];
raw.base = anim_create();
raw.base.ext = raw;
raw.base.ext_type = "anim_bone_t";
for (let i: i32 = 0; i < scene_armatures.length; ++i) {
let a: armature_t = scene_armatures[i];
if (a.name == armature_name) {
raw.base.armature = a;
break;
}
}
return raw;
}
function anim_bone_set_skin(raw: anim_bone_t, mo: mesh_object_t) {
raw.object = mo;
raw.data = mo != null ? mo.data : null;
raw.base.is_skinned = raw.data != null ? raw.data.skin != null : false;
if (raw.base.is_skinned) {
let bone_size: i32 = 8; // Dual-quat skinning
raw.skin_buffer = f32_array_create(_anim_bone_skin_max_bones * bone_size);
for (let i: i32 = 0; i < raw.skin_buffer.length; ++i) {
raw.skin_buffer[i] = 0;
}
// Rotation is already applied to skin at export
raw.object.base.transform.rot = quat_create(0, 0, 0, 1);
transform_build_matrix(raw.object.base.transform);
let refs: string[] = mo.base.parent.raw.anim.bone_actions;
if (refs != null && refs.length > 0) {
let action: scene_t = data_get_scene_raw(refs[0]);
anim_bone_play(raw, action.name);
}
}
}
function anim_bone_add_bone_child(raw: anim_bone_t, bone: string, o: object_t) {
if (raw.bone_children == null) {
raw.bone_children = map_create();
}
let ar: object_t[] = map_get(raw.bone_children, bone);
if (ar == null) {
ar = [];
map_set(raw.bone_children, bone, ar);
}
array_push(ar, o);
}
function anim_bone_remove_bone_child(raw: anim_bone_t, bone: string, o: object_t) {
if (raw.bone_children != null) {
let ar: object_t[] = map_get(raw.bone_children, bone);
if (ar != null) {
array_remove(ar, o);
}
}
}
function anim_bone_update_bone_children(raw: anim_bone_t, bone: obj_t, bm: mat4_t) {
let ar: object_t[] = map_get(raw.bone_children, bone.name);
if (ar == null) {
return;
}
for (let i: i32 = 0; i < ar.length; ++i) {
let o: object_t = ar[i];
let t: transform_t = o.transform;
if (t.bone_parent == null) {
t.bone_parent = mat4_identity();
}
if (o.raw.anim.parent_bone_tail != null) {
if (o.raw.anim.parent_bone_connected || raw.base.is_skinned) {
let v: f32_array_t = o.raw.anim.parent_bone_tail;
t.bone_parent = mat4_init_translate(v[0], v[1], v[2]);
t.bone_parent = mat4_mult_mat(t.bone_parent, bm);
}
else {
let v: f32_array_t = o.raw.anim.parent_bone_tail_pose;
t.bone_parent = mat4_clone(bm);
t.bone_parent = mat4_translate(t.bone_parent, v[0], v[1], v[2]);
}
}
else {
t.bone_parent = mat4_clone(bm);
}
transform_build_matrix(t);
}
}
function anim_bone_num_parents(b: obj_t): i32 {
let i: i32 = 0;
let p: obj_t = b.parent;
while (p != null) {
i++;
p = p.parent;
}
return i;
}
let _anim_bone_sort_data: any;
function anim_bone_sort(pa: any_ptr, pb: any_ptr) {
let a: i32 = DEREFERENCE(pa);
let b: i32 = DEREFERENCE(pb);
let i: i32 = anim_bone_num_parents(_anim_bone_sort_data[a]);
let j: i32 = anim_bone_num_parents(_anim_bone_sort_data[b]);
return i < j ? -1 : i > j ? 1 : 0;
}
function anim_bone_set_mats(raw: anim_bone_t) {
while (raw.mats_fast.length < raw.skeleton_bones.length) {
array_push(raw.mats_fast, mat4_identity());
array_push(raw.mats_fast_sort, raw.mats_fast_sort.length);
}
// Calc bones with 0 parents first
_anim_bone_sort_data = raw.skeleton_bones;
array_sort(raw.mats_fast_sort, anim_bone_sort);
if (raw.skeleton_bones_blend != null) {
while (raw.mats_fast_blend.length < raw.skeleton_bones_blend.length) {
array_push(raw.mats_fast_blend, mat4_identity());
array_push(raw.mats_fast_blend_sort, raw.mats_fast_blend_sort.length);
}
_anim_bone_sort_data = raw.skeleton_bones_blend;
array_sort(raw.mats_fast_blend_sort, anim_bone_sort);
}
}
function anim_bone_set_action(raw: anim_bone_t, action: string) {
if (raw.base.is_skinned) {
raw.skeleton_bones = map_get(raw.data._actions, action);
raw.skeleton_mats = map_get(raw.data._mats, action);
raw.skeleton_bones_blend = null;
raw.skeleton_mats_blend = null;
}
else {
armature_init_mats(raw.base.armature);
let a = armature_get_action(raw.base.armature, action);
raw.skeleton_bones = a.bones;
raw.skeleton_mats = a.mats;
}
anim_bone_set_mats(raw);
}
function anim_bone_set_action_blend(raw: anim_bone_t, action: string) {
if (raw.base.is_skinned) {
raw.skeleton_bones_blend = raw.skeleton_bones;
raw.skeleton_mats_blend = raw.skeleton_mats;
raw.skeleton_bones = map_get(raw.data._actions, action);
raw.skeleton_mats = map_get(raw.data._mats, action);
}
else {
armature_init_mats(raw.base.armature);
let a: armature_action_t = armature_get_action(raw.base.armature, action);
raw.skeleton_bones = a.bones;
raw.skeleton_mats = a.mats;
}
anim_bone_set_mats(raw);
}
function anim_bone_mult_parent(raw: anim_bone_t, i: i32, fasts: mat4_t[], bones: obj_t[], mats: mat4_t[]) {
let f: mat4_t = fasts[i];
if (raw.apply_parent != null && !raw.apply_parent[i]) {
f = mat4_clone(mats[i]);
return;
}
let p = bones[i].parent;
let bi = anim_bone_get_bone_index(raw, p, bones);
if (p == null || bi == -1) {
f = mat4_clone(mats[i]);
}
else {
f = mat4_mult_mat(mats[i], fasts[bi]);
}
}
function anim_bone_mult_parents(raw: anim_bone_t, m: mat4_t, i: i32, bones: obj_t[], mats: mat4_t[]) {
let bone: obj_t = bones[i];
let p: obj_t = bone.parent;
while (p != null) {
let i: i32 = anim_bone_get_bone_index(raw, p, bones);
if (i == -1) {
continue;
}
m = mat4_mult_mat(m, mats[i]);
p = p.parent;
}
}
function anim_bone_notify_on_update(raw: anim_bone_t, f: ()=>void) {
if (raw.on_updates == null) {
raw.on_updates = [];
}
array_push(raw.on_updates, f);
}
function anim_bone_remove_update(raw: anim_bone_t, f: ()=>void) {
array_remove(raw.on_updates, f);
}
function anim_bone_update_bones_only(raw: anim_bone_t) {
if (raw.bone_children != null) {
for (let i: i32 = 0; i < raw.skeleton_bones.length; ++i) {
let b = raw.skeleton_bones[i]; // TODO: blend_time > 0
_anim_bone_m = mat4_clone(raw.mats_fast[i]);
anim_bone_update_bone_children(raw, b, _anim_bone_m);
}
}
}
function anim_bone_update_skin_gpu(raw: anim_bone_t) {
let bones: obj_t[] = raw.skeleton_bones;
let s: f32 = raw.base.blend_current / raw.base.blend_time;
s = s * s * (3.0 - 2.0 * s); // Smoothstep
if (raw.base.blend_factor != 0.0) {
s = 1.0 - raw.base.blend_factor;
}
// Update skin buffer
for (let i: i32 = 0; i < bones.length; ++i) {
_anim_bone_m = mat4_clone(raw.mats_fast[i]);
if (raw.base.blend_time > 0 && raw.skeleton_bones_blend != null) {
// Decompose
_anim_bone_m1 = mat4_clone(raw.mats_fast_blend[i]);
mat4_decompose(_anim_bone_m1, _anim_bone_vpos, _anim_bone_q1, _anim_bone_vscale);
mat4_decompose(_anim_bone_m, _anim_bone_vpos2, _anim_bone_q2, _anim_bone_vscale2);
// Lerp
_anim_bone_v1 = vec4_lerp(_anim_bone_vpos, _anim_bone_vpos2, s);
_anim_bone_v2 = vec4_lerp(_anim_bone_vscale, _anim_bone_vscale2, s);
_anim_bone_q3 = quat_lerp(_anim_bone_q1, _anim_bone_q2, s);
// Compose
_anim_bone_m = mat4_from_quat(_anim_bone_q3);
_anim_bone_m = mat4_scale(_anim_bone_m, _anim_bone_v2);
_anim_bone_m.m30 = _anim_bone_v1.x;
_anim_bone_m.m31 = _anim_bone_v1.y;
_anim_bone_m.m32 = _anim_bone_v1.z;
}
if (raw.abs_mats != null && i < raw.abs_mats.length) {
raw.abs_mats[i] = mat4_clone(_anim_bone_m);
}
if (raw.bone_children != null) {
anim_bone_update_bone_children(raw, bones[i], _anim_bone_m);
}
_anim_bone_m = mat4_mult_mat(raw.data._skeleton_transforms_inv[i], _anim_bone_m);
anim_bone_update_skin_buffer(raw, _anim_bone_m, i);
}
}
function anim_bone_update_skin_buffer(raw: anim_bone_t, m: mat4_t, i: i32) {
// Dual quat skinning
mat4_decompose(m, _anim_bone_vpos, _anim_bone_q1, _anim_bone_vscale);
_anim_bone_q1 = quat_norm(_anim_bone_q1);
_anim_bone_q2 = quat_create(_anim_bone_vpos.x, _anim_bone_vpos.y, _anim_bone_vpos.z, 0.0);
_anim_bone_q2 = quat_mult(_anim_bone_q2, _anim_bone_q1);
raw.skin_buffer[i * 8] = _anim_bone_q1.x; // Real
raw.skin_buffer[i * 8 + 1] = _anim_bone_q1.y;
raw.skin_buffer[i * 8 + 2] = _anim_bone_q1.z;
raw.skin_buffer[i * 8 + 3] = _anim_bone_q1.w;
raw.skin_buffer[i * 8 + 4] = _anim_bone_q2.x * 0.5; // Dual
raw.skin_buffer[i * 8 + 5] = _anim_bone_q2.y * 0.5;
raw.skin_buffer[i * 8 + 6] = _anim_bone_q2.z * 0.5;
raw.skin_buffer[i * 8 + 7] = _anim_bone_q2.w * 0.5;
}
function anim_bone_get_bone(raw: anim_bone_t, name: string): obj_t {
if (raw.skeleton_bones == null) {
return null;
}
for (let i: i32 = 0; i < raw.skeleton_bones.length; ++i) {
let b: obj_t = raw.skeleton_bones[i];
if (b.name == name) {
return b;
}
}
return null;
}
function anim_bone_get_bone_index(raw: anim_bone_t, bone: obj_t, bones: obj_t[] = null): i32 {
if (bones == null) {
bones = raw.skeleton_bones;
}
if (bones != null) {
for (let i: i32 = 0; i < bones.length; ++i) {
if (bones[i] == bone) {
return i;
}
}
}
return -1;
}
function anim_bone_get_bone_mat(raw: anim_bone_t, bone: obj_t): mat4_t {
return raw.skeleton_mats != null ? raw.skeleton_mats[anim_bone_get_bone_index(raw, bone)] : null;
}
function anim_bone_get_bone_mat_blend(raw: anim_bone_t, bone: obj_t): mat4_t {
return raw.skeleton_mats_blend != null ? raw.skeleton_mats_blend[anim_bone_get_bone_index(raw, bone)] : null;
}
function anim_bone_get_abs_mat(raw: anim_bone_t, bone: obj_t): mat4_t {
// With applied blending
if (raw.skeleton_mats == null) {
return null;
}
if (raw.abs_mats == null) {
raw.abs_mats = [];
while (raw.abs_mats.length < raw.skeleton_mats.length) {
array_push(raw.abs_mats, mat4_identity());
}
}
return raw.abs_mats[anim_bone_get_bone_index(raw, bone)];
}
function anim_bone_get_world_mat(raw: anim_bone_t, bone: obj_t): mat4_t {
if (raw.skeleton_mats == null) {
return null;
}
if (raw.apply_parent == null) {
raw.apply_parent = [];
for (let i: i32 = 0; i < raw.skeleton_mats.length; ++i) {
array_push(raw.apply_parent, true);
}
}
let i: i32 = anim_bone_get_bone_index(raw, bone);
_anim_bone_wm = mat4_clone(raw.skeleton_mats[i]);
anim_bone_mult_parents(raw, _anim_bone_wm, i, raw.skeleton_bones, raw.skeleton_mats);
// anim_bone_wm = mat4_clone(raw.mats_fast[i]); // TODO
return _anim_bone_wm;
}
function anim_bone_get_bone_len(raw: anim_bone_t, bone: obj_t): f32 {
let refs: string[] = raw.data.skin.bone_ref_array;
let lens: f32_array_t = raw.data.skin.bone_len_array;
for (let i: i32 = 0; i < refs.length; ++i) {
if (refs[i] == bone.name) {
return lens[i];
}
}
return 0.0;
}
// Returns bone length with scale applied
function anim_bone_get_bone_abs_len(raw: anim_bone_t, bone: obj_t): f32 {
let refs: string[] = raw.data.skin.bone_ref_array;
let lens: f32_array_t = raw.data.skin.bone_len_array;
let scale: f32 = mat4_get_scale(raw.object.base.parent.transform.world).z;
for (let i: i32 = 0; i < refs.length; ++i) {
if (refs[i] == bone.name) {
return lens[i] * scale;
}
}
return 0.0;
}
// Returns bone matrix in world space
function anim_bone_get_abs_world_mat(raw: anim_bone_t, bone: obj_t): mat4_t {
let wm = anim_bone_get_world_mat(raw, bone);
wm = mat4_mult_mat(wm, raw.object.base.parent.transform.world);
return wm;
}
function anim_bone_solve_ik(raw: anim_bone_t, effector: obj_t, goal: vec4_t, precision: f32 = 0.01, max_iters: i32 = 100, chain_lenght: i32 = 100, roll_angle: f32 = 0.0) {
// Array of bones to solve IK for, effector at 0
let bones: obj_t[] = [];
// Array of bones lengths, effector length at 0
let lengths: f32[] = [];
// Array of bones matrices in world coordinates, effector at 0
let bone_world_mats: mat4_t[];
let temp_loc: vec4_t = vec4_create();
let temp_rot: quat_t = quat_create();
let temp_rot2: quat_t = quat_create();
let temp_scale: vec4_t = vec4_create();
let roll: quat_t = quat_from_euler(0, roll_angle, 0);
// Store all bones and lengths in array
let tip: obj_t = effector;
array_push(bones, tip);
array_push(lengths, anim_bone_get_bone_abs_len(raw, tip));
let root: obj_t = tip;
while (root.parent != null) {
if (bones.length > chain_lenght - 1) {
break;
}
array_push(bones, root.parent);
array_push(lengths, anim_bone_get_bone_abs_len(raw, root.parent));
root = root.parent;
}
// Root bone
root = bones[bones.length - 1];
// World matrix of root bone
let root_world_mat: mat4_t = mat4_clone(anim_bone_get_world_mat(raw, root));
// World matrix of armature
let armature_mat: mat4_t = mat4_clone(raw.object.base.parent.transform.world);
// Apply armature transform to world matrix
root_world_mat = mat4_mult_mat(root_world_mat, armature_mat);
// Distance from root to goal
let dist: f32 = vec4_dist(goal, mat4_get_loc(root_world_mat));
// Total bones length
let total_length: f32 = 0.0;
for (let i: i32 = 0; i < lengths.length; ++i) {
let l: f32 = lengths[i];
total_length += l;
}
// Unreachable distance
if (dist > total_length) {
// Calculate unit vector from root to goal
let new_look: vec4_t = vec4_clone(goal);
new_look = vec4_sub(new_look, mat4_get_loc(root_world_mat));
new_look = vec4_norm(new_look);
// Rotate root bone to point at goal
mat4_decompose(root_world_mat, temp_loc, temp_rot, temp_scale);
temp_rot2 = quat_from_to(vec4_norm(mat4_look(root_world_mat)), new_look);
temp_rot2 = quat_mult(temp_rot2, temp_rot);
temp_rot2 = quat_mult(temp_rot2, roll);
root_world_mat = mat4_compose(temp_loc, temp_rot2, temp_scale);
// Set bone matrix in local space from world space
anim_bone_set_bone_mat_from_world_mat(raw, root_world_mat, root);
// Set child bone rotations to zero
for (let i: i32 = 0; i < bones.length - 1; ++i) {
mat4_decompose(anim_bone_get_bone_mat(raw, bones[i]), temp_loc, temp_rot, temp_scale);
mat4_compose(anim_bone_get_bone_mat(raw, bones[i]), temp_loc, roll, temp_scale);
}
return;
}
// Get all bone mats in world space
bone_world_mats = anim_bone_get_world_mats_fast(raw, effector, bones.length);
// Array of bone locations in world space, root location at [0]
let bone_world_locs: vec4_t[] = [];
for (let i: i32 = 0; i < bone_world_mats.length; ++i) {
let b: mat4_t = bone_world_mats[i];
array_push(bone_world_locs, mat4_get_loc(b));
}
// Solve FABRIK
let vec: vec4_t = vec4_create();
let start_loc: vec4_t = vec4_clone(bone_world_locs[0]);
let l: i32 = bone_world_locs.length;
for (let iter: i32 = 0; iter < max_iters; ++iter) {
// Backward
vec = vec4_clone(goal);
vec = vec4_sub(vec, bone_world_locs[l - 1]);
vec = vec4_norm(vec);
vec = vec4_mult(vec, lengths[0]);
bone_world_locs[l - 1] = vec4_clone(goal);
bone_world_locs[l - 1] = vec4_sub(bone_world_locs[l - 1], vec);
for (let j: i32 = 1; j < l; ++j) {
vec = vec4_clone(bone_world_locs[l - 1 - j]);
vec = vec4_sub(vec, bone_world_locs[l - j]);
vec = vec4_norm(vec);
vec = vec4_mult(vec, lengths[j]);
bone_world_locs[l - 1 - j] = vec4_clone(bone_world_locs[l - j]);
bone_world_locs[l - 1 - j] = vec4_add(bone_world_locs[l - 1 - j], vec);
}
// Forward
bone_world_locs[0] = vec4_clone(start_loc);
for (let j: i32 = 1; j < l; ++j) {
vec = vec4_clone(bone_world_locs[j]);
vec = vec4_sub(vec, bone_world_locs[j - 1]);
vec = vec4_norm(vec);
vec = vec4_mult(vec, lengths[l - j]);
bone_world_locs[j] = vec4_clone(bone_world_locs[j - 1]);
bone_world_locs[j] = vec4_add(bone_world_locs[j], vec);
}
if (vec4_dist(bone_world_locs[l - 1], goal) - lengths[0] <= precision) {
break;
}
}
// Correct rotations
// Applying locations and rotations
let temp_look: vec4_t = vec4_create();
let temp_loc2: vec4_t = vec4_create();
for (let i: i32 = 0; i < l - 1; ++i) {
// Decompose matrix
mat4_decompose(bone_world_mats[i], temp_loc, temp_rot, temp_scale);
// Rotate to point to parent bone
temp_loc2 = vec4_clone(bone_world_locs[i + 1]);
temp_loc2 = vec4_sub(temp_loc2, bone_world_locs[i]);
temp_loc2 = vec4_norm(temp_loc2);
temp_look = vec4_clone(mat4_look(bone_world_mats[i]));
temp_look = vec4_norm(temp_look);
temp_rot2 = quat_from_to(temp_look, temp_loc2);
temp_rot2 = quat_mult(temp_rot2, temp_rot);
temp_rot2 = quat_mult(temp_rot2, roll);
// Compose matrix with new rotation and location
bone_world_mats[i] = mat4_compose(bone_world_locs[i], temp_rot2, temp_scale);
// Set bone matrix in local space from world space
anim_bone_set_bone_mat_from_world_mat(raw, bone_world_mats[i], bones[bones.length - 1 - i]);
}
// Decompose matrix
mat4_decompose(bone_world_mats[l - 1], temp_loc, temp_rot, temp_scale);
// Rotate to point to goal
temp_loc2 = vec4_clone(goal);
temp_loc2 = vec4_sub(temp_loc2, temp_loc);
temp_loc2 = vec4_norm(temp_loc2);
temp_look = vec4_clone(mat4_look(bone_world_mats[l - 1]));
temp_look = vec4_norm(temp_look);
temp_rot2 = quat_from_to(temp_look, temp_loc2);
temp_rot2 = quat_mult(temp_rot2, temp_rot);
temp_rot2 = quat_mult(temp_rot2, roll);
// Compose matrix with new rotation and location
bone_world_mats[l - 1] = mat4_compose(bone_world_locs[l - 1], temp_rot2, temp_scale);
// Set bone matrix in local space from world space
anim_bone_set_bone_mat_from_world_mat(raw, bone_world_mats[l - 1], bones[0]);
}
// Returns an array of bone matrices in world space
function anim_bone_get_world_mats_fast(raw: anim_bone_t, tip: obj_t, chain_len: i32): mat4_t[] {
let wm_array: mat4_t[] = [];
let root: obj_t = tip;
let num_p: i32 = chain_len;
for (let i: i32 = 0; i < chain_len; ++i) {
let wm: mat4_t = anim_bone_get_abs_world_mat(raw, root);
wm_array[chain_len - 1 - i] = mat4_clone(wm);
root = root.parent;
num_p--;
}
// Root bone at [0]
return wm_array;
}
// Set bone transforms in world space
function anim_bone_set_bone_mat_from_world_mat(raw: anim_bone_t, wm: mat4_t, bone: obj_t) {
let inv_mat: mat4_t = mat4_identity();
let temp_mat: mat4_t = mat4_clone(wm);
inv_mat = mat4_inv(raw.object.base.parent.transform.world);
temp_mat = mat4_mult_mat(temp_mat, inv_mat);
let bones: obj_t[] = [];
let p_bone: obj_t = bone;
while (p_bone.parent != null) {
array_push(bones, p_bone.parent);
p_bone = p_bone.parent;
}
for (let i: i32 = 0; i < bones.length; ++i) {
let x: i32 = bones.length - 1;
inv_mat = mat4_inv(anim_bone_get_bone_mat(raw, bones[x - i]));
temp_mat = mat4_mult_mat(temp_mat, inv_mat);
}
mat4_set_from(anim_bone_get_bone_mat(raw, bone), temp_mat);
}
function anim_bone_play(raw: anim_bone_t, action: string = "", on_complete: ()=>void = null, blend_time: f32 = 0.2, speed: f32 = 1.0, loop: bool = true) {
if (action != "") {
blend_time > 0 ? anim_bone_set_action_blend(raw, action) : anim_bone_set_action(raw, action);
}
raw.base.blend_factor = 0.0;
}
function anim_bone_blend(raw: anim_bone_t, action1: string, action2: string, factor: f32) {
if (factor == 0.0) {
anim_bone_set_action(raw, action1);
return;
}
anim_bone_set_action(raw, action2);
anim_bone_set_action_blend(raw, action1);
anim_blend_super(raw.base, action1, action2, factor);
}
function anim_bone_update(raw: anim_bone_t, delta: f32) {
if (!raw.base.is_skinned && raw.skeleton_bones == null) {
anim_bone_set_action(raw, raw.base.armature.actions[0].name);
}
if (raw.object != null && (!raw.object.base.visible || raw.object.base.culled)) {
return;
}
if (raw.skeleton_bones == null || raw.skeleton_bones.length == 0) {
return;
}
anim_update_super(raw.base, delta);
if (raw.base.paused || raw.base.speed == 0.0) {
return;
}
let last_bones: obj_t[] = raw.skeleton_bones;
for (let i: i32 = 0; i < raw.skeleton_bones.length; ++i) {
let b: obj_t = raw.skeleton_bones[i];
if (b.anim != null) {
anim_update_track(raw.base, b.anim);
break;
}
}
// Action has been changed by on_complete
if (last_bones != raw.skeleton_bones) {
return;
}
for (let i: i32 = 0; i < raw.skeleton_bones.length; ++i) {
anim_update_anim_sampled(raw.base, raw.skeleton_bones[i].anim, raw.skeleton_mats[i]);
}
if (raw.base.blend_time > 0 && raw.skeleton_bones_blend != null) {
for (let i: i32 = 0; i < raw.skeleton_bones_blend.length; ++i) {
let b: obj_t = raw.skeleton_bones_blend[i];
if (b.anim != null) {
anim_update_track(raw.base, b.anim);
break;
}
}
for (let i: i32 = 0; i < raw.skeleton_bones_blend.length; ++i) {
anim_update_anim_sampled(raw.base, raw.skeleton_bones_blend[i].anim, raw.skeleton_mats_blend[i]);
}
}
// Do forward kinematics and inverse kinematics here
if (raw.on_updates != null) {
let i: i32 = 0;
let l: i32 = raw.on_updates.length;
while (i < l) {
raw.on_updates[i]();
l <= raw.on_updates.length ? i++ : l = raw.on_updates.length;
}
}
// Calc absolute bones
for (let i: i32 = 0; i < raw.skeleton_bones.length; ++i) {
// Take bones with 0 parents first
anim_bone_mult_parent(raw, raw.mats_fast_sort[i], raw.mats_fast, raw.skeleton_bones, raw.skeleton_mats);
}
if (raw.skeleton_bones_blend != null) {
for (let i: i32 = 0; i < raw.skeleton_bones_blend.length; ++i) {
anim_bone_mult_parent(raw, raw.mats_fast_blend_sort[i], raw.mats_fast_blend, raw.skeleton_bones_blend, raw.skeleton_mats_blend);
}
}
if (raw.base.is_skinned) {
anim_bone_update_skin_gpu(raw);
}
else {
anim_bone_update_bones_only(raw);
}
}
function anim_bone_total_frames(raw: anim_bone_t): i32 {
if (raw.skeleton_bones == null) {
return 0;
}
let track: track_t = raw.skeleton_bones[0].anim.tracks[0];
return math_floor(track.frames[track.frames.length - 1] - track.frames[0]);
}
///end