|
2 | 2 | //!
|
3 | 3 | //! Will default to the PBR shader code if custom code is not specified.
|
4 | 4 |
|
5 |
| -use std::{marker::PhantomData, sync::Arc}; |
| 5 | +use std::{cmp::Ordering, marker::PhantomData, sync::Arc}; |
6 | 6 |
|
7 | 7 | use arrayvec::ArrayVec;
|
8 | 8 | use encase::{ShaderSize, StorageBuffer};
|
| 9 | +use ordered_float::OrderedFloat; |
9 | 10 | use rend3::{
|
10 | 11 | graph::{DataHandle, NodeResourceUsage, RenderGraph, RenderPassTargets},
|
11 |
| - types::{Material, SampleCount}, |
| 12 | + managers::{CameraState, InternalObject, MaterialArchetypeView, TextureBindGroupIndex}, |
| 13 | + types::{Material, RawObjectHandle, SampleCount, SortingOrder, SortingReason}, |
12 | 14 | util::bind_merge::BindGroupBuilder,
|
13 | 15 | ProfileData, Renderer, RendererDataCore, RendererProfile, ShaderPreProcessor,
|
14 | 16 | };
|
@@ -165,6 +167,8 @@ impl<M: Material> ForwardRoutine<M> {
|
165 | 167 | CameraSpecifier::Shadow(idx) => &ctx.eval_output.shadows[idx as usize].camera,
|
166 | 168 | };
|
167 | 169 |
|
| 170 | + let objects = sort(objects, archetype_view, self.material_key, camera); |
| 171 | + |
168 | 172 | let per_camera_uniform_values = PerCameraUniform {
|
169 | 173 | view: camera.view(),
|
170 | 174 | view_proj: camera.view_proj(),
|
@@ -230,6 +234,104 @@ impl<M: Material> ForwardRoutine<M> {
|
230 | 234 | }
|
231 | 235 | }
|
232 | 236 |
|
| 237 | +fn sort<'a, M, I>( |
| 238 | + objects: I, |
| 239 | + material_archetype: MaterialArchetypeView<'_, M>, |
| 240 | + requested_material_key: u64, |
| 241 | + camera: &CameraState, |
| 242 | +) -> Vec<(RawObjectHandle, &'a InternalObject<M>)> |
| 243 | +where |
| 244 | + M: Material, |
| 245 | + I: IntoIterator<Item = (RawObjectHandle, &'a InternalObject<M>)>, |
| 246 | + I::IntoIter: ExactSizeIterator, |
| 247 | +{ |
| 248 | + let objects = objects.into_iter(); |
| 249 | + |
| 250 | + let mut sorted_objects = Vec::with_capacity(objects.len()); |
| 251 | + { |
| 252 | + profiling::scope!("Sort Key Creation"); |
| 253 | + for (raw_handle, object) in objects { |
| 254 | + let material = material_archetype.material(*object.material_handle); |
| 255 | + let object_material_key = material.inner.key(); |
| 256 | + let sorting = material.inner.sorting(); |
| 257 | + |
| 258 | + if object_material_key != requested_material_key { |
| 259 | + continue; |
| 260 | + } |
| 261 | + |
| 262 | + // Frustum culling |
| 263 | + if !camera.world_frustum().contains_sphere(object.inner.bounding_sphere) { |
| 264 | + continue; |
| 265 | + } |
| 266 | + |
| 267 | + let bind_group_index = material.bind_group_index.map_gpu(|_| TextureBindGroupIndex::DUMMY).into_common(); |
| 268 | + |
| 269 | + let mut distance_sq = camera.location().distance_squared(object.location.into()); |
| 270 | + |
| 271 | + if sorting.order == SortingOrder::BackToFront { |
| 272 | + distance_sq = -distance_sq; |
| 273 | + } |
| 274 | + sorted_objects.push(( |
| 275 | + ObjectSortingKey { |
| 276 | + bind_group_index, |
| 277 | + distance: OrderedFloat(distance_sq), |
| 278 | + sorting_reason: sorting.reason, |
| 279 | + }, |
| 280 | + (raw_handle, object), |
| 281 | + )) |
| 282 | + } |
| 283 | + } |
| 284 | + |
| 285 | + { |
| 286 | + profiling::scope!("Sorting"); |
| 287 | + sorted_objects.sort_unstable_by_key(|(k, _)| *k); |
| 288 | + } |
| 289 | + |
| 290 | + sorted_objects.into_iter().map(|(_, o)| o).collect() |
| 291 | +} |
| 292 | + |
| 293 | +#[derive(Debug, Clone, Copy, Eq)] |
| 294 | +pub(super) struct ObjectSortingKey { |
| 295 | + pub bind_group_index: TextureBindGroupIndex, |
| 296 | + pub distance: OrderedFloat<f32>, |
| 297 | + pub sorting_reason: SortingReason, |
| 298 | +} |
| 299 | + |
| 300 | +impl PartialEq for ObjectSortingKey { |
| 301 | + fn eq(&self, other: &Self) -> bool { |
| 302 | + self.cmp(other).is_eq() |
| 303 | + } |
| 304 | +} |
| 305 | + |
| 306 | +impl PartialOrd for ObjectSortingKey { |
| 307 | + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
| 308 | + Some(self.cmp(other)) |
| 309 | + } |
| 310 | +} |
| 311 | + |
| 312 | +impl Ord for ObjectSortingKey { |
| 313 | + fn cmp(&self, other: &Self) -> Ordering { |
| 314 | + match self.sorting_reason.cmp(&other.sorting_reason) { |
| 315 | + Ordering::Equal => {} |
| 316 | + ord => return ord, |
| 317 | + } |
| 318 | + // The above comparison means that both sides are equal |
| 319 | + if self.sorting_reason == SortingReason::Requirement { |
| 320 | + match self.distance.cmp(&other.distance) { |
| 321 | + Ordering::Equal => {} |
| 322 | + ord => return ord, |
| 323 | + } |
| 324 | + self.bind_group_index.cmp(&other.bind_group_index) |
| 325 | + } else { |
| 326 | + match self.bind_group_index.cmp(&other.bind_group_index) { |
| 327 | + Ordering::Equal => {} |
| 328 | + ord => return ord, |
| 329 | + } |
| 330 | + self.distance.cmp(&other.distance) |
| 331 | + } |
| 332 | + } |
| 333 | +} |
| 334 | + |
233 | 335 | fn build_forward_pipeline_inner<M: Material>(
|
234 | 336 | pll: &wgpu::PipelineLayout,
|
235 | 337 | args: &ForwardRoutineCreateArgs<'_, M>,
|
|
0 commit comments