1use crate::core::*;
2use crate::renderer::*;
3use std::f32::consts::PI;
4
5const NO_VIEW_ANGLES: u32 = 8;
6
7pub struct Imposters {
14 context: Context,
15 sprites: Sprites,
16 material: ImpostersMaterial,
17}
18
19impl Imposters {
20 pub fn new(
25 context: &Context,
26 positions: &[Vec3],
27 objects: impl IntoIterator<Item = impl Object> + Clone,
28 lights: &[&dyn Light],
29 max_texture_size: u32,
30 ) -> Self {
31 let mut aabb = AxisAlignedBoundingBox::EMPTY;
32 objects
33 .clone()
34 .into_iter()
35 .for_each(|o| aabb.expand_with_aabb(o.aabb()));
36 let mut sprites = Sprites::new(context, positions, Some(vec3(0.0, 1.0, 0.0)));
37 sprites.set_transformation(get_sprite_transform(aabb));
38 Imposters {
39 context: context.clone(),
40 sprites,
41 material: ImpostersMaterial::new(context, aabb, objects, lights, max_texture_size),
42 }
43 }
44
45 pub fn set_positions(&mut self, positions: &[Vec3]) {
49 self.sprites.set_centers(positions);
50 }
51
52 pub fn update_texture(
57 &mut self,
58 objects: impl IntoIterator<Item = impl Object> + Clone,
59 lights: &[&dyn Light],
60 max_texture_size: u32,
61 ) {
62 let mut aabb = AxisAlignedBoundingBox::EMPTY;
63 objects
64 .clone()
65 .into_iter()
66 .for_each(|o| aabb.expand_with_aabb(o.aabb()));
67 self.sprites.set_transformation(get_sprite_transform(aabb));
68 self.material
69 .update(aabb, objects, lights, max_texture_size);
70 }
71}
72
73fn get_sprite_transform(aabb: AxisAlignedBoundingBox) -> Mat4 {
74 if aabb.is_empty() {
75 Mat4::identity()
76 } else {
77 let (min, max) = (aabb.min(), aabb.max());
78 let width = f32::sqrt(f32::powi(max.x - min.x, 2) + f32::powi(max.z - min.z, 2));
79 let height = max.y - min.y;
80 let center = 0.5 * min + 0.5 * max;
81 Mat4::from_translation(center) * Mat4::from_nonuniform_scale(0.5 * width, 0.5 * height, 0.0)
82 }
83}
84
85impl<'a> IntoIterator for &'a Imposters {
86 type Item = &'a dyn Object;
87 type IntoIter = std::iter::Once<&'a dyn Object>;
88
89 fn into_iter(self) -> Self::IntoIter {
90 std::iter::once(self)
91 }
92}
93
94use std::ops::Deref;
95impl Deref for Imposters {
96 type Target = Sprites;
97 fn deref(&self) -> &Self::Target {
98 &self.sprites
99 }
100}
101
102impl std::ops::DerefMut for Imposters {
103 fn deref_mut(&mut self) -> &mut Self::Target {
104 &mut self.sprites
105 }
106}
107
108impl Geometry for Imposters {
109 impl_geometry_body!(deref);
110}
111
112impl Object for Imposters {
113 fn render(&self, viewer: &dyn Viewer, lights: &[&dyn Light]) {
114 render_with_material(&self.context, viewer, &self, &self.material, lights)
115 }
116
117 fn material_type(&self) -> MaterialType {
118 self.material.material_type()
119 }
120}
121
122struct ImpostersMaterial {
123 context: Context,
124 texture: Texture2DArray,
125}
126
127impl ImpostersMaterial {
128 pub fn new(
129 context: &Context,
130 aabb: AxisAlignedBoundingBox,
131 objects: impl IntoIterator<Item = impl Object> + Clone,
132 lights: &[&dyn Light],
133 max_texture_size: u32,
134 ) -> Self {
135 let mut m = Self {
136 context: context.clone(),
137 texture: Texture2DArray::new_empty::<[u8; 4]>(
138 context,
139 1,
140 1,
141 NO_VIEW_ANGLES,
142 Interpolation::Nearest,
143 Interpolation::Nearest,
144 None,
145 Wrapping::ClampToEdge,
146 Wrapping::ClampToEdge,
147 ),
148 };
149 m.update(aabb, objects, lights, max_texture_size);
150 m
151 }
152 pub fn update(
153 &mut self,
154 aabb: AxisAlignedBoundingBox,
155 objects: impl IntoIterator<Item = impl Object> + Clone,
156 lights: &[&dyn Light],
157 max_texture_size: u32,
158 ) {
159 if !aabb.is_empty() {
160 let (min, max) = (aabb.min(), aabb.max());
161 let width = f32::sqrt(f32::powi(max.x - min.x, 2) + f32::powi(max.z - min.z, 2));
162 let height = max.y - min.y;
163 let texture_width = (max_texture_size as f32 * (width / height).min(1.0)) as u32;
164 let texture_height = (max_texture_size as f32 * (height / width).min(1.0)) as u32;
165 let viewport = Viewport::new_at_origo(texture_width, texture_height);
166 let center = 0.5 * min + 0.5 * max;
167 let mut camera = Camera::new_orthographic(
168 viewport,
169 center + vec3(0.0, 0.0, -1.0),
170 center,
171 vec3(0.0, 1.0, 0.0),
172 height,
173 -2.0 * (width + height),
174 2.0 * (width + height),
175 );
176 camera.disable_tone_and_color_mapping();
177 self.texture = Texture2DArray::new_empty::<[f16; 4]>(
178 &self.context,
179 texture_width,
180 texture_height,
181 NO_VIEW_ANGLES,
182 Interpolation::Linear,
183 Interpolation::Linear,
184 None,
185 Wrapping::ClampToEdge,
186 Wrapping::ClampToEdge,
187 );
188 let mut depth_texture = DepthTexture2D::new::<f32>(
189 &self.context,
190 texture_width,
191 texture_height,
192 Wrapping::ClampToEdge,
193 Wrapping::ClampToEdge,
194 );
195 for i in 0..NO_VIEW_ANGLES {
196 let layers = [i];
197 let angle = i as f32 * 2.0 * PI / NO_VIEW_ANGLES as f32;
198 camera.set_view(
199 center + vec3(f32::cos(angle), 0.0, f32::sin(angle)),
200 center,
201 vec3(0.0, 1.0, 0.0),
202 );
203 RenderTarget::new(
204 self.texture.as_color_target(&layers, None),
205 depth_texture.as_depth_target(),
206 )
207 .clear(ClearState::color_and_depth(0.0, 0.0, 0.0, 0.0, 1.0))
208 .render(&camera, objects.clone(), lights);
209 }
210 }
211 }
212}
213
214impl Material for ImpostersMaterial {
215 fn id(&self) -> EffectMaterialId {
216 EffectMaterialId::ImpostersMaterial
217 }
218
219 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
220 format!(
221 "{}{}{}{}",
222 ToneMapping::fragment_shader_source(),
223 ColorMapping::fragment_shader_source(),
224 include_str!("../../core/shared.frag"),
225 include_str!("shaders/imposter.frag")
226 )
227 }
228
229 fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, _lights: &[&dyn Light]) {
230 viewer.tone_mapping().use_uniforms(program);
231 viewer.color_mapping().use_uniforms(program);
232 program.use_uniform("no_views", NO_VIEW_ANGLES as i32);
233 program.use_uniform("view", viewer.view());
234 program.use_texture_array("tex", &self.texture);
235 }
236
237 fn render_states(&self) -> RenderStates {
238 RenderStates {
239 blend: Blend::TRANSPARENCY,
240 cull: Cull::Back,
241 ..Default::default()
242 }
243 }
244 fn material_type(&self) -> MaterialType {
245 MaterialType::Transparent
246 }
247}