use myth::prelude::*;
use myth::resources::Key;
use myth::utils::FpsCounter;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DemoMode {
SolidColor,
Gradient,
Planar,
Equirectangular,
CubeMap,
}
impl DemoMode {
fn label(self) -> &'static str {
match self {
Self::SolidColor => "Solid Color",
Self::Gradient => "Gradient",
Self::Equirectangular => "Equirectangular HDR",
Self::CubeMap => "Cubemap Skybox",
Self::Planar => "Planar Texture",
}
}
}
struct SkyboxDemo {
cam_node: NodeHandle,
controls: OrbitControls,
fps_counter: FpsCounter,
mode: DemoMode,
render_path: RenderPath,
env_texture: TextureHandle,
cube_env_texture: TextureHandle,
}
impl SkyboxDemo {
fn apply_mode(&self, scene: &mut Scene) {
scene.background.set_mode(match self.mode {
DemoMode::SolidColor => BackgroundMode::color(0.1, 0.1, 0.15),
DemoMode::Gradient => BackgroundMode::gradient(
Vec4::new(0.05, 0.05, 0.25, 1.0), Vec4::new(0.7, 0.45, 0.2, 1.0), ),
DemoMode::Planar => BackgroundMode::planar(self.env_texture, 1.0),
DemoMode::CubeMap => BackgroundMode::cubemap(self.cube_env_texture, 1.0),
DemoMode::Equirectangular => BackgroundMode::equirectangular(self.env_texture, 1.0),
});
scene.environment.set_env_map(match self.mode {
DemoMode::CubeMap => Some(self.cube_env_texture),
_ => Some(self.env_texture),
});
}
fn print_help() {
println!("╔═══════════════════════════════════════╗");
println!("║ Skybox Demo Controls ║");
println!("╠═══════════════════════════════════════╣");
println!("║ 1 — Solid color (hardware clear) ║");
println!("║ 2 — Gradient (procedural sky) ║");
println!("║ 3 — Planar Texture ║");
println!("║ 4 — Equirectangular HDR panorama ║");
println!("║ 5 — Cubemap Skybox ║");
println!("║ H — Toggle HighFidelity/BasicForward ║");
println!("║ Mouse drag / Scroll — Orbit / Zoom ║");
println!("╚═══════════════════════════════════════╝");
}
}
impl AppHandler for SkyboxDemo {
fn init(engine: &mut Engine, _window: &dyn Window) -> Self {
let env_texture = engine
.assets
.load_hdr_texture_blocking("examples/assets/envs/royal_esplanade_2k.hdr.jpg")
.expect("Failed to load HDR environment map");
let cube_env_texture = engine
.assets
.load_cube_texture_blocking(
[
"examples/assets/envs/Park2/posx.jpg",
"examples/assets/envs/Park2/negx.jpg",
"examples/assets/envs/Park2/posy.jpg",
"examples/assets/envs/Park2/negy.jpg",
"examples/assets/envs/Park2/posz.jpg",
"examples/assets/envs/Park2/negz.jpg",
],
ColorSpace::Srgb,
true,
)
.expect("Failed to load environment map");
let scene = engine.scene_manager.create_active();
scene.add_light(Light::new_directional(Vec3::new(1.0, 1.0, 1.0), 1.0));
scene.environment.set_env_map(Some(env_texture));
scene.environment.set_intensity(1.0);
let mode = DemoMode::Gradient;
let gltf_path =
std::path::Path::new("examples/assets/DamagedHelmet/glTF/DamagedHelmet.gltf");
let prefab =
GltfLoader::load(gltf_path, engine.assets.clone()).expect("Failed to load glTF model");
let node = scene.instantiate(&prefab);
scene.node(&node).set_scale(1.0).set_position(0.0, 0.0, 0.0);
let cam_node = scene.add_camera(Camera::new_perspective(45.0, 1280.0 / 720.0, 0.1));
scene
.node(&cam_node)
.set_position(0.0, 0.0, 3.5)
.look_at(Vec3::ZERO);
scene.active_camera = Some(cam_node);
let render_path = engine.renderer.render_path().clone();
Self::print_help();
let demo = Self {
cam_node,
controls: OrbitControls::new(Vec3::new(0.0, 0.0, 3.5), Vec3::ZERO),
fps_counter: FpsCounter::new(),
mode,
render_path,
env_texture,
cube_env_texture,
};
demo.apply_mode(scene);
demo
}
fn update(&mut self, engine: &mut Engine, window: &dyn Window, frame: &FrameState) {
let Some(scene) = engine.scene_manager.active_scene_mut() else {
return;
};
let mut mode_changed = false;
if engine.input.get_key_down(Key::Key1) && self.mode != DemoMode::SolidColor {
self.mode = DemoMode::SolidColor;
mode_changed = true;
}
if engine.input.get_key_down(Key::Key2) && self.mode != DemoMode::Gradient {
self.mode = DemoMode::Gradient;
mode_changed = true;
}
if engine.input.get_key_down(Key::Key3) && self.mode != DemoMode::Planar {
self.mode = DemoMode::Planar;
mode_changed = true;
}
if engine.input.get_key_down(Key::Key4) && self.mode != DemoMode::Equirectangular {
self.mode = DemoMode::Equirectangular;
mode_changed = true;
}
if engine.input.get_key_down(Key::Key5) && self.mode != DemoMode::CubeMap {
self.mode = DemoMode::CubeMap;
mode_changed = true;
}
if mode_changed {
self.apply_mode(scene);
println!("[Mode] → {}", self.mode.label());
}
if engine.input.get_key_down(Key::H) {
self.render_path = if self.render_path.supports_post_processing() {
RenderPath::BasicForward
} else {
RenderPath::HighFidelity
};
engine.renderer.set_render_path(self.render_path);
let path = if self.render_path.supports_post_processing() {
"HighFidelity"
} else {
"BasicForward"
};
println!("[Path] → {path}");
}
if let Some(cam_node) = scene.get_node_mut(self.cam_node) {
self.controls
.update(&mut cam_node.transform, &engine.input, 45.0, frame.dt);
}
if let Some(fps) = self.fps_counter.update() {
let path = if self.render_path.supports_post_processing() {
"HighFidelity"
} else {
"BasicForward"
};
window.set_title(&format!(
"Skybox Demo — {} | {} | FPS: {fps:.0}",
self.mode.label(),
path,
));
}
}
}
fn main() -> myth::Result<()> {
env_logger::init();
App::new()
.with_settings(RendererSettings {
path: RenderPath::BasicForward,
vsync: false,
..Default::default()
})
.run::<SkyboxDemo>()
}