use concinnity_core::gfx::render_types::{
self, LightUniforms, NUM_SHADOW_CASCADES, SpotShadowData,
};
use concinnity_core::render::backend_init::ShadowParams;
use concinnity_core::render::csm;
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::lights;
use concinnity_core::render::spot_shadow;
use objc2_metal::{
MTLDevice as _, MTLResourceOptions, MTLSamplerAddressMode, MTLSamplerDescriptor,
MTLSamplerMinMagFilter,
};
use super::InitGpu;
use crate::metal::context::{ShadowState, SpotShadowState, bytes_of_slice};
use crate::metal::depth::SHADOW_SAMPLE_COMPARE;
use crate::metal::texture::{create_shadow_map_array, create_shadow_map_fallback};
pub(super) fn build_shadow(
gpu: &InitGpu<'_>,
shadows: &ShadowParams,
light_uniforms: &LightUniforms,
) -> RenderResult<ShadowState> {
let device = &*gpu.hw.device;
let enabled = shadows.map_size > 0;
let (map, map_size) = if enabled {
let shadow_tex =
create_shadow_map_array(device, shadows.map_size, NUM_SHADOW_CASCADES as u32)?;
(shadow_tex, shadows.map_size)
} else {
(create_shadow_map_fallback(device)?, 1)
};
let uniforms = csm::empty_shadow_uniforms();
let sampler = {
let desc = MTLSamplerDescriptor::new();
desc.setMinFilter(MTLSamplerMinMagFilter::Linear);
desc.setMagFilter(MTLSamplerMinMagFilter::Linear);
desc.setSAddressMode(MTLSamplerAddressMode::ClampToEdge);
desc.setTAddressMode(MTLSamplerAddressMode::ClampToEdge);
desc.setCompareFunction(SHADOW_SAMPLE_COMPARE);
desc.setSupportArgumentBuffers(true);
device
.newSamplerStateWithDescriptor(&desc)
.ok_or_else(|| RenderError::Other("failed to create shadow sampler state".into()))?
};
let light_dir = lights::sun_direction(light_uniforms);
Ok(ShadowState {
enabled,
map,
map_size,
cadence: shadows.cadence,
scheduler: Default::default(),
render_mask: 0,
sampler,
uniforms,
light_dir,
})
}
pub(super) fn build_spot_shadow(
gpu: &InitGpu<'_>,
spot_shadows: &[SpotShadowData],
shadow_map_size: u32,
) -> RenderResult<SpotShadowState> {
let device = &*gpu.hw.device;
let count = spot_shadows.len() as u32;
let map = if spot_shadows.is_empty() {
create_shadow_map_fallback(device)?
} else {
create_shadow_map_array(
device,
render_types::spot_shadow_slice_size(shadow_map_size),
count,
)?
};
let buffer = if spot_shadows.is_empty() {
gpu.hw.allocator.alloc_buffer(
std::mem::size_of::<SpotShadowData>(),
MTLResourceOptions::StorageModeShared,
)
} else {
gpu.hw.allocator.alloc_buffer_with_bytes(
bytes_of_slice(spot_shadows),
MTLResourceOptions::StorageModeShared,
)
}
.map_err(|e| e.context("spot-shadow buffer"))?;
Ok(SpotShadowState {
map,
buffer,
count,
frusta: spot_shadows
.iter()
.map(spot_shadow::slice_frustum)
.collect(),
scheduler: Default::default(),
render_mask: 0,
})
}