This commit is contained in:
luboslenco
2025-08-11 23:08:58 +02:00
parent c6dbfb40fb
commit 3b3269f033
24 changed files with 9 additions and 1436 deletions
Binary file not shown.
-1
View File
@@ -83,7 +83,6 @@ if (!flags.lite) {
project.add_define("arm_physics");
}
// project.add_define("arm_skin");
// project.add_define("arm_audio");
if (platform === "android") {
+1 -20
View File
@@ -2,13 +2,9 @@
///if arm_anim
type anim_raw_t = {
ext?: any; // anim_bone_t | anim_object_t
ext?: any; // anim_object_t
ext_type?: string;
is_skinned?: bool;
action?: string;
///if arm_skin
armature?: armature_t; // Bone
///end
time?: f32;
speed?: f32;
loop?: bool;
@@ -77,11 +73,6 @@ function anim_play(raw: anim_raw_t, action: string = "", on_complete: ()=>void =
if (raw.ext_type == "anim_object_t") {
anim_object_play(raw.ext, action, on_complete, blend_time, speed, loop);
}
///if arm_skin
else if (raw.ext_type == "anim_bone_t") {
anim_bone_play(raw.ext, action, on_complete, blend_time, speed, loop);
}
///end
else {
anim_play_super(raw, action, on_complete, blend_time, speed, loop);
}
@@ -94,11 +85,6 @@ function anim_blend_super(raw: anim_raw_t, action1: string, action2: string, fac
function anim_blend(raw: anim_raw_t, action1: string, action2: string, factor: f32) {
if (raw.ext != null) {
///if arm_skin
if (raw.ext_type == "anim_bone_t") {
anim_bone_blend(raw.ext, action1, action2, factor);
}
///end
}
else {
anim_blend_super(raw, action1, action2, factor);
@@ -135,11 +121,6 @@ function anim_update(raw: anim_raw_t, delta: f32) {
if (raw.ext_type == "anim_object_t") {
anim_object_update(raw.ext, delta);
}
///if arm_skin
else if (raw.ext_type == "anim_bone_t") {
anim_bone_update(raw.ext, delta);
}
///end
else {
anim_update_super(raw, delta);
}
-720
View File
@@ -1,720 +0,0 @@
///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
+2 -5
View File
@@ -12,10 +12,9 @@ function anim_object_create(object: object_t, oactions: scene_t[]): anim_object_
let raw: anim_object_t = {};
raw.base = anim_create();
raw.base.ext = raw;
raw.base.ext_type = "anim_bone_t";
raw.base.ext_type = "anim_object_t";
raw.object = object;
raw.oactions = oactions;
raw.base.is_skinned = false;
anim_object_play(raw);
return raw;
}
@@ -209,9 +208,7 @@ function anim_object_update(raw: anim_object_t, delta: f32) {
if (raw.base.paused) {
return;
}
if (!raw.base.is_skinned) {
anim_object_update_object_anim(raw);
}
anim_object_update_object_anim(raw);
}
function anim_object_is_track_end(raw: anim_object_t, track: track_t): bool {
-103
View File
@@ -1,103 +0,0 @@
///if arm_skin
type armature_t = {
uid?: i32;
name?: string;
actions?: armature_action_t[];
mats_ready?: bool;
};
type armature_action_t = {
name?: string;
bones?: obj_t[];
mats?: mat4_t[];
};
let _armature_traverse_bones_data: any;
function armature_traverse_bones_done(object: obj_t) {
array_push(_armature_traverse_bones_data, object);
}
function armature_create(uid: i32, name: string, actions: scene_t[]): armature_t {
let raw: armature_t = {};
raw.actions = [];
raw.mats_ready = false;
raw.uid = uid;
raw.name = name;
for (let i: i32 = 0; i < actions.length; ++i) {
let a: scene_t = actions[i];
for (let i: i32 = 0; i < a.objects.length; ++i) {
let o: obj_t = a.objects[i];
armature_set_parents(o);
}
let bones: obj_t[] = [];
_armature_traverse_bones_data = bones;
armature_traverse_bones(a.objects, armature_traverse_bones_done);
array_push(raw.actions, { name: a.name, bones: bones, mats: null });
}
return raw;
}
function armature_init_mats(raw: armature_t) {
if (raw.mats_ready) {
return;
}
raw.mats_ready = true;
for (let i: i32 = 0; i < raw.actions.length; ++i) {
let a: armature_action_t = raw.actions[i];
if (a.mats != null) {
continue;
}
a.mats = [];
for (let i: i32 = 0; i < a.bones.length; ++i) {
let b: obj_t = a.bones[i];
array_push(a.mats, mat4_from_f32_array(b.transform));
}
}
}
function armature_get_action(raw: armature_t, name: string): armature_action_t {
for (let i: i32 = 0; i < raw.actions.length; ++i) {
let a: armature_action_t = raw.actions[i];
if (a.name == name) {
return a;
}
}
return null;
}
function armature_set_parents(object: obj_t) {
if (object.children == null) {
return;
}
for (let i: i32 = 0; i < object.children.length; ++i) {
let o: obj_t = object.children[i];
o.parent = object;
armature_set_parents(o);
}
}
function armature_traverse_bones(objects: obj_t[], callback: (o: obj_t)=>void) {
for (let i: i32 = 0; i < objects.length; ++i) {
armature_traverse_bones_step(objects[i], callback);
}
}
function armature_traverse_bones_step(object: obj_t, callback: (o: obj_t)=>void) {
if (object.type == "bone_object") {
callback(object);
}
if (object.children == null) {
return;
}
for (let i: i32 = 0; i < object.children.length; ++i) {
armature_traverse_bones_step(object.children[i], callback);
}
}
///end
-103
View File
@@ -8,11 +8,6 @@ function mesh_data_parse(name: string, id: string): mesh_data_t {
}
let data: mesh_data_t = mesh_data_create(raw);
///if arm_skin
if (raw.skin != null) {
mesh_data_init_skeleton_transforms(data, raw.skin.transforms_inv);
}
///end
return data;
}
@@ -41,53 +36,6 @@ function mesh_data_create(raw: mesh_data_t): mesh_data_t {
raw._.refcount = 0;
raw._.vertex_buffer_map = map_create();
raw._.ready = false;
///if arm_skin
// Prepare vertex array for skinning and fill size data
let vertex_arrays: vertex_array_t[] = raw.vertex_arrays;
if (raw.skin != null) {
let va_bone: vertex_array_t = {};
va_bone.attrib = "bone";
va_bone.values = null;
va_bone.data = "short4norm";
let va_weight: vertex_array_t = {};
va_weight.attrib = "weight";
va_weight.values = null;
va_weight.data = "short4norm";
array_push(vertex_arrays, va_bone);
array_push(vertex_arrays, va_weight);
}
if (raw.skin != null) {
let size: i32 = mesh_data_get_vertex_size(vertex_arrays[0].data);
let vertex_length: i32 = math_floor(vertex_arrays[0].values.length / size);
let l: i32 = vertex_length * 4;
let bonea: i16_array_t = i16_array_create(l);
let weighta: i16_array_t = i16_array_create(l);
let index: i32 = 0;
let ai: i32 = 0;
for (let i: i32 = 0; i < vertex_length; ++i) {
let bone_count: i32 = raw.skin.bone_count_array[i];
for (let j: i32 = index; j < index + bone_count; ++j) {
bonea[ai] = raw.skin.bone_index_array[j];
weighta[ai] = raw.skin.bone_weight_array[j];
ai++;
}
// Fill unused weights
for (let j: i32 = bone_count; j < 4; ++j) {
bonea[ai] = 0;
weighta[ai] = 0;
ai++;
}
index += bone_count;
}
vertex_arrays[vertex_arrays.length - 2].values = bonea;
vertex_arrays[vertex_arrays.length - 1].values = weighta;
}
///end
// Make vertex buffers
raw._.structure = mesh_data_get_vertex_struct(raw.vertex_arrays);
return raw;
@@ -237,57 +185,6 @@ function mesh_data_build(raw: mesh_data_t) {
raw._.ready = true;
}
///if arm_skin
function mesh_data_add_armature(raw: mesh_data_t, armature: armature_t) {
for (let i: i32 = 0; i < armature.actions.length; ++i) {
let a: armature_action_t = armature.actions[i];
mesh_data_add_action(raw, a.bones, a.name);
}
}
function mesh_data_add_action(raw: mesh_data_t, bones: obj_t[], name: string) {
if (bones == null) {
return;
}
if (raw._.actions == null) {
raw._.actions = map_create();
raw._.mats = map_create();
}
if (map_get(raw._.actions, name) != null) {
return;
}
let action_bones: obj_t[] = [];
// Set bone references
for (let i: i32 = 0; i < raw.skin.bone_ref_array.length; ++i) {
let s: string = raw.skin.bone_ref_array[i];
for (let j: i32 = 0; j < bones.length; ++j) {
let b: obj_t = bones[j];
if (b.name == s) {
array_push(action_bones, b);
}
}
}
map_set(raw._.actions, name, action_bones);
let action_mats: mat4_t[] = [];
for (let i: i32 = 0; i < action_bones.length; ++i) {
let b: obj_t = action_bones[i];
array_push(action_mats, mat4_from_f32_array(b.transform));
}
map_set(raw._.mats, name, action_mats);
}
function mesh_data_init_skeleton_transforms(raw: mesh_data_t, transforms_inv: f32_array_t[]) {
raw._.skeleton_transforms_inv = [];
for (let i: i32 = 0; i < transforms_inv.length; ++i) {
let t: f32_array_t = transforms_inv[i];
let mi = mat4_from_f32_array(t);
array_push(raw._.skeleton_transforms_inv, mi);
}
}
///end
function mesh_data_calculate_aabb(raw: mesh_data_t): vec4_t {
let aabb_min: vec4_t = vec4_create(-0.01, -0.01, -0.01);
let aabb_max: vec4_t = vec4_create(0.01, 0.01, 0.01);
-14
View File
@@ -45,20 +45,6 @@ function mesh_object_remove(raw: mesh_object_t) {
}
function mesh_object_setup_animation(raw: mesh_object_t, oactions: scene_t[] = null) {
///if arm_skin
let has_action: bool = raw.base.parent != null && raw.base.parent.raw != null && raw.base.parent.raw.anim != null && raw.base.parent.raw.anim.bone_actions != null;
if (has_action) {
let armature_name: string = raw.base.parent.name;
raw.base.animation = object_get_parent_armature(raw.base, armature_name).base;
if (raw.base.animation == null) {
raw.base.animation = anim_bone_create(armature_name).base;
}
if (raw.data.skin != null) {
anim_bone_set_skin(raw.base.animation.ext, raw);
}
}
///end
///if arm_anim
object_setup_animation_super(raw.base, oactions);
///end
-24
View File
@@ -125,12 +125,6 @@ function object_get_children(raw: object_t, recursive: bool = false): object_t[]
///if arm_anim
function object_setup_animation_super(raw: object_t, oactions: scene_t[] = null) {
// Parented to bone
///if arm_skin
if (raw.raw.anim != null && raw.raw.anim.parent_bone != null) {
sys_notify_on_init(_object_setup_animation_on_init, raw);
}
///end
// object_t actions
if (oactions == null) {
return;
@@ -138,24 +132,6 @@ function object_setup_animation_super(raw: object_t, oactions: scene_t[] = null)
raw.animation = anim_object_create(raw, oactions).base;
}
///if arm_skin
function _object_setup_animation_on_init(raw: object_t) {
let banim: anim_bone_t = object_get_parent_armature(raw, raw.parent.name);
if (banim != null) {
anim_bone_add_bone_child(banim, raw.raw.anim.parent_bone, raw);
}
}
function object_get_parent_armature(raw: object_t, name: string): anim_bone_t {
for (let i: i32 = 0; i < scene_animations.length; ++i) {
let a: anim_raw_t = scene_animations[i];
if (a.armature != null && a.armature.name == name) {
return a.ext;
}
}
return null;
}
///end
function object_setup_animation(raw: object_t, oactions: scene_t[] = null) {
if (raw.ext_type == "mesh_object_t") {
mesh_object_setup_animation(raw.ext, oactions);
+1 -70
View File
@@ -10,9 +10,6 @@ let scene_empties: object_t[];
///if arm_anim
let scene_animations: anim_raw_t[];
///end
///if arm_skin
let scene_armatures: armature_t[];
///end
let scene_embedded: map_t<string, gpu_texture_t>;
let _scene_ready: bool;
@@ -35,9 +32,6 @@ function scene_create(format: scene_t): object_t {
///if arm_anim
scene_animations = [];
///end
///if arm_skin
scene_armatures = [];
///end
scene_embedded = map_create();
_scene_root = object_create();
_scene_root.name = "Root";
@@ -328,43 +322,6 @@ function scene_create_mesh_object(o: obj_t, format: scene_t, parent: object_t, p
data_ref = o.data_ref;
}
// Bone objects are stored in armature parent
///if arm_skin
if (parent_object != null && parent_object.anim != null && parent_object.anim.bone_actions != null) {
let bactions: scene_t[] = [];
for (let i: i32 = 0; i < parent_object.anim.bone_actions.length; ++i) {
let ref: string = parent_object.anim.bone_actions[i];
let action: scene_t = data_get_scene_raw(ref);
array_push(bactions, action);
}
let armature: armature_t = null;
// Check if armature exists
for (let j: i32 = 0; j < scene_armatures.length; ++j) {
let a: armature_t = scene_armatures[j];
if (a.uid == parent.uid) {
armature = a;
break;
}
}
// Create new one
if (armature == null) {
// Unique name if armature was already instantiated for different object
for (let j: i32 = 0; j < scene_armatures.length; ++j) {
let a: armature_t = scene_armatures[j];
let aname: string = a.name;
if (aname == parent.name) {
parent.name += "." + parent.uid;
break;
}
}
armature = armature_create(parent.uid, parent.name, bactions);
array_push(scene_armatures, armature);
}
return scene_return_mesh_object(
object_file, data_ref, scene_name, armature, materials, parent, parent_object, o);
}
///end
return scene_return_mesh_object(object_file, data_ref, scene_name, null, materials, parent, parent_object, o);
}
@@ -372,11 +329,6 @@ function scene_return_mesh_object(object_file: string, data_ref: string, scene_n
materials: material_data_t[], parent: object_t, parent_object: obj_t, o: obj_t): object_t {
let mesh: mesh_data_t = data_get_mesh(object_file, data_ref);
///if arm_skin
if (mesh.skin != null) {
armature != null ? mesh_data_add_armature(mesh, armature) : mesh_data_add_action(mesh, _scene_raw.objects, "none");
}
///end
let object: mesh_object_t = scene_add_mesh_object(mesh, materials, parent);
return scene_return_object(object.base, o);
@@ -466,7 +418,6 @@ type mesh_data_t = {
name?: string;
scale_pos?: f32; // Unpack pos from (-1,1) coords
scale_tex?: f32; // Unpack tex from (-1,1) coords
skin?: skin_t;
vertex_arrays?: vertex_array_t[];
index_arrays?: index_array_t[];
_?: mesh_data_runtime_t;
@@ -480,21 +431,6 @@ type mesh_data_runtime_t = {
index_buffers?: gpu_buffer_t[];
ready?: bool;
structure?: gpu_vertex_structure_t;
///if arm_skin
skeleton_transforms_inv?: mat4_t[];
actions?: map_t<string, obj_t[]>;
mats?: map_t<string, mat4_t[]>;
///end
};
type skin_t = {
transform?: f32_array_t;
bone_ref_array?: string[];
bone_len_array?: f32_array_t;
transforms_inv?: f32_array_t[]; // per-bone, size = 16, with skin.transform, pre-inverted
bone_count_array?: i16_array_t;
bone_index_array?: i16_array_t;
bone_weight_array?: i16_array_t;
};
type vertex_array_t = {
@@ -652,7 +588,7 @@ type obj_t = {
dimensions?: f32_array_t; // Geometry objects
visible?: bool;
spawn?: bool; // Auto add object when creating scene
anim?: anim_t; // Bone/object animation
anim?: anim_t; // Object animation
material_refs?: string[];
children?: obj_t[];
_?: obj_runtime_t;
@@ -664,11 +600,6 @@ type obj_runtime_t = {
type anim_t = {
object_actions?: string[];
bone_actions?: string[];
parent_bone?: string;
parent_bone_tail?: f32_array_t; // Translate from head to tail
parent_bone_tail_pose?: f32_array_t;
parent_bone_connected?: bool;
tracks?: track_t[];
begin?: i32; // Frames, for non-sampled
end?: i32;
-12
View File
@@ -12,17 +12,11 @@ type transform_t = {
object?: object_t;
dim?: vec4_t;
radius?: f32;
///if arm_skin
bone_parent?: mat4_t;
///end
///if arm_anim
// Wrong order returned from get_euler(), store last state for animation
_euler_x?: f32;
_euler_y?: f32;
_euler_z?: f32;
// Animated delta transform
dloc?: vec4_t;
drot?: quat_t;
@@ -79,12 +73,6 @@ function transform_build_matrix(raw: transform_t) {
// transform_compose_delta(raw);
// }
///if arm_skin
if (raw.bone_parent != null) {
raw.local = mat4_mult_mat(raw.local, raw.bone_parent);
}
///end
if (raw.object.parent != null && !raw.local_only) {
raw.world = mat4_mult_mat3x4(raw.local, raw.object.parent.transform.world);
}
+1 -8
View File
@@ -514,14 +514,7 @@ function uniforms_set_obj_const(obj: object_t, loc: i32, c: shader_const_t) {
else if (c.type == "floats") {
let fa: f32_array_t = null;
if (c.link == "_skin_bones") {
///if arm_skin
if (obj.animation != null) {
fa = obj.animation.ext.skin_buffer;
}
///end
}
else if (uniforms_f32_array_links != null) {
if (uniforms_f32_array_links != null) {
fa = uniforms_f32_array_links(obj, current_material(obj), c.link);
}
-24
View File
@@ -303,30 +303,6 @@ fun pack_f32_i16(f: float, i: uint): float { \
// } \
// ";
///if arm_skin
let str_get_skinning_dual_quat: string = "\
fun get_skinning_dual_quat(bone: int4, weight: float4, out A: float4, inout B: float4) { \
var bonei: int4 = bone * 2; \
var mat_a: float4x4 = float4x4( \
skin_bones[bonei.x], \
skin_bones[bonei.y], \
skin_bones[bonei.z], \
skin_bones[bonei.w]); \
var mat_b: float4x4 = float4x4( \
skin_bones[bonei.x + 1], \
skin_bones[bonei.y + 1], \
skin_bones[bonei.z + 1], \
skin_bones[bonei.w + 1]); \
weight.xyz *= sign(mat_a[3] * mat_a).xyz; \
A = mat_a * weight; \
B = mat_b * weight; \
var inv_norm_a: float = 1.0 / length(A); \
A *= inv_norm_a; \
B *= inv_norm_a; \
} \
";
///end
let str_create_basis: string = "\
fun create_basis(normal: float3, out tangent: float3, out binormal: float3) { \
tangent = normalize(camera_right - normal * dot(camera_right, normal)); \
-5
View File
@@ -305,11 +305,6 @@ function util_clone_anim(a: anim_t): anim_t {
}
let r: anim_t = {};
r.object_actions = util_clone_string_array(a.object_actions);
r.bone_actions = util_clone_string_array(a.bone_actions);
r.parent_bone = a.parent_bone;
r.parent_bone_tail = util_clone_f32_array(a.parent_bone_tail);
r.parent_bone_tail_pose = util_clone_f32_array(a.parent_bone_tail_pose);
r.parent_bone_connected = a.parent_bone_connected;
r.tracks = util_clone_tracks(a.tracks);
r.begin = a.begin;
r.end = a.end;
-2
View File
@@ -92,8 +92,6 @@ function util_encode_mesh_datas(datas: mesh_data_t[]) {
armpack_encode_f32(datas[i].scale_pos);
armpack_encode_string("scale_tex");
armpack_encode_f32(datas[i].scale_tex);
armpack_encode_string("skin");
armpack_encode_null(); // skin_t
armpack_encode_string("vertex_arrays");
armpack_encode_array(datas[i].vertex_arrays.length);
for (let j: i32 = 0; j < datas[i].vertex_arrays.length; ++j) {
+4 -299
View File
@@ -24,15 +24,14 @@ bl_info = {
"location": "File -> Export",
"description": "Armory mesh data",
"author": "Armory3D.org",
"version": (2025, 2, 0),
"version": (2025, 8, 0),
"blender": (4, 3, 1),
"doc_url": "",
"tracker_url": "",
}
NodeTypeBone = 1
NodeTypeMesh = 2
structIdentifier = ["object", "bone_object", "mesh_object"]
NodeTypeMesh = 1
structIdentifier = ["object", "mesh_object"]
class ArmoryExporter(bpy.types.Operator, ExportHelper):
"""Export to Armory format"""
@@ -48,10 +47,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.scene = context.scene
self.output = {}
self.bobjectArray = {}
self.bobjectBoneArray = {}
self.meshArray = {}
self.boneParentArray = {}
self.bone_tracks = []
self.depsgraph = context.evaluated_depsgraph_get()
scene_objects = self.scene.collection.all_objects
@@ -59,8 +55,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
if not bobject.parent:
self.process_bobject(bobject)
self.process_skinned_meshes()
self.output["name"] = self.scene.name
self.output["objects"] = []
for bo in scene_objects:
@@ -84,7 +78,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.scene.frame_set(current_frame, subframe=current_subframe)
print(f"Scene exported in {str(time.time() - profile_time)}")
return {"FINISHED"}
def write_arm(self, filepath, output):
@@ -111,121 +104,16 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
matrix[3][3],
]
def find_bone(self, name):
return next(
(
bobject_ref
for bobject_ref in self.bobjectBoneArray.items()
if bobject_ref[0].name == name
),
None,
)
def collect_bone_animation(self, armature, name):
path = 'pose.bones["' + name + '"].'
curve_array = []
if armature.animation_data:
if action := armature.animation_data.action:
curve_array.extend(
fcurve
for fcurve in action.fcurves
if fcurve.data_path.startswith(path)
)
return curve_array
def export_bone(self, armature, bone, scene, o, action):
if bobjectRef := self.bobjectBoneArray.get(bone):
o["name"] = bobjectRef["structName"]
o["type"] = structIdentifier[bobjectRef["objectType"]]
self.export_bone_transform(armature, bone, o, action)
o["children"] = []
for subbobject in bone.children:
so = {}
self.export_bone(armature, subbobject, scene, so, action)
o["children"].append(so)
def export_pose_markers(self, oanim, action):
if action.pose_markers is None or len(action.pose_markers) == 0:
return
oanim["marker_frames"] = []
oanim["marker_names"] = []
for m in action.pose_markers:
oanim["marker_frames"].append(int(m.frame))
oanim["marker_names"].append(m.name)
def process_bone(self, bone):
self.bobjectBoneArray[bone] = {
"objectType": NodeTypeBone,
"structName": bone.name,
}
for subbobject in bone.children:
self.process_bone(subbobject)
def process_bobject(self, bobject):
if bobject.type not in ["MESH", "ARMATURE"]:
if bobject.type not in ["MESH"]:
return
btype = NodeTypeMesh if bobject.type == "MESH" else 0
self.bobjectArray[bobject] = {"objectType": btype, "structName": bobject.name}
if bobject.type == "ARMATURE":
if skeleton := bobject.data:
for bone in skeleton.bones:
if not bone.parent:
self.process_bone(bone)
for subbobject in bobject.children:
self.process_bobject(subbobject)
def process_skinned_meshes(self):
for bobjectRef in self.bobjectArray.items():
if bobjectRef[1]["objectType"] == NodeTypeMesh:
if armature := bobjectRef[0].find_armature():
for bone in armature.data.bones:
boneRef = self.find_bone(bone.name)
if boneRef:
boneRef[1]["objectType"] = NodeTypeBone
def export_bone_transform(self, armature, bone, o, action):
pose_bone = armature.pose.bones.get(bone.name)
transform = bone.matrix_local.copy()
if bone.parent is not None:
transform = bone.parent.matrix_local.inverted_safe() @ transform
o["transform"] = {}
o["transform"]["values"] = self.write_matrix(transform)
curve_array = self.collect_bone_animation(armature, bone.name)
animation = len(curve_array) != 0
if animation and pose_bone:
begin_frame = int(action.frame_range[0])
end_frame = int(action.frame_range[1])
tracko = {}
o["anim"] = {}
o["anim"]["tracks"] = [tracko]
tracko["target"] = "transform"
tracko["frames"] = [
i - begin_frame for i in range(begin_frame, end_frame + 1)
]
tracko["values"] = []
self.bone_tracks.append((tracko["values"], pose_bone))
def write_bone_matrices(self, scene, action):
if len(self.bone_tracks) > 0:
begin_frame = int(action.frame_range[0])
end_frame = int(action.frame_range[1])
for i in range(begin_frame, end_frame + 1):
scene.frame_set(i)
for track in self.bone_tracks:
values, pose_bone = track[0], track[1]
if parent := pose_bone.parent:
values += self.write_matrix(
(parent.matrix.inverted_safe() @ pose_bone.matrix)
)
else:
values += self.write_matrix(pose_bone.matrix)
def export_object(self, bobject, scene, parento=None):
if bobjectRef := self.bobjectArray.get(bobject):
o = {}
@@ -240,9 +128,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o["material_refs"] = None
o["children"] = None
if bobject.parent_type == "BONE":
o["anim"]["parent_bone"] = bobject.parent_bone
if bobjectRef["objectType"] == NodeTypeMesh:
objref = bobject.data
if objref not in self.meshArray:
@@ -256,90 +141,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o["data_ref"] = oid
o["dimensions"] = self.calc_aabb(bobject)
# If the object is parented to a bone and is not relative, undo the
# bone's transform
if bobject.parent_type == "BONE":
armature = bobject.parent.data
bone = armature.bones[bobject.parent_bone]
o["anim"]["parent_bone_connected"] = bone.use_connect
if bone.use_connect:
bone_translation = Vector((0, bone.length, 0)) + bone.head
o["anim"]["parent_bone_tail"] = [
bone_translation[0],
bone_translation[1],
bone_translation[2],
]
else:
bone_translation = bone.tail - bone.head
o["anim"]["parent_bone_tail"] = [
bone_translation[0],
bone_translation[1],
bone_translation[2],
]
pose_bone = bobject.parent.pose.bones[bobject.parent_bone]
bone_translation_pose = pose_bone.tail - pose_bone.head
o["anim"]["parent_bone_tail_pose"] = [
bone_translation_pose[0],
bone_translation_pose[1],
bone_translation_pose[2],
]
if bobject.type == "ARMATURE" and bobject.data is not None:
bdata = bobject.data
action = None
adata = bobject.animation_data
# Active action
if adata is not None:
action = adata.action
if action is None:
bobject.animation_data_create()
actions = bpy.data.actions
action = actions.get("armory_pose")
if action is None:
action = actions.new(name="armory_pose")
# Collect export actions
export_actions = [action]
if hasattr(adata, "nla_tracks") and adata.nla_tracks is not None:
for track in adata.nla_tracks:
if track.strips is None:
continue
for strip in track.strips:
if strip.action is None:
continue
if strip.action.name == action.name:
continue
export_actions.append(strip.action)
basename = os.path.basename(self.filepath)[:-4]
o["anim"]["bone_actions"] = []
for action in export_actions:
o["anim"]["bone_actions"].append(basename + "_" + action.name)
orig_action = bobject.animation_data.action
for action in export_actions:
bobject.animation_data.action = action
bones = []
self.bone_tracks = []
for bone in bdata.bones:
if not bone.parent:
boneo = {}
self.export_bone(bobject, bone, scene, boneo, action)
bones.append(boneo)
self.write_bone_matrices(scene, action)
if len(bones) > 0 and "anim" in bones[0]:
self.export_pose_markers(bones[0]["anim"], action)
# Save action separately
action_obj = {}
action_obj["name"] = action.name
action_obj["objects"] = bones
self.write_arm(
self.filepath[:-4] + "_" + action.name + ".arm", action_obj
)
bobject.animation_data.action = orig_action
if parento is None:
self.output["objects"].append(o)
else:
@@ -351,96 +152,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
for subbobject in bobject.children:
self.export_object(subbobject, scene, o)
def export_skin(self, bobject, armature, exportMesh, o):
# This function exports all skinning data, which includes the skeleton
# and per-vertex bone influence data
oskin = {}
o["skin"] = oskin
# Write the skin bind pose transform
otrans = {}
oskin["transform"] = otrans
otrans["values"] = self.write_matrix(bobject.matrix_world)
bone_array = armature.data.bones
bone_count = len(bone_array)
max_bones = 128
bone_count = min(bone_count, max_bones)
# Write the bone object reference array
oskin["bone_ref_array"] = np.empty(bone_count, dtype=object)
oskin["bone_len_array"] = np.empty(bone_count, dtype="<f4")
for i in range(bone_count):
if boneRef := self.find_bone(bone_array[i].name):
oskin["bone_ref_array"][i] = boneRef[1]["structName"]
oskin["bone_len_array"][i] = bone_array[i].length
else:
oskin["bone_ref_array"][i] = ""
oskin["bone_len_array"][i] = 0.0
# Write the bind pose transform array
oskin["transforms_inv"] = []
for i in range(bone_count):
skeleton_inv = (
armature.matrix_world @ bone_array[i].matrix_local
).inverted_safe()
skeleton_inv = skeleton_inv @ bobject.matrix_world
oskin["transforms_inv"].append(self.write_matrix(skeleton_inv))
# Export the per-vertex bone influence data
group_remap = []
for group in bobject.vertex_groups:
for i in range(bone_count):
if bone_array[i].name == group.name:
group_remap.append(i)
break
else:
group_remap.append(-1)
bone_count_array = np.empty(len(exportMesh.loops), dtype="<i2")
bone_index_array = np.empty(len(exportMesh.loops) * 4, dtype="<i2")
bone_weight_array = np.empty(len(exportMesh.loops) * 4, dtype="<f4")
vertices = bobject.data.vertices
count = 0
for index, l in enumerate(exportMesh.loops):
bone_count = 0
total_weight = 0.0
bone_values = []
for g in vertices[l.vertex_index].groups:
bone_index = group_remap[g.group]
bone_weight = g.weight
if bone_index >= 0: # and bone_weight != 0.0:
bone_values.append((bone_weight, bone_index))
total_weight += bone_weight
bone_count += 1
if bone_count > 4:
bone_count = 4
bone_values.sort(reverse=True)
bone_values = bone_values[:4]
bone_count_array[index] = bone_count
for bv in bone_values:
bone_weight_array[count] = bv[0]
bone_index_array[count] = bv[1]
count += 1
if total_weight not in (0.0, 1.0):
normalizer = 1.0 / total_weight
for i in range(bone_count):
bone_weight_array[count - i - 1] *= normalizer
bone_index_array = bone_index_array[:count]
bone_weight_array = bone_weight_array[:count]
bone_weight_array *= 32767
bone_weight_array = np.array(bone_weight_array, dtype="<i2")
oskin["bone_count_array"] = bone_count_array
oskin["bone_index_array"] = bone_index_array
oskin["bone_weight_array"] = bone_weight_array
def calc_aabb(self, bobject):
aabb_center = 0.125 * sum((Vector(b) for b in bobject.bound_box), Vector())
return [
@@ -480,8 +191,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o["scale_pos"] = maxdim / 2
else:
o["scale_pos"] = 1.0
if has_armature: # Allow up to 2x bigger bounds for skinned mesh
o["scale_pos"] *= 2.0
pdata = np.empty(num_verts * 4, dtype="<f4") # p.xyz, n.z
ndata = np.empty(num_verts * 2, dtype="<f4") # n.xy
@@ -524,8 +233,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
if has_col:
cdata = np.empty(num_verts * 4, dtype="<f4")
o["skin"] = None
scale_pos = o["scale_pos"]
invscale_pos = (1 / scale_pos) * 32767
@@ -678,8 +385,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
exportMesh = bobject_eval.to_mesh()
self.export_mesh_data(exportMesh, bobject, o, has_armature=armature is not None)
if armature:
self.export_skin(bobject, armature, exportMesh, o)
self.output["mesh_datas"].append(o)
bobject_eval.to_mesh_clear()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-26
View File
@@ -31,26 +31,6 @@ function make_mesh_preview_run(data: material_t, matcon: material_context_t): no
let pos: string = "input.pos";
///if arm_skin
let skin: bool = mesh_data_get_vertex_array(context_raw.paint_object.data, "bone") != null;
if (skin) {
pos = "spos";
node_shader_context_add_elem(con_mesh, "bone", "short4norm");
node_shader_context_add_elem(con_mesh, "weight", "short4norm");
node_shader_add_function(kong, str_get_skinning_dual_quat);
node_shader_add_constant(kong, "skin_bones float4[128 * 2]", "_skin_bones");
node_shader_add_constant(kong, "pos_unpack: float", "_pos_unpack");
node_shader_write_attrib_vert(kong, "var skin_a: float4;");
node_shader_write_attrib_vert(kong, "var skin_b: float4;");
node_shader_write_attrib_vert(kong, "get_skinning_dual_quat(int4(bone * 32767), weight, skin_a, skin_b);");
node_shader_write_attrib_vert(kong, "var spos: float3 = input.pos.xyz;");
node_shader_write_attrib_vert(kong, "spos.xyz *= constants.pos_unpack;");
node_shader_write_attrib_vert(kong, "spos.xyz += 2.0 * cross(skin_a.xyz, cross(skin_a.xyz, spos.xyz) + skin_a.w * spos.xyz);");
node_shader_write_attrib_vert(kong, "spos.xyz += 2.0 * (skin_a.w * skin_b.xyz - skin_b.w * skin_a.xyz + cross(skin_a.xyz, skin_b.xyz));");
node_shader_write_attrib_vert(kong, "spos.xyz /= constants.pos_unpack;");
}
///end
node_shader_add_constant(kong, "WVP: float4x4", "_world_view_proj_matrix");
node_shader_write_attrib_vert(kong, "output.pos = constants.WVP * float4(" + pos + ".xyz, 1.0);");
@@ -142,12 +122,6 @@ function make_mesh_preview_run(data: material_t, matcon: material_context_t): no
parser_material_finalize(con_mesh);
///if arm_skin
if (skin) {
node_shader_write_vert(kong, "wnormal = normalize(constants.N * float3(input.nor.xy, input.pos.w) + 2.0 * cross(skin_a.xyz, cross(skin_a.xyz, float3(input.nor.xy, input.pos.w)) + skin_a.w * float3(input.nor.xy, input.pos.w)));");
}
///end
con_mesh.data.shader_from_source = true;
gpu_create_shaders_from_kong(node_shader_get(kong), ADDRESS(con_mesh.data.vertex_shader), ADDRESS(con_mesh.data.fragment_shader), ADDRESS(con_mesh.data._.vertex_shader_size), ADDRESS(con_mesh.data._.fragment_shader_size));