stl_loader/stl_loader.rs
1//! STL Model Loading Demo - demonstrates loading 3D models from STL files.
2//!
3//! This example shows the fluent geometry loading API:
4//! - Load STL files with `ctx.load("path.stl")`
5//! - Center, normalize, and scale models with chained methods
6//! - Orbit around the model with mouse drag or auto-rotate
7
8use hoplite::{AppConfig, Color, OrbitCamera, OrbitMode, Quat, Transform, Vec3, run_with_config};
9
10fn main() {
11 run_with_config(
12 AppConfig::new().title("STL Loader Demo").size(1280, 720),
13 |ctx| {
14 ctx.default_font(18.0);
15 ctx.background_color(Color::rgb(0.08, 0.08, 0.12));
16 ctx.enable_mesh_rendering();
17
18 // Load the chess piece STL with the fluent API
19 // - centered() moves the bounding box center to origin
20 // - normalized() scales to fit in a unit cube
21 // - scaled() then scales to desired size
22 let chess_piece = ctx
23 .load("examples/assets/models/chess.stl")
24 .centered()
25 .upright()
26 .normalized()
27 .scaled(2.0)
28 .unwrap();
29
30 // Create some primitive meshes for the scene
31 let plane = ctx.mesh_plane(10.0);
32 let cube = ctx.mesh_cube();
33
34 // Orbit camera for viewing the model
35 let mut orbit = OrbitCamera::new()
36 .target(Vec3::new(0.0, 1.0, 0.0))
37 .distance(5.0)
38 .elevation(0.4)
39 .fov(60.0)
40 .mode(OrbitMode::AutoRotate { speed: 0.3 });
41
42 let mut time = 0.0f32;
43
44 move |frame| {
45 time += frame.dt;
46
47 // Update camera
48 orbit.update(frame.input, frame.dt);
49 frame.set_camera(orbit.camera());
50
51 // Draw the floor
52 frame
53 .mesh(plane)
54 .transform(Transform::new())
55 .color(Color::rgb(0.15, 0.15, 0.18))
56 .draw();
57
58 // Draw the chess piece on a pedestal
59 // Pedestal base
60 frame
61 .mesh(cube)
62 .transform(
63 Transform::new()
64 .position(Vec3::new(0.0, 0.15, 0.0))
65 .scale(Vec3::new(1.5, 0.3, 1.5)),
66 )
67 .color(Color::rgb(0.2, 0.2, 0.25))
68 .draw();
69
70 // The chess piece - positioned on top of pedestal
71 // Slowly bobbing up and down
72 let bob = (time * 1.5).sin() * 0.05;
73 frame
74 .mesh(chess_piece)
75 .transform(
76 Transform::new()
77 .position(Vec3::new(0.0, 1.3 + bob, 0.0))
78 .rotation(Quat::from_rotation_y(time * 0.2)),
79 )
80 .color(Color::rgb(0.9, 0.85, 0.7))
81 .draw();
82
83 // Some decorative elements around the scene
84 let decoration_color = Color::rgb(0.3, 0.25, 0.35);
85 for i in 0..4 {
86 let angle = (i as f32) * std::f32::consts::FRAC_PI_2 + time * 0.1;
87 let radius = 3.0;
88 let x = angle.cos() * radius;
89 let z = angle.sin() * radius;
90 let height = 0.3 + (time + i as f32).sin().abs() * 0.2;
91
92 frame
93 .mesh(cube)
94 .transform(
95 Transform::new()
96 .position(Vec3::new(x, height, z))
97 .scale(Vec3::new(0.4, height * 2.0, 0.4))
98 .rotation(Quat::from_rotation_y(time + i as f32)),
99 )
100 .color(decoration_color)
101 .draw();
102 }
103
104 // UI text
105 frame.text(10.0, 10.0, "STL Model Loading Demo");
106 frame.text_color(
107 10.0,
108 35.0,
109 "Drag mouse to orbit, scroll to zoom",
110 Color::rgb(0.6, 0.6, 0.6),
111 );
112 frame.text_color(
113 10.0,
114 55.0,
115 &format!("FPS: {:.0}", frame.fps()),
116 Color::rgb(0.5, 0.5, 0.5),
117 );
118 }
119 },
120 );
121}