Skip to main content

RenderAssets

Struct RenderAssets 

Source
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.
  • RenderAssets bridges the two and caches uploads.

Implementations§

Source§

impl RenderAssets

Source

pub fn new() -> Self

Source

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
Hide additional 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}
Source

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.

Source

pub fn wireframe_box_mesh(&mut self, thickness: f32) -> CpuMeshHandle

Return a shared unit wireframe-box mesh for the requested relative edge thickness.

Source

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.

Source

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}
Source

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}
Source

pub fn cpu_mesh_count(&self) -> usize

Source

pub fn imported_mesh_count(&self) -> usize

Source

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

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RenderAssets

Source§

fn default() -> RenderAssets

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

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>

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)

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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more