Last active
December 29, 2025 18:31
-
-
Save sketchpunk/d91aab8e20186cc3a37ae8ed70db0ef9 to your computer and use it in GitHub Desktop.
ThreeJS GLTFLoader - Parse out skeleton & clips
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // let [clips, aSkel, aGrp] = await loadAnimations( `${url}/anim/source-animation.glb` ); | |
| // let aHelper = new THREE.SkeletonHelper( aSkel.bones[0] ); | |
| // App.scene.add( aGrp || aSkel.bones[0], aHelper ); | |
| // const mixer = new THREE.AnimationMixer( new THREE.Object3D() ); | |
| // const action = this.mixer.clipAction( clips[0], aSkel.bones[0] ); | |
| // action.play(); | |
| async function loadAnimations( url ){ | |
| const tf = await new GLTFLoader().loadAsync( url ); | |
| const [ skel, grp ] = parseGLTFSkeleton( tf ); | |
| return [ tf.animations ?? [], skel, grp ]; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function parseGLTFSkeleton( obj ){ | |
| const json = obj.parser.json; // GLTF Json Data | |
| const n = json.nodes; // Shortcut to nodes | |
| const joints = json.skins[0].joints; // Shortcut to skeleton joint node indices | |
| const map = {}; // Map Node Index to Bone Index | |
| const bones = []; // Collection of bones to build skeleton | |
| let j; // GLTF Joint | |
| let b; // 3JS Bone | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Create Bones | |
| for( const [i,ni] of joints.entries() ){ | |
| j = n[ni] | |
| if( !j.isBone ) continue; | |
| // NOTE: Remove periods, clip removes it from track names which causes not | |
| // found errors cause of name mismatch | |
| b = new THREE.Bone(); | |
| b.name = j.name.replaceAll('.', ''); | |
| map[ ni ] = i; | |
| // console.log( b.name ) | |
| if( j.rotation ) b.quaternion.fromArray( j.rotation ); | |
| if( j.translation ) b.position.fromArray( j.translation ); | |
| if( j.scale ) b.scale.fromArray( j.scale ); | |
| bones.push( b ); | |
| } | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Create Parent-Child Relationship | |
| for( const [i,ni] of joints.entries() ){ | |
| j = n[ni]; | |
| if( j.isBone && j.children ){ | |
| b = bones[i]; | |
| for( const c of j.children ) b.add( bones[ map[c] ] ); | |
| } | |
| } | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| const skel = new THREE.Skeleton( bones ); | |
| // const hlpr = new THREE.SkeletonHelper( bones[0] ); | |
| // App.scene.add( bones[0], hlpr ); | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Ancestors transforms as Groups | |
| // NOTE: Blender tends to add parent nodes that transform the skeleton's root | |
| const nFind = ( ni )=>{ | |
| for( const [i, o] of n.entries() ){ | |
| if( o.children && o.children.includes( ni ) ) return i; | |
| } | |
| return null; | |
| }; | |
| const groups = []; | |
| let ni = joints[0]; | |
| do{ | |
| if( ( ni = nFind( ni ) ) !== null ){ | |
| j = n[ni]; | |
| if( j.translation || j.rotation || j.scale ){ | |
| const g = new THREE.Group(); | |
| g.name = j.name; | |
| if( j.translation ) g.position.fromArray( j.translation ); | |
| if( j.rotation ) g.quaternion.fromArray( j.rotation ); | |
| if( j.scale ) g.scale.fromArray( j.scale ); | |
| // Add last transform group as a child to the new one | |
| if( groups.length > 0 ) g.add( groups.at( -1 ) ); | |
| groups.push( g ); | |
| } | |
| } | |
| }while( ni !== null ); | |
| let grp = null; | |
| if( groups.length > 0 ){ | |
| groups[0].add( skel.bones[0] ); // Root lives under most inner group | |
| grp = groups.at( -1 ); // Save outer most group for adding to scene | |
| } | |
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| return [ skel, grp ]; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment