use crate::grid::grid::{GpuGrid, WgGrid};
use crate::grid::prefix_sum::{PrefixSumWorkspace, WgPrefixSum};
use crate::grid::sort::WgSort;
use crate::models::GpuModels;
use crate::solver::{
GpuImpulses, GpuParticles, GpuRigidParticles, GpuSimulationParams, Particle, SimulationParams,
WgG2P, WgG2PCdf, WgGridUpdate, WgGridUpdateCdf, WgP2G, WgP2GCdf, WgParticleUpdate,
WgRigidImpulses, WgRigidParticleUpdate,
};
use nexus::dynamics::body::{BodyCoupling, BodyCouplingEntry};
use nexus::dynamics::GpuBodySet;
use nexus::math::GpuSim;
use rapier::dynamics::RigidBodySet;
use rapier::geometry::ColliderSet;
use slang_hal::backend::{Backend, Encoder};
use slang_hal::re_exports::minislang::SlangCompiler;
use slang_hal::Shader;
use stensor::tensor::GpuVector;
use wgpu::BufferUsages;
pub struct MpmPipeline<B: Backend> {
grid: WgGrid<B>,
prefix_sum: WgPrefixSum<B>,
sort: WgSort<B>,
p2g: WgP2G<B>,
p2g_cdf: WgP2GCdf<B>,
grid_update_cdf: WgGridUpdateCdf<B>,
grid_update: WgGridUpdate<B>,
particles_update: WgParticleUpdate<B>,
g2p: WgG2P<B>,
g2p_cdf: WgG2PCdf<B>,
rigid_particles_update: WgRigidParticleUpdate<B>,
pub impulses: WgRigidImpulses<B>,
}
pub struct MpmData<B: Backend> {
pub sim_params: GpuSimulationParams<B>,
pub grid: GpuGrid<B>,
pub particles: GpuParticles<B>, pub rigid_particles: GpuRigidParticles<B>,
pub bodies: GpuBodySet<B>,
pub impulses: GpuImpulses<B>,
pub poses_staging: GpuVector<GpuSim, B>,
prefix_sum: PrefixSumWorkspace<B>,
models: GpuModels<B>,
coupling: Vec<BodyCouplingEntry>,
}
impl<B: Backend> MpmData<B> {
pub fn new(
backend: &B,
params: SimulationParams,
particles: &[Particle],
bodies: &RigidBodySet,
colliders: &ColliderSet,
cell_width: f32,
grid_capacity: u32,
) -> Result<Self, B::Error> {
let coupling: Vec<_> = colliders
.iter()
.filter_map(|(co_handle, co)| {
let rb_handle = co.parent()?;
Some(BodyCouplingEntry {
body: rb_handle,
collider: co_handle,
mode: BodyCoupling::OneWay,
})
})
.collect();
Self::with_select_coupling(
backend,
params,
particles,
bodies,
colliders,
coupling,
cell_width,
grid_capacity,
)
}
pub fn with_select_coupling(
backend: &B,
params: SimulationParams,
particles: &[Particle],
bodies: &RigidBodySet,
colliders: &ColliderSet,
coupling: Vec<BodyCouplingEntry>,
cell_width: f32,
grid_capacity: u32,
) -> Result<Self, B::Error> {
let sampling_step = cell_width; let bodies = GpuBodySet::from_rapier(backend, bodies, colliders, &coupling)?;
let sim_params = GpuSimulationParams::new(backend, params)?;
let models = GpuModels::from_particles(backend, particles)?;
let particles = GpuParticles::from_particles(backend, particles)?;
let rigid_particles =
GpuRigidParticles::from_rapier(backend, colliders, &bodies, &coupling, sampling_step)?;
let grid = GpuGrid::with_capacity(backend, grid_capacity, cell_width)?;
let prefix_sum = PrefixSumWorkspace::with_capacity(backend, grid_capacity)?;
let impulses = GpuImpulses::new(backend)?;
let poses_staging = unsafe {
GpuVector::vector_uninit(
backend,
bodies.len(),
BufferUsages::COPY_DST | BufferUsages::MAP_READ,
)?
};
Ok(Self {
sim_params,
particles,
rigid_particles,
bodies,
impulses,
grid,
prefix_sum,
models,
poses_staging,
coupling,
})
}
pub fn coupling(&self) -> &[BodyCouplingEntry] {
&self.coupling
}
}
impl<B: Backend> MpmPipeline<B> {
pub fn new(backend: &B, compiler: &SlangCompiler) -> Result<Self, B::Error> {
Ok(Self {
grid: WgGrid::from_backend(backend, compiler)?,
prefix_sum: WgPrefixSum::from_backend(backend, compiler)?,
sort: WgSort::from_backend(backend, compiler)?,
p2g: WgP2G::from_backend(backend, compiler)?,
p2g_cdf: WgP2GCdf::from_backend(backend, compiler)?,
grid_update: WgGridUpdate::from_backend(backend, compiler)?,
grid_update_cdf: WgGridUpdateCdf::from_backend(backend, compiler)?,
particles_update: WgParticleUpdate::from_backend(backend, compiler)?,
rigid_particles_update: WgRigidParticleUpdate::from_backend(backend, compiler)?,
g2p: WgG2P::from_backend(backend, compiler)?,
g2p_cdf: WgG2PCdf::from_backend(backend, compiler)?,
impulses: WgRigidImpulses::from_backend(backend, compiler)?,
})
}
pub fn launch_step(
&self,
backend: &B,
encoder: &mut B::Encoder,
data: &mut MpmData<B>,
) -> Result<(), B::Error> {
{
let mut pass = encoder.begin_pass(); self.impulses.launch_update_world_mass_properties(
backend,
&mut pass,
&data.impulses,
&data.bodies,
)?;
self.rigid_particles_update.launch(
backend,
&mut pass,
&data.bodies,
&data.rigid_particles,
)?;
}
{
let mut pass = encoder.begin_pass(); self.grid.launch_sort(
backend,
&mut pass,
&data.particles,
&data.rigid_particles,
&data.grid,
&mut data.prefix_sum,
&self.sort,
&self.prefix_sum,
)?;
self.sort.launch_sort_rigid_particles(
backend,
&mut pass,
&data.rigid_particles,
&data.grid,
)?;
}
{
let mut pass = encoder.begin_pass(); self.grid_update_cdf
.launch(backend, &mut pass, &data.grid, &data.bodies)?;
}
{
let mut pass = encoder.begin_pass(); self.p2g_cdf.launch(
backend,
&mut pass,
&data.grid,
&data.rigid_particles,
&data.bodies,
)?;
}
{
let mut pass = encoder.begin_pass(); self.g2p_cdf.launch(
backend,
&mut pass,
&data.sim_params,
&data.grid,
&data.particles,
)?;
}
{
let mut pass = encoder.begin_pass(); self.p2g.launch(
backend,
&mut pass,
&data.grid,
&data.particles,
&data.impulses,
&data.bodies,
)?;
}
{
let mut pass = encoder.begin_pass(); self.grid_update
.launch(backend, &mut pass, &data.sim_params, &data.grid)?;
}
{
let mut pass = encoder.begin_pass(); self.g2p.launch(
backend,
&mut pass,
&data.sim_params,
&data.grid,
&data.particles,
&data.bodies,
)?;
}
{
let mut pass = encoder.begin_pass(); self.particles_update.launch(
backend,
&mut pass,
&data.sim_params,
&data.grid,
&data.particles,
&data.models,
&data.bodies,
)?;
}
{
let mut pass = encoder.begin_pass(); self.impulses.launch(
backend,
&mut pass,
&data.grid,
&data.sim_params,
&data.impulses,
&data.bodies,
)?;
}
Ok(())
}
}