dynamis-world 0.7.0

Host scene registry, step orchestration, and readback
Documentation
use super::World;
use super::rows::{RowJournal, RowMap};
use dynamis_abi::{
    BodyEditRecord, BodyEditRunRecord, BodyStateRecord, ConstraintRuntimeRecord, RowMoveRecord,
};

#[derive(Clone, Copy)]
pub(crate) enum ConstraintCommand {
    Add { slot: u32, id: u32, generation: u32 },
    Swap { slot: u32, tail: u32 },
}

#[derive(Clone, Copy)]
pub(crate) enum BodyCommand {
    Add {
        row: u32,
        state: BodyStateRecord,
    },
    Remove {
        hole: u32,
        tail: u32,
    },
    Swap {
        first: u32,
        second: u32,
    },
    Patch {
        row: u32,
        mask: u32,
        state: BodyStateRecord,
    },
    Force {
        row: u32,
        force: [f32; 3],
    },
    ForceAtPoint {
        row: u32,
        force: [f32; 3],
        point: [f32; 3],
    },
    Torque {
        row: u32,
        torque: [f32; 3],
    },
    Impulse {
        row: u32,
        impulse: [f32; 3],
    },
    ImpulseAtPoint {
        row: u32,
        impulse: [f32; 3],
        point: [f32; 3],
    },
    AngularImpulse {
        row: u32,
        impulse: [f32; 3],
    },
    Sleep {
        row: u32,
    },
    Wake {
        row: u32,
    },
}

impl BodyCommand {
    pub(crate) fn row(&self) -> u32 {
        match *self {
            Self::Add { row, .. }
            | Self::Patch { row, .. }
            | Self::Force { row, .. }
            | Self::ForceAtPoint { row, .. }
            | Self::Torque { row, .. }
            | Self::Impulse { row, .. }
            | Self::ImpulseAtPoint { row, .. }
            | Self::AngularImpulse { row, .. }
            | Self::Sleep { row }
            | Self::Wake { row } => row,
            Self::Remove { hole, .. } => hole,
            Self::Swap { first, .. } => first,
        }
    }

    fn edit(&self) -> Option<BodyEditRecord> {
        Some(match *self {
            Self::Patch { mask, state, .. } => BodyEditRecord::patch(mask, state),
            Self::Force { force, .. } => BodyEditRecord::force(force),
            Self::ForceAtPoint { force, point, .. } => BodyEditRecord::force_at_point(force, point),
            Self::Torque { torque, .. } => BodyEditRecord::torque(torque),
            Self::Impulse { impulse, .. } => BodyEditRecord::impulse(impulse),
            Self::ImpulseAtPoint { impulse, point, .. } => {
                BodyEditRecord::impulse_at_point(impulse, point)
            }
            Self::AngularImpulse { impulse, .. } => BodyEditRecord::angular_impulse(impulse),
            Self::Sleep { .. } => BodyEditRecord::sleep(),
            Self::Wake { .. } => BodyEditRecord::wake(),
            Self::Add { .. } | Self::Remove { .. } | Self::Swap { .. } => return None,
        })
    }
}

pub(crate) struct CompiledBodyCommands {
    pub(crate) moves: Vec<RowMoveRecord>,
    pub(crate) fresh: Vec<BodyStateRecord>,
    pub(crate) edits: Vec<BodyEditRecord>,
    pub(crate) runs: Vec<BodyEditRunRecord>,
}

pub(crate) struct CompiledConstraintCommands {
    pub(crate) moves: Vec<RowMoveRecord>,
    pub(crate) fresh: Vec<ConstraintRuntimeRecord>,
}

impl World {
    pub(crate) fn compile_body_commands(&self) -> CompiledBodyCommands {
        let mut map = RowMap::new();
        let mut journal: RowJournal<BodyCommand> = RowJournal::new();
        let mut fresh: Vec<BodyStateRecord> = Vec::new();
        for command in &self.bodies.commands {
            match command {
                BodyCommand::Add { row, state } => {
                    map.add(*row, fresh.len() as u32);
                    fresh.push(*state);
                }
                BodyCommand::Remove { hole, tail } => map.remove(*hole, *tail),
                BodyCommand::Swap { first, second } => map.swap(*first, *second),
                _ => journal.push(map.identity_of(command.row()), *command),
            }
        }
        let rows = self.bodies.alive.len() as u32;
        let moves = map.moves(rows);
        let mut edits = Vec::new();
        let mut runs = Vec::new();
        for (identity, commands) in journal.iter() {
            let Some(row) = map.row_of(identity) else {
                continue;
            };
            assert!(row < rows, "an edit run escapes the live rows");
            let first = edits.len();
            for command in commands {
                if let Some(edit) = command.edit() {
                    edits.push(edit);
                }
            }
            if edits.len() > first {
                runs.push(BodyEditRunRecord::new(
                    row as usize,
                    first,
                    edits.len() - first,
                ));
            }
        }
        CompiledBodyCommands {
            moves,
            fresh,
            edits,
            runs,
        }
    }

    pub(crate) fn compile_constraint_commands(&self) -> CompiledConstraintCommands {
        let mut map = RowMap::new();
        let mut fresh: Vec<ConstraintRuntimeRecord> = Vec::new();
        for command in &self.constraints.commands {
            match *command {
                ConstraintCommand::Add {
                    slot,
                    id,
                    generation,
                } => {
                    map.add(slot, fresh.len() as u32);
                    fresh.push(ConstraintRuntimeRecord::fresh(id, generation));
                }
                ConstraintCommand::Swap { slot, tail } => map.swap(slot, tail),
            }
        }
        CompiledConstraintCommands {
            moves: map.moves(self.constraints.alive.len() as u32),
            fresh,
        }
    }
}