Compare commits

..

2 Commits

Author SHA1 Message Date
ce50fcc082 the more 2026-02-26 17:27:44 -08:00
c830e67ab7 wip 2026-02-26 10:30:52 -08:00
10 changed files with 129 additions and 172 deletions

2
Cargo.lock generated
View File

@@ -3873,7 +3873,7 @@ dependencies = [
[[package]]
name = "strafesnet_common"
version = "0.8.5"
version = "0.8.3"
dependencies = [
"arrayvec",
"bitflags 2.10.0",

View File

@@ -50,7 +50,7 @@ fixed_wide = { version = "0.2.2", path = "lib/fixed_wide", registry = "strafesne
linear_ops = { version = "0.1.1", path = "lib/linear_ops", registry = "strafesnet" }
ratio_ops = { version = "0.1.0", path = "lib/ratio_ops", registry = "strafesnet" }
strafesnet_bsp_loader = { path = "lib/bsp_loader", registry = "strafesnet" }
strafesnet_common = { version = "0.8.5", path = "lib/common", registry = "strafesnet" }
strafesnet_common = { version = "0.8.3", path = "lib/common", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.1", path = "lib/deferred_loader", registry = "strafesnet" }
strafesnet_rbx_loader = { path = "lib/rbx_loader", registry = "strafesnet" }
strafesnet_snf = { version = "0.3.2", path = "lib/snf", registry = "strafesnet" }

View File

@@ -1,6 +1,6 @@
[package]
name = "strafesnet_common"
version = "0.8.5"
version = "0.8.3"
edition = "2024"
repository = "https://git.itzana.me/StrafesNET/strafe-project"
license = "MIT OR Apache-2.0"

View File

@@ -56,14 +56,6 @@ impl<T> Time<T>{
pub const fn coerce<U>(self)->Time<U>{
Time::raw(self.0)
}
#[inline]
pub const fn div_euclid(self,other:Self)->Self{
Time::raw(self.0.div_euclid(other.0))
}
#[inline]
pub const fn rem_euclid(self,other:Self)->Self{
Time::raw(self.0.rem_euclid(other.0))
}
}
impl<T> From<Planar64> for Time<T>{
#[inline]

View File

@@ -144,38 +144,10 @@ impl MeshBuilder{
}
pub fn build(
self,
mut polygon_groups:Vec<PolygonGroup>,
mut graphics_groups:Vec<IndexedGraphicsGroup>,
mut physics_groups:Vec<IndexedPhysicsGroup>,
polygon_groups:Vec<PolygonGroup>,
graphics_groups:Vec<IndexedGraphicsGroup>,
physics_groups:Vec<IndexedPhysicsGroup>,
)->Mesh{
let mut id=0;
let mut map=Vec::new();
polygon_groups.retain(|group|{
let keep=match group{
PolygonGroup::PolygonList(polygon_list)=>!polygon_list.0.is_empty(),
};
if keep{
map.push(Some(PolygonGroupId::new(id)));
id+=1;
}else{
map.push(None);
}
!keep
});
for group in &mut graphics_groups{
// drop empty groups
group.groups.retain(|polygon_group_id|map[polygon_group_id.get() as usize].is_some());
for polygon_group_id in &mut group.groups{
*polygon_group_id=map[polygon_group_id.get() as usize].unwrap();
}
}
for group in &mut physics_groups{
// drop empty groups
group.groups.retain(|polygon_group_id|map[polygon_group_id.get() as usize].is_some());
for polygon_group_id in &mut group.groups{
*polygon_group_id=map[polygon_group_id.get() as usize].unwrap();
}
}
let MeshBuilder{
unique_pos,
unique_normal,

View File

@@ -69,8 +69,10 @@ impl<In,Out> Scaled<In,Out>
const fn get_scale(&self)->Ratio64{
self.scale
}
const fn set_scale(&mut self,new_scale:Ratio64){
fn set_scale(&mut self,time:Time<In>,new_scale:Ratio64){
let new_time=self.get_time(time);
self.scale=new_scale;
self.set_time(time,new_time);
}
}
@@ -130,7 +132,7 @@ pub struct TimerFixed<T:TimerState,P:PauseState>{
_paused:P,
}
//some scaled timer methods are generic across PauseState
//scaled timer methods are generic across PauseState
impl<P:PauseState,In,Out> TimerFixed<Scaled<In,Out>,P>
where Time<In>:Copy,
{
@@ -145,22 +147,8 @@ impl<P:PauseState,In,Out> TimerFixed<Scaled<In,Out>,P>
pub const fn get_scale(&self)->Ratio64{
self.state.get_scale()
}
}
// setting the scale of an unpaused timer is different than a paused timer
impl<In,Out> TimerFixed<Scaled<In,Out>,Unpaused>
where Time<In>:Copy,
{
pub fn set_scale(&mut self,time:Time<In>,new_scale:Ratio64){
let new_time=self.state.get_time(time);
self.state.set_scale(new_scale);
self.state.set_time(time,new_time);
}
}
impl<In,Out> TimerFixed<Scaled<In,Out>,Paused>
where Time<In>:Copy,
{
pub fn set_scale(&mut self,new_scale:Ratio64){
self.state.set_scale(new_scale);
self.state.set_scale(time,new_scale)
}
}
@@ -317,7 +305,7 @@ impl<In,Out> Timer<Scaled<In,Out>>
}
pub fn set_scale(&mut self,time:Time<In>,new_scale:Ratio64){
match self{
Self::Paused(timer)=>timer.set_scale(new_scale),
Self::Paused(timer)=>timer.set_scale(time,new_scale),
Self::Unpaused(timer)=>timer.set_scale(time,new_scale),
}
}

View File

@@ -81,6 +81,15 @@ pub struct SegmentId(u32);
pub struct Segment{
pub instructions:Vec<TimedPhysicsInstruction>
}
impl Segment{
fn append<R:BinReaderExt>(&mut self,segment_info:&SegmentInfo,mut data:R)->Result<(),Error>{
for _ in 0..segment_info.instruction_count{
let instruction:newtypes::physics::TimedInstruction=data.read_le().map_err(Error::InvalidSegment)?;
self.instructions.push(instruction.try_into().map_err(Error::SegmentConvert)?);
}
Ok(())
}
}
#[derive(Clone,Copy,Debug)]
pub struct SegmentInfo{
@@ -94,14 +103,13 @@ pub struct SegmentInfo{
block_id:BlockId,
}
pub struct StreamableBot<R:BinReaderExt>{
file:crate::file::File<R>,
pub struct StreamableBot{
segment_map:Vec<SegmentInfo>,
}
impl<R:BinReaderExt> StreamableBot<R>{
pub(crate) fn new(mut file:crate::file::File<R>)->Result<Self,Error>{
impl StreamableBot{
pub(crate) fn new<R:BinReaderExt>(mut data:R)->Result<Self,Error>{
//assume the file seek is in the right place to start reading header
let header:Header=file.data_mut().read_le().map_err(Error::InvalidHeader)?;
let header:Header=data.read_le().map_err(Error::InvalidHeader)?;
let mut instructions_subtotal=0;
let segment_map=header.segments.into_iter().map(|SegmentHeader{time,instruction_count,block_id}|{
instructions_subtotal+=instruction_count as u64;
@@ -113,13 +121,12 @@ impl<R:BinReaderExt> StreamableBot<R>{
}
}).collect();
Ok(Self{
file,
segment_map,
})
}
#[expect(dead_code)]
fn get_segment_info(&self,segment_id:SegmentId)->Result<SegmentInfo,Error>{
Ok(*self.segment_map.get(segment_id.get() as usize).ok_or(Error::InvalidSegmentId(segment_id))?)
fn get_segment_info(&self,segment_id:SegmentId)->Result<&SegmentInfo,Error>{
self.segment_map.get(segment_id.get() as usize).ok_or(Error::InvalidSegmentId(segment_id))
}
pub fn find_segments_instruction_range(&self,start_instruction:u64,end_instruction:u64)->&[SegmentInfo]{
let start=self.segment_map.partition_point(|segment_info|segment_info.instructions_subtotal<start_instruction);
@@ -132,28 +139,21 @@ impl<R:BinReaderExt> StreamableBot<R>{
// let end=self.segment_map.partition_point(|segment_info|segment_info.time<end_time);
// &self.segment_map[start..=end]
// }
fn append_to_segment(&mut self,segment_info:SegmentInfo,segment:&mut Segment)->Result<(),Error>{
let mut block=self.file.block_reader(segment_info.block_id).map_err(Error::File)?;
for _ in 0..segment_info.instruction_count{
let instruction:newtypes::physics::TimedInstruction=block.read_le().map_err(Error::InvalidSegment)?;
segment.instructions.push(instruction.try_into().map_err(Error::SegmentConvert)?);
}
Ok(())
}
pub fn load_segment(&mut self,segment_info:SegmentInfo)->Result<Segment,Error>{
pub fn load_segment<R:BinReaderExt>(&self,segment_info:&SegmentInfo,mut data:R)->Result<Segment,Error>{
let mut segment=Segment{
instructions:Vec::with_capacity(segment_info.instruction_count as usize),
};
self.append_to_segment(segment_info,&mut segment)?;
segment.append(segment_info,data)?;
Ok(segment)
}
pub fn read_all(&mut self)->Result<Segment,Error>{
pub fn read_all<R:BinReaderExt>(self,header:&crate::file::Header,mut data:R)->Result<Segment,Error>{
let mut segment=Segment{
instructions:Vec::new(),
};
for i in 0..self.segment_map.len(){
let segment_info=self.segment_map[i];
self.append_to_segment(segment_info,&mut segment)?;
for segment_info in &self.segment_map{
let block_info=header.block_info(segment_info.block_id).map_err(Error::File)?;
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
segment.append(segment_info,block)?;
}
Ok(segment)
}

View File

@@ -1,6 +1,8 @@
//file format "sniff"
use binrw::{binrw,BinReaderExt,io::TakeSeekExt};
use std::io::{SeekFrom,Error as IoError};
use binrw::binrw;
use binrw::BinReaderExt;
use binrw::io::{TakeSeek,TakeSeekExt};
#[derive(Debug)]
pub enum Error{
@@ -54,65 +56,69 @@ pub(crate) enum FourCC{
#[brw(magic=b"SNFB")]
Bot,
}
#[binrw]
#[brw(little)]
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)]
pub struct BlockId(u32);
#[binrw]
#[brw(little)]
#[derive(Debug,Clone)]
struct BlockPosition(u64);
/// The range of data for a specific Block, relative to the start of the file.
#[derive(Debug,Clone)]
pub struct BlockInfo(core::ops::Range<u64>);
impl BlockInfo{
/// Create an adapter which seeks to the block start and reads at most the block length.
pub fn take_seek<R:BinReaderExt>(&self,mut data:R)->Result<TakeSeek<R>,IoError>{
data.seek(SeekFrom::Start(self.start))?;
Ok(data.take_seek(self.end-self.start))
}
}
impl core::ops::Deref for BlockInfo{
type Target=core::ops::Range<u64>;
fn deref(&self)->&Self::Target{
&self.0
}
}
#[binrw]
#[brw(little)]
#[derive(Debug)]
pub struct Header{
/// Type of file
pub fourcc:FourCC,
fourcc:FourCC,
/// File format version
pub version:u32,
version:u32,
/// Minimum data required to know the location of all streamable resources for this specific file
pub priming:u64,
priming:u64,
/// uuid for this file
pub resource:u128,
resource:u128,
//don't need try_calc: the struct will force field initialization anyways and it can be calculated there
pub block_count:u32,
block_count:u32,
#[br(count=block_count+1)]
pub block_location:Vec<u64>,
block_positions:Vec<BlockPosition>,
}
impl Header{
pub const fn fourcc(&self)->FourCC{
self.fourcc
}
pub const fn calculate_size(block_count:u32)->usize{
4 // fourcc
+4 // version
+8 // priming
+16 // resource
+4 // block_count
+(block_count as usize+1)*8 // block_location
+(block_count as usize+1)*8 // block_positions
}
}
#[binrw]
#[brw(little)]
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)]
pub struct BlockId(u32);
pub(crate) struct File<R:BinReaderExt>{
header:Header,
//reference to the data
data:R,
}
impl<R:BinReaderExt> File<R>{
pub(crate) fn new(mut input:R)->Result<File<R>,Error>{
Ok(File{
header:input.read_le().map_err(Error::InvalidHeader)?,
data:input,
})
}
pub(crate) fn data_mut(&mut self)->&mut R{
&mut self.data
}
pub(crate) fn block_reader(&mut self,block_id:BlockId)->Result<binrw::io::TakeSeek<&mut R>,Error>{
if self.header.block_location.len() as u32<=block_id.get(){
pub fn block_info(&self,block_id:BlockId)->Result<BlockInfo,Error>{
let BlockId(id)=block_id;
if self.block_positions.len() as u32<=id{
return Err(Error::InvalidBlockId(block_id))
}
let block_start=self.header.block_location[block_id.get() as usize];
let block_end=self.header.block_location[block_id.get() as usize+1];
self.data.seek(std::io::SeekFrom::Start(block_start)).map_err(Error::Seek)?;
Ok(self.data_mut().take_seek(block_end-block_start))
}
pub(crate) fn fourcc(&self)->FourCC{
self.header.fourcc
let BlockPosition(start)=self.block_positions[id as usize];
let BlockPosition(end)=self.block_positions[id as usize+1];
Ok(BlockInfo(start..end))
}
}

View File

@@ -20,31 +20,34 @@ impl std::fmt::Display for Error{
}
impl std::error::Error for Error{}
pub enum SNF<R:BinReaderExt>{
Map(map::StreamableMap<R>),
Bot(bot::StreamableBot<R>),
pub enum SNF{
Map(map::StreamableMap),
Bot(bot::StreamableBot),
}
pub fn read_snf<R:BinReaderExt>(input:R)->Result<SNF<R>,Error>{
let file=file::File::new(input).map_err(Error::Header)?;
Ok(match file.fourcc(){
file::FourCC::Map=>SNF::Map(map::StreamableMap::new(file).map_err(Error::Map)?),
file::FourCC::Bot=>SNF::Bot(bot::StreamableBot::new(file).map_err(Error::Bot)?),
})
pub fn read_snf<R:BinReaderExt>(mut input:R)->Result<(file::Header,SNF),Error>{
let file:file::Header=input.read_le().map_err(|e|Error::Header(file::Error::InvalidHeader(e)))?;
let snf=match file.fourcc(){
file::FourCC::Map=>SNF::Map(map::StreamableMap::new(input).map_err(Error::Map)?),
file::FourCC::Bot=>SNF::Bot(bot::StreamableBot::new(input).map_err(Error::Bot)?),
};
Ok((file,snf))
}
pub fn read_map<R:BinReaderExt>(input:R)->Result<map::StreamableMap<R>,Error>{
let file=file::File::new(input).map_err(Error::Header)?;
match file.fourcc(){
file::FourCC::Map=>Ok(map::StreamableMap::new(file).map_err(Error::Map)?),
_=>Err(Error::UnexpectedFourCC)
}
pub fn read_map<R:BinReaderExt>(mut input:R)->Result<(file::Header,map::StreamableMap),Error>{
let file:file::Header=input.read_le().map_err(|e|Error::Header(file::Error::InvalidHeader(e)))?;
let map=match file.fourcc(){
file::FourCC::Map=>map::StreamableMap::new(input).map_err(Error::Map)?,
_=>return Err(Error::UnexpectedFourCC)
};
Ok((file,map))
}
pub fn read_bot<R:BinReaderExt>(input:R)->Result<bot::StreamableBot<R>,Error>{
let file=file::File::new(input).map_err(Error::Header)?;
match file.fourcc(){
file::FourCC::Bot=>Ok(bot::StreamableBot::new(file).map_err(Error::Bot)?),
_=>Err(Error::UnexpectedFourCC)
}
pub fn read_bot<R:BinReaderExt>(mut input:R)->Result<(file::Header,bot::StreamableBot),Error>{
let file:file::Header=input.read_le().map_err(|e|Error::Header(file::Error::InvalidHeader(e)))?;
let bot=match file.fourcc(){
file::FourCC::Bot=>bot::StreamableBot::new(input).map_err(Error::Bot)?,
_=>return Err(Error::UnexpectedFourCC)
};
Ok((file,bot))
}
#[cfg(test)]

View File

@@ -155,31 +155,27 @@ struct Region{
}
//code deduplication reused in into_complete_map
fn read_region<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<(model::ModelId,model::Model)>,Error>{
pub fn read_region<R:BinReaderExt>(mut data:R)->Result<Vec<(model::ModelId,model::Model)>,Error>{
//load region from disk
//parse the models and determine what resources need to be loaded
//load resources into self.resources
//return Region
let mut block=file.block_reader(block_id).map_err(Error::File)?;
let region:Region=block.read_le().map_err(Error::InvalidData)?;
let region:Region=data.read_le().map_err(Error::InvalidData)?;
Ok(region.models.into_iter().map(|(model_id,model)|
(model::ModelId::new(model_id),model.into())
).collect())
}
fn read_mesh<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<model::Mesh,Error>{
let mut block=file.block_reader(block_id).map_err(Error::File)?;
let mesh:newtypes::model::Mesh=block.read_le().map_err(Error::InvalidData)?;
pub fn read_mesh<R:BinReaderExt>(mut data:R)->Result<model::Mesh,Error>{
let mesh:newtypes::model::Mesh=data.read_le().map_err(Error::InvalidData)?;
Ok(mesh.into())
}
fn read_texture<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<u8>,Error>{
let mut block=file.block_reader(block_id).map_err(Error::File)?;
pub fn read_texture<R:BinReaderExt>(mut data:R)->Result<Vec<u8>,Error>{
let mut texture=Vec::new();
block.read_to_end(&mut texture).map_err(Error::IO)?;
data.read_to_end(&mut texture).map_err(Error::IO)?;
Ok(texture)
}
pub struct StreamableMap<R:BinReaderExt>{
file:crate::file::File<R>,
pub struct StreamableMap{
//this includes every platform... move the unconstrained datas to their appropriate data block?
modes:gameplay_modes::NormalizedModes,
//this is every possible attribute... need some sort of streaming system
@@ -192,10 +188,10 @@ pub struct StreamableMap<R:BinReaderExt>{
resource_blocks:ResourceMap<BlockId>,
//resource_external:ResourceMap<ResourceId>,
}
impl<R:BinReaderExt> StreamableMap<R>{
pub(crate) fn new(mut file:crate::file::File<R>)->Result<Self,Error>{
impl StreamableMap{
pub(crate) fn new<R:BinReaderExt>(mut data:R)->Result<Self,Error>{
//assume the file seek is in the right place to start reading a map header
let header:MapHeader=file.data_mut().read_le().map_err(Error::InvalidHeader)?;
let header:MapHeader=data.read_le().map_err(Error::InvalidHeader)?;
let modes=header.modes.into_iter().map(TryInto::try_into).collect::<Result<_,_>>().map_err(Error::InvalidMode)?;
let attributes=header.attributes.into_iter().map(Into::into).collect();
let render_configs=header.render_configs.into_iter().map(Into::into).collect();
@@ -222,7 +218,6 @@ impl<R:BinReaderExt> StreamableMap<R>{
}
}
Ok(Self{
file,
modes:gameplay_modes::NormalizedModes::new(modes),
attributes,
render_configs,
@@ -236,18 +231,13 @@ impl<R:BinReaderExt> StreamableMap<R>{
self.bvh.sample_aabb(aabb,&mut |&block_id|block_ids.push(block_id));
block_ids
}
pub fn load_region(&mut self,block_id:BlockId)->Result<Vec<(model::ModelId,model::Model)>,Error>{
read_region(&mut self.file,block_id)
pub fn get_mesh_block_id<R:BinReaderExt>(&self,mesh_id:model::MeshId)->Result<&BlockId,Error>{
self.resource_blocks.meshes.get(&mesh_id).ok_or(Error::InvalidMeshId(mesh_id))
}
pub fn load_mesh(&mut self,mesh_id:model::MeshId)->Result<model::Mesh,Error>{
let block_id=*self.resource_blocks.meshes.get(&mesh_id).ok_or(Error::InvalidMeshId(mesh_id))?;
read_mesh(&mut self.file,block_id)
pub fn get_texture_block_id<R:BinReaderExt>(&self,texture_id:model::TextureId)->Result<&BlockId,Error>{
self.resource_blocks.textures.get(&texture_id).ok_or(Error::InvalidTextureId(texture_id))
}
pub fn load_texture(&mut self,texture_id:model::TextureId)->Result<Vec<u8>,Error>{
let block_id=*self.resource_blocks.textures.get(&texture_id).ok_or(Error::InvalidTextureId(texture_id))?;
read_texture(&mut self.file,block_id)
}
pub fn into_complete_map(mut self)->Result<strafesnet_common::map::CompleteMap,Error>{
pub fn into_complete_map<R:BinReaderExt>(self,header:&crate::file::Header,mut data:R)->Result<strafesnet_common::map::CompleteMap,Error>{
let mut block_ids=Vec::new();
self.bvh.into_visitor(&mut |block_id|block_ids.push(block_id));
//count on reading the file in sequential order being fastest
@@ -255,7 +245,9 @@ impl<R:BinReaderExt> StreamableMap<R>{
//load all regions
let mut model_pairs=HashMap::new();
for block_id in block_ids{
model_pairs.extend(read_region(&mut self.file,block_id)?);
let block_info=header.block_info(block_id).map_err(Error::File)?;
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
model_pairs.extend(read_region(block)?);
}
let mut models=Vec::with_capacity(model_pairs.len());
for model_id in 0..model_pairs.len() as u32{
@@ -267,14 +259,18 @@ impl<R:BinReaderExt> StreamableMap<R>{
for mesh_id in 0..self.resource_blocks.meshes.len() as u32{
let mesh_id=model::MeshId::new(mesh_id);
let block_id=self.resource_blocks.meshes[&mesh_id];
meshes.push(read_mesh(&mut self.file,block_id)?);
let block_info=header.block_info(block_id).map_err(Error::File)?;
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
meshes.push(read_mesh(block)?);
};
//load all textures
let mut textures=Vec::with_capacity(self.resource_blocks.textures.len());
for texture_id in 0..self.resource_blocks.textures.len() as u32{
let texture_id=model::TextureId::new(texture_id);
let block_id=self.resource_blocks.textures[&texture_id];
textures.push(read_texture(&mut self.file,block_id)?);
let block_info=header.block_info(block_id).map_err(Error::File)?;
let block=block_info.take_seek(&mut data).map_err(Error::IO)?;
textures.push(read_texture(block)?);
}
Ok(strafesnet_common::map::CompleteMap{
modes:self.modes,