Squash of 25 work commits (Sep 1–12):237da90render: the sprite lane hands the screen draw back the way it found it895b9a7particles: an emitter can ride a body's own frame914471fai-hub: a feed session whose last socket left ends on its idle timeout; skin: parent, skinned centroid and a nodes-only rig for retargets3fcccf3sim: the whole world is implicitly editable, and one seam says where the ground is5692fabsim: the landform world proves itself — walker through the tunnel includedf4af6dfsim: terrain knows who changed it — a plan layer over player historyadbb078render: a water volume can be physics without a picturec17480frender: a non-rigid body may carry its own orientationacad401sim: an agent with no route holds and retries instead of walking into the wall22b2bf9sim + chat: the composed world surface takes a map floor; the chat gets plan toolsfaf8112web path: the tessellator's lap timer, the trace span and the fusion cycle timer have no clock on the webd3472ebtsdf: the clock-taking XR helpers are native-only — the browser has no depth camera and no Instant4946c9esim + render: a repaint is not a world edit — colour and glow restyles never rebake the lightmap4317f58sim + render + chat: walk decks as a surface, the filmed body is no obstruction, the brief never asks9791279render: support rigged models and custom materials across viewers3ce2792sim: use deck geometry for collision and sensing c8b79ff Add portable PBR, rig and soft-body authoring support 108d423 Add transactional polygon modeling and editable asset documents 38f4d3b Refine editable modeling and firm yarn character behavior 08a2637 sim: add entity-owned lights and vehicle headlights 00d96a7 render: add clustered lights, local shadows and incremental GI cf916af render: import glTF asset extensions and wire clustered GI c6d2cca model: cut transaction memory and raise capacity limits 41af47a raytrace: add a CPU probe-ray BVH budget example c963d0a libs: the game sim splits into makepad-scene and makepad-soft-body; render, model, fab and the asset importer retarget Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
64 lines
3.7 KiB
Rust
64 lines
3.7 KiB
Rust
//! Headless end-to-end editable, textured, rigged and animated asset example.
|
|
//! cargo run --release -p makepad-model --example robot -- <output-directory>
|
|
use makepad_model::*;
|
|
fn apply(doc:&mut Document,id:&str,operations:Vec<Operation>) {
|
|
doc.apply(Transaction {request_id:id.into(),expected:doc.head(),operations},None).unwrap();
|
|
}
|
|
fn main()->std::result::Result<(),Box<dyn std::error::Error>> {
|
|
let path=std::env::args_os().nth(1).ok_or("supply output directory")?;
|
|
let path=std::path::PathBuf::from(path);
|
|
let mut doc=Document::new(Limits::default())?;
|
|
apply(&mut doc,"blockout",vec![
|
|
Operation::Cube {object:"body".into(),size:[0.6,0.8,0.3]},
|
|
Operation::Cube {object:"head".into(),size:[0.4,0.4,0.35]},
|
|
Operation::Cube {object:"arm".into(),size:[0.22,0.7,0.22]},
|
|
Operation::SetMaterial {material:1,value:Material {color:[0.18,0.65,0.85],base_color_png:Vec::new()}},
|
|
Operation::SetMaterial {material:2,value:Material {color:[0.9,0.55,0.12],base_color_png:Vec::new()}},
|
|
]);
|
|
for (i,(name,position,material)) in [
|
|
("body",[0.0,0.8,0.0],1),("head",[0.0,1.45,0.0],2),("arm",[0.48,0.9,0.0],2)
|
|
].into_iter().enumerate() {
|
|
let mesh=doc.object(name).unwrap();
|
|
let vertices=mesh.vertices().iter().map(|v|v.id).collect();
|
|
let faces=mesh.faces().iter().map(|f|f.id).collect();
|
|
apply(&mut doc,&format!("place-{i}"),vec![
|
|
Operation::Transform {object:name.into(),vertices,matrix:[
|
|
[1.,0.,0.,position[0]],[0.,1.,0.,position[1]],[0.,0.,1.,position[2]],[0.,0.,0.,1.]]},
|
|
Operation::AssignMaterial {object:name.into(),faces,material},
|
|
]);
|
|
}
|
|
apply(&mut doc,"surface",vec![
|
|
Operation::TextureSolid {material:1,width:64,height:64,color:[255;3]},
|
|
Operation::PaintTexture {material:1,center:[0.5,0.5],radius:0.2,color:[32,48,72]},
|
|
Operation::SetSkeleton {skeleton:Skeleton {joints:vec![
|
|
Joint {name:"root".into(),parent:None,translation:[0.0,0.8,0.0]},
|
|
Joint {name:"head".into(),parent:Some(0),translation:[0.0,0.65,0.0]},
|
|
Joint {name:"shoulder".into(),parent:Some(0),translation:[0.48,0.4,0.0]},
|
|
]}},
|
|
]);
|
|
// Deliberately rigid authored weights make the expected motion unambiguous.
|
|
for (name,joint) in [("body",0),("head",1),("arm",2)] {
|
|
let ops=doc.object(name).unwrap().vertices().iter().map(|v|Operation::SetWeights {
|
|
object:name.into(),vertex:v.id,weights:vec![mesh::JointWeight {joint,weight:1.0}]
|
|
}).collect();
|
|
apply(&mut doc,&format!("bind-{name}"),ops);
|
|
}
|
|
apply(&mut doc,"wave",vec![Operation::SetClip {clip:AnimationClip {
|
|
name:"wave".into(),channels:vec![AnimationChannel {joint:2,path:AnimationPath::Rotation,
|
|
keys:vec![Keyframe {time:0.0,value:[0.,0.,0.,1.]},
|
|
Keyframe {time:0.5,value:[0.,0.,0.5,0.75f64.sqrt()]},
|
|
Keyframe {time:1.0,value:[0.,0.,0.,1.]}]}],
|
|
}}]);
|
|
let source=doc.to_bytes(None)?;
|
|
let reopened=Document::from_bytes(&source,Limits::default(),None)?;
|
|
assert_eq!(reopened.head(),doc.head());
|
|
let compiled=reopened.compile(None)?;
|
|
let parsed=makepad_gltf::parse_glb_bytes(&compiled.glb)?;
|
|
assert_eq!(parsed.document.skins.as_ref().map(Vec::len),Some(1));
|
|
assert_eq!(parsed.document.animations.as_ref().map(Vec::len),Some(1));
|
|
std::fs::create_dir_all(&path)?;
|
|
std::fs::write(path.join("robot.mpmodel"),source)?;
|
|
std::fs::write(path.join("robot.glb"),compiled.glb)?;
|
|
println!("wrote editable source + GLB: {} vertices, {} triangles, {} material primitives, 3 joints, 1 clip",compiled.vertices,compiled.triangles,compiled.primitives.len());
|
|
Ok(())
|
|
}
|