use super::BakedDataIdentifier;
use super::{BakeError, BAKE_LOCK};
use crate::callback::ProgressCallback;
use crate::context::Context;
use crate::device::open_cl::OpenClDevice;
use crate::device::radeon_rays::RadeonRaysDevice;
use crate::geometry::Scene;
use crate::probe::ProbeBatch;
use crate::ray_tracing::{CustomRayTracer, DefaultRayTracer, Embree, RadeonRays, RayTracer};
use std::marker::PhantomData;
#[cfg(doc)]
use super::BakedDataVariation;
#[derive(Default)]
pub struct ReflectionsBaker<'a, T: RayTracer> {
ray_batch_size: i32,
open_cl_device: Option<&'a OpenClDevice>,
radeon_rays_device: Option<&'a RadeonRaysDevice>,
_marker: PhantomData<T>,
}
impl ReflectionsBaker<'_, DefaultRayTracer> {
pub const fn new() -> Self {
Self {
ray_batch_size: 0,
open_cl_device: None,
radeon_rays_device: None,
_marker: PhantomData,
}
}
}
impl ReflectionsBaker<'_, Embree> {
pub const fn new() -> Self {
Self {
ray_batch_size: 0,
open_cl_device: None,
radeon_rays_device: None,
_marker: PhantomData,
}
}
}
impl<'a> ReflectionsBaker<'a, RadeonRays> {
pub const fn new(
open_cl_device: &'a OpenClDevice,
radeon_rays_device: &'a RadeonRaysDevice,
) -> Self {
Self {
ray_batch_size: 0,
open_cl_device: Some(open_cl_device),
radeon_rays_device: Some(radeon_rays_device),
_marker: PhantomData,
}
}
}
impl ReflectionsBaker<'_, CustomRayTracer> {
pub const fn new(ray_batch_size: u32) -> Self {
Self {
ray_batch_size: ray_batch_size as i32,
open_cl_device: None,
radeon_rays_device: None,
_marker: PhantomData,
}
}
}
impl<T: RayTracer> ReflectionsBaker<'_, T> {
pub fn bake(
&self,
context: &Context,
probe_batch: &mut ProbeBatch,
scene: &Scene<T>,
params: ReflectionsBakeParams,
) -> Result<(), BakeError> {
self.bake_with_optional_progress_callback(context, probe_batch, scene, params, None)
}
pub fn bake_with_progress_callback(
&self,
context: &Context,
probe_batch: &mut ProbeBatch,
scene: &Scene<T>,
params: ReflectionsBakeParams,
progress_callback: ProgressCallback,
) -> Result<(), BakeError> {
self.bake_with_optional_progress_callback(
context,
probe_batch,
scene,
params,
Some(progress_callback),
)
}
fn bake_with_optional_progress_callback(
&self,
context: &Context,
probe_batch: &mut ProbeBatch,
scene: &Scene<T>,
params: ReflectionsBakeParams,
progress_callback: Option<ProgressCallback>,
) -> Result<(), BakeError> {
let _guard = BAKE_LOCK
.try_lock()
.map_err(|_| BakeError::BakeInProgress)?;
let (callback, user_data) = progress_callback.as_ref().map_or(
(None, std::ptr::null_mut()),
|callback_information| {
let (callback, user_data) = callback_information.as_raw_parts();
(Some(callback), user_data)
},
);
let mut ffi_params = audionimbus_sys::IPLReflectionsBakeParams {
scene: scene.raw_ptr(),
probeBatch: probe_batch.raw_ptr(),
sceneType: T::scene_type(),
identifier: params.identifier.into(),
bakeFlags: params.bake_flags.into(),
numRays: params.num_rays as i32,
numDiffuseSamples: params.num_diffuse_samples as i32,
numBounces: params.num_bounces as i32,
simulatedDuration: params.simulated_duration,
savedDuration: params.saved_duration,
order: params.order as i32,
numThreads: params.num_threads as i32,
rayBatchSize: self.ray_batch_size,
irradianceMinDistance: params.irradiance_min_distance,
bakeBatchSize: params.bake_batch_size as i32,
openCLDevice: self
.open_cl_device
.map_or(std::ptr::null_mut(), OpenClDevice::raw_ptr),
radeonRaysDevice: self
.radeon_rays_device
.map_or(std::ptr::null_mut(), RadeonRaysDevice::raw_ptr),
};
unsafe {
audionimbus_sys::iplReflectionsBakerBake(
context.raw_ptr(),
&raw mut ffi_params,
callback,
user_data,
);
}
Ok(())
}
pub fn cancel_bake(&self, context: &Context) {
unsafe { audionimbus_sys::iplReflectionsBakerCancelBake(context.raw_ptr()) }
}
}
#[derive(Debug, Copy, Clone)]
pub struct ReflectionsBakeParams {
pub identifier: BakedDataIdentifier,
pub bake_flags: ReflectionsBakeFlags,
pub num_rays: u32,
pub num_diffuse_samples: u32,
pub num_bounces: u32,
pub simulated_duration: f32,
pub saved_duration: f32,
pub order: u32,
pub num_threads: u32,
pub irradiance_min_distance: f32,
pub bake_batch_size: u32,
}
bitflags::bitflags! {
#[derive(Copy, Clone, Debug)]
pub struct ReflectionsBakeFlags: u32 {
const BAKE_CONVOLUTION = 1 << 0;
const BAKE_PARAMETRIC = 1 << 1;
}
}
impl From<ReflectionsBakeFlags> for audionimbus_sys::IPLReflectionsBakeFlags {
fn from(reflections_bake_flags: ReflectionsBakeFlags) -> Self {
Self(reflections_bake_flags.bits() as _)
}
}
#[cfg(test)]
pub mod tests {
use crate::*;
fn test_scene(context: &Context) -> Scene<'_, DefaultRayTracer> {
let mut scene = Scene::try_new(context).unwrap();
let vertices = vec![
Vector3::new(-5.0, 0.0, -5.0),
Vector3::new(5.0, 0.0, -5.0),
Vector3::new(5.0, 0.0, 5.0),
Vector3::new(-5.0, 0.0, 5.0),
Vector3::new(-5.0, 3.0, -5.0),
Vector3::new(5.0, 3.0, -5.0),
Vector3::new(5.0, 3.0, 5.0),
Vector3::new(-5.0, 3.0, 5.0),
];
let triangles = [
[0, 1, 2],
[0, 2, 3],
[4, 6, 5],
[4, 7, 6],
[0, 4, 5],
[0, 5, 1],
[1, 5, 6],
[1, 6, 2],
[2, 6, 7],
[2, 7, 3],
[3, 7, 4],
[3, 4, 0],
]
.iter()
.map(|indices| Triangle::new(indices[0], indices[1], indices[2]))
.collect::<Vec<_>>();
let material_indices = vec![0; triangles.len()];
let materials = vec![Material::default()];
let settings = StaticMeshSettings {
vertices: &vertices,
triangles: &triangles,
material_indices: &material_indices,
materials: &materials,
};
let static_mesh = StaticMesh::try_new(&scene, &settings).unwrap();
scene.add_static_mesh(static_mesh);
scene.commit();
scene
}
fn test_probe_batch(context: &Context, scene: &Scene) -> ProbeBatch {
let mut probe_batch = ProbeBatch::try_new(context).unwrap();
let params = ProbeGenerationParams::Centroid {
transform: Matrix4::new([
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]),
};
let mut probe_array = ProbeArray::try_new(context).unwrap();
probe_array.generate_probes(scene, ¶ms);
probe_batch.add_probe_array(&probe_array);
probe_batch.commit();
probe_batch
}
pub fn test_bake() {
{
let context = Context::default();
let scene = test_scene(&context);
let mut probe_batch = test_probe_batch(&context, &scene);
let baker = ReflectionsBaker::<DefaultRayTracer>::new();
let params = ReflectionsBakeParams {
identifier: BakedDataIdentifier::Reflections {
variation: BakedDataVariation::Reverb,
},
bake_flags: ReflectionsBakeFlags::BAKE_CONVOLUTION,
num_rays: 1024,
num_diffuse_samples: 32,
num_bounces: 8,
simulated_duration: 2.0,
saved_duration: 2.0,
order: 1,
num_threads: 2,
irradiance_min_distance: 1.0,
bake_batch_size: 8,
};
assert!(baker
.bake(&context, &mut probe_batch, &scene, params)
.is_ok());
}
{
let context = Context::default();
let scene = test_scene(&context);
let mut probe_batch = test_probe_batch(&context, &scene);
let baker = ReflectionsBaker::<DefaultRayTracer>::new();
let params = ReflectionsBakeParams {
identifier: BakedDataIdentifier::Reflections {
variation: BakedDataVariation::Reverb,
},
bake_flags: ReflectionsBakeFlags::BAKE_PARAMETRIC,
num_rays: 512,
num_diffuse_samples: 16,
num_bounces: 4,
simulated_duration: 1.0,
saved_duration: 1.0,
order: 1,
num_threads: 1,
irradiance_min_distance: 0.5,
bake_batch_size: 4,
};
assert!(baker
.bake(&context, &mut probe_batch, &scene, params)
.is_ok());
}
{
let context = Context::default();
let scene = test_scene(&context);
let mut probe_batch = test_probe_batch(&context, &scene);
let baker = ReflectionsBaker::<DefaultRayTracer>::new();
let params = ReflectionsBakeParams {
identifier: BakedDataIdentifier::Reflections {
variation: BakedDataVariation::Reverb,
},
bake_flags: ReflectionsBakeFlags::BAKE_CONVOLUTION
| ReflectionsBakeFlags::BAKE_PARAMETRIC,
num_rays: 512,
num_diffuse_samples: 16,
num_bounces: 4,
simulated_duration: 1.0,
saved_duration: 1.0,
order: 1,
num_threads: 1,
irradiance_min_distance: 0.5,
bake_batch_size: 4,
};
assert!(baker
.bake(&context, &mut probe_batch, &scene, params)
.is_ok());
}
{
let context = Context::default();
let scene = test_scene(&context);
let mut probe_batch = test_probe_batch(&context, &scene);
let baker = ReflectionsBaker::<DefaultRayTracer>::new();
let params = ReflectionsBakeParams {
identifier: BakedDataIdentifier::Reflections {
variation: BakedDataVariation::StaticSource {
endpoint_influence: Sphere {
center: Vector3::new(0.0, 1.5, 0.0),
radius: 1.0,
},
},
},
bake_flags: ReflectionsBakeFlags::BAKE_CONVOLUTION,
num_rays: 512,
num_diffuse_samples: 16,
num_bounces: 4,
simulated_duration: 1.0,
saved_duration: 1.0,
order: 1,
num_threads: 1,
irradiance_min_distance: 0.5,
bake_batch_size: 4,
};
assert!(baker
.bake(&context, &mut probe_batch, &scene, params)
.is_ok());
}
{
let context = Context::default();
let scene = test_scene(&context);
let mut probe_batch = test_probe_batch(&context, &scene);
let baker = ReflectionsBaker::<DefaultRayTracer>::new();
let params = ReflectionsBakeParams {
identifier: BakedDataIdentifier::Reflections {
variation: BakedDataVariation::Reverb,
},
bake_flags: ReflectionsBakeFlags::BAKE_CONVOLUTION,
num_rays: 512,
num_diffuse_samples: 16,
num_bounces: 4,
simulated_duration: 1.0,
saved_duration: 1.0,
order: 1,
num_threads: 1,
irradiance_min_distance: 0.5,
bake_batch_size: 4,
};
assert!(baker
.bake_with_progress_callback(
&context,
&mut probe_batch,
&scene,
params,
ProgressCallback::new(|progress| {
println!("baking progress: {:.1}%", progress * 100.0);
}),
)
.is_ok());
}
}
}