mod record;
mod selection;
use std::fmt;
use std::mem::MaybeUninit;
use oxijolt_sys::*;
pub use selection::BodySelection;
use crate::world::WorldTag;
use crate::{CharacterState, PhysicsWorld, StateError};
#[derive(Clone)]
pub struct WorldState {
world: Option<WorldTag>,
epoch: u64,
body_count: u32,
constraint_count: u32,
character_ids: Vec<u32>,
jolt: Vec<MaybeUninit<u8>>,
characters: Vec<CharacterState>,
cache_invalidations: Vec<u32>,
selection: Vec<u32>,
}
impl WorldState {
pub fn new() -> Self {
Self {
world: None,
epoch: 0,
body_count: 0,
constraint_count: 0,
character_ids: Vec::new(),
jolt: Vec::new(),
characters: Vec::new(),
cache_invalidations: Vec::new(),
selection: Vec::new(),
}
}
pub fn data_size(&self) -> usize {
self.jolt.len()
}
}
impl Default for WorldState {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for WorldState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WorldState")
.field("epoch", &self.epoch)
.field("body_count", &self.body_count)
.field("constraint_count", &self.constraint_count)
.field("characters", &self.character_ids.len())
.field("jolt_bytes", &self.jolt.len())
.field("cache_invalidations", &self.cache_invalidations.len())
.field("saved", &self.world.is_some())
.finish_non_exhaustive()
}
}
impl PhysicsWorld {
pub fn save_state(&self) -> WorldState {
let mut state = WorldState::new();
self.record_into(None, &mut state.jolt);
self.save_world_parts_into(&mut state);
state
}
pub fn save_state_of(&self, bodies: BodySelection<'_>) -> Result<WorldState, StateError> {
let mut state = WorldState::new();
self.save_state_into(bodies, &mut state)?;
Ok(state)
}
pub fn save_state_into(
&self,
bodies: BodySelection<'_>,
state: &mut WorldState,
) -> Result<(), StateError> {
if let BodySelection::All = bodies {
state.selection.clear();
state.selection.reserve(self.body_count() as usize);
}
let listed = self.select_bodies(bodies, &mut state.selection)?;
self.record_into(listed, &mut state.jolt);
self.save_world_parts_into(state);
Ok(())
}
pub fn restore_state(&mut self, state: &WorldState) -> Result<(), StateError> {
self.check_restorable(state)?;
self.restore_checked(state)
}
pub fn restore_state_of(
&mut self,
state: &WorldState,
bodies: BodySelection<'_>,
) -> Result<(), StateError> {
self.check_restorable(state)?;
let mut selection = Vec::new();
let Some(selected) = self.select_bodies(bodies, &mut selection)? else {
return self.restore_checked(state);
};
let mut kept = Vec::new();
self.unselected_body_ids_into(selected, &mut kept);
let mut current = Vec::new();
self.record_bodies_into(&kept, &mut current);
self.restore_checked(state)?;
let restored = unsafe { self.restore_jolt(¤t) };
debug_assert!(restored, "the unselected bodies failed to restore");
if restored {
Ok(())
} else {
Err(StateError::RestoreFailed)
}
}
fn check_restorable(&self, state: &WorldState) -> Result<(), StateError> {
if state.world != Some(self.tag) {
return Err(StateError::WrongWorld);
}
let same_characters = self.characters.keys().eq(state.character_ids.iter());
if state.epoch != self.structure_epoch
|| state.body_count != self.body_count()
|| state.constraint_count != self.jolt_constraint_count()
|| !same_characters
{
return Err(StateError::WorldChanged);
}
Ok(())
}
fn restore_checked(&mut self, state: &WorldState) -> Result<(), StateError> {
let ids: Vec<_> = self.character_ids().collect();
for (id, character_state) in ids.into_iter().zip(&state.characters) {
let restored = self
.character_mut(id)
.map_err(|_| ())
.and_then(|mut character| character.restore_state(character_state).map_err(|_| ()));
if restored.is_err() {
return Err(StateError::RestoreFailed);
}
self.retain_contact_materials(id);
}
let restored = unsafe { self.restore_jolt(&state.jolt) };
debug_assert!(restored, "a saved world state failed to restore");
if restored {
self.pending_cache_invalidations = state.cache_invalidations.iter().copied().collect();
Ok(())
} else {
Err(StateError::RestoreFailed)
}
}
fn jolt_constraint_count(&self) -> u32 {
unsafe { JPH_PhysicsSystem_GetNumConstraints(self.system.as_ptr()) }
}
fn save_world_parts_into(&self, state: &mut WorldState) {
state.world = Some(self.tag);
state.epoch = self.structure_epoch;
state.body_count = self.body_count();
state.constraint_count = self.jolt_constraint_count();
state.character_ids.clear();
state.character_ids.extend(self.characters.keys());
for (index, id) in self.character_ids().enumerate() {
let character = self
.character(id)
.unwrap_or_else(|_| unreachable!("listed by the world"));
match state.characters.get_mut(index) {
Some(saved) => character.save_state_into(saved),
None => state.characters.push(character.save_state()),
}
}
state.cache_invalidations.clear();
state
.cache_invalidations
.extend(&self.pending_cache_invalidations);
}
}