133 lines
3.6 KiB
Plaintext
133 lines
3.6 KiB
Plaintext
// Skinning module for retro3d
|
|
// Handles skeleton/joint management and joint palette building for GPU skinning
|
|
var model_c = use('model')
|
|
|
|
// Prepare skin data from a loaded model
|
|
// Extracts inverse bind matrices and joint indices
|
|
function prepare_skins(model) {
|
|
if (!model._gltf || !model._gltf.skins) return []
|
|
|
|
var g = model._gltf
|
|
var buffer_blob = g.buffers[0] ? g.buffers[0].blob : null
|
|
if (!buffer_blob) return []
|
|
|
|
var prepared = []
|
|
|
|
for (var si = 0; si < length(g.skins); si++) {
|
|
var skin = g.skins[si]
|
|
|
|
// Extract inverse bind matrices
|
|
var inv_bind_blob = null
|
|
if (skin.inverse_bind_matrices != null) {
|
|
var acc = g.accessors[skin.inverse_bind_matrices]
|
|
if (acc) {
|
|
var view = g.views[acc.view]
|
|
inv_bind_blob = model_c.extract_accessor(
|
|
buffer_blob,
|
|
view.byte_offset || 0,
|
|
view.byte_stride || 0,
|
|
acc.byte_offset || 0,
|
|
acc.count,
|
|
acc.component_type,
|
|
acc.type
|
|
)
|
|
}
|
|
}
|
|
|
|
prepared.push({
|
|
name: skin.name,
|
|
joints: skin.joints,
|
|
joint_count: length(skin.joints),
|
|
inv_bind: inv_bind_blob,
|
|
skeleton: skin.skeleton,
|
|
palette: null // Will be built each frame
|
|
})
|
|
}
|
|
|
|
return prepared
|
|
}
|
|
|
|
// Build joint palette for a skin
|
|
// This computes world * inv_bind for each joint
|
|
function build_palette(skin, model, retro3d) {
|
|
if (!skin || !skin.joints || length(skin.joints) == 0) return null
|
|
|
|
// Collect world matrices for each joint
|
|
var world_matrices = []
|
|
for (var j = 0; j < length(skin.joints); j++) {
|
|
var node_idx = skin.joints[j]
|
|
var node = model.nodes[node_idx]
|
|
if (node) {
|
|
var world_mat = retro3d.transform_get_world_matrix(node)
|
|
world_matrices.push(world_mat)
|
|
} else {
|
|
world_matrices.push(model_c.mat4_identity())
|
|
}
|
|
}
|
|
|
|
// Build palette using C function
|
|
return model_c.build_joint_palette(world_matrices, skin.inv_bind, skin.joint_count)
|
|
}
|
|
|
|
// Get a joint's world matrix (for attachments)
|
|
function get_joint_world(skin, joint_index, model, retro3d) {
|
|
if (!skin || joint_index >= length(skin.joints)) {
|
|
return model_c.mat4_identity()
|
|
}
|
|
|
|
var node_idx = skin.joints[joint_index]
|
|
var node = model.nodes[node_idx]
|
|
if (!node) return model_c.mat4_identity()
|
|
|
|
return retro3d.transform_get_world_matrix(node)
|
|
}
|
|
|
|
// Find joint index by name
|
|
function find_joint(skin, name, model) {
|
|
if (!skin || !skin.joints) return -1
|
|
|
|
for (var j = 0; j < length(skin.joints); j++) {
|
|
var node_idx = skin.joints[j]
|
|
var node = model.nodes[node_idx]
|
|
if (node && node.name == name) return j
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// Find joint index by node index
|
|
function joint_from_node(skin, node_idx) {
|
|
if (!skin || !skin.joints) return -1
|
|
|
|
for (var j = 0; j < length(skin.joints); j++) {
|
|
if (skin.joints[j] == node_idx) return j
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// Get node index from joint index
|
|
function node_from_joint(skin, joint_idx) {
|
|
if (!skin || !skin.joints || joint_idx >= length(skin.joints)) return -1
|
|
return skin.joints[joint_idx]
|
|
}
|
|
|
|
// Compute attachment transform
|
|
// Returns world matrix for an object attached to a joint with an offset
|
|
function attachment_transform(skin, joint_index, model, retro3d, offset_mat) {
|
|
var joint_world = get_joint_world(skin, joint_index, model, retro3d)
|
|
|
|
if (offset_mat) {
|
|
return model_c.mat4_mul(joint_world, offset_mat)
|
|
}
|
|
return joint_world
|
|
}
|
|
|
|
return {
|
|
prepare_skins: prepare_skins,
|
|
build_palette: build_palette,
|
|
get_joint_world: get_joint_world,
|
|
find_joint: find_joint,
|
|
joint_from_node: joint_from_node,
|
|
node_from_joint: node_from_joint,
|
|
attachment_transform: attachment_transform
|
|
}
|