#![deny(unsafe_op_in_unsafe_fn)]
use concinnity_core::gfx::transform::mat4_inverse;
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_metal::{MTLDevice, MTLPixelFormat, MTLTexture, MTLTextureType, MTLTextureUsage};
use super::context::MtlContext;
use super::descriptors::TextureDesc;
use concinnity_core::gfx::transform::mat4_mul;
const PLANAR_CLIP_BIAS: f32 = 0.02;
pub(in crate::metal) const MAX_PLANAR_PLANES: usize =
crate::gfx::planar_reflection::MAX_PLANAR_PLANES;
pub(in crate::metal) struct PlanarReflectionTargets {
pub(in crate::metal) msaa_color: Retained<ProtocolObject<dyn MTLTexture>>,
pub(in crate::metal) msaa_depth: Retained<ProtocolObject<dyn MTLTexture>>,
pub(in crate::metal) resolve: Retained<ProtocolObject<dyn MTLTexture>>,
}
pub(in crate::metal) struct PlanarReflectionSet {
pub(in crate::metal) targets: Vec<PlanarReflectionTargets>,
pub(in crate::metal) planes: Vec<[f32; 4]>,
}
pub(in crate::metal) fn create_planar_targets(
device: &ProtocolObject<dyn MTLDevice>,
width: u32,
height: u32,
sample_count: u32,
) -> Result<PlanarReflectionTargets, String> {
let color = {
let desc = TextureDesc {
kind: MTLTextureType::Type2DMultisample,
format: MTLPixelFormat::RGBA16Float,
width: width as usize,
height: height as usize,
sample_count: sample_count as usize,
usage: MTLTextureUsage::RenderTarget,
..Default::default()
}
.build();
device
.newTextureWithDescriptor(&desc)
.ok_or("planar: failed to create MSAA colour target")?
};
let depth = {
let desc = TextureDesc {
kind: MTLTextureType::Type2DMultisample,
format: MTLPixelFormat::Depth32Float,
width: width as usize,
height: height as usize,
sample_count: sample_count as usize,
usage: MTLTextureUsage::RenderTarget,
..Default::default()
}
.build();
device
.newTextureWithDescriptor(&desc)
.ok_or("planar: failed to create MSAA depth target")?
};
let resolve = {
let desc = TextureDesc {
format: MTLPixelFormat::RGBA16Float,
width: width as usize,
height: height as usize,
usage: MTLTextureUsage(MTLTextureUsage::ShaderRead.0 | MTLTextureUsage::RenderTarget.0),
..Default::default()
}
.build();
device
.newTextureWithDescriptor(&desc)
.ok_or("planar: failed to create resolve target")?
};
Ok(PlanarReflectionTargets {
msaa_color: color,
msaa_depth: depth,
resolve,
})
}
pub(in crate::metal) fn create_planar_set(
device: &ProtocolObject<dyn MTLDevice>,
width: u32,
height: u32,
sample_count: u32,
planes: &[[f32; 4]],
) -> Result<PlanarReflectionSet, String> {
let mut targets = Vec::with_capacity(planes.len());
for _ in planes {
targets.push(create_planar_targets(device, width, height, sample_count)?);
}
Ok(PlanarReflectionSet {
targets,
planes: planes.to_vec(),
})
}
impl MtlContext {
pub(in crate::metal) fn encode_planar_reflections(
&self,
cmd_buf: &ProtocolObject<dyn objc2_metal::MTLCommandBuffer>,
params: &super::graph_exec::GraphFrameParams,
) -> Result<(), String> {
let Some(set) = self.planar_reflection.as_ref() else {
return Ok(());
};
let proj = mat4_mul(params.vp, mat4_inverse(self.view.matrix));
let mut reflected_visible: Vec<u32> = Vec::new();
for (slot, (plane, targets)) in set.planes.iter().zip(set.targets.iter()).enumerate() {
let oriented =
crate::gfx::planar_reflection::orient_plane_toward(*plane, params.cam_pos);
let m = crate::gfx::planar_reflection::planar_matrices(
self.view.matrix,
proj,
params.cam_pos,
oriented,
PLANAR_CLIP_BIAS,
);
let mirror_frustum = crate::gfx::frustum::Frustum::from_view_projection(m.view_proj);
let (icb_override, visible): (
Option<&ProtocolObject<dyn objc2_metal::MTLIndirectCommandBuffer>>,
&[u32],
) = if let (Some(object_buffer), Some(draw_args)) =
(params.object_buffer, params.draw_args_buffer)
{
self.encode_mirror_cull(
cmd_buf,
object_buffer,
draw_args,
&mirror_frustum,
m.eye,
slot,
)?;
(
self.cull.mirror_slots.get(slot).map(|s| s.icb.as_ref()),
params.visible,
)
} else {
crate::gfx::planar_reflection::reflected_visible_set(
&self.draw.bvh,
&mirror_frustum,
m.eye,
&self.draw.always,
&mut reflected_visible,
);
(None, reflected_visible.as_slice())
};
self.encode_main_into_face(
cmd_buf,
crate::metal::draw::main::FaceTargets {
color_msaa: &targets.msaa_color,
depth_msaa: &targets.msaa_depth,
resolve: &targets.resolve,
},
crate::metal::draw::main::MainPassCamera {
elapsed: params.elapsed,
vp: m.view_proj,
view: m.view,
cam_pos: m.eye,
},
crate::metal::draw::main::DrawInputs {
visible,
prepared_instances: params.prepared_instances,
skinned_joint_bufs: params.skinned_joint_bufs,
},
crate::metal::draw::main::GpuFrameBuffers {
object_buffer: params.object_buffer,
bindless_tex_args: params.bindless_tex_args,
deformed_skinned: params.deformed_skinned,
counts: self.draw_record_counts(),
},
icb_override,
)?;
}
Ok(())
}
}