// 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 }