Skip to main content

Sky

Struct Sky 

Source
pub struct Sky {
Show 14 fields pub atmosphere: bool, pub haze: f32, pub sun_size: f32, pub stars: f32, pub night_light: Color, pub fog_density: f32, pub fog_height: f32, pub fog_falloff: f32, pub fog_color: Color, pub fog_start: f32, pub fog_end: f32, pub fog_sun_glow: f32, pub fog_sun_focus: f32, pub light_shafts: f32,
}
Expand description

The sky behind the 3D world, and the fog in front of it: ctx.sky.

With atmosphere on (the default), the sky is worked out from the sun in Lighting: blue by day, orange and red when the sun is low, dark with stars at night. The sun itself turns orange near the horizon and sets, and the sky’s light replaces Lighting::sky_color and ground_color. One world unit is taken to be a meter.

// Late evening, a little hazy, with fog lying in the valleys.
ctx.lighting.sun_direction = Sky::sun_direction_at(18.5);
ctx.sky.haze = 3.0;
ctx.sky.fog_density = 0.01;
ctx.sky.fog_height = 5.0;

Fields§

§atmosphere: bool

Draw a physical sky lit by the sun, and light the scene from it. Off: the background is the clear color and the light comes from Lighting’s colors. Default on.

§haze: f32

Dust and water in the air, from 0 (none) up: more makes the sky paler, the glow around the sun bigger and sunsets redder. Default 1.

§sun_size: f32

The sun’s size in the sky, in degrees across. Default 1.5 (the real sun is about 0.5, which looks small on a screen).

§stars: f32

How bright the stars are at night, 0 for none. Default 1.

§night_light: Color

Light at night, when the sun is down: moonlight and starlight. Default a dim blue.

§fog_density: f32

How thick the fog is near the ground: the share of light it takes away per world unit. 0 (the default) is no fog; 0.002 hazes the far distance; 0.05 is thick.

§fog_height: f32

The height the fog lies at: below it, it’s thickest. Default 0.

§fog_falloff: f32

How quickly the fog thins out above fog_height, in world units: it halves about every 0.7 times this. Default 50.

§fog_color: Color

The fog’s color without an atmosphere; with one, the fog takes the sky’s color behind it. Default a pale grey-blue.

§fog_start: f32

Distance fog: none closer than fog_start world units, all fog past fog_end, and a straight blend between. It hides the far end of a big world whatever the height. Off while fog_end is not past fog_start (default 0 and 0). The sky itself takes none.

§fog_end: f32

Where the distance fog hides everything. Default 0 (off).

§fog_sun_glow: f32

How much sunlight the fog scatters toward the camera, so it glows when looking toward the sun: 0 (the default) none, 0.1 a soft glow.

§fog_sun_focus: f32

How tight that glow is around the sun, 0 (all around) to just under 1 (a small bright halo). Default 0.7.

§light_shafts: f32

Light shafts: how much shadows cut the fog’s sun glow, from 0 (the default, off) to 1. Where the fog is in a tree’s or a house’s shadow it stops glowing, so beams of sunlight show between them. It needs fog, a fog_sun_glow and the sun’s shadows, and reaches as far as they do. It costs 16 shadow map taps per pixel, and it is grainy without AntiAliasing::Taa in ctx.post, which smooths it out.

Implementations§

Source§

impl Sky

Source

pub fn sun_direction_at(hour: f32) -> Vec3

Which way sunlight travels at hour o’clock (0 to 24), for a day and night cycle: the sun rises in the east (+X) at 6, is highest at noon (60° up, to the south, -Z) and sets in the west at 18.

assert!(Sky::sun_direction_at(12.0).y < -0.8, "noon: from high up");
assert!(Sky::sun_direction_at(0.0).y > 0.0, "midnight: from below the ground");
Examples found in repository?
examples/scene_viewer.rs (line 114)
39fn main() {
40    let scene = std::env::var("SCENE").unwrap_or_else(|_| {
41        eprintln!("say which scene: SCENE=town/Town.json (a file under assets/)");
42        std::process::exit(2);
43    });
44    if std::env::var("HEADLESS").is_ok() {
45        Game::new("scene viewer", 1, 1).run_headless(0, |ctx| {
46            let start = Instant::now();
47            ctx.load_scene(&scene).unwrap_or_else(|e| panic!("{e}"));
48            let models = ctx.world.query::<&Model>().iter().count();
49            let (middle, size) = extent(&mut ctx.world);
50            println!(
51                "{scene}: {models} models in {:.2} s, around {middle} across {size:.0} m",
52                start.elapsed().as_secs_f32()
53            );
54        });
55        return;
56    }
57    let shot = std::env::var("SHOT").ok();
58    let (mut yaw, mut pitch) = (0.0f32, -0.3f32);
59    Game::new("Crabby Engine: scene viewer", 1280, 720)
60        .system(move |ctx: &mut Ctx| {
61            if ctx.key_pressed(Key::Escape) {
62                ctx.quit();
63            }
64            let dt = ctx.dt();
65            if ctx.frame() == 1 {
66                let (y, x, _) = ctx.camera3d.rotation.to_euler(glam::EulerRot::YXZ);
67                (yaw, pitch) = (y, x);
68            }
69            let turn = 1.2 * dt;
70            if ctx.key_down(Key::Left) {
71                yaw += turn;
72            }
73            if ctx.key_down(Key::Right) {
74                yaw -= turn;
75            }
76            if ctx.key_down(Key::Up) {
77                pitch = (pitch + turn).min(1.5);
78            }
79            if ctx.key_down(Key::Down) {
80                pitch = (pitch - turn).max(-1.5);
81            }
82            let rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
83            let speed = if ctx.key_down(Key::LeftShift) { 120.0 } else { 20.0 } * dt;
84            let mut go = Vec3::ZERO;
85            for (key, way) in [
86                (Key::W, Vec3::NEG_Z),
87                (Key::S, Vec3::Z),
88                (Key::A, Vec3::NEG_X),
89                (Key::D, Vec3::X),
90            ] {
91                if ctx.key_down(key) {
92                    go += rotation * way;
93                }
94            }
95            if ctx.key_down(Key::E) {
96                go += Vec3::Y;
97            }
98            if ctx.key_down(Key::Q) {
99                go -= Vec3::Y;
100            }
101            ctx.camera3d.position += go * speed;
102            ctx.camera3d.rotation = rotation;
103            if ctx.frame() == 300
104                && let Some(path) = &shot
105            {
106                ctx.save_screenshot(path);
107            }
108            if ctx.frame() == 302 && shot.is_some() {
109                ctx.quit();
110            }
111        })
112        .run(move |ctx| {
113            ctx.load_scene(&scene).unwrap_or_else(|e| panic!("{e}"));
114            ctx.lighting.sun_direction = Sky::sun_direction_at(15.0);
115            ctx.lighting.shadow_distance = 400.0;
116            ctx.camera3d.far = 3000.0;
117            ctx.post.bloom = 0.05;
118            let (middle, size) = extent(&mut ctx.world);
119            let (at, yaw, pitch) =
120                camera_from_env().unwrap_or((middle + vec3(0.0, size * 0.25, size * 0.4), 0.0, -0.5));
121            ctx.camera3d.position = at;
122            ctx.camera3d.rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
123        });
124}
More examples
Hide additional examples
examples/compute.rs (line 93)
44fn main() {
45    let mut seed = 11u32;
46    let mut random = move || {
47        seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
48        (seed >> 8) as f32 / (1 << 24) as f32
49    };
50    let mut next_drop = 0.0;
51    Game::new("Crabby Engine: compute", 1280, 720)
52        .system(move |ctx: &mut Ctx| {
53            if ctx.key_pressed(Key::Escape) {
54                ctx.quit();
55            }
56            let Some(pass) = ctx.compute_passes.first().copied() else {
57                return;
58            };
59            let Some(drops) = pass.material else { return };
60            // Rain every so often, or a stone where the mouse clicks.
61            let mut drop = Vec4::ZERO;
62            next_drop -= ctx.dt();
63            if next_drop <= 0.0 {
64                next_drop = 0.05 + random() * 0.2;
65                drop = vec4(random(), random(), 0.3 + random() * 0.4, 0.0);
66            }
67            if ctx.mouse_pressed(MouseButton::Left) {
68                let (from, dir) = ctx.camera3d.screen_ray(ctx.mouse_pos());
69                if dir.y < 0.0 {
70                    let at = from + dir * (-from.y / dir.y);
71                    drop = vec4(at.x / SIZE + 0.5, at.z / SIZE + 0.5, 3.0, 0.0);
72                }
73            }
74            ctx.update_material(
75                drops,
76                MaterialDesc {
77                    custom: [drop, Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
78                    ..Default::default()
79                },
80            );
81            if ctx.frame() == 120
82                && let Ok(path) = std::env::var("SHOT")
83            {
84                ctx.save_screenshot(path);
85            }
86            if ctx.frame() == 122 && std::env::var("SHOT").is_ok() {
87                ctx.quit();
88            }
89        })
90        .run(|ctx| {
91            ctx.camera3d.position = vec3(0.0, 7.0, 10.0);
92            ctx.camera3d.look_at(vec3(0.0, 0.0, -1.0));
93            ctx.lighting.sun_direction = Sky::sun_direction_at(15.0);
94            ctx.post.bloom = 0.15;
95
96            let heights = ctx.create_render_target(512, 512);
97            let ripples = ctx.create_compute("ripples", RIPPLES);
98            let drops = ctx.create_material(MaterialDesc::default());
99            ctx.compute_passes
100                .push(ComputePass::new(ripples, heights).with_material(drops));
101
102            let shader = ctx.create_shader("water", WATER);
103            let water = ctx.create_material(MaterialDesc {
104                shader: Some(shader),
105                custom_textures: [Some(heights), None],
106                ..MaterialDesc::color(Color::hex(0x2A5A75))
107            });
108            let surface = ctx.create_mesh(&MeshData::plane(SIZE), water);
109            ctx.world.spawn((Transform3D::default(), Model::new(surface)));
110
111            let stone = ctx.create_material(MaterialDesc {
112                roughness: 0.8,
113                ..MaterialDesc::color(Color::hex(0x8A8578))
114            });
115            let rock = ctx.create_mesh(&MeshData::sphere(0.6, 16), stone);
116            for i in 0..40 {
117                let a = i as f32 / 40.0 * std::f32::consts::TAU;
118                let edge = vec3(a.cos(), 0.0, a.sin()) * SIZE * 0.55;
119                let t = Transform3D {
120                    scale: Vec3::splat(0.8 + (i % 3) as f32 * 0.3),
121                    ..Transform3D::at(edge)
122                };
123                ctx.world.spawn((t, Model::new(rock)));
124            }
125            let grass = ctx.create_material(MaterialDesc {
126                roughness: 0.95,
127                ..MaterialDesc::color(Color::hex(0x4E6B33))
128            });
129            let ground = ctx.create_mesh(&MeshData::plane(200.0), grass);
130            ctx.world
131                .spawn((Transform3D::at(vec3(0.0, -0.3, 0.0)), Model::new(ground)));
132        });
133}
examples/streaming.rs (line 187)
82fn main() {
83    let bench = std::env::var("BENCH").is_ok();
84    let shot = std::env::var("SHOT").ok();
85    let mut cells = CellStreamer::new(TILE, LOAD, KEEP);
86    let mut tiles: HashMap<(i32, i32), Tile> = HashMap::new();
87    let mut ground: Option<Material> = None;
88    let (mut yaw, mut speed, mut altitude) = (0.4f32, if bench { 600.0f32 } else { 120.0 }, 90.0f32);
89    // The streaming's cost on the game's thread, for BENCH.
90    let (mut uploaded, mut freed, mut upload_time, mut worst_upload) = (0usize, 0usize, 0.0f64, 0.0f64);
91    Game::new("Crabby Engine: streaming", 1280, 720)
92        .vsync(!bench)
93        .system(move |ctx: &mut Ctx| {
94            if ctx.key_pressed(Key::Escape) {
95                ctx.quit();
96            }
97            let dt = ctx.dt();
98            if ctx.key_down(Key::Left) {
99                yaw += dt * 0.8;
100            }
101            if ctx.key_down(Key::Right) {
102                yaw -= dt * 0.8;
103            }
104            if ctx.key_down(Key::W) {
105                speed = (speed + 80.0 * dt).min(600.0);
106            }
107            if ctx.key_down(Key::S) {
108                speed = (speed - 80.0 * dt).max(0.0);
109            }
110            if ctx.key_down(Key::E) {
111                altitude += 40.0 * dt;
112            }
113            if ctx.key_down(Key::Q) {
114                altitude = (altitude - 40.0 * dt).max(5.0);
115            }
116            // Fly on, keeping above the hills ahead.
117            let rotation = Quat::from_rotation_y(yaw);
118            let mut at = ctx.camera3d.position + rotation * Vec3::NEG_Z * speed * dt;
119            let ahead = at + rotation * Vec3::NEG_Z * 120.0;
120            let floor = height(at.x, at.z).max(height(ahead.x, ahead.z));
121            at.y += (floor + altitude - at.y) * (dt * 1.5).min(1.0);
122            ctx.camera3d.position = at;
123            ctx.camera3d.rotation = rotation * Quat::from_rotation_x(-0.22);
124
125            // Which tiles to start and drop.
126            cells.update(at, STARTS);
127            for &cell in cells.to_load() {
128                let corner = cells.corner(cell);
129                tiles.insert(cell, Tile::Building(Job::spawn(move || tile_data(corner))));
130            }
131            for cell in cells.to_drop() {
132                // A job still building is dropped with its result.
133                if let Some(Tile::Live(entity, tile)) = tiles.remove(cell) {
134                    ctx.world.despawn(entity);
135                    ctx.free_terrain_tile(tile);
136                    freed += 1;
137                }
138            }
139            // Finished tiles go on the GPU, a few a frame.
140            let ground = *ground.get_or_insert_with(|| {
141                let shader = ctx.create_shader("ground", GROUND);
142                ctx.create_material(MaterialDesc {
143                    shader: Some(shader),
144                    ..MaterialDesc::color(Color::WHITE)
145                })
146            });
147            let mut uploads = 0;
148            for (&cell, tile) in tiles.iter_mut() {
149                if uploads == UPLOADS {
150                    break;
151                }
152                if let Tile::Building(job) = tile
153                    && let Some(data) = job.take()
154                {
155                    let start = Instant::now();
156                    let made = ctx.upload_terrain_tile(&data, ground);
157                    let entity = ctx.world.spawn((Transform3D::at(cells.corner(cell)), made));
158                    let took = start.elapsed().as_secs_f64() * 1000.0;
159                    upload_time += took;
160                    worst_upload = worst_upload.max(took);
161                    uploaded += 1;
162                    uploads += 1;
163                    *tile = Tile::Live(entity, made);
164                }
165            }
166
167            if ctx.frame() == 400
168                && let Some(path) = &shot
169            {
170                ctx.save_screenshot(path);
171            }
172            if ctx.frame() == 402 && shot.is_some() {
173                ctx.quit();
174            }
175            if bench && ctx.frame() == 3000 {
176                let live = tiles.values().filter(|t| matches!(t, Tile::Live(..))).count();
177                println!(
178                    "{uploaded} tiles uploaded ({:.2} ms each, worst {worst_upload:.2} ms), {freed} freed, {live} live; {:?}",
179                    upload_time / uploaded.max(1) as f64,
180                    ctx.render_stats()
181                );
182                ctx.quit();
183            }
184        })
185        .run(move |ctx| {
186            ctx.post.bloom = 0.08;
187            ctx.lighting.sun_direction = Sky::sun_direction_at(15.5);
188            ctx.lighting.shadow_distance = 500.0;
189            ctx.camera3d.far = LOAD + TILE;
190            // Fog thick enough that tiles appear behind it, not at an edge.
191            ctx.sky.fog_density = 0.0007;
192            ctx.sky.fog_height = 40.0;
193            ctx.sky.fog_falloff = 90.0;
194            ctx.camera3d.position = vec3(0.0, height(0.0, 0.0) + 60.0, 0.0);
195        });
196}
examples/shaders.rs (line 59)
40fn main() {
41    let env = |name| std::env::var(name).ok().and_then(|v| v.parse::<f32>().ok());
42    let mut hour = env("HOUR").unwrap_or(15.5);
43    let mut fog = env("FOG").unwrap_or(0.0) > 0.0;
44    Game::new("Crabby Engine: shaders", 1280, 720)
45        .system(move |ctx: &mut Ctx| {
46            if ctx.key_pressed(Key::Escape) {
47                ctx.quit();
48            }
49            // Two hours a second.
50            if ctx.key_down(Key::Right) {
51                hour = (hour + ctx.dt() * 2.0) % 24.0;
52            }
53            if ctx.key_down(Key::Left) {
54                hour = (hour - ctx.dt() * 2.0).rem_euclid(24.0);
55            }
56            if ctx.key_pressed(Key::F) {
57                fog = !fog;
58            }
59            ctx.lighting.sun_direction = Sky::sun_direction_at(hour);
60            ctx.sky.fog_density = if fog { 0.02 } else { 0.0 };
61            ctx.sky.fog_falloff = 8.0;
62            if ctx.frame() == 2
63                && let Ok(path) = std::env::var("SHOT")
64            {
65                ctx.save_screenshot(path);
66            }
67            if ctx.frame() == 4 && std::env::var("SHOT").is_ok() {
68                ctx.quit();
69            }
70        })
71        .run(|ctx| {
72            ctx.clear(Color::hex(0x9CC4E4));
73            ctx.camera3d.position = vec3(0.0, 4.0, 11.0);
74            ctx.camera3d.look_at(vec3(0.0, 0.5, 0.0));
75            ctx.post.bloom = 0.1;
76
77            let ground_look = MaterialDesc {
78                roughness: 0.95,
79                ..MaterialDesc::color(Color::hex(0x5E7A3A))
80            };
81            let ground = ctx.create_material(ground_look);
82            let ground = ctx.create_mesh(&MeshData::plane(600.0), ground);
83            ctx.world.spawn((Transform3D::default(), Model::new(ground)));
84
85            let water = ctx.load_shader("shaders/water.wgsl");
86            let lake = ctx.create_material(MaterialDesc {
87                shader: Some(water),
88                blend: Blend::Transparent,
89                custom: [vec4(0.06, 1.5, 1.2, 1.2), Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
90                ..MaterialDesc::color(Color::hex(0x1E4D6B))
91            });
92            let mut surface = MeshData::plane(12.0);
93            // More vertices, so the waves have something to move.
94            surface = subdivide(surface, 5);
95            let lake = ctx.create_mesh(&surface, lake);
96            ctx.world
97                .spawn((Transform3D::at(vec3(-3.0, 0.15, -4.0)), Model::new(lake)));
98
99            // The lake bed, under the water: sand sloping down, and stones.
100            let sand = ctx.create_material(MaterialDesc {
101                roughness: 0.9,
102                ..MaterialDesc::color(Color::hex(0xC2A878))
103            });
104            let bed = ctx.create_mesh(&MeshData::plane(12.0), sand);
105            let tilt = Quat::from_rotation_x(0.12);
106            ctx.world.spawn((
107                Transform3D {
108                    rotation: tilt,
109                    ..Transform3D::at(vec3(-3.0, -0.6, -4.0))
110                },
111                Model::new(bed),
112            ));
113            let stone = ctx.create_material(MaterialDesc {
114                roughness: 0.7,
115                ..MaterialDesc::color(Color::hex(0x8A8A80))
116            });
117            let pebble = ctx.create_mesh(&MeshData::sphere(0.35, 16), stone);
118            for i in 0..12 {
119                let at = vec3(
120                    -7.0 + i as f32 * 0.8,
121                    -0.3 - (i % 3) as f32 * 0.2,
122                    -3.0 + (i % 4) as f32 * -1.5,
123                );
124                ctx.world.spawn((Transform3D::at(at), Model::new(pebble)));
125            }
126
127            let wind = ctx.load_shader("shaders/wind.wgsl");
128            let grass = ctx.create_material(MaterialDesc {
129                shader: Some(wind),
130                double_sided: true,
131                roughness: 0.8,
132                custom: [vec4(0.25, 2.0, 0.0, 0.0), Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
133                ..MaterialDesc::color(Color::hex(0x7FA83C))
134            });
135            let blade = ctx.create_mesh(&blade(), grass);
136            let mut seed = 7u32;
137            let mut random = move || {
138                seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
139                (seed >> 8) as f32 / (1 << 24) as f32
140            };
141            for _ in 0..6000 {
142                let at = vec3(random() * 14.0 - 2.0, 0.0, random() * 10.0 - 3.0);
143                if at.x < 4.0 && at.z < 2.5 {
144                    continue; // the lake
145                }
146                let t = Transform3D {
147                    rotation: Quat::from_rotation_y(random() * 6.3),
148                    scale: Vec3::splat(0.6 + random() * 0.8),
149                    ..Transform3D::at(at)
150                };
151                ctx.world.spawn((t, Model::new(blade)));
152            }
153
154            let tiles = ctx.create_shader("tiles", TILES);
155            let ball = ctx.create_material(MaterialDesc {
156                shader: Some(tiles),
157                ..Default::default()
158            });
159            let ball = ctx.create_mesh(&MeshData::sphere(1.0, 48), ball);
160            ctx.world
161                .spawn((Transform3D::at(vec3(3.5, 1.0, 1.0)), Model::new(ball)));
162        });
163}
examples/terrain.rs (line 257)
148fn main() {
149    let view = std::env::var("VIEW").unwrap_or_default();
150    let hour = std::env::var("HOUR").ok().and_then(|h| h.parse().ok()).unwrap_or(16.0);
151    // The first spot nearest the middle where the ground fits.
152    let spot = |fits: &dyn Fn(f32) -> bool| {
153        (0..200 * 200)
154            .map(|i| vec2((i % 200) as f32 * 4.0 - 400.0, (i / 200) as f32 * 4.0 - 400.0))
155            .filter(|p| fits(height(p.x, p.y)))
156            .min_by(|a, b| a.length().total_cmp(&b.length()))
157            .unwrap_or(Vec2::ZERO)
158    };
159    let (start, (mut yaw, mut pitch)) = match view.as_str() {
160        "low" => {
161            let p = spot(&|h| h > WATER + 4.0);
162            (vec3(p.x, height(p.x, p.y) + 1.8, p.y), (0.6f32, -0.08f32))
163        }
164        "under" => {
165            let p = spot(&|h| h < WATER - 2.0 && h > WATER - 5.0);
166            (vec3(p.x, WATER - 1.0, p.y), (0.6, -0.6))
167        }
168        "cliff" => {
169            // The steepest ground near the middle, seen from 30 m off its foot.
170            let slope = |p: Vec2| {
171                (height(p.x + 2.0, p.y) - height(p.x - 2.0, p.y)).hypot(height(p.x, p.y + 2.0) - height(p.x, p.y - 2.0))
172            };
173            let steep = (0..200 * 200)
174                .map(|i| vec2((i % 200) as f32 * 4.0 - 400.0, (i / 200) as f32 * 4.0 - 400.0))
175                .filter(|p| height(p.x, p.y) > WATER + 2.0)
176                .max_by(|a, b| slope(*a).total_cmp(&slope(*b)))
177                .unwrap_or(Vec2::ZERO);
178            // Downhill, away from the face.
179            let down = -vec2(
180                height(steep.x + 2.0, steep.y) - height(steep.x - 2.0, steep.y),
181                height(steep.x, steep.y + 2.0) - height(steep.x, steep.y - 2.0),
182            )
183            .normalize_or_zero();
184            let eye = steep + down * 30.0;
185            let at = vec3(eye.x, height(eye.x, eye.y).max(WATER) + 6.0, eye.y);
186            let to = vec3(steep.x, height(steep.x, steep.y), steep.y) - at;
187            (at, (f32::atan2(-to.x, -to.z), (to.y / to.length()).asin()))
188        }
189        _ => (vec3(-300.0, 90.0, 300.0), (-0.8, -0.25)),
190    };
191    let mut parked: Vec<ScreenEffect> = Vec::new();
192    let bench = std::env::var("BENCH").is_ok();
193    Game::new("Crabby Engine: terrain", 1280, 720)
194        .vsync(!bench)
195        .fullscreen(bench)
196        .system(move |ctx: &mut Ctx| {
197            if ctx.key_pressed(Key::Escape) {
198                ctx.quit();
199            }
200            let dt = ctx.dt();
201            if ctx.key_down(Key::Left) {
202                yaw += dt;
203            }
204            if ctx.key_down(Key::Right) {
205                yaw -= dt;
206            }
207            if ctx.key_down(Key::Up) {
208                pitch = (pitch + dt).min(1.4);
209            }
210            if ctx.key_down(Key::Down) {
211                pitch = (pitch - dt).max(-1.4);
212            }
213            let rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
214            let forward = rotation * Vec3::NEG_Z;
215            let right = rotation * Vec3::X;
216            let speed = 60.0 * dt;
217            let mut go = Vec3::ZERO;
218            for (key, dir) in [
219                (Key::W, forward),
220                (Key::S, -forward),
221                (Key::D, right),
222                (Key::A, -right),
223                (Key::E, Vec3::Y),
224                (Key::Q, Vec3::NEG_Y),
225            ] {
226                if ctx.key_down(key) {
227                    go += dir;
228                }
229            }
230            ctx.camera3d.position += go * speed;
231            ctx.camera3d.rotation = rotation;
232            // The lake's mirror follows the camera.
233            if let Some(mirror) = ctx.target_cameras.first().map(|c| c.target) {
234                ctx.target_cameras[0] = TargetCamera::reflection(ctx.camera3d, mirror, WATER);
235            }
236            if ctx.key_pressed(Key::U) {
237                std::mem::swap(&mut ctx.effects, &mut parked);
238            }
239            if ctx.frame() == 3
240                && let Ok(path) = std::env::var("SHOT")
241            {
242                ctx.save_screenshot(path);
243            }
244            if ctx.frame() == 5 && std::env::var("SHOT").is_ok() {
245                ctx.quit();
246            }
247            if ctx.frame() == 300 {
248                let stats = ctx.render_stats();
249                println!("{stats:?} models drawn: {}", ctx.models_drawn());
250                if bench {
251                    ctx.quit();
252                }
253            }
254        })
255        .run(move |ctx| {
256            ctx.post.bloom = 0.1;
257            ctx.lighting.sun_direction = Sky::sun_direction_at(hour);
258            ctx.lighting.shadow_distance = 400.0;
259            ctx.camera3d.far = 3000.0;
260            ctx.sky.fog_density = 0.0015;
261            ctx.sky.fog_height = 0.0;
262            ctx.sky.fog_falloff = 60.0;
263
264            let ground = ground(ctx);
265            let half = TILES / 2;
266            for tz in -half..=half {
267                for tx in -half..=half {
268                    let corner = vec3(tx as f32 * TILE - TILE / 2.0, 0.0, tz as f32 * TILE - TILE / 2.0);
269                    let heights: Vec<f32> = (0..N * N)
270                        .map(|i| height(corner.x + (i % N) as f32 * 2.0, corner.z + (i / N) as f32 * 2.0))
271                        .collect();
272                    let tile = ctx.create_terrain_tile(&heights, TILE, ground);
273                    ctx.world.spawn((Transform3D::at(corner), tile));
274                }
275            }
276
277            // Half the window's size is plenty for a rippled mirror.
278            let size = ctx.window_size();
279            let mirror = ctx.create_render_target((size.x as u32 / 2).max(1), (size.y as u32 / 2).max(1));
280            ctx.target_cameras
281                .push(TargetCamera::reflection(ctx.camera3d, mirror, WATER));
282            let water = ctx.create_shader("lake", LAKE);
283            let lake = ctx.create_material(MaterialDesc {
284                shader: Some(water),
285                blend: Blend::Transparent,
286                custom_textures: [Some(mirror), None],
287                custom: [vec4(0.06, 1.5, 1.2, 1.2), Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
288                ..MaterialDesc::color(Color::hex(0x1E4D6B))
289            });
290            let surface = ctx.create_mesh(&MeshData::plane(TILES as f32 * TILE), lake);
291            ctx.world
292                .spawn((Transform3D::at(vec3(0.0, WATER, 0.0)), Model::new(surface)));
293
294            let underwater = ctx.create_effect("underwater", UNDERWATER);
295            let level = ctx.create_material(MaterialDesc {
296                custom: [vec4(WATER, 0.0, 0.0, 0.0), Vec4::ZERO, Vec4::ZERO, Vec4::ZERO],
297                ..Default::default()
298            });
299            ctx.effects.push(ScreenEffect::new(underwater).with_material(level));
300
301            ctx.camera3d.position = start;
302        });
303}

Trait Implementations§

Source§

impl Clone for Sky

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Sky

Source§

impl Debug for Sky

Source§

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

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

impl Default for Sky

Source§

fn default() -> Self

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

impl PartialEq for Sky

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Sky

Auto Trait Implementations§

§

impl Forget for Sky

§

impl Freeze for Sky

§

impl RefUnwindSafe for Sky

§

impl Send for Sky

§

impl Sync for Sky

§

impl Unpin for Sky

§

impl UnsafeUnpin for Sky

§

impl UnwindSafe for Sky

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Component for T
where T: Send + Sync + 'static,

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> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &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)

Converts &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> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

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

Source§

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

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<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

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

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be 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<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

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

Source§

fn into_sample(self) -> T

Source§

impl<T> MaybeSync for T
where T: Sync + ?Sized,

Source§

impl<T> MaybeSync for T
where T: Sync + ?Sized,

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<R, P> ReadPrimitive<R> for P
where R: Read + ReadEndian<P>, P: Default,

Source§

fn read_from_little_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_little_endian().
Source§

fn read_from_big_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_big_endian().
Source§

fn read_from_native_endian(read: &mut R) -> Result<Self, Error>

Read this value from the supplied reader. Same as ReadEndian::read_from_native_endian().
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

Source§

impl<T> SerializableAny for T
where T: 'static + Any + Clone + for<'a> Send + Sync,

Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(_simd: S, value: T) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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 = !

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

fn try_from(value: U) -> Result<T, !>

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<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,

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