pub struct RenderAssets { /* private fields */ }Expand description
Renderer-side asset registry used by ECS systems.
Design:
- ECS and gameplay code refer to geometry by
CpuMeshHandle(CPU asset identity). - The renderer owns GPU resources and returns
MeshHandle. RenderAssetsbridges the two and caches uploads.
Implementations§
Source§impl RenderAssets
impl RenderAssets
pub fn new() -> Self
Sourcepub fn get_mesh(&mut self, mesh: BuiltinMeshType) -> CpuMeshHandle
pub fn get_mesh(&mut self, mesh: BuiltinMeshType) -> CpuMeshHandle
Return the CPU mesh handle for a built-in mesh.
Builtins are pre-registered, but this is also safe to call if a RenderAssets was
constructed via Default.
Examples found in repository?
examples/vr-input.rs (line 68)
62fn spawn_sun_background(universe: &mut engine::Universe) {
63 let bg_root = universe
64 .world
65 .add_component(engine::ecs::component::BackgroundComponent::new());
66 universe.add(bg_root);
67
68 let circle_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Circle2D);
69
70 // Big yellow disk.
71 let sun_t = universe.world.add_component(
72 TransformComponent::new()
73 .with_position(2.0, 1.5, -8.0)
74 .with_scale(3.5, 3.5, 3.5),
75 );
76 let sun_r = universe
77 .world
78 .add_component(RenderableComponent::new(Renderable::new(
79 circle_mesh,
80 MaterialHandle::TOON_MESH,
81 )));
82 let sun_color = universe
83 .world
84 .add_component(ColorComponent::rgba(1.0, 0.85, 0.15, 1.0));
85 let sun_emissive = universe.world.add_component(EmissiveComponent::on());
86
87 let _ = universe.attach(bg_root, sun_t);
88 let _ = universe.attach(sun_t, sun_r);
89 let _ = universe.attach(sun_r, sun_color);
90 let _ = universe.attach(sun_r, sun_emissive);
91
92 // Small white highlight disk.
93 let highlight_t = universe.world.add_component(
94 TransformComponent::new()
95 .with_position(-0.35, 0.35, -0.01)
96 .with_scale(0.45, 0.45, 0.45),
97 );
98 let highlight_r = universe
99 .world
100 .add_component(RenderableComponent::new(Renderable::new(
101 circle_mesh,
102 MaterialHandle::TOON_MESH,
103 )));
104 let highlight_color = universe
105 .world
106 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
107 let highlight_emissive = universe.world.add_component(EmissiveComponent::on());
108
109 let _ = universe.attach(sun_t, highlight_t);
110 let _ = universe.attach(highlight_t, highlight_r);
111 let _ = universe.attach(highlight_r, highlight_color);
112 let _ = universe.attach(highlight_r, highlight_emissive);
113}More examples
examples/example_util/mod.rs (line 116)
105pub fn spawn_cloud_ring(
106 universe: &mut engine::Universe,
107 bg_root: engine::ecs::ComponentId,
108 p: CloudRingParams,
109) {
110 if p.cloud_count == 0 || p.radius == 0.0 {
111 return;
112 }
113
114 let cube_mesh = universe
115 .render_assets
116 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
117
118 let step = std::f32::consts::TAU / (p.cloud_count as f32);
119 let angle_jitter = p.angle_jitter.clamp(0.0, 1.0);
120 let high_y_probability = p.high_y_probability.clamp(0.0, 1.0);
121
122 for i in 0..p.cloud_count {
123 let seed_i = p.seed ^ i.wrapping_mul(0x9E37_79B9);
124 let jitter = (rand01(seed_i ^ 0xa53a_9d2d) - 0.5) * step * angle_jitter;
125 let a = (i as f32) * step + jitter;
126 let cx = p.radius * a.cos();
127 let cz = p.radius * a.sin();
128
129 let cy = if rand01(seed_i ^ 0x7f4a_7c15) < high_y_probability {
130 p.center_y * p.high_y_multiplier
131 } else {
132 p.center_y
133 };
134
135 let center_tx = universe.world.add_component(
136 engine::ecs::component::TransformComponent::new().with_position(cx, cy, cz),
137 );
138 let _ = universe.attach(bg_root, center_tx);
139
140 let base_seed = seed_i ^ 0x243f_6a88;
141 for puff_i in 0..p.puffs_per_cloud {
142 let seed = base_seed ^ puff_i.wrapping_mul(1_103_515_245);
143
144 let ox = (rand01(seed ^ 0x68bc_21eb) - 0.5) * 9.0;
145 let oy = (rand01(seed ^ 0x02e5_be93) - 0.5) * 4.0;
146 let oz = (rand01(seed ^ 0xa1d3_4f2b) - 0.5) * 9.0;
147
148 let base = 0.7 + rand01(seed ^ 0x9e37_79b9) * 2.8;
149 let sx = base * (0.7 + rand01(seed ^ 0x243f_6a88) * 0.9);
150 let sy = base * (0.6 + rand01(seed ^ 0x85a3_08d3) * 1.0);
151 let sz = base * (0.7 + rand01(seed ^ 0x1319_8a2e) * 0.9);
152
153 let tx = universe.world.add_component(
154 engine::ecs::component::TransformComponent::new()
155 .with_position(ox, oy, oz)
156 .with_scale(sx, sy, sz),
157 );
158 let renderable =
159 universe
160 .world
161 .add_component(engine::ecs::component::RenderableComponent::new(
162 engine::graphics::primitives::Renderable::new(
163 cube_mesh,
164 engine::graphics::primitives::MaterialHandle::TOON_MESH,
165 ),
166 ));
167
168 let t = rand01(seed ^ 0x7f4a_7c15);
169 let r = 0.70 + 0.10 * t;
170 let g = 0.72 + 0.10 * t;
171 let b = 0.80 + 0.12 * t;
172 let color = universe
173 .world
174 .add_component(engine::ecs::component::ColorComponent::rgba(r, g, b, 1.0));
175
176 let _ = universe.attach(center_tx, tx);
177 let _ = universe.attach(tx, renderable);
178 let _ = universe.attach(renderable, color);
179 }
180 }
181}examples/openxr.rs (line 65)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 let world = engine::ecs::World::default();
11 let mut universe = engine::Universe::new(world);
12
13 let background = universe
14 .world
15 .add_component(engine::ecs::component::BackgroundColorComponent::new());
16 let background_c = universe
17 .world
18 .add_component(engine::ecs::component::ColorComponent::rgba(
19 0.3, 0.1, 1.0, 1.0,
20 ));
21 let _ = universe.world.add_child(background, background_c);
22 universe.add(background);
23
24 // --- Camera rig (WASD/QE) ---
25 // Keep this similar to the main demo so we can fly around the cube field.
26 let input = universe
27 .world
28 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
29 let input_mode = universe.world.add_component(
30 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
31 );
32 let _ = universe.attach(input, input_mode);
33
34 // Start pulled back so the grid is in view.
35 let rig_transform = universe.world.add_component(
36 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 4.0),
37 );
38 let _ = universe.attach(input, rig_transform);
39
40 let camera3d = universe
41 .world
42 .add_component(engine::ecs::component::Camera3DComponent::new());
43 let _ = universe.attach(rig_transform, camera3d);
44
45 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
46 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
47
48 // Simple point light so the toon shader reads well.
49 let light = universe.world.add_component(
50 engine::ecs::component::PointLightComponent::new()
51 .with_distance(50.0)
52 .with_color(1.0, 1.0, 1.0),
53 );
54 let light_transform = universe.world.add_component(
55 engine::ecs::component::TransformComponent::new().with_position(0.0, 5.0, 2.0),
56 );
57 let _ = universe.attach(light_transform, light);
58
59 universe.add(input);
60 universe.add(light_transform);
61
62 // --- 16x16x16 cube grid ---
63 let cube_mesh = universe
64 .render_assets
65 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
66
67 let n: usize = 32;
68 let cube_scale: f32 = 0.10;
69 let gap: f32 = 0.50;
70 let step: f32 = cube_scale + gap;
71
72 // Center positions around 0 by subtracting half the extent (in steps).
73 let half_extent_x = (n as f32 - 1.0) * step * 0.5;
74 let half_extent_y = (n as f32 - 1.0) * step * 0.5;
75 let half_extent_z = (n as f32 - 1.0) * step * 0.5;
76
77 // Move the whole container up/back based on its content size, plus the requested offsets.
78 // - up by +0.5 and by half the content height
79 // - back by -(0.5 + 1.0) and by half the content depth
80 let container_offset_y = half_extent_y + 0.5;
81 let container_offset_z = -(half_extent_z + 1.0 + 0.5);
82
83 let container = universe.world.add_component(
84 engine::ecs::component::TransformComponent::new().with_position(
85 0.0,
86 container_offset_y,
87 container_offset_z,
88 ),
89 );
90
91 for z in 0..n {
92 for y in 0..n {
93 for x in 0..n {
94 let px = x as f32 * step - half_extent_x;
95 let py = y as f32 * step - half_extent_y;
96 let pz = z as f32 * step - half_extent_z;
97
98 let tx = universe.world.add_component(
99 engine::ecs::component::TransformComponent::new()
100 .with_position(px, py, pz)
101 .with_scale(cube_scale, cube_scale, cube_scale),
102 );
103 let renderable =
104 universe
105 .world
106 .add_component(engine::ecs::component::RenderableComponent::new(
107 engine::graphics::primitives::Renderable::new(
108 cube_mesh,
109 engine::graphics::primitives::MaterialHandle::TOON_MESH,
110 ),
111 ));
112
113 let denom = (n - 1) as f32;
114 let color = engine::ecs::component::ColorComponent::rgba(
115 f32::sin(x as f32 / 10.0),
116 y as f32 / denom,
117 z as f32 / denom,
118 1.0,
119 );
120 let color_c = universe.world.add_component(color);
121
122 let _ = universe.attach(container, tx);
123 let _ = universe.attach(tx, renderable);
124 let _ = universe.attach(renderable, color_c);
125 }
126 }
127 }
128
129 universe.add(container);
130 universe.systems.process_commands(
131 &mut universe.world,
132 &mut universe.visuals,
133 &mut universe.render_assets,
134 &mut universe.command_queue,
135 );
136
137 let xr_input = universe
138 .world
139 .add_component(engine::ecs::component::InputXRComponent::on());
140 let xr_gamepad = universe
141 .world
142 .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
143 let xr_head = universe
144 .world
145 .add_component(engine::ecs::component::TransformComponent::new());
146 let camera_xr = universe
147 .world
148 .add_component(engine::ecs::component::CameraXRComponent::on());
149 let _ = universe.attach(xr_input, xr_head);
150 let _ = universe.attach(xr_input, xr_gamepad);
151 let _ = universe.attach(xr_head, camera_xr);
152 universe.add(xr_input);
153
154 // Add an OpenXR component so OpenXRSystem initializes and starts polling events.
155 let xr_root = universe
156 .world
157 .add_component(engine::ecs::component::XrComponent::on());
158 universe.add(xr_root);
159 universe.systems.process_commands(
160 &mut universe.world,
161 &mut universe.visuals,
162 &mut universe.render_assets,
163 &mut universe.command_queue,
164 );
165
166 universe.enable_repl();
167 engine::Windowing::run_app(universe).expect("Windowing failed");
168}examples/gestures-and-gizmos.rs (line 22)
13fn build_gestures_and_gizmos_scene(universe: &mut engine::Universe) -> Scene {
14 use engine::ecs::component::{
15 BackgroundColorComponent, BackgroundComponent, Camera3DComponent, ColorComponent,
16 DirectionalLightComponent, InputComponent, InputTransformModeComponent, PointerComponent,
17 RaycastableComponent, RenderableComponent, TransformComponent, TransformGizmoComponent,
18 };
19 use engine::graphics::BuiltinMeshType;
20 use engine::graphics::primitives::{MaterialHandle, Renderable};
21
22 let tri_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Triangle2D);
23 let cube_mesh = universe.render_assets.get_mesh(BuiltinMeshType::Cube);
24 let tetra_mesh = universe
25 .render_assets
26 .get_mesh(BuiltinMeshType::Tetrahedron);
27
28 // BackgroundColor { C.rgba }
29 let bg_color = universe
30 .world
31 .add_component(BackgroundColorComponent::new());
32 let bg_color_c = universe
33 .world
34 .add_component(ColorComponent::rgba(0.90, 0.90, 0.90, 1.0));
35 let _ = universe.world.add_child(bg_color, bg_color_c);
36 universe.add(bg_color);
37
38 // ambient light
39 let ambient = universe
40 .world
41 .add_component(AmbientLightComponent::rgb(0.25, 0.25, 0.25));
42 universe.add(ambient);
43
44 // ground plane
45 let ground_tx = universe.world.add_component(
46 TransformComponent::new()
47 .with_position(0.0, -2.5, 0.0)
48 .with_scale(20.0, 1.0, 20.0),
49 );
50 let ground_r = universe
51 .world
52 .add_component(RenderableComponent::new(Renderable::new(
53 universe.render_assets.get_mesh(BuiltinMeshType::Cube),
54 MaterialHandle::TOON_MESH,
55 )));
56 let ground_c = universe
57 .world
58 .add_component(ColorComponent::rgba(0.75, 0.75, 0.75, 1.0));
59 let _ = universe.attach(ground_tx, ground_r);
60 let _ = universe.attach(ground_r, ground_c);
61 let _ = universe.add(ground_tx);
62
63 // Background {
64 // with_occlusion_and_lighting()
65 // // using the example utils to add clouds to the background
66 // }
67 let bg_root = universe
68 .world
69 .add_component(BackgroundComponent::new().with_occlusion_and_lighting());
70 universe.add(bg_root);
71
72 // DirectionalLight {
73 // T { translate [1, 1, 1] }
74 // }
75 // Directional lights encode their direction in the node's world position.
76 let sun_t = universe
77 .world
78 .add_component(TransformComponent::new().with_position(1.0, 1.0, 1.0));
79 let sun = universe
80 .world
81 .add_component(DirectionalLightComponent::new());
82 let _ = universe.attach(sun_t, sun);
83 universe.add(sun_t);
84
85 // i = input
86 // t = transform
87 // c3d = camera3d
88 //
89 // I {
90 // T {
91 // C3D { with_fps_rotation().with_roll_axis_y() }
92 // }
93 // }
94 let input = universe
95 .world
96 .add_component(InputComponent::new().with_speed(2.5));
97 let input_mode = universe.world.add_component(
98 InputTransformModeComponent::forward_z()
99 .with_fps_rotation()
100 .with_roll_axis_y(),
101 );
102
103 let _ = universe.attach(input, input_mode);
104
105 // Forward is -Z, so put the camera at +Z looking toward the origin.
106 let rig_t = universe
107 .world
108 .add_component(TransformComponent::new().with_position(0.0, 0.0, 3.5));
109 let _ = universe.attach(input, rig_t);
110
111 let cam = universe
112 .world
113 .add_component(Camera3DComponent::new().with_far(600.0).with_fov(70.0));
114 let _ = universe.attach(rig_t, cam);
115
116 // Opt-in: treat this camera rig as a pointer source.
117 let pointer = universe.world.add_component(PointerComponent::new());
118 let _ = universe.attach(cam, pointer);
119
120 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
121 example_util::spawn_desktop_camera_controls_hint(universe, rig_t);
122
123 fn spawn_shape_with_gizmo(
124 universe: &mut engine::Universe,
125 mesh: engine::graphics::primitives::CpuMeshHandle,
126 pos: [f32; 3],
127 scale: [f32; 3],
128 rot_euler: [f32; 3],
129 color: [f32; 4],
130 ) {
131 let t = universe.world.add_component(
132 TransformComponent::new()
133 .with_position(pos[0], pos[1], pos[2])
134 .with_scale(scale[0], scale[1], scale[2])
135 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
136 );
137 let r = universe
138 .world
139 .add_component(RenderableComponent::new(Renderable::new(
140 mesh,
141 MaterialHandle::TOON_MESH,
142 )));
143 let c = universe
144 .world
145 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
146 let rc = universe
147 .world
148 .add_component(RaycastableComponent::enabled());
149 let g = universe.world.add_component(TransformGizmoComponent::new());
150
151 let _ = universe.attach(t, r);
152 let _ = universe.attach(r, c);
153 let _ = universe.attach(r, rc);
154 let _ = universe.attach(t, g);
155
156 universe.add(t);
157 }
158
159 fn spawn_shape_raycastable_no_gizmo(
160 universe: &mut engine::Universe,
161 mesh: engine::graphics::primitives::CpuMeshHandle,
162 pos: [f32; 3],
163 scale: [f32; 3],
164 rot_euler: [f32; 3],
165 color: [f32; 4],
166 ) {
167 let t = universe.world.add_component(
168 TransformComponent::new()
169 .with_position(pos[0], pos[1], pos[2])
170 .with_scale(scale[0], scale[1], scale[2])
171 .with_rotation_euler(rot_euler[0], rot_euler[1], rot_euler[2]),
172 );
173 let r = universe
174 .world
175 .add_component(RenderableComponent::new(Renderable::new(
176 mesh,
177 MaterialHandle::TOON_MESH,
178 )));
179 let c = universe
180 .world
181 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
182 let rc = universe
183 .world
184 .add_component(RaycastableComponent::enabled());
185
186 let _ = universe.attach(t, r);
187 let _ = universe.attach(r, c);
188 let _ = universe.attach(r, rc);
189
190 universe.add(t);
191 }
192
193 spawn_shape_with_gizmo(
194 universe,
195 tri_mesh,
196 [-1.2, 0.0, 0.0],
197 [0.65, 0.65, 0.65],
198 [0.0, 0.0, 0.0],
199 [0.2, 0.9, 0.25, 1.0],
200 );
201 spawn_shape_with_gizmo(
202 universe,
203 cube_mesh,
204 [0.0, 0.0, 0.0],
205 [0.55, 0.55, 0.55],
206 [0.0, 0.0, 0.0],
207 [0.95, 0.25, 0.2, 1.0],
208 );
209 spawn_shape_with_gizmo(
210 universe,
211 tetra_mesh,
212 [1.2, 0.0, 0.0],
213 [0.7, 0.7, 0.7],
214 [0.0, 0.0, 0.0],
215 [0.2, 0.55, 1.0, 1.0],
216 );
217
218 // Standalone tetrahedron: no gizmo, but explicitly raycastable.
219 // This helps isolate tetra picking vs gizmo-handle interception.
220 spawn_shape_raycastable_no_gizmo(
221 universe,
222 tetra_mesh,
223 [2.6, -0.6, 0.0],
224 [0.95, 0.95, 0.95],
225 [0.0, 0.0, 0.0],
226 [0.85, 0.85, 1.0, 1.0],
227 );
228
229 universe.add(input);
230
231 Scene { bg_root }
232}examples/background-example.rs (line 78)
20fn main() {
21 mittens_engine::example_support::ensure_model_assets();
22 utils::logger::init();
23
24 let world = engine::ecs::World::default();
25 let mut universe = engine::Universe::new(world);
26
27 // Dark-ish background clear color so the effect reads.
28 let clear = universe
29 .world
30 .add_component(engine::ecs::component::BackgroundColorComponent::new());
31 let clear_c = universe
32 .world
33 .add_component(engine::ecs::component::ColorComponent::rgba(
34 0.01, 0.01, 0.02, 1.0,
35 ));
36 let _ = universe.world.add_child(clear, clear_c);
37 universe.add(clear);
38
39 // --- Camera rig (WASD/QE) ---
40 let input = universe
41 .world
42 .add_component(engine::ecs::component::InputComponent::new().with_speed(2.0));
43 let input_mode = universe.world.add_component(
44 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
45 );
46 let _ = universe.attach(input, input_mode);
47
48 // Start pulled back so both background + foreground are in view.
49 let rig_transform = universe.world.add_component(
50 engine::ecs::component::TransformComponent::new().with_position(0.0, 1.0, 6.0),
51 );
52 let _ = universe.attach(input, rig_transform);
53
54 let camera3d = universe
55 .world
56 .add_component(engine::ecs::component::Camera3DComponent::new());
57 let _ = universe.attach(rig_transform, camera3d);
58
59 // Simple light for toon-shaded foreground.
60 let light = universe.world.add_component(
61 engine::ecs::component::PointLightComponent::new()
62 .with_distance(50.0)
63 .with_color(1.0, 1.0, 1.0),
64 );
65 let light_transform = universe.world.add_component(
66 engine::ecs::component::TransformComponent::new().with_position(0.0, 6.0, 2.0),
67 );
68 let _ = universe.attach(light_transform, light);
69
70 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
71 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
72
73 universe.add(input);
74 universe.add(light_transform);
75
76 let cube_mesh = universe
77 .render_assets
78 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
79
80 // --- Background world ---
81 // Any renderables under this node will go through the background draw list.
82 let bg_root = universe
83 .world
84 .add_component(engine::ecs::component::BackgroundComponent::new());
85 universe.add(bg_root);
86
87 // A large thin "ground" plane in the background layer.
88 // (Using a scaled cube for now; visually it's a plane.)
89 let ground_tx = universe.world.add_component(
90 engine::ecs::component::TransformComponent::new()
91 .with_position(0.0, -40.0, 0.0)
92 .with_scale(200.0, 1.0, 200.0),
93 );
94 let ground_renderable =
95 universe
96 .world
97 .add_component(engine::ecs::component::RenderableComponent::new(
98 engine::graphics::primitives::Renderable::new(
99 cube_mesh,
100 engine::graphics::primitives::MaterialHandle::UNLIT_MESH,
101 ),
102 ));
103 let ground_color = universe
104 .world
105 .add_component(engine::ecs::component::ColorComponent::rgba(
106 0.015, 0.02, 0.03, 1.0,
107 ));
108
109 let _ = universe.attach(bg_root, ground_tx);
110 let _ = universe.attach(ground_tx, ground_renderable);
111 let _ = universe.attach(ground_renderable, ground_color);
112
113 // Add some bright "stars" (small unlit cubes) scattered on a sphere.
114 // Use a grid + jitter + conditional skip so the distribution looks more even.
115 let lat_steps: u32 = 24;
116 let lon_steps: u32 = 48;
117 let density: f32 = 0.14;
118 let radius = 25.0;
119
120 for lat in 0..lat_steps {
121 for lon in 0..lon_steps {
122 let cell = lon + lat * lon_steps;
123 if rand01(cell ^ 0x5a1d_c0de) > density {
124 continue;
125 }
126
127 // Jitter within the cell.
128 let jx = rand01(cell ^ 0xA341_316C) - 0.5;
129 let jy = rand01(cell ^ 0xC801_3EA4) - 0.5;
130
131 // Latitude in [-pi/2, +pi/2], longitude in [0, 2pi).
132 let lat_t = (lat as f32 + 0.5 + 0.9 * jy) / (lat_steps as f32);
133 let lon_t = (lon as f32 + 0.5 + 0.9 * jx) / (lon_steps as f32);
134 let phi = (lat_t - 0.5) * std::f32::consts::PI; // [-pi/2,+pi/2]
135 let theta = lon_t * std::f32::consts::TAU;
136
137 let x = phi.cos() * theta.cos();
138 let y = phi.sin();
139 let z = phi.cos() * theta.sin();
140
141 let px = x * radius;
142 let py = y * radius;
143 let pz = z * radius;
144
145 let scale = 0.12 + rand01(cell.wrapping_add(12345)) * 0.20;
146
147 let tx = universe.world.add_component(
148 engine::ecs::component::TransformComponent::new()
149 .with_position(px, py, pz)
150 .with_scale(scale, scale, scale),
151 );
152 let renderable =
153 universe
154 .world
155 .add_component(engine::ecs::component::RenderableComponent::new(
156 engine::graphics::primitives::Renderable::new(
157 cube_mesh,
158 engine::graphics::primitives::MaterialHandle::UNLIT_MESH,
159 ),
160 ));
161
162 let c = 0.75 + rand01(cell.wrapping_mul(3)) * 0.25;
163 let color = universe
164 .world
165 .add_component(engine::ecs::component::ColorComponent::rgba(c, c, c, 1.0));
166
167 let _ = universe.attach(bg_root, tx);
168 let _ = universe.attach(tx, renderable);
169 let _ = universe.attach(renderable, color);
170 }
171 }
172
173 // --- Foreground world ---
174 // A small cube field that should parallax as you move.
175 let fg_root = universe.world.add_component(
176 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 0.0),
177 );
178
179 let n: i32 = 8;
180 let step: f32 = 0.8;
181 for z in 0..n {
182 for x in 0..n {
183 let px = (x - n / 2) as f32 * step;
184 let pz = -(z as f32) * step;
185
186 let tx = universe.world.add_component(
187 engine::ecs::component::TransformComponent::new()
188 .with_position(px, 0.5, pz)
189 .with_scale(0.25, 0.25, 0.25),
190 );
191 let renderable =
192 universe
193 .world
194 .add_component(engine::ecs::component::RenderableComponent::new(
195 engine::graphics::primitives::Renderable::new(
196 cube_mesh,
197 engine::graphics::primitives::MaterialHandle::TOON_MESH,
198 ),
199 ));
200
201 let fx = (x as f32) / ((n - 1) as f32);
202 let fz = (z as f32) / ((n - 1) as f32);
203 let color = universe
204 .world
205 .add_component(engine::ecs::component::ColorComponent::rgba(
206 0.2 + 0.8 * fx,
207 0.2 + 0.8 * (1.0 - fz),
208 0.4,
209 1.0,
210 ));
211
212 let _ = universe.attach(fg_root, tx);
213 let _ = universe.attach(tx, renderable);
214 let _ = universe.attach(renderable, color);
215 }
216 }
217
218 universe.add(fg_root);
219
220 universe.systems.process_commands(
221 &mut universe.world,
222 &mut universe.visuals,
223 &mut universe.render_assets,
224 &mut universe.command_queue,
225 );
226
227 universe.enable_repl();
228 engine::Windowing::run_app(universe).expect("Windowing failed");
229}examples/mesh-factory-example.rs (line 148)
6fn main() {
7 mittens_engine::example_support::ensure_model_assets();
8 utils::logger::init();
9
10 const LABEL_WRAP_AT: usize = 13;
11
12 let world = engine::ecs::World::default();
13 let mut universe = engine::Universe::new(world);
14
15 // add a clock
16 let clock = universe
17 .world
18 .add_component(engine::ecs::component::ClockComponent::new().with_bpm(60.0));
19 universe.add(clock);
20
21 // Input-driven camera rig.
22 // Topology: I { T { C3D } }
23 let input = universe
24 .world
25 .add_component(engine::ecs::component::InputComponent::new().with_speed(1.5));
26 let camera3d = universe
27 .world
28 .add_component(engine::ecs::component::Camera3DComponent::new());
29 let rig_transform = universe.world.add_component(
30 engine::ecs::component::TransformComponent::new().with_position(0.0, 0.0, 11.0),
31 );
32 let input_mode = universe.world.add_component(
33 engine::ecs::component::InputTransformModeComponent::forward_z().with_roll_axis_y(),
34 );
35 let _ = universe.attach(input, input_mode);
36 let _ = universe.attach(input, rig_transform);
37 let _ = universe.attach(rig_transform, camera3d);
38
39 // Topology: I { T { C3D } } — add a small camera-attached controls hint.
40 example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
41 universe.add(input);
42
43 // Light.
44 let light_transform = universe.world.add_component(
45 engine::ecs::component::TransformComponent::new().with_position(0.0, 2.0, 2.0),
46 );
47 let light = universe.world.add_component(
48 engine::ecs::component::PointLightComponent::new()
49 .with_distance(50.0)
50 .with_color(1.0, 1.0, 1.0),
51 );
52 let _ = universe.attach(light_transform, light);
53 universe.add(light_transform);
54
55 fn spawn_labeled_mesh(
56 universe: &mut engine::Universe,
57 x: f32,
58 y: f32,
59 label: &str,
60 mesh: engine::graphics::primitives::CpuMeshHandle,
61 scale: [f32; 3],
62 color: [f32; 4],
63 ) {
64 use engine::ecs::component::{
65 ActionComponent, AnimationComponent, AnimationState, ColorComponent, EmissiveComponent,
66 KeyframeComponent, RenderableComponent, TextComponent, TransformComponent,
67 };
68 use engine::graphics::primitives::{MaterialHandle, Renderable};
69
70 // Mesh.
71 let root = universe.world.add_component(
72 TransformComponent::new()
73 .with_position(x, y, 0.0)
74 .with_scale(scale[0], scale[1], scale[2]),
75 );
76
77 // Spin each shape around its own +Y axis using AnimationComponent + keyframes.
78 // We fill [0, 2) beats densely so it looks smooth.
79 let anim = universe
80 .world
81 .add_component(AnimationComponent::new().with_state(AnimationState::Looping));
82 let _ = universe.attach(root, anim);
83
84 let steps: usize = 64;
85 for i in 0..steps {
86 let beat = (i as f64) * (2.0 / (steps as f64));
87 let kf = universe.world.add_component(KeyframeComponent::new(beat));
88 let _ = universe.attach(anim, kf);
89
90 // Full turn over 2 beats.
91 let angle = (std::f64::consts::TAU * (beat / 2.0)) as f32;
92 let rotation = utils::math::quat_from_axis_angle([0.0, 1.0, 0.0], angle);
93
94 let action_cid = universe.world.add_component(ActionComponent::new(
95 engine::ecs::IntentValue::UpdateTransform {
96 component_ids: vec![root],
97 translation: [x, y, 0.0],
98 rotation_quat_xyzw: rotation,
99 scale,
100 },
101 ));
102 let _ = universe.attach(kf, action_cid);
103 }
104
105 let renderable = universe
106 .world
107 .add_component(RenderableComponent::new(Renderable::new(
108 mesh,
109 MaterialHandle::TOON_MESH,
110 )));
111 let color_c = universe
112 .world
113 .add_component(ColorComponent::rgba(color[0], color[1], color[2], color[3]));
114 let emissive = universe.world.add_component(EmissiveComponent::on());
115
116 let _ = universe.attach(root, renderable);
117 let _ = universe.attach(renderable, color_c);
118 let _ = universe.attach(renderable, emissive);
119
120 universe.add(root);
121
122 // Label (separate transform so we can scale text independently).
123 let text_root = universe.world.add_component(
124 TransformComponent::new()
125 .with_position(x, y + 0.75, 0.05)
126 .with_scale(0.09, 0.09, 1.0),
127 );
128 let text = universe
129 .world
130 .add_component(TextComponent::with_word_wrap_tokens(
131 label,
132 LABEL_WRAP_AT,
133 ["::", "(", ")", ",", "."],
134 ));
135 let text_color = universe
136 .world
137 .add_component(ColorComponent::rgba(1.0, 1.0, 1.0, 1.0));
138 let text_emissive = universe.world.add_component(EmissiveComponent::on());
139 let _ = universe.attach(text_root, text);
140 let _ = universe.attach(text, text_color);
141 let _ = universe.attach(text, text_emissive);
142 universe.add(text_root);
143 }
144
145 // Built-in meshes (stable ids).
146 let tri = universe
147 .render_assets
148 .get_mesh(engine::graphics::BuiltinMeshType::Triangle2D);
149 let quad = universe
150 .render_assets
151 .get_mesh(engine::graphics::BuiltinMeshType::Quad2D);
152 let cube = universe
153 .render_assets
154 .get_mesh(engine::graphics::BuiltinMeshType::Cube);
155 let tetra = universe
156 .render_assets
157 .get_mesh(engine::graphics::BuiltinMeshType::Tetrahedron);
158 let sphere = universe
159 .render_assets
160 .get_mesh(engine::graphics::BuiltinMeshType::Sphere);
161 let cone = universe
162 .render_assets
163 .get_mesh(engine::graphics::BuiltinMeshType::Cone);
164 let circle = universe
165 .render_assets
166 .get_mesh(engine::graphics::BuiltinMeshType::Circle2D);
167
168 // Layout.
169 let y = 0.0;
170 let dx = 1.8;
171 let x0 = -dx * 3.0;
172
173 spawn_labeled_mesh(
174 &mut universe,
175 x0 + dx * 0.0,
176 y,
177 "Triangle2D\nMeshFactory::triangle_2d()",
178 tri,
179 [1.0, 1.0, 1.0],
180 [1.0, 1.0, 1.0, 1.0],
181 );
182 spawn_labeled_mesh(
183 &mut universe,
184 x0 + dx * 1.0,
185 y,
186 "Quad2D\nMeshFactory::quad_2d()",
187 quad,
188 [1.0, 1.0, 1.0],
189 [1.0, 1.0, 1.0, 1.0],
190 );
191 spawn_labeled_mesh(
192 &mut universe,
193 x0 + dx * 2.0,
194 y,
195 "Cube\nMeshFactory::cube()",
196 cube,
197 [0.9, 0.9, 0.9],
198 [1.0, 1.0, 1.0, 1.0],
199 );
200 spawn_labeled_mesh(
201 &mut universe,
202 x0 + dx * 3.0,
203 y,
204 "Tetrahedron\nMeshFactory::tetrahedron()",
205 tetra,
206 [1.0, 1.0, 1.0],
207 [1.0, 1.0, 1.0, 1.0],
208 );
209 spawn_labeled_mesh(
210 &mut universe,
211 x0 + dx * 4.0,
212 y,
213 "Sphere\nMeshFactory::sphere()",
214 sphere,
215 [1.0, 1.0, 1.0],
216 [1.0, 1.0, 1.0, 1.0],
217 );
218 spawn_labeled_mesh(
219 &mut universe,
220 x0 + dx * 5.0,
221 y,
222 "Cone\nMeshFactory::cone(32)",
223 cone,
224 [1.0, 1.0, 1.0],
225 [1.0, 1.0, 1.0, 1.0],
226 );
227 spawn_labeled_mesh(
228 &mut universe,
229 x0 + dx * 6.0,
230 y,
231 "Circle2D\nMeshFactory::circle_2d(0.45, 0.5, 64)",
232 circle,
233 [1.0, 1.0, 1.0],
234 [1.0, 1.0, 1.0, 1.0],
235 );
236
237 // Process init-time registrations (Text expands into glyph subtrees here).
238 universe.systems.process_commands(
239 &mut universe.world,
240 &mut universe.visuals,
241 &mut universe.render_assets,
242 &mut universe.command_queue,
243 );
244
245 engine::Windowing::run_app(universe).expect("Windowing failed");
246}Additional examples can be found in:
Sourcepub fn register_mesh(&mut self, mesh: CpuMesh) -> CpuMeshHandle
pub fn register_mesh(&mut self, mesh: CpuMesh) -> CpuMeshHandle
Register CPU mesh data and get a stable CPU-side handle.
If callers want reuse, they should keep and share this handle.
Sourcepub fn wireframe_box_mesh(&mut self, thickness: f32) -> CpuMeshHandle
pub fn wireframe_box_mesh(&mut self, thickness: f32) -> CpuMeshHandle
Return a shared unit wireframe-box mesh for the requested relative edge thickness.
Sourcepub fn register_imported_mesh(
&mut self,
key: impl Into<String>,
mesh: CpuMesh,
) -> CpuMeshHandle
pub fn register_imported_mesh( &mut self, key: impl Into<String>, mesh: CpuMesh, ) -> CpuMeshHandle
Register an imported mesh and index it by key for later lookup.
Sourcepub fn imported_mesh(&self, key: &str) -> Option<CpuMeshHandle>
pub fn imported_mesh(&self, key: &str) -> Option<CpuMeshHandle>
Look up an imported mesh handle by key.
Examples found in repository?
examples/vtuber-joints-example.rs (line 459)
422fn debug_print_selected_joint_influence(
423 universe: &engine::Universe,
424 mesh_key: &str,
425 model_root: engine::ecs::ComponentId,
426 selected: &[(usize, engine::ecs::ComponentId)],
427) {
428 let Some(renderable) = find_renderable_by_mesh_key(&universe.world, model_root, mesh_key)
429 else {
430 println!(
431 "[vtuber-joints-example] influence: mesh_key='{}' renderable not found",
432 mesh_key
433 );
434 return;
435 };
436
437 let renderable_handle = universe
438 .world
439 .get_component_by_id_as::<RenderableComponent>(renderable)
440 .and_then(|r| r.get_handle());
441 println!(
442 "[vtuber-joints-example] influence: mesh_key='{}' renderable={:?} instance_handle={:?}",
443 mesh_key, renderable, renderable_handle
444 );
445 let Some(skin_id) = find_skin_id_for_renderable(&universe.world, renderable) else {
446 println!(
447 "[vtuber-joints-example] influence: mesh_key='{}' has no skin_id",
448 mesh_key
449 );
450 return;
451 };
452 let Some(skin) = universe.visuals.skin(skin_id) else {
453 println!(
454 "[vtuber-joints-example] influence: skin_id={:?} missing in visuals",
455 skin_id
456 );
457 return;
458 };
459 let Some(cpu_h) = universe.render_assets.imported_mesh(mesh_key) else {
460 println!(
461 "[vtuber-joints-example] influence: mesh_key='{}' missing in RenderAssets (did meshes register?)",
462 mesh_key
463 );
464 return;
465 };
466 let Some(cpu) = universe.render_assets.cpu_mesh(cpu_h) else {
467 println!(
468 "[vtuber-joints-example] influence: mesh_key='{}' cpu mesh handle invalid",
469 mesh_key
470 );
471 return;
472 };
473 let (Some(joints0), Some(weights0)) = (cpu.joints0.as_ref(), cpu.weights0.as_ref()) else {
474 println!(
475 "[vtuber-joints-example] influence: mesh_key='{}' has no skin attributes",
476 mesh_key
477 );
478 return;
479 };
480
481 let joint_count = skin.joint_count();
482 let totals = compute_total_weight_per_joint(joints0, weights0, joint_count);
483
484 println!(
485 "[vtuber-joints-example] influence: mesh_key='{}' verts={} joints={}",
486 mesh_key,
487 cpu.vertices.len(),
488 joint_count
489 );
490
491 for &(node_index, joint_tx) in selected.iter() {
492 // Find the skin joint index that maps to this node.
493 let skin_joint_index = skin
494 .joint_node_indices
495 .iter()
496 .position(|&ni| ni == node_index);
497
498 let name = universe
499 .world
500 .get_component_record(joint_tx)
501 .map(|r| r.name.as_str())
502 .unwrap_or("<unknown>");
503
504 match skin_joint_index {
505 Some(j) => {
506 let total = totals.get(j).copied().unwrap_or(0.0);
507 println!(
508 " influence: name='{}' node_index={} skin_joint_index={} total_weight={:.3}",
509 name, node_index, j, total
510 );
511 }
512 None => {
513 println!(
514 " influence: name='{}' node_index={} skin_joint_index=<none>",
515 name, node_index
516 );
517 }
518 }
519 }
520}
521
522fn compute_total_weight_per_joint(
523 joints0: &[[u16; 4]],
524 weights0: &[[f32; 4]],
525 joint_count: usize,
526) -> Vec<f32> {
527 let mut totals = vec![0.0f32; joint_count];
528 if joint_count == 0 {
529 return totals;
530 }
531 for (jv, wv) in joints0.iter().zip(weights0.iter()) {
532 for lane in 0..4 {
533 let j = jv[lane] as usize;
534 if j >= joint_count {
535 continue;
536 }
537 let w = wv[lane];
538 if w > 0.0 {
539 totals[j] += w;
540 }
541 }
542 }
543 totals
544}
545
546fn select_body_prim0_influencers(
547 universe: &engine::Universe,
548 model_root: engine::ecs::ComponentId,
549 mesh_key: &str,
550 count: usize,
551 node_index_to_transform: &HashMap<usize, engine::ecs::ComponentId>,
552) -> Option<Vec<(usize, engine::ecs::ComponentId)>> {
553 if count == 0 {
554 return Some(Vec::new());
555 }
556
557 let renderable = find_renderable_by_mesh_key(&universe.world, model_root, mesh_key)?;
558 let skin_id = find_skin_id_for_renderable(&universe.world, renderable)?;
559 let skin = universe.visuals.skin(skin_id)?;
560
561 let cpu_h = universe.render_assets.imported_mesh(mesh_key)?;
562 let cpu = universe.render_assets.cpu_mesh(cpu_h)?;
563 let joints0 = cpu.joints0.as_ref()?;
564 let weights0 = cpu.weights0.as_ref()?;
565
566 let joint_count = skin.joint_count();
567 if joint_count == 0 {
568 return None;
569 }
570
571 let mut total_weight_per_joint: Vec<f32> = vec![0.0; joint_count];
572 for (jv, wv) in joints0.iter().zip(weights0.iter()) {
573 for lane in 0..4 {
574 let j = jv[lane] as usize;
575 if j >= joint_count {
576 continue;
577 }
578 let w = wv[lane];
579 if w > 0.0 {
580 total_weight_per_joint[j] += w;
581 }
582 }
583 }
584
585 let mut ranked: Vec<(usize, f32)> =
586 total_weight_per_joint.iter().copied().enumerate().collect();
587 ranked.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
588
589 let min_total: f32 = 10.0;
590
591 println!(
592 "[vtuber-joints-example] auto joints for mesh_key='{}': verts={} joint_count={} min_total={}",
593 mesh_key,
594 cpu.vertices.len(),
595 joint_count,
596 min_total
597 );
598
599 let mut out: Vec<(usize, engine::ecs::ComponentId)> = Vec::with_capacity(count);
600 let mut seen: HashSet<engine::ecs::ComponentId> = HashSet::new();
601
602 for (joint_index, total) in ranked.into_iter() {
603 if out.len() >= count {
604 break;
605 }
606 if total < min_total {
607 continue;
608 }
609
610 let node_index = *skin.joint_node_indices.get(joint_index)?;
611 let Some(&joint_tx) = node_index_to_transform.get(&node_index) else {
612 continue;
613 };
614 if !seen.insert(joint_tx) {
615 continue;
616 }
617
618 let name = universe
619 .world
620 .get_component_record(joint_tx)
621 .map(|n| n.name.clone())
622 .unwrap_or_else(|| "<unknown>".to_string());
623
624 println!(
625 " auto[{:<2}] joint_index={:<3} total_weight={:<10.3} node_index={:<3} name={} tx={:?}",
626 out.len(),
627 joint_index,
628 total,
629 node_index,
630 name,
631 joint_tx
632 );
633
634 out.push((node_index, joint_tx));
635 }
636
637 if out.is_empty() { None } else { Some(out) }
638}Sourcepub fn cpu_mesh(&self, h: CpuMeshHandle) -> Option<&CpuMesh>
pub fn cpu_mesh(&self, h: CpuMeshHandle) -> Option<&CpuMesh>
Examples found in repository?
examples/vtuber-joints-example.rs (line 466)
422fn debug_print_selected_joint_influence(
423 universe: &engine::Universe,
424 mesh_key: &str,
425 model_root: engine::ecs::ComponentId,
426 selected: &[(usize, engine::ecs::ComponentId)],
427) {
428 let Some(renderable) = find_renderable_by_mesh_key(&universe.world, model_root, mesh_key)
429 else {
430 println!(
431 "[vtuber-joints-example] influence: mesh_key='{}' renderable not found",
432 mesh_key
433 );
434 return;
435 };
436
437 let renderable_handle = universe
438 .world
439 .get_component_by_id_as::<RenderableComponent>(renderable)
440 .and_then(|r| r.get_handle());
441 println!(
442 "[vtuber-joints-example] influence: mesh_key='{}' renderable={:?} instance_handle={:?}",
443 mesh_key, renderable, renderable_handle
444 );
445 let Some(skin_id) = find_skin_id_for_renderable(&universe.world, renderable) else {
446 println!(
447 "[vtuber-joints-example] influence: mesh_key='{}' has no skin_id",
448 mesh_key
449 );
450 return;
451 };
452 let Some(skin) = universe.visuals.skin(skin_id) else {
453 println!(
454 "[vtuber-joints-example] influence: skin_id={:?} missing in visuals",
455 skin_id
456 );
457 return;
458 };
459 let Some(cpu_h) = universe.render_assets.imported_mesh(mesh_key) else {
460 println!(
461 "[vtuber-joints-example] influence: mesh_key='{}' missing in RenderAssets (did meshes register?)",
462 mesh_key
463 );
464 return;
465 };
466 let Some(cpu) = universe.render_assets.cpu_mesh(cpu_h) else {
467 println!(
468 "[vtuber-joints-example] influence: mesh_key='{}' cpu mesh handle invalid",
469 mesh_key
470 );
471 return;
472 };
473 let (Some(joints0), Some(weights0)) = (cpu.joints0.as_ref(), cpu.weights0.as_ref()) else {
474 println!(
475 "[vtuber-joints-example] influence: mesh_key='{}' has no skin attributes",
476 mesh_key
477 );
478 return;
479 };
480
481 let joint_count = skin.joint_count();
482 let totals = compute_total_weight_per_joint(joints0, weights0, joint_count);
483
484 println!(
485 "[vtuber-joints-example] influence: mesh_key='{}' verts={} joints={}",
486 mesh_key,
487 cpu.vertices.len(),
488 joint_count
489 );
490
491 for &(node_index, joint_tx) in selected.iter() {
492 // Find the skin joint index that maps to this node.
493 let skin_joint_index = skin
494 .joint_node_indices
495 .iter()
496 .position(|&ni| ni == node_index);
497
498 let name = universe
499 .world
500 .get_component_record(joint_tx)
501 .map(|r| r.name.as_str())
502 .unwrap_or("<unknown>");
503
504 match skin_joint_index {
505 Some(j) => {
506 let total = totals.get(j).copied().unwrap_or(0.0);
507 println!(
508 " influence: name='{}' node_index={} skin_joint_index={} total_weight={:.3}",
509 name, node_index, j, total
510 );
511 }
512 None => {
513 println!(
514 " influence: name='{}' node_index={} skin_joint_index=<none>",
515 name, node_index
516 );
517 }
518 }
519 }
520}
521
522fn compute_total_weight_per_joint(
523 joints0: &[[u16; 4]],
524 weights0: &[[f32; 4]],
525 joint_count: usize,
526) -> Vec<f32> {
527 let mut totals = vec![0.0f32; joint_count];
528 if joint_count == 0 {
529 return totals;
530 }
531 for (jv, wv) in joints0.iter().zip(weights0.iter()) {
532 for lane in 0..4 {
533 let j = jv[lane] as usize;
534 if j >= joint_count {
535 continue;
536 }
537 let w = wv[lane];
538 if w > 0.0 {
539 totals[j] += w;
540 }
541 }
542 }
543 totals
544}
545
546fn select_body_prim0_influencers(
547 universe: &engine::Universe,
548 model_root: engine::ecs::ComponentId,
549 mesh_key: &str,
550 count: usize,
551 node_index_to_transform: &HashMap<usize, engine::ecs::ComponentId>,
552) -> Option<Vec<(usize, engine::ecs::ComponentId)>> {
553 if count == 0 {
554 return Some(Vec::new());
555 }
556
557 let renderable = find_renderable_by_mesh_key(&universe.world, model_root, mesh_key)?;
558 let skin_id = find_skin_id_for_renderable(&universe.world, renderable)?;
559 let skin = universe.visuals.skin(skin_id)?;
560
561 let cpu_h = universe.render_assets.imported_mesh(mesh_key)?;
562 let cpu = universe.render_assets.cpu_mesh(cpu_h)?;
563 let joints0 = cpu.joints0.as_ref()?;
564 let weights0 = cpu.weights0.as_ref()?;
565
566 let joint_count = skin.joint_count();
567 if joint_count == 0 {
568 return None;
569 }
570
571 let mut total_weight_per_joint: Vec<f32> = vec![0.0; joint_count];
572 for (jv, wv) in joints0.iter().zip(weights0.iter()) {
573 for lane in 0..4 {
574 let j = jv[lane] as usize;
575 if j >= joint_count {
576 continue;
577 }
578 let w = wv[lane];
579 if w > 0.0 {
580 total_weight_per_joint[j] += w;
581 }
582 }
583 }
584
585 let mut ranked: Vec<(usize, f32)> =
586 total_weight_per_joint.iter().copied().enumerate().collect();
587 ranked.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
588
589 let min_total: f32 = 10.0;
590
591 println!(
592 "[vtuber-joints-example] auto joints for mesh_key='{}': verts={} joint_count={} min_total={}",
593 mesh_key,
594 cpu.vertices.len(),
595 joint_count,
596 min_total
597 );
598
599 let mut out: Vec<(usize, engine::ecs::ComponentId)> = Vec::with_capacity(count);
600 let mut seen: HashSet<engine::ecs::ComponentId> = HashSet::new();
601
602 for (joint_index, total) in ranked.into_iter() {
603 if out.len() >= count {
604 break;
605 }
606 if total < min_total {
607 continue;
608 }
609
610 let node_index = *skin.joint_node_indices.get(joint_index)?;
611 let Some(&joint_tx) = node_index_to_transform.get(&node_index) else {
612 continue;
613 };
614 if !seen.insert(joint_tx) {
615 continue;
616 }
617
618 let name = universe
619 .world
620 .get_component_record(joint_tx)
621 .map(|n| n.name.clone())
622 .unwrap_or_else(|| "<unknown>".to_string());
623
624 println!(
625 " auto[{:<2}] joint_index={:<3} total_weight={:<10.3} node_index={:<3} name={} tx={:?}",
626 out.len(),
627 joint_index,
628 total,
629 node_index,
630 name,
631 joint_tx
632 );
633
634 out.push((node_index, joint_tx));
635 }
636
637 if out.is_empty() { None } else { Some(out) }
638}pub fn cpu_mesh_count(&self) -> usize
pub fn imported_mesh_count(&self) -> usize
Sourcepub fn gpu_mesh_handle(
&mut self,
uploader: &mut dyn MeshUploader,
cpu_mesh: CpuMeshHandle,
) -> Result<MeshHandle, Box<dyn Error>>
pub fn gpu_mesh_handle( &mut self, uploader: &mut dyn MeshUploader, cpu_mesh: CpuMeshHandle, ) -> Result<MeshHandle, Box<dyn Error>>
Get (or upload) a mesh into the renderer and return a renderer-owned MeshHandle.
Trait Implementations§
Source§impl Debug for RenderAssets
impl Debug for RenderAssets
Source§impl Default for RenderAssets
impl Default for RenderAssets
Source§fn default() -> RenderAssets
fn default() -> RenderAssets
Returns the “default value” for a type. Read more
Auto Trait Implementations§
impl Freeze for RenderAssets
impl RefUnwindSafe for RenderAssets
impl Send for RenderAssets
impl Sync for RenderAssets
impl Unpin for RenderAssets
impl UnsafeUnpin for RenderAssets
impl UnwindSafe for RenderAssets
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.