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 if let Err(e) = render_with_material(&self.context, viewer, &self, &self.material, lights) {
115 panic!("{}", e.to_string());
116 }
117 }
118
119 fn material_type(&self) -> MaterialType {
120 self.material.material_type()
121 }
122}
123
124struct ImpostersMaterial {
125 context: Context,
126 texture: Texture2DArray,
127}
128
129impl ImpostersMaterial {
130 pub fn new(
131 context: &Context,
132 aabb: AxisAlignedBoundingBox,
133 objects: impl IntoIterator<Item = impl Object> + Clone,
134 lights: &[&dyn Light],
135 max_texture_size: u32,
136 ) -> Self {
137 let mut m = Self {
138 context: context.clone(),
139 texture: Texture2DArray::new_empty::<[u8; 4]>(
140 context,
141 1,
142 1,
143 NO_VIEW_ANGLES,
144 Interpolation::Nearest,
145 Interpolation::Nearest,
146 None,
147 Wrapping::ClampToEdge,
148 Wrapping::ClampToEdge,
149 ),
150 };
151 m.update(aabb, objects, lights, max_texture_size);
152 m
153 }
154 pub fn update(
155 &mut self,
156 aabb: AxisAlignedBoundingBox,
157 objects: impl IntoIterator<Item = impl Object> + Clone,
158 lights: &[&dyn Light],
159 max_texture_size: u32,
160 ) {
161 if !aabb.is_empty() {
162 let (min, max) = (aabb.min(), aabb.max());
163 let width = f32::sqrt(f32::powi(max.x - min.x, 2) + f32::powi(max.z - min.z, 2));
164 let height = max.y - min.y;
165 let texture_width = (max_texture_size as f32 * (width / height).min(1.0)) as u32;
166 let texture_height = (max_texture_size as f32 * (height / width).min(1.0)) as u32;
167 let viewport = Viewport::new_at_origo(texture_width, texture_height);
168 let center = 0.5 * min + 0.5 * max;
169 let mut camera = Camera::new_orthographic(
170 viewport,
171 center + vec3(0.0, 0.0, -1.0),
172 center,
173 vec3(0.0, 1.0, 0.0),
174 height,
175 -2.0 * (width + height),
176 2.0 * (width + height),
177 );
178 camera.disable_tone_and_color_mapping();
179 self.texture = Texture2DArray::new_empty::<[f16; 4]>(
180 &self.context,
181 texture_width,
182 texture_height,
183 NO_VIEW_ANGLES,
184 Interpolation::Linear,
185 Interpolation::Linear,
186 None,
187 Wrapping::ClampToEdge,
188 Wrapping::ClampToEdge,
189 );
190 let depth_texture = DepthTexture2D::new::<f32>(
191 &self.context,
192 texture_width,
193 texture_height,
194 Wrapping::ClampToEdge,
195 Wrapping::ClampToEdge,
196 );
197 for i in 0..NO_VIEW_ANGLES {
198 let layers = [i];
199 let angle = i as f32 * 2.0 * PI / NO_VIEW_ANGLES as f32;
200 camera.set_view(
201 center + vec3(f32::cos(angle), 0.0, f32::sin(angle)),
202 center,
203 vec3(0.0, 1.0, 0.0),
204 );
205 RenderTarget::new(
206 self.texture.as_color_target(&layers, None),
207 depth_texture.as_depth_target(),
208 )
209 .clear(ClearState::color_and_depth(0.0, 0.0, 0.0, 0.0, 1.0))
210 .render(&camera, objects.clone(), lights);
211 }
212 }
213 }
214}
215
216impl Material for ImpostersMaterial {
217 fn id(&self) -> EffectMaterialId {
218 EffectMaterialId::ImpostersMaterial
219 }
220
221 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
222 format!(
223 "{}{}{}{}",
224 ToneMapping::fragment_shader_source(),
225 ColorMapping::fragment_shader_source(),
226 include_str!("../../core/shared.frag"),
227 include_str!("shaders/imposter.frag")
228 )
229 }
230
231 fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, _lights: &[&dyn Light]) {
232 viewer.tone_mapping().use_uniforms(program);
233 viewer.color_mapping().use_uniforms(program);
234 program.use_uniform("no_views", NO_VIEW_ANGLES as i32);
235 program.use_uniform("view", viewer.view());
236 program.use_texture_array("tex", &self.texture);
237 }
238
239 fn render_states(&self) -> RenderStates {
240 RenderStates {
241 blend: Blend::TRANSPARENCY,
242 cull: Cull::Back,
243 ..Default::default()
244 }
245 }
246 fn material_type(&self) -> MaterialType {
247 MaterialType::Transparent
248 }
249}