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>
48 lines
4.2 KiB
Rust
48 lines
4.2 KiB
Rust
use makepad_mesh_edit::*;
|
|
fn ctx()->Context<'static>{Context::default()}
|
|
fn closed(m:&Mesh){assert!(m.validate(&mut ctx()).unwrap().is_closed_manifold);m.triangulate(&mut ctx()).unwrap();}
|
|
fn volume(m:&Mesh)->f64{let t=m.triangulate(&mut ctx()).unwrap();t.triangles.iter().map(|t0|{let [a,b,c]=t0.indices.map(|i|t.vertices[i as usize].position);(a[0]*(b[1]*c[2]-b[2]*c[1])+a[1]*(b[2]*c[0]-b[0]*c[2])+a[2]*(b[0]*c[1]-b[1]*c[0]))/6.}).sum()}
|
|
#[test]
|
|
fn connected_region_has_only_boundary_walls_and_retains_caps(){
|
|
let mut m=Mesh::cube([2.;3],&mut ctx()).unwrap();let fs=[m.faces()[1].id,m.faces()[3].id];
|
|
let out=m.extrude_region(&fs,[0.,1.,1.],&mut ctx()).unwrap();
|
|
assert_eq!(out.side_faces.len(),6);assert_eq!(out.caps,fs);assert_eq!(m.vertices().len(),14);closed(&m);assert!(volume(&m)>8.);
|
|
let saved=m.clone();assert!(m.extrude_region(&fs,[1.,0.,0.],&mut ctx()).is_err());assert_eq!(m,saved);
|
|
}
|
|
#[test]
|
|
fn cube_loop_cut_splits_four_faces_and_preserves_volume(){
|
|
let mut m=Mesh::cube([2.;3],&mut ctx()).unwrap();let before=volume(&m);let edge=*m.adjacency(&mut ctx()).unwrap().edges.keys().next().unwrap();
|
|
let out=m.loop_cut(edge,0.3,&mut ctx()).unwrap();assert_eq!(m.vertices().len(),12);assert_eq!(m.faces().len(),10);closed(&m);assert!((volume(&m)-before).abs()<1e-10);
|
|
assert_eq!(out.remapped.iter().filter(|(s,_)|matches!(s,ElementId::Face(_))).count(),4);
|
|
let bytes=m.to_bytes(&mut ctx()).unwrap();assert_eq!(Mesh::from_bytes(&bytes,&mut ctx()).unwrap(),m);
|
|
}
|
|
#[test]
|
|
fn split_and_collapse_preserve_closed_topology_and_interpolate_weights(){
|
|
let mut m=Mesh::cube([2.;3],&mut ctx()).unwrap();let e=*m.adjacency(&mut ctx()).unwrap().edges.keys().next().unwrap();
|
|
m.set_vertex_weights(e.0,&[JointWeight{joint:0,weight:1.}],&mut ctx()).unwrap();m.set_vertex_weights(e.1,&[JointWeight{joint:1,weight:1.}],&mut ctx()).unwrap();
|
|
let change=m.split_edges(&[e],0.25,&mut ctx()).unwrap();closed(&m);let v=change.created.iter().find_map(|i|if let ElementId::Vertex(v)=i{Some(*v)}else{None}).unwrap();
|
|
assert_eq!(m.vertex(v).unwrap().weights,vec![JointWeight{joint:0,weight:0.75},JointWeight{joint:1,weight:0.25}]);
|
|
m.collapse_edge(EdgeKey::new(e.0,v),0.5,&mut ctx()).unwrap();closed(&m);assert_eq!(m.vertices().len(),8);
|
|
}
|
|
#[test]
|
|
fn fill_and_bridge_close_real_boundaries_and_solidify_has_positive_volume(){
|
|
let mut m=Mesh::cube([2.;3],&mut ctx()).unwrap();let top=m.faces()[3].id;let cycle=m.face_corners(top).unwrap().iter().map(|c|c.vertex).collect::<Vec<_>>();m.delete_faces(&[top],&mut ctx()).unwrap();
|
|
m.fill_boundary(&cycle,0,&mut ctx()).unwrap();closed(&m);assert!((volume(&m)-8.).abs()<1e-10);
|
|
let mut p=Mesh::plane([2.,3.],&mut ctx()).unwrap();p.solidify(0.5,&mut ctx()).unwrap();closed(&p);assert!((volume(&p)-3.).abs()<1e-10);
|
|
let mut two=Mesh::from_polygons(&[[-1.,0.,-1.],[-1.,0.,1.],[1.,0.,1.],[1.,0.,-1.],[-1.,1.,-1.],[-1.,1.,1.],[1.,1.,1.],[1.,1.,-1.]],
|
|
&[Polygon::new(vec![3,2,1,0]),Polygon::new(vec![4,5,6,7])],&mut ctx()).unwrap();
|
|
let a=two.vertices()[..4].iter().map(|v|v.id).collect::<Vec<_>>();let b=two.vertices()[4..].iter().map(|v|v.id).collect::<Vec<_>>();
|
|
two.bridge_boundaries(&a,&b,0,&mut ctx()).unwrap();closed(&two);assert!((volume(&two)-4.).abs()<1e-10);
|
|
}
|
|
#[test]
|
|
fn coplanar_dissolve_preserves_area_and_refuses_material_or_uv_loss(){
|
|
let mut m=Mesh::from_polygons(&[[0.,0.,0.],[1.,0.,0.],[1.,1.,0.],[0.,1.,0.]],&[Polygon::new(vec![0,1,2]),Polygon::new(vec![0,2,3])],&mut ctx()).unwrap();
|
|
let e=EdgeKey::new(m.vertices()[0].id,m.vertices()[2].id);m.dissolve_edge(e,&mut ctx()).unwrap();assert_eq!(m.faces().len(),1);assert_eq!(m.corners().len(),4);assert_eq!(m.triangulate(&mut ctx()).unwrap().triangles.len(),2);
|
|
}
|
|
#[test]
|
|
fn isolated_cube_edge_bevel_has_uniform_width_and_refuses_overrun(){
|
|
let mut m=Mesh::cube([2.;3],&mut ctx()).unwrap();let e=*m.adjacency(&mut ctx()).unwrap().edges.keys().next().unwrap();
|
|
let before=m.clone();assert!(m.bevel_edges(&[e],2.,&mut ctx()).is_err());assert_eq!(m,before);
|
|
let changes=m.bevel_edges(&[e],0.2,&mut ctx()).unwrap();closed(&m);assert_eq!(m.vertices().len(),10);assert_eq!(m.faces().len(),7);
|
|
assert!((volume(&m)-(8.-0.2*0.2)).abs()<1e-10);assert!(changes.deleted.contains(&ElementId::Vertex(e.0)));assert!(changes.deleted.contains(&ElementId::Vertex(e.1)));
|
|
}
|