use crate::ffi;
use crate::types::*;
pub struct ExtStressSolver {
handle: *mut ffi::ExtStressSolverHandle,
}
unsafe impl Send for ExtStressSolver {}
unsafe impl Sync for ExtStressSolver {}
impl ExtStressSolver {
pub fn new(nodes: &[NodeDesc], bonds: &[BondDesc], settings: &SolverSettings) -> Option<Self> {
if nodes.is_empty() || bonds.is_empty() {
return None;
}
let ffi_nodes: Vec<ffi::FfiExtStressNodeDesc> = nodes
.iter()
.map(|n| ffi::FfiExtStressNodeDesc {
centroid: n.centroid,
mass: n.mass,
volume: n.volume,
})
.collect();
let ffi_bonds: Vec<ffi::FfiExtStressBondDesc> = bonds
.iter()
.map(|b| ffi::FfiExtStressBondDesc {
centroid: b.centroid,
normal: b.normal,
area: b.area,
node0: b.node0,
node1: b.node1,
})
.collect();
let ffi_settings = to_ffi_settings(settings);
let handle = unsafe {
ffi::ext_stress_solver_create(
ffi_nodes.as_ptr(),
ffi_nodes.len() as u32,
ffi_bonds.as_ptr(),
ffi_bonds.len() as u32,
&ffi_settings,
)
};
if handle.is_null() {
None
} else {
Some(Self { handle })
}
}
pub fn set_settings(&mut self, settings: &SolverSettings) {
let ffi_settings = to_ffi_settings(settings);
unsafe { ffi::ext_stress_solver_set_settings(self.handle, &ffi_settings) }
}
pub fn reset(&mut self) {
unsafe { ffi::ext_stress_solver_reset(self.handle) }
}
pub fn add_force(&mut self, node_index: u32, position: Vec3, force: Vec3, mode: ForceMode) {
unsafe {
ffi::ext_stress_solver_add_force(
self.handle,
node_index,
&position,
&force,
mode as u32,
)
}
}
pub fn add_gravity(&mut self, gravity: Vec3) {
unsafe { ffi::ext_stress_solver_add_gravity(self.handle, &gravity) }
}
pub fn add_actor_gravity(&mut self, actor_index: u32, gravity: Vec3) -> bool {
unsafe { ffi::ext_stress_solver_add_actor_gravity(self.handle, actor_index, &gravity) != 0 }
}
pub fn update(&mut self) {
unsafe { ffi::ext_stress_solver_update(self.handle) }
}
pub fn overstressed_bond_count(&self) -> u32 {
unsafe { ffi::ext_stress_solver_overstressed_bond_count(self.handle) }
}
pub fn converged(&self) -> bool {
unsafe { ffi::ext_stress_solver_converged(self.handle) != 0 }
}
pub fn linear_error(&self) -> f32 {
unsafe { ffi::ext_stress_solver_get_linear_error(self.handle) }
}
pub fn angular_error(&self) -> f32 {
unsafe { ffi::ext_stress_solver_get_angular_error(self.handle) }
}
pub fn actor_count(&self) -> u32 {
unsafe { ffi::ext_stress_solver_actor_count(self.handle) }
}
pub fn node_count(&self) -> u32 {
unsafe { ffi::ext_stress_solver_graph_node_count(self.handle) }
}
pub fn bond_count(&self) -> u32 {
unsafe { ffi::ext_stress_solver_bond_count(self.handle) }
}
pub fn actors(&self) -> Vec<Actor> {
let actor_count = self.actor_count();
if actor_count == 0 {
return Vec::new();
}
let node_count = self.node_count();
let mut actor_buffer = vec![
ffi::FfiExtStressActor {
actor_index: u32::MAX,
nodes: std::ptr::null(),
node_count: 0,
};
actor_count as usize
];
let mut nodes_buffer = vec![0u32; node_count as usize];
let mut out_actor_count = 0u32;
let mut out_node_count = 0u32;
unsafe {
ffi::ext_stress_solver_collect_actors(
self.handle,
actor_buffer.as_mut_ptr(),
actor_count,
nodes_buffer.as_mut_ptr(),
node_count,
&mut out_actor_count,
&mut out_node_count,
);
}
let mut result = Vec::with_capacity(out_actor_count as usize);
for i in 0..out_actor_count as usize {
let ffi_actor = &actor_buffer[i];
let nodes = if !ffi_actor.nodes.is_null() && ffi_actor.node_count > 0 {
let offset = unsafe { ffi_actor.nodes.offset_from(nodes_buffer.as_ptr()) } as usize;
nodes_buffer[offset..offset + ffi_actor.node_count as usize].to_vec()
} else {
Vec::new()
};
result.push(Actor {
actor_index: ffi_actor.actor_index,
nodes,
});
}
result
}
pub fn generate_fracture_commands(&self) -> Vec<FractureCommand> {
let actor_count = self.actor_count();
let bond_count = self.bond_count();
if actor_count == 0 || bond_count == 0 {
return Vec::new();
}
let mut command_buffer = vec![
ffi::FfiExtStressFractureCommands {
actor_index: u32::MAX,
bond_fractures: std::ptr::null_mut(),
bond_fracture_count: 0,
};
actor_count as usize
];
let mut bond_buffer = vec![ffi::FfiExtStressBondFracture::default(); bond_count as usize];
let mut out_command_count = 0u32;
let mut out_bond_count = 0u32;
unsafe {
ffi::ext_stress_solver_generate_fracture_commands_per_actor(
self.handle,
command_buffer.as_mut_ptr(),
actor_count,
bond_buffer.as_mut_ptr(),
bond_count,
&mut out_command_count,
&mut out_bond_count,
);
}
let mut result = Vec::with_capacity(out_command_count as usize);
for i in 0..out_command_count as usize {
let cmd = &command_buffer[i];
let fractures = if !cmd.bond_fractures.is_null() && cmd.bond_fracture_count > 0 {
let offset =
unsafe { cmd.bond_fractures.offset_from(bond_buffer.as_ptr()) } as usize;
bond_buffer[offset..offset + cmd.bond_fracture_count as usize]
.iter()
.map(|f| BondFracture {
userdata: f.userdata,
node_index0: f.node_index0,
node_index1: f.node_index1,
health: f.health,
})
.collect()
} else {
Vec::new()
};
result.push(FractureCommand {
actor_index: cmd.actor_index,
bond_fractures: fractures,
});
}
result
}
pub fn apply_fracture_commands(&mut self, commands: &[FractureCommand]) -> Vec<SplitEvent> {
if commands.is_empty() {
return Vec::new();
}
let total_bonds: usize = commands.iter().map(|c| c.bond_fractures.len()).sum();
let mut flat_bonds = vec![ffi::FfiExtStressBondFracture::default(); total_bonds];
let mut ffi_commands = Vec::with_capacity(commands.len());
let mut offset = 0usize;
for cmd in commands {
for (i, f) in cmd.bond_fractures.iter().enumerate() {
flat_bonds[offset + i] = ffi::FfiExtStressBondFracture {
userdata: f.userdata,
node_index0: f.node_index0,
node_index1: f.node_index1,
health: f.health,
};
}
ffi_commands.push(ffi::FfiExtStressFractureCommands {
actor_index: cmd.actor_index,
bond_fractures: if cmd.bond_fractures.is_empty() {
std::ptr::null_mut()
} else {
unsafe { flat_bonds.as_mut_ptr().add(offset) }
},
bond_fracture_count: cmd.bond_fractures.len() as u32,
});
offset += cmd.bond_fractures.len();
}
let node_count = self.node_count() as usize;
let max_events = commands.len();
let max_children = node_count;
let mut events_buffer = vec![
ffi::FfiExtStressSplitEvent {
parent_actor_index: u32::MAX,
children: std::ptr::null_mut(),
child_count: 0,
};
max_events
];
let mut child_buffer = vec![
ffi::FfiExtStressActor {
actor_index: u32::MAX,
nodes: std::ptr::null(),
node_count: 0,
};
max_children
];
let mut nodes_buffer = vec![0u32; node_count];
let mut out_event_count = 0u32;
let mut out_child_count = 0u32;
let mut out_node_count = 0u32;
unsafe {
ffi::ext_stress_solver_apply_fracture_commands(
self.handle,
ffi_commands.as_ptr(),
ffi_commands.len() as u32,
events_buffer.as_mut_ptr(),
max_events as u32,
child_buffer.as_mut_ptr(),
max_children as u32,
&mut out_event_count,
&mut out_child_count,
nodes_buffer.as_mut_ptr(),
node_count as u32,
&mut out_node_count,
);
}
let mut result = Vec::with_capacity(out_event_count as usize);
for i in 0..out_event_count as usize {
let evt = &events_buffer[i];
let mut children = Vec::with_capacity(evt.child_count as usize);
if !evt.children.is_null() && evt.child_count > 0 {
let child_offset =
unsafe { evt.children.offset_from(child_buffer.as_ptr()) } as usize;
for ci in 0..evt.child_count as usize {
let child = &child_buffer[child_offset + ci];
let nodes = if !child.nodes.is_null() && child.node_count > 0 {
let node_off =
unsafe { child.nodes.offset_from(nodes_buffer.as_ptr()) } as usize;
nodes_buffer[node_off..node_off + child.node_count as usize].to_vec()
} else {
Vec::new()
};
children.push(SplitChild {
actor_index: child.actor_index,
nodes,
});
}
}
result.push(SplitEvent {
parent_actor_index: evt.parent_actor_index,
children,
});
}
result
}
pub fn get_excess_forces(&self, actor_index: u32, com: Vec3) -> Option<(Vec3, Vec3)> {
let mut force = Vec3::ZERO;
let mut torque = Vec3::ZERO;
let ok = unsafe {
ffi::ext_stress_solver_get_excess_forces(
self.handle,
actor_index,
&com,
&mut force,
&mut torque,
)
};
if ok != 0 {
Some((force, torque))
} else {
None
}
}
}
impl Drop for ExtStressSolver {
fn drop(&mut self) {
unsafe { ffi::ext_stress_solver_destroy(self.handle) }
}
}
fn to_ffi_settings(s: &SolverSettings) -> ffi::FfiExtStressSolverSettingsDesc {
ffi::FfiExtStressSolverSettingsDesc {
max_solver_iterations_per_frame: s.max_solver_iterations_per_frame,
graph_reduction_level: s.graph_reduction_level,
compression_elastic_limit: s.compression_elastic_limit,
compression_fatal_limit: s.compression_fatal_limit,
tension_elastic_limit: s.tension_elastic_limit,
tension_fatal_limit: s.tension_fatal_limit,
shear_elastic_limit: s.shear_elastic_limit,
shear_fatal_limit: s.shear_fatal_limit,
}
}