1use ponko::*;
12
13fn blade() -> MeshData {
15 MeshData {
16 positions: vec![
17 vec3(-0.03, 0.0, 0.0),
18 vec3(0.03, 0.0, 0.0),
19 vec3(0.01, 0.6, 0.0),
20 vec3(-0.01, 0.6, 0.0),
21 ],
22 normals: vec![Vec3::Z; 4],
23 uvs: vec![vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)],
24 indices: vec![0, 1, 2, 0, 2, 3],
25 ..Default::default()
26 }
27}
28
29const TILES: &str = "
31fn surface(s: Surface) -> Surface {
32 var out = s;
33 let cell = floor(s.uv * vec2<f32>(16.0, 8.0));
34 let checker = (cell.x + cell.y) % 2.0;
35 out.albedo = mix(vec3<f32>(0.8, 0.3, 0.1), vec3<f32>(0.95, 0.85, 0.6), checker);
36 out.roughness = mix(0.25, 0.7, checker);
37 return out;
38}";
39
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 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 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 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; }
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}
164
165fn subdivide(mut mesh: MeshData, times: u32) -> MeshData {
167 for _ in 0..times {
168 let mut indices = Vec::with_capacity(mesh.indices.len() * 4);
169 for tri in mesh.indices.as_chunks::<3>().0 {
170 let mut mid = |a: u32, b: u32| {
171 let (a, b) = (a as usize, b as usize);
172 mesh.positions.push((mesh.positions[a] + mesh.positions[b]) / 2.0);
173 mesh.normals.push(mesh.normals[a]);
174 mesh.uvs.push((mesh.uvs[a] + mesh.uvs[b]) / 2.0);
175 mesh.positions.len() as u32 - 1
176 };
177 let (a, b, c) = (tri[0], tri[1], tri[2]);
178 let (ab, bc, ca) = (mid(a, b), mid(b, c), mid(c, a));
179 indices.extend_from_slice(&[a, ab, ca, ab, b, bc, ca, bc, c, ab, bc, ca]);
180 }
181 mesh.indices = indices;
182 }
183 mesh
184}