use nightshade_api::prelude::*;
use std::f32::consts::TAU;
const RADIUS: f32 = 5.0;
const TICK_BASE: [f32; 4] = [0.20, 0.22, 0.28, 1.0];
struct Clock {
running: Stopwatch,
chase: Timer,
ticks: Vec<Entity>,
lit: usize,
second: Entity,
minute: Entity,
hour: Entity,
scale_slider: Entity,
pause_button: Entity,
state_label: Entity,
elapsed_label: Entity,
}
fn hand(world: &mut World, length: f32, width: f32, color: [f32; 4], glow: [f32; 3]) -> Entity {
let pivot = spawn_group(world, vec3(0.0, 0.0, 0.0));
let arm = spawn_object(
world,
Object {
shape: Shape::Cube,
position: vec3(0.0, length * 0.5, 0.2),
scale: vec3(width, length, width),
color,
..Object::default()
},
);
set_emissive(world, arm, glow, 3.0);
set_parent(world, arm, Some(pivot));
pivot
}
fn main() {
run(
|world| {
set_background(world, Background::Nebula);
show_grid(world, false);
set_bloom(world, true);
orbit_camera(world, vec3(0.0, 0.0, 0.0), 16.0);
set_orbit_view(world, vec3(0.0, 0.0, 0.0), 16.0, 0.0, 0.0);
point_light(world, vec3(0.0, 0.0, 6.0), [1.0, 1.0, 1.0], 6.0);
spawn_object(
world,
Object {
shape: Shape::Cube,
position: vec3(0.0, 0.0, -0.4),
scale: vec3(RADIUS * 2.4, RADIUS * 2.4, 0.3),
color: [0.05, 0.05, 0.08, 1.0],
..Object::default()
},
);
let mut ticks = Vec::new();
for index in 0..12 {
let angle = index as f32 / 12.0 * TAU;
let major = index % 3 == 0;
let size = if major { 0.6 } else { 0.35 };
let tick = spawn_object(
world,
Object {
shape: Shape::Cube,
position: vec3(angle.sin() * RADIUS, angle.cos() * RADIUS, 0.0),
scale: vec3(size, size, size),
color: TICK_BASE,
..Object::default()
},
);
ticks.push(tick);
}
let hour = hand(world, 2.6, 0.22, STEEL, [0.4, 0.5, 0.9]);
let minute = hand(world, 3.8, 0.16, TEAL, [0.1, 0.7, 0.7]);
let second = hand(world, 4.6, 0.08, GOLD, [1.0, 0.7, 0.1]);
let panel = spawn_panel(world, ScreenAnchor::TopLeft, 260.0, 240.0);
panel_heading(world, panel, "Clock");
panel_separator(world, panel);
panel_label(world, panel, "Time scale");
let scale_slider = panel_slider(world, panel, 0.0, 3.0, 1.0);
let pause_button = panel_button(world, panel, "Pause / Resume");
panel_separator(world, panel);
let state_label = panel_label(world, panel, "running");
let elapsed_label = panel_label(world, panel, "0.0 s");
Clock {
running: Stopwatch::new(),
chase: Timer::repeating(1.0),
ticks,
lit: 0,
second,
minute,
hour,
scale_slider,
pause_button,
state_label,
elapsed_label,
}
},
|world, state| {
set_time_scale(world, slider_value(world, state.scale_slider));
if button_clicked(world, state.pause_button) {
if is_paused(world) {
unpause(world);
} else {
pause(world);
}
}
let step = delta_time(world);
state.running.tick(step);
let fired = state.chase.tick(step).times_finished_this_tick();
if fired > 0 {
set_emissive(world, state.ticks[state.lit], [0.0, 0.0, 0.0], 0.0);
state.lit = (state.lit + fired as usize) % state.ticks.len();
set_emissive(world, state.ticks[state.lit], [1.0, 0.8, 0.25], 6.0);
}
let elapsed = state.running.elapsed();
set_rotation(world, state.second, Vec3::z(), -(elapsed / 12.0) * TAU);
set_rotation(world, state.minute, Vec3::z(), -(elapsed / 144.0) * TAU);
set_rotation(world, state.hour, Vec3::z(), -(elapsed / 1728.0) * TAU);
set_text(
world,
state.state_label,
if is_paused(world) {
"paused"
} else {
"running"
},
);
set_text(world, state.elapsed_label, &format!("{elapsed:.1} s"));
},
)
.unwrap();
}