<span style="font-family:-apple-system,sans-serif">From another planet:</span><br><div class="gmail_quote gmail_quote_container" dir="auto"><div dir="auto"><br></div><div dir="auto">Untested!  Need Blender humanoid model (skinned+armature) with ‘Walk’ and ‘Wave’ actions.</div><div dir="auto"><br></div><div dir="auto">Let me know if you want to pursue anything this direct (skipping X3D/glTF/USD).</div><div dir="auto"><br></div><div dir="auto">Also consider exporting X3D JavaScript/TypeScript SAI from Blender, probably for use in X_ITE.</div><div dir="auto"><br></div><div dir="auto">John</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">From Google Search!</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote" dir="auto"><div style="font-size:inherit" dir="auto">=========</div><div style="font-size:inherit" dir="auto">Blender Python below </div><div style="font-size:inherit" dir="auto">=========</div><div style="font-size:inherit" dir="auto"><br></div><div style="font-size:inherit" dir="auto">import bpy<br style="font-size:inherit">import os<br style="font-size:inherit">import json<br style="font-size:inherit"><br style="font-size:inherit"># Configuration<br style="font-size:inherit">output_file = os.path.join(bpy.path.abspath("//"), "HumanoidComplete.js")<br style="font-size:inherit">mesh_obj = bpy.context.active_object<br style="font-size:inherit">armature_obj = mesh_obj.parent<br style="font-size:inherit"><br style="font-size:inherit">if not (mesh_obj and mesh_obj.type == 'MESH' and armature_obj):<br style="font-size:inherit">    raise Exception("Select the Mesh object (must be parented to an Armature)")<br style="font-size:inherit"><br style="font-size:inherit">def get_action_data(action_name):<br style="font-size:inherit">    action = bpy.data.actions.get(action_name)<br style="font-size:inherit">    if not action: return None<br style="font-size:inherit">    fps = bpy.context.scene.render.fps<br style="font-size:inherit">    tracks = []<br style="font-size:inherit">    for fcurve in action.fcurves:<br style="font-size:inherit">        if "pose.bones" not in fcurve.data_path: continue<br style="font-size:inherit">        bone_name = fcurve.data_path.split('"')[1]<br style="font-size:inherit">        p_type = "position" if "location" in fcurve.data_path else "quaternion" if "quaternion" in fcurve.data_path else "scale"<br style="font-size:inherit">        times = [round(<a href="http://kp.co" target="_blank">kp.co</a>[0] / fps, 3) for kp in fcurve.keyframe_points]<br style="font-size:inherit">        values = [round(<a href="http://kp.co" target="_blank">kp.co</a>[1], 4) for kp in fcurve.keyframe_points]<br style="font-size:inherit">        tracks.append({"name": f"{bone_name}.{p_type}", "times": times, "values": values})<br style="font-size:inherit">    return {"name": action_name, "duration": round(action.frame_range[1]/fps, 3), "tracks": tracks}<br style="font-size:inherit"><br style="font-size:inherit"># 1. EXTRACT MESH DATA (Triangulated for WebGL)<br style="font-size:inherit">depsgraph = bpy.context.evaluated_depsgraph_get()<br style="font-size:inherit">mesh = mesh_obj.evaluated_get(depsgraph).to_mesh()<br style="font-size:inherit">mesh.calc_loop_triangles()<br style="font-size:inherit"><br style="font-size:inherit">positions, uvs, colors = [], [], []<br style="font-size:inherit">skin_idx, skin_wgt = [], []<br style="font-size:inherit">bone_map = {<a href="http://b.name" target="_blank">b.name</a>: i for i, b in enumerate(armature_obj.data.bones)}<br style="font-size:inherit"><br style="font-size:inherit">for tri in mesh.loop_triangles:<br style="font-size:inherit">    for loop_idx in tri.loops:<br style="font-size:inherit">        v_idx = mesh.loops[loop_idx].vertex_index<br style="font-size:inherit">        v = mesh.vertices[v_idx]<br style="font-size:inherit">        positions.extend([round(v.co.x, 4), round(v.co.z, 4), round(-v.co.y, 4)])<br style="font-size:inherit">        if mesh.uv_layers.active:<br style="font-size:inherit">            uv = mesh.uv_layers.active.data[loop_idx].uv<br style="font-size:inherit">            uvs.extend([round(uv.x, 4), round(1.0 - uv.y, 4)])<br style="font-size:inherit">        if mesh.color_attributes.active:<br style="font-size:inherit">            c = mesh.color_attributes.active.data[loop_idx].color<br style="font-size:inherit">            colors.extend([round(c[0], 3), round(c[1], 3), round(c[2], 3)])<br style="font-size:inherit">        <br style="font-size:inherit">        # Weights (Top 4)<br style="font-size:inherit">        grps = sorted(v.groups, key=lambda g: g.weight, reverse=True)[:4]<br style="font-size:inherit">        idx, wgt = [0]*4, [0.0]*4<br style="font-size:inherit">        for i, g in enumerate(grps):<br style="font-size:inherit">            name = mesh_obj.vertex_groups[g.group].name<br style="font-size:inherit">            if name in bone_map:<br style="font-size:inherit">                idx[i], wgt[i] = bone_map[name], round(g.weight, 4)<br style="font-size:inherit">        skin_idx.extend(idx); skin_wgt.extend(wgt)<br style="font-size:inherit"><br style="font-size:inherit"># 2. EXTRACT SKELETON<br style="font-size:inherit">bones = []<br style="font-size:inherit">for b in armature_obj.data.bones:<br style="font-size:inherit">    mat = b.matrix_local if not b.parent else b.parent.matrix_local.inverted() @ b.matrix_local<br style="font-size:inherit">    bones.append({"name": <a href="http://b.name" target="_blank">b.name</a>, "parent": <a href="http://b.parent.name" target="_blank">b.parent.name</a> if b.parent else None, <br style="font-size:inherit">                  "pos": [round(mat.translation.x, 4), round(mat.translation.z, 4), round(-mat.translation.y, 4)]})<br style="font-size:inherit"><br style="font-size:inherit"># 3. WRITE TO JS<br style="font-size:inherit">with open(output_file, 'w') as f:<br style="font-size:inherit">    f.write("import * as THREE from 'three';\n\nexport const createHumanoid = () => {\n")<br style="font-size:inherit">    f.write(f"  const geo = new THREE.BufferGeometry();\n")<br style="font-size:inherit">    f.write(f"  geo.setAttribute('position', new THREE.Float32BufferAttribute({positions}, 3));\n")<br style="font-size:inherit">    f.write(f"  geo.setAttribute('uv', new THREE.Float32BufferAttribute({uvs}, 2));\n")<br style="font-size:inherit">    f.write(f"  geo.setAttribute('color', new THREE.Float32BufferAttribute({colors}, 3));\n")<br style="font-size:inherit">    f.write(f"  geo.setAttribute('skinIndex', new THREE.Uint16BufferAttribute({skin_idx}, 4));\n")<br style="font-size:inherit">    f.write(f"  geo.setAttribute('skinWeight', new THREE.Float32BufferAttribute({skin_wgt}, 4));\n\n")<br style="font-size:inherit">    <br style="font-size:inherit">    f.write("  const bones = [];\n  const boneMap = {};\n")<br style="font-size:inherit">    for b in bones:<br style="font-size:inherit">        f.write(f"  const {b['name']} = new THREE.Bone(); {b['name']}.name = '{b['name']}';\n")<br style="font-size:inherit">        f.write(f"  {b['name']}.position.set({b['pos'][0]},{b['pos'][1]},{b['pos'][2]});\n")<br style="font-size:inherit">        f.write(f"  boneMap['{b['name']}'] = {b['name']}; bones.push({b['name']});\n")<br style="font-size:inherit">    for b in bones:<br style="font-size:inherit">        if b['parent']: f.write(f"  boneMap['{b['parent']}'].add(boneMap['{b['name']}']);\n")<br style="font-size:inherit">    <br style="font-size:inherit">    f.write(f"\n  const walk = {json.dumps(get_action_data('Walk'))};\n")<br style="font-size:inherit">    f.write(f"  const wave = {json.dumps(get_action_data('Wave'))};\n")<br style="font-size:inherit">    f.write("  const clips = [walk, wave].filter(d => d).map(d => new THREE.AnimationClip(<a href="http://d.name" target="_blank">d.name</a>, d.duration, d.tracks.map(t => new THREE.KeyframeTrack(<a href="http://t.name" target="_blank">t.name</a>, t.times, t.values))));\n")<br style="font-size:inherit">    <br style="font-size:inherit">    f.write("\n  const mesh = new THREE.SkinnedMesh(geo, new THREE.MeshStandardMaterial({vertexColors: true, skinning: true}));\n")<br style="font-size:inherit">    f.write("  mesh.add(bones[0]); mesh.bind(new THREE.Skeleton(bones));\n")<br style="font-size:inherit">    f.write("  return { mesh, animations: clips };\n};")<br style="font-size:inherit"></div><div style="font-size:inherit" dir="auto"><br></div><div style="font-size:inherit" dir="auto"><br></div><div style="font-size:inherit" dir="auto">=============</div><div style="font-size:inherit" dir="auto">JavaScript below</div><div style="font-size:inherit" dir="auto">=============</div><div style="font-size:inherit" dir="auto"><div style="font-size:inherit">import { createHumanoid } from './HumanoidComplete.js';<br style="font-size:inherit"><br style="font-size:inherit">const { mesh, animations } = createHumanoid();<br style="font-size:inherit">scene.add(mesh);<br style="font-size:inherit"><br style="font-size:inherit">const mixer = new THREE.AnimationMixer(mesh);<br style="font-size:inherit">const walkAction = mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Walk'));<br style="font-size:inherit">walkAction.play();<br style="font-size:inherit"><br style="font-size:inherit">// Main loop<br style="font-size:inherit">function animate() {<br style="font-size:inherit">    mixer.update(clock.getDelta());<br style="font-size:inherit">    renderer.render(scene, camera);<br style="font-size:inherit">    requestAnimationFrame(animate);<br style="font-size:inherit">}<br style="font-size:inherit"></div><br></div>
</div></div>
</div>