Skip to main content

vtuber_example/
vtuber-example.rs

1use mittens_engine::engine::ecs::component::{
2    AmbientLightComponent, BackgroundColorComponent, Camera3DComponent, CameraXRComponent,
3    ColorComponent, DirectionalLightComponent, GLTFComponent, InputComponent,
4    InputTransformModeComponent, InputXRComponent, RenderableComponent, TransformComponent,
5};
6use mittens_engine::{engine, utils};
7
8#[path = "example_util/mod.rs"]
9mod example_util;
10
11fn main() {
12    mittens_engine::example_support::ensure_model_assets();
13    utils::logger::init();
14
15    let world = engine::ecs::World::default();
16    let mut universe = engine::Universe::new(world);
17
18    // Light pink background.
19    let background = universe
20        .world
21        .add_component(BackgroundColorComponent::new());
22    let background_c = universe
23        .world
24        .add_component(ColorComponent::rgba(1.0, 0.82, 0.90, 1.0));
25    let _ = universe.world.add_child(background, background_c);
26    universe.add(background);
27
28    // Small ambient so shadowed areas aren't pure black.
29    let ambient = universe
30        .world
31        .add_component(AmbientLightComponent::rgb(0.10, 0.10, 0.12));
32    universe.add(ambient);
33
34    // --- Camera rig (WASD + mouse) ---
35    // InputComponent is the root, and it owns a Transform (the camera rig).
36    let input = universe
37        .world
38        .add_component(InputComponent::new().with_speed(1.5));
39    let input_mode = universe.world.add_component(
40        InputTransformModeComponent::forward_z()
41            .with_fps_rotation()
42            .with_roll_axis_y(),
43    );
44    let _ = universe.attach(input, input_mode);
45
46    // Start slightly pulled back looking towards the origin.
47    let rig_transform = universe
48        .world
49        .add_component(TransformComponent::new().with_position(0.0, 0.0, 6.0));
50    let _ = universe.attach(input, rig_transform);
51
52    let camera3d = universe.world.add_component(Camera3DComponent::new());
53    let _ = universe.attach(rig_transform, camera3d);
54
55    // Topology: I { T { C3D } } — add a small camera-attached controls hint.
56    example_util::spawn_desktop_camera_controls_hint(&mut universe, rig_transform);
57
58    universe.add(input);
59
60    // --- lighting ---
61    // Directional key light (slightly down + forward Z).
62    let sun = universe.world.add_component(
63        DirectionalLightComponent::new()
64            .with_intensity(1.2)
65            .with_color(1.0, 0.98, 0.94),
66    );
67    let sun_dir = universe
68        .world
69        .add_component(TransformComponent::new().with_position(0.0, -0.35, 1.0));
70    let _ = universe.attach(sun_dir, sun);
71    universe.add(sun_dir);
72
73    let light_transform = universe.world.add_component(
74        TransformComponent::new()
75            .with_position(1.0, 6.0, 3.0)
76            .with_scale(0.1, 0.1, 0.1),
77    );
78    let light = universe.world.add_component(
79        engine::ecs::component::PointLightComponent::new()
80            .with_distance(120.0)
81            .with_color(1.0, 1.0, 1.0),
82    );
83    let _ = universe.attach(light_transform, light);
84    universe.add(light_transform);
85
86    // --- VTuber model ---
87    let model_root = universe.world.add_component(TransformComponent::new());
88    let model = universe
89        .world
90        .add_component(GLTFComponent::new("assets/models/pc-rei.hoodie.glb"));
91    // emissive for pc-rei
92    let emissive = universe
93        .world
94        .add_component(engine::ecs::component::EmissiveComponent::on());
95
96    let _ = universe.attach(model, emissive);
97
98    let xr_input = universe.world.add_component(InputXRComponent::on());
99    let xr_gamepad = universe
100        .world
101        .add_component(engine::ecs::component::InputXRGamepadComponent::new().speed(1.5));
102    let xr_head = universe.world.add_component(TransformComponent::new());
103    let xr_camera = universe.world.add_component(CameraXRComponent::on());
104    let _ = universe.attach(xr_input, xr_head);
105    let _ = universe.attach(xr_input, xr_gamepad);
106    let _ = universe.attach(xr_head, xr_camera);
107    let _ = universe.attach(xr_head, model_root);
108
109    let _ = universe.attach(model_root, model);
110    universe.add(xr_input);
111    universe.add(model_root);
112
113    // --- Background clouds (occluded + lit) ---
114    let bg_root = universe.world.add_component(
115        engine::ecs::component::BackgroundComponent::new().with_occlusion_and_lighting(),
116    );
117    universe.add(bg_root);
118    let mut cloud_params = example_util::CloudRingParams::default();
119    cloud_params.cloud_count = 8; // +3 clusters
120    cloud_params.angle_jitter = 0.35;
121    cloud_params.high_y_probability = 0.5;
122    cloud_params.high_y_multiplier = 1.5;
123    cloud_params.seed = 0x57_55_B0_01u32;
124    example_util::spawn_cloud_ring(&mut universe, bg_root, cloud_params);
125
126    // --- Simple environment ---
127    let spawn_cube = |universe: &mut engine::Universe,
128                      position: (f32, f32, f32),
129                      scale: (f32, f32, f32),
130                      color: (f32, f32, f32, f32)| {
131        let transform = universe.world.add_component(
132            TransformComponent::new()
133                .with_position(position.0, position.1, position.2)
134                .with_scale(scale.0, scale.1, scale.2),
135        );
136        let renderable = universe.world.add_component(RenderableComponent::cube());
137        let color = universe
138            .world
139            .add_component(ColorComponent::rgba(color.0, color.1, color.2, color.3));
140
141        let _ = universe.attach(transform, renderable);
142        let _ = universe.attach(renderable, color);
143
144        universe.add(transform);
145    };
146
147    // floor
148    spawn_cube(
149        &mut universe,
150        (0.0, -0.05, 0.0),
151        (10.0, 0.1, 10.0),
152        (0.92, 0.92, 0.92, 1.0),
153    );
154
155    // back wall
156    spawn_cube(
157        &mut universe,
158        (0.0, 1.5, -5.0),
159        (10.0, 3.0, 1.0),
160        (0.95, 0.94, 0.96, 1.0),
161    );
162
163    // desk
164    spawn_cube(
165        &mut universe,
166        (0.0, 0.35, 1.0),
167        (1.0, 0.75, 0.5),
168        (0.75, 0.70, 0.65, 1.0),
169    );
170
171    let xr_root = universe
172        .world
173        .add_component(engine::ecs::component::XrComponent::on());
174    universe.add(xr_root);
175
176    universe.systems.process_commands(
177        &mut universe.world,
178        &mut universe.visuals,
179        &mut universe.render_assets,
180        &mut universe.command_queue,
181    );
182
183    universe.enable_repl();
184    engine::Windowing::run_app(universe).expect("Windowing failed");
185}