Unquatification #8

Open
interpreterK wants to merge 1 commits from interpreterK/strafe-project:Unquatification into master
88 changed files with 18282 additions and 14619 deletions

View File

@ -1,127 +1,150 @@
use strafesnet_common::{map,model,integer,gameplay_attributes};
use strafesnet_common::{gameplay_attributes, integer, map, model};
const VALVE_SCALE:f32=1.0/16.0;
fn valve_transform([x,y,z]:[f32;3])->integer::Planar64Vec3{
integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
const VALVE_SCALE: f32 = 1.0 / 16.0;
fn valve_transform([x, y, z]: [f32; 3]) -> integer::Planar64Vec3 {
integer::vec3::try_from_f32_array([x * VALVE_SCALE, z * VALVE_SCALE, -y * VALVE_SCALE]).unwrap()
}
pub fn convert_bsp<AcquireRenderConfigId,AcquireMeshId>(
bsp:&vbsp::Bsp,
mut acquire_render_config_id:AcquireRenderConfigId,
mut acquire_mesh_id:AcquireMeshId
)->PartialMap1
pub fn convert_bsp<AcquireRenderConfigId, AcquireMeshId>(
bsp: &vbsp::Bsp,
mut acquire_render_config_id: AcquireRenderConfigId,
mut acquire_mesh_id: AcquireMeshId,
) -> PartialMap1
where
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
AcquireMeshId:FnMut(&str)->model::MeshId,
AcquireRenderConfigId: FnMut(Option<&str>) -> model::RenderConfigId,
AcquireMeshId: FnMut(&str) -> model::MeshId,
{
//figure out real attributes later
let mut unique_attributes=Vec::new();
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
const TEMP_TOUCH_ME_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0);
//figure out real attributes later
let mut unique_attributes = Vec::new();
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
const TEMP_TOUCH_ME_ATTRIBUTE: gameplay_attributes::CollisionAttributesId =
gameplay_attributes::CollisionAttributesId::new(0);
let mut prop_mesh_count=0;
//declare all prop models to Loader
let prop_models=bsp.static_props().map(|prop|{
//get or create mesh_id
let mesh_id=acquire_mesh_id(prop.model());
//not the most failsafe code but this is just for the map tool lmao
if prop_mesh_count==mesh_id.get(){
prop_mesh_count+=1;
};
let placement=prop.as_prop_placement();
model::Model{
mesh:mesh_id,
attributes:TEMP_TOUCH_ME_ATTRIBUTE,
transform:integer::Planar64Affine3::new(
integer::mat3::try_from_f32_array_2d((
glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
let mut prop_mesh_count = 0;
//declare all prop models to Loader
let prop_models = bsp
.static_props()
.map(|prop| {
//get or create mesh_id
let mesh_id = acquire_mesh_id(prop.model());
//not the most failsafe code but this is just for the map tool lmao
if prop_mesh_count == mesh_id.get() {
prop_mesh_count += 1;
};
let placement = prop.as_prop_placement();
model::Model {
mesh: mesh_id,
attributes: TEMP_TOUCH_ME_ATTRIBUTE,
transform: integer::Planar64Affine3::new(
integer::mat3::try_from_f32_array_2d(
(glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
//TODO: figure this out
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
).to_cols_array_2d()).unwrap(),
valve_transform(placement.origin.into()),
),
color:glam::Vec4::ONE,
}
}).collect();
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into())))
.to_cols_array_2d(),
)
.unwrap(),
valve_transform(placement.origin.into()),
),
color: glam::Vec4::ONE,
}
})
.collect();
//TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups
//TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups
//the generated MeshIds in here will collide with the Loader Mesh Ids
//but I can't think of a good workaround other than just remapping one later.
let world_meshes:Vec<model::Mesh>=bsp.models().map(|world_model|{
//non-deduplicated
let mut spam_pos=Vec::new();
let mut spam_tex=Vec::new();
let mut spam_normal=Vec::new();
let mut spam_vertices=Vec::new();
let mut graphics_groups=Vec::new();
let mut physics_group=model::IndexedPhysicsGroup::default();
let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
let face_texture=face.texture();
let face_texture_data=face_texture.texture_data();
//this would be better as a 4x2 matrix
let texture_transform_u=glam::Vec4::from_array(face_texture.texture_transforms_u)/(face_texture_data.width as f32);
let texture_transform_v=glam::Vec4::from_array(face_texture.texture_transforms_v)/(face_texture_data.height as f32);
//the generated MeshIds in here will collide with the Loader Mesh Ids
//but I can't think of a good workaround other than just remapping one later.
let world_meshes: Vec<model::Mesh> = bsp
.models()
.map(|world_model| {
//non-deduplicated
let mut spam_pos = Vec::new();
let mut spam_tex = Vec::new();
let mut spam_normal = Vec::new();
let mut spam_vertices = Vec::new();
let mut graphics_groups = Vec::new();
let mut physics_group = model::IndexedPhysicsGroup::default();
let polygon_groups = world_model
.faces()
.enumerate()
.map(|(polygon_group_id, face)| {
let polygon_group_id = model::PolygonGroupId::new(polygon_group_id as u32);
let face_texture = face.texture();
let face_texture_data = face_texture.texture_data();
//this would be better as a 4x2 matrix
let texture_transform_u =
glam::Vec4::from_array(face_texture.texture_transforms_u)
/ (face_texture_data.width as f32);
let texture_transform_v =
glam::Vec4::from_array(face_texture.texture_transforms_v)
/ (face_texture_data.height as f32);
//this automatically figures out what the texture is trying to do and creates
//a render config for it, and then returns the id to that render config
let render_id=acquire_render_config_id(Some(face_texture_data.name()));
//this automatically figures out what the texture is trying to do and creates
//a render config for it, and then returns the id to that render config
let render_id = acquire_render_config_id(Some(face_texture_data.name()));
//normal
let normal=face.normal();
let normal_idx=spam_normal.len() as u32;
spam_normal.push(valve_transform(normal.into()));
let mut polygon_iter=face.vertex_positions().map(|vertex_position|{
//world_model.origin seems to always be 0,0,0
let vertex_xyz=(world_model.origin+vertex_position).into();
let pos_idx=spam_pos.len();
spam_pos.push(valve_transform(vertex_xyz));
//normal
let normal = face.normal();
let normal_idx = spam_normal.len() as u32;
spam_normal.push(valve_transform(normal.into()));
let mut polygon_iter = face.vertex_positions().map(|vertex_position| {
//world_model.origin seems to always be 0,0,0
let vertex_xyz = (world_model.origin + vertex_position).into();
let pos_idx = spam_pos.len();
spam_pos.push(valve_transform(vertex_xyz));
//calculate texture coordinates
let pos=glam::Vec3::from_array(vertex_xyz).extend(1.0);
let tex=glam::vec2(texture_transform_u.dot(pos),texture_transform_v.dot(pos));
let tex_idx=spam_tex.len() as u32;
spam_tex.push(tex);
//calculate texture coordinates
let pos = glam::Vec3::from_array(vertex_xyz).extend(1.0);
let tex =
glam::vec2(texture_transform_u.dot(pos), texture_transform_v.dot(pos));
let tex_idx = spam_tex.len() as u32;
spam_tex.push(tex);
let vertex_id=model::VertexId::new(spam_vertices.len() as u32);
spam_vertices.push(model::IndexedVertex{
pos:model::PositionId::new(pos_idx as u32),
tex:model::TextureCoordinateId::new(tex_idx as u32),
normal:model::NormalId::new(normal_idx),
color:model::ColorId::new(0),
});
vertex_id
});
let polygon_list=std::iter::from_fn(move||{
match (polygon_iter.next(),polygon_iter.next(),polygon_iter.next()){
(Some(v1),Some(v2),Some(v3))=>Some(vec![v1,v2,v3]),
//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
_=>None,
}
}).collect();
if face.is_visible(){
//TODO: deduplicate graphics groups by render id
graphics_groups.push(model::IndexedGraphicsGroup{
render:render_id,
groups:vec![polygon_group_id],
})
}
physics_group.groups.push(polygon_group_id);
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
}).collect();
model::Mesh{
unique_pos:spam_pos,
unique_tex:spam_tex,
unique_normal:spam_normal,
unique_color:vec![glam::Vec4::ONE],
unique_vertices:spam_vertices,
polygon_groups,
graphics_groups,
physics_groups:vec![physics_group],
}
}).collect();
let vertex_id = model::VertexId::new(spam_vertices.len() as u32);
spam_vertices.push(model::IndexedVertex {
pos: model::PositionId::new(pos_idx as u32),
tex: model::TextureCoordinateId::new(tex_idx as u32),
normal: model::NormalId::new(normal_idx),
color: model::ColorId::new(0),
});
vertex_id
});
let polygon_list = std::iter::from_fn(move || {
match (
polygon_iter.next(),
polygon_iter.next(),
polygon_iter.next(),
) {
(Some(v1), Some(v2), Some(v3)) => Some(vec![v1, v2, v3]),
//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
_ => None,
}
})
.collect();
if face.is_visible() {
//TODO: deduplicate graphics groups by render id
graphics_groups.push(model::IndexedGraphicsGroup {
render: render_id,
groups: vec![polygon_group_id],
})
}
physics_group.groups.push(polygon_group_id);
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
})
.collect();
model::Mesh {
unique_pos: spam_pos,
unique_tex: spam_tex,
unique_normal: spam_normal,
unique_color: vec![glam::Vec4::ONE],
unique_vertices: spam_vertices,
polygon_groups,
graphics_groups,
physics_groups: vec![physics_group],
}
})
.collect();
let world_models:Vec<model::Model>=
let world_models:Vec<model::Model>=
//one instance of the main world mesh
std::iter::once((
//world_model
@ -158,35 +181,35 @@ where
}
}).collect();
PartialMap1{
attributes:unique_attributes,
world_meshes,
prop_models,
world_models,
modes:strafesnet_common::gameplay_modes::Modes::new(Vec::new()),
}
PartialMap1 {
attributes: unique_attributes,
world_meshes,
prop_models,
world_models,
modes: strafesnet_common::gameplay_modes::Modes::new(Vec::new()),
}
}
//partially constructed map types
pub struct PartialMap1{
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>,
modes:strafesnet_common::gameplay_modes::Modes,
pub struct PartialMap1 {
attributes: Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
prop_models: Vec<model::Model>,
world_meshes: Vec<model::Mesh>,
world_models: Vec<model::Model>,
modes: strafesnet_common::gameplay_modes::Modes,
}
impl PartialMap1{
pub fn add_prop_meshes<AcquireRenderConfigId>(
self,
prop_meshes:impl IntoIterator<Item=(model::MeshId,crate::data::ModelData)>,
mut acquire_render_config_id:AcquireRenderConfigId,
)->PartialMap2
where
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
{
PartialMap2{
attributes:self.attributes,
prop_meshes:prop_meshes.into_iter().filter_map(|(mesh_id,model_data)|
impl PartialMap1 {
pub fn add_prop_meshes<AcquireRenderConfigId>(
self,
prop_meshes: impl IntoIterator<Item = (model::MeshId, crate::data::ModelData)>,
mut acquire_render_config_id: AcquireRenderConfigId,
) -> PartialMap2
where
AcquireRenderConfigId: FnMut(Option<&str>) -> model::RenderConfigId,
{
PartialMap2 {
attributes: self.attributes,
prop_meshes: prop_meshes.into_iter().filter_map(|(mesh_id,model_data)|
//this will generate new render ids and texture ids
match convert_mesh(model_data,&mut acquire_render_config_id){
Ok(mesh)=>Some((mesh_id,mesh)),
@ -196,138 +219,175 @@ impl PartialMap1{
}
}
).collect(),
prop_models:self.prop_models,
world_meshes:self.world_meshes,
world_models:self.world_models,
modes:self.modes,
}
}
prop_models: self.prop_models,
world_meshes: self.world_meshes,
world_models: self.world_models,
modes: self.modes,
}
}
}
pub struct PartialMap2{
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
prop_meshes:Vec<(model::MeshId,model::Mesh)>,
prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>,
modes:strafesnet_common::gameplay_modes::Modes,
pub struct PartialMap2 {
attributes: Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
prop_meshes: Vec<(model::MeshId, model::Mesh)>,
prop_models: Vec<model::Model>,
world_meshes: Vec<model::Mesh>,
world_models: Vec<model::Model>,
modes: strafesnet_common::gameplay_modes::Modes,
}
impl PartialMap2{
pub fn add_render_configs_and_textures(
mut self,
render_configs:impl IntoIterator<Item=(model::RenderConfigId,model::RenderConfig)>,
textures:impl IntoIterator<Item=(model::TextureId,Vec<u8>)>,
)->map::CompleteMap{
//merge mesh and model lists, flatten and remap all ids
let mesh_id_offset=self.world_meshes.len();
println!("prop_meshes.len()={}",self.prop_meshes.len());
let (mut prop_meshes,prop_mesh_id_map):(Vec<model::Mesh>,std::collections::HashMap<model::MeshId,model::MeshId>)
=self.prop_meshes.into_iter().enumerate().map(|(new_mesh_id,(old_mesh_id,mesh))|{
(mesh,(old_mesh_id,model::MeshId::new((mesh_id_offset+new_mesh_id) as u32)))
}).unzip();
self.world_meshes.append(&mut prop_meshes);
//there is no modes or runtime behaviour with references to the model ids currently
//so just relentlessly cull them if the mesh is missing
self.world_models.extend(self.prop_models.into_iter().filter_map(|mut model|
prop_mesh_id_map.get(&model.mesh).map(|&new_mesh_id|{
model.mesh=new_mesh_id;
model
})
));
//let mut models=Vec::new();
let (textures,texture_id_map):(Vec<Vec<u8>>,std::collections::HashMap<model::TextureId,model::TextureId>)
=textures.into_iter()
//.filter_map(f) cull unused textures
.enumerate().map(|(new_texture_id,(old_texture_id,texture))|{
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
}).unzip();
let render_configs=render_configs.into_iter().map(|(render_config_id,mut render_config)|{
//this may generate duplicate no-texture render configs but idc
render_config.texture=render_config.texture.and_then(|texture_id|
texture_id_map.get(&texture_id).copied()
);
render_config
}).collect();
map::CompleteMap{
modes:self.modes,
attributes:self.attributes,
meshes:self.world_meshes,
models:self.world_models,
textures,
render_configs,
}
}
impl PartialMap2 {
pub fn add_render_configs_and_textures(
mut self,
render_configs: impl IntoIterator<Item = (model::RenderConfigId, model::RenderConfig)>,
textures: impl IntoIterator<Item = (model::TextureId, Vec<u8>)>,
) -> map::CompleteMap {
//merge mesh and model lists, flatten and remap all ids
let mesh_id_offset = self.world_meshes.len();
println!("prop_meshes.len()={}", self.prop_meshes.len());
let (mut prop_meshes, prop_mesh_id_map): (
Vec<model::Mesh>,
std::collections::HashMap<model::MeshId, model::MeshId>,
) = self
.prop_meshes
.into_iter()
.enumerate()
.map(|(new_mesh_id, (old_mesh_id, mesh))| {
(
mesh,
(
old_mesh_id,
model::MeshId::new((mesh_id_offset + new_mesh_id) as u32),
),
)
})
.unzip();
self.world_meshes.append(&mut prop_meshes);
//there is no modes or runtime behaviour with references to the model ids currently
//so just relentlessly cull them if the mesh is missing
self.world_models
.extend(self.prop_models.into_iter().filter_map(|mut model| {
prop_mesh_id_map.get(&model.mesh).map(|&new_mesh_id| {
model.mesh = new_mesh_id;
model
})
}));
//let mut models=Vec::new();
let (textures, texture_id_map): (
Vec<Vec<u8>>,
std::collections::HashMap<model::TextureId, model::TextureId>,
) = textures
.into_iter()
//.filter_map(f) cull unused textures
.enumerate()
.map(|(new_texture_id, (old_texture_id, texture))| {
(
texture,
(old_texture_id, model::TextureId::new(new_texture_id as u32)),
)
})
.unzip();
let render_configs = render_configs
.into_iter()
.map(|(render_config_id, mut render_config)| {
//this may generate duplicate no-texture render configs but idc
render_config.texture = render_config
.texture
.and_then(|texture_id| texture_id_map.get(&texture_id).copied());
render_config
})
.collect();
map::CompleteMap {
modes: self.modes,
attributes: self.attributes,
meshes: self.world_meshes,
models: self.world_models,
textures,
render_configs,
}
}
}
fn convert_mesh<AcquireRenderConfigId>(
model_data:crate::data::ModelData,
acquire_render_config_id:&mut AcquireRenderConfigId,
)->Result<model::Mesh,vmdl::ModelError>
model_data: crate::data::ModelData,
acquire_render_config_id: &mut AcquireRenderConfigId,
) -> Result<model::Mesh, vmdl::ModelError>
where
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
AcquireRenderConfigId: FnMut(Option<&str>) -> model::RenderConfigId,
{
let model=model_data.read_model()?;
let texture_paths=model.texture_directories();
if texture_paths.len()!=1{
println!("WARNING: multiple texture paths");
}
let skin=model.skin_tables().nth(0).unwrap();
let model = model_data.read_model()?;
let texture_paths = model.texture_directories();
if texture_paths.len() != 1 {
println!("WARNING: multiple texture paths");
}
let skin = model.skin_tables().nth(0).unwrap();
let mut spam_pos=Vec::with_capacity(model.vertices().len());
let mut spam_normal=Vec::with_capacity(model.vertices().len());
let mut spam_tex=Vec::with_capacity(model.vertices().len());
let mut spam_vertices=Vec::with_capacity(model.vertices().len());
for (i,vertex) in model.vertices().iter().enumerate(){
spam_pos.push(valve_transform(vertex.position.into()));
spam_normal.push(valve_transform(vertex.normal.into()));
spam_tex.push(glam::Vec2::from_array(vertex.texture_coordinates));
spam_vertices.push(model::IndexedVertex{
pos:model::PositionId::new(i as u32),
tex:model::TextureCoordinateId::new(i as u32),
normal:model::NormalId::new(i as u32),
color:model::ColorId::new(0),
});
}
let mut graphics_groups=Vec::new();
let mut physics_groups=Vec::new();
let polygon_groups=model.meshes().enumerate().map(|(polygon_group_id,mesh)|{
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
let mut spam_pos = Vec::with_capacity(model.vertices().len());
let mut spam_normal = Vec::with_capacity(model.vertices().len());
let mut spam_tex = Vec::with_capacity(model.vertices().len());
let mut spam_vertices = Vec::with_capacity(model.vertices().len());
for (i, vertex) in model.vertices().iter().enumerate() {
spam_pos.push(valve_transform(vertex.position.into()));
spam_normal.push(valve_transform(vertex.normal.into()));
spam_tex.push(glam::Vec2::from_array(vertex.texture_coordinates));
spam_vertices.push(model::IndexedVertex {
pos: model::PositionId::new(i as u32),
tex: model::TextureCoordinateId::new(i as u32),
normal: model::NormalId::new(i as u32),
color: model::ColorId::new(0),
});
}
let mut graphics_groups = Vec::new();
let mut physics_groups = Vec::new();
let polygon_groups = model
.meshes()
.enumerate()
.map(|(polygon_group_id, mesh)| {
let polygon_group_id = model::PolygonGroupId::new(polygon_group_id as u32);
let render_id=if let (Some(texture_path),Some(texture_name))=(texture_paths.get(0),skin.texture(mesh.material_index())){
let mut path=std::path::PathBuf::from(texture_path.as_str());
path.push(texture_name);
acquire_render_config_id(path.as_os_str().to_str())
}else{
acquire_render_config_id(None)
};
let render_id = if let (Some(texture_path), Some(texture_name)) =
(texture_paths.get(0), skin.texture(mesh.material_index()))
{
let mut path = std::path::PathBuf::from(texture_path.as_str());
path.push(texture_name);
acquire_render_config_id(path.as_os_str().to_str())
} else {
acquire_render_config_id(None)
};
graphics_groups.push(model::IndexedGraphicsGroup{
render:render_id,
groups:vec![polygon_group_id],
});
physics_groups.push(model::IndexedPhysicsGroup{
groups:vec![polygon_group_id],
});
model::PolygonGroup::PolygonList(model::PolygonList::new(
//looking at the code, it would seem that the strips are pre-deindexed into triangle lists when calling this function
mesh.vertex_strip_indices().flat_map(|mut strip|
std::iter::from_fn(move||{
match (strip.next(),strip.next(),strip.next()){
(Some(v1),Some(v2),Some(v3))=>Some([v1,v2,v3].map(|vertex_id|model::VertexId::new(vertex_id as u32)).to_vec()),
//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
_=>None,
}
})
).collect()
))
}).collect();
Ok(model::Mesh{
unique_pos:spam_pos,
unique_normal:spam_normal,
unique_tex:spam_tex,
unique_color:vec![glam::Vec4::ONE],
unique_vertices:spam_vertices,
polygon_groups,
graphics_groups,
physics_groups,
})
graphics_groups.push(model::IndexedGraphicsGroup {
render: render_id,
groups: vec![polygon_group_id],
});
physics_groups.push(model::IndexedPhysicsGroup {
groups: vec![polygon_group_id],
});
model::PolygonGroup::PolygonList(model::PolygonList::new(
//looking at the code, it would seem that the strips are pre-deindexed into triangle lists when calling this function
mesh.vertex_strip_indices()
.flat_map(|mut strip| {
std::iter::from_fn(move || {
match (strip.next(), strip.next(), strip.next()) {
(Some(v1), Some(v2), Some(v3)) => Some(
[v1, v2, v3]
.map(|vertex_id| model::VertexId::new(vertex_id as u32))
.to_vec(),
),
//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
_ => None,
}
})
})
.collect(),
))
})
.collect();
Ok(model::Mesh {
unique_pos: spam_pos,
unique_normal: spam_normal,
unique_tex: spam_tex,
unique_color: vec![glam::Vec4::ONE],
unique_vertices: spam_vertices,
polygon_groups,
graphics_groups,
physics_groups,
})
}

View File

@ -1,60 +1,60 @@
pub struct Bsp(vbsp::Bsp);
impl Bsp{
pub const fn new(value:vbsp::Bsp)->Self{
Self(value)
}
impl Bsp {
pub const fn new(value: vbsp::Bsp) -> Self {
Self(value)
}
}
impl AsRef<vbsp::Bsp> for Bsp{
fn as_ref(&self)->&vbsp::Bsp{
&self.0
}
impl AsRef<vbsp::Bsp> for Bsp {
fn as_ref(&self) -> &vbsp::Bsp {
&self.0
}
}
pub struct MdlData(Vec<u8>);
impl MdlData{
pub const fn new(value:Vec<u8>)->Self{
Self(value)
}
impl MdlData {
pub const fn new(value: Vec<u8>) -> Self {
Self(value)
}
}
impl AsRef<[u8]> for MdlData{
fn as_ref(&self)->&[u8]{
self.0.as_ref()
}
impl AsRef<[u8]> for MdlData {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
pub struct VtxData(Vec<u8>);
impl VtxData{
pub const fn new(value:Vec<u8>)->Self{
Self(value)
}
impl VtxData {
pub const fn new(value: Vec<u8>) -> Self {
Self(value)
}
}
impl AsRef<[u8]> for VtxData{
fn as_ref(&self)->&[u8]{
self.0.as_ref()
}
impl AsRef<[u8]> for VtxData {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
pub struct VvdData(Vec<u8>);
impl VvdData{
pub const fn new(value:Vec<u8>)->Self{
Self(value)
}
impl VvdData {
pub const fn new(value: Vec<u8>) -> Self {
Self(value)
}
}
impl AsRef<[u8]> for VvdData{
fn as_ref(&self)->&[u8]{
self.0.as_ref()
}
impl AsRef<[u8]> for VvdData {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
pub struct ModelData{
pub mdl:MdlData,
pub vtx:VtxData,
pub vvd:VvdData,
pub struct ModelData {
pub mdl: MdlData,
pub vtx: VtxData,
pub vvd: VvdData,
}
impl ModelData {
pub fn read_model(&self) -> Result<vmdl::Model, vmdl::ModelError> {
Ok(vmdl::Model::from_parts(
vmdl::mdl::Mdl::read(self.mdl.as_ref())?,
vmdl::vtx::Vtx::read(self.vtx.as_ref())?,
vmdl::vvd::Vvd::read(self.vvd.as_ref())?,
))
}
}
impl ModelData{
pub fn read_model(&self)->Result<vmdl::Model,vmdl::ModelError>{
Ok(vmdl::Model::from_parts(
vmdl::mdl::Mdl::read(self.mdl.as_ref())?,
vmdl::vtx::Vtx::read(self.vtx.as_ref())?,
vmdl::vvd::Vvd::read(self.vvd.as_ref())?,
))
}
}

View File

@ -4,34 +4,36 @@ pub mod data;
pub use data::Bsp;
#[derive(Debug)]
pub enum ReadError{
Bsp(vbsp::BspError),
Io(std::io::Error),
pub enum ReadError {
Bsp(vbsp::BspError),
Io(std::io::Error),
}
impl std::fmt::Display for ReadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for ReadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ReadError{}
impl std::error::Error for ReadError {}
pub fn read<R:std::io::Read>(mut input:R)->Result<Bsp,ReadError>{
let mut s=Vec::new();
pub fn read<R: std::io::Read>(mut input: R) -> Result<Bsp, ReadError> {
let mut s = Vec::new();
//TODO: mmap
input.read_to_end(&mut s).map_err(ReadError::Io)?;
//TODO: mmap
input.read_to_end(&mut s).map_err(ReadError::Io)?;
vbsp::Bsp::read(s.as_slice()).map(Bsp::new).map_err(ReadError::Bsp)
vbsp::Bsp::read(s.as_slice())
.map(Bsp::new)
.map_err(ReadError::Bsp)
}
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
bsp:&Bsp,
acquire_render_config_id:AcquireRenderConfigId,
acquire_mesh_id:AcquireMeshId
)->bsp::PartialMap1
pub fn convert<AcquireRenderConfigId, AcquireMeshId>(
bsp: &Bsp,
acquire_render_config_id: AcquireRenderConfigId,
acquire_mesh_id: AcquireMeshId,
) -> bsp::PartialMap1
where
AcquireRenderConfigId:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId,
AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId,
AcquireRenderConfigId: FnMut(Option<&str>) -> strafesnet_common::model::RenderConfigId,
AcquireMeshId: FnMut(&str) -> strafesnet_common::model::MeshId,
{
bsp::convert_bsp(bsp.as_ref(),acquire_render_config_id,acquire_mesh_id)
}
bsp::convert_bsp(bsp.as_ref(), acquire_render_config_id, acquire_mesh_id)
}

View File

@ -1,56 +1,59 @@
use crate::integer::{vec3,Planar64Vec3};
use crate::integer::{vec3, Planar64Vec3};
#[derive(Clone)]
pub struct Aabb{
min:Planar64Vec3,
max:Planar64Vec3,
pub struct Aabb {
min: Planar64Vec3,
max: Planar64Vec3,
}
impl Default for Aabb{
fn default()->Self{
Self{min:vec3::MAX,max:vec3::MIN}
}
impl Default for Aabb {
fn default() -> Self {
Self {
min: vec3::MAX,
max: vec3::MIN,
}
}
}
impl Aabb{
pub const fn new(min:Planar64Vec3,max:Planar64Vec3)->Self{
Self{min,max}
}
pub const fn max(&self)->Planar64Vec3{
self.max
}
pub const fn min(&self)->Planar64Vec3{
self.min
}
pub fn grow(&mut self,point:Planar64Vec3){
self.min=self.min.min(point);
self.max=self.max.max(point);
}
pub fn join(&mut self,aabb:&Aabb){
self.min=self.min.min(aabb.min);
self.max=self.max.max(aabb.max);
}
pub fn inflate(&mut self,hs:Planar64Vec3){
self.min-=hs;
self.max+=hs;
}
pub fn intersects(&self,aabb:&Aabb)->bool{
let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max);
bvec.all()
}
pub fn size(&self)->Planar64Vec3{
self.max-self.min
}
pub fn center(&self)->Planar64Vec3{
self.min+(self.max-self.min)>>1
}
//probably use floats for area & volume because we don't care about precision
// pub fn area_weight(&self)->f32{
// let d=self.max-self.min;
// d.x*d.y+d.y*d.z+d.z*d.x
// }
// pub fn volume(&self)->f32{
// let d=self.max-self.min;
// d.x*d.y*d.z
// }
impl Aabb {
pub const fn new(min: Planar64Vec3, max: Planar64Vec3) -> Self {
Self { min, max }
}
pub const fn max(&self) -> Planar64Vec3 {
self.max
}
pub const fn min(&self) -> Planar64Vec3 {
self.min
}
pub fn grow(&mut self, point: Planar64Vec3) {
self.min = self.min.min(point);
self.max = self.max.max(point);
}
pub fn join(&mut self, aabb: &Aabb) {
self.min = self.min.min(aabb.min);
self.max = self.max.max(aabb.max);
}
pub fn inflate(&mut self, hs: Planar64Vec3) {
self.min -= hs;
self.max += hs;
}
pub fn intersects(&self, aabb: &Aabb) -> bool {
let bvec = self.min.lt(aabb.max) & aabb.min.lt(self.max);
bvec.all()
}
pub fn size(&self) -> Planar64Vec3 {
self.max - self.min
}
pub fn center(&self) -> Planar64Vec3 {
self.min + (self.max - self.min) >> 1
}
//probably use floats for area & volume because we don't care about precision
// pub fn area_weight(&self)->f32{
// let d=self.max-self.min;
// d.x*d.y+d.y*d.z+d.z*d.x
// }
// pub fn volume(&self)->f32{
// let d=self.max-self.min;
// d.x*d.y*d.z
// }
}

View File

@ -10,185 +10,217 @@ use crate::aabb::Aabb;
//sort the centerpoints on each axis (3 lists)
//bv is put into octant based on whether it is upper or lower in each list
pub enum RecursiveContent<R,T>{
Branch(Vec<R>),
Leaf(T),
pub enum RecursiveContent<R, T> {
Branch(Vec<R>),
Leaf(T),
}
impl<R,T> Default for RecursiveContent<R,T>{
fn default()->Self{
Self::Branch(Vec::new())
}
impl<R, T> Default for RecursiveContent<R, T> {
fn default() -> Self {
Self::Branch(Vec::new())
}
}
pub struct BvhNode<T>{
content:RecursiveContent<BvhNode<T>,T>,
aabb:Aabb,
pub struct BvhNode<T> {
content: RecursiveContent<BvhNode<T>, T>,
aabb: Aabb,
}
impl<T> Default for BvhNode<T>{
fn default()->Self{
Self{
content:Default::default(),
aabb:Aabb::default(),
}
}
impl<T> Default for BvhNode<T> {
fn default() -> Self {
Self {
content: Default::default(),
aabb: Aabb::default(),
}
}
}
pub struct BvhWeightNode<W,T>{
content:RecursiveContent<BvhWeightNode<W,T>,T>,
weight:W,
aabb:Aabb,
pub struct BvhWeightNode<W, T> {
content: RecursiveContent<BvhWeightNode<W, T>, T>,
weight: W,
aabb: Aabb,
}
impl<T> BvhNode<T>{
pub fn the_tester<F:FnMut(&T)>(&self,aabb:&Aabb,f:&mut F){
match &self.content{
RecursiveContent::Leaf(model)=>f(model),
RecursiveContent::Branch(children)=>for child in children{
//this test could be moved outside the match statement
//but that would test the root node aabb
//you're probably not going to spend a lot of time outside the map,
//so the test is extra work for nothing
if aabb.intersects(&child.aabb){
child.the_tester(aabb,f);
}
},
}
}
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
match self.content{
RecursiveContent::Leaf(model)=>f(model),
RecursiveContent::Branch(children)=>for child in children{
child.into_visitor(f)
},
}
}
pub fn weigh_contents<W:Copy+std::iter::Sum<W>,F:Fn(&T)->W>(self,f:&F)->BvhWeightNode<W,T>{
match self.content{
RecursiveContent::Leaf(model)=>BvhWeightNode{
weight:f(&model),
content:RecursiveContent::Leaf(model),
aabb:self.aabb,
},
RecursiveContent::Branch(children)=>{
let branch:Vec<BvhWeightNode<W,T>>=children.into_iter().map(|child|
child.weigh_contents(f)
).collect();
BvhWeightNode{
weight:branch.iter().map(|node|node.weight).sum(),
content:RecursiveContent::Branch(branch),
aabb:self.aabb,
}
},
}
}
impl<T> BvhNode<T> {
pub fn the_tester<F: FnMut(&T)>(&self, aabb: &Aabb, f: &mut F) {
match &self.content {
RecursiveContent::Leaf(model) => f(model),
RecursiveContent::Branch(children) => {
for child in children {
//this test could be moved outside the match statement
//but that would test the root node aabb
//you're probably not going to spend a lot of time outside the map,
//so the test is extra work for nothing
if aabb.intersects(&child.aabb) {
child.the_tester(aabb, f);
}
}
}
}
}
pub fn into_visitor<F: FnMut(T)>(self, f: &mut F) {
match self.content {
RecursiveContent::Leaf(model) => f(model),
RecursiveContent::Branch(children) => {
for child in children {
child.into_visitor(f)
}
}
}
}
pub fn weigh_contents<W: Copy + std::iter::Sum<W>, F: Fn(&T) -> W>(
self,
f: &F,
) -> BvhWeightNode<W, T> {
match self.content {
RecursiveContent::Leaf(model) => BvhWeightNode {
weight: f(&model),
content: RecursiveContent::Leaf(model),
aabb: self.aabb,
},
RecursiveContent::Branch(children) => {
let branch: Vec<BvhWeightNode<W, T>> = children
.into_iter()
.map(|child| child.weigh_contents(f))
.collect();
BvhWeightNode {
weight: branch.iter().map(|node| node.weight).sum(),
content: RecursiveContent::Branch(branch),
aabb: self.aabb,
}
}
}
}
}
impl <W,T> BvhWeightNode<W,T>{
pub const fn weight(&self)->&W{
&self.weight
}
pub const fn aabb(&self)->&Aabb{
&self.aabb
}
pub fn into_content(self)->RecursiveContent<BvhWeightNode<W,T>,T>{
self.content
}
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
match self.content{
RecursiveContent::Leaf(model)=>f(model),
RecursiveContent::Branch(children)=>for child in children{
child.into_visitor(f)
},
}
}
impl<W, T> BvhWeightNode<W, T> {
pub const fn weight(&self) -> &W {
&self.weight
}
pub const fn aabb(&self) -> &Aabb {
&self.aabb
}
pub fn into_content(self) -> RecursiveContent<BvhWeightNode<W, T>, T> {
self.content
}
pub fn into_visitor<F: FnMut(T)>(self, f: &mut F) {
match self.content {
RecursiveContent::Leaf(model) => f(model),
RecursiveContent::Branch(children) => {
for child in children {
child.into_visitor(f)
}
}
}
}
}
pub fn generate_bvh<T>(boxen:Vec<(T,Aabb)>)->BvhNode<T>{
generate_bvh_node(boxen,false)
pub fn generate_bvh<T>(boxen: Vec<(T, Aabb)>) -> BvhNode<T> {
generate_bvh_node(boxen, false)
}
fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
let n=boxen.len();
if force||n<20{
let mut aabb=Aabb::default();
let nodes=boxen.into_iter().map(|b|{
aabb.join(&b.1);
BvhNode{
content:RecursiveContent::Leaf(b.0),
aabb:b.1,
}
}).collect();
BvhNode{
content:RecursiveContent::Branch(nodes),
aabb,
}
}else{
let mut sort_x=Vec::with_capacity(n);
let mut sort_y=Vec::with_capacity(n);
let mut sort_z=Vec::with_capacity(n);
for (i,(_,aabb)) in boxen.iter().enumerate(){
let center=aabb.center();
sort_x.push((i,center.x));
sort_y.push((i,center.y));
sort_z.push((i,center.z));
}
sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
sort_z.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
let h=n/2;
let median_x=sort_x[h].1;
let median_y=sort_y[h].1;
let median_z=sort_z[h].1;
//locate a run of values equal to the median
//partition point gives the first index for which the predicate evaluates to false
let first_index_eq_median_x=sort_x.partition_point(|&(_,x)|x<median_x);
let first_index_eq_median_y=sort_y.partition_point(|&(_,y)|y<median_y);
let first_index_eq_median_z=sort_z.partition_point(|&(_,z)|z<median_z);
let first_index_gt_median_x=sort_x.partition_point(|&(_,x)|x<=median_x);
let first_index_gt_median_y=sort_y.partition_point(|&(_,y)|y<=median_y);
let first_index_gt_median_z=sort_z.partition_point(|&(_,z)|z<=median_z);
//pick which side median value copies go into such that both sides are as balanced as possible based on distance from n/2
let partition_point_x=if n.abs_diff(2*first_index_eq_median_x)<n.abs_diff(2*first_index_gt_median_x){first_index_eq_median_x}else{first_index_gt_median_x};
let partition_point_y=if n.abs_diff(2*first_index_eq_median_y)<n.abs_diff(2*first_index_gt_median_y){first_index_eq_median_y}else{first_index_gt_median_y};
let partition_point_z=if n.abs_diff(2*first_index_eq_median_z)<n.abs_diff(2*first_index_gt_median_z){first_index_eq_median_z}else{first_index_gt_median_z};
//this ids which octant the boxen is put in
let mut octant=vec![0;n];
for &(i,_) in &sort_x[partition_point_x..]{
octant[i]+=1<<0;
}
for &(i,_) in &sort_y[partition_point_y..]{
octant[i]+=1<<1;
}
for &(i,_) in &sort_z[partition_point_z..]{
octant[i]+=1<<2;
}
//generate lists for unique octant values
let mut list_list=Vec::with_capacity(8);
let mut octant_list=Vec::with_capacity(8);
for (i,(data,aabb)) in boxen.into_iter().enumerate(){
let octant_id=octant[i];
let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){
list_id
}else{
let list_id=list_list.len();
octant_list.push(octant_id);
list_list.push(Vec::new());
list_id
};
list_list[list_id].push((data,aabb));
}
let mut aabb=Aabb::default();
if list_list.len()==1{
generate_bvh_node(list_list.remove(0),true)
}else{
BvhNode{
content:RecursiveContent::Branch(
list_list.into_iter().map(|b|{
let node=generate_bvh_node(b,false);
aabb.join(&node.aabb);
node
}).collect()
),
aabb,
}
}
}
fn generate_bvh_node<T>(boxen: Vec<(T, Aabb)>, force: bool) -> BvhNode<T> {
let n = boxen.len();
if force || n < 20 {
let mut aabb = Aabb::default();
let nodes = boxen
.into_iter()
.map(|b| {
aabb.join(&b.1);
BvhNode {
content: RecursiveContent::Leaf(b.0),
aabb: b.1,
}
})
.collect();
BvhNode {
content: RecursiveContent::Branch(nodes),
aabb,
}
} else {
let mut sort_x = Vec::with_capacity(n);
let mut sort_y = Vec::with_capacity(n);
let mut sort_z = Vec::with_capacity(n);
for (i, (_, aabb)) in boxen.iter().enumerate() {
let center = aabb.center();
sort_x.push((i, center.x));
sort_y.push((i, center.y));
sort_z.push((i, center.z));
}
sort_x.sort_by(|tup0, tup1| tup0.1.cmp(&tup1.1));
sort_y.sort_by(|tup0, tup1| tup0.1.cmp(&tup1.1));
sort_z.sort_by(|tup0, tup1| tup0.1.cmp(&tup1.1));
let h = n / 2;
let median_x = sort_x[h].1;
let median_y = sort_y[h].1;
let median_z = sort_z[h].1;
//locate a run of values equal to the median
//partition point gives the first index for which the predicate evaluates to false
let first_index_eq_median_x = sort_x.partition_point(|&(_, x)| x < median_x);
let first_index_eq_median_y = sort_y.partition_point(|&(_, y)| y < median_y);
let first_index_eq_median_z = sort_z.partition_point(|&(_, z)| z < median_z);
let first_index_gt_median_x = sort_x.partition_point(|&(_, x)| x <= median_x);
let first_index_gt_median_y = sort_y.partition_point(|&(_, y)| y <= median_y);
let first_index_gt_median_z = sort_z.partition_point(|&(_, z)| z <= median_z);
//pick which side median value copies go into such that both sides are as balanced as possible based on distance from n/2
let partition_point_x =
if n.abs_diff(2 * first_index_eq_median_x) < n.abs_diff(2 * first_index_gt_median_x) {
first_index_eq_median_x
} else {
first_index_gt_median_x
};
let partition_point_y =
if n.abs_diff(2 * first_index_eq_median_y) < n.abs_diff(2 * first_index_gt_median_y) {
first_index_eq_median_y
} else {
first_index_gt_median_y
};
let partition_point_z =
if n.abs_diff(2 * first_index_eq_median_z) < n.abs_diff(2 * first_index_gt_median_z) {
first_index_eq_median_z
} else {
first_index_gt_median_z
};
//this ids which octant the boxen is put in
let mut octant = vec![0; n];
for &(i, _) in &sort_x[partition_point_x..] {
octant[i] += 1 << 0;
}
for &(i, _) in &sort_y[partition_point_y..] {
octant[i] += 1 << 1;
}
for &(i, _) in &sort_z[partition_point_z..] {
octant[i] += 1 << 2;
}
//generate lists for unique octant values
let mut list_list = Vec::with_capacity(8);
let mut octant_list = Vec::with_capacity(8);
for (i, (data, aabb)) in boxen.into_iter().enumerate() {
let octant_id = octant[i];
let list_id = if let Some(list_id) = octant_list.iter().position(|&id| id == octant_id)
{
list_id
} else {
let list_id = list_list.len();
octant_list.push(octant_id);
list_list.push(Vec::new());
list_id
};
list_list[list_id].push((data, aabb));
}
let mut aabb = Aabb::default();
if list_list.len() == 1 {
generate_bvh_node(list_list.remove(0), true)
} else {
BvhNode {
content: RecursiveContent::Branch(
list_list
.into_iter()
.map(|b| {
let node = generate_bvh_node(b, false);
aabb.join(&node.aabb);
node
})
.collect(),
),
aabb,
}
}
}
}

View File

@ -1,25 +1,25 @@
bitflags::bitflags!{
#[derive(Clone,Copy,Debug,Default)]
pub struct Controls:u32{
const MoveForward=1<<0;
const MoveLeft=1<<1;
const MoveBackward=1<<2;
const MoveRight=1<<3;
const MoveUp=1<<4;
const MoveDown=1<<5;
const LookUp=1<<6;
const LookLeft=1<<7;
const LookDown=1<<8;
const LookRight=1<<9;
const Jump=1<<10;
const Crouch=1<<11;
const Sprint=1<<12;
const Zoom=1<<13;
const Use=1<<14;//Interact with object
const PrimaryAction=1<<15;//LBM/Shoot/Melee
const SecondaryAction=1<<16;//RMB/ADS/Block
bitflags::bitflags! {
#[derive(Clone,Copy,Debug,Default)]
pub struct Controls:u32{
const MoveForward=1<<0;
const MoveLeft=1<<1;
const MoveBackward=1<<2;
const MoveRight=1<<3;
const MoveUp=1<<4;
const MoveDown=1<<5;
const LookUp=1<<6;
const LookLeft=1<<7;
const LookDown=1<<8;
const LookRight=1<<9;
const Jump=1<<10;
const Crouch=1<<11;
const Sprint=1<<12;
const Zoom=1<<13;
const Use=1<<14;//Interact with object
const PrimaryAction=1<<15;//LBM/Shoot/Melee
const SecondaryAction=1<<16;//RMB/ADS/Block
const WASD=Self::MoveForward.union(Self::MoveLeft).union(Self::MoveBackward).union(Self::MoveRight).bits();
const WASDQE=Self::MoveForward.union(Self::MoveLeft).union(Self::MoveBackward).union(Self::MoveRight).union(Self::MoveUp).union(Self::MoveDown).bits();
}
const WASD=Self::MoveForward.union(Self::MoveLeft).union(Self::MoveBackward).union(Self::MoveRight).bits();
const WASDQE=Self::MoveForward.union(Self::MoveLeft).union(Self::MoveBackward).union(Self::MoveRight).union(Self::MoveUp).union(Self::MoveDown).bits();
}
}

View File

@ -1,88 +1,106 @@
use crate::integer::{AbsoluteTime, Planar64, Planar64Vec3};
use crate::model;
use crate::integer::{AbsoluteTime,Planar64,Planar64Vec3};
//you have this effect while in contact
#[derive(Clone,Hash,Eq,PartialEq)]
pub struct ContactingLadder{
pub sticky:bool
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ContactingLadder {
pub sticky: bool,
}
#[derive(Clone,Hash,Eq,PartialEq)]
pub enum ContactingBehaviour{
Surf,
Ladder(ContactingLadder),
NoJump,
Cling,//usable as a zipline, or other weird and wonderful things
Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum ContactingBehaviour {
Surf,
Ladder(ContactingLadder),
NoJump,
Cling, //usable as a zipline, or other weird and wonderful things
Elastic(u32), //[1/2^32,1] 0=None (elasticity+1)/2^32
}
//you have this effect while intersecting
#[derive(Clone,Hash,Eq,PartialEq)]
pub struct IntersectingWater{
pub viscosity:Planar64,
pub density:Planar64,
pub velocity:Planar64Vec3,
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct IntersectingWater {
pub viscosity: Planar64,
pub density: Planar64,
pub velocity: Planar64Vec3,
}
//All models can be given these attributes
#[derive(Clone,Hash,Eq,PartialEq)]
pub struct Accelerator{
pub acceleration:Planar64Vec3
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Accelerator {
pub acceleration: Planar64Vec3,
}
#[derive(Clone,Hash,Eq,PartialEq)]
pub enum Booster{
//Affine(crate::integer::Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more
Velocity(Planar64Vec3),//straight up boost velocity adds to your current velocity
Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction
AirTime(AbsoluteTime),//increase airtime, invariant across mass and gravity changes
Height(Planar64),//increase height, invariant across mass and gravity changes
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum Booster {
//Affine(crate::integer::Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more
Velocity(Planar64Vec3), //straight up boost velocity adds to your current velocity
Energy {
direction: Planar64Vec3,
energy: Planar64,
}, //increase energy in direction
AirTime(AbsoluteTime), //increase airtime, invariant across mass and gravity changes
Height(Planar64), //increase height, invariant across mass and gravity changes
}
impl Booster{
pub fn boost(&self,velocity:Planar64Vec3)->Planar64Vec3{
match self{
&Booster::Velocity(boost_velocity)=>velocity+boost_velocity,
&Booster::Energy{..}=>{
todo!()
//let d=direction.dot(velocity);
//TODO: think about negative
//velocity+direction.with_length((d*d+energy).sqrt()-d)
},
Booster::AirTime(_)=>todo!(),
Booster::Height(_)=>todo!(),
}
}
impl Booster {
pub fn boost(&self, velocity: Planar64Vec3) -> Planar64Vec3 {
match self {
&Booster::Velocity(boost_velocity) => velocity + boost_velocity,
&Booster::Energy { .. } => {
todo!()
//let d=direction.dot(velocity);
//TODO: think about negative
//velocity+direction.with_length((d*d+energy).sqrt()-d)
}
Booster::AirTime(_) => todo!(),
Booster::Height(_) => todo!(),
}
}
}
#[derive(Clone,Hash,Eq,PartialEq)]
pub enum TrajectoryChoice{
HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time
LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum TrajectoryChoice {
HighArcLongDuration, //underhand lob at target: less horizontal speed and more air time
LowArcShortDuration, //overhand throw at target: more horizontal speed and less air time
}
#[derive(Clone,Hash,Eq,PartialEq)]
pub enum SetTrajectory{
//Speed-type SetTrajectory
AirTime(AbsoluteTime),//air time (relative to gravity direction) is invariant across mass and gravity changes
Height(Planar64),//boost height (relative to gravity direction) is invariant across mass and gravity changes
DotVelocity{direction:Planar64Vec3,dot:Planar64},//set your velocity in a specific direction without touching other directions
//Velocity-type SetTrajectory
TargetPointTime{//launch on a trajectory that will land at a target point in a set amount of time
target_point:Planar64Vec3,
time:AbsoluteTime,//short time = fast and direct, long time = launch high in the air, negative time = wrong way
},
TargetPointSpeed{//launch at a fixed speed and land at a target point
target_point:Planar64Vec3,
speed:Planar64,//if speed is too low this will fail to reach the target. The closest-passing trajectory will be chosen instead
trajectory_choice:TrajectoryChoice,
},
Velocity(Planar64Vec3),//SetVelocity
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum SetTrajectory {
//Speed-type SetTrajectory
AirTime(AbsoluteTime), //air time (relative to gravity direction) is invariant across mass and gravity changes
Height(Planar64), //boost height (relative to gravity direction) is invariant across mass and gravity changes
DotVelocity {
direction: Planar64Vec3,
dot: Planar64,
}, //set your velocity in a specific direction without touching other directions
//Velocity-type SetTrajectory
TargetPointTime {
//launch on a trajectory that will land at a target point in a set amount of time
target_point: Planar64Vec3,
time: AbsoluteTime, //short time = fast and direct, long time = launch high in the air, negative time = wrong way
},
TargetPointSpeed {
//launch at a fixed speed and land at a target point
target_point: Planar64Vec3,
speed: Planar64, //if speed is too low this will fail to reach the target. The closest-passing trajectory will be chosen instead
trajectory_choice: TrajectoryChoice,
},
Velocity(Planar64Vec3), //SetVelocity
}
impl SetTrajectory{
pub const fn is_absolute(&self)->bool{
match self{
SetTrajectory::AirTime(_)
|SetTrajectory::Height(_)
|SetTrajectory::DotVelocity{direction:_,dot:_}=>false,
SetTrajectory::TargetPointTime{target_point:_,time:_}
|SetTrajectory::TargetPointSpeed{target_point:_,speed:_,trajectory_choice:_}
|SetTrajectory::Velocity(_)=>true,
}
}
impl SetTrajectory {
pub const fn is_absolute(&self) -> bool {
match self {
SetTrajectory::AirTime(_)
| SetTrajectory::Height(_)
| SetTrajectory::DotVelocity {
direction: _,
dot: _,
} => false,
SetTrajectory::TargetPointTime {
target_point: _,
time: _,
}
| SetTrajectory::TargetPointSpeed {
target_point: _,
speed: _,
trajectory_choice: _,
}
| SetTrajectory::Velocity(_) => true,
}
}
}
// enum TrapCondition{
// FasterThan(Planar64),
@ -90,85 +108,85 @@ impl SetTrajectory{
// InRange(Planar64,Planar64),
// OutsideRange(Planar64,Planar64),
// }
#[derive(Clone,Hash,Eq,PartialEq)]
pub struct Wormhole{
//destination does not need to be another wormhole
//this defines a one way portal to a destination model transform
//two of these can create a two way wormhole
pub destination_model:model::ModelId,
//(position,angles)*=origin.transform.inverse()*destination.transform
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Wormhole {
//destination does not need to be another wormhole
//this defines a one way portal to a destination model transform
//two of these can create a two way wormhole
pub destination_model: model::ModelId,
//(position,angles)*=origin.transform.inverse()*destination.transform
}
//attributes listed in order of handling
#[derive(Default,Clone,Hash,Eq,PartialEq)]
pub struct GeneralAttributes{
pub booster:Option<Booster>,
pub trajectory:Option<SetTrajectory>,
pub wormhole:Option<Wormhole>,
pub accelerator:Option<Accelerator>,
#[derive(Default, Clone, Hash, Eq, PartialEq)]
pub struct GeneralAttributes {
pub booster: Option<Booster>,
pub trajectory: Option<SetTrajectory>,
pub wormhole: Option<Wormhole>,
pub accelerator: Option<Accelerator>,
}
impl GeneralAttributes{
pub const fn any(&self)->bool{
self.booster.is_some()
||self.trajectory.is_some()
||self.wormhole.is_some()
||self.accelerator.is_some()
}
pub fn is_wrcp(&self)->bool{
self.trajectory.as_ref().map_or(false,|t|t.is_absolute())
/*
&&match &self.teleport_behaviour{
Some(TeleportBehaviour::StageElement(
StageElement{
mode_id,
stage_id:_,
force:true,
behaviour:StageElementBehaviour::Trigger|StageElementBehaviour::Teleport
}
))=>current_mode_id==*mode_id,
_=>false,
}
*/
}
impl GeneralAttributes {
pub const fn any(&self) -> bool {
self.booster.is_some()
|| self.trajectory.is_some()
|| self.wormhole.is_some()
|| self.accelerator.is_some()
}
pub fn is_wrcp(&self) -> bool {
self.trajectory.as_ref().map_or(false, |t| t.is_absolute())
/*
&&match &self.teleport_behaviour{
Some(TeleportBehaviour::StageElement(
StageElement{
mode_id,
stage_id:_,
force:true,
behaviour:StageElementBehaviour::Trigger|StageElementBehaviour::Teleport
}
))=>current_mode_id==*mode_id,
_=>false,
}
*/
}
}
#[derive(Default,Clone,Hash,Eq,PartialEq)]
pub struct ContactingAttributes{
//friction?
pub contact_behaviour:Option<ContactingBehaviour>,
#[derive(Default, Clone, Hash, Eq, PartialEq)]
pub struct ContactingAttributes {
//friction?
pub contact_behaviour: Option<ContactingBehaviour>,
}
impl ContactingAttributes{
pub const fn any(&self)->bool{
self.contact_behaviour.is_some()
}
impl ContactingAttributes {
pub const fn any(&self) -> bool {
self.contact_behaviour.is_some()
}
}
#[derive(Default,Clone,Hash,Eq,PartialEq)]
pub struct IntersectingAttributes{
pub water:Option<IntersectingWater>,
#[derive(Default, Clone, Hash, Eq, PartialEq)]
pub struct IntersectingAttributes {
pub water: Option<IntersectingWater>,
}
impl IntersectingAttributes{
pub const fn any(&self)->bool{
self.water.is_some()
}
impl IntersectingAttributes {
pub const fn any(&self) -> bool {
self.water.is_some()
}
}
#[derive(Clone,Copy,id::Id,Hash,Eq,PartialEq)]
#[derive(Clone, Copy, id::Id, Hash, Eq, PartialEq)]
pub struct CollisionAttributesId(u32);
#[derive(Clone,Default,Hash,Eq,PartialEq)]
pub struct ContactAttributes{
pub contacting:ContactingAttributes,
pub general:GeneralAttributes,
#[derive(Clone, Default, Hash, Eq, PartialEq)]
pub struct ContactAttributes {
pub contacting: ContactingAttributes,
pub general: GeneralAttributes,
}
#[derive(Clone,Default,Hash,Eq,PartialEq)]
pub struct IntersectAttributes{
pub intersecting:IntersectingAttributes,
pub general:GeneralAttributes,
#[derive(Clone, Default, Hash, Eq, PartialEq)]
pub struct IntersectAttributes {
pub intersecting: IntersectingAttributes,
pub general: GeneralAttributes,
}
#[derive(Clone,Hash,Eq,PartialEq)]
pub enum CollisionAttributes{
Decoration,//visual only
Contact(ContactAttributes),//track whether you are contacting the object
Intersect(IntersectAttributes),//track whether you are intersecting the object
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum CollisionAttributes {
Decoration, //visual only
Contact(ContactAttributes), //track whether you are contacting the object
Intersect(IntersectAttributes), //track whether you are intersecting the object
}
impl CollisionAttributes{
pub fn contact_default()->Self{
Self::Contact(ContactAttributes::default())
}
impl CollisionAttributes {
pub fn contact_default() -> Self {
Self::Contact(ContactAttributes::default())
}
}

View File

@ -1,332 +1,358 @@
use std::collections::{HashSet,HashMap};
use crate::model::ModelId;
use crate::gameplay_style;
use crate::model::ModelId;
use crate::updatable::Updatable;
use std::collections::{HashMap, HashSet};
#[derive(Clone)]
pub struct StageElement{
stage_id:StageId,//which stage spawn to send to
force:bool,//allow setting to lower spawn id i.e. 7->3
behaviour:StageElementBehaviour,
jump_limit:Option<u8>,
pub struct StageElement {
stage_id: StageId, //which stage spawn to send to
force: bool, //allow setting to lower spawn id i.e. 7->3
behaviour: StageElementBehaviour,
jump_limit: Option<u8>,
}
impl StageElement{
#[inline]
pub const fn new(stage_id:StageId,force:bool,behaviour:StageElementBehaviour,jump_limit:Option<u8>)->Self{
Self{
stage_id,
force,
behaviour,
jump_limit,
}
}
#[inline]
pub const fn stage_id(&self)->StageId{
self.stage_id
}
#[inline]
pub const fn force(&self)->bool{
self.force
}
#[inline]
pub const fn behaviour(&self)->StageElementBehaviour{
self.behaviour
}
#[inline]
pub const fn jump_limit(&self)->Option<u8>{
self.jump_limit
}
impl StageElement {
#[inline]
pub const fn new(
stage_id: StageId,
force: bool,
behaviour: StageElementBehaviour,
jump_limit: Option<u8>,
) -> Self {
Self {
stage_id,
force,
behaviour,
jump_limit,
}
}
#[inline]
pub const fn stage_id(&self) -> StageId {
self.stage_id
}
#[inline]
pub const fn force(&self) -> bool {
self.force
}
#[inline]
pub const fn behaviour(&self) -> StageElementBehaviour {
self.behaviour
}
#[inline]
pub const fn jump_limit(&self) -> Option<u8> {
self.jump_limit
}
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
pub enum StageElementBehaviour{
SpawnAt,//must be standing on top to get effect. except cancollide false
Trigger,
Teleport,
Platform,
//Check(point) acts like a trigger if you haven't hit all the checkpoints on previous stages yet.
//Note that all stage elements act like this, this is just the isolated behaviour.
Check,
Checkpoint,//this is a combined behaviour for Ordered & Unordered in case a model is used multiple times or for both.
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
pub enum StageElementBehaviour {
SpawnAt, //must be standing on top to get effect. except cancollide false
Trigger,
Teleport,
Platform,
//Check(point) acts like a trigger if you haven't hit all the checkpoints on previous stages yet.
//Note that all stage elements act like this, this is just the isolated behaviour.
Check,
Checkpoint, //this is a combined behaviour for Ordered & Unordered in case a model is used multiple times or for both.
}
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
#[derive(Clone, Copy, Debug, Hash, id::Id, Eq, PartialEq)]
pub struct CheckpointId(u32);
impl CheckpointId{
pub const FIRST:Self=Self(0);
impl CheckpointId {
pub const FIRST: Self = Self(0);
}
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq,Ord,PartialOrd)]
#[derive(Clone, Copy, Debug, Hash, id::Id, Eq, PartialEq, Ord, PartialOrd)]
pub struct StageId(u32);
impl StageId{
pub const FIRST:Self=Self(0);
impl StageId {
pub const FIRST: Self = Self(0);
}
#[derive(Clone)]
pub struct Stage{
spawn:ModelId,
//open world support lol
ordered_checkpoints_count:u32,
unordered_checkpoints_count:u32,
//currently loaded checkpoint models
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
unordered_checkpoints:HashSet<ModelId>,
pub struct Stage {
spawn: ModelId,
//open world support lol
ordered_checkpoints_count: u32,
unordered_checkpoints_count: u32,
//currently loaded checkpoint models
ordered_checkpoints: HashMap<CheckpointId, ModelId>,
unordered_checkpoints: HashSet<ModelId>,
}
impl Stage{
pub fn new(
spawn:ModelId,
ordered_checkpoints_count:u32,
unordered_checkpoints_count:u32,
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
unordered_checkpoints:HashSet<ModelId>,
)->Self{
Self{
spawn,
ordered_checkpoints_count,
unordered_checkpoints_count,
ordered_checkpoints,
unordered_checkpoints,
}
}
pub fn empty(spawn:ModelId)->Self{
Self{
spawn,
ordered_checkpoints_count:0,
unordered_checkpoints_count:0,
ordered_checkpoints:HashMap::new(),
unordered_checkpoints:HashSet::new(),
}
}
#[inline]
pub const fn spawn(&self)->ModelId{
self.spawn
}
#[inline]
pub const fn ordered_checkpoints_count(&self)->u32{
self.ordered_checkpoints_count
}
#[inline]
pub const fn unordered_checkpoints_count(&self)->u32{
self.unordered_checkpoints_count
}
pub fn into_inner(self)->(HashMap<CheckpointId,ModelId>,HashSet<ModelId>){
(self.ordered_checkpoints,self.unordered_checkpoints)
}
/// Returns true if the stage has no checkpoints.
#[inline]
pub const fn is_empty(&self)->bool{
self.is_complete(0,0)
}
#[inline]
pub const fn is_complete(&self,ordered_checkpoints_count:u32,unordered_checkpoints_count:u32)->bool{
self.ordered_checkpoints_count==ordered_checkpoints_count&&self.unordered_checkpoints_count==unordered_checkpoints_count
}
#[inline]
pub fn is_next_ordered_checkpoint(&self,next_ordered_checkpoint_id:CheckpointId,model_id:ModelId)->bool{
self.ordered_checkpoints.get(&next_ordered_checkpoint_id).is_some_and(|&next_checkpoint|model_id==next_checkpoint)
}
#[inline]
pub fn is_unordered_checkpoint(&self,model_id:ModelId)->bool{
self.unordered_checkpoints.contains(&model_id)
}
impl Stage {
pub fn new(
spawn: ModelId,
ordered_checkpoints_count: u32,
unordered_checkpoints_count: u32,
ordered_checkpoints: HashMap<CheckpointId, ModelId>,
unordered_checkpoints: HashSet<ModelId>,
) -> Self {
Self {
spawn,
ordered_checkpoints_count,
unordered_checkpoints_count,
ordered_checkpoints,
unordered_checkpoints,
}
}
pub fn empty(spawn: ModelId) -> Self {
Self {
spawn,
ordered_checkpoints_count: 0,
unordered_checkpoints_count: 0,
ordered_checkpoints: HashMap::new(),
unordered_checkpoints: HashSet::new(),
}
}
#[inline]
pub const fn spawn(&self) -> ModelId {
self.spawn
}
#[inline]
pub const fn ordered_checkpoints_count(&self) -> u32 {
self.ordered_checkpoints_count
}
#[inline]
pub const fn unordered_checkpoints_count(&self) -> u32 {
self.unordered_checkpoints_count
}
pub fn into_inner(self) -> (HashMap<CheckpointId, ModelId>, HashSet<ModelId>) {
(self.ordered_checkpoints, self.unordered_checkpoints)
}
/// Returns true if the stage has no checkpoints.
#[inline]
pub const fn is_empty(&self) -> bool {
self.is_complete(0, 0)
}
#[inline]
pub const fn is_complete(
&self,
ordered_checkpoints_count: u32,
unordered_checkpoints_count: u32,
) -> bool {
self.ordered_checkpoints_count == ordered_checkpoints_count
&& self.unordered_checkpoints_count == unordered_checkpoints_count
}
#[inline]
pub fn is_next_ordered_checkpoint(
&self,
next_ordered_checkpoint_id: CheckpointId,
model_id: ModelId,
) -> bool {
self.ordered_checkpoints
.get(&next_ordered_checkpoint_id)
.is_some_and(|&next_checkpoint| model_id == next_checkpoint)
}
#[inline]
pub fn is_unordered_checkpoint(&self, model_id: ModelId) -> bool {
self.unordered_checkpoints.contains(&model_id)
}
}
#[derive(Default)]
pub struct StageUpdate{
//other behaviour models of this stage can have
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
unordered_checkpoints:HashSet<ModelId>,
pub struct StageUpdate {
//other behaviour models of this stage can have
ordered_checkpoints: HashMap<CheckpointId, ModelId>,
unordered_checkpoints: HashSet<ModelId>,
}
impl Updatable<StageUpdate> for Stage{
fn update(&mut self,update:StageUpdate){
self.ordered_checkpoints.extend(update.ordered_checkpoints);
self.unordered_checkpoints.extend(update.unordered_checkpoints);
}
impl Updatable<StageUpdate> for Stage {
fn update(&mut self, update: StageUpdate) {
self.ordered_checkpoints.extend(update.ordered_checkpoints);
self.unordered_checkpoints
.extend(update.unordered_checkpoints);
}
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
pub enum Zone{
Start,
Finish,
Anticheat,
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
pub enum Zone {
Start,
Finish,
Anticheat,
}
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq,Ord,PartialOrd)]
#[derive(Clone, Copy, Debug, Hash, id::Id, Eq, PartialEq, Ord, PartialOrd)]
pub struct ModeId(u32);
impl ModeId{
pub const MAIN:Self=Self(0);
pub const BONUS:Self=Self(1);
impl ModeId {
pub const MAIN: Self = Self(0);
pub const BONUS: Self = Self(1);
}
#[derive(Clone)]
pub struct Mode{
style:gameplay_style::StyleModifiers,
start:ModelId,//when you press reset you go here
zones:HashMap<ModelId,Zone>,
stages:Vec<Stage>,//when you load the map you go to stages[0].spawn
//mutually exlusive stage element behaviour
elements:HashMap<ModelId,StageElement>,
pub struct Mode {
style: gameplay_style::StyleModifiers,
start: ModelId, //when you press reset you go here
zones: HashMap<ModelId, Zone>,
stages: Vec<Stage>, //when you load the map you go to stages[0].spawn
//mutually exlusive stage element behaviour
elements: HashMap<ModelId, StageElement>,
}
impl Mode{
pub fn new(
style:gameplay_style::StyleModifiers,
start:ModelId,
zones:HashMap<ModelId,Zone>,
stages:Vec<Stage>,
elements:HashMap<ModelId,StageElement>,
)->Self{
Self{
style,
start,
zones,
stages,
elements,
}
}
pub fn empty(style:gameplay_style::StyleModifiers,start:ModelId)->Self{
Self{
style,
start,
zones:HashMap::new(),
stages:Vec::new(),
elements:HashMap::new(),
}
}
pub fn into_inner(self)->(
gameplay_style::StyleModifiers,
ModelId,
HashMap<ModelId,Zone>,
Vec<Stage>,
HashMap<ModelId,StageElement>,
){
(
self.style,
self.start,
self.zones,
self.stages,
self.elements,
)
}
pub const fn get_start(&self)->ModelId{
self.start
}
pub const fn get_style(&self)->&gameplay_style::StyleModifiers{
&self.style
}
pub fn push_stage(&mut self,stage:Stage){
self.stages.push(stage)
}
pub fn get_stage_mut(&mut self,stage:StageId)->Option<&mut Stage>{
self.stages.get_mut(stage.0 as usize)
}
pub fn get_spawn_model_id(&self,stage:StageId)->Option<ModelId>{
self.stages.get(stage.0 as usize).map(|s|s.spawn)
}
pub fn get_zone(&self,model_id:ModelId)->Option<&Zone>{
self.zones.get(&model_id)
}
pub fn get_stage(&self,stage_id:StageId)->Option<&Stage>{
self.stages.get(stage_id.0 as usize)
}
pub fn get_element(&self,model_id:ModelId)->Option<&StageElement>{
self.elements.get(&model_id)
}
//TODO: put this in the SNF
pub fn denormalize_data(&mut self){
//expand and index normalized data
self.zones.insert(self.start,Zone::Start);
for (stage_id,stage) in self.stages.iter().enumerate(){
self.elements.insert(stage.spawn,StageElement{
stage_id:StageId(stage_id as u32),
force:false,
behaviour:StageElementBehaviour::SpawnAt,
jump_limit:None,
});
for (_,&model) in &stage.ordered_checkpoints{
self.elements.insert(model,StageElement{
stage_id:StageId(stage_id as u32),
force:false,
behaviour:StageElementBehaviour::Checkpoint,
jump_limit:None,
});
}
for &model in &stage.unordered_checkpoints{
self.elements.insert(model,StageElement{
stage_id:StageId(stage_id as u32),
force:false,
behaviour:StageElementBehaviour::Checkpoint,
jump_limit:None,
});
}
}
}
impl Mode {
pub fn new(
style: gameplay_style::StyleModifiers,
start: ModelId,
zones: HashMap<ModelId, Zone>,
stages: Vec<Stage>,
elements: HashMap<ModelId, StageElement>,
) -> Self {
Self {
style,
start,
zones,
stages,
elements,
}
}
pub fn empty(style: gameplay_style::StyleModifiers, start: ModelId) -> Self {
Self {
style,
start,
zones: HashMap::new(),
stages: Vec::new(),
elements: HashMap::new(),
}
}
pub fn into_inner(
self,
) -> (
gameplay_style::StyleModifiers,
ModelId,
HashMap<ModelId, Zone>,
Vec<Stage>,
HashMap<ModelId, StageElement>,
) {
(
self.style,
self.start,
self.zones,
self.stages,
self.elements,
)
}
pub const fn get_start(&self) -> ModelId {
self.start
}
pub const fn get_style(&self) -> &gameplay_style::StyleModifiers {
&self.style
}
pub fn push_stage(&mut self, stage: Stage) {
self.stages.push(stage)
}
pub fn get_stage_mut(&mut self, stage: StageId) -> Option<&mut Stage> {
self.stages.get_mut(stage.0 as usize)
}
pub fn get_spawn_model_id(&self, stage: StageId) -> Option<ModelId> {
self.stages.get(stage.0 as usize).map(|s| s.spawn)
}
pub fn get_zone(&self, model_id: ModelId) -> Option<&Zone> {
self.zones.get(&model_id)
}
pub fn get_stage(&self, stage_id: StageId) -> Option<&Stage> {
self.stages.get(stage_id.0 as usize)
}
pub fn get_element(&self, model_id: ModelId) -> Option<&StageElement> {
self.elements.get(&model_id)
}
//TODO: put this in the SNF
pub fn denormalize_data(&mut self) {
//expand and index normalized data
self.zones.insert(self.start, Zone::Start);
for (stage_id, stage) in self.stages.iter().enumerate() {
self.elements.insert(
stage.spawn,
StageElement {
stage_id: StageId(stage_id as u32),
force: false,
behaviour: StageElementBehaviour::SpawnAt,
jump_limit: None,
},
);
for (_, &model) in &stage.ordered_checkpoints {
self.elements.insert(
model,
StageElement {
stage_id: StageId(stage_id as u32),
force: false,
behaviour: StageElementBehaviour::Checkpoint,
jump_limit: None,
},
);
}
for &model in &stage.unordered_checkpoints {
self.elements.insert(
model,
StageElement {
stage_id: StageId(stage_id as u32),
force: false,
behaviour: StageElementBehaviour::Checkpoint,
jump_limit: None,
},
);
}
}
}
}
//this would be nice as a macro
#[derive(Default)]
pub struct ModeUpdate{
zones:HashMap<ModelId,Zone>,
stages:HashMap<StageId,StageUpdate>,
//mutually exlusive stage element behaviour
elements:HashMap<ModelId,StageElement>,
pub struct ModeUpdate {
zones: HashMap<ModelId, Zone>,
stages: HashMap<StageId, StageUpdate>,
//mutually exlusive stage element behaviour
elements: HashMap<ModelId, StageElement>,
}
impl Updatable<ModeUpdate> for Mode{
fn update(&mut self,update:ModeUpdate){
self.zones.extend(update.zones);
for (stage,stage_update) in update.stages{
if let Some(stage)=self.stages.get_mut(stage.0 as usize){
stage.update(stage_update);
}
}
self.elements.extend(update.elements);
}
impl Updatable<ModeUpdate> for Mode {
fn update(&mut self, update: ModeUpdate) {
self.zones.extend(update.zones);
for (stage, stage_update) in update.stages {
if let Some(stage) = self.stages.get_mut(stage.0 as usize) {
stage.update(stage_update);
}
}
self.elements.extend(update.elements);
}
}
impl ModeUpdate{
pub fn zone(model_id:ModelId,zone:Zone)->Self{
let mut mu=Self::default();
mu.zones.insert(model_id,zone);
mu
}
pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{
let mut mu=Self::default();
mu.stages.insert(stage_id,stage_update);
mu
}
pub fn element(model_id:ModelId,element:StageElement)->Self{
let mut mu=Self::default();
mu.elements.insert(model_id,element);
mu
}
pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){
for (_,stage_element) in self.elements.iter_mut(){
stage_element.stage_id=f(stage_element.stage_id);
}
}
impl ModeUpdate {
pub fn zone(model_id: ModelId, zone: Zone) -> Self {
let mut mu = Self::default();
mu.zones.insert(model_id, zone);
mu
}
pub fn stage(stage_id: StageId, stage_update: StageUpdate) -> Self {
let mut mu = Self::default();
mu.stages.insert(stage_id, stage_update);
mu
}
pub fn element(model_id: ModelId, element: StageElement) -> Self {
let mut mu = Self::default();
mu.elements.insert(model_id, element);
mu
}
pub fn map_stage_element_ids<F: Fn(StageId) -> StageId>(&mut self, f: F) {
for (_, stage_element) in self.elements.iter_mut() {
stage_element.stage_id = f(stage_element.stage_id);
}
}
}
#[derive(Default,Clone)]
pub struct Modes{
pub modes:Vec<Mode>,
#[derive(Default, Clone)]
pub struct Modes {
pub modes: Vec<Mode>,
}
impl Modes{
pub const fn new(modes:Vec<Mode>)->Self{
Self{
modes,
}
}
pub fn into_inner(self)->Vec<Mode>{
self.modes
}
pub fn push_mode(&mut self,mode:Mode){
self.modes.push(mode)
}
pub fn get_mode(&self,mode:ModeId)->Option<&Mode>{
self.modes.get(mode.0 as usize)
}
impl Modes {
pub const fn new(modes: Vec<Mode>) -> Self {
Self { modes }
}
pub fn into_inner(self) -> Vec<Mode> {
self.modes
}
pub fn push_mode(&mut self, mode: Mode) {
self.modes.push(mode)
}
pub fn get_mode(&self, mode: ModeId) -> Option<&Mode> {
self.modes.get(mode.0 as usize)
}
}
pub struct ModesUpdate{
modes:HashMap<ModeId,ModeUpdate>,
pub struct ModesUpdate {
modes: HashMap<ModeId, ModeUpdate>,
}
impl Updatable<ModesUpdate> for Modes{
fn update(&mut self,update:ModesUpdate){
for (mode,mode_update) in update.modes{
if let Some(mode)=self.modes.get_mut(mode.0 as usize){
mode.update(mode_update);
}
}
}
impl Updatable<ModesUpdate> for Modes {
fn update(&mut self, update: ModesUpdate) {
for (mode, mode_update) in update.modes {
if let Some(mode) = self.modes.get_mut(mode.0 as usize) {
mode.update(mode_update);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +1,66 @@
use crate::integer::Time;
#[derive(Debug)]
pub struct TimedInstruction<I,T>{
pub time:Time<T>,
pub instruction:I,
pub struct TimedInstruction<I, T> {
pub time: Time<T>,
pub instruction: I,
}
pub trait InstructionEmitter{
type Instruction;
type TimeInner;
fn next_instruction(&self,time_limit:Time<Self::TimeInner>)->Option<TimedInstruction<Self::Instruction,Self::TimeInner>>;
pub trait InstructionEmitter {
type Instruction;
type TimeInner;
fn next_instruction(
&self,
time_limit: Time<Self::TimeInner>,
) -> Option<TimedInstruction<Self::Instruction, Self::TimeInner>>;
}
pub trait InstructionConsumer{
type Instruction;
type TimeInner;
fn process_instruction(&mut self, instruction:TimedInstruction<Self::Instruction,Self::TimeInner>);
pub trait InstructionConsumer {
type Instruction;
type TimeInner;
fn process_instruction(
&mut self,
instruction: TimedInstruction<Self::Instruction, Self::TimeInner>,
);
}
//PROPER PRIVATE FIELDS!!!
pub struct InstructionCollector<I,T>{
time:Time<T>,
instruction:Option<I>,
pub struct InstructionCollector<I, T> {
time: Time<T>,
instruction: Option<I>,
}
impl<I,T> InstructionCollector<I,T>
where Time<T>:Copy+PartialOrd,
impl<I, T> InstructionCollector<I, T>
where
Time<T>: Copy + PartialOrd,
{
pub const fn new(time:Time<T>)->Self{
Self{
time,
instruction:None
}
}
#[inline]
pub const fn time(&self)->Time<T>{
self.time
}
pub fn collect(&mut self,instruction:Option<TimedInstruction<I,T>>){
match instruction{
Some(unwrap_instruction)=>{
if unwrap_instruction.time<self.time {
self.time=unwrap_instruction.time;
self.instruction=Some(unwrap_instruction.instruction);
}
},
None=>(),
}
}
pub fn instruction(self)->Option<TimedInstruction<I,T>>{
//STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR
match self.instruction{
Some(instruction)=>Some(TimedInstruction{
time:self.time,
instruction
}),
None=>None,
}
}
pub const fn new(time: Time<T>) -> Self {
Self {
time,
instruction: None,
}
}
#[inline]
pub const fn time(&self) -> Time<T> {
self.time
}
pub fn collect(&mut self, instruction: Option<TimedInstruction<I, T>>) {
match instruction {
Some(unwrap_instruction) => {
if unwrap_instruction.time < self.time {
self.time = unwrap_instruction.time;
self.instruction = Some(unwrap_instruction.instruction);
}
}
None => (),
}
}
pub fn instruction(self) -> Option<TimedInstruction<I, T>> {
//STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR
match self.instruction {
Some(instruction) => Some(TimedInstruction {
time: self.time,
instruction,
}),
None => None,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
pub mod bvh;
pub mod map;
pub mod run;
pub mod aabb;
pub mod model;
pub mod mouse;
pub mod timer;
pub mod integer;
pub mod physics;
pub mod session;
pub mod updatable;
pub mod instruction;
pub mod bvh;
pub mod controls_bitflag;
pub mod gameplay_attributes;
pub mod gameplay_modes;
pub mod gameplay_style;
pub mod controls_bitflag;
pub mod instruction;
pub mod integer;
pub mod map;
pub mod model;
pub mod mouse;
pub mod physics;
pub mod run;
pub mod session;
pub mod timer;
pub mod updatable;

View File

@ -1,14 +1,14 @@
use crate::model;
use crate::gameplay_modes;
use crate::gameplay_attributes;
use crate::gameplay_modes;
use crate::model;
//this is a temporary struct to try to get the code running again
//TODO: use snf::map::Region to update the data in physics and graphics instead of this
pub struct CompleteMap{
pub modes:gameplay_modes::Modes,
pub attributes:Vec<gameplay_attributes::CollisionAttributes>,
pub meshes:Vec<model::Mesh>,
pub models:Vec<model::Model>,
//RenderPattern
pub textures:Vec<Vec<u8>>,
pub render_configs:Vec<model::RenderConfig>,
pub struct CompleteMap {
pub modes: gameplay_modes::Modes,
pub attributes: Vec<gameplay_attributes::CollisionAttributes>,
pub meshes: Vec<model::Mesh>,
pub models: Vec<model::Model>,
//RenderPattern
pub textures: Vec<Vec<u8>>,
pub render_configs: Vec<model::RenderConfig>,
}

View File

@ -1,51 +1,56 @@
use crate::integer::{Planar64Vec3,Planar64Affine3};
use crate::gameplay_attributes;
use crate::integer::{Planar64Affine3, Planar64Vec3};
pub type TextureCoordinate=glam::Vec2;
pub type Color4=glam::Vec4;
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
pub type TextureCoordinate = glam::Vec2;
pub type Color4 = glam::Vec4;
#[derive(Clone, Copy, Hash, id::Id, PartialEq, Eq)]
pub struct PositionId(u32);
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
#[derive(Clone, Copy, Hash, id::Id, PartialEq, Eq)]
pub struct TextureCoordinateId(u32);
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
#[derive(Clone, Copy, Hash, id::Id, PartialEq, Eq)]
pub struct NormalId(u32);
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
#[derive(Clone, Copy, Hash, id::Id, PartialEq, Eq)]
pub struct ColorId(u32);
#[derive(Clone,Hash,PartialEq,Eq)]
pub struct IndexedVertex{
pub pos:PositionId,
pub tex:TextureCoordinateId,
pub normal:NormalId,
pub color:ColorId,
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct IndexedVertex {
pub pos: PositionId,
pub tex: TextureCoordinateId,
pub normal: NormalId,
pub color: ColorId,
}
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
#[derive(Clone, Copy, Hash, id::Id, PartialEq, Eq)]
pub struct VertexId(u32);
pub type IndexedVertexList=Vec<VertexId>;
pub trait PolygonIter{
fn polys(&self)->impl Iterator<Item=&[VertexId]>;
pub type IndexedVertexList = Vec<VertexId>;
pub trait PolygonIter {
fn polys(&self) -> impl Iterator<Item = &[VertexId]>;
}
pub trait MapVertexId{
fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self;
pub trait MapVertexId {
fn map_vertex_id<F: Fn(VertexId) -> VertexId>(self, f: F) -> Self;
}
#[derive(Clone)]
pub struct PolygonList(Vec<IndexedVertexList>);
impl PolygonList{
pub const fn new(list:Vec<IndexedVertexList>)->Self{
Self(list)
}
pub fn extend<T:IntoIterator<Item=IndexedVertexList>>(&mut self,iter:T){
self.0.extend(iter);
}
impl PolygonList {
pub const fn new(list: Vec<IndexedVertexList>) -> Self {
Self(list)
}
pub fn extend<T: IntoIterator<Item = IndexedVertexList>>(&mut self, iter: T) {
self.0.extend(iter);
}
}
impl PolygonIter for PolygonList{
fn polys(&self)->impl Iterator<Item=&[VertexId]>{
self.0.iter().map(|poly|poly.as_slice())
}
impl PolygonIter for PolygonList {
fn polys(&self) -> impl Iterator<Item = &[VertexId]> {
self.0.iter().map(|poly| poly.as_slice())
}
}
impl MapVertexId for PolygonList{
fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self{
Self(self.0.into_iter().map(|ivl|ivl.into_iter().map(&f).collect()).collect())
}
impl MapVertexId for PolygonList {
fn map_vertex_id<F: Fn(VertexId) -> VertexId>(self, f: F) -> Self {
Self(
self.0
.into_iter()
.map(|ivl| ivl.into_iter().map(&f).collect())
.collect(),
)
}
}
// pub struct TriangleStrip(IndexedVertexList);
// impl PolygonIter for TriangleStrip{
@ -53,81 +58,81 @@ impl MapVertexId for PolygonList{
// self.0.vertices.windows(3).enumerate().map(|(i,s)|if i&0!=0{return s.iter().rev()}else{return s.iter()})
// }
// }
#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)]
#[derive(Clone, Copy, Hash, id::Id, PartialEq, Eq)]
pub struct PolygonGroupId(u32);
#[derive(Clone)]
pub enum PolygonGroup{
PolygonList(PolygonList),
//TriangleStrip(TriangleStrip),
pub enum PolygonGroup {
PolygonList(PolygonList),
//TriangleStrip(TriangleStrip),
}
impl PolygonIter for PolygonGroup{
fn polys(&self)->impl Iterator<Item=&[VertexId]>{
match self{
PolygonGroup::PolygonList(list)=>list.polys(),
//PolygonGroup::TriangleStrip(strip)=>strip.polys(),
}
}
impl PolygonIter for PolygonGroup {
fn polys(&self) -> impl Iterator<Item = &[VertexId]> {
match self {
PolygonGroup::PolygonList(list) => list.polys(),
//PolygonGroup::TriangleStrip(strip)=>strip.polys(),
}
}
}
impl MapVertexId for PolygonGroup{
fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self{
match self{
PolygonGroup::PolygonList(polys)=>Self::PolygonList(polys.map_vertex_id(f)),
}
}
impl MapVertexId for PolygonGroup {
fn map_vertex_id<F: Fn(VertexId) -> VertexId>(self, f: F) -> Self {
match self {
PolygonGroup::PolygonList(polys) => Self::PolygonList(polys.map_vertex_id(f)),
}
}
}
/// Ah yes, a group of things to render at the same time
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
#[derive(Clone, Copy, Debug, Hash, id::Id, Eq, PartialEq)]
pub struct TextureId(u32);
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
#[derive(Clone, Copy, Hash, id::Id, Eq, PartialEq)]
pub struct RenderConfigId(u32);
#[derive(Clone,Copy,Default)]
pub struct RenderConfig{
pub texture:Option<TextureId>,
#[derive(Clone, Copy, Default)]
pub struct RenderConfig {
pub texture: Option<TextureId>,
}
impl RenderConfig{
pub const fn texture(texture:TextureId)->Self{
Self{
texture:Some(texture),
}
}
impl RenderConfig {
pub const fn texture(texture: TextureId) -> Self {
Self {
texture: Some(texture),
}
}
}
#[derive(Clone)]
pub struct IndexedGraphicsGroup{
//Render pattern material/texture/shader/flat color
pub render:RenderConfigId,
pub groups:Vec<PolygonGroupId>,
pub struct IndexedGraphicsGroup {
//Render pattern material/texture/shader/flat color
pub render: RenderConfigId,
pub groups: Vec<PolygonGroupId>,
}
#[derive(Clone,Default)]
pub struct IndexedPhysicsGroup{
//the polygons in this group are guaranteed to make a closed convex shape
pub groups:Vec<PolygonGroupId>,
#[derive(Clone, Default)]
pub struct IndexedPhysicsGroup {
//the polygons in this group are guaranteed to make a closed convex shape
pub groups: Vec<PolygonGroupId>,
}
//This is a superset of PhysicsModel and GraphicsModel
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
#[derive(Clone, Copy, Debug, Hash, id::Id, Eq, PartialEq)]
pub struct MeshId(u32);
#[derive(Clone)]
pub struct Mesh{
pub unique_pos:Vec<Planar64Vec3>,//Unit32Vec3
pub unique_normal:Vec<Planar64Vec3>,//Unit32Vec3
pub unique_tex:Vec<TextureCoordinate>,
pub unique_color:Vec<Color4>,
pub unique_vertices:Vec<IndexedVertex>,
//polygon groups are constant texture AND convexity slices
//note that this may need to be changed to be a list of individual faces
//for submeshes to work since face ids need to be consistent across submeshes
//so face == polygon_groups[face_id]
pub polygon_groups:Vec<PolygonGroup>,
//graphics indexed (by texture)
pub graphics_groups:Vec<IndexedGraphicsGroup>,
//physics indexed (by convexity)
pub physics_groups:Vec<IndexedPhysicsGroup>,
pub struct Mesh {
pub unique_pos: Vec<Planar64Vec3>, //Unit32Vec3
pub unique_normal: Vec<Planar64Vec3>, //Unit32Vec3
pub unique_tex: Vec<TextureCoordinate>,
pub unique_color: Vec<Color4>,
pub unique_vertices: Vec<IndexedVertex>,
//polygon groups are constant texture AND convexity slices
//note that this may need to be changed to be a list of individual faces
//for submeshes to work since face ids need to be consistent across submeshes
//so face == polygon_groups[face_id]
pub polygon_groups: Vec<PolygonGroup>,
//graphics indexed (by texture)
pub graphics_groups: Vec<IndexedGraphicsGroup>,
//physics indexed (by convexity)
pub physics_groups: Vec<IndexedPhysicsGroup>,
}
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
#[derive(Debug, Clone, Copy, Hash, id::Id, Eq, PartialEq)]
pub struct ModelId(u32);
pub struct Model{
pub mesh:MeshId,
pub attributes:gameplay_attributes::CollisionAttributesId,
pub color:Color4,//transparency is in here
pub transform:Planar64Affine3,
pub struct Model {
pub mesh: MeshId,
pub attributes: gameplay_attributes::CollisionAttributesId,
pub color: Color4, //transparency is in here
pub transform: Planar64Affine3,
}

View File

@ -1,28 +1,29 @@
use crate::integer::Time;
#[derive(Clone,Debug)]
pub struct MouseState<T>{
pub pos:glam::IVec2,
pub time:Time<T>,
#[derive(Clone, Debug)]
pub struct MouseState<T> {
pub pos: glam::IVec2,
pub time: Time<T>,
}
impl<T> Default for MouseState<T>{
fn default()->Self{
Self{
time:Time::ZERO,
pos:glam::IVec2::ZERO,
}
}
impl<T> Default for MouseState<T> {
fn default() -> Self {
Self {
time: Time::ZERO,
pos: glam::IVec2::ZERO,
}
}
}
impl<T> MouseState<T>
where Time<T>:Copy,
where
Time<T>: Copy,
{
pub fn lerp(&self,target:&MouseState<T>,time:Time<T>)->glam::IVec2{
let m0=self.pos.as_i64vec2();
let m1=target.pos.as_i64vec2();
//these are deltas
let t1t=(target.time-time).nanos();
let tt0=(time-self.time).nanos();
let dt=(target.time-self.time).nanos();
((m0*t1t+m1*tt0)/dt).as_ivec2()
}
pub fn lerp(&self, target: &MouseState<T>, time: Time<T>) -> glam::IVec2 {
let m0 = self.pos.as_i64vec2();
let m1 = target.pos.as_i64vec2();
//these are deltas
let t1t = (target.time - time).nanos();
let tt0 = (time - self.time).nanos();
let dt = (target.time - self.time).nanos();
((m0 * t1t + m1 * tt0) / dt).as_ivec2()
}
}

View File

@ -1,31 +1,37 @@
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub enum TimeInner{}
pub type Time=crate::integer::Time<TimeInner>;
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Debug)]
pub enum TimeInner {}
pub type Time = crate::integer::Time<TimeInner>;
#[derive(Clone,Debug)]
pub enum Instruction{
ReplaceMouse(crate::mouse::MouseState<TimeInner>,crate::mouse::MouseState<TimeInner>),
SetNextMouse(crate::mouse::MouseState<TimeInner>),
SetMoveRight(bool),
SetMoveUp(bool),
SetMoveBack(bool),
SetMoveLeft(bool),
SetMoveDown(bool),
SetMoveForward(bool),
SetJump(bool),
SetZoom(bool),
/// Reset: fully replace the physics state.
/// This forgets all inputs and settings which need to be reapplied.
Reset,
/// Restart: Teleport to the start zone.
Restart,
/// Spawn: Teleport to a specific mode's spawn
/// Sets current mode & spawn
Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId),
Idle,
//Idle: there were no input events, but the simulation is safe to advance to this timestep
//for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation.
PracticeFly,
SetSensitivity(crate::integer::Ratio64Vec2),
#[derive(Clone, Debug)]
pub enum Instruction {
ReplaceMouse(
crate::mouse::MouseState<TimeInner>,
crate::mouse::MouseState<TimeInner>,
),
SetNextMouse(crate::mouse::MouseState<TimeInner>),
SetMoveRight(bool),
SetMoveUp(bool),
SetMoveBack(bool),
SetMoveLeft(bool),
SetMoveDown(bool),
SetMoveForward(bool),
SetJump(bool),
SetZoom(bool),
/// Reset: fully replace the physics state.
/// This forgets all inputs and settings which need to be reapplied.
Reset,
/// Restart: Teleport to the start zone.
Restart,
/// Spawn: Teleport to a specific mode's spawn
/// Sets current mode & spawn
Spawn(
crate::gameplay_modes::ModeId,
crate::gameplay_modes::StageId,
),
Idle,
//Idle: there were no input events, but the simulation is safe to advance to this timestep
//for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation.
PracticeFly,
SetSensitivity(crate::integer::Ratio64Vec2),
}

View File

@ -1,113 +1,117 @@
use crate::timer::{TimerFixed,Realtime,Paused,Unpaused};
use crate::timer::{Paused, Realtime, TimerFixed, Unpaused};
use crate::physics::{TimeInner as PhysicsTimeInner,Time as PhysicsTime};
use crate::physics::{Time as PhysicsTime, TimeInner as PhysicsTimeInner};
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub enum TimeInner{}
pub type Time=crate::integer::Time<TimeInner>;
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Debug)]
pub enum TimeInner {}
pub type Time = crate::integer::Time<TimeInner>;
#[derive(Clone,Copy,Debug)]
pub enum FlagReason{
Anticheat,
StyleChange,
Clock,
Pause,
Flying,
Gravity,
Timescale,
TimeTravel,
Teleport,
#[derive(Clone, Copy, Debug)]
pub enum FlagReason {
Anticheat,
StyleChange,
Clock,
Pause,
Flying,
Gravity,
Timescale,
TimeTravel,
Teleport,
}
impl ToString for FlagReason{
fn to_string(&self)->String{
self.as_ref().to_owned()
}
impl ToString for FlagReason {
fn to_string(&self) -> String {
self.as_ref().to_owned()
}
}
impl AsRef<str> for FlagReason{
fn as_ref(&self)->&str{
match self{
FlagReason::Anticheat=>"Passed through anticheat zone.",
FlagReason::StyleChange=>"Changed style.",
FlagReason::Clock=>"Incorrect clock. (This can be caused by internet hiccups)",
FlagReason::Pause=>"Pausing is not allowed in this style.",
FlagReason::Flying=>"Flying is not allowed in this style.",
FlagReason::Gravity=>"Gravity modification is not allowed in this style.",
FlagReason::Timescale=>"Timescale is not allowed in this style.",
FlagReason::TimeTravel=>"Time travel is not allowed in this style.",
FlagReason::Teleport=>"Illegal teleport.",
}
}
impl AsRef<str> for FlagReason {
fn as_ref(&self) -> &str {
match self {
FlagReason::Anticheat => "Passed through anticheat zone.",
FlagReason::StyleChange => "Changed style.",
FlagReason::Clock => "Incorrect clock. (This can be caused by internet hiccups)",
FlagReason::Pause => "Pausing is not allowed in this style.",
FlagReason::Flying => "Flying is not allowed in this style.",
FlagReason::Gravity => "Gravity modification is not allowed in this style.",
FlagReason::Timescale => "Timescale is not allowed in this style.",
FlagReason::TimeTravel => "Time travel is not allowed in this style.",
FlagReason::Teleport => "Illegal teleport.",
}
}
}
#[derive(Debug)]
pub enum Error{
NotStarted,
AlreadyStarted,
AlreadyFinished,
pub enum Error {
NotStarted,
AlreadyStarted,
AlreadyFinished,
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
#[derive(Clone,Copy,Debug)]
enum RunState{
Created,
Started{timer:TimerFixed<Realtime<PhysicsTimeInner,TimeInner>,Unpaused>},
Finished{timer:TimerFixed<Realtime<PhysicsTimeInner,TimeInner>,Paused>},
#[derive(Clone, Copy, Debug)]
enum RunState {
Created,
Started {
timer: TimerFixed<Realtime<PhysicsTimeInner, TimeInner>, Unpaused>,
},
Finished {
timer: TimerFixed<Realtime<PhysicsTimeInner, TimeInner>, Paused>,
},
}
#[derive(Clone,Copy,Debug)]
pub struct Run{
state:RunState,
flagged:Option<FlagReason>,
#[derive(Clone, Copy, Debug)]
pub struct Run {
state: RunState,
flagged: Option<FlagReason>,
}
impl Run{
pub fn new()->Self{
Self{
state:RunState::Created,
flagged:None,
}
}
pub fn time(&self,time:PhysicsTime)->Time{
match &self.state{
RunState::Created=>Time::ZERO,
RunState::Started{timer}=>timer.time(time),
RunState::Finished{timer}=>timer.time(time),
}
}
pub fn start(&mut self,time:PhysicsTime)->Result<(),Error>{
match &self.state{
RunState::Created=>{
self.state=RunState::Started{
timer:TimerFixed::new(time,Time::ZERO),
};
Ok(())
},
RunState::Started{..}=>Err(Error::AlreadyStarted),
RunState::Finished{..}=>Err(Error::AlreadyFinished),
}
}
pub fn finish(&mut self,time:PhysicsTime)->Result<(),Error>{
//this uses Copy
match &self.state{
RunState::Created=>Err(Error::NotStarted),
RunState::Started{timer}=>{
self.state=RunState::Finished{
timer:timer.into_paused(time),
};
Ok(())
},
RunState::Finished{..}=>Err(Error::AlreadyFinished),
}
}
pub fn flag(&mut self,flag_reason:FlagReason){
//don't replace the first reason the run was flagged
if self.flagged.is_none(){
self.flagged=Some(flag_reason);
}
}
impl Run {
pub fn new() -> Self {
Self {
state: RunState::Created,
flagged: None,
}
}
pub fn time(&self, time: PhysicsTime) -> Time {
match &self.state {
RunState::Created => Time::ZERO,
RunState::Started { timer } => timer.time(time),
RunState::Finished { timer } => timer.time(time),
}
}
pub fn start(&mut self, time: PhysicsTime) -> Result<(), Error> {
match &self.state {
RunState::Created => {
self.state = RunState::Started {
timer: TimerFixed::new(time, Time::ZERO),
};
Ok(())
}
RunState::Started { .. } => Err(Error::AlreadyStarted),
RunState::Finished { .. } => Err(Error::AlreadyFinished),
}
}
pub fn finish(&mut self, time: PhysicsTime) -> Result<(), Error> {
//this uses Copy
match &self.state {
RunState::Created => Err(Error::NotStarted),
RunState::Started { timer } => {
self.state = RunState::Finished {
timer: timer.into_paused(time),
};
Ok(())
}
RunState::Finished { .. } => Err(Error::AlreadyFinished),
}
}
pub fn flag(&mut self, flag_reason: FlagReason) {
//don't replace the first reason the run was flagged
if self.flagged.is_none() {
self.flagged = Some(flag_reason);
}
}
}

View File

@ -1,3 +1,3 @@
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub enum TimeInner{}
pub type Time=crate::integer::Time<TimeInner>;
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Debug)]
pub enum TimeInner {}
pub type Time = crate::integer::Time<TimeInner>;

View File

@ -1,363 +1,372 @@
use crate::integer::{Time,Ratio64};
use crate::integer::{Ratio64, Time};
#[derive(Clone,Copy,Debug)]
#[derive(Clone, Copy, Debug)]
pub struct Paused;
#[derive(Clone,Copy,Debug)]
#[derive(Clone, Copy, Debug)]
pub struct Unpaused;
pub trait PauseState:Copy+std::fmt::Debug{
const IS_PAUSED:bool;
fn new()->Self;
pub trait PauseState: Copy + std::fmt::Debug {
const IS_PAUSED: bool;
fn new() -> Self;
}
impl PauseState for Paused{
const IS_PAUSED:bool=true;
fn new()->Self{
Self
}
impl PauseState for Paused {
const IS_PAUSED: bool = true;
fn new() -> Self {
Self
}
}
impl PauseState for Unpaused{
const IS_PAUSED:bool=false;
fn new()->Self{
Self
}
impl PauseState for Unpaused {
const IS_PAUSED: bool = false;
fn new() -> Self {
Self
}
}
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
enum Inner{}
type InnerTime=Time<Inner>;
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Debug)]
enum Inner {}
type InnerTime = Time<Inner>;
#[derive(Clone,Copy,Debug)]
pub struct Realtime<In,Out>{
offset:InnerTime,
_in:core::marker::PhantomData<In>,
_out:core::marker::PhantomData<Out>,
#[derive(Clone, Copy, Debug)]
pub struct Realtime<In, Out> {
offset: InnerTime,
_in: core::marker::PhantomData<In>,
_out: core::marker::PhantomData<Out>,
}
impl<In,Out> Realtime<In,Out>{
pub const fn new(offset:InnerTime)->Self{
Self{
offset,
_in:core::marker::PhantomData,
_out:core::marker::PhantomData,
}
}
impl<In, Out> Realtime<In, Out> {
pub const fn new(offset: InnerTime) -> Self {
Self {
offset,
_in: core::marker::PhantomData,
_out: core::marker::PhantomData,
}
}
}
#[derive(Clone,Copy,Debug)]
pub struct Scaled<In,Out>{
scale:Ratio64,
offset:InnerTime,
_in:core::marker::PhantomData<In>,
_out:core::marker::PhantomData<Out>,
#[derive(Clone, Copy, Debug)]
pub struct Scaled<In, Out> {
scale: Ratio64,
offset: InnerTime,
_in: core::marker::PhantomData<In>,
_out: core::marker::PhantomData<Out>,
}
impl<In,Out> Scaled<In,Out>
where Time<In>:Copy,
impl<In, Out> Scaled<In, Out>
where
Time<In>: Copy,
{
pub const fn new(scale:Ratio64,offset:InnerTime)->Self{
Self{
scale,
offset,
_in:core::marker::PhantomData,
_out:core::marker::PhantomData,
}
}
const fn with_scale(scale:Ratio64)->Self{
Self::new(scale,InnerTime::ZERO)
}
const fn scale(&self,time:Time<In>)->InnerTime{
InnerTime::raw(self.scale.mul_int(time.get()))
}
const fn get_scale(&self)->Ratio64{
self.scale
}
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);
}
pub const fn new(scale: Ratio64, offset: InnerTime) -> Self {
Self {
scale,
offset,
_in: core::marker::PhantomData,
_out: core::marker::PhantomData,
}
}
const fn with_scale(scale: Ratio64) -> Self {
Self::new(scale, InnerTime::ZERO)
}
const fn scale(&self, time: Time<In>) -> InnerTime {
InnerTime::raw(self.scale.mul_int(time.get()))
}
const fn get_scale(&self) -> Ratio64 {
self.scale
}
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);
}
}
pub trait TimerState{
type In;
type Out;
fn identity()->Self;
fn get_time(&self,time:Time<Self::In>)->Time<Self::Out>;
fn set_time(&mut self,time:Time<Self::In>,new_time:Time<Self::Out>);
fn get_offset(&self)->InnerTime;
fn set_offset(&mut self,offset:InnerTime);
pub trait TimerState {
type In;
type Out;
fn identity() -> Self;
fn get_time(&self, time: Time<Self::In>) -> Time<Self::Out>;
fn set_time(&mut self, time: Time<Self::In>, new_time: Time<Self::Out>);
fn get_offset(&self) -> InnerTime;
fn set_offset(&mut self, offset: InnerTime);
}
impl<In,Out> TimerState for Realtime<In,Out>{
type In=In;
type Out=Out;
fn identity()->Self{
Self::new(InnerTime::ZERO)
}
fn get_time(&self,time:Time<In>)->Time<Out>{
time.coerce()+self.offset.coerce()
}
fn set_time(&mut self,time:Time<In>,new_time:Time<Out>){
self.offset=new_time.coerce()-time.coerce();
}
fn get_offset(&self)->InnerTime{
self.offset
}
fn set_offset(&mut self,offset:InnerTime){
self.offset=offset;
}
impl<In, Out> TimerState for Realtime<In, Out> {
type In = In;
type Out = Out;
fn identity() -> Self {
Self::new(InnerTime::ZERO)
}
fn get_time(&self, time: Time<In>) -> Time<Out> {
time.coerce() + self.offset.coerce()
}
fn set_time(&mut self, time: Time<In>, new_time: Time<Out>) {
self.offset = new_time.coerce() - time.coerce();
}
fn get_offset(&self) -> InnerTime {
self.offset
}
fn set_offset(&mut self, offset: InnerTime) {
self.offset = offset;
}
}
impl<In,Out> TimerState for Scaled<In,Out>
where Time<In>:Copy,
impl<In, Out> TimerState for Scaled<In, Out>
where
Time<In>: Copy,
{
type In=In;
type Out=Out;
fn identity()->Self{
Self::new(Ratio64::ONE,InnerTime::ZERO)
}
fn get_time(&self,time:Time<In>)->Time<Out>{
(self.scale(time)+self.offset).coerce()
}
fn set_time(&mut self,time:Time<In>,new_time:Time<Out>){
self.offset=new_time.coerce()-self.scale(time);
}
fn get_offset(&self)->InnerTime{
self.offset
}
fn set_offset(&mut self,offset:InnerTime){
self.offset=offset;
}
type In = In;
type Out = Out;
fn identity() -> Self {
Self::new(Ratio64::ONE, InnerTime::ZERO)
}
fn get_time(&self, time: Time<In>) -> Time<Out> {
(self.scale(time) + self.offset).coerce()
}
fn set_time(&mut self, time: Time<In>, new_time: Time<Out>) {
self.offset = new_time.coerce() - self.scale(time);
}
fn get_offset(&self) -> InnerTime {
self.offset
}
fn set_offset(&mut self, offset: InnerTime) {
self.offset = offset;
}
}
#[derive(Clone,Copy,Debug)]
pub struct TimerFixed<T:TimerState,P:PauseState>{
state:T,
_paused:P,
#[derive(Clone, Copy, Debug)]
pub struct TimerFixed<T: TimerState, P: PauseState> {
state: T,
_paused: P,
}
//scaled timer methods are generic across PauseState
impl<P:PauseState,In,Out> TimerFixed<Scaled<In,Out>,P>
where Time<In>:Copy,
impl<P: PauseState, In, Out> TimerFixed<Scaled<In, Out>, P>
where
Time<In>: Copy,
{
pub fn scaled(time:Time<In>,new_time:Time<Out>,scale:Ratio64)->Self{
let mut timer=Self{
state:Scaled::with_scale(scale),
_paused:P::new(),
};
timer.set_time(time,new_time);
timer
}
pub const fn get_scale(&self)->Ratio64{
self.state.get_scale()
}
pub fn set_scale(&mut self,time:Time<In>,new_scale:Ratio64){
self.state.set_scale(time,new_scale)
}
pub fn scaled(time: Time<In>, new_time: Time<Out>, scale: Ratio64) -> Self {
let mut timer = Self {
state: Scaled::with_scale(scale),
_paused: P::new(),
};
timer.set_time(time, new_time);
timer
}
pub const fn get_scale(&self) -> Ratio64 {
self.state.get_scale()
}
pub fn set_scale(&mut self, time: Time<In>, new_scale: Ratio64) {
self.state.set_scale(time, new_scale)
}
}
//pause and unpause is generic across TimerState
impl<T:TimerState> TimerFixed<T,Paused>
where Time<T::In>:Copy,
impl<T: TimerState> TimerFixed<T, Paused>
where
Time<T::In>: Copy,
{
pub fn into_unpaused(self,time:Time<T::In>)->TimerFixed<T,Unpaused>{
let new_time=self.time(time);
let mut timer=TimerFixed{
state:self.state,
_paused:Unpaused,
};
timer.set_time(time,new_time);
timer
}
pub fn into_unpaused(self, time: Time<T::In>) -> TimerFixed<T, Unpaused> {
let new_time = self.time(time);
let mut timer = TimerFixed {
state: self.state,
_paused: Unpaused,
};
timer.set_time(time, new_time);
timer
}
}
impl<T:TimerState> TimerFixed<T,Unpaused>
where Time<T::In>:Copy,
impl<T: TimerState> TimerFixed<T, Unpaused>
where
Time<T::In>: Copy,
{
pub fn into_paused(self,time:Time<T::In>)->TimerFixed<T,Paused>{
let new_time=self.time(time);
let mut timer=TimerFixed{
state:self.state,
_paused:Paused,
};
timer.set_time(time,new_time);
timer
}
pub fn into_paused(self, time: Time<T::In>) -> TimerFixed<T, Paused> {
let new_time = self.time(time);
let mut timer = TimerFixed {
state: self.state,
_paused: Paused,
};
timer.set_time(time, new_time);
timer
}
}
//the new constructor and time queries are generic across both
impl<T:TimerState,P:PauseState> TimerFixed<T,P>{
pub fn new(time:Time<T::In>,new_time:Time<T::Out>)->Self{
let mut timer=Self{
state:T::identity(),
_paused:P::new(),
};
timer.set_time(time,new_time);
timer
}
pub fn from_state(state:T)->Self{
Self{
state,
_paused:P::new(),
}
}
pub fn into_state(self)->T{
self.state
}
pub fn time(&self,time:Time<T::In>)->Time<T::Out>{
match P::IS_PAUSED{
true=>self.state.get_offset().coerce(),
false=>self.state.get_time(time),
}
}
pub fn set_time(&mut self,time:Time<T::In>,new_time:Time<T::Out>){
match P::IS_PAUSED{
true=>self.state.set_offset(new_time.coerce()),
false=>self.state.set_time(time,new_time),
}
}
impl<T: TimerState, P: PauseState> TimerFixed<T, P> {
pub fn new(time: Time<T::In>, new_time: Time<T::Out>) -> Self {
let mut timer = Self {
state: T::identity(),
_paused: P::new(),
};
timer.set_time(time, new_time);
timer
}
pub fn from_state(state: T) -> Self {
Self {
state,
_paused: P::new(),
}
}
pub fn into_state(self) -> T {
self.state
}
pub fn time(&self, time: Time<T::In>) -> Time<T::Out> {
match P::IS_PAUSED {
true => self.state.get_offset().coerce(),
false => self.state.get_time(time),
}
}
pub fn set_time(&mut self, time: Time<T::In>, new_time: Time<T::Out>) {
match P::IS_PAUSED {
true => self.state.set_offset(new_time.coerce()),
false => self.state.set_time(time, new_time),
}
}
}
#[derive(Debug)]
pub enum Error{
AlreadyPaused,
AlreadyUnpaused,
pub enum Error {
AlreadyPaused,
AlreadyUnpaused,
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
//wrapper type which holds type state internally
#[derive(Clone,Debug)]
pub enum Timer<T:TimerState>{
Paused(TimerFixed<T,Paused>),
Unpaused(TimerFixed<T,Unpaused>),
#[derive(Clone, Debug)]
pub enum Timer<T: TimerState> {
Paused(TimerFixed<T, Paused>),
Unpaused(TimerFixed<T, Unpaused>),
}
impl<T:TimerState> Timer<T>
where
T:Copy,
Time<T::In>:Copy,
impl<T: TimerState> Timer<T>
where
T: Copy,
Time<T::In>: Copy,
{
pub fn from_state(state:T,paused:bool)->Self{
match paused{
true=>Self::Paused(TimerFixed::from_state(state)),
false=>Self::Unpaused(TimerFixed::from_state(state)),
}
}
pub fn into_state(self)->(T,bool){
match self{
Self::Paused(timer)=>(timer.into_state(),true),
Self::Unpaused(timer)=>(timer.into_state(),false),
}
}
pub fn paused(time:Time<T::In>,new_time:Time<T::Out>)->Self{
Self::Paused(TimerFixed::new(time,new_time))
}
pub fn unpaused(time:Time<T::In>,new_time:Time<T::Out>)->Self{
Self::Unpaused(TimerFixed::new(time,new_time))
}
pub fn time(&self,time:Time<T::In>)->Time<T::Out>{
match self{
Self::Paused(timer)=>timer.time(time),
Self::Unpaused(timer)=>timer.time(time),
}
}
pub fn set_time(&mut self,time:Time<T::In>,new_time:Time<T::Out>){
match self{
Self::Paused(timer)=>timer.set_time(time,new_time),
Self::Unpaused(timer)=>timer.set_time(time,new_time),
}
}
pub fn pause(&mut self,time:Time<T::In>)->Result<(),Error>{
*self=match *self{
Self::Paused(_)=>return Err(Error::AlreadyPaused),
Self::Unpaused(timer)=>Self::Paused(timer.into_paused(time)),
};
Ok(())
}
pub fn unpause(&mut self,time:Time<T::In>)->Result<(),Error>{
*self=match *self{
Self::Paused(timer)=>Self::Unpaused(timer.into_unpaused(time)),
Self::Unpaused(_)=>return Err(Error::AlreadyUnpaused),
};
Ok(())
}
pub fn is_paused(&self)->bool{
match self{
Self::Paused(_)=>true,
Self::Unpaused(_)=>false,
}
}
pub fn set_paused(&mut self,time:Time<T::In>,paused:bool)->Result<(),Error>{
match paused{
true=>self.pause(time),
false=>self.unpause(time),
}
}
pub fn from_state(state: T, paused: bool) -> Self {
match paused {
true => Self::Paused(TimerFixed::from_state(state)),
false => Self::Unpaused(TimerFixed::from_state(state)),
}
}
pub fn into_state(self) -> (T, bool) {
match self {
Self::Paused(timer) => (timer.into_state(), true),
Self::Unpaused(timer) => (timer.into_state(), false),
}
}
pub fn paused(time: Time<T::In>, new_time: Time<T::Out>) -> Self {
Self::Paused(TimerFixed::new(time, new_time))
}
pub fn unpaused(time: Time<T::In>, new_time: Time<T::Out>) -> Self {
Self::Unpaused(TimerFixed::new(time, new_time))
}
pub fn time(&self, time: Time<T::In>) -> Time<T::Out> {
match self {
Self::Paused(timer) => timer.time(time),
Self::Unpaused(timer) => timer.time(time),
}
}
pub fn set_time(&mut self, time: Time<T::In>, new_time: Time<T::Out>) {
match self {
Self::Paused(timer) => timer.set_time(time, new_time),
Self::Unpaused(timer) => timer.set_time(time, new_time),
}
}
pub fn pause(&mut self, time: Time<T::In>) -> Result<(), Error> {
*self = match *self {
Self::Paused(_) => return Err(Error::AlreadyPaused),
Self::Unpaused(timer) => Self::Paused(timer.into_paused(time)),
};
Ok(())
}
pub fn unpause(&mut self, time: Time<T::In>) -> Result<(), Error> {
*self = match *self {
Self::Paused(timer) => Self::Unpaused(timer.into_unpaused(time)),
Self::Unpaused(_) => return Err(Error::AlreadyUnpaused),
};
Ok(())
}
pub fn is_paused(&self) -> bool {
match self {
Self::Paused(_) => true,
Self::Unpaused(_) => false,
}
}
pub fn set_paused(&mut self, time: Time<T::In>, paused: bool) -> Result<(), Error> {
match paused {
true => self.pause(time),
false => self.unpause(time),
}
}
}
//scaled timer methods are generic across PauseState
impl<In,Out> Timer<Scaled<In,Out>>
where Time<In>:Copy,
impl<In, Out> Timer<Scaled<In, Out>>
where
Time<In>: Copy,
{
pub const fn get_scale(&self)->Ratio64{
match self{
Self::Paused(timer)=>timer.get_scale(),
Self::Unpaused(timer)=>timer.get_scale(),
}
}
pub fn set_scale(&mut self,time:Time<In>,new_scale:Ratio64){
match self{
Self::Paused(timer)=>timer.set_scale(time,new_scale),
Self::Unpaused(timer)=>timer.set_scale(time,new_scale),
}
}
pub const fn get_scale(&self) -> Ratio64 {
match self {
Self::Paused(timer) => timer.get_scale(),
Self::Unpaused(timer) => timer.get_scale(),
}
}
pub fn set_scale(&mut self, time: Time<In>, new_scale: Ratio64) {
match self {
Self::Paused(timer) => timer.set_scale(time, new_scale),
Self::Unpaused(timer) => timer.set_scale(time, new_scale),
}
}
}
#[cfg(test)]
mod test{
use super::*;
macro_rules! sec {
($s: expr) => {
Time::from_secs($s)
};
}
mod test {
use super::*;
macro_rules! sec {
($s: expr) => {
Time::from_secs($s)
};
}
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
enum Parent{}
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
enum Calculated{}
#[test]
fn test_timerfixed_scaled(){
//create a paused timer that reads 0s
let timer=TimerFixed::<Scaled<Parent,Calculated>,Paused>::from_state(Scaled::new(0.5f32.try_into().unwrap(),sec!(0)));
//the paused timer at 1 second should read 0s
assert_eq!(timer.time(sec!(1)),sec!(0));
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Debug)]
enum Parent {}
#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Debug)]
enum Calculated {}
#[test]
fn test_timerfixed_scaled() {
//create a paused timer that reads 0s
let timer = TimerFixed::<Scaled<Parent, Calculated>, Paused>::from_state(Scaled::new(
0.5f32.try_into().unwrap(),
sec!(0),
));
//the paused timer at 1 second should read 0s
assert_eq!(timer.time(sec!(1)), sec!(0));
//unpause it after one second
let timer=timer.into_unpaused(sec!(1));
//the timer at 6 seconds should read 2.5s
assert_eq!(timer.time(sec!(6)),Time::from_millis(2500));
//unpause it after one second
let timer = timer.into_unpaused(sec!(1));
//the timer at 6 seconds should read 2.5s
assert_eq!(timer.time(sec!(6)), Time::from_millis(2500));
//pause the timer after 11 seconds
let timer=timer.into_paused(sec!(11));
//the paused timer at 20 seconds should read 5s
assert_eq!(timer.time(sec!(20)),sec!(5));
}
#[test]
fn test_timer()->Result<(),Error>{
//create a paused timer that reads 0s
let mut timer=Timer::<Realtime<Parent,Calculated>>::paused(sec!(0),sec!(0));
//the paused timer at 1 second should read 0s
assert_eq!(timer.time(sec!(1)),sec!(0));
//pause the timer after 11 seconds
let timer = timer.into_paused(sec!(11));
//the paused timer at 20 seconds should read 5s
assert_eq!(timer.time(sec!(20)), sec!(5));
}
#[test]
fn test_timer() -> Result<(), Error> {
//create a paused timer that reads 0s
let mut timer = Timer::<Realtime<Parent, Calculated>>::paused(sec!(0), sec!(0));
//the paused timer at 1 second should read 0s
assert_eq!(timer.time(sec!(1)), sec!(0));
//unpause it after one second
timer.unpause(sec!(1))?;
//the timer at 6 seconds should read 5s
assert_eq!(timer.time(sec!(6)),sec!(5));
//unpause it after one second
timer.unpause(sec!(1))?;
//the timer at 6 seconds should read 5s
assert_eq!(timer.time(sec!(6)), sec!(5));
//pause the timer after 11 seconds
timer.pause(sec!(11))?;
//the paused timer at 20 seconds should read 10s
assert_eq!(timer.time(sec!(20)),sec!(10));
//pause the timer after 11 seconds
timer.pause(sec!(11))?;
//the paused timer at 20 seconds should read 10s
assert_eq!(timer.time(sec!(20)), sec!(10));
Ok(())
}
Ok(())
}
}

View File

@ -1,56 +1,56 @@
pub trait Updatable<Updater>{
fn update(&mut self,update:Updater);
pub trait Updatable<Updater> {
fn update(&mut self, update: Updater);
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
struct InnerId(u32);
#[derive(Clone)]
struct Inner{
id:InnerId,
enabled:bool,
struct Inner {
id: InnerId,
enabled: bool,
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
struct OuterId(u32);
struct Outer{
id:OuterId,
inners:std::collections::HashMap<InnerId,Inner>,
struct Outer {
id: OuterId,
inners: std::collections::HashMap<InnerId, Inner>,
}
enum Update<I,U>{
Insert(I),
Update(U),
Remove
enum Update<I, U> {
Insert(I),
Update(U),
Remove,
}
struct InnerUpdate{
//#[updatable(Update)]
enabled:Option<bool>,
struct InnerUpdate {
//#[updatable(Update)]
enabled: Option<bool>,
}
struct OuterUpdate{
//#[updatable(Insert,Update,Remove)]
inners:std::collections::HashMap<InnerId,Update<Inner,InnerUpdate>>,
//#[updatable(Update)]
//inners:std::collections::HashMap<InnerId,InnerUpdate>,
struct OuterUpdate {
//#[updatable(Insert,Update,Remove)]
inners: std::collections::HashMap<InnerId, Update<Inner, InnerUpdate>>,
//#[updatable(Update)]
//inners:std::collections::HashMap<InnerId,InnerUpdate>,
}
impl Updatable<InnerUpdate> for Inner{
fn update(&mut self,update:InnerUpdate){
if let Some(enabled)=update.enabled{
self.enabled=enabled;
}
}
impl Updatable<InnerUpdate> for Inner {
fn update(&mut self, update: InnerUpdate) {
if let Some(enabled) = update.enabled {
self.enabled = enabled;
}
}
}
impl Updatable<OuterUpdate> for Outer{
fn update(&mut self,update:OuterUpdate){
for (id,up) in update.inners{
match up{
Update::Insert(new_inner)=>self.inners.insert(id,new_inner),
Update::Update(inner_update)=>self.inners.get_mut(&id).map(|inner|{
let old=inner.clone();
inner.update(inner_update);
old
}),
Update::Remove=>self.inners.remove(&id),
};
}
}
impl Updatable<OuterUpdate> for Outer {
fn update(&mut self, update: OuterUpdate) {
for (id, up) in update.inners {
match up {
Update::Insert(new_inner) => self.inners.insert(id, new_inner),
Update::Update(inner_update) => self.inners.get_mut(&id).map(|inner| {
let old = inner.clone();
inner.update(inner_update);
old
}),
Update::Remove => self.inners.remove(&id),
};
}
}
}
//*/
//*/

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
pub mod fixed;
pub mod types;
#[cfg(feature="zeroes")]
#[cfg(feature = "zeroes")]
pub mod zeroes;
#[cfg(test)]

View File

@ -1,218 +1,230 @@
use crate::types::I32F32;
use crate::types::I256F256;
use crate::types::I32F32;
#[test]
fn you_can_add_numbers(){
let a=I256F256::from((3i128*2).pow(4));
assert_eq!(a+a,I256F256::from((3i128*2).pow(4)*2));
fn you_can_add_numbers() {
let a = I256F256::from((3i128 * 2).pow(4));
assert_eq!(a + a, I256F256::from((3i128 * 2).pow(4) * 2));
}
#[test]
fn to_f32(){
let a=I256F256::from(1)>>2;
let f:f32=a.into();
assert_eq!(f,0.25f32);
let f:f32=(-a).into();
assert_eq!(f,-0.25f32);
let a=I256F256::from(0);
let f:f32=(-a).into();
assert_eq!(f,0f32);
let a=I256F256::from(237946589723468975i64)<<16;
let f:f32=a.into();
assert_eq!(f,237946589723468975f32*2.0f32.powi(16));
fn to_f32() {
let a = I256F256::from(1) >> 2;
let f: f32 = a.into();
assert_eq!(f, 0.25f32);
let f: f32 = (-a).into();
assert_eq!(f, -0.25f32);
let a = I256F256::from(0);
let f: f32 = (-a).into();
assert_eq!(f, 0f32);
let a = I256F256::from(237946589723468975i64) << 16;
let f: f32 = a.into();
assert_eq!(f, 237946589723468975f32 * 2.0f32.powi(16));
}
#[test]
fn to_f64(){
let a=I256F256::from(1)>>2;
let f:f64=a.into();
assert_eq!(f,0.25f64);
let f:f64=(-a).into();
assert_eq!(f,-0.25f64);
let a=I256F256::from(0);
let f:f64=(-a).into();
assert_eq!(f,0f64);
let a=I256F256::from(237946589723468975i64)<<16;
let f:f64=a.into();
assert_eq!(f,237946589723468975f64*2.0f64.powi(16));
fn to_f64() {
let a = I256F256::from(1) >> 2;
let f: f64 = a.into();
assert_eq!(f, 0.25f64);
let f: f64 = (-a).into();
assert_eq!(f, -0.25f64);
let a = I256F256::from(0);
let f: f64 = (-a).into();
assert_eq!(f, 0f64);
let a = I256F256::from(237946589723468975i64) << 16;
let f: f64 = a.into();
assert_eq!(f, 237946589723468975f64 * 2.0f64.powi(16));
}
#[test]
fn from_f32(){
let a=I256F256::from(1)>>2;
let b:Result<I256F256,_>=0.25f32.try_into();
assert_eq!(b,Ok(a));
let a=I256F256::from(-1)>>2;
let b:Result<I256F256,_>=(-0.25f32).try_into();
assert_eq!(b,Ok(a));
let a=I256F256::from(0);
let b:Result<I256F256,_>=0.try_into();
assert_eq!(b,Ok(a));
let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16;
let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f32*2.0f32.powi(16)).try_into();
assert_eq!(b,Ok(a));
//I32F32::MAX into f32 is truncated into this value
let a=I32F32::raw(0b111111111111111111111111000000000000000000000000000000000000000i64);
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MAX).try_into();
assert_eq!(b,Ok(a));
//I32F32::MIN hits a special case since it's not representable as a positive signed integer
//TODO: don't return an overflow because this is technically possible
let a=I32F32::MIN;
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
//16 is within the 24 bits of float precision
let b:Result<I32F32,_>=Into::<f32>::into(-I32F32::MIN.fix_2()).try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Underflow));
//test many cases
for i in 0..64{
let a=crate::fixed::Fixed::<2,64>::raw_digit(0b111111111111111111111111000000000000000000000000000000000000000i64)<<i;
let f:f32=a.into();
let b:Result<crate::fixed::Fixed<2,64>,_>=f.try_into();
assert_eq!(b,Ok(a));
}
fn from_f32() {
let a = I256F256::from(1) >> 2;
let b: Result<I256F256, _> = 0.25f32.try_into();
assert_eq!(b, Ok(a));
let a = I256F256::from(-1) >> 2;
let b: Result<I256F256, _> = (-0.25f32).try_into();
assert_eq!(b, Ok(a));
let a = I256F256::from(0);
let b: Result<I256F256, _> = 0.try_into();
assert_eq!(b, Ok(a));
let a = I256F256::from(0b101011110101001010101010000000000000000000000000000i64) << 16;
let b: Result<I256F256, _> = (0b101011110101001010101010000000000000000000000000000u64 as f32
* 2.0f32.powi(16))
.try_into();
assert_eq!(b, Ok(a));
//I32F32::MAX into f32 is truncated into this value
let a = I32F32::raw(0b111111111111111111111111000000000000000000000000000000000000000i64);
let b: Result<I32F32, _> = Into::<f32>::into(I32F32::MAX).try_into();
assert_eq!(b, Ok(a));
//I32F32::MIN hits a special case since it's not representable as a positive signed integer
//TODO: don't return an overflow because this is technically possible
let a = I32F32::MIN;
let b: Result<I32F32, _> = Into::<f32>::into(I32F32::MIN).try_into();
assert_eq!(b, Err(crate::fixed::FixedFromFloatError::Overflow));
//16 is within the 24 bits of float precision
let b: Result<I32F32, _> = Into::<f32>::into(-I32F32::MIN.fix_2()).try_into();
assert_eq!(b, Err(crate::fixed::FixedFromFloatError::Overflow));
let b: Result<I32F32, _> = f32::MIN_POSITIVE.try_into();
assert_eq!(b, Err(crate::fixed::FixedFromFloatError::Underflow));
//test many cases
for i in 0..64 {
let a = crate::fixed::Fixed::<2, 64>::raw_digit(
0b111111111111111111111111000000000000000000000000000000000000000i64,
) << i;
let f: f32 = a.into();
let b: Result<crate::fixed::Fixed<2, 64>, _> = f.try_into();
assert_eq!(b, Ok(a));
}
}
#[test]
fn from_f64(){
let a=I256F256::from(1)>>2;
let b:Result<I256F256,_>=0.25f64.try_into();
assert_eq!(b,Ok(a));
let a=I256F256::from(-1)>>2;
let b:Result<I256F256,_>=(-0.25f64).try_into();
assert_eq!(b,Ok(a));
let a=I256F256::from(0);
let b:Result<I256F256,_>=0.try_into();
assert_eq!(b,Ok(a));
let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16;
let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f64*2.0f64.powi(16)).try_into();
assert_eq!(b,Ok(a));
fn from_f64() {
let a = I256F256::from(1) >> 2;
let b: Result<I256F256, _> = 0.25f64.try_into();
assert_eq!(b, Ok(a));
let a = I256F256::from(-1) >> 2;
let b: Result<I256F256, _> = (-0.25f64).try_into();
assert_eq!(b, Ok(a));
let a = I256F256::from(0);
let b: Result<I256F256, _> = 0.try_into();
assert_eq!(b, Ok(a));
let a = I256F256::from(0b101011110101001010101010000000000000000000000000000i64) << 16;
let b: Result<I256F256, _> = (0b101011110101001010101010000000000000000000000000000u64 as f64
* 2.0f64.powi(16))
.try_into();
assert_eq!(b, Ok(a));
}
#[test]
fn you_can_shr_numbers(){
let a=I32F32::from(4);
assert_eq!(a>>1,I32F32::from(2));
fn you_can_shr_numbers() {
let a = I32F32::from(4);
assert_eq!(a >> 1, I32F32::from(2));
}
#[test]
fn test_wide_mul(){
let a=I32F32::ONE;
let aa=a.wide_mul_1_1(a);
assert_eq!(aa,crate::types::I64F64::ONE);
fn test_wide_mul() {
let a = I32F32::ONE;
let aa = a.wide_mul_1_1(a);
assert_eq!(aa, crate::types::I64F64::ONE);
}
#[test]
fn test_wide_div(){
let a=I32F32::ONE*4;
let b=I32F32::ONE*2;
let wide_a=a.wide_mul_1_1(I32F32::ONE);
let wide_b=b.wide_mul_1_1(I32F32::ONE);
let ab=a.wide_div_1_1(b);
assert_eq!(ab,crate::types::I64F64::ONE*2);
let wab=wide_a.wide_div_2_1(b);
assert_eq!(wab,crate::fixed::Fixed::<3,96>::ONE*2);
let awb=a.wide_div_1_2(wide_b);
assert_eq!(awb,crate::fixed::Fixed::<3,96>::ONE*2);
fn test_wide_div() {
let a = I32F32::ONE * 4;
let b = I32F32::ONE * 2;
let wide_a = a.wide_mul_1_1(I32F32::ONE);
let wide_b = b.wide_mul_1_1(I32F32::ONE);
let ab = a.wide_div_1_1(b);
assert_eq!(ab, crate::types::I64F64::ONE * 2);
let wab = wide_a.wide_div_2_1(b);
assert_eq!(wab, crate::fixed::Fixed::<3, 96>::ONE * 2);
let awb = a.wide_div_1_2(wide_b);
assert_eq!(awb, crate::fixed::Fixed::<3, 96>::ONE * 2);
}
#[test]
fn test_wide_mul_repeated() {
let a=I32F32::from(2);
let b=I32F32::from(3);
let a = I32F32::from(2);
let b = I32F32::from(3);
let w1=a.wide_mul_1_1(b);
let w2=w1.wide_mul_2_2(w1);
let w3=w2.wide_mul_4_4(w2);
let w1 = a.wide_mul_1_1(b);
let w2 = w1.wide_mul_2_2(w1);
let w3 = w2.wide_mul_4_4(w2);
assert_eq!(w3,I256F256::from((3i128*2).pow(4)));
assert_eq!(w3, I256F256::from((3i128 * 2).pow(4)));
}
#[test]
fn test_bint(){
let a=I32F32::ONE;
assert_eq!(a*2,I32F32::from(2));
fn test_bint() {
let a = I32F32::ONE;
assert_eq!(a * 2, I32F32::from(2));
}
#[test]
fn test_fix(){
assert_eq!(I32F32::ONE.fix_8(),I256F256::ONE);
assert_eq!(I32F32::ONE,I256F256::ONE.fix_1());
assert_eq!(I32F32::NEG_ONE.fix_8(),I256F256::NEG_ONE);
assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.fix_1());
fn test_fix() {
assert_eq!(I32F32::ONE.fix_8(), I256F256::ONE);
assert_eq!(I32F32::ONE, I256F256::ONE.fix_1());
assert_eq!(I32F32::NEG_ONE.fix_8(), I256F256::NEG_ONE);
assert_eq!(I32F32::NEG_ONE, I256F256::NEG_ONE.fix_1());
}
#[test]
fn test_sqrt(){
let a=I32F32::ONE*4;
assert_eq!(a.sqrt(),I32F32::from(2));
fn test_sqrt() {
let a = I32F32::ONE * 4;
assert_eq!(a.sqrt(), I32F32::from(2));
}
#[test]
fn test_sqrt_zero(){
let a=I32F32::ZERO;
assert_eq!(a.sqrt(),I32F32::ZERO);
fn test_sqrt_zero() {
let a = I32F32::ZERO;
assert_eq!(a.sqrt(), I32F32::ZERO);
}
#[test]
fn test_sqrt_low(){
let a=I32F32::HALF;
let b=a.fixed_mul(a);
assert_eq!(b.sqrt(),a);
fn test_sqrt_low() {
let a = I32F32::HALF;
let b = a.fixed_mul(a);
assert_eq!(b.sqrt(), a);
}
fn find_equiv_sqrt_via_f64(n:I32F32)->I32F32{
//GIMME THEM BITS BOY
let &[bits]=n.to_bits().to_bits().digits();
let ibits=bits as i64;
let f=(ibits as f64)/((1u64<<32) as f64);
let f_ans=f.sqrt();
let i=(f_ans*((1u64<<32) as f64)) as i64;
let r=I32F32::from_bits(bnum::BInt::<1>::from(i));
//mimic the behaviour of the algorithm,
//return the result if it truncates to the exact answer
if (r+I32F32::EPSILON).wide_mul_1_1(r+I32F32::EPSILON)==n.wide_mul_1_1(I32F32::ONE){
return r+I32F32::EPSILON;
}
if (r-I32F32::EPSILON).wide_mul_1_1(r-I32F32::EPSILON)==n.wide_mul_1_1(I32F32::ONE){
return r-I32F32::EPSILON;
}
return r;
fn find_equiv_sqrt_via_f64(n: I32F32) -> I32F32 {
//GIMME THEM BITS BOY
let &[bits] = n.to_bits().to_bits().digits();
let ibits = bits as i64;
let f = (ibits as f64) / ((1u64 << 32) as f64);
let f_ans = f.sqrt();
let i = (f_ans * ((1u64 << 32) as f64)) as i64;
let r = I32F32::from_bits(bnum::BInt::<1>::from(i));
//mimic the behaviour of the algorithm,
//return the result if it truncates to the exact answer
if (r + I32F32::EPSILON).wide_mul_1_1(r + I32F32::EPSILON) == n.wide_mul_1_1(I32F32::ONE) {
return r + I32F32::EPSILON;
}
if (r - I32F32::EPSILON).wide_mul_1_1(r - I32F32::EPSILON) == n.wide_mul_1_1(I32F32::ONE) {
return r - I32F32::EPSILON;
}
return r;
}
fn test_exact(n:I32F32){
assert_eq!(n.sqrt(),find_equiv_sqrt_via_f64(n));
fn test_exact(n: I32F32) {
assert_eq!(n.sqrt(), find_equiv_sqrt_via_f64(n));
}
#[test]
fn test_sqrt_exact(){
//43
for i in 0..((i64::MAX as f32).ln() as u32){
let n=I32F32::from_bits(bnum::BInt::<1>::from((i as f32).exp() as i64));
test_exact(n);
}
fn test_sqrt_exact() {
//43
for i in 0..((i64::MAX as f32).ln() as u32) {
let n = I32F32::from_bits(bnum::BInt::<1>::from((i as f32).exp() as i64));
test_exact(n);
}
}
#[test]
fn test_sqrt_max(){
let a=I32F32::MAX;
test_exact(a);
fn test_sqrt_max() {
let a = I32F32::MAX;
test_exact(a);
}
#[test]
#[cfg(all(feature="zeroes",not(feature="deferred-division")))]
fn test_zeroes_normal(){
// (x-1)*(x+1)
// x^2-1
let zeroes=I32F32::zeroes2(I32F32::NEG_ONE,I32F32::ZERO,I32F32::ONE);
assert_eq!(zeroes,arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE,I32F32::ONE]));
let zeroes=I32F32::zeroes2(I32F32::NEG_ONE*3,I32F32::ONE*2,I32F32::ONE);
assert_eq!(zeroes,arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE*3,I32F32::ONE]));
#[cfg(all(feature = "zeroes", not(feature = "deferred-division")))]
fn test_zeroes_normal() {
// (x-1)*(x+1)
// x^2-1
let zeroes = I32F32::zeroes2(I32F32::NEG_ONE, I32F32::ZERO, I32F32::ONE);
assert_eq!(
zeroes,
arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE, I32F32::ONE])
);
let zeroes = I32F32::zeroes2(I32F32::NEG_ONE * 3, I32F32::ONE * 2, I32F32::ONE);
assert_eq!(
zeroes,
arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE * 3, I32F32::ONE])
);
}
#[test]
#[cfg(all(feature="zeroes",feature="deferred-division"))]
fn test_zeroes_deferred_division(){
// (x-1)*(x+1)
// x^2-1
let zeroes=I32F32::zeroes2(I32F32::NEG_ONE,I32F32::ZERO,I32F32::ONE);
assert_eq!(
zeroes,
arrayvec::ArrayVec::from_iter([
ratio_ops::ratio::Ratio::new(I32F32::ONE*2,I32F32::NEG_ONE*2),
ratio_ops::ratio::Ratio::new(I32F32::ONE*2,I32F32::ONE*2),
])
);
#[cfg(all(feature = "zeroes", feature = "deferred-division"))]
fn test_zeroes_deferred_division() {
// (x-1)*(x+1)
// x^2-1
let zeroes = I32F32::zeroes2(I32F32::NEG_ONE, I32F32::ZERO, I32F32::ONE);
assert_eq!(
zeroes,
arrayvec::ArrayVec::from_iter([
ratio_ops::ratio::Ratio::new(I32F32::ONE * 2, I32F32::NEG_ONE * 2),
ratio_ops::ratio::Ratio::new(I32F32::ONE * 2, I32F32::ONE * 2),
])
);
}

View File

@ -1,4 +1,4 @@
pub type I32F32=crate::fixed::Fixed<1,32>;
pub type I64F64=crate::fixed::Fixed<2,64>;
pub type I128F128=crate::fixed::Fixed<4,128>;
pub type I256F256=crate::fixed::Fixed<8,256>;
pub type I32F32 = crate::fixed::Fixed<1, 32>;
pub type I64F64 = crate::fixed::Fixed<2, 64>;
pub type I128F128 = crate::fixed::Fixed<4, 128>;
pub type I256F256 = crate::fixed::Fixed<8, 256>;

View File

@ -2,45 +2,63 @@ use crate::fixed::Fixed;
use arrayvec::ArrayVec;
use std::cmp::Ordering;
macro_rules! impl_zeroes{
($n:expr)=>{
impl Fixed<$n,{$n*32}>{
#[inline]
pub fn zeroes2(a0:Self,a1:Self,a2:Self)->ArrayVec<<Self as core::ops::Div>::Output,2>{
let a2pos=match a2.cmp(&Self::ZERO){
Ordering::Greater=>true,
Ordering::Equal=>return ArrayVec::from_iter(Self::zeroes1(a0,a1).into_iter()),
Ordering::Less=>false,
};
let radicand=a1*a1-a2*a0*4;
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){
Ordering::Greater=>{
paste::item!{
let planar_radicand=radicand.sqrt().[<fix_ $n>]();
}
//sort roots ascending and avoid taking the difference of large numbers
let zeroes=match (a2pos,Self::ZERO<a1){
(true, true )=>[(-a1-planar_radicand)/(a2*2),(a0*2)/(-a1-planar_radicand)],
(true, false)=>[(a0*2)/(-a1+planar_radicand),(-a1+planar_radicand)/(a2*2)],
(false,true )=>[(a0*2)/(-a1-planar_radicand),(-a1-planar_radicand)/(a2*2)],
(false,false)=>[(-a1+planar_radicand)/(a2*2),(a0*2)/(-a1+planar_radicand)],
};
ArrayVec::from_iter(zeroes)
},
Ordering::Equal=>ArrayVec::from_iter([(a1)/(a2*-2)]),
Ordering::Less=>ArrayVec::new_const(),
}
}
#[inline]
pub fn zeroes1(a0:Self,a1:Self)->ArrayVec<<Self as core::ops::Div>::Output,1>{
if a1==Self::ZERO{
ArrayVec::new_const()
}else{
ArrayVec::from_iter([(-a0)/(a1)])
}
}
}
};
macro_rules! impl_zeroes {
($n:expr) => {
impl Fixed<$n, { $n * 32 }> {
#[inline]
pub fn zeroes2(
a0: Self,
a1: Self,
a2: Self,
) -> ArrayVec<<Self as core::ops::Div>::Output, 2> {
let a2pos = match a2.cmp(&Self::ZERO) {
Ordering::Greater => true,
Ordering::Equal => {
return ArrayVec::from_iter(Self::zeroes1(a0, a1).into_iter())
}
Ordering::Less => false,
};
let radicand = a1 * a1 - a2 * a0 * 4;
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO) {
Ordering::Greater => {
paste::item! {
let planar_radicand=radicand.sqrt().[<fix_ $n>]();
}
//sort roots ascending and avoid taking the difference of large numbers
let zeroes = match (a2pos, Self::ZERO < a1) {
(true, true) => [
(-a1 - planar_radicand) / (a2 * 2),
(a0 * 2) / (-a1 - planar_radicand),
],
(true, false) => [
(a0 * 2) / (-a1 + planar_radicand),
(-a1 + planar_radicand) / (a2 * 2),
],
(false, true) => [
(a0 * 2) / (-a1 - planar_radicand),
(-a1 - planar_radicand) / (a2 * 2),
],
(false, false) => [
(-a1 + planar_radicand) / (a2 * 2),
(a0 * 2) / (-a1 + planar_radicand),
],
};
ArrayVec::from_iter(zeroes)
}
Ordering::Equal => ArrayVec::from_iter([(a1) / (a2 * -2)]),
Ordering::Less => ArrayVec::new_const(),
}
}
#[inline]
pub fn zeroes1(a0: Self, a1: Self) -> ArrayVec<<Self as core::ops::Div>::Output, 1> {
if a1 == Self::ZERO {
ArrayVec::new_const()
} else {
ArrayVec::from_iter([(-a0) / (a1)])
}
}
}
};
}
impl_zeroes!(1);
impl_zeroes!(2);

View File

@ -1,9 +1,9 @@
mod macros;
pub mod matrix;
pub mod types;
pub mod vector;
pub mod matrix;
#[cfg(feature="named-fields")]
#[cfg(feature = "named-fields")]
mod named;
#[cfg(test)]

View File

@ -1,79 +1,328 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_fixed_wide_vector_not_const_generic {
(
(
(),
$n:expr
) => {
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$n,{$n*32}>>{
#[inline]
pub fn length(self)-><fixed_wide::fixed::Fixed::<$n,{$n*32}> as core::ops::Mul>::Output{
self.length_squared().sqrt_unchecked()
}
#[inline]
pub fn with_length<U,V>(self,length:U)-><Vector<N,V> as core::ops::Div<<fixed_wide::fixed::Fixed::<$n,{$n*32}> as core::ops::Mul>::Output>>::Output
where
fixed_wide::fixed::Fixed<$n,{$n*32}>:core::ops::Mul<U,Output=V>,
U:Copy,
V:core::ops::Div<<fixed_wide::fixed::Fixed::<$n,{$n*32}> as core::ops::Mul>::Output>,
{
self*length/self.length()
}
}
};
impl<const N: usize> Vector<N, fixed_wide::fixed::Fixed<$n, { $n * 32 }>> {
#[inline]
pub fn length(
self,
) -> <fixed_wide::fixed::Fixed<$n, { $n * 32 }> as core::ops::Mul>::Output {
self.length_squared().sqrt_unchecked()
}
#[inline]
pub fn with_length<U, V>(
self,
length: U,
) -> <Vector<N, V> as core::ops::Div<
<fixed_wide::fixed::Fixed<$n, { $n * 32 }> as core::ops::Mul>::Output,
>>::Output
where
fixed_wide::fixed::Fixed<$n, { $n * 32 }>: core::ops::Mul<U, Output = V>,
U: Copy,
V: core::ops::Div<
<fixed_wide::fixed::Fixed<$n, { $n * 32 }> as core::ops::Mul>::Output,
>,
{
self * length / self.length()
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! macro_4 {
( $macro: ident, $any:tt ) => {
$crate::macro_repeated!($macro,$any,1,2,3,4);
}
( $macro: ident, $any:tt ) => {
$crate::macro_repeated!($macro, $any, 1, 2, 3, 4);
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_fixed_wide_vector {
() => {
$crate::macro_4!(impl_fixed_wide_vector_not_const_generic,());
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
$crate::macro_repeated!(
impl_fix_not_const_generic,(),
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),
(1,2),(2,2),(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),(15,2),(16,2),
(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3),
(1,4),(2,4),(3,4),(4,4),(5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(13,4),(14,4),(15,4),(16,4),
(1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5),
(1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),(8,8),(9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),(9,9),(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),(10,10),(11,10),(12,10),(13,10),(14,10),(15,10),(16,10),
(1,11),(2,11),(3,11),(4,11),(5,11),(6,11),(7,11),(8,11),(9,11),(10,11),(11,11),(12,11),(13,11),(14,11),(15,11),(16,11),
(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(10,12),(11,12),(12,12),(13,12),(14,12),(15,12),(16,12),
(1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13),(13,13),(14,13),(15,13),(16,13),
(1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14),(14,14),(15,14),(16,14),
(1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15),(15,15),(16,15),
(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16),(16,16)
);
};
() => {
$crate::macro_4!(impl_fixed_wide_vector_not_const_generic, ());
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
$crate::macro_repeated!(
impl_fix_not_const_generic,
(),
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 1),
(7, 1),
(8, 1),
(9, 1),
(10, 1),
(11, 1),
(12, 1),
(13, 1),
(14, 1),
(15, 1),
(16, 1),
(1, 2),
(2, 2),
(3, 2),
(4, 2),
(5, 2),
(6, 2),
(7, 2),
(8, 2),
(9, 2),
(10, 2),
(11, 2),
(12, 2),
(13, 2),
(14, 2),
(15, 2),
(16, 2),
(1, 3),
(2, 3),
(3, 3),
(4, 3),
(5, 3),
(6, 3),
(7, 3),
(8, 3),
(9, 3),
(10, 3),
(11, 3),
(12, 3),
(13, 3),
(14, 3),
(15, 3),
(16, 3),
(1, 4),
(2, 4),
(3, 4),
(4, 4),
(5, 4),
(6, 4),
(7, 4),
(8, 4),
(9, 4),
(10, 4),
(11, 4),
(12, 4),
(13, 4),
(14, 4),
(15, 4),
(16, 4),
(1, 5),
(2, 5),
(3, 5),
(4, 5),
(5, 5),
(6, 5),
(7, 5),
(8, 5),
(9, 5),
(10, 5),
(11, 5),
(12, 5),
(13, 5),
(14, 5),
(15, 5),
(16, 5),
(1, 6),
(2, 6),
(3, 6),
(4, 6),
(5, 6),
(6, 6),
(7, 6),
(8, 6),
(9, 6),
(10, 6),
(11, 6),
(12, 6),
(13, 6),
(14, 6),
(15, 6),
(16, 6),
(1, 7),
(2, 7),
(3, 7),
(4, 7),
(5, 7),
(6, 7),
(7, 7),
(8, 7),
(9, 7),
(10, 7),
(11, 7),
(12, 7),
(13, 7),
(14, 7),
(15, 7),
(16, 7),
(1, 8),
(2, 8),
(3, 8),
(4, 8),
(5, 8),
(6, 8),
(7, 8),
(8, 8),
(9, 8),
(10, 8),
(11, 8),
(12, 8),
(13, 8),
(14, 8),
(15, 8),
(16, 8),
(1, 9),
(2, 9),
(3, 9),
(4, 9),
(5, 9),
(6, 9),
(7, 9),
(8, 9),
(9, 9),
(10, 9),
(11, 9),
(12, 9),
(13, 9),
(14, 9),
(15, 9),
(16, 9),
(1, 10),
(2, 10),
(3, 10),
(4, 10),
(5, 10),
(6, 10),
(7, 10),
(8, 10),
(9, 10),
(10, 10),
(11, 10),
(12, 10),
(13, 10),
(14, 10),
(15, 10),
(16, 10),
(1, 11),
(2, 11),
(3, 11),
(4, 11),
(5, 11),
(6, 11),
(7, 11),
(8, 11),
(9, 11),
(10, 11),
(11, 11),
(12, 11),
(13, 11),
(14, 11),
(15, 11),
(16, 11),
(1, 12),
(2, 12),
(3, 12),
(4, 12),
(5, 12),
(6, 12),
(7, 12),
(8, 12),
(9, 12),
(10, 12),
(11, 12),
(12, 12),
(13, 12),
(14, 12),
(15, 12),
(16, 12),
(1, 13),
(2, 13),
(3, 13),
(4, 13),
(5, 13),
(6, 13),
(7, 13),
(8, 13),
(9, 13),
(10, 13),
(11, 13),
(12, 13),
(13, 13),
(14, 13),
(15, 13),
(16, 13),
(1, 14),
(2, 14),
(3, 14),
(4, 14),
(5, 14),
(6, 14),
(7, 14),
(8, 14),
(9, 14),
(10, 14),
(11, 14),
(12, 14),
(13, 14),
(14, 14),
(15, 14),
(16, 14),
(1, 15),
(2, 15),
(3, 15),
(4, 15),
(5, 15),
(6, 15),
(7, 15),
(8, 15),
(9, 15),
(10, 15),
(11, 15),
(12, 15),
(13, 15),
(14, 15),
(15, 15),
(16, 15),
(1, 16),
(2, 16),
(3, 16),
(4, 16),
(5, 16),
(6, 16),
(7, 16),
(8, 16),
(9, 16),
(10, 16),
(11, 16),
(12, 16),
(13, 16),
(14, 16),
(15, 16),
(16, 16)
);
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_fix_not_const_generic{
(
macro_rules! impl_fix_not_const_generic {
(
(),
($lhs:expr,$rhs:expr)
)=>{
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>
{
paste::item!{
#[inline]
pub fn [<fix_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
self.map(|t|t.[<fix_ $rhs>]())
}
}
}
}
) => {
impl<const N: usize> Vector<N, fixed_wide::fixed::Fixed<$lhs, { $lhs * 32 }>> {
paste::item! {
#[inline]
pub fn [<fix_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
self.map(|t|t.[<fix_ $rhs>]())
}
}
}
};
}

View File

@ -1,219 +1,217 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix {
() => {
impl<const X:usize,const Y:usize,T> Matrix<X,Y,T>{
#[inline(always)]
pub const fn new(array:[[T;Y];X])->Self{
Self{array}
}
#[inline(always)]
pub fn to_array(self)->[[T;Y];X]{
self.array
}
#[inline]
pub fn from_cols(cols:[Vector<Y,T>;X])->Self
{
Matrix::new(
cols.map(|col|col.array),
)
}
#[inline]
pub fn map<F,U>(self,f:F)->Matrix<X,Y,U>
where
F:Fn(T)->U
{
Matrix::new(
self.array.map(|inner|inner.map(&f)),
)
}
#[inline]
pub fn transpose(self)->Matrix<Y,X,T>{
//how did I think of this
let mut array_of_iterators=self.array.map(|axis|axis.into_iter());
Matrix::new(
core::array::from_fn(|_|
array_of_iterators.each_mut().map(|iter|
iter.next().unwrap()
)
)
)
}
#[inline]
// old (list of rows) MatY<VecX>.MatX<VecZ> = MatY<VecZ>
// new (list of columns) MatX<VecY>.MatZ<VecX> = MatZ<VecY>
pub fn dot<const Z:usize,U,V>(self,rhs:Matrix<Z,X,U>)->Matrix<Z,Y,V>
where
T:core::ops::Mul<U,Output=V>+Copy,
V:core::iter::Sum,
U:Copy,
{
let mut array_of_iterators=self.array.map(|axis|axis.into_iter().cycle());
Matrix{
array:rhs.array.map(|rhs_axis|
core::array::from_fn(|_|
array_of_iterators
.iter_mut()
.zip(rhs_axis.iter())
.map(|(lhs_iter,&rhs_value)|
lhs_iter.next().unwrap()*rhs_value
).sum()
)
)
}
}
#[inline]
// MatX<VecY>.VecY = VecX
pub fn transform_vector<U,V>(self,rhs:Vector<X,U>)->Vector<Y,V>
where
T:core::ops::Mul<U,Output=V>,
V:core::iter::Sum,
U:Copy,
{
let mut array_of_iterators=self.array.map(|axis|axis.into_iter());
Vector::new(
core::array::from_fn(|_|
array_of_iterators
.iter_mut()
.zip(rhs.array.iter())
.map(|(lhs_iter,&rhs_value)|
lhs_iter.next().unwrap()*rhs_value
).sum()
)
)
}
}
impl<const X:usize,const Y:usize,T> Matrix<X,Y,T>
where
T:Copy
{
#[inline(always)]
pub const fn from_value(value:T)->Self{
Self::new([[value;Y];X])
}
}
() => {
impl<const X: usize, const Y: usize, T> Matrix<X, Y, T> {
#[inline(always)]
pub const fn new(array: [[T; Y]; X]) -> Self {
Self { array }
}
#[inline(always)]
pub fn to_array(self) -> [[T; Y]; X] {
self.array
}
#[inline]
pub fn from_cols(cols: [Vector<Y, T>; X]) -> Self {
Matrix::new(cols.map(|col| col.array))
}
#[inline]
pub fn map<F, U>(self, f: F) -> Matrix<X, Y, U>
where
F: Fn(T) -> U,
{
Matrix::new(self.array.map(|inner| inner.map(&f)))
}
#[inline]
pub fn transpose(self) -> Matrix<Y, X, T> {
//how did I think of this
let mut array_of_iterators = self.array.map(|axis| axis.into_iter());
Matrix::new(core::array::from_fn(|_| {
array_of_iterators
.each_mut()
.map(|iter| iter.next().unwrap())
}))
}
#[inline]
// old (list of rows) MatY<VecX>.MatX<VecZ> = MatY<VecZ>
// new (list of columns) MatX<VecY>.MatZ<VecX> = MatZ<VecY>
pub fn dot<const Z: usize, U, V>(self, rhs: Matrix<Z, X, U>) -> Matrix<Z, Y, V>
where
T: core::ops::Mul<U, Output = V> + Copy,
V: core::iter::Sum,
U: Copy,
{
let mut array_of_iterators = self.array.map(|axis| axis.into_iter().cycle());
Matrix {
array: rhs.array.map(|rhs_axis| {
core::array::from_fn(|_| {
array_of_iterators
.iter_mut()
.zip(rhs_axis.iter())
.map(|(lhs_iter, &rhs_value)| lhs_iter.next().unwrap() * rhs_value)
.sum()
})
}),
}
}
#[inline]
// MatX<VecY>.VecY = VecX
pub fn transform_vector<U, V>(self, rhs: Vector<X, U>) -> Vector<Y, V>
where
T: core::ops::Mul<U, Output = V>,
V: core::iter::Sum,
U: Copy,
{
let mut array_of_iterators = self.array.map(|axis| axis.into_iter());
Vector::new(core::array::from_fn(|_| {
array_of_iterators
.iter_mut()
.zip(rhs.array.iter())
.map(|(lhs_iter, &rhs_value)| lhs_iter.next().unwrap() * rhs_value)
.sum()
}))
}
}
impl<const X: usize, const Y: usize, T> Matrix<X, Y, T>
where
T: Copy,
{
#[inline(always)]
pub const fn from_value(value: T) -> Self {
Self::new([[value; Y]; X])
}
}
impl<const X:usize,const Y:usize,T:Default> Default for Matrix<X,Y,T>{
#[inline]
fn default()->Self{
Self::new(
core::array::from_fn(|_|core::array::from_fn(|_|Default::default()))
)
}
}
impl<const X: usize, const Y: usize, T: Default> Default for Matrix<X, Y, T> {
#[inline]
fn default() -> Self {
Self::new(core::array::from_fn(|_| {
core::array::from_fn(|_| Default::default())
}))
}
}
impl<const X:usize,const Y:usize,T:core::fmt::Display> core::fmt::Display for Matrix<X,Y,T>{
#[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
for col in &self.array[0..X]{
core::write!(f,"\n")?;
for elem in &col[0..Y-1]{
core::write!(f,"{}, ",elem)?;
}
// assume we will be using matrices of size 1x1 or greater
core::write!(f,"{}",col.last().unwrap())?;
}
Ok(())
}
}
impl<const X: usize, const Y: usize, T: core::fmt::Display> core::fmt::Display
for Matrix<X, Y, T>
{
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
for col in &self.array[0..X] {
core::write!(f, "\n")?;
for elem in &col[0..Y - 1] {
core::write!(f, "{}, ", elem)?;
}
// assume we will be using matrices of size 1x1 or greater
core::write!(f, "{}", col.last().unwrap())?;
}
Ok(())
}
}
impl<const X:usize,const Y:usize,const Z:usize,T,U,V> core::ops::Mul<Matrix<Z,X,U>> for Matrix<X,Y,T>
where
T:core::ops::Mul<U,Output=V>+Copy,
V:core::iter::Sum,
U:Copy,
{
type Output=Matrix<Z,Y,V>;
#[inline]
fn mul(self,rhs:Matrix<Z,X,U>)->Self::Output{
self.dot(rhs)
}
}
impl<const X:usize,const Y:usize,T,U,V> core::ops::Mul<Vector<X,U>> for Matrix<X,Y,T>
where
T:core::ops::Mul<U,Output=V>,
V:core::iter::Sum,
U:Copy,
{
type Output=Vector<Y,V>;
#[inline]
fn mul(self,rhs:Vector<X,U>)->Self::Output{
self.transform_vector(rhs)
}
}
#[cfg(feature="deferred-division")]
$crate::impl_matrix_deferred_division!();
}
impl<const X: usize, const Y: usize, const Z: usize, T, U, V>
core::ops::Mul<Matrix<Z, X, U>> for Matrix<X, Y, T>
where
T: core::ops::Mul<U, Output = V> + Copy,
V: core::iter::Sum,
U: Copy,
{
type Output = Matrix<Z, Y, V>;
#[inline]
fn mul(self, rhs: Matrix<Z, X, U>) -> Self::Output {
self.dot(rhs)
}
}
impl<const X: usize, const Y: usize, T, U, V> core::ops::Mul<Vector<X, U>>
for Matrix<X, Y, T>
where
T: core::ops::Mul<U, Output = V>,
V: core::iter::Sum,
U: Copy,
{
type Output = Vector<Y, V>;
#[inline]
fn mul(self, rhs: Vector<X, U>) -> Self::Output {
self.transform_vector(rhs)
}
}
#[cfg(feature = "deferred-division")]
$crate::impl_matrix_deferred_division!();
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_deferred_division {
() => {
impl<const X:usize,const Y:usize,T:ratio_ops::ratio::Divide<U,Output=V>,U:Copy,V> ratio_ops::ratio::Divide<U> for Matrix<X,Y,T>{
type Output=Matrix<X,Y,V>;
#[inline]
fn divide(self,rhs:U)->Self::Output{
self.map(|t|t.divide(rhs))
}
}
impl<const X:usize,const Y:usize,T,U> core::ops::Div<U> for Matrix<X,Y,T>{
type Output=ratio_ops::ratio::Ratio<Matrix<X,Y,T>,U>;
#[inline]
fn div(self,rhs:U)->Self::Output{
ratio_ops::ratio::Ratio::new(self,rhs)
}
}
}
() => {
impl<
const X: usize,
const Y: usize,
T: ratio_ops::ratio::Divide<U, Output = V>,
U: Copy,
V,
> ratio_ops::ratio::Divide<U> for Matrix<X, Y, T>
{
type Output = Matrix<X, Y, V>;
#[inline]
fn divide(self, rhs: U) -> Self::Output {
self.map(|t| t.divide(rhs))
}
}
impl<const X: usize, const Y: usize, T, U> core::ops::Div<U> for Matrix<X, Y, T> {
type Output = ratio_ops::ratio::Ratio<Matrix<X, Y, T>, U>;
#[inline]
fn div(self, rhs: U) -> Self::Output {
ratio_ops::ratio::Ratio::new(self, rhs)
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_extend {
( $x: expr, $y: expr ) => {
impl<T> Matrix<$x,$y,T>{
#[inline]
pub fn extend_column(self,value:Vector<$y,T>)->Matrix<{$x+1},$y,T>{
let mut iter=self.array.into_iter().chain(core::iter::once(value.array));
Matrix::new(
core::array::from_fn(|_|iter.next().unwrap()),
)
}
#[inline]
pub fn extend_row(self,value:Vector<$x,T>)->Matrix<$x,{$y+1},T>{
let mut iter_rows=value.array.into_iter();
Matrix::new(
self.array.map(|axis|{
let mut elements_iter=axis.into_iter().chain(core::iter::once(iter_rows.next().unwrap()));
core::array::from_fn(|_|elements_iter.next().unwrap())
})
)
}
}
}
( $x: expr, $y: expr ) => {
impl<T> Matrix<$x, $y, T> {
#[inline]
pub fn extend_column(self, value: Vector<$y, T>) -> Matrix<{ $x + 1 }, $y, T> {
let mut iter = self.array.into_iter().chain(core::iter::once(value.array));
Matrix::new(core::array::from_fn(|_| iter.next().unwrap()))
}
#[inline]
pub fn extend_row(self, value: Vector<$x, T>) -> Matrix<$x, { $y + 1 }, T> {
let mut iter_rows = value.array.into_iter();
Matrix::new(self.array.map(|axis| {
let mut elements_iter = axis
.into_iter()
.chain(core::iter::once(iter_rows.next().unwrap()));
core::array::from_fn(|_| elements_iter.next().unwrap())
}))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_named_fields_shape {
(
(
($struct_outer:ident, $size_outer: expr),
($size_inner: expr)
) => {
impl<T> core::ops::Deref for Matrix<$size_outer,$size_inner,T>{
type Target=$struct_outer<Vector<$size_inner,T>>;
#[inline]
fn deref(&self)->&Self::Target{
unsafe{core::mem::transmute(&self.array)}
}
}
impl<T> core::ops::DerefMut for Matrix<$size_outer,$size_inner,T>{
#[inline]
fn deref_mut(&mut self)->&mut Self::Target{
unsafe{core::mem::transmute(&mut self.array)}
}
}
}
impl<T> core::ops::Deref for Matrix<$size_outer, $size_inner, T> {
type Target = $struct_outer<Vector<$size_inner, T>>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { core::mem::transmute(&self.array) }
}
}
impl<T> core::ops::DerefMut for Matrix<$size_outer, $size_inner, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { core::mem::transmute(&mut self.array) }
}
}
};
}
#[doc(hidden)]
@ -241,32 +239,44 @@ macro_rules! impl_matrix_named_fields {
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_matrix_3x3 {
()=>{
impl<T,T2,T3> Matrix<3,3,T>
where
//cross
T:core::ops::Mul<T,Output=T2>+Copy,
T2:core::ops::Sub,
//dot
T:core::ops::Mul<<T2 as core::ops::Sub>::Output,Output=T3>,
T3:core::iter::Sum,
{
pub fn det(self)->T3{
self.x_axis.dot(self.y_axis.cross(self.z_axis))
}
}
impl<T,T2> Matrix<3,3,T>
where
T:core::ops::Mul<T,Output=T2>+Copy,
T2:core::ops::Sub,
{
pub fn adjugate(self)->Matrix<3,3,<T2 as core::ops::Sub>::Output>{
Matrix::new([
[self.y_axis.y*self.z_axis.z-self.y_axis.z*self.z_axis.y,self.x_axis.z*self.z_axis.y-self.x_axis.y*self.z_axis.z,self.x_axis.y*self.y_axis.z-self.x_axis.z*self.y_axis.y],
[self.y_axis.z*self.z_axis.x-self.y_axis.x*self.z_axis.z,self.x_axis.x*self.z_axis.z-self.x_axis.z*self.z_axis.x,self.x_axis.z*self.y_axis.x-self.x_axis.x*self.y_axis.z],
[self.y_axis.x*self.z_axis.y-self.y_axis.y*self.z_axis.x,self.x_axis.y*self.z_axis.x-self.x_axis.x*self.z_axis.y,self.x_axis.x*self.y_axis.y-self.x_axis.y*self.y_axis.x],
])
}
}
}
() => {
impl<T, T2, T3> Matrix<3, 3, T>
where
//cross
T: core::ops::Mul<T, Output = T2> + Copy,
T2: core::ops::Sub,
//dot
T: core::ops::Mul<<T2 as core::ops::Sub>::Output, Output = T3>,
T3: core::iter::Sum,
{
pub fn det(self) -> T3 {
self.x_axis.dot(self.y_axis.cross(self.z_axis))
}
}
impl<T, T2> Matrix<3, 3, T>
where
T: core::ops::Mul<T, Output = T2> + Copy,
T2: core::ops::Sub,
{
pub fn adjugate(self) -> Matrix<3, 3, <T2 as core::ops::Sub>::Output> {
Matrix::new([
[
self.y_axis.y * self.z_axis.z - self.y_axis.z * self.z_axis.y,
self.x_axis.z * self.z_axis.y - self.x_axis.y * self.z_axis.z,
self.x_axis.y * self.y_axis.z - self.x_axis.z * self.y_axis.y,
],
[
self.y_axis.z * self.z_axis.x - self.y_axis.x * self.z_axis.z,
self.x_axis.x * self.z_axis.z - self.x_axis.z * self.z_axis.x,
self.x_axis.z * self.y_axis.x - self.x_axis.x * self.y_axis.z,
],
[
self.y_axis.x * self.z_axis.y - self.y_axis.y * self.z_axis.x,
self.x_axis.y * self.z_axis.x - self.x_axis.x * self.z_axis.y,
self.x_axis.x * self.y_axis.y - self.x_axis.y * self.y_axis.x,
],
])
}
}
};
}

View File

@ -1,8 +1,8 @@
pub mod common;
pub mod vector;
pub mod matrix;
pub mod vector;
#[cfg(feature="fixed-wide")]
#[cfg(feature = "fixed-wide")]
pub mod fixed_wide;
#[doc(hidden)]

View File

@ -1,357 +1,368 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector {
() => {
impl<const N:usize,T> Vector<N,T>{
#[inline(always)]
pub const fn new(array:[T;N])->Self{
Self{array}
}
#[inline(always)]
pub fn to_array(self)->[T;N]{
self.array
}
#[inline]
pub fn map<F,U>(self,f:F)->Vector<N,U>
where
F:Fn(T)->U
{
Vector::new(
self.array.map(f)
)
}
#[inline]
pub fn map_zip<F,U,V>(self,other:Vector<N,U>,f:F)->Vector<N,V>
where
F:Fn((T,U))->V,
{
let mut iter=self.array.into_iter().zip(other.array);
Vector::new(
core::array::from_fn(|_|f(iter.next().unwrap())),
)
}
}
impl<const N:usize,T:Copy> Vector<N,T>{
#[inline(always)]
pub const fn from_value(value:T)->Self{
Self::new([value;N])
}
}
() => {
impl<const N: usize, T> Vector<N, T> {
#[inline(always)]
pub const fn new(array: [T; N]) -> Self {
Self { array }
}
#[inline(always)]
pub fn to_array(self) -> [T; N] {
self.array
}
#[inline]
pub fn map<F, U>(self, f: F) -> Vector<N, U>
where
F: Fn(T) -> U,
{
Vector::new(self.array.map(f))
}
#[inline]
pub fn map_zip<F, U, V>(self, other: Vector<N, U>, f: F) -> Vector<N, V>
where
F: Fn((T, U)) -> V,
{
let mut iter = self.array.into_iter().zip(other.array);
Vector::new(core::array::from_fn(|_| f(iter.next().unwrap())))
}
}
impl<const N: usize, T: Copy> Vector<N, T> {
#[inline(always)]
pub const fn from_value(value: T) -> Self {
Self::new([value; N])
}
}
impl<const N:usize,T:Default> Default for Vector<N,T>{
#[inline]
fn default()->Self{
Self::new(
core::array::from_fn(|_|Default::default())
)
}
}
impl<const N: usize, T: Default> Default for Vector<N, T> {
#[inline]
fn default() -> Self {
Self::new(core::array::from_fn(|_| Default::default()))
}
}
impl<const N:usize,T:core::fmt::Display> core::fmt::Display for Vector<N,T>{
#[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
for elem in &self.array[0..N-1]{
core::write!(f,"{}, ",elem)?;
}
// assume we will be using vectors of length 1 or greater
core::write!(f,"{}",self.array.last().unwrap())
}
}
impl<const N: usize, T: core::fmt::Display> core::fmt::Display for Vector<N, T> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
for elem in &self.array[0..N - 1] {
core::write!(f, "{}, ", elem)?;
}
// assume we will be using vectors of length 1 or greater
core::write!(f, "{}", self.array.last().unwrap())
}
}
impl<const N:usize,T:Ord> Vector<N,T>{
#[inline]
pub fn min(self,rhs:Self)->Self{
self.map_zip(rhs,|(a,b)|a.min(b))
}
#[inline]
pub fn max(self,rhs:Self)->Self{
self.map_zip(rhs,|(a,b)|a.max(b))
}
#[inline]
pub fn cmp(self,rhs:Self)->Vector<N,core::cmp::Ordering>{
self.map_zip(rhs,|(a,b)|a.cmp(&b))
}
#[inline]
pub fn lt(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.lt(&b))
}
#[inline]
pub fn gt(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.gt(&b))
}
#[inline]
pub fn ge(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.ge(&b))
}
#[inline]
pub fn le(self,rhs:Self)->Vector<N,bool>{
self.map_zip(rhs,|(a,b)|a.le(&b))
}
}
impl<const N: usize, T: Ord> Vector<N, T> {
#[inline]
pub fn min(self, rhs: Self) -> Self {
self.map_zip(rhs, |(a, b)| a.min(b))
}
#[inline]
pub fn max(self, rhs: Self) -> Self {
self.map_zip(rhs, |(a, b)| a.max(b))
}
#[inline]
pub fn cmp(self, rhs: Self) -> Vector<N, core::cmp::Ordering> {
self.map_zip(rhs, |(a, b)| a.cmp(&b))
}
#[inline]
pub fn lt(self, rhs: Self) -> Vector<N, bool> {
self.map_zip(rhs, |(a, b)| a.lt(&b))
}
#[inline]
pub fn gt(self, rhs: Self) -> Vector<N, bool> {
self.map_zip(rhs, |(a, b)| a.gt(&b))
}
#[inline]
pub fn ge(self, rhs: Self) -> Vector<N, bool> {
self.map_zip(rhs, |(a, b)| a.ge(&b))
}
#[inline]
pub fn le(self, rhs: Self) -> Vector<N, bool> {
self.map_zip(rhs, |(a, b)| a.le(&b))
}
}
impl<const N:usize> Vector<N,bool>{
#[inline]
pub fn all(&self)->bool{
self.array==[true;N]
}
#[inline]
pub fn any(&self)->bool{
self.array!=[false;N]
}
}
impl<const N: usize> Vector<N, bool> {
#[inline]
pub fn all(&self) -> bool {
self.array == [true; N]
}
#[inline]
pub fn any(&self) -> bool {
self.array != [false; N]
}
}
impl<const N:usize,T:core::ops::Neg<Output=V>,V> core::ops::Neg for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn neg(self)->Self::Output{
Vector::new(
self.array.map(|t|-t)
)
}
}
impl<const N: usize, T: core::ops::Neg<Output = V>, V> core::ops::Neg for Vector<N, T> {
type Output = Vector<N, V>;
#[inline]
fn neg(self) -> Self::Output {
Vector::new(self.array.map(|t| -t))
}
}
impl<const N:usize,T> Vector<N,T>
{
#[inline]
pub fn dot<U,V>(self,rhs:Vector<N,U>)->V
where
T:core::ops::Mul<U,Output=V>,
V:core::iter::Sum,
{
self.array.into_iter().zip(rhs.array).map(|(a,b)|a*b).sum()
}
}
impl<const N: usize, T> Vector<N, T> {
#[inline]
pub fn dot<U, V>(self, rhs: Vector<N, U>) -> V
where
T: core::ops::Mul<U, Output = V>,
V: core::iter::Sum,
{
self.array
.into_iter()
.zip(rhs.array)
.map(|(a, b)| a * b)
.sum()
}
}
impl<const N:usize,T,V> Vector<N,T>
where
T:core::ops::Mul<Output=V>+Copy,
V:core::iter::Sum,
{
#[inline]
pub fn length_squared(self)->V{
self.array.into_iter().map(|t|t*t).sum()
}
}
impl<const N: usize, T, V> Vector<N, T>
where
T: core::ops::Mul<Output = V> + Copy,
V: core::iter::Sum,
{
#[inline]
pub fn length_squared(self) -> V {
self.array.into_iter().map(|t| t * t).sum()
}
}
// Impl arithmetic operators
$crate::impl_vector_assign_operator!(AddAssign, add_assign );
$crate::impl_vector_operator!(Add, add );
$crate::impl_vector_assign_operator!(SubAssign, sub_assign );
$crate::impl_vector_operator!(Sub, sub );
$crate::impl_vector_assign_operator!(RemAssign, rem_assign );
$crate::impl_vector_operator!(Rem, rem );
// Impl arithmetic operators
$crate::impl_vector_assign_operator!(AddAssign, add_assign);
$crate::impl_vector_operator!(Add, add);
$crate::impl_vector_assign_operator!(SubAssign, sub_assign);
$crate::impl_vector_operator!(Sub, sub);
$crate::impl_vector_assign_operator!(RemAssign, rem_assign);
$crate::impl_vector_operator!(Rem, rem);
// mul and div are special, usually you multiply by a scalar
// and implementing both vec*vec and vec*scalar is conflicting implementations Q_Q
$crate::impl_vector_assign_operator_scalar!(MulAssign, mul_assign );
$crate::impl_vector_operator_scalar!(Mul, mul );
$crate::impl_vector_assign_operator_scalar!(DivAssign, div_assign );
#[cfg(not(feature="deferred-division"))]
$crate::impl_vector_operator_scalar!(Div, div );
#[cfg(feature="deferred-division")]
$crate::impl_vector_deferred_division!();
// mul and div are special, usually you multiply by a scalar
// and implementing both vec*vec and vec*scalar is conflicting implementations Q_Q
$crate::impl_vector_assign_operator_scalar!(MulAssign, mul_assign);
$crate::impl_vector_operator_scalar!(Mul, mul);
$crate::impl_vector_assign_operator_scalar!(DivAssign, div_assign);
#[cfg(not(feature = "deferred-division"))]
$crate::impl_vector_operator_scalar!(Div, div);
#[cfg(feature = "deferred-division")]
$crate::impl_vector_deferred_division!();
// Impl bitwise operators
$crate::impl_vector_assign_operator!(BitAndAssign, bitand_assign );
$crate::impl_vector_operator!(BitAnd, bitand );
$crate::impl_vector_assign_operator!(BitOrAssign, bitor_assign );
$crate::impl_vector_operator!(BitOr, bitor );
$crate::impl_vector_assign_operator!(BitXorAssign, bitxor_assign );
$crate::impl_vector_operator!(BitXor, bitxor );
// Impl bitwise operators
$crate::impl_vector_assign_operator!(BitAndAssign, bitand_assign);
$crate::impl_vector_operator!(BitAnd, bitand);
$crate::impl_vector_assign_operator!(BitOrAssign, bitor_assign);
$crate::impl_vector_operator!(BitOr, bitor);
$crate::impl_vector_assign_operator!(BitXorAssign, bitxor_assign);
$crate::impl_vector_operator!(BitXor, bitxor);
// Impl shift operators
$crate::impl_vector_shift_assign_operator!(ShlAssign, shl_assign);
$crate::impl_vector_shift_operator!(Shl, shl);
$crate::impl_vector_shift_assign_operator!(ShrAssign, shr_assign);
$crate::impl_vector_shift_operator!(Shr, shr);
// Impl shift operators
$crate::impl_vector_shift_assign_operator!(ShlAssign, shl_assign);
$crate::impl_vector_shift_operator!(Shl, shl);
$crate::impl_vector_shift_assign_operator!(ShrAssign, shr_assign);
$crate::impl_vector_shift_operator!(Shr, shr);
// dedicated methods for this type
#[cfg(feature="fixed-wide")]
$crate::impl_fixed_wide_vector!();
}
// dedicated methods for this type
#[cfg(feature = "fixed-wide")]
$crate::impl_fixed_wide_vector!();
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_deferred_division {
() => {
impl<const N:usize,T:ratio_ops::ratio::Divide<U,Output=V>,U:Copy,V> ratio_ops::ratio::Divide<U> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn divide(self,rhs:U)->Self::Output{
self.map(|t|t.divide(rhs))
}
}
impl<const N:usize,T,U> core::ops::Div<U> for Vector<N,T>{
type Output=ratio_ops::ratio::Ratio<Vector<N,T>,U>;
#[inline]
fn div(self,rhs:U)->Self::Output{
ratio_ops::ratio::Ratio::new(self,rhs)
}
}
}
() => {
impl<const N: usize, T: ratio_ops::ratio::Divide<U, Output = V>, U: Copy, V>
ratio_ops::ratio::Divide<U> for Vector<N, T>
{
type Output = Vector<N, V>;
#[inline]
fn divide(self, rhs: U) -> Self::Output {
self.map(|t| t.divide(rhs))
}
}
impl<const N: usize, T, U> core::ops::Div<U> for Vector<N, T> {
type Output = ratio_ops::ratio::Ratio<Vector<N, T>, U>;
#[inline]
fn div(self, rhs: U) -> Self::Output {
ratio_ops::ratio::Ratio::new(self, rhs)
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_operator_scalar {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U,Output=V>,U:Copy,V> core::ops::$trait<U> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:U)->Self::Output{
self.map(|t|t.$method(rhs))
}
}
}
($trait: ident, $method: ident ) => {
impl<const N: usize, T: core::ops::$trait<U, Output = V>, U: Copy, V> core::ops::$trait<U>
for Vector<N, T>
{
type Output = Vector<N, V>;
#[inline]
fn $method(self, rhs: U) -> Self::Output {
self.map(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U,Output=V>,U,V> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:Vector<N,U>)->Self::Output{
self.map_zip(rhs,|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<i64,Output=T>> core::ops::$trait<i64> for Vector<N,T>{
type Output=Self;
#[inline]
fn $method(self,rhs:i64)->Self::Output{
self.map(|t|t.$method(rhs))
}
}
}
($trait: ident, $method: ident ) => {
impl<const N: usize, T: core::ops::$trait<U, Output = V>, U, V>
core::ops::$trait<Vector<N, U>> for Vector<N, T>
{
type Output = Vector<N, V>;
#[inline]
fn $method(self, rhs: Vector<N, U>) -> Self::Output {
self.map_zip(rhs, |(a, b)| a.$method(b))
}
}
impl<const N: usize, T: core::ops::$trait<i64, Output = T>> core::ops::$trait<i64>
for Vector<N, T>
{
type Output = Self;
#[inline]
fn $method(self, rhs: i64) -> Self::Output {
self.map(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_assign_operator_scalar {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U>,U:Copy> core::ops::$trait<U> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:U){
self.array.iter_mut()
.for_each(|t|t.$method(rhs))
}
}
}
($trait: ident, $method: ident ) => {
impl<const N: usize, T: core::ops::$trait<U>, U: Copy> core::ops::$trait<U>
for Vector<N, T>
{
#[inline]
fn $method(&mut self, rhs: U) {
self.array.iter_mut().for_each(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_assign_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U>,U> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:Vector<N,U>){
self.array.iter_mut().zip(rhs.array)
.for_each(|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<i64>> core::ops::$trait<i64> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:i64){
self.array.iter_mut()
.for_each(|t|t.$method(rhs))
}
}
}
($trait: ident, $method: ident ) => {
impl<const N: usize, T: core::ops::$trait<U>, U> core::ops::$trait<Vector<N, U>>
for Vector<N, T>
{
#[inline]
fn $method(&mut self, rhs: Vector<N, U>) {
self.array
.iter_mut()
.zip(rhs.array)
.for_each(|(a, b)| a.$method(b))
}
}
impl<const N: usize, T: core::ops::$trait<i64>> core::ops::$trait<i64> for Vector<N, T> {
#[inline]
fn $method(&mut self, rhs: i64) {
self.array.iter_mut().for_each(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_shift_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U,Output=V>,U,V> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:Vector<N,U>)->Self::Output{
self.map_zip(rhs,|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<u32,Output=V>,V> core::ops::$trait<u32> for Vector<N,T>{
type Output=Vector<N,V>;
#[inline]
fn $method(self,rhs:u32)->Self::Output{
self.map(|t|t.$method(rhs))
}
}
}
($trait: ident, $method: ident ) => {
impl<const N: usize, T: core::ops::$trait<U, Output = V>, U, V>
core::ops::$trait<Vector<N, U>> for Vector<N, T>
{
type Output = Vector<N, V>;
#[inline]
fn $method(self, rhs: Vector<N, U>) -> Self::Output {
self.map_zip(rhs, |(a, b)| a.$method(b))
}
}
impl<const N: usize, T: core::ops::$trait<u32, Output = V>, V> core::ops::$trait<u32>
for Vector<N, T>
{
type Output = Vector<N, V>;
#[inline]
fn $method(self, rhs: u32) -> Self::Output {
self.map(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_shift_assign_operator {
($trait: ident, $method: ident ) => {
impl<const N:usize,T:core::ops::$trait<U>,U> core::ops::$trait<Vector<N,U>> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:Vector<N,U>){
self.array.iter_mut().zip(rhs.array)
.for_each(|(a,b)|a.$method(b))
}
}
impl<const N:usize,T:core::ops::$trait<u32>> core::ops::$trait<u32> for Vector<N,T>{
#[inline]
fn $method(&mut self,rhs:u32){
self.array.iter_mut()
.for_each(|t|t.$method(rhs))
}
}
}
($trait: ident, $method: ident ) => {
impl<const N: usize, T: core::ops::$trait<U>, U> core::ops::$trait<Vector<N, U>>
for Vector<N, T>
{
#[inline]
fn $method(&mut self, rhs: Vector<N, U>) {
self.array
.iter_mut()
.zip(rhs.array)
.for_each(|(a, b)| a.$method(b))
}
}
impl<const N: usize, T: core::ops::$trait<u32>> core::ops::$trait<u32> for Vector<N, T> {
#[inline]
fn $method(&mut self, rhs: u32) {
self.array.iter_mut().for_each(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_extend {
( $size: expr ) => {
impl<T> Vector<$size,T>{
#[inline]
pub fn extend(self,value:T)->Vector<{$size+1},T>{
let mut iter=self.array.into_iter().chain(core::iter::once(value));
Vector::new(
core::array::from_fn(|_|iter.next().unwrap()),
)
}
}
}
( $size: expr ) => {
impl<T> Vector<$size, T> {
#[inline]
pub fn extend(self, value: T) -> Vector<{ $size + 1 }, T> {
let mut iter = self.array.into_iter().chain(core::iter::once(value));
Vector::new(core::array::from_fn(|_| iter.next().unwrap()))
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_named_fields {
( $struct:ident, $size: expr ) => {
impl<T> core::ops::Deref for Vector<$size,T>{
type Target=$struct<T>;
#[inline]
fn deref(&self)->&Self::Target{
unsafe{core::mem::transmute(&self.array)}
}
}
impl<T> core::ops::DerefMut for Vector<$size,T>{
#[inline]
fn deref_mut(&mut self)->&mut Self::Target{
unsafe{core::mem::transmute(&mut self.array)}
}
}
}
( $struct:ident, $size: expr ) => {
impl<T> core::ops::Deref for Vector<$size, T> {
type Target = $struct<T>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { core::mem::transmute(&self.array) }
}
}
impl<T> core::ops::DerefMut for Vector<$size, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { core::mem::transmute(&mut self.array) }
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_3 {
()=>{
impl<T> Vector<3,T>
{
#[inline]
pub fn cross<U,V>(self,rhs:Vector<3,U>)->Vector<3,<V as core::ops::Sub>::Output>
where
T:core::ops::Mul<U,Output=V>+Copy,
U:Copy,
V:core::ops::Sub,
{
Vector::new([
self.y*rhs.z-self.z*rhs.y,
self.z*rhs.x-self.x*rhs.z,
self.x*rhs.y-self.y*rhs.x,
])
}
}
}
() => {
impl<T> Vector<3, T> {
#[inline]
pub fn cross<U, V>(self, rhs: Vector<3, U>) -> Vector<3, <V as core::ops::Sub>::Output>
where
T: core::ops::Mul<U, Output = V> + Copy,
U: Copy,
V: core::ops::Sub,
{
Vector::new([
self.y * rhs.z - self.z * rhs.y,
self.z * rhs.x - self.x * rhs.z,
self.x * rhs.y - self.y * rhs.x,
])
}
}
};
}

View File

@ -1,17 +1,17 @@
use crate::vector::Vector;
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
pub struct Matrix<const X:usize,const Y:usize,T>{
pub(crate) array:[[T;Y];X],
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub struct Matrix<const X: usize, const Y: usize, T> {
pub(crate) array: [[T; Y]; X],
}
crate::impl_matrix!();
crate::impl_matrix_extend!(2,2);
crate::impl_matrix_extend!(2,3);
crate::impl_matrix_extend!(3,2);
crate::impl_matrix_extend!(3,3);
crate::impl_matrix_extend!(2, 2);
crate::impl_matrix_extend!(2, 3);
crate::impl_matrix_extend!(3, 2);
crate::impl_matrix_extend!(3, 3);
//Special case 3x3 matrix operations because I cba to write macros for the arbitrary cases
#[cfg(feature="named-fields")]
#[cfg(feature = "named-fields")]
crate::impl_matrix_3x3!();

View File

@ -1,23 +1,23 @@
use crate::vector::Vector;
use crate::matrix::Matrix;
use crate::vector::Vector;
#[repr(C)]
pub struct Vector2<T> {
pub x: T,
pub y: T,
pub x: T,
pub y: T,
}
#[repr(C)]
pub struct Vector3<T> {
pub x: T,
pub y: T,
pub z: T,
pub x: T,
pub y: T,
pub z: T,
}
#[repr(C)]
pub struct Vector4<T> {
pub x: T,
pub y: T,
pub z: T,
pub w: T,
pub x: T,
pub y: T,
pub z: T,
pub w: T,
}
crate::impl_vector_named_fields!(Vector2, 2);
@ -26,34 +26,26 @@ crate::impl_vector_named_fields!(Vector4, 4);
#[repr(C)]
pub struct Matrix2<T> {
pub x_axis: T,
pub y_axis: T,
pub x_axis: T,
pub y_axis: T,
}
#[repr(C)]
pub struct Matrix3<T> {
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
}
#[repr(C)]
pub struct Matrix4<T> {
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
pub w_axis: T,
pub x_axis: T,
pub y_axis: T,
pub z_axis: T,
pub w_axis: T,
}
crate::impl_matrix_named_fields!(
//outer struct
(
(Matrix2, 2),
(Matrix3, 3),
(Matrix4, 4)
),
//inner struct
(
(2),
(3),
(4)
)
//outer struct
((Matrix2, 2), (Matrix3, 3), (Matrix4, 4)),
//inner struct
((2), (3), (4))
);

View File

@ -1,96 +1,131 @@
use crate::types::{Matrix3,Matrix3x2,Matrix3x4,Matrix4x2,Vector3};
use crate::types::{Matrix3, Matrix3x2, Matrix3x4, Matrix4x2, Vector3};
type Planar64=fixed_wide::types::I32F32;
type Planar64Wide1=fixed_wide::types::I64F64;
type Planar64 = fixed_wide::types::I32F32;
type Planar64Wide1 = fixed_wide::types::I64F64;
//type Planar64Wide2=fixed_wide::types::I128F128;
type Planar64Wide3=fixed_wide::types::I256F256;
type Planar64Wide3 = fixed_wide::types::I256F256;
#[test]
fn wide_vec3(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v*v.x;
let v2=v1*v1.y;
let v3=v2*v2.z;
fn wide_vec3() {
let v = Vector3::from_value(Planar64::from(3));
let v1 = v * v.x;
let v2 = v1 * v1.y;
let v3 = v2 * v2.z;
assert_eq!(v3.array,Vector3::from_value(Planar64Wide3::from(3i128.pow(8))).array);
assert_eq!(
v3.array,
Vector3::from_value(Planar64Wide3::from(3i128.pow(8))).array
);
}
#[test]
fn wide_vec3_dot(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v*v.x;
let v2=v1*v1.y;
let v3=v2.dot(v2);
fn wide_vec3_dot() {
let v = Vector3::from_value(Planar64::from(3));
let v1 = v * v.x;
let v2 = v1 * v1.y;
let v3 = v2.dot(v2);
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
assert_eq!(v3, Planar64Wide3::from(3i128.pow(8) * 3));
}
#[test]
fn wide_vec3_length_squared(){
let v=Vector3::from_value(Planar64::from(3));
let v1=v*v.x;
let v2=v1*v1.y;
let v3=v2.length_squared();
fn wide_vec3_length_squared() {
let v = Vector3::from_value(Planar64::from(3));
let v1 = v * v.x;
let v2 = v1 * v1.y;
let v3 = v2.length_squared();
assert_eq!(v3,Planar64Wide3::from(3i128.pow(8)*3));
assert_eq!(v3, Planar64Wide3::from(3i128.pow(8) * 3));
}
#[test]
fn wide_matrix_dot(){
let lhs=Matrix3x4::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3),Planar64::from(4)],
[Planar64::from(5),Planar64::from(6),Planar64::from(7),Planar64::from(8)],
[Planar64::from(9),Planar64::from(10),Planar64::from(11),Planar64::from(12)],
]).transpose();
let rhs=Matrix4x2::new([
[Planar64::from(1),Planar64::from(2)],
[Planar64::from(3),Planar64::from(4)],
[Planar64::from(5),Planar64::from(6)],
[Planar64::from(7),Planar64::from(8)],
]).transpose();
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs*rhs;
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot.array,
Matrix3x2::new([
[Planar64Wide1::from(50),Planar64Wide1::from(60)],
[Planar64Wide1::from(114),Planar64Wide1::from(140)],
[Planar64Wide1::from(178),Planar64Wide1::from(220)],
]).transpose().array
);
fn wide_matrix_dot() {
let lhs = Matrix3x4::new([
[
Planar64::from(1),
Planar64::from(2),
Planar64::from(3),
Planar64::from(4),
],
[
Planar64::from(5),
Planar64::from(6),
Planar64::from(7),
Planar64::from(8),
],
[
Planar64::from(9),
Planar64::from(10),
Planar64::from(11),
Planar64::from(12),
],
])
.transpose();
let rhs = Matrix4x2::new([
[Planar64::from(1), Planar64::from(2)],
[Planar64::from(3), Planar64::from(4)],
[Planar64::from(5), Planar64::from(6)],
[Planar64::from(7), Planar64::from(8)],
])
.transpose();
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot = lhs * rhs;
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot.array,
Matrix3x2::new([
[Planar64Wide1::from(50), Planar64Wide1::from(60)],
[Planar64Wide1::from(114), Planar64Wide1::from(140)],
[Planar64Wide1::from(178), Planar64Wide1::from(220)],
])
.transpose()
.array
);
}
#[test]
#[cfg(feature="named-fields")]
fn wide_matrix_det(){
let m=Matrix3::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3)],
[Planar64::from(4),Planar64::from(5),Planar64::from(7)],
[Planar64::from(6),Planar64::from(8),Planar64::from(9)],
]);
// In[2]:= Det[{{1, 2, 3}, {4, 5, 7}, {6, 8, 9}}]
// Out[2]= 7
assert_eq!(m.det(),fixed_wide::fixed::Fixed::<3,96>::from(7));
#[cfg(feature = "named-fields")]
fn wide_matrix_det() {
let m = Matrix3::new([
[Planar64::from(1), Planar64::from(2), Planar64::from(3)],
[Planar64::from(4), Planar64::from(5), Planar64::from(7)],
[Planar64::from(6), Planar64::from(8), Planar64::from(9)],
]);
// In[2]:= Det[{{1, 2, 3}, {4, 5, 7}, {6, 8, 9}}]
// Out[2]= 7
assert_eq!(m.det(), fixed_wide::fixed::Fixed::<3, 96>::from(7));
}
#[test]
#[cfg(feature="named-fields")]
fn wide_matrix_adjugate(){
let m=Matrix3::new([
[Planar64::from(1),Planar64::from(2),Planar64::from(3)],
[Planar64::from(4),Planar64::from(5),Planar64::from(7)],
[Planar64::from(6),Planar64::from(8),Planar64::from(9)],
]);
// In[6]:= Adjugate[{{1, 2, 3}, {4, 5, 7}, {6, 8, 9}}]
// Out[6]= {{-11, 6, -1}, {6, -9, 5}, {2, 4, -3}}
assert_eq!(
m.adjugate().array,
Matrix3::new([
[Planar64Wide1::from(-11),Planar64Wide1::from(6),Planar64Wide1::from(-1)],
[Planar64Wide1::from(6),Planar64Wide1::from(-9),Planar64Wide1::from(5)],
[Planar64Wide1::from(2),Planar64Wide1::from(4),Planar64Wide1::from(-3)],
]).array
);
#[cfg(feature = "named-fields")]
fn wide_matrix_adjugate() {
let m = Matrix3::new([
[Planar64::from(1), Planar64::from(2), Planar64::from(3)],
[Planar64::from(4), Planar64::from(5), Planar64::from(7)],
[Planar64::from(6), Planar64::from(8), Planar64::from(9)],
]);
// In[6]:= Adjugate[{{1, 2, 3}, {4, 5, 7}, {6, 8, 9}}]
// Out[6]= {{-11, 6, -1}, {6, -9, 5}, {2, 4, -3}}
assert_eq!(
m.adjugate().array,
Matrix3::new([
[
Planar64Wide1::from(-11),
Planar64Wide1::from(6),
Planar64Wide1::from(-1)
],
[
Planar64Wide1::from(6),
Planar64Wide1::from(-9),
Planar64Wide1::from(5)
],
[
Planar64Wide1::from(2),
Planar64Wide1::from(4),
Planar64Wide1::from(-3)
],
])
.array
);
}

View File

@ -1,6 +1,6 @@
mod tests;
#[cfg(feature="named-fields")]
#[cfg(feature = "named-fields")]
mod named;
mod fixed_wide;

View File

@ -1,30 +1,29 @@
use crate::types::{Vector3,Matrix3};
use crate::types::{Matrix3, Vector3};
#[test]
fn test_vector(){
let mut v=Vector3::new([1,2,3]);
assert_eq!(v.x,1);
assert_eq!(v.y,2);
assert_eq!(v.z,3);
fn test_vector() {
let mut v = Vector3::new([1, 2, 3]);
assert_eq!(v.x, 1);
assert_eq!(v.y, 2);
assert_eq!(v.z, 3);
v.x=5;
assert_eq!(v.x,5);
v.x = 5;
assert_eq!(v.x, 5);
v.y*=v.x;
assert_eq!(v.y,10);
v.y *= v.x;
assert_eq!(v.y, 10);
}
#[test]
fn test_matrix(){
let mut v=Matrix3::from_value(2);
assert_eq!(v.x_axis.x,2);
assert_eq!(v.y_axis.y,2);
assert_eq!(v.z_axis.z,2);
fn test_matrix() {
let mut v = Matrix3::from_value(2);
assert_eq!(v.x_axis.x, 2);
assert_eq!(v.y_axis.y, 2);
assert_eq!(v.z_axis.z, 2);
v.x_axis.x=5;
assert_eq!(v.x_axis.x,5);
v.x_axis.x = 5;
assert_eq!(v.x_axis.x, 5);
v.y_axis.z*=v.x_axis.x;
assert_eq!(v.y_axis.z,10);
v.y_axis.z *= v.x_axis.x;
assert_eq!(v.y_axis.z, 10);
}

View File

@ -1,59 +1,51 @@
use crate::types::{Vector2,Vector3,Matrix3x4,Matrix4x2,Matrix3x2,Matrix2x3};
use crate::types::{Matrix2x3, Matrix3x2, Matrix3x4, Matrix4x2, Vector2, Vector3};
#[test]
fn test_bool(){
assert_eq!(Vector3::new([false,false,false]).any(),false);
assert_eq!(Vector3::new([false,false,true]).any(),true);
assert_eq!(Vector3::new([false,false,true]).all(),false);
assert_eq!(Vector3::new([true,true,true]).all(),true);
fn test_bool() {
assert_eq!(Vector3::new([false, false, false]).any(), false);
assert_eq!(Vector3::new([false, false, true]).any(), true);
assert_eq!(Vector3::new([false, false, true]).all(), false);
assert_eq!(Vector3::new([true, true, true]).all(), true);
}
#[test]
fn test_length_squared(){
assert_eq!(Vector3::new([1,2,3]).length_squared(),14);
fn test_length_squared() {
assert_eq!(Vector3::new([1, 2, 3]).length_squared(), 14);
}
#[test]
fn test_arithmetic(){
let a=Vector3::new([1,2,3]);
assert_eq!((a+a*2).array,Vector3::new([1*3,2*3,3*3]).array);
fn test_arithmetic() {
let a = Vector3::new([1, 2, 3]);
assert_eq!((a + a * 2).array, Vector3::new([1 * 3, 2 * 3, 3 * 3]).array);
}
#[test]
fn matrix_transform_vector(){
let m=Matrix2x3::new([
[1,2,3],
[4,5,6],
]).transpose();
let v=Vector3::new([1,2,3]);
let transformed=m*v;
assert_eq!(transformed.array,Vector2::new([14,32]).array);
fn matrix_transform_vector() {
let m = Matrix2x3::new([[1, 2, 3], [4, 5, 6]]).transpose();
let v = Vector3::new([1, 2, 3]);
let transformed = m * v;
assert_eq!(transformed.array, Vector2::new([14, 32]).array);
}
#[test]
fn matrix_dot(){
// All this code was written row major and I converted the lib to colum major
let rhs=Matrix4x2::new([
[ 1.0, 2.0],
[ 3.0, 4.0],
[ 5.0, 6.0],
[ 7.0, 8.0],
]).transpose(); // | | |
let lhs=Matrix3x4::new([ // | | |
[1.0, 2.0, 3.0, 4.0],// [ 50.0, 60.0],
[5.0, 6.0, 7.0, 8.0],// [114.0,140.0],
[9.0,10.0,11.0,12.0],// [178.0,220.0],
]).transpose();
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot=lhs*rhs;
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot.array,
Matrix3x2::new([
[50.0,60.0],
[114.0,140.0],
[178.0,220.0],
]).transpose().array
);
fn matrix_dot() {
// All this code was written row major and I converted the lib to colum major
let rhs = Matrix4x2::new([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0]]).transpose(); // | | |
let lhs = Matrix3x4::new([
// | | |
[1.0, 2.0, 3.0, 4.0], // [ 50.0, 60.0],
[5.0, 6.0, 7.0, 8.0], // [114.0,140.0],
[9.0, 10.0, 11.0, 12.0], // [178.0,220.0],
])
.transpose();
// Mat3<Vec4>.dot(Mat4<Vec2>) -> Mat3<Vec2>
let m_dot = lhs * rhs;
//In[1]:= {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} . {{1, 2}, {3, 4}, {5, 6}, {7, 8}}
//Out[1]= {{50, 60}, {114, 140}, {178, 220}}
assert_eq!(
m_dot.array,
Matrix3x2::new([[50.0, 60.0], [114.0, 140.0], [178.0, 220.0],])
.transpose()
.array
);
}

View File

@ -1,18 +1,18 @@
use crate::vector::Vector;
use crate::matrix::Matrix;
use crate::vector::Vector;
pub type Vector2<T>=Vector<2,T>;
pub type Vector3<T>=Vector<3,T>;
pub type Vector4<T>=Vector<4,T>;
pub type Vector2<T> = Vector<2, T>;
pub type Vector3<T> = Vector<3, T>;
pub type Vector4<T> = Vector<4, T>;
pub type Matrix2<T>=Matrix<2,2,T>;
pub type Matrix2x3<T>=Matrix<2,3,T>;
pub type Matrix2x4<T>=Matrix<2,4,T>;
pub type Matrix2<T> = Matrix<2, 2, T>;
pub type Matrix2x3<T> = Matrix<2, 3, T>;
pub type Matrix2x4<T> = Matrix<2, 4, T>;
pub type Matrix3x2<T>=Matrix<3,2,T>;
pub type Matrix3<T>=Matrix<3,3,T>;
pub type Matrix3x4<T>=Matrix<3,4,T>;
pub type Matrix3x2<T> = Matrix<3, 2, T>;
pub type Matrix3<T> = Matrix<3, 3, T>;
pub type Matrix3x4<T> = Matrix<3, 4, T>;
pub type Matrix4x2<T>=Matrix<4,2,T>;
pub type Matrix4x3<T>=Matrix<4,3,T>;
pub type Matrix4<T>=Matrix<4,4,T>;
pub type Matrix4x2<T> = Matrix<4, 2, T>;
pub type Matrix4x3<T> = Matrix<4, 3, T>;
pub type Matrix4<T> = Matrix<4, 4, T>;

View File

@ -3,9 +3,9 @@
/// v.x += v.z;
/// println!("v.x={}",v.x);
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
pub struct Vector<const N:usize,T>{
pub(crate) array:[T;N],
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub struct Vector<const N: usize, T> {
pub(crate) array: [T; N],
}
crate::impl_vector!();
@ -15,5 +15,5 @@ crate::impl_vector_extend!(2);
crate::impl_vector_extend!(3);
//cross product
#[cfg(feature="named-fields")]
#[cfg(feature = "named-fields")]
crate::impl_vector_3!();

View File

@ -1,77 +1,92 @@
#[derive(Clone,Copy,Debug,Hash)]
pub struct Ratio<Num,Den>{
pub num:Num,
pub den:Den,
#[derive(Clone, Copy, Debug, Hash)]
pub struct Ratio<Num, Den> {
pub num: Num,
pub den: Den,
}
impl<Num,Den> Ratio<Num,Den>{
#[inline(always)]
pub const fn new(num:Num,den:Den)->Self{
Self{num,den}
}
impl<Num, Den> Ratio<Num, Den> {
#[inline(always)]
pub const fn new(num: Num, den: Den) -> Self {
Self { num, den }
}
}
/// The actual divide implementation, Div is replaced with a Ratio constructor
pub trait Divide<Rhs=Self>{
type Output;
fn divide(self,rhs:Rhs)->Self::Output;
pub trait Divide<Rhs = Self> {
type Output;
fn divide(self, rhs: Rhs) -> Self::Output;
}
impl<Num,Den> Ratio<Num,Den>
where
Num:Divide<Den>,
impl<Num, Den> Ratio<Num, Den>
where
Num: Divide<Den>,
{
#[inline]
pub fn divide(self)-><Num as Divide<Den>>::Output{
self.num.divide(self.den)
}
#[inline]
pub fn divide(self) -> <Num as Divide<Den>>::Output {
self.num.divide(self.den)
}
}
//take care to use the ratio methods to avoid nested ratios
impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
#[inline]
pub fn mul_ratio<RhsNum,RhsDen>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsNum as core::ops::Mul<RhsNum>>::Output,<LhsDen as core::ops::Mul<RhsDen>>::Output>
where
LhsNum:core::ops::Mul<RhsNum>,
LhsDen:core::ops::Mul<RhsDen>,
{
Ratio::new(self.num*rhs.num,self.den*rhs.den)
}
#[inline]
pub fn div_ratio<RhsNum,RhsDen>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsNum as core::ops::Mul<RhsDen>>::Output,<LhsDen as core::ops::Mul<RhsNum>>::Output>
where
LhsNum:core::ops::Mul<RhsDen>,
LhsDen:core::ops::Mul<RhsNum>,
{
Ratio::new(self.num*rhs.den,self.den*rhs.num)
}
impl<LhsNum, LhsDen> Ratio<LhsNum, LhsDen> {
#[inline]
pub fn mul_ratio<RhsNum, RhsDen>(
self,
rhs: Ratio<RhsNum, RhsDen>,
) -> Ratio<<LhsNum as core::ops::Mul<RhsNum>>::Output, <LhsDen as core::ops::Mul<RhsDen>>::Output>
where
LhsNum: core::ops::Mul<RhsNum>,
LhsDen: core::ops::Mul<RhsDen>,
{
Ratio::new(self.num * rhs.num, self.den * rhs.den)
}
#[inline]
pub fn div_ratio<RhsNum, RhsDen>(
self,
rhs: Ratio<RhsNum, RhsDen>,
) -> Ratio<<LhsNum as core::ops::Mul<RhsDen>>::Output, <LhsDen as core::ops::Mul<RhsNum>>::Output>
where
LhsNum: core::ops::Mul<RhsDen>,
LhsDen: core::ops::Mul<RhsNum>,
{
Ratio::new(self.num * rhs.den, self.den * rhs.num)
}
}
macro_rules! impl_ratio_method {
($trait:ident, $method:ident, $ratio_method:ident) => {
impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
#[inline]
pub fn $ratio_method<RhsNum,RhsDen,LhsCrossMul,RhsCrossMul>(self,rhs:Ratio<RhsNum,RhsDen>)->Ratio<<LhsCrossMul as core::ops::$trait<RhsCrossMul>>::Output,<LhsDen as core::ops::Mul<RhsDen>>::Output>
where
LhsNum:core::ops::Mul<RhsDen,Output=LhsCrossMul>,
LhsDen:core::ops::Mul<RhsNum,Output=RhsCrossMul>,
LhsDen:core::ops::Mul<RhsDen>,
LhsDen:Copy,
RhsDen:Copy,
LhsCrossMul:core::ops::$trait<RhsCrossMul>,
{
Ratio::new((self.num*rhs.den).$method(self.den*rhs.num),self.den*rhs.den)
}
}
};
($trait:ident, $method:ident, $ratio_method:ident) => {
impl<LhsNum, LhsDen> Ratio<LhsNum, LhsDen> {
#[inline]
pub fn $ratio_method<RhsNum, RhsDen, LhsCrossMul, RhsCrossMul>(
self,
rhs: Ratio<RhsNum, RhsDen>,
) -> Ratio<
<LhsCrossMul as core::ops::$trait<RhsCrossMul>>::Output,
<LhsDen as core::ops::Mul<RhsDen>>::Output,
>
where
LhsNum: core::ops::Mul<RhsDen, Output = LhsCrossMul>,
LhsDen: core::ops::Mul<RhsNum, Output = RhsCrossMul>,
LhsDen: core::ops::Mul<RhsDen>,
LhsDen: Copy,
RhsDen: Copy,
LhsCrossMul: core::ops::$trait<RhsCrossMul>,
{
Ratio::new(
(self.num * rhs.den).$method(self.den * rhs.num),
self.den * rhs.den,
)
}
}
};
}
impl_ratio_method!(Add,add,add_ratio);
impl_ratio_method!(Sub,sub,sub_ratio);
impl_ratio_method!(Rem,rem,rem_ratio);
impl_ratio_method!(Add, add, add_ratio);
impl_ratio_method!(Sub, sub, sub_ratio);
impl_ratio_method!(Rem, rem, rem_ratio);
/// Comparing two ratios needs to know the parity of the denominators
/// For signed integers this can be implemented with is_negative()
pub trait Parity{
fn parity(&self)->bool;
pub trait Parity {
fn parity(&self) -> bool;
}
macro_rules! impl_parity_unsigned{
($($type:ty),*)=>{
@ -107,191 +122,196 @@ macro_rules! impl_parity_float{
};
}
impl_parity_unsigned!(u8,u16,u32,u64,u128,usize);
impl_parity_signed!(i8,i16,i32,i64,i128,isize);
impl_parity_float!(f32,f64);
impl_parity_unsigned!(u8, u16, u32, u64, u128, usize);
impl_parity_signed!(i8, i16, i32, i64, i128, isize);
impl_parity_float!(f32, f64);
macro_rules! impl_ratio_ord_method{
($method:ident, $ratio_method:ident, $output:ty)=>{
impl<LhsNum,LhsDen:Parity> Ratio<LhsNum,LhsDen>{
#[inline]
pub fn $ratio_method<RhsNum,RhsDen:Parity,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
where
LhsNum:core::ops::Mul<RhsDen,Output=T>,
LhsDen:core::ops::Mul<RhsNum,Output=T>,
T:Ord,
{
match self.den.parity()^rhs.den.parity(){
true=>(self.den*rhs.num).$method(&(self.num*rhs.den)),
false=>(self.num*rhs.den).$method(&(self.den*rhs.num)),
}
}
}
}
macro_rules! impl_ratio_ord_method {
($method:ident, $ratio_method:ident, $output:ty) => {
impl<LhsNum, LhsDen: Parity> Ratio<LhsNum, LhsDen> {
#[inline]
pub fn $ratio_method<RhsNum, RhsDen: Parity, T>(
self,
rhs: Ratio<RhsNum, RhsDen>,
) -> $output
where
LhsNum: core::ops::Mul<RhsDen, Output = T>,
LhsDen: core::ops::Mul<RhsNum, Output = T>,
T: Ord,
{
match self.den.parity() ^ rhs.den.parity() {
true => (self.den * rhs.num).$method(&(self.num * rhs.den)),
false => (self.num * rhs.den).$method(&(self.den * rhs.num)),
}
}
}
};
}
//PartialEq
impl_ratio_ord_method!(eq,eq_ratio,bool);
impl_ratio_ord_method!(eq, eq_ratio, bool);
//PartialOrd
impl_ratio_ord_method!(lt,lt_ratio,bool);
impl_ratio_ord_method!(gt,gt_ratio,bool);
impl_ratio_ord_method!(le,le_ratio,bool);
impl_ratio_ord_method!(ge,ge_ratio,bool);
impl_ratio_ord_method!(partial_cmp,partial_cmp_ratio,Option<core::cmp::Ordering>);
impl_ratio_ord_method!(lt, lt_ratio, bool);
impl_ratio_ord_method!(gt, gt_ratio, bool);
impl_ratio_ord_method!(le, le_ratio, bool);
impl_ratio_ord_method!(ge, ge_ratio, bool);
impl_ratio_ord_method!(partial_cmp, partial_cmp_ratio, Option<core::cmp::Ordering>);
//Ord
impl_ratio_ord_method!(cmp,cmp_ratio,core::cmp::Ordering);
impl_ratio_ord_method!(cmp, cmp_ratio, core::cmp::Ordering);
/* generic rhs mul is not possible!
impl<Lhs,RhsNum,RhsDen> core::ops::Mul<Ratio<RhsNum,RhsDen>> for Lhs
where
Lhs:core::ops::Mul<RhsNum>,
where
Lhs:core::ops::Mul<RhsNum>,
{
type Output=Ratio<<Lhs as core::ops::Mul<RhsNum>>::Output,RhsDen>;
#[inline]
fn mul(self,rhs:Ratio<RhsNum,RhsDen>)->Self::Output{
Ratio::new(self*rhs.num,rhs.den)
}
type Output=Ratio<<Lhs as core::ops::Mul<RhsNum>>::Output,RhsDen>;
#[inline]
fn mul(self,rhs:Ratio<RhsNum,RhsDen>)->Self::Output{
Ratio::new(self*rhs.num,rhs.den)
}
}
*/
//operators
impl<LhsNum,LhsDen> core::ops::Neg for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::Neg,
impl<LhsNum, LhsDen> core::ops::Neg for Ratio<LhsNum, LhsDen>
where
LhsNum: core::ops::Neg,
{
type Output=Ratio<<LhsNum as core::ops::Neg>::Output,LhsDen>;
#[inline]
fn neg(self)->Self::Output{
Ratio::new(-self.num,self.den)
}
type Output = Ratio<<LhsNum as core::ops::Neg>::Output, LhsDen>;
#[inline]
fn neg(self) -> Self::Output {
Ratio::new(-self.num, self.den)
}
}
impl<LhsNum,LhsDen,Rhs> core::ops::Mul<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::Mul<Rhs>,
impl<LhsNum, LhsDen, Rhs> core::ops::Mul<Rhs> for Ratio<LhsNum, LhsDen>
where
LhsNum: core::ops::Mul<Rhs>,
{
type Output=Ratio<<LhsNum as core::ops::Mul<Rhs>>::Output,LhsDen>;
#[inline]
fn mul(self,rhs:Rhs)->Self::Output{
Ratio::new(self.num*rhs,self.den)
}
type Output = Ratio<<LhsNum as core::ops::Mul<Rhs>>::Output, LhsDen>;
#[inline]
fn mul(self, rhs: Rhs) -> Self::Output {
Ratio::new(self.num * rhs, self.den)
}
}
impl<LhsNum,LhsDen,Rhs> core::ops::Div<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsDen:core::ops::Mul<Rhs>,
impl<LhsNum, LhsDen, Rhs> core::ops::Div<Rhs> for Ratio<LhsNum, LhsDen>
where
LhsDen: core::ops::Mul<Rhs>,
{
type Output=Ratio<LhsNum,<LhsDen as core::ops::Mul<Rhs>>::Output>;
#[inline]
fn div(self,rhs:Rhs)->Self::Output{
Ratio::new(self.num,self.den*rhs)
}
type Output = Ratio<LhsNum, <LhsDen as core::ops::Mul<Rhs>>::Output>;
#[inline]
fn div(self, rhs: Rhs) -> Self::Output {
Ratio::new(self.num, self.den * rhs)
}
}
macro_rules! impl_ratio_operator {
($trait:ident, $method:ident) => {
impl<LhsNum,LhsDen,Rhs,Intermediate> core::ops::$trait<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::$trait<Intermediate>,
LhsDen:Copy,
Rhs:core::ops::Mul<LhsDen,Output=Intermediate>,
{
type Output=Ratio<<LhsNum as core::ops::$trait<Intermediate>>::Output,LhsDen>;
#[inline]
fn $method(self,rhs:Rhs)->Self::Output{
Ratio::new(self.num.$method(rhs*self.den),self.den)
}
}
};
($trait:ident, $method:ident) => {
impl<LhsNum, LhsDen, Rhs, Intermediate> core::ops::$trait<Rhs> for Ratio<LhsNum, LhsDen>
where
LhsNum: core::ops::$trait<Intermediate>,
LhsDen: Copy,
Rhs: core::ops::Mul<LhsDen, Output = Intermediate>,
{
type Output = Ratio<<LhsNum as core::ops::$trait<Intermediate>>::Output, LhsDen>;
#[inline]
fn $method(self, rhs: Rhs) -> Self::Output {
Ratio::new(self.num.$method(rhs * self.den), self.den)
}
}
};
}
impl_ratio_operator!(Add,add);
impl_ratio_operator!(Sub,sub);
impl_ratio_operator!(Rem,rem);
impl_ratio_operator!(Add, add);
impl_ratio_operator!(Sub, sub);
impl_ratio_operator!(Rem, rem);
//assign operators
impl<LhsNum,LhsDen,Rhs> core::ops::MulAssign<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::MulAssign<Rhs>,
impl<LhsNum, LhsDen, Rhs> core::ops::MulAssign<Rhs> for Ratio<LhsNum, LhsDen>
where
LhsNum: core::ops::MulAssign<Rhs>,
{
#[inline]
fn mul_assign(&mut self,rhs:Rhs){
self.num*=rhs;
}
#[inline]
fn mul_assign(&mut self, rhs: Rhs) {
self.num *= rhs;
}
}
impl<LhsNum,LhsDen,Rhs> core::ops::DivAssign<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsDen:core::ops::MulAssign<Rhs>,
impl<LhsNum, LhsDen, Rhs> core::ops::DivAssign<Rhs> for Ratio<LhsNum, LhsDen>
where
LhsDen: core::ops::MulAssign<Rhs>,
{
#[inline]
fn div_assign(&mut self,rhs:Rhs){
self.den*=rhs;
}
#[inline]
fn div_assign(&mut self, rhs: Rhs) {
self.den *= rhs;
}
}
macro_rules! impl_ratio_assign_operator {
($trait:ident, $method:ident) => {
impl<LhsNum,LhsDen,Rhs> core::ops::$trait<Rhs> for Ratio<LhsNum,LhsDen>
where
LhsNum:core::ops::$trait,
LhsDen:Copy,
Rhs:core::ops::Mul<LhsDen,Output=LhsNum>,
{
#[inline]
fn $method(&mut self,rhs:Rhs){
self.num.$method(rhs*self.den)
}
}
};
($trait:ident, $method:ident) => {
impl<LhsNum, LhsDen, Rhs> core::ops::$trait<Rhs> for Ratio<LhsNum, LhsDen>
where
LhsNum: core::ops::$trait,
LhsDen: Copy,
Rhs: core::ops::Mul<LhsDen, Output = LhsNum>,
{
#[inline]
fn $method(&mut self, rhs: Rhs) {
self.num.$method(rhs * self.den)
}
}
};
}
impl_ratio_assign_operator!(AddAssign,add_assign);
impl_ratio_assign_operator!(SubAssign,sub_assign);
impl_ratio_assign_operator!(RemAssign,rem_assign);
impl_ratio_assign_operator!(AddAssign, add_assign);
impl_ratio_assign_operator!(SubAssign, sub_assign);
impl_ratio_assign_operator!(RemAssign, rem_assign);
// Only implement PartialEq<Self>
// Rust's operators aren't actually that good
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
where
LhsNum:Copy,
LhsDen:Copy,
RhsNum:Copy,
RhsDen:Copy,
LhsNum:core::ops::Mul<RhsDen,Output=T>,
RhsNum:core::ops::Mul<LhsDen,Output=U>,
T:PartialEq<U>,
impl<LhsNum, LhsDen, RhsNum, RhsDen, T, U> PartialEq<Ratio<RhsNum, RhsDen>>
for Ratio<LhsNum, LhsDen>
where
LhsNum: Copy,
LhsDen: Copy,
RhsNum: Copy,
RhsDen: Copy,
LhsNum: core::ops::Mul<RhsDen, Output = T>,
RhsNum: core::ops::Mul<LhsDen, Output = U>,
T: PartialEq<U>,
{
#[inline]
fn eq(&self,other:&Ratio<RhsNum,RhsDen>)->bool{
(self.num*other.den).eq(&(other.num*self.den))
}
#[inline]
fn eq(&self, other: &Ratio<RhsNum, RhsDen>) -> bool {
(self.num * other.den).eq(&(other.num * self.den))
}
}
impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}
impl<Num, Den> Eq for Ratio<Num, Den> where Self: PartialEq {}
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
where
LhsNum:Copy,
LhsDen:Copy,
RhsNum:Copy,
RhsDen:Copy,
LhsNum:core::ops::Mul<RhsDen,Output=T>,
RhsNum:core::ops::Mul<LhsDen,Output=U>,
T:PartialOrd<U>,
impl<LhsNum, LhsDen, RhsNum, RhsDen, T, U> PartialOrd<Ratio<RhsNum, RhsDen>>
for Ratio<LhsNum, LhsDen>
where
LhsNum: Copy,
LhsDen: Copy,
RhsNum: Copy,
RhsDen: Copy,
LhsNum: core::ops::Mul<RhsDen, Output = T>,
RhsNum: core::ops::Mul<LhsDen, Output = U>,
T: PartialOrd<U>,
{
#[inline]
fn partial_cmp(&self,other:&Ratio<RhsNum,RhsDen>)->Option<core::cmp::Ordering>{
(self.num*other.den).partial_cmp(&(other.num*self.den))
}
#[inline]
fn partial_cmp(&self, other: &Ratio<RhsNum, RhsDen>) -> Option<core::cmp::Ordering> {
(self.num * other.den).partial_cmp(&(other.num * self.den))
}
}
impl<Num,Den,T> Ord for Ratio<Num,Den>
where
Num:Copy,
Den:Copy,
Num:core::ops::Mul<Den,Output=T>,
T:Ord,
impl<Num, Den, T> Ord for Ratio<Num, Den>
where
Num: Copy,
Den: Copy,
Num: core::ops::Mul<Den, Output = T>,
T: Ord,
{
#[inline]
fn cmp(&self,other:&Self)->std::cmp::Ordering{
(self.num*other.den).cmp(&(other.num*self.den))
}
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.num * other.den).cmp(&(other.num * self.den))
}
}

View File

@ -1,55 +1,55 @@
use crate::ratio::Ratio;
macro_rules! test_op{
($ratio_op:ident,$op:ident,$a:expr,$b:expr,$c:expr,$d:expr)=>{
assert_eq!(
Ratio::new($a,$b).$ratio_op(Ratio::new($c,$d)),
(($a as f32)/($b as f32)).$op(&(($c as f32)/($d as f32)))
);
};
macro_rules! test_op {
($ratio_op:ident,$op:ident,$a:expr,$b:expr,$c:expr,$d:expr) => {
assert_eq!(
Ratio::new($a, $b).$ratio_op(Ratio::new($c, $d)),
(($a as f32) / ($b as f32)).$op(&(($c as f32) / ($d as f32)))
);
};
}
macro_rules! test_many_ops{
($ratio_op:ident,$op:ident)=>{
test_op!($ratio_op,$op,1,2,3,4);
test_op!($ratio_op,$op,1,2,-3,4);
test_op!($ratio_op,$op,-1,2,-3,4);
test_op!($ratio_op,$op,-1,-2,-3,4);
test_op!($ratio_op,$op,2,1,6,3);
test_op!($ratio_op,$op,-2,1,6,3);
test_op!($ratio_op,$op,2,-1,-6,3);
test_op!($ratio_op,$op,2,1,6,-3);
};
macro_rules! test_many_ops {
($ratio_op:ident,$op:ident) => {
test_op!($ratio_op, $op, 1, 2, 3, 4);
test_op!($ratio_op, $op, 1, 2, -3, 4);
test_op!($ratio_op, $op, -1, 2, -3, 4);
test_op!($ratio_op, $op, -1, -2, -3, 4);
test_op!($ratio_op, $op, 2, 1, 6, 3);
test_op!($ratio_op, $op, -2, 1, 6, 3);
test_op!($ratio_op, $op, 2, -1, -6, 3);
test_op!($ratio_op, $op, 2, 1, 6, -3);
};
}
#[test]
fn test_lt(){
test_many_ops!(lt_ratio,lt);
fn test_lt() {
test_many_ops!(lt_ratio, lt);
}
#[test]
fn test_gt(){
test_many_ops!(gt_ratio,gt);
fn test_gt() {
test_many_ops!(gt_ratio, gt);
}
#[test]
fn test_le(){
test_many_ops!(le_ratio,le);
fn test_le() {
test_many_ops!(le_ratio, le);
}
#[test]
fn test_ge(){
test_many_ops!(ge_ratio,ge);
fn test_ge() {
test_many_ops!(ge_ratio, ge);
}
#[test]
fn test_eq(){
test_many_ops!(eq_ratio,eq);
fn test_eq() {
test_many_ops!(eq_ratio, eq);
}
#[test]
fn test_partial_cmp(){
test_many_ops!(partial_cmp_ratio,partial_cmp);
fn test_partial_cmp() {
test_many_ops!(partial_cmp_ratio, partial_cmp);
}
// #[test]

View File

@ -1,107 +1,113 @@
use std::io::Read;
use rbx_dom_weak::WeakDom;
use std::io::Read;
mod rbx;
mod mesh;
mod primitives;
mod rbx;
pub mod data{
pub struct RobloxMeshBytes(Vec<u8>);
impl RobloxMeshBytes{
pub fn new(bytes:Vec<u8>)->Self{
Self(bytes)
}
pub(crate) fn cursor(self)->std::io::Cursor<Vec<u8>>{
std::io::Cursor::new(self.0)
}
}
pub mod data {
pub struct RobloxMeshBytes(Vec<u8>);
impl RobloxMeshBytes {
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub(crate) fn cursor(self) -> std::io::Cursor<Vec<u8>> {
std::io::Cursor::new(self.0)
}
}
}
pub struct Model{
dom:WeakDom,
pub struct Model {
dom: WeakDom,
}
impl Model{
fn new(dom:WeakDom)->Self{
Self{dom}
}
pub fn into_place(self)->Place{
let Self{mut dom}=self;
let context=roblox_emulator::context::Context::from_mut(&mut dom);
let services=context.convert_into_place();
Place{dom,services}
}
impl Model {
fn new(dom: WeakDom) -> Self {
Self { dom }
}
pub fn into_place(self) -> Place {
let Self { mut dom } = self;
let context = roblox_emulator::context::Context::from_mut(&mut dom);
let services = context.convert_into_place();
Place { dom, services }
}
}
impl AsRef<WeakDom> for Model{
fn as_ref(&self)->&WeakDom{
&self.dom
}
impl AsRef<WeakDom> for Model {
fn as_ref(&self) -> &WeakDom {
&self.dom
}
}
pub struct Place{
dom:WeakDom,
services:roblox_emulator::context::Services,
pub struct Place {
dom: WeakDom,
services: roblox_emulator::context::Services,
}
impl Place{
fn new(dom:WeakDom)->Option<Self>{
let context=roblox_emulator::context::Context::from_ref(&dom);
Some(Self{
services:context.find_services()?,
dom,
})
}
pub fn run_scripts(&mut self){
let Place{dom,services}=self;
let runner=roblox_emulator::runner::Runner::new().unwrap();
let context=roblox_emulator::context::Context::from_mut(dom);
let scripts=context.scripts();
let runnable=runner.runnable_context_with_services(context,services).unwrap();
for script in scripts{
if let Err(e)=runnable.run_script(script){
println!("runner error: {e}");
}
}
}
impl Place {
fn new(dom: WeakDom) -> Option<Self> {
let context = roblox_emulator::context::Context::from_ref(&dom);
Some(Self {
services: context.find_services()?,
dom,
})
}
pub fn run_scripts(&mut self) {
let Place { dom, services } = self;
let runner = roblox_emulator::runner::Runner::new().unwrap();
let context = roblox_emulator::context::Context::from_mut(dom);
let scripts = context.scripts();
let runnable = runner
.runnable_context_with_services(context, services)
.unwrap();
for script in scripts {
if let Err(e) = runnable.run_script(script) {
println!("runner error: {e}");
}
}
}
}
impl AsRef<WeakDom> for Place{
fn as_ref(&self)->&WeakDom{
&self.dom
}
impl AsRef<WeakDom> for Place {
fn as_ref(&self) -> &WeakDom {
&self.dom
}
}
#[derive(Debug)]
pub enum ReadError{
RbxBinary(rbx_binary::DecodeError),
RbxXml(rbx_xml::DecodeError),
Io(std::io::Error),
UnknownFileFormat,
pub enum ReadError {
RbxBinary(rbx_binary::DecodeError),
RbxXml(rbx_xml::DecodeError),
Io(std::io::Error),
UnknownFileFormat,
}
impl std::fmt::Display for ReadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for ReadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ReadError{}
impl std::error::Error for ReadError {}
pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..8]{
b"<roblox!"=>rbx_binary::from_reader(buf).map(Model::new).map_err(ReadError::RbxBinary),
b"<roblox "=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml),
_=>Err(ReadError::UnknownFileFormat),
}
pub fn read<R: Read>(input: R) -> Result<Model, ReadError> {
let mut buf = std::io::BufReader::new(input);
let peek = std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..8] {
b"<roblox!" => rbx_binary::from_reader(buf)
.map(Model::new)
.map_err(ReadError::RbxBinary),
b"<roblox " => rbx_xml::from_reader_default(buf)
.map(Model::new)
.map_err(ReadError::RbxXml),
_ => Err(ReadError::UnknownFileFormat),
}
}
//ConvertError
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
dom:impl AsRef<WeakDom>,
acquire_render_config_id:AcquireRenderConfigId,
acquire_mesh_id:AcquireMeshId
)->rbx::PartialMap1
pub fn convert<AcquireRenderConfigId, AcquireMeshId>(
dom: impl AsRef<WeakDom>,
acquire_render_config_id: AcquireRenderConfigId,
acquire_mesh_id: AcquireMeshId,
) -> rbx::PartialMap1
where
AcquireRenderConfigId:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId,
AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId,
AcquireRenderConfigId: FnMut(Option<&str>) -> strafesnet_common::model::RenderConfigId,
AcquireMeshId: FnMut(&str) -> strafesnet_common::model::MeshId,
{
rbx::convert(&dom.as_ref(),acquire_render_config_id,acquire_mesh_id)
rbx::convert(&dom.as_ref(), acquire_render_config_id, acquire_mesh_id)
}

View File

@ -1,210 +1,284 @@
use std::collections::HashMap;
use rbx_mesh::mesh::{Vertex2, Vertex2Truncated};
use strafesnet_common::{integer::vec3,model::{self, ColorId, IndexedVertex, NormalId, PolygonGroup, PolygonList, PositionId, TextureCoordinateId, VertexId}};
use strafesnet_common::{
integer::vec3,
model::{
self, ColorId, IndexedVertex, NormalId, PolygonGroup, PolygonList, PositionId,
TextureCoordinateId, VertexId,
},
};
#[derive(Debug)]
pub enum Error{
Planar64Vec3(strafesnet_common::integer::Planar64TryFromFloatError),
RbxMesh(rbx_mesh::mesh::Error)
pub enum Error {
Planar64Vec3(strafesnet_common::integer::Planar64TryFromFloatError),
RbxMesh(rbx_mesh::mesh::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
fn ingest_vertices2<
AcquirePosId,
AcquireTexId,
AcquireNormalId,
AcquireColorId,
AcquireVertexId,
>(
vertices:Vec<Vertex2>,
acquire_pos_id:&mut AcquirePosId,
acquire_tex_id:&mut AcquireTexId,
acquire_normal_id:&mut AcquireNormalId,
acquire_color_id:&mut AcquireColorId,
acquire_vertex_id:&mut AcquireVertexId,
)->Result<HashMap<rbx_mesh::mesh::VertexId2,VertexId>,Error>
fn ingest_vertices2<AcquirePosId, AcquireTexId, AcquireNormalId, AcquireColorId, AcquireVertexId>(
vertices: Vec<Vertex2>,
acquire_pos_id: &mut AcquirePosId,
acquire_tex_id: &mut AcquireTexId,
acquire_normal_id: &mut AcquireNormalId,
acquire_color_id: &mut AcquireColorId,
acquire_vertex_id: &mut AcquireVertexId,
) -> Result<HashMap<rbx_mesh::mesh::VertexId2, VertexId>, Error>
where
AcquirePosId:FnMut([f32;3])->Result<PositionId,Error>,
AcquireTexId:FnMut([f32;2])->TextureCoordinateId,
AcquireNormalId:FnMut([f32;3])->Result<NormalId,Error>,
AcquireColorId:FnMut([f32;4])->ColorId,
AcquireVertexId:FnMut(IndexedVertex)->VertexId,
AcquirePosId: FnMut([f32; 3]) -> Result<PositionId, Error>,
AcquireTexId: FnMut([f32; 2]) -> TextureCoordinateId,
AcquireNormalId: FnMut([f32; 3]) -> Result<NormalId, Error>,
AcquireColorId: FnMut([f32; 4]) -> ColorId,
AcquireVertexId: FnMut(IndexedVertex) -> VertexId,
{
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
//while also doing the inserting unique entries into lists simultaneously
Ok(vertices.into_iter().enumerate().map(|(vertex_id,vertex)|Ok((
rbx_mesh::mesh::VertexId2(vertex_id as u32),
acquire_vertex_id(IndexedVertex{
pos:acquire_pos_id(vertex.pos)?,
tex:acquire_tex_id(vertex.tex),
normal:acquire_normal_id(vertex.norm)?,
color:acquire_color_id(vertex.color.map(|f|f as f32/255.0f32))
}),
))).collect::<Result<_,_>>()?)
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
//while also doing the inserting unique entries into lists simultaneously
Ok(vertices
.into_iter()
.enumerate()
.map(|(vertex_id, vertex)| {
Ok((
rbx_mesh::mesh::VertexId2(vertex_id as u32),
acquire_vertex_id(IndexedVertex {
pos: acquire_pos_id(vertex.pos)?,
tex: acquire_tex_id(vertex.tex),
normal: acquire_normal_id(vertex.norm)?,
color: acquire_color_id(vertex.color.map(|f| f as f32 / 255.0f32)),
}),
))
})
.collect::<Result<_, _>>()?)
}
fn ingest_vertices_truncated2<
AcquirePosId,
AcquireTexId,
AcquireNormalId,
AcquireVertexId,
>(
vertices:Vec<Vertex2Truncated>,
acquire_pos_id:&mut AcquirePosId,
acquire_tex_id:&mut AcquireTexId,
acquire_normal_id:&mut AcquireNormalId,
static_color_id:ColorId,//pick one color and fill everything with it
acquire_vertex_id:&mut AcquireVertexId,
)->Result<HashMap<rbx_mesh::mesh::VertexId2,VertexId>,Error>
fn ingest_vertices_truncated2<AcquirePosId, AcquireTexId, AcquireNormalId, AcquireVertexId>(
vertices: Vec<Vertex2Truncated>,
acquire_pos_id: &mut AcquirePosId,
acquire_tex_id: &mut AcquireTexId,
acquire_normal_id: &mut AcquireNormalId,
static_color_id: ColorId, //pick one color and fill everything with it
acquire_vertex_id: &mut AcquireVertexId,
) -> Result<HashMap<rbx_mesh::mesh::VertexId2, VertexId>, Error>
where
AcquirePosId:FnMut([f32;3])->Result<PositionId,Error>,
AcquireTexId:FnMut([f32;2])->TextureCoordinateId,
AcquireNormalId:FnMut([f32;3])->Result<NormalId,Error>,
AcquireVertexId:FnMut(IndexedVertex)->VertexId,
AcquirePosId: FnMut([f32; 3]) -> Result<PositionId, Error>,
AcquireTexId: FnMut([f32; 2]) -> TextureCoordinateId,
AcquireNormalId: FnMut([f32; 3]) -> Result<NormalId, Error>,
AcquireVertexId: FnMut(IndexedVertex) -> VertexId,
{
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
//while also doing the inserting unique entries into lists simultaneously
Ok(vertices.into_iter().enumerate().map(|(vertex_id,vertex)|Ok((
rbx_mesh::mesh::VertexId2(vertex_id as u32),
acquire_vertex_id(IndexedVertex{
pos:acquire_pos_id(vertex.pos)?,
tex:acquire_tex_id(vertex.tex),
normal:acquire_normal_id(vertex.norm)?,
color:static_color_id
}),
))).collect::<Result<_,_>>()?)
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
//while also doing the inserting unique entries into lists simultaneously
Ok(vertices
.into_iter()
.enumerate()
.map(|(vertex_id, vertex)| {
Ok((
rbx_mesh::mesh::VertexId2(vertex_id as u32),
acquire_vertex_id(IndexedVertex {
pos: acquire_pos_id(vertex.pos)?,
tex: acquire_tex_id(vertex.tex),
normal: acquire_normal_id(vertex.norm)?,
color: static_color_id,
}),
))
})
.collect::<Result<_, _>>()?)
}
fn ingest_faces2_lods3(
polygon_groups:&mut Vec<PolygonGroup>,
vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>,
faces:&Vec<rbx_mesh::mesh::Face2>,
lods:&Vec<rbx_mesh::mesh::Lod3>
){
//faces have to be split into polygon groups based on lod
polygon_groups.extend(lods.windows(2).map(|lod_pair|
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|face|
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
).collect()))
))
polygon_groups: &mut Vec<PolygonGroup>,
vertex_id_map: &HashMap<rbx_mesh::mesh::VertexId2, VertexId>,
faces: &Vec<rbx_mesh::mesh::Face2>,
lods: &Vec<rbx_mesh::mesh::Lod3>,
) {
//faces have to be split into polygon groups based on lod
polygon_groups.extend(lods.windows(2).map(|lod_pair| {
PolygonGroup::PolygonList(PolygonList::new(
faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize]
.iter()
.map(|face| {
vec![
vertex_id_map[&face.0],
vertex_id_map[&face.1],
vertex_id_map[&face.2],
]
})
.collect(),
))
}))
}
pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<model::Mesh,Error>{
//generate that mesh boi
let mut unique_pos=Vec::new();
let mut pos_id_from=HashMap::new();
let mut unique_tex=Vec::new();
let mut tex_id_from=HashMap::new();
let mut unique_normal=Vec::new();
let mut normal_id_from=HashMap::new();
let mut unique_color=Vec::new();
let mut color_id_from=HashMap::new();
let mut unique_vertices=Vec::new();
let mut vertex_id_from=HashMap::new();
let mut polygon_groups=Vec::new();
let mut acquire_pos_id=|pos|{
let p=vec3::try_from_f32_array(pos).map_err(Error::Planar64Vec3)?;
Ok(PositionId::new(*pos_id_from.entry(p).or_insert_with(||{
let pos_id=unique_pos.len();
unique_pos.push(p);
pos_id
}) as u32))
};
let mut acquire_tex_id=|tex|{
let h=bytemuck::cast::<[f32;2],[u32;2]>(tex);
TextureCoordinateId::new(*tex_id_from.entry(h).or_insert_with(||{
let tex_id=unique_tex.len();
unique_tex.push(glam::Vec2::from_array(tex));
tex_id
}) as u32)
};
let mut acquire_normal_id=|normal|{
let n=vec3::try_from_f32_array(normal).map_err(Error::Planar64Vec3)?;
Ok(NormalId::new(*normal_id_from.entry(n).or_insert_with(||{
let normal_id=unique_normal.len();
unique_normal.push(n);
normal_id
}) as u32))
};
let mut acquire_color_id=|color|{
let h=bytemuck::cast::<[f32;4],[u32;4]>(color);
ColorId::new(*color_id_from.entry(h).or_insert_with(||{
let color_id=unique_color.len();
unique_color.push(glam::Vec4::from_array(color));
color_id
}) as u32)
};
let mut acquire_vertex_id=|vertex:IndexedVertex|{
VertexId::new(*vertex_id_from.entry(vertex.clone()).or_insert_with(||{
let vertex_id=unique_vertices.len();
unique_vertices.push(vertex);
vertex_id
}) as u32)
};
match rbx_mesh::read_versioned(roblox_mesh_bytes.cursor()).map_err(Error::RbxMesh)?{
rbx_mesh::mesh::VersionedMesh::Version1(mesh)=>{
let color_id=acquire_color_id([1.0f32;4]);
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.vertices.chunks_exact(3).map(|trip|{
let mut ingest_vertex1=|vertex:&rbx_mesh::mesh::Vertex1|Ok(acquire_vertex_id(IndexedVertex{
pos:acquire_pos_id(vertex.pos)?,
tex:acquire_tex_id([vertex.tex[0],vertex.tex[1]]),
normal:acquire_normal_id(vertex.norm)?,
color:color_id,
}));
Ok(vec![ingest_vertex1(&trip[0])?,ingest_vertex1(&trip[1])?,ingest_vertex1(&trip[2])?])
}).collect::<Result<_,_>>()?)));
},
rbx_mesh::mesh::VersionedMesh::Version2(mesh)=>{
let vertex_id_map=match mesh.header.sizeof_vertex{
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
//pick white and make all the vertices white
let color_id=acquire_color_id([1.0f32;4]);
ingest_vertices_truncated2(mesh.vertices_truncated,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,color_id,&mut acquire_vertex_id)
},
rbx_mesh::mesh::SizeOfVertex2::Full=>ingest_vertices2(mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id),
}?;
//one big happy group for all the faces
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.faces.into_iter().map(|face|
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
).collect())));
},
rbx_mesh::mesh::VersionedMesh::Version3(mesh)=>{
let vertex_id_map=match mesh.header.sizeof_vertex{
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
let color_id=acquire_color_id([1.0f32;4]);
ingest_vertices_truncated2(mesh.vertices_truncated,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,color_id,&mut acquire_vertex_id)
},
rbx_mesh::mesh::SizeOfVertex2::Full=>ingest_vertices2(mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id),
}?;
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
},
rbx_mesh::mesh::VersionedMesh::Version4(mesh)=>{
let vertex_id_map=ingest_vertices2(
mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id
)?;
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
},
rbx_mesh::mesh::VersionedMesh::Version5(mesh)=>{
let vertex_id_map=ingest_vertices2(
mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id
)?;
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
},
}
Ok(model::Mesh{
unique_pos,
unique_normal,
unique_tex,
unique_color,
unique_vertices,
polygon_groups,
//these should probably be moved to the model...
graphics_groups:Vec::new(),
physics_groups:Vec::new(),
})
pub fn convert(roblox_mesh_bytes: crate::data::RobloxMeshBytes) -> Result<model::Mesh, Error> {
//generate that mesh boi
let mut unique_pos = Vec::new();
let mut pos_id_from = HashMap::new();
let mut unique_tex = Vec::new();
let mut tex_id_from = HashMap::new();
let mut unique_normal = Vec::new();
let mut normal_id_from = HashMap::new();
let mut unique_color = Vec::new();
let mut color_id_from = HashMap::new();
let mut unique_vertices = Vec::new();
let mut vertex_id_from = HashMap::new();
let mut polygon_groups = Vec::new();
let mut acquire_pos_id = |pos| {
let p = vec3::try_from_f32_array(pos).map_err(Error::Planar64Vec3)?;
Ok(PositionId::new(*pos_id_from.entry(p).or_insert_with(|| {
let pos_id = unique_pos.len();
unique_pos.push(p);
pos_id
}) as u32))
};
let mut acquire_tex_id = |tex| {
let h = bytemuck::cast::<[f32; 2], [u32; 2]>(tex);
TextureCoordinateId::new(*tex_id_from.entry(h).or_insert_with(|| {
let tex_id = unique_tex.len();
unique_tex.push(glam::Vec2::from_array(tex));
tex_id
}) as u32)
};
let mut acquire_normal_id = |normal| {
let n = vec3::try_from_f32_array(normal).map_err(Error::Planar64Vec3)?;
Ok(NormalId::new(*normal_id_from.entry(n).or_insert_with(|| {
let normal_id = unique_normal.len();
unique_normal.push(n);
normal_id
}) as u32))
};
let mut acquire_color_id = |color| {
let h = bytemuck::cast::<[f32; 4], [u32; 4]>(color);
ColorId::new(*color_id_from.entry(h).or_insert_with(|| {
let color_id = unique_color.len();
unique_color.push(glam::Vec4::from_array(color));
color_id
}) as u32)
};
let mut acquire_vertex_id = |vertex: IndexedVertex| {
VertexId::new(*vertex_id_from.entry(vertex.clone()).or_insert_with(|| {
let vertex_id = unique_vertices.len();
unique_vertices.push(vertex);
vertex_id
}) as u32)
};
match rbx_mesh::read_versioned(roblox_mesh_bytes.cursor()).map_err(Error::RbxMesh)? {
rbx_mesh::mesh::VersionedMesh::Version1(mesh) => {
let color_id = acquire_color_id([1.0f32; 4]);
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(
mesh.vertices
.chunks_exact(3)
.map(|trip| {
let mut ingest_vertex1 = |vertex: &rbx_mesh::mesh::Vertex1| {
Ok(acquire_vertex_id(IndexedVertex {
pos: acquire_pos_id(vertex.pos)?,
tex: acquire_tex_id([vertex.tex[0], vertex.tex[1]]),
normal: acquire_normal_id(vertex.norm)?,
color: color_id,
}))
};
Ok(vec![
ingest_vertex1(&trip[0])?,
ingest_vertex1(&trip[1])?,
ingest_vertex1(&trip[2])?,
])
})
.collect::<Result<_, _>>()?,
)));
}
rbx_mesh::mesh::VersionedMesh::Version2(mesh) => {
let vertex_id_map = match mesh.header.sizeof_vertex {
rbx_mesh::mesh::SizeOfVertex2::Truncated => {
//pick white and make all the vertices white
let color_id = acquire_color_id([1.0f32; 4]);
ingest_vertices_truncated2(
mesh.vertices_truncated,
&mut acquire_pos_id,
&mut acquire_tex_id,
&mut acquire_normal_id,
color_id,
&mut acquire_vertex_id,
)
}
rbx_mesh::mesh::SizeOfVertex2::Full => ingest_vertices2(
mesh.vertices,
&mut acquire_pos_id,
&mut acquire_tex_id,
&mut acquire_normal_id,
&mut acquire_color_id,
&mut acquire_vertex_id,
),
}?;
//one big happy group for all the faces
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(
mesh.faces
.into_iter()
.map(|face| {
vec![
vertex_id_map[&face.0],
vertex_id_map[&face.1],
vertex_id_map[&face.2],
]
})
.collect(),
)));
}
rbx_mesh::mesh::VersionedMesh::Version3(mesh) => {
let vertex_id_map = match mesh.header.sizeof_vertex {
rbx_mesh::mesh::SizeOfVertex2::Truncated => {
let color_id = acquire_color_id([1.0f32; 4]);
ingest_vertices_truncated2(
mesh.vertices_truncated,
&mut acquire_pos_id,
&mut acquire_tex_id,
&mut acquire_normal_id,
color_id,
&mut acquire_vertex_id,
)
}
rbx_mesh::mesh::SizeOfVertex2::Full => ingest_vertices2(
mesh.vertices,
&mut acquire_pos_id,
&mut acquire_tex_id,
&mut acquire_normal_id,
&mut acquire_color_id,
&mut acquire_vertex_id,
),
}?;
ingest_faces2_lods3(&mut polygon_groups, &vertex_id_map, &mesh.faces, &mesh.lods);
}
rbx_mesh::mesh::VersionedMesh::Version4(mesh) => {
let vertex_id_map = ingest_vertices2(
mesh.vertices,
&mut acquire_pos_id,
&mut acquire_tex_id,
&mut acquire_normal_id,
&mut acquire_color_id,
&mut acquire_vertex_id,
)?;
ingest_faces2_lods3(&mut polygon_groups, &vertex_id_map, &mesh.faces, &mesh.lods);
}
rbx_mesh::mesh::VersionedMesh::Version5(mesh) => {
let vertex_id_map = ingest_vertices2(
mesh.vertices,
&mut acquire_pos_id,
&mut acquire_tex_id,
&mut acquire_normal_id,
&mut acquire_color_id,
&mut acquire_vertex_id,
)?;
ingest_faces2_lods3(&mut polygon_groups, &vertex_id_map, &mesh.faces, &mesh.lods);
}
}
Ok(model::Mesh {
unique_pos,
unique_normal,
unique_tex,
unique_color,
unique_vertices,
polygon_groups,
//these should probably be moved to the model...
graphics_groups: Vec::new(),
physics_groups: Vec::new(),
})
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,93 +1,106 @@
use rbx_dom_weak::{types::Ref,InstanceBuilder,WeakDom};
use rbx_dom_weak::{types::Ref, InstanceBuilder, WeakDom};
pub fn class_is_a(class:&str,superclass:&str)->bool{
class==superclass
||rbx_reflection_database::get().classes.get(class)
.is_some_and(|descriptor|
descriptor.superclass.as_ref().is_some_and(|class_super|
class_is_a(class_super,superclass)
)
)
pub fn class_is_a(class: &str, superclass: &str) -> bool {
class == superclass
|| rbx_reflection_database::get()
.classes
.get(class)
.is_some_and(|descriptor| {
descriptor
.superclass
.as_ref()
.is_some_and(|class_super| class_is_a(class_super, superclass))
})
}
#[repr(transparent)]
pub struct Context{
pub(crate)dom:WeakDom,
pub struct Context {
pub(crate) dom: WeakDom,
}
impl Context{
pub const fn new(dom:WeakDom)->Self{
Self{dom}
}
pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance,Services){
let script=InstanceBuilder::new("Script")
.with_property("Source",rbx_types::Variant::String(source));
let script_ref=script.referent();
let mut context=Self::new(WeakDom::new(
InstanceBuilder::new("DataModel")
.with_child(script)
));
let services=context.convert_into_place();
(context,crate::runner::instance::Instance::new(script_ref),services)
}
pub fn from_ref(dom:&WeakDom)->&Context{
unsafe{&*(dom as *const WeakDom as *const Context)}
}
pub fn from_mut(dom:&mut WeakDom)->&mut Context{
unsafe{&mut *(dom as *mut WeakDom as *mut Context)}
}
/// Creates an iterator over all items of a particular class.
pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
self.dom.descendants().filter(|&instance|
class_is_a(instance.class.as_ref(),superclass)
).map(|instance|instance.referent())
}
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect()
}
impl Context {
pub const fn new(dom: WeakDom) -> Self {
Self { dom }
}
pub fn script_singleton(
source: String,
) -> (Context, crate::runner::instance::Instance, Services) {
let script = InstanceBuilder::new("Script")
.with_property("Source", rbx_types::Variant::String(source));
let script_ref = script.referent();
let mut context = Self::new(WeakDom::new(
InstanceBuilder::new("DataModel").with_child(script),
));
let services = context.convert_into_place();
(
context,
crate::runner::instance::Instance::new(script_ref),
services,
)
}
pub fn from_ref(dom: &WeakDom) -> &Context {
unsafe { &*(dom as *const WeakDom as *const Context) }
}
pub fn from_mut(dom: &mut WeakDom) -> &mut Context {
unsafe { &mut *(dom as *mut WeakDom as *mut Context) }
}
/// Creates an iterator over all items of a particular class.
pub fn superclass_iter<'a>(&'a self, superclass: &'a str) -> impl Iterator<Item = Ref> + 'a {
self.dom
.descendants()
.filter(|&instance| class_is_a(instance.class.as_ref(), superclass))
.map(|instance| instance.referent())
}
pub fn scripts(&self) -> Vec<crate::runner::instance::Instance> {
self.superclass_iter("LuaSourceContainer")
.map(crate::runner::instance::Instance::new)
.collect()
}
pub fn find_services(&self)->Option<Services>{
Some(Services{
workspace:*self.dom.root().children().iter().find(|&&r|
self.dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace")
)?,
game:self.dom.root_ref(),
})
}
pub fn convert_into_place(&mut self)->Services{
//snapshot root instances
let children=self.dom.root().children().to_owned();
pub fn find_services(&self) -> Option<Services> {
Some(Services {
workspace: *self.dom.root().children().iter().find(|&&r| {
self.dom
.get_by_ref(r)
.is_some_and(|instance| instance.class == "Workspace")
})?,
game: self.dom.root_ref(),
})
}
pub fn convert_into_place(&mut self) -> Services {
//snapshot root instances
let children = self.dom.root().children().to_owned();
//insert services
let game=self.dom.root_ref();
let terrain_bldr=InstanceBuilder::new("Terrain");
let workspace=self.dom.insert(game,
InstanceBuilder::new("Workspace")
//Set Workspace.Terrain property equal to Terrain
.with_property("Terrain",terrain_bldr.referent())
.with_child(terrain_bldr)
);
{
//Lowercase and upper case workspace property!
let game=self.dom.root_mut();
game.properties.insert("workspace".to_owned(),rbx_types::Variant::Ref(workspace));
game.properties.insert("Workspace".to_owned(),rbx_types::Variant::Ref(workspace));
}
self.dom.insert(game,InstanceBuilder::new("Lighting"));
//insert services
let game = self.dom.root_ref();
let terrain_bldr = InstanceBuilder::new("Terrain");
let workspace = self.dom.insert(
game,
InstanceBuilder::new("Workspace")
//Set Workspace.Terrain property equal to Terrain
.with_property("Terrain", terrain_bldr.referent())
.with_child(terrain_bldr),
);
{
//Lowercase and upper case workspace property!
let game = self.dom.root_mut();
game.properties
.insert("workspace".to_owned(), rbx_types::Variant::Ref(workspace));
game.properties
.insert("Workspace".to_owned(), rbx_types::Variant::Ref(workspace));
}
self.dom.insert(game, InstanceBuilder::new("Lighting"));
//transfer original root instances into workspace
for instance in children{
self.dom.transfer_within(instance,workspace);
}
//transfer original root instances into workspace
for instance in children {
self.dom.transfer_within(instance, workspace);
}
Services{
game,
workspace,
}
}
Services { game, workspace }
}
}
pub struct Services{
pub game:Ref,
pub workspace:Ref,
pub struct Services {
pub game: Ref,
pub workspace: Ref,
}

View File

@ -1,6 +1,6 @@
pub mod runner;
pub mod context;
#[cfg(feature="run-service")]
pub mod runner;
#[cfg(feature = "run-service")]
pub(crate) mod scheduler;
#[cfg(test)]

View File

@ -1,174 +1,233 @@
use super::vector3::Vector3;
#[derive(Clone,Copy)]
pub struct CFrame(pub(crate)glam::Affine3A);
#[derive(Clone, Copy)]
pub struct CFrame(pub(crate) glam::Affine3A);
impl CFrame{
pub fn new(
x:f32,y:f32,z:f32,
xx:f32,yx:f32,zx:f32,
xy:f32,yy:f32,zy:f32,
xz:f32,yz:f32,zz:f32,
)->Self{
Self(glam::Affine3A::from_mat3_translation(
glam::mat3(
glam::vec3(xx,yx,zx),
glam::vec3(xy,yy,zy),
glam::vec3(xz,yz,zz)
),
glam::vec3(x,y,z)
))
}
pub fn point(x:f32,y:f32,z:f32)->Self{
Self(glam::Affine3A::from_translation(glam::vec3(x,y,z)))
}
pub fn angles(x:f32,y:f32,z:f32)->Self{
Self(glam::Affine3A::from_mat3(glam::Mat3::from_euler(glam::EulerRot::YXZ,y,x,z)))
}
impl CFrame {
pub fn new(
x: f32,
y: f32,
z: f32,
xx: f32,
yx: f32,
zx: f32,
xy: f32,
yy: f32,
zy: f32,
xz: f32,
yz: f32,
zz: f32,
) -> Self {
Self(glam::Affine3A::from_mat3_translation(
glam::mat3(
glam::vec3(xx, yx, zx),
glam::vec3(xy, yy, zy),
glam::vec3(xz, yz, zz),
),
glam::vec3(x, y, z),
))
}
pub fn point(x: f32, y: f32, z: f32) -> Self {
Self(glam::Affine3A::from_translation(glam::vec3(x, y, z)))
}
pub fn angles(x: f32, y: f32, z: f32) -> Self {
Self(glam::Affine3A::from_mat3(glam::Mat3::from_euler(
glam::EulerRot::YXZ,
y,
x,
z,
)))
}
}
fn vec3_to_glam(v:glam::Vec3A)->rbx_types::Vector3{
rbx_types::Vector3::new(v.x,v.y,v.z)
fn vec3_to_glam(v: glam::Vec3A) -> rbx_types::Vector3 {
rbx_types::Vector3::new(v.x, v.y, v.z)
}
fn vec3_from_glam(v:rbx_types::Vector3)->glam::Vec3A{
glam::vec3a(v.x,v.y,v.z)
fn vec3_from_glam(v: rbx_types::Vector3) -> glam::Vec3A {
glam::vec3a(v.x, v.y, v.z)
}
impl Into<rbx_types::CFrame> for CFrame{
fn into(self)->rbx_types::CFrame{
rbx_types::CFrame::new(
vec3_to_glam(self.0.translation),
rbx_types::Matrix3::new(
vec3_to_glam(self.0.matrix3.x_axis),
vec3_to_glam(self.0.matrix3.y_axis),
vec3_to_glam(self.0.matrix3.z_axis),
)
)
}
impl Into<rbx_types::CFrame> for CFrame {
fn into(self) -> rbx_types::CFrame {
rbx_types::CFrame::new(
vec3_to_glam(self.0.translation),
rbx_types::Matrix3::new(
vec3_to_glam(self.0.matrix3.x_axis),
vec3_to_glam(self.0.matrix3.y_axis),
vec3_to_glam(self.0.matrix3.z_axis),
),
)
}
}
impl From<rbx_types::CFrame> for CFrame{
fn from(value:rbx_types::CFrame)->Self{
CFrame(glam::Affine3A{
matrix3:glam::mat3a(
vec3_from_glam(value.orientation.x),
vec3_from_glam(value.orientation.y),
vec3_from_glam(value.orientation.z),
),
translation:vec3_from_glam(value.position)
})
}
impl From<rbx_types::CFrame> for CFrame {
fn from(value: rbx_types::CFrame) -> Self {
CFrame(glam::Affine3A {
matrix3: glam::mat3a(
vec3_from_glam(value.orientation.x),
vec3_from_glam(value.orientation.y),
vec3_from_glam(value.orientation.z),
),
translation: vec3_from_glam(value.position),
})
}
}
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let cframe_table=lua.create_table()?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let cframe_table = lua.create_table()?;
//CFrame.new
cframe_table.raw_set("new",
lua.create_function(|_,tuple:(
mlua::Value,mlua::Value,Option<f32>,
Option<f32>,Option<f32>,Option<f32>,
Option<f32>,Option<f32>,Option<f32>,
Option<f32>,Option<f32>,Option<f32>,
)|match tuple{
//CFrame.new(pos)
(
mlua::Value::UserData(pos),mlua::Value::Nil,None,
None,None,None,
None,None,None,
None,None,None,
)=>{
let pos:Vector3=pos.take()?;
Ok(CFrame::point(pos.0.x,pos.0.y,pos.0.z))
},
//TODO: CFrame.new(pos,look)
(
mlua::Value::UserData(pos),mlua::Value::UserData(look),None,
None,None,None,
None,None,None,
None,None,None,
)=>{
let _pos:Vector3=pos.take()?;
let _look:Vector3=look.take()?;
Err(mlua::Error::runtime("Not yet implemented"))
},
//CFrame.new(x,y,z)
(
mlua::Value::Number(x),mlua::Value::Number(y),Some(z),
None,None,None,
None,None,None,
None,None,None,
)=>Ok(CFrame::point(x as f32,y as f32,z)),
//CFrame.new(x,y,z,xx,yx,zx,xy,yy,zy,xz,yz,zz)
(
mlua::Value::Number(x),mlua::Value::Number(y),Some(z),
Some(xx),Some(yx),Some(zx),
Some(xy),Some(yy),Some(zy),
Some(xz),Some(yz),Some(zz),
)=>Ok(CFrame::new(x as f32,y as f32,z,
xx,yx,zx,
xy,yy,zy,
xz,yz,zz,
)),
_=>Err(mlua::Error::runtime("Invalid arguments"))
})?
)?;
//CFrame.new
cframe_table.raw_set(
"new",
lua.create_function(
|_,
tuple: (
mlua::Value,
mlua::Value,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
)| match tuple {
//CFrame.new(pos)
(
mlua::Value::UserData(pos),
mlua::Value::Nil,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
) => {
let pos: Vector3 = pos.take()?;
Ok(CFrame::point(pos.0.x, pos.0.y, pos.0.z))
}
//TODO: CFrame.new(pos,look)
(
mlua::Value::UserData(pos),
mlua::Value::UserData(look),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
) => {
let _pos: Vector3 = pos.take()?;
let _look: Vector3 = look.take()?;
Err(mlua::Error::runtime("Not yet implemented"))
}
//CFrame.new(x,y,z)
(
mlua::Value::Number(x),
mlua::Value::Number(y),
Some(z),
None,
None,
None,
None,
None,
None,
None,
None,
None,
) => Ok(CFrame::point(x as f32, y as f32, z)),
//CFrame.new(x,y,z,xx,yx,zx,xy,yy,zy,xz,yz,zz)
(
mlua::Value::Number(x),
mlua::Value::Number(y),
Some(z),
Some(xx),
Some(yx),
Some(zx),
Some(xy),
Some(yy),
Some(zy),
Some(xz),
Some(yz),
Some(zz),
) => Ok(CFrame::new(
x as f32, y as f32, z, xx, yx, zx, xy, yy, zy, xz, yz, zz,
)),
_ => Err(mlua::Error::runtime("Invalid arguments")),
},
)?,
)?;
//CFrame.Angles
cframe_table.raw_set("Angles",
lua.create_function(|_,(x,y,z):(f32,f32,f32)|
Ok(CFrame::angles(x,y,z))
)?
)?;
//CFrame.Angles
cframe_table.raw_set(
"Angles",
lua.create_function(|_, (x, y, z): (f32, f32, f32)| Ok(CFrame::angles(x, y, z)))?,
)?;
globals.set("CFrame",cframe_table)?;
globals.set("CFrame", cframe_table)?;
Ok(())
Ok(())
}
impl mlua::UserData for CFrame{
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
//CFrame.p
fields.add_field_method_get("p",|_,this|Ok(Vector3(this.0.translation)));
}
impl mlua::UserData for CFrame {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
//CFrame.p
fields.add_field_method_get("p", |_, this| Ok(Vector3(this.0.translation)));
}
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_method("components",|_,this,()|Ok((
this.0.translation.x,
this.0.translation.y,
this.0.translation.z,
this.0.matrix3.x_axis.x,
this.0.matrix3.y_axis.x,
this.0.matrix3.z_axis.x,
this.0.matrix3.x_axis.y,
this.0.matrix3.y_axis.y,
this.0.matrix3.z_axis.y,
this.0.matrix3.x_axis.z,
this.0.matrix3.y_axis.z,
this.0.matrix3.z_axis.z,
)));
methods.add_method("VectorToWorldSpace",|_,this,v:Vector3|
Ok(Vector3(this.0.transform_vector3a(v.0)))
);
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("components", |_, this, ()| {
Ok((
this.0.translation.x,
this.0.translation.y,
this.0.translation.z,
this.0.matrix3.x_axis.x,
this.0.matrix3.y_axis.x,
this.0.matrix3.z_axis.x,
this.0.matrix3.x_axis.y,
this.0.matrix3.y_axis.y,
this.0.matrix3.z_axis.y,
this.0.matrix3.x_axis.z,
this.0.matrix3.y_axis.z,
this.0.matrix3.z_axis.z,
))
});
methods.add_method("VectorToWorldSpace", |_, this, v: Vector3| {
Ok(Vector3(this.0.transform_vector3a(v.0)))
});
//methods.add_meta_method(mlua::MetaMethod::Mul,|_,this,val:&Vector3|Ok(Vector3(this.0.matrix3*val.0+this.0.translation)));
methods.add_meta_function(mlua::MetaMethod::Mul,|_,(this,val):(Self,Self)|Ok(Self(this.0*val.0)));
methods.add_meta_function(mlua::MetaMethod::ToString,|_,this:Self|
Ok(format!("CFrame.new({},{},{},{},{},{},{},{},{},{},{},{})",
this.0.translation.x,
this.0.translation.y,
this.0.translation.z,
this.0.matrix3.x_axis.x,
this.0.matrix3.y_axis.x,
this.0.matrix3.z_axis.x,
this.0.matrix3.x_axis.y,
this.0.matrix3.y_axis.y,
this.0.matrix3.z_axis.y,
this.0.matrix3.x_axis.z,
this.0.matrix3.y_axis.z,
this.0.matrix3.z_axis.z,
))
);
}
//methods.add_meta_method(mlua::MetaMethod::Mul,|_,this,val:&Vector3|Ok(Vector3(this.0.matrix3*val.0+this.0.translation)));
methods.add_meta_function(mlua::MetaMethod::Mul, |_, (this, val): (Self, Self)| {
Ok(Self(this.0 * val.0))
});
methods.add_meta_function(mlua::MetaMethod::ToString, |_, this: Self| {
Ok(format!(
"CFrame.new({},{},{},{},{},{},{},{},{},{},{},{})",
this.0.translation.x,
this.0.translation.y,
this.0.translation.z,
this.0.matrix3.x_axis.x,
this.0.matrix3.y_axis.x,
this.0.matrix3.z_axis.x,
this.0.matrix3.x_axis.y,
this.0.matrix3.y_axis.y,
this.0.matrix3.z_axis.y,
this.0.matrix3.x_axis.z,
this.0.matrix3.y_axis.z,
this.0.matrix3.z_axis.z,
))
});
}
}
type_from_lua_userdata!(CFrame);

View File

@ -1,68 +1,72 @@
#[derive(Clone,Copy)]
pub struct Color3{
r:f32,
g:f32,
b:f32,
#[derive(Clone, Copy)]
pub struct Color3 {
r: f32,
g: f32,
b: f32,
}
impl Color3{
pub const fn new(r:f32,g:f32,b:f32)->Self{
Self{r,g,b}
}
impl Color3 {
pub const fn new(r: f32, g: f32, b: f32) -> Self {
Self { r, g, b }
}
}
impl Into<rbx_types::Color3> for Color3{
fn into(self)->rbx_types::Color3{
rbx_types::Color3::new(self.r,self.g,self.b)
}
impl Into<rbx_types::Color3> for Color3 {
fn into(self) -> rbx_types::Color3 {
rbx_types::Color3::new(self.r, self.g, self.b)
}
}
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let color3_table=lua.create_table()?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let color3_table = lua.create_table()?;
color3_table.raw_set("new",
lua.create_function(|_,(r,g,b):(f32,f32,f32)|
Ok(Color3::new(r,g,b))
)?
)?;
color3_table.raw_set("fromRGB",
lua.create_function(|_,(r,g,b):(u8,u8,u8)|
Ok(Color3::new(r as f32/255.0,g as f32/255.0,b as f32/255.0))
)?
)?;
color3_table.raw_set(
"new",
lua.create_function(|_, (r, g, b): (f32, f32, f32)| Ok(Color3::new(r, g, b)))?,
)?;
color3_table.raw_set(
"fromRGB",
lua.create_function(|_, (r, g, b): (u8, u8, u8)| {
Ok(Color3::new(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
))
})?,
)?;
globals.set("Color3",color3_table)?;
globals.set("Color3", color3_table)?;
Ok(())
Ok(())
}
fn lerp(lhs:f32,rhs:f32,t:f32)->f32{
lhs+(rhs-lhs)*t
fn lerp(lhs: f32, rhs: f32, t: f32) -> f32 {
lhs + (rhs - lhs) * t
}
impl mlua::UserData for Color3{
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
fields.add_field_method_get("r",|_,this|Ok(this.r));
fields.add_field_method_set("r",|_,this,val|{
this.r=val;
Ok(())
});
fields.add_field_method_get("g",|_,this|Ok(this.g));
fields.add_field_method_set("g",|_,this,val|{
this.g=val;
Ok(())
});
fields.add_field_method_get("b",|_,this|Ok(this.b));
fields.add_field_method_set("b",|_,this,val|{
this.b=val;
Ok(())
});
}
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_method("Lerp",|_,this,(other,t):(Self,f32)|
Ok(Color3::new(
lerp(this.r,other.r,t),
lerp(this.g,other.g,t),
lerp(this.b,other.b,t),
))
)
}
impl mlua::UserData for Color3 {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("r", |_, this| Ok(this.r));
fields.add_field_method_set("r", |_, this, val| {
this.r = val;
Ok(())
});
fields.add_field_method_get("g", |_, this| Ok(this.g));
fields.add_field_method_set("g", |_, this, val| {
this.g = val;
Ok(())
});
fields.add_field_method_get("b", |_, this| Ok(this.b));
fields.add_field_method_set("b", |_, this, val| {
this.b = val;
Ok(())
});
}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("Lerp", |_, this, (other, t): (Self, f32)| {
Ok(Color3::new(
lerp(this.r, other.r, t),
lerp(this.g, other.g, t),
lerp(this.b, other.b, t),
))
})
}
}
type_from_lua_userdata!(Color3);

View File

@ -1,31 +1,30 @@
#[derive(Clone,Copy)]
pub struct ColorSequence{}
impl ColorSequence{
pub const fn new()->Self{
Self{}
}
#[derive(Clone, Copy)]
pub struct ColorSequence {}
impl ColorSequence {
pub const fn new() -> Self {
Self {}
}
}
impl Into<rbx_types::ColorSequence> for ColorSequence{
fn into(self)->rbx_types::ColorSequence{
rbx_types::ColorSequence{
keypoints:Vec::new()
}
}
impl Into<rbx_types::ColorSequence> for ColorSequence {
fn into(self) -> rbx_types::ColorSequence {
rbx_types::ColorSequence {
keypoints: Vec::new(),
}
}
}
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let number_sequence_table=lua.create_table()?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let number_sequence_table = lua.create_table()?;
number_sequence_table.raw_set("new",
lua.create_function(|_,_:mlua::MultiValue|
Ok(ColorSequence::new())
)?
)?;
number_sequence_table.raw_set(
"new",
lua.create_function(|_, _: mlua::MultiValue| Ok(ColorSequence::new()))?,
)?;
globals.set("ColorSequence",number_sequence_table)?;
globals.set("ColorSequence", number_sequence_table)?;
Ok(())
Ok(())
}
impl mlua::UserData for ColorSequence{}
impl mlua::UserData for ColorSequence {}
type_from_lua_userdata!(ColorSequence);

View File

@ -1,63 +1,67 @@
use mlua::IntoLua;
#[derive(Clone,Copy)]
#[derive(Clone, Copy)]
pub struct Enum(u32);
#[derive(Clone,Copy)]
#[derive(Clone, Copy)]
pub struct EnumItems;
#[derive(Clone,Copy)]
pub struct EnumItem<'a>{
ed:&'a rbx_reflection::EnumDescriptor<'a>,
#[derive(Clone, Copy)]
pub struct EnumItem<'a> {
ed: &'a rbx_reflection::EnumDescriptor<'a>,
}
impl Into<rbx_types::Enum> for Enum{
fn into(self)->rbx_types::Enum{
rbx_types::Enum::from_u32(self.0)
}
impl Into<rbx_types::Enum> for Enum {
fn into(self) -> rbx_types::Enum {
rbx_types::Enum::from_u32(self.0)
}
}
impl<'a> EnumItem<'a>{
const fn new(ed:&'a rbx_reflection::EnumDescriptor)->Self{
Self{ed}
}
impl<'a> EnumItem<'a> {
const fn new(ed: &'a rbx_reflection::EnumDescriptor) -> Self {
Self { ed }
}
}
pub fn set_globals(_lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
globals.set("Enum",EnumItems)
pub fn set_globals(_lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
globals.set("Enum", EnumItems)
}
impl mlua::UserData for EnumItem<'_>{
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
}
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,val):(EnumItem<'_>,mlua::String)|{
match this.ed.items.get(&*val.to_str()?){
Some(&id)=>Enum(id).into_lua(lua),
None=>mlua::Value::Nil.into_lua(lua),
}
});
}
impl mlua::UserData for EnumItem<'_> {
fn add_fields<F: mlua::UserDataFields<Self>>(_fields: &mut F) {}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_function(
mlua::MetaMethod::Index,
|lua, (this, val): (EnumItem<'_>, mlua::String)| match this
.ed
.items
.get(&*val.to_str()?)
{
Some(&id) => Enum(id).into_lua(lua),
None => mlua::Value::Nil.into_lua(lua),
},
);
}
}
type_from_lua_userdata_lua_lifetime!(EnumItem);
impl mlua::UserData for EnumItems{
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
}
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(_,val):(Self,mlua::String)|{
let db=rbx_reflection_database::get();
match db.enums.get(&*val.to_str()?){
Some(ed)=>EnumItem::new(ed).into_lua(lua),
None=>mlua::Value::Nil.into_lua(lua),
}
});
}
impl mlua::UserData for EnumItems {
fn add_fields<F: mlua::UserDataFields<Self>>(_fields: &mut F) {}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_function(
mlua::MetaMethod::Index,
|lua, (_, val): (Self, mlua::String)| {
let db = rbx_reflection_database::get();
match db.enums.get(&*val.to_str()?) {
Some(ed) => EnumItem::new(ed).into_lua(lua),
None => mlua::Value::Nil.into_lua(lua),
}
},
);
}
}
type_from_lua_userdata!(EnumItems);
impl mlua::UserData for Enum{
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
}
fn add_methods<M:mlua::UserDataMethods<Self>>(_methods:&mut M){
}
impl mlua::UserData for Enum {
fn add_fields<F: mlua::UserDataFields<Self>>(_fields: &mut F) {}
fn add_methods<M: mlua::UserDataMethods<Self>>(_methods: &mut M) {}
}
type_from_lua_userdata!(Enum);

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,32 @@
macro_rules! type_from_lua_userdata{
($asd:ident)=>{
impl mlua::FromLua for $asd{
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
match value{
mlua::Value::UserData(ud)=>Ok(*ud.borrow::<Self>()?),
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!($asd),other))),
}
}
}
};
macro_rules! type_from_lua_userdata {
($asd:ident) => {
impl mlua::FromLua for $asd {
fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> Result<Self, mlua::Error> {
match value {
mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
other => Err(mlua::Error::runtime(format!(
"Expected {} got {:?}",
stringify!($asd),
other
))),
}
}
}
};
}
macro_rules! type_from_lua_userdata_lua_lifetime{
($asd:ident)=>{
impl mlua::FromLua for $asd<'static>{
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
match value{
mlua::Value::UserData(ud)=>Ok(*ud.borrow::<Self>()?),
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!($asd),other))),
}
}
}
};
macro_rules! type_from_lua_userdata_lua_lifetime {
($asd:ident) => {
impl mlua::FromLua for $asd<'static> {
fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> Result<Self, mlua::Error> {
match value {
mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
other => Err(mlua::Error::runtime(format!(
"Expected {} got {:?}",
stringify!($asd),
other
))),
}
}
}
};
}

View File

@ -2,13 +2,13 @@
mod macros;
mod runner;
mod r#enum;
mod color3;
mod cframe;
mod vector3;
pub mod instance;
mod script_signal;
mod color3;
mod color_sequence;
mod r#enum;
pub mod instance;
mod number_sequence;
mod script_signal;
mod vector3;
pub use runner::{Runner,Runnable,Error};
pub use runner::{Error, Runnable, Runner};

View File

@ -1,31 +1,30 @@
#[derive(Clone,Copy)]
pub struct NumberSequence{}
impl NumberSequence{
pub const fn new()->Self{
Self{}
}
#[derive(Clone, Copy)]
pub struct NumberSequence {}
impl NumberSequence {
pub const fn new() -> Self {
Self {}
}
}
impl Into<rbx_types::NumberSequence> for NumberSequence{
fn into(self)->rbx_types::NumberSequence{
rbx_types::NumberSequence{
keypoints:Vec::new()
}
}
impl Into<rbx_types::NumberSequence> for NumberSequence {
fn into(self) -> rbx_types::NumberSequence {
rbx_types::NumberSequence {
keypoints: Vec::new(),
}
}
}
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let number_sequence_table=lua.create_table()?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let number_sequence_table = lua.create_table()?;
number_sequence_table.raw_set("new",
lua.create_function(|_,_:mlua::MultiValue|
Ok(NumberSequence::new())
)?
)?;
number_sequence_table.raw_set(
"new",
lua.create_function(|_, _: mlua::MultiValue| Ok(NumberSequence::new()))?,
)?;
globals.set("NumberSequence",number_sequence_table)?;
globals.set("NumberSequence", number_sequence_table)?;
Ok(())
Ok(())
}
impl mlua::UserData for NumberSequence{}
impl mlua::UserData for NumberSequence {}
type_from_lua_userdata!(NumberSequence);

View File

@ -1,143 +1,171 @@
use crate::context::Context;
#[cfg(feature="run-service")]
#[cfg(feature = "run-service")]
use crate::scheduler::scheduler_mut;
pub struct Runner{
lua:mlua::Lua,
pub struct Runner {
lua: mlua::Lua,
}
#[derive(Debug)]
pub enum Error{
Lua{
source:String,
error:mlua::Error
},
RustLua(mlua::Error),
NoServices,
pub enum Error {
Lua { source: String, error: mlua::Error },
RustLua(mlua::Error),
NoServices,
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
match self{
Self::Lua{source,error}=>write!(f,"lua error: source:\n{source}\n{error}"),
Self::RustLua(error)=>write!(f,"rust-side lua error: {error}"),
other=>write!(f,"{other:?}"),
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Lua { source, error } => write!(f, "lua error: source:\n{source}\n{error}"),
Self::RustLua(error) => write!(f, "rust-side lua error: {error}"),
other => write!(f, "{other:?}"),
}
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
fn init(lua:&mlua::Lua)->mlua::Result<()>{
lua.sandbox(true)?;
fn init(lua: &mlua::Lua) -> mlua::Result<()> {
lua.sandbox(true)?;
//global environment
let globals=lua.globals();
//global environment
let globals = lua.globals();
#[cfg(feature="run-service")]
crate::scheduler::set_globals(lua,&globals)?;
super::script_signal::set_globals(lua,&globals)?;
super::r#enum::set_globals(lua,&globals)?;
super::color3::set_globals(lua,&globals)?;
super::vector3::set_globals(lua,&globals)?;
super::cframe::set_globals(lua,&globals)?;
super::instance::instance::set_globals(lua,&globals)?;
super::number_sequence::set_globals(lua,&globals)?;
super::color_sequence::set_globals(lua,&globals)?;
#[cfg(feature = "run-service")]
crate::scheduler::set_globals(lua, &globals)?;
super::script_signal::set_globals(lua, &globals)?;
super::r#enum::set_globals(lua, &globals)?;
super::color3::set_globals(lua, &globals)?;
super::vector3::set_globals(lua, &globals)?;
super::cframe::set_globals(lua, &globals)?;
super::instance::instance::set_globals(lua, &globals)?;
super::number_sequence::set_globals(lua, &globals)?;
super::color_sequence::set_globals(lua, &globals)?;
Ok(())
Ok(())
}
impl Runner{
pub fn new()->Result<Self,Error>{
let runner=Self{
lua:mlua::Lua::new(),
};
init(&runner.lua).map_err(Error::RustLua)?;
Ok(runner)
}
pub fn runnable_context<'a>(self,context:&'a mut Context)->Result<Runnable<'a>,Error>{
let services=context.find_services().ok_or(Error::NoServices)?;
self.runnable_context_with_services(context,&services)
}
pub fn runnable_context_with_services<'a>(self,context:&'a mut Context,services:&crate::context::Services)->Result<Runnable<'a>,Error>{
{
let globals=self.lua.globals();
globals.set("game",super::instance::Instance::new(services.game)).map_err(Error::RustLua)?;
globals.set("workspace",super::instance::Instance::new(services.workspace)).map_err(Error::RustLua)?;
}
//this makes set_app_data shut up about the lifetime
self.lua.set_app_data::<&'static mut rbx_dom_weak::WeakDom>(unsafe{core::mem::transmute(&mut context.dom)});
#[cfg(feature="run-service")]
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
Ok(Runnable{
lua:self.lua,
_lifetime:&std::marker::PhantomData
})
}
impl Runner {
pub fn new() -> Result<Self, Error> {
let runner = Self {
lua: mlua::Lua::new(),
};
init(&runner.lua).map_err(Error::RustLua)?;
Ok(runner)
}
pub fn runnable_context<'a>(self, context: &'a mut Context) -> Result<Runnable<'a>, Error> {
let services = context.find_services().ok_or(Error::NoServices)?;
self.runnable_context_with_services(context, &services)
}
pub fn runnable_context_with_services<'a>(
self,
context: &'a mut Context,
services: &crate::context::Services,
) -> Result<Runnable<'a>, Error> {
{
let globals = self.lua.globals();
globals
.set("game", super::instance::Instance::new(services.game))
.map_err(Error::RustLua)?;
globals
.set(
"workspace",
super::instance::Instance::new(services.workspace),
)
.map_err(Error::RustLua)?;
}
//this makes set_app_data shut up about the lifetime
self.lua
.set_app_data::<&'static mut rbx_dom_weak::WeakDom>(unsafe {
core::mem::transmute(&mut context.dom)
});
#[cfg(feature = "run-service")]
self.lua
.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
Ok(Runnable {
lua: self.lua,
_lifetime: &std::marker::PhantomData,
})
}
}
//Runnable is the same thing but has context set, which it holds the lifetime for.
pub struct Runnable<'a>{
lua:mlua::Lua,
_lifetime:&'a std::marker::PhantomData<()>
pub struct Runnable<'a> {
lua: mlua::Lua,
_lifetime: &'a std::marker::PhantomData<()>,
}
impl Runnable<'_>{
pub fn drop_context(self)->Runner{
self.lua.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
#[cfg(feature="run-service")]
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
Runner{
lua:self.lua,
}
}
pub fn run_script(&self,script:super::instance::Instance)->Result<(),Error>{
let (name,source)=super::instance::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?;
self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
let f=self.lua.load(source.as_str())
.set_name(name).into_function().map_err(Error::RustLua)?;
// TODO: set_environment without losing the ability to print from Lua
let thread=self.lua.create_thread(f).map_err(Error::RustLua)?;
thread.resume::<mlua::MultiValue>(()).map_err(|error|Error::Lua{source,error})?;
// wait() is called from inside Lua and goes to a rust function that schedules the thread and then yields
// No need to schedule the thread here
Ok(())
}
#[cfg(feature="run-service")]
pub fn has_scheduled_threads(&self)->Result<bool,mlua::Error>{
scheduler_mut(&self.lua,|scheduler|
Ok(scheduler.has_scheduled_threads())
)
}
#[cfg(feature="run-service")]
pub fn game_tick(&self)->Result<(),mlua::Error>{
if let Some(threads)=scheduler_mut(&self.lua,|scheduler|Ok(scheduler.tick_threads()))?{
for thread in threads{
//TODO: return dt and total run time
let result=thread.resume::<mlua::MultiValue>((1.0/30.0,0.0))
.map_err(|error|Error::Lua{source:"source unavailable".to_owned(),error});
match result{
Ok(_)=>(),
Err(e)=>println!("game_tick Error: {e}"),
}
}
}
Ok(())
}
#[cfg(feature="run-service")]
pub fn run_service_step(&self)->Result<(),mlua::Error>{
let render_stepped=super::instance::instance::dom_mut(&self.lua,|dom|{
let run_service=super::instance::instance::find_first_child_of_class(dom,dom.root(),"RunService").ok_or_else(||mlua::Error::runtime("RunService missing"))?;
super::instance::instance::instance_value_store_mut(&self.lua,|instance_value_store|{
//unwrap because I trust my find_first_child_of_class function to
let mut instance_values=instance_value_store.get_or_create_instance_values(run_service).ok_or_else(||mlua::Error::runtime("RunService InstanceValues missing"))?;
let render_stepped=instance_values.get_or_create_value(&self.lua,"RenderStepped")?;
//let stepped=instance_values.get_or_create_value(&self.lua,"Stepped")?;
//let heartbeat=instance_values.get_or_create_value(&self.lua,"Heartbeat")?;
Ok(render_stepped)
})
})?;
if let Some(render_stepped)=render_stepped{
let signal:&super::script_signal::ScriptSignal=&*render_stepped.borrow()?;
signal.fire(&mlua::MultiValue::new());
}
Ok(())
}
impl Runnable<'_> {
pub fn drop_context(self) -> Runner {
self.lua
.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
#[cfg(feature = "run-service")]
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
Runner { lua: self.lua }
}
pub fn run_script(&self, script: super::instance::Instance) -> Result<(), Error> {
let (name, source) = super::instance::instance::get_name_source(&self.lua, script)
.map_err(Error::RustLua)?;
self.lua
.globals()
.raw_set("script", script)
.map_err(Error::RustLua)?;
let f = self
.lua
.load(source.as_str())
.set_name(name)
.into_function()
.map_err(Error::RustLua)?;
// TODO: set_environment without losing the ability to print from Lua
let thread = self.lua.create_thread(f).map_err(Error::RustLua)?;
thread
.resume::<mlua::MultiValue>(())
.map_err(|error| Error::Lua { source, error })?;
// wait() is called from inside Lua and goes to a rust function that schedules the thread and then yields
// No need to schedule the thread here
Ok(())
}
#[cfg(feature = "run-service")]
pub fn has_scheduled_threads(&self) -> Result<bool, mlua::Error> {
scheduler_mut(&self.lua, |scheduler| Ok(scheduler.has_scheduled_threads()))
}
#[cfg(feature = "run-service")]
pub fn game_tick(&self) -> Result<(), mlua::Error> {
if let Some(threads) = scheduler_mut(&self.lua, |scheduler| Ok(scheduler.tick_threads()))? {
for thread in threads {
//TODO: return dt and total run time
let result = thread
.resume::<mlua::MultiValue>((1.0 / 30.0, 0.0))
.map_err(|error| Error::Lua {
source: "source unavailable".to_owned(),
error,
});
match result {
Ok(_) => (),
Err(e) => println!("game_tick Error: {e}"),
}
}
}
Ok(())
}
#[cfg(feature = "run-service")]
pub fn run_service_step(&self) -> Result<(), mlua::Error> {
let render_stepped = super::instance::instance::dom_mut(&self.lua, |dom| {
let run_service =
super::instance::instance::find_first_child_of_class(dom, dom.root(), "RunService")
.ok_or_else(|| mlua::Error::runtime("RunService missing"))?;
super::instance::instance::instance_value_store_mut(&self.lua, |instance_value_store| {
//unwrap because I trust my find_first_child_of_class function to
let mut instance_values = instance_value_store
.get_or_create_instance_values(run_service)
.ok_or_else(|| mlua::Error::runtime("RunService InstanceValues missing"))?;
let render_stepped =
instance_values.get_or_create_value(&self.lua, "RenderStepped")?;
//let stepped=instance_values.get_or_create_value(&self.lua,"Stepped")?;
//let heartbeat=instance_values.get_or_create_value(&self.lua,"Heartbeat")?;
Ok(render_stepped)
})
})?;
if let Some(render_stepped) = render_stepped {
let signal: &super::script_signal::ScriptSignal = &*render_stepped.borrow()?;
signal.fire(&mlua::MultiValue::new());
}
Ok(())
}
}

View File

@ -1,172 +1,189 @@
use std::{cell::RefCell,rc::Rc};
use std::{cell::RefCell, rc::Rc};
use mlua::UserDataFields;
#[derive(Clone)]
struct FunctionList{
functions:Vec<mlua::Function>,
struct FunctionList {
functions: Vec<mlua::Function>,
}
impl FunctionList{
pub fn new()->Self{
Self{
functions:Vec::new(),
}
}
// This eats the Lua error
pub fn fire(self,args:&mlua::MultiValue){
// Make a copy of the list in case Lua attempts to modify it during the loop
for function in self.functions{
//wee let's allocate for our function calls
if let Err(e)=function.call::<mlua::MultiValue>(args.clone()){
println!("Script Signal Error: {e}");
}
}
}
impl FunctionList {
pub fn new() -> Self {
Self {
functions: Vec::new(),
}
}
// This eats the Lua error
pub fn fire(self, args: &mlua::MultiValue) {
// Make a copy of the list in case Lua attempts to modify it during the loop
for function in self.functions {
//wee let's allocate for our function calls
if let Err(e) = function.call::<mlua::MultiValue>(args.clone()) {
println!("Script Signal Error: {e}");
}
}
}
}
#[derive(Clone)]
struct RcFunctionList{
functions:Rc<RefCell<FunctionList>>,
struct RcFunctionList {
functions: Rc<RefCell<FunctionList>>,
}
impl RcFunctionList{
pub fn new()->Self{
Self{
functions:Rc::new(RefCell::new(FunctionList::new())),
}
}
pub fn fire(&self,args:&mlua::MultiValue){
// Make a copy of the list in case Lua attempts to modify it during the loop
self.functions.borrow().clone().fire(args)
}
impl RcFunctionList {
pub fn new() -> Self {
Self {
functions: Rc::new(RefCell::new(FunctionList::new())),
}
}
pub fn fire(&self, args: &mlua::MultiValue) {
// Make a copy of the list in case Lua attempts to modify it during the loop
self.functions.borrow().clone().fire(args)
}
}
#[derive(Clone)]
pub struct ScriptSignal{
// Emulate the garbage roblox api.
// ScriptConnection should not exist.
// :Disconnect should be a method on ScriptSignal, and this would be avoided entirely.
connections:RcFunctionList,
once:RcFunctionList,
wait:Rc<RefCell<Vec<mlua::Thread>>>,
pub struct ScriptSignal {
// Emulate the garbage roblox api.
// ScriptConnection should not exist.
// :Disconnect should be a method on ScriptSignal, and this would be avoided entirely.
connections: RcFunctionList,
once: RcFunctionList,
wait: Rc<RefCell<Vec<mlua::Thread>>>,
}
pub struct ScriptConnection{
connection:RcFunctionList,
function:mlua::Function,
pub struct ScriptConnection {
connection: RcFunctionList,
function: mlua::Function,
}
impl ScriptSignal{
pub fn new()->Self{
Self{
connections:RcFunctionList::new(),
once:RcFunctionList::new(),
wait:Rc::new(RefCell::new(Vec::new())),
}
}
pub fn fire(&self,args:&mlua::MultiValue){
self.connections.fire(args);
//Replace the FunctionList with an empty one and drop the borrow
let once=std::mem::replace(&mut *self.once.functions.borrow_mut(),FunctionList::new());
once.fire(args);
//resume threads waiting for this signal
let threads=std::mem::replace(&mut *self.wait.borrow_mut(),Vec::new());
for thread in threads{
if let Err(e)=thread.resume::<mlua::MultiValue>(args.clone()){
println!("Script Signal thread resume Error: {e}");
}
}
}
pub fn connect(&self,function:mlua::Function)->ScriptConnection{
self.connections.functions.borrow_mut().functions.push(function.clone());
ScriptConnection{
connection:self.connections.clone(),
function,
}
}
pub fn once(&self,function:mlua::Function)->ScriptConnection{
self.once.functions.borrow_mut().functions.push(function.clone());
ScriptConnection{
connection:self.once.clone(),
function,
}
}
pub fn wait(&self,thread:mlua::Thread){
self.wait.borrow_mut().push(thread);
}
impl ScriptSignal {
pub fn new() -> Self {
Self {
connections: RcFunctionList::new(),
once: RcFunctionList::new(),
wait: Rc::new(RefCell::new(Vec::new())),
}
}
pub fn fire(&self, args: &mlua::MultiValue) {
self.connections.fire(args);
//Replace the FunctionList with an empty one and drop the borrow
let once = std::mem::replace(&mut *self.once.functions.borrow_mut(), FunctionList::new());
once.fire(args);
//resume threads waiting for this signal
let threads = std::mem::replace(&mut *self.wait.borrow_mut(), Vec::new());
for thread in threads {
if let Err(e) = thread.resume::<mlua::MultiValue>(args.clone()) {
println!("Script Signal thread resume Error: {e}");
}
}
}
pub fn connect(&self, function: mlua::Function) -> ScriptConnection {
self.connections
.functions
.borrow_mut()
.functions
.push(function.clone());
ScriptConnection {
connection: self.connections.clone(),
function,
}
}
pub fn once(&self, function: mlua::Function) -> ScriptConnection {
self.once
.functions
.borrow_mut()
.functions
.push(function.clone());
ScriptConnection {
connection: self.once.clone(),
function,
}
}
pub fn wait(&self, thread: mlua::Thread) {
self.wait.borrow_mut().push(thread);
}
}
impl ScriptConnection{
pub fn position(&self)->Option<usize>{
self.connection.functions.borrow().functions.iter().position(|function|function==&self.function)
}
impl ScriptConnection {
pub fn position(&self) -> Option<usize> {
self.connection
.functions
.borrow()
.functions
.iter()
.position(|function| function == &self.function)
}
}
impl mlua::UserData for ScriptSignal{
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_method("Connect",|_lua,this,f:mlua::Function|
Ok(this.connect(f))
);
methods.add_method("Once",|_lua,this,f:mlua::Function|
Ok(this.once(f))
);
// Fire is not allowed to be called from Lua
// methods.add_method("Fire",|_lua,this,args:mlua::MultiValue|
// Ok(this.fire(args))
// );
}
impl mlua::UserData for ScriptSignal {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("Connect", |_lua, this, f: mlua::Function| {
Ok(this.connect(f))
});
methods.add_method("Once", |_lua, this, f: mlua::Function| Ok(this.once(f)));
// Fire is not allowed to be called from Lua
// methods.add_method("Fire",|_lua,this,args:mlua::MultiValue|
// Ok(this.fire(args))
// );
}
}
impl mlua::FromLua for ScriptSignal{
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
match value{
mlua::Value::UserData(ud)=>Ok(ud.borrow::<Self>()?.clone()),
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!(ScriptSignal),other))),
}
}
impl mlua::FromLua for ScriptSignal {
fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> Result<Self, mlua::Error> {
match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
other => Err(mlua::Error::runtime(format!(
"Expected {} got {:?}",
stringify!(ScriptSignal),
other
))),
}
}
}
impl mlua::UserData for ScriptConnection{
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
fields.add_field_method_get("Connected",|_,this|{
Ok(this.position().is_some())
});
}
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_method("Disconnect",|_,this,_:()|{
if let Some(index)=this.position(){
this.connection.functions.borrow_mut().functions.remove(index);
}
Ok(())
});
}
impl mlua::UserData for ScriptConnection {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("Connected", |_, this| Ok(this.position().is_some()));
}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("Disconnect", |_, this, _: ()| {
if let Some(index) = this.position() {
this.connection
.functions
.borrow_mut()
.functions
.remove(index);
}
Ok(())
});
}
}
fn wait_thread(lua:&mlua::Lua,this:ScriptSignal)->Result<(),mlua::Error>{
Ok(this.wait(lua.current_thread()))
fn wait_thread(lua: &mlua::Lua, this: ScriptSignal) -> Result<(), mlua::Error> {
Ok(this.wait(lua.current_thread()))
}
// This is used to avoid calling coroutine.yield from the rust side.
const LUA_WAIT:&str=
"local coroutine_yield=coroutine.yield
const LUA_WAIT: &str = "local coroutine_yield=coroutine.yield
local wait_thread=wait_thread
return function(signal)
wait_thread(signal)
return coroutine_yield()
end";
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let coroutine_table=globals.get::<mlua::Table>("coroutine")?;
let wait_thread=lua.create_function(wait_thread)?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let coroutine_table = globals.get::<mlua::Table>("coroutine")?;
let wait_thread = lua.create_function(wait_thread)?;
//create wait function environment
let wait_env=lua.create_table()?;
wait_env.raw_set("coroutine",coroutine_table)?;
wait_env.raw_set("wait_thread",wait_thread)?;
//create wait function environment
let wait_env = lua.create_table()?;
wait_env.raw_set("coroutine", coroutine_table)?;
wait_env.raw_set("wait_thread", wait_thread)?;
//construct wait function from Lua code
let wait=lua.load(LUA_WAIT)
.set_name("wait")
.set_environment(wait_env)
.call::<mlua::Function>(())?;
//construct wait function from Lua code
let wait = lua
.load(LUA_WAIT)
.set_name("wait")
.set_environment(wait_env)
.call::<mlua::Function>(())?;
lua.register_userdata_type::<ScriptSignal>(|reg|{
reg.add_field("Wait",wait);
mlua::UserData::register(reg);
})?;
lua.register_userdata_type::<ScriptSignal>(|reg| {
reg.add_field("Wait", wait);
mlua::UserData::register(reg);
})?;
Ok(())
Ok(())
}

View File

@ -1,82 +1,85 @@
#[derive(Clone,Copy)]
pub struct Vector3(pub(crate)glam::Vec3A);
#[derive(Clone, Copy)]
pub struct Vector3(pub(crate) glam::Vec3A);
impl Vector3{
pub const fn new(x:f32,y:f32,z:f32)->Self{
Self(glam::vec3a(x,y,z))
}
impl Vector3 {
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self(glam::vec3a(x, y, z))
}
}
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let vector3_table=lua.create_table()?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let vector3_table = lua.create_table()?;
//Vector3.new
vector3_table.raw_set("new",
lua.create_function(|_,(x,y,z):(f32,f32,f32)|
Ok(Vector3::new(x,y,z))
)?
)?;
//Vector3.new
vector3_table.raw_set(
"new",
lua.create_function(|_, (x, y, z): (f32, f32, f32)| Ok(Vector3::new(x, y, z)))?,
)?;
globals.set("Vector3",vector3_table)?;
globals.set("Vector3", vector3_table)?;
Ok(())
Ok(())
}
impl Into<rbx_types::Vector3> for Vector3{
fn into(self)->rbx_types::Vector3{
rbx_types::Vector3::new(self.0.x,self.0.y,self.0.z)
}
impl Into<rbx_types::Vector3> for Vector3 {
fn into(self) -> rbx_types::Vector3 {
rbx_types::Vector3::new(self.0.x, self.0.y, self.0.z)
}
}
impl From<rbx_types::Vector3> for Vector3{
fn from(value:rbx_types::Vector3)->Vector3{
Vector3::new(value.x,value.y,value.z)
}
impl From<rbx_types::Vector3> for Vector3 {
fn from(value: rbx_types::Vector3) -> Vector3 {
Vector3::new(value.x, value.y, value.z)
}
}
impl mlua::UserData for Vector3{
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
fields.add_field_method_get("magnitude",|_,this|Ok(this.0.length()));
fields.add_field_method_get("x",|_,this|Ok(this.0.x));
fields.add_field_method_set("x",|_,this,val|{
this.0.x=val;
Ok(())
});
fields.add_field_method_get("y",|_,this|Ok(this.0.y));
fields.add_field_method_set("y",|_,this,val|{
this.0.y=val;
Ok(())
});
fields.add_field_method_get("z",|_,this|Ok(this.0.z));
fields.add_field_method_set("z",|_,this,val|{
this.0.z=val;
Ok(())
});
}
impl mlua::UserData for Vector3 {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("magnitude", |_, this| Ok(this.0.length()));
fields.add_field_method_get("x", |_, this| Ok(this.0.x));
fields.add_field_method_set("x", |_, this, val| {
this.0.x = val;
Ok(())
});
fields.add_field_method_get("y", |_, this| Ok(this.0.y));
fields.add_field_method_set("y", |_, this, val| {
this.0.y = val;
Ok(())
});
fields.add_field_method_get("z", |_, this| Ok(this.0.z));
fields.add_field_method_set("z", |_, this, val| {
this.0.z = val;
Ok(())
});
}
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
//methods.add_method("area",|_,this,()| Ok(this.length * this.width));
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
//methods.add_method("area",|_,this,()| Ok(this.length * this.width));
methods.add_meta_function(mlua::MetaMethod::Add,|_,(this,val):(Self,Self)|Ok(Self(this.0+val.0)));
methods.add_meta_function(mlua::MetaMethod::Div,|_,(this,val):(Self,mlua::Value)|{
match val{
mlua::Value::Integer(n)=>Ok(Self(this.0/(n as f32))),
mlua::Value::Number(n)=>Ok(Self(this.0/(n as f32))),
mlua::Value::UserData(ud)=>{
let rhs:Vector3=ud.take()?;
Ok(Self(this.0/rhs.0))
},
other=>Err(mlua::Error::runtime(format!("Attempt to divide Vector3 by {other:?}"))),
}
});
methods.add_meta_function(mlua::MetaMethod::ToString,|_,this:Self|
Ok(format!("Vector3.new({},{},{})",
this.0.x,
this.0.y,
this.0.z,
))
);
}
methods.add_meta_function(mlua::MetaMethod::Add, |_, (this, val): (Self, Self)| {
Ok(Self(this.0 + val.0))
});
methods.add_meta_function(
mlua::MetaMethod::Div,
|_, (this, val): (Self, mlua::Value)| match val {
mlua::Value::Integer(n) => Ok(Self(this.0 / (n as f32))),
mlua::Value::Number(n) => Ok(Self(this.0 / (n as f32))),
mlua::Value::UserData(ud) => {
let rhs: Vector3 = ud.take()?;
Ok(Self(this.0 / rhs.0))
}
other => Err(mlua::Error::runtime(format!(
"Attempt to divide Vector3 by {other:?}"
))),
},
);
methods.add_meta_function(mlua::MetaMethod::ToString, |_, this: Self| {
Ok(format!(
"Vector3.new({},{},{})",
this.0.x, this.0.y, this.0.z,
))
});
}
}
type_from_lua_userdata!(Vector3);

View File

@ -1,106 +1,114 @@
pub use tick::Tick;
mod tick{
#[derive(Clone,Copy,Default,Hash,PartialEq,Eq,PartialOrd,Ord)]
pub struct Tick(u64);
impl std::ops::Add<u64> for Tick{
type Output=Self;
fn add(self,rhs:u64)->Self::Output{
Self(self.0+rhs)
}
}
impl std::ops::Sub<u64> for Tick{
type Output=Self;
fn sub(self,rhs:u64)->Self::Output{
Self(self.0-rhs)
}
}
impl std::ops::AddAssign<u64> for Tick{
fn add_assign(&mut self,rhs:u64){
self.0+=rhs;
}
}
impl std::ops::SubAssign<u64> for Tick{
fn sub_assign(&mut self,rhs:u64){
self.0-=rhs;
}
}
mod tick {
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Tick(u64);
impl std::ops::Add<u64> for Tick {
type Output = Self;
fn add(self, rhs: u64) -> Self::Output {
Self(self.0 + rhs)
}
}
impl std::ops::Sub<u64> for Tick {
type Output = Self;
fn sub(self, rhs: u64) -> Self::Output {
Self(self.0 - rhs)
}
}
impl std::ops::AddAssign<u64> for Tick {
fn add_assign(&mut self, rhs: u64) {
self.0 += rhs;
}
}
impl std::ops::SubAssign<u64> for Tick {
fn sub_assign(&mut self, rhs: u64) {
self.0 -= rhs;
}
}
}
#[derive(Default)]
pub struct Scheduler{
tick:Tick,
schedule:std::collections::HashMap<Tick,Vec<mlua::Thread>>,
pub struct Scheduler {
tick: Tick,
schedule: std::collections::HashMap<Tick, Vec<mlua::Thread>>,
}
impl Scheduler{
pub fn has_scheduled_threads(&self)->bool{
!self.schedule.is_empty()
}
pub fn schedule_thread(&mut self,delay:u64,thread:mlua::Thread){
self.schedule.entry(self.tick+delay.max(1))
.or_insert(Vec::new())
.push(thread);
}
pub fn tick_threads(&mut self)->Option<Vec<mlua::Thread>>{
self.tick+=1;
self.schedule.remove(&self.tick)
}
impl Scheduler {
pub fn has_scheduled_threads(&self) -> bool {
!self.schedule.is_empty()
}
pub fn schedule_thread(&mut self, delay: u64, thread: mlua::Thread) {
self.schedule
.entry(self.tick + delay.max(1))
.or_insert(Vec::new())
.push(thread);
}
pub fn tick_threads(&mut self) -> Option<Vec<mlua::Thread>> {
self.tick += 1;
self.schedule.remove(&self.tick)
}
}
pub fn scheduler_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut crate::scheduler::Scheduler)->mlua::Result<T>)->mlua::Result<T>{
let mut scheduler=lua.app_data_mut::<crate::scheduler::Scheduler>().ok_or_else(||mlua::Error::runtime("Scheduler missing"))?;
f(&mut *scheduler)
pub fn scheduler_mut<T>(
lua: &mlua::Lua,
mut f: impl FnMut(&mut crate::scheduler::Scheduler) -> mlua::Result<T>,
) -> mlua::Result<T> {
let mut scheduler = lua
.app_data_mut::<crate::scheduler::Scheduler>()
.ok_or_else(|| mlua::Error::runtime("Scheduler missing"))?;
f(&mut *scheduler)
}
fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{
let delay=match dt{
mlua::Value::Integer(i)=>i.max(0) as u64*60,
mlua::Value::Number(f)=>{
let delay=f.max(0.0)*60.0;
match delay.classify(){
std::num::FpCategory::Nan=>Err(mlua::Error::runtime("NaN"))?,
// cases where the number is too large to schedule
std::num::FpCategory::Infinite=>return Ok(()),
std::num::FpCategory::Normal=>if (u64::MAX as f64)<delay{
return Ok(());
},
_=>(),
}
delay as u64
},
mlua::Value::Nil=>0,
_=>Err(mlua::Error::runtime("Expected float"))?,
};
scheduler_mut(lua,|scheduler|{
scheduler.schedule_thread(delay.max(2),lua.current_thread());
Ok(())
})
fn schedule_thread(lua: &mlua::Lua, dt: mlua::Value) -> Result<(), mlua::Error> {
let delay = match dt {
mlua::Value::Integer(i) => i.max(0) as u64 * 60,
mlua::Value::Number(f) => {
let delay = f.max(0.0) * 60.0;
match delay.classify() {
std::num::FpCategory::Nan => Err(mlua::Error::runtime("NaN"))?,
// cases where the number is too large to schedule
std::num::FpCategory::Infinite => return Ok(()),
std::num::FpCategory::Normal => {
if (u64::MAX as f64) < delay {
return Ok(());
}
}
_ => (),
}
delay as u64
}
mlua::Value::Nil => 0,
_ => Err(mlua::Error::runtime("Expected float"))?,
};
scheduler_mut(lua, |scheduler| {
scheduler.schedule_thread(delay.max(2), lua.current_thread());
Ok(())
})
}
// This is used to avoid calling coroutine.yield from the rust side.
const LUA_WAIT:&str=
"local coroutine_yield=coroutine.yield
const LUA_WAIT: &str = "local coroutine_yield=coroutine.yield
local schedule_thread=schedule_thread
return function(dt)
schedule_thread(dt)
return coroutine_yield()
end";
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let coroutine_table=globals.get::<mlua::Table>("coroutine")?;
let schedule_thread=lua.create_function(schedule_thread)?;
pub fn set_globals(lua: &mlua::Lua, globals: &mlua::Table) -> Result<(), mlua::Error> {
let coroutine_table = globals.get::<mlua::Table>("coroutine")?;
let schedule_thread = lua.create_function(schedule_thread)?;
//create wait function environment
let wait_env=lua.create_table()?;
wait_env.raw_set("coroutine",coroutine_table)?;
wait_env.raw_set("schedule_thread",schedule_thread)?;
//create wait function environment
let wait_env = lua.create_table()?;
wait_env.raw_set("coroutine", coroutine_table)?;
wait_env.raw_set("schedule_thread", schedule_thread)?;
//construct wait function from Lua code
let wait=lua.load(LUA_WAIT)
.set_name("wait")
.set_environment(wait_env)
.call::<mlua::Function>(())?;
//construct wait function from Lua code
let wait = lua
.load(LUA_WAIT)
.set_name("wait")
.set_environment(wait_env)
.call::<mlua::Function>(())?;
globals.raw_set("wait",wait)?;
globals.raw_set("wait", wait)?;
Ok(())
Ok(())
}

View File

@ -0,0 +1 @@

View File

@ -1,11 +1,11 @@
use binrw::{BinReaderExt, binrw};
use binrw::{binrw, BinReaderExt};
#[derive(Debug)]
pub enum Error{
InvalidHeader,
InvalidSegment(binrw::Error),
InvalidSegmentId(SegmentId),
File(crate::file::Error),
pub enum Error {
InvalidHeader,
InvalidSegment(binrw::Error),
InvalidSegmentId(SegmentId),
File(crate::file::Error),
}
/* block types
@ -16,8 +16,8 @@ u128 map_resource_uuid //which map is this bot running
//blocks are laid out in chronological order, but indices may jump around.
u64 num_segments
for _ in 0..num_segments{
i64 time //simulation_state timestamp
u64 block_id
i64 time //simulation_state timestamp
u64 block_id
}
BLOCK_BOT_SEGMENT:
@ -25,74 +25,77 @@ BLOCK_BOT_SEGMENT:
SimulationState simulation_state //SimulationState is just under ClientState which includes Play/Pause events that the simulation doesn't know about.
//to read, greedily decode instructions until eof
loop{
//delta encode as much as possible (time,mousepos)
//strafe ticks are implied
//physics can be implied in an input-only bot file
TimedInstruction<SimulationInstruction> instruction
//delta encode as much as possible (time,mousepos)
//strafe ticks are implied
//physics can be implied in an input-only bot file
TimedInstruction<SimulationInstruction> instruction
}
*/
//error hiding mock code
mod simulation{
#[super::binrw]
#[brw(little)]
pub struct State{}
#[super::binrw]
#[brw(little)]
pub struct Instruction{}
mod simulation {
#[super::binrw]
#[brw(little)]
pub struct State {}
#[super::binrw]
#[brw(little)]
pub struct Instruction {}
}
// mod instruction{
// #[super::binrw]
// #[brw(little)]
// pub struct TimedInstruction<Instruction:binrw::BinRead+binrw::BinWrite>{
// time:u64,
// instruction:Instruction
// }
// #[super::binrw]
// #[brw(little)]
// pub struct TimedInstruction<Instruction:binrw::BinRead+binrw::BinWrite>{
// time:u64,
// instruction:Instruction
// }
// }
// mod timeline{
// #[super::binrw]
// #[brw(little)]
// pub struct Timeline<Instruction:binrw::BinRead+binrw::BinWrite>{
// #[bw(try_calc(u32::try_from(instructions.len())))]
// instruction_count:u32,
// #[br(count=instruction_count)]
// instructions:Vec<super::instruction::TimedInstruction<Instruction>>
// }
// #[super::binrw]
// #[brw(little)]
// pub struct Timeline<Instruction:binrw::BinRead+binrw::BinWrite>{
// #[bw(try_calc(u32::try_from(instructions.len())))]
// instruction_count:u32,
// #[br(count=instruction_count)]
// instructions:Vec<super::instruction::TimedInstruction<Instruction>>
// }
// }
//serious code
#[binrw]
#[brw(little)]
#[derive(Clone,Copy,Debug,id::Id)]
#[derive(Clone, Copy, Debug, id::Id)]
pub struct SegmentId(u32);
#[binrw]
#[brw(little)]
pub struct Segment{
state:simulation::State,
//#[bw(try_calc(u32::try_from(instructions.len())))]
//instruction_count:u32,
//#[br(count=instruction_count)]
//instructions:Vec<instruction::TimedInstruction<simulation::Instruction>>
pub struct Segment {
state: simulation::State,
//#[bw(try_calc(u32::try_from(instructions.len())))]
//instruction_count:u32,
//#[br(count=instruction_count)]
//instructions:Vec<instruction::TimedInstruction<simulation::Instruction>>
//please remember that strafe ticks are implicit! 33% smaller bot files
//please remember that strafe ticks are implicit! 33% smaller bot files
}
pub struct StreamableBot<R:BinReaderExt>{
file:crate::file::File<R>,
//timeline:timeline::Timeline<SegmentId>,
segment_id_to_block_id:Vec<crate::file::BlockId>,
pub struct StreamableBot<R: BinReaderExt> {
file: crate::file::File<R>,
//timeline:timeline::Timeline<SegmentId>,
segment_id_to_block_id: Vec<crate::file::BlockId>,
}
impl<R:BinReaderExt> StreamableBot<R>{
pub(crate) fn new(file:crate::file::File<R>)->Result<Self,Error>{
Err(Error::InvalidHeader)
}
pub fn load_segment(&mut self,segment_id:SegmentId)->Result<Segment,Error>{
let block_id=*self.segment_id_to_block_id.get(segment_id.get() as usize).ok_or(Error::InvalidSegmentId(segment_id))?;
let mut block=self.file.block_reader(block_id).map_err(Error::File)?;
let segment=block.read_le().map_err(Error::InvalidSegment)?;
Ok(segment)
}
impl<R: BinReaderExt> StreamableBot<R> {
pub(crate) fn new(file: crate::file::File<R>) -> Result<Self, Error> {
Err(Error::InvalidHeader)
}
pub fn load_segment(&mut self, segment_id: SegmentId) -> Result<Segment, Error> {
let block_id = *self
.segment_id_to_block_id
.get(segment_id.get() as usize)
.ok_or(Error::InvalidSegmentId(segment_id))?;
let mut block = self.file.block_reader(block_id).map_err(Error::File)?;
let segment = block.read_le().map_err(Error::InvalidSegment)?;
Ok(segment)
}
}

View File

@ -1,23 +1,23 @@
use binrw::BinReaderExt;
#[derive(Debug)]
pub enum Error{
InvalidHeader,
pub enum Error {
InvalidHeader,
}
/*
BLOCK_DEMO_HEADER:
u32 num_maps
for map_id in 0..num_maps{
i64 simulation_time
u128 map_resource_id
u64 map_header_block_id
i64 simulation_time
u128 map_resource_id
u64 map_header_block_id
}
u32 num_bots
for bot_id in 0..num_bots{
i64 simulation_time
u128 bot_resource_id
u64 bot_header_block_id
i64 simulation_time
u128 bot_resource_id
u64 bot_header_block_id
}
//map loading timeline
@ -28,12 +28,12 @@ how to do worldstate for deathrun!?
*/
pub struct StreamableDemo<R:BinReaderExt>{
map:Vec<crate::map::StreamableMap<R>>,
bots:Vec<crate::bot::StreamableBot<R>>,
pub struct StreamableDemo<R: BinReaderExt> {
map: Vec<crate::map::StreamableMap<R>>,
bots: Vec<crate::bot::StreamableBot<R>>,
}
impl<R:BinReaderExt> StreamableDemo<R>{
pub(crate) fn new(file:crate::file::File<R>)->Result<Self,Error>{
Err(Error::InvalidHeader)
}
impl<R: BinReaderExt> StreamableDemo<R> {
pub(crate) fn new(file: crate::file::File<R>) -> Result<Self, Error> {
Err(Error::InvalidHeader)
}
}

View File

@ -1,20 +1,20 @@
//file format "sniff"
use binrw::{binrw,BinReaderExt,io::TakeSeekExt};
use binrw::{binrw, io::TakeSeekExt, BinReaderExt};
#[derive(Debug)]
pub enum Error{
InvalidHeader(binrw::Error),
UnexpectedEOF,
InvalidBlockId(BlockId),
Seek(std::io::Error),
pub enum Error {
InvalidHeader(binrw::Error),
UnexpectedEOF,
InvalidBlockId(BlockId),
Seek(std::io::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
/* spec
@ -24,18 +24,18 @@ impl std::error::Error for Error{}
b"SNFB"
u32 format_version
u64 priming_bytes
//how many bytes of the file must be read to guarantee all of the expected
//format-specific metadata is available to facilitate streaming the remaining contents
//used by the database to guarantee that it serves at least the bare minimum
//how many bytes of the file must be read to guarantee all of the expected
//format-specific metadata is available to facilitate streaming the remaining contents
//used by the database to guarantee that it serves at least the bare minimum
u128 resource_uuid
//identifies the file from anywhere for any other file
//identifies the file from anywhere for any other file
//global block layout (variable size)
u64 num_blocks
//the start of the first block is implicitly after the global header (32)
//num_blocks+1 used in Header.block_location is implicitly the end of the file
for block_id in 1..num_blocks{
u64 first_byte
u64 first_byte
}
//end global header
@ -47,64 +47,69 @@ for block_id in 1..num_blocks{
*/
#[binrw]
#[brw(little)]
#[derive(Clone,Copy,Debug)]
pub(crate) enum FourCC{
#[brw(magic=b"SNFM")]
Map,
#[brw(magic=b"SNFB")]
Bot,
#[brw(magic=b"SNFD")]
Demo,
#[derive(Clone, Copy, Debug)]
pub(crate) enum FourCC {
#[brw(magic = b"SNFM")]
Map,
#[brw(magic = b"SNFB")]
Bot,
#[brw(magic = b"SNFD")]
Demo,
}
#[binrw]
#[brw(little)]
#[derive(Debug)]
pub struct Header{
/// Type of file
pub fourcc:FourCC,
/// File format version
pub version:u32,
/// Minimum data required to know the location of all streamable resources for this specific file
pub priming:u64,
/// uuid for this file
pub resource:u128,
//don't need try_calc: the struct will force field initialization anyways and it can be calculated there
pub block_count:u32,
#[br(count=block_count+1)]
pub block_location:Vec<u64>,
pub struct Header {
/// Type of file
pub fourcc: FourCC,
/// File format version
pub version: u32,
/// Minimum data required to know the location of all streamable resources for this specific file
pub priming: u64,
/// uuid for this file
pub resource: u128,
//don't need try_calc: the struct will force field initialization anyways and it can be calculated there
pub block_count: u32,
#[br(count=block_count+1)]
pub block_location: Vec<u64>,
}
#[binrw]
#[brw(little)]
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,Ord,PartialEq,PartialOrd)]
#[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,
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(){
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
}
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() {
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
}
}

View File

@ -2,67 +2,66 @@ use binrw::BinReaderExt;
mod newtypes;
mod file;
pub mod map;
pub mod bot;
pub mod demo;
mod file;
pub mod map;
#[derive(Debug)]
pub enum Error{
UnexpectedFourCC,
Header(file::Error),
Map(map::Error),
Bot(bot::Error),
Demo(demo::Error),
pub enum Error {
UnexpectedFourCC,
Header(file::Error),
Map(map::Error),
Bot(bot::Error),
Demo(demo::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
pub enum SNF<R:BinReaderExt>{
Map(map::StreamableMap<R>),
Bot(bot::StreamableBot<R>),
Demo(demo::StreamableDemo<R>),
pub enum SNF<R: BinReaderExt> {
Map(map::StreamableMap<R>),
Bot(bot::StreamableBot<R>),
Demo(demo::StreamableDemo<R>),
}
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)?),
file::FourCC::Demo=>SNF::Demo(demo::StreamableDemo::new(file).map_err(Error::Demo)?),
})
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)?),
file::FourCC::Demo => SNF::Demo(demo::StreamableDemo::new(file).map_err(Error::Demo)?),
})
}
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>(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_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>(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_demo<R:BinReaderExt>(input:R)->Result<demo::StreamableDemo<R>,Error>{
let file=file::File::new(input).map_err(Error::Header)?;
match file.fourcc(){
file::FourCC::Demo=>Ok(demo::StreamableDemo::new(file).map_err(Error::Demo)?),
_=>Err(Error::UnexpectedFourCC)
}
pub fn read_demo<R: BinReaderExt>(input: R) -> Result<demo::StreamableDemo<R>, Error> {
let file = file::File::new(input).map_err(Error::Header)?;
match file.fourcc() {
file::FourCC::Demo => Ok(demo::StreamableDemo::new(file).map_err(Error::Demo)?),
_ => Err(Error::UnexpectedFourCC),
}
}
#[cfg(test)]
mod tests {
//use super::*;
//use super::*;
#[test]
fn test() {
}
#[test]
fn test() {}
}

View File

@ -1,32 +1,32 @@
use std::io::Read;
use std::collections::HashMap;
use std::io::Read;
use crate::newtypes;
use crate::file::BlockId;
use binrw::{binrw,BinReaderExt,BinWriterExt};
use strafesnet_common::model;
use crate::newtypes;
use binrw::{binrw, BinReaderExt, BinWriterExt};
use strafesnet_common::aabb::Aabb;
use strafesnet_common::bvh::BvhNode;
use strafesnet_common::gameplay_modes;
use strafesnet_common::model;
#[derive(Debug)]
pub enum Error{
InvalidHeader(binrw::Error),
InvalidMode(newtypes::gameplay_modes::ModeError),
InvalidBlockId(BlockId),
InvalidMeshId(model::MeshId),
InvalidModelId(model::ModelId),
InvalidTextureId(model::TextureId),
InvalidData(binrw::Error),
IO(std::io::Error),
File(crate::file::Error),
pub enum Error {
InvalidHeader(binrw::Error),
InvalidMode(newtypes::gameplay_modes::ModeError),
InvalidBlockId(BlockId),
InvalidMeshId(model::MeshId),
InvalidModelId(model::ModelId),
InvalidTextureId(model::TextureId),
InvalidData(binrw::Error),
IO(std::io::Error),
File(crate::file::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for Error {}
/* block types
@ -39,7 +39,7 @@ u32 num_resource_blocks
u32 num_resources_external
//node 0 parent node is implied to be None
for node_id in 1..num_nodes{
u32 parent_node
u32 parent_node
}
//NOTE: alternate realities are not necessary.
@ -49,36 +49,36 @@ for node_id in 1..num_nodes{
//ideally spacial blocks are sorted from distance to start zone
//texture blocks are inserted before the first spacial block they are used in
for spacial_block_id in 0..num_spacial_blocks{
u32 node_id
u32 block_id //data block
Aabb extents
u32 node_id
u32 block_id //data block
Aabb extents
}
//the order of these lists uniquely generates the incremental Ids
//MeshId, TextureId etc. based on resource type
//the first 8 bits of resource_uuid describe the type (mesh, texture, etc)
//if the map file references only external resources, num_resource_blocks = 0
for resource_idx in 0..num_resource_blocks{
Resource resource_type
u32 block_id
Resource resource_type
u32 block_id
}
for resource_idx in 0..num_resources_external{
u128 resource_uuid
u128 resource_uuid
}
BLOCK_MAP_RESOURCE:
Resource resource_type
//an individual one of the following:
- model (Mesh)
- shader (compiled SPIR-V)
- image (JpegXL)
- sound (Opus)
- video (AV1)
- animation (Trey thing)
- model (Mesh)
- shader (compiled SPIR-V)
- image (JpegXL)
- sound (Opus)
- video (AV1)
- animation (Trey thing)
BLOCK_MAP_REGION:
u64 num_models
for model_id in 0..num_models{
ModelInstance mode_instance
ModelInstance mode_instance
}
*/
@ -86,359 +86,422 @@ for model_id in 0..num_models{
//if you hash the resource itself and set the first 8 bits to this, that's the resource uuid
#[binrw]
#[brw(little,repr=u8)]
enum ResourceType{
Mesh,
Texture,
//Shader,
//Sound,
//Video,
//Animation,
enum ResourceType {
Mesh,
Texture,
//Shader,
//Sound,
//Video,
//Animation,
}
const RESOURCE_TYPE_VARIANT_COUNT:u8=2;
const RESOURCE_TYPE_VARIANT_COUNT: u8 = 2;
#[binrw]
#[brw(little)]
struct ResourceId(u128);
impl ResourceId{
fn resource_type(&self)->Option<ResourceType>{
let discriminant=self.0 as u8;
//TODO: use this when it is stabilized https://github.com/rust-lang/rust/issues/73662
//if (discriminant as usize)<std::mem::variant_count::<ResourceType>(){
match discriminant<RESOURCE_TYPE_VARIANT_COUNT{
true=>Some(unsafe{std::mem::transmute::<u8,ResourceType>(discriminant)}),
false=>None,
}
}
impl ResourceId {
fn resource_type(&self) -> Option<ResourceType> {
let discriminant = self.0 as u8;
//TODO: use this when it is stabilized https://github.com/rust-lang/rust/issues/73662
//if (discriminant as usize)<std::mem::variant_count::<ResourceType>(){
match discriminant < RESOURCE_TYPE_VARIANT_COUNT {
true => Some(unsafe { std::mem::transmute::<u8, ResourceType>(discriminant) }),
false => None,
}
}
}
struct ResourceMap<T>{
meshes:HashMap<strafesnet_common::model::MeshId,T>,
textures:HashMap<strafesnet_common::model::TextureId,T>,
struct ResourceMap<T> {
meshes: HashMap<strafesnet_common::model::MeshId, T>,
textures: HashMap<strafesnet_common::model::TextureId, T>,
}
impl<T> Default for ResourceMap<T>{
fn default()->Self{
Self{
meshes:HashMap::new(),
textures:HashMap::new(),
}
}
impl<T> Default for ResourceMap<T> {
fn default() -> Self {
Self {
meshes: HashMap::new(),
textures: HashMap::new(),
}
}
}
#[binrw]
#[brw(little)]
struct SpacialBlockHeader{
id:BlockId,
extents:newtypes::aabb::Aabb,
struct SpacialBlockHeader {
id: BlockId,
extents: newtypes::aabb::Aabb,
}
#[binrw]
#[brw(little)]
struct ResourceBlockHeader{
resource:ResourceType,
id:BlockId,
struct ResourceBlockHeader {
resource: ResourceType,
id: BlockId,
}
#[binrw]
#[brw(little)]
struct ResourceExternalHeader{
resource_uuid:ResourceId,
struct ResourceExternalHeader {
resource_uuid: ResourceId,
}
#[binrw]
#[brw(little)]
struct MapHeader{
num_spacial_blocks:u32,
num_resource_blocks:u32,
//num_resources_external:u32,
num_modes:u32,
num_attributes:u32,
num_render_configs:u32,
#[br(count=num_spacial_blocks)]
spacial_blocks:Vec<SpacialBlockHeader>,
#[br(count=num_resource_blocks)]
resource_blocks:Vec<ResourceBlockHeader>,
//#[br(count=num_resources_external)]
//external_resources:Vec<ResourceExternalHeader>,
#[br(count=num_modes)]
modes:Vec<newtypes::gameplay_modes::Mode>,
#[br(count=num_attributes)]
attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>,
#[br(count=num_render_configs)]
render_configs:Vec<newtypes::model::RenderConfig>,
struct MapHeader {
num_spacial_blocks: u32,
num_resource_blocks: u32,
//num_resources_external:u32,
num_modes: u32,
num_attributes: u32,
num_render_configs: u32,
#[br(count=num_spacial_blocks)]
spacial_blocks: Vec<SpacialBlockHeader>,
#[br(count=num_resource_blocks)]
resource_blocks: Vec<ResourceBlockHeader>,
//#[br(count=num_resources_external)]
//external_resources:Vec<ResourceExternalHeader>,
#[br(count=num_modes)]
modes: Vec<newtypes::gameplay_modes::Mode>,
#[br(count=num_attributes)]
attributes: Vec<newtypes::gameplay_attributes::CollisionAttributes>,
#[br(count=num_render_configs)]
render_configs: Vec<newtypes::model::RenderConfig>,
}
#[binrw]
#[brw(little)]
struct Region{
//consider including a bvh in the region instead of needing to rebalance the physics bvh on the fly
model_count:u32,
#[br(count=model_count)]
models:Vec<(u32,newtypes::model::Model)>,
struct Region {
//consider including a bvh in the region instead of needing to rebalance the physics bvh on the fly
model_count: u32,
#[br(count=model_count)]
models: Vec<(u32, newtypes::model::Model)>,
}
//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>{
//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)?;
Ok(region.models.into_iter().map(|(model_id,model)|
(model::ModelId::new(model_id),model.into())
).collect())
fn read_region<R: BinReaderExt>(
file: &mut crate::file::File<R>,
block_id: BlockId,
) -> 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)?;
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)?;
Ok(mesh.into())
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)?;
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)?;
let mut texture=Vec::new();
block.read_to_end(&mut texture).map_err(Error::IO)?;
Ok(texture)
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)?;
let mut texture = Vec::new();
block.read_to_end(&mut texture).map_err(Error::IO)?;
Ok(texture)
}
pub struct StreamableMap<R:BinReaderExt>{
file:crate::file::File<R>,
//this includes every platform... move the unconstrained datas to their appropriate data block?
modes:gameplay_modes::Modes,
//this is every possible attribute... need some sort of streaming system
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
//this is every possible render configuration... shaders and such... need streaming
render_configs:Vec<strafesnet_common::model::RenderConfig>,
//this makes sense to keep in memory for streaming, a map of which blocks occupy what space
bvh:BvhNode<BlockId>,
//something something resources hashmaps
resource_blocks:ResourceMap<BlockId>,
//resource_external:ResourceMap<ResourceId>,
pub struct StreamableMap<R: BinReaderExt> {
file: crate::file::File<R>,
//this includes every platform... move the unconstrained datas to their appropriate data block?
modes: gameplay_modes::Modes,
//this is every possible attribute... need some sort of streaming system
attributes: Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
//this is every possible render configuration... shaders and such... need streaming
render_configs: Vec<strafesnet_common::model::RenderConfig>,
//this makes sense to keep in memory for streaming, a map of which blocks occupy what space
bvh: BvhNode<BlockId>,
//something something resources hashmaps
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>{
//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 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();
let bvh=header.spacial_blocks.into_iter().map(|spacial_block|
(spacial_block.id,spacial_block.extents.into())
).collect();
//generate mesh ids and texture ids from resource list order
let mut resource_blocks=ResourceMap::default();
for resource_block_header in header.resource_blocks{
match resource_block_header.resource{
ResourceType::Mesh=>{
resource_blocks.meshes.insert(
//generate the id from the current length
model::MeshId::new(resource_blocks.meshes.len() as u32),
resource_block_header.id
);
},
ResourceType::Texture=>{
resource_blocks.textures.insert(
model::TextureId::new(resource_blocks.textures.len() as u32),
resource_block_header.id
);
},
}
}
Ok(Self{
file,
modes:strafesnet_common::gameplay_modes::Modes::new(modes),
attributes,
render_configs,
bvh:strafesnet_common::bvh::generate_bvh(bvh),
resource_blocks,
//resource_external:Default::default(),
})
}
pub fn get_intersecting_region_block_ids(&self,aabb:&Aabb)->Vec<BlockId>{
let mut block_ids=Vec::new();
self.bvh.the_tester(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 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 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>{
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
block_ids.sort_unstable();
//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 mut models=Vec::with_capacity(model_pairs.len());
for model_id in 0..model_pairs.len() as u32{
let model_id=model::ModelId::new(model_id);
models.push(model_pairs.remove(&model_id).ok_or(Error::InvalidModelId(model_id))?);
}
//load all meshes
let mut meshes=Vec::with_capacity(self.resource_blocks.meshes.len());
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)?);
};
//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)?);
}
Ok(strafesnet_common::map::CompleteMap{
modes:self.modes,
attributes:self.attributes,
meshes,
models,
textures,
render_configs:self.render_configs,
})
}
impl<R: BinReaderExt> StreamableMap<R> {
pub(crate) fn new(mut file: crate::file::File<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 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();
let bvh = header
.spacial_blocks
.into_iter()
.map(|spacial_block| (spacial_block.id, spacial_block.extents.into()))
.collect();
//generate mesh ids and texture ids from resource list order
let mut resource_blocks = ResourceMap::default();
for resource_block_header in header.resource_blocks {
match resource_block_header.resource {
ResourceType::Mesh => {
resource_blocks.meshes.insert(
//generate the id from the current length
model::MeshId::new(resource_blocks.meshes.len() as u32),
resource_block_header.id,
);
}
ResourceType::Texture => {
resource_blocks.textures.insert(
model::TextureId::new(resource_blocks.textures.len() as u32),
resource_block_header.id,
);
}
}
}
Ok(Self {
file,
modes: strafesnet_common::gameplay_modes::Modes::new(modes),
attributes,
render_configs,
bvh: strafesnet_common::bvh::generate_bvh(bvh),
resource_blocks,
//resource_external:Default::default(),
})
}
pub fn get_intersecting_region_block_ids(&self, aabb: &Aabb) -> Vec<BlockId> {
let mut block_ids = Vec::new();
self.bvh
.the_tester(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 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 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> {
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
block_ids.sort_unstable();
//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 mut models = Vec::with_capacity(model_pairs.len());
for model_id in 0..model_pairs.len() as u32 {
let model_id = model::ModelId::new(model_id);
models.push(
model_pairs
.remove(&model_id)
.ok_or(Error::InvalidModelId(model_id))?,
);
}
//load all meshes
let mut meshes = Vec::with_capacity(self.resource_blocks.meshes.len());
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)?);
}
//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)?);
}
Ok(strafesnet_common::map::CompleteMap {
modes: self.modes,
attributes: self.attributes,
meshes,
models,
textures,
render_configs: self.render_configs,
})
}
}
const BVH_NODE_MAX_WEIGHT:usize=64*1024;//64 kB
const BVH_NODE_MAX_WEIGHT: usize = 64 * 1024; //64 kB
fn collect_spacial_blocks(
block_location:&mut Vec<u64>,
block_headers:&mut Vec<SpacialBlockHeader>,
sequential_block_data:&mut std::io::Cursor<&mut Vec<u8>>,
bvh_node:strafesnet_common::bvh::BvhWeightNode<usize,(model::ModelId,newtypes::model::Model)>
)->Result<(),Error>{
//inspect the node weights top-down.
//When a node weighs less than the limit,
//serialize its entire contents into a region block
if *bvh_node.weight()<BVH_NODE_MAX_WEIGHT{
let mut models=Vec::new();
let mut model_count=0;
let extents=bvh_node.aabb().clone().into();
bvh_node.into_visitor(&mut |(model_id,model)|{
model_count+=1;
models.push((model_id.get(),model));
});
let id=BlockId::new(block_headers.len() as u32+1);
block_headers.push(SpacialBlockHeader{
id,
extents,
});
let region=Region{
model_count,
models,
};
binrw::BinWrite::write_le(&region,sequential_block_data).map_err(Error::InvalidData)?;
block_location.push(sequential_block_data.position());
}else{
match bvh_node.into_content(){
strafesnet_common::bvh::RecursiveContent::Branch(bvh_node_list)=>{
for bvh_node in bvh_node_list{
collect_spacial_blocks(block_location,block_headers,sequential_block_data,bvh_node)?;
}
},
strafesnet_common::bvh::RecursiveContent::Leaf(_)=>panic!(),//bvh branches are 20 leaves minimum
}
}
Ok(())
block_location: &mut Vec<u64>,
block_headers: &mut Vec<SpacialBlockHeader>,
sequential_block_data: &mut std::io::Cursor<&mut Vec<u8>>,
bvh_node: strafesnet_common::bvh::BvhWeightNode<
usize,
(model::ModelId, newtypes::model::Model),
>,
) -> Result<(), Error> {
//inspect the node weights top-down.
//When a node weighs less than the limit,
//serialize its entire contents into a region block
if *bvh_node.weight() < BVH_NODE_MAX_WEIGHT {
let mut models = Vec::new();
let mut model_count = 0;
let extents = bvh_node.aabb().clone().into();
bvh_node.into_visitor(&mut |(model_id, model)| {
model_count += 1;
models.push((model_id.get(), model));
});
let id = BlockId::new(block_headers.len() as u32 + 1);
block_headers.push(SpacialBlockHeader { id, extents });
let region = Region {
model_count,
models,
};
binrw::BinWrite::write_le(&region, sequential_block_data).map_err(Error::InvalidData)?;
block_location.push(sequential_block_data.position());
} else {
match bvh_node.into_content() {
strafesnet_common::bvh::RecursiveContent::Branch(bvh_node_list) => {
for bvh_node in bvh_node_list {
collect_spacial_blocks(
block_location,
block_headers,
sequential_block_data,
bvh_node,
)?;
}
}
strafesnet_common::bvh::RecursiveContent::Leaf(_) => panic!(), //bvh branches are 20 leaves minimum
}
}
Ok(())
}
/// TODO: Optionally provide a bot that describes the path through the map
/// otherwise sort by distance to start zone
pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::CompleteMap)->Result<(),Error>{
//serialize models and make a bvh that knows the file size of the branch
let boxen=map.models.into_iter().enumerate().map(|(model_id,model)|{
//grow your own aabb
let mesh=map.meshes.get(model.mesh.get() as usize).ok_or(Error::InvalidMeshId(model.mesh))?;
let mut aabb=strafesnet_common::aabb::Aabb::default();
for &pos in &mesh.unique_pos{
aabb.grow(model.transform.transform_point3(pos).fix_1());
}
Ok(((model::ModelId::new(model_id as u32),model.into()),aabb))
}).collect::<Result<Vec<_>,_>>()?;
let bvh=strafesnet_common::bvh::generate_bvh(boxen).weigh_contents(&|_|std::mem::size_of::<newtypes::model::Model>());
//build blocks
//block location is initialized with two values
//the first value represents the location of the first byte after the file header
//the second value represents the first byte of the second data block
//the first data block is always the map header, so the difference is the map header size.
//this information is filled in later after the sizes are known.
let mut block_location=vec![0,0];//for file header
let mut spacial_blocks=Vec::new();//for map header
let mut sequential_block_data=Vec::new();
let mut cursor_to_data=std::io::Cursor::new(&mut sequential_block_data);
collect_spacial_blocks(&mut block_location,&mut spacial_blocks,&mut cursor_to_data,bvh)?;
let mut block_count=spacial_blocks.len() as u32+1;//continue block id
let mut resource_blocks=Vec::new();//for map header
//meshes
for mesh in map.meshes.into_iter(){
resource_blocks.push(ResourceBlockHeader{
resource:ResourceType::Mesh,
id:BlockId::new(block_count),
});
block_count+=1;
let serializable_mesh:newtypes::model::Mesh=mesh.into();
binrw::BinWrite::write_le(&serializable_mesh,&mut cursor_to_data).map_err(Error::InvalidData)?;
block_location.push(cursor_to_data.position());
}
//textures
for mut texture in map.textures.into_iter(){
resource_blocks.push(ResourceBlockHeader{
resource:ResourceType::Texture,
id:BlockId::new(block_count),
});
block_count+=1;
sequential_block_data.append(&mut texture);
block_location.push(sequential_block_data.len() as u64);
}
//build header
let map_header=MapHeader{
num_spacial_blocks:spacial_blocks.len() as u32,
num_resource_blocks:resource_blocks.len() as u32,
//num_resources_external:0,
num_modes:map.modes.modes.len() as u32,
num_attributes:map.attributes.len() as u32,
num_render_configs:map.render_configs.len() as u32,
spacial_blocks,
resource_blocks,
//external_resources:Vec::new(),
modes:map.modes.modes.into_iter().map(Into::into).collect(),
attributes:map.attributes.into_iter().map(Into::into).collect(),
render_configs:map.render_configs.into_iter().map(Into::into).collect(),
};
let mut file_header=crate::file::Header{
fourcc:crate::file::FourCC::Map,
version:0,
priming:0,
resource:0,
block_count,
block_location,
};
//probe header length
let mut file_header_data=Vec::new();
binrw::BinWrite::write_le(&file_header,&mut std::io::Cursor::new(&mut file_header_data)).map_err(Error::InvalidData)?;
let mut map_header_data=Vec::new();
binrw::BinWrite::write_le(&map_header,&mut std::io::Cursor::new(&mut map_header_data)).map_err(Error::InvalidData)?;
pub fn write_map<W: BinWriterExt>(
mut writer: W,
map: strafesnet_common::map::CompleteMap,
) -> Result<(), Error> {
//serialize models and make a bvh that knows the file size of the branch
let boxen = map
.models
.into_iter()
.enumerate()
.map(|(model_id, model)| {
//grow your own aabb
let mesh = map
.meshes
.get(model.mesh.get() as usize)
.ok_or(Error::InvalidMeshId(model.mesh))?;
let mut aabb = strafesnet_common::aabb::Aabb::default();
for &pos in &mesh.unique_pos {
aabb.grow(model.transform.transform_point3(pos).fix_1());
}
Ok(((model::ModelId::new(model_id as u32), model.into()), aabb))
})
.collect::<Result<Vec<_>, _>>()?;
let bvh = strafesnet_common::bvh::generate_bvh(boxen)
.weigh_contents(&|_| std::mem::size_of::<newtypes::model::Model>());
//build blocks
//block location is initialized with two values
//the first value represents the location of the first byte after the file header
//the second value represents the first byte of the second data block
//the first data block is always the map header, so the difference is the map header size.
//this information is filled in later after the sizes are known.
let mut block_location = vec![0, 0]; //for file header
let mut spacial_blocks = Vec::new(); //for map header
let mut sequential_block_data = Vec::new();
let mut cursor_to_data = std::io::Cursor::new(&mut sequential_block_data);
collect_spacial_blocks(
&mut block_location,
&mut spacial_blocks,
&mut cursor_to_data,
bvh,
)?;
let mut block_count = spacial_blocks.len() as u32 + 1; //continue block id
let mut resource_blocks = Vec::new(); //for map header
//meshes
for mesh in map.meshes.into_iter() {
resource_blocks.push(ResourceBlockHeader {
resource: ResourceType::Mesh,
id: BlockId::new(block_count),
});
block_count += 1;
let serializable_mesh: newtypes::model::Mesh = mesh.into();
binrw::BinWrite::write_le(&serializable_mesh, &mut cursor_to_data)
.map_err(Error::InvalidData)?;
block_location.push(cursor_to_data.position());
}
//textures
for mut texture in map.textures.into_iter() {
resource_blocks.push(ResourceBlockHeader {
resource: ResourceType::Texture,
id: BlockId::new(block_count),
});
block_count += 1;
sequential_block_data.append(&mut texture);
block_location.push(sequential_block_data.len() as u64);
}
//build header
let map_header = MapHeader {
num_spacial_blocks: spacial_blocks.len() as u32,
num_resource_blocks: resource_blocks.len() as u32,
//num_resources_external:0,
num_modes: map.modes.modes.len() as u32,
num_attributes: map.attributes.len() as u32,
num_render_configs: map.render_configs.len() as u32,
spacial_blocks,
resource_blocks,
//external_resources:Vec::new(),
modes: map.modes.modes.into_iter().map(Into::into).collect(),
attributes: map.attributes.into_iter().map(Into::into).collect(),
render_configs: map.render_configs.into_iter().map(Into::into).collect(),
};
let mut file_header = crate::file::Header {
fourcc: crate::file::FourCC::Map,
version: 0,
priming: 0,
resource: 0,
block_count,
block_location,
};
//probe header length
let mut file_header_data = Vec::new();
binrw::BinWrite::write_le(
&file_header,
&mut std::io::Cursor::new(&mut file_header_data),
)
.map_err(Error::InvalidData)?;
let mut map_header_data = Vec::new();
binrw::BinWrite::write_le(&map_header, &mut std::io::Cursor::new(&mut map_header_data))
.map_err(Error::InvalidData)?;
//update file header according to probe data
let mut offset=file_header_data.len() as u64;
file_header.priming=offset;
file_header.block_location[0]=offset;
offset+=map_header_data.len() as u64;
for position in &mut file_header.block_location[1..]{
*position+=offset;
}
//update file header according to probe data
let mut offset = file_header_data.len() as u64;
file_header.priming = offset;
file_header.block_location[0] = offset;
offset += map_header_data.len() as u64;
for position in &mut file_header.block_location[1..] {
*position += offset;
}
//write (updated) file header
writer.write_le(&file_header).map_err(Error::InvalidData)?;
//write map header
writer.write(&map_header_data).map_err(Error::IO)?;
//write blocks
writer.write(&sequential_block_data).map_err(Error::IO)?;
Ok(())
//write (updated) file header
writer.write_le(&file_header).map_err(Error::InvalidData)?;
//write map header
writer.write(&map_header_data).map_err(Error::IO)?;
//write blocks
writer.write(&sequential_block_data).map_err(Error::IO)?;
Ok(())
}

View File

@ -1,7 +1,7 @@
mod common;
pub mod aabb;
pub mod model;
pub mod integer;
mod common;
pub mod gameplay_attributes;
pub mod gameplay_modes;
pub mod gameplay_style;
pub mod gameplay_attributes;
pub mod integer;
pub mod model;

View File

@ -1,23 +1,23 @@
use super::integer::Planar64Vec3;
#[binrw::binrw]
#[brw(little)]
pub struct Aabb{
pub min:Planar64Vec3,
pub max:Planar64Vec3,
pub struct Aabb {
pub min: Planar64Vec3,
pub max: Planar64Vec3,
}
impl Into<strafesnet_common::aabb::Aabb> for Aabb{
fn into(self)->strafesnet_common::aabb::Aabb{
strafesnet_common::aabb::Aabb::new(
strafesnet_common::integer::vec3::raw_array(self.min),
strafesnet_common::integer::vec3::raw_array(self.max),
)
}
impl Into<strafesnet_common::aabb::Aabb> for Aabb {
fn into(self) -> strafesnet_common::aabb::Aabb {
strafesnet_common::aabb::Aabb::new(
strafesnet_common::integer::vec3::raw_array(self.min),
strafesnet_common::integer::vec3::raw_array(self.max),
)
}
}
impl From<strafesnet_common::aabb::Aabb> for Aabb{
fn from(value:strafesnet_common::aabb::Aabb)->Self{
Self{
max:value.max().map(|t|t.to_raw()).to_array(),
min:value.min().map(|t|t.to_raw()).to_array(),
}
}
impl From<strafesnet_common::aabb::Aabb> for Aabb {
fn from(value: strafesnet_common::aabb::Aabb) -> Self {
Self {
max: value.max().map(|t| t.to_raw()).to_array(),
min: value.min().map(|t| t.to_raw()).to_array(),
}
}
}

View File

@ -1,3 +1,3 @@
pub const fn flag(b:bool,mask:u8)->u8{
(-(b as i8) as u8)&mask
pub const fn flag(b: bool, mask: u8) -> u8 {
(-(b as i8) as u8) & mask
}

View File

@ -1,493 +1,541 @@
use super::common::flag;
use super::integer::{Time,Planar64,Planar64Vec3};
use super::integer::{Planar64, Planar64Vec3, Time};
#[binrw::binrw]
#[brw(little)]
pub struct ContactingLadder{
#[br(map=|paused:u8|paused!=0)]
#[bw(map=|paused:&bool|*paused as u8)]
pub sticky:bool,
pub struct ContactingLadder {
#[br(map=|paused:u8|paused!=0)]
#[bw(map=|paused:&bool|*paused as u8)]
pub sticky: bool,
}
impl Into<strafesnet_common::gameplay_attributes::ContactingLadder> for ContactingLadder{
fn into(self)->strafesnet_common::gameplay_attributes::ContactingLadder{
strafesnet_common::gameplay_attributes::ContactingLadder{
sticky:self.sticky.into(),
}
}
impl Into<strafesnet_common::gameplay_attributes::ContactingLadder> for ContactingLadder {
fn into(self) -> strafesnet_common::gameplay_attributes::ContactingLadder {
strafesnet_common::gameplay_attributes::ContactingLadder {
sticky: self.sticky.into(),
}
}
}
impl From<strafesnet_common::gameplay_attributes::ContactingLadder> for ContactingLadder{
fn from(value:strafesnet_common::gameplay_attributes::ContactingLadder)->Self{
Self{
sticky:value.sticky.into(),
}
}
impl From<strafesnet_common::gameplay_attributes::ContactingLadder> for ContactingLadder {
fn from(value: strafesnet_common::gameplay_attributes::ContactingLadder) -> Self {
Self {
sticky: value.sticky.into(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub enum ContactingBehaviour{
#[brw(magic=0u8)]
Surf,
#[brw(magic=1u8)]
Ladder(ContactingLadder),
#[brw(magic=2u8)]
NoJump,
#[brw(magic=3u8)]
Cling,
#[brw(magic=4u8)]
Elastic(u32),
pub enum ContactingBehaviour {
#[brw(magic = 0u8)]
Surf,
#[brw(magic = 1u8)]
Ladder(ContactingLadder),
#[brw(magic = 2u8)]
NoJump,
#[brw(magic = 3u8)]
Cling,
#[brw(magic = 4u8)]
Elastic(u32),
}
impl Into<strafesnet_common::gameplay_attributes::ContactingBehaviour> for ContactingBehaviour{
fn into(self)->strafesnet_common::gameplay_attributes::ContactingBehaviour{
match self{
ContactingBehaviour::Surf=>
strafesnet_common::gameplay_attributes::ContactingBehaviour::Surf,
ContactingBehaviour::Ladder(contacting_ladder)=>
strafesnet_common::gameplay_attributes::ContactingBehaviour::Ladder(
contacting_ladder.into(),
),
ContactingBehaviour::NoJump=>
strafesnet_common::gameplay_attributes::ContactingBehaviour::NoJump,
ContactingBehaviour::Cling=>
strafesnet_common::gameplay_attributes::ContactingBehaviour::Cling,
ContactingBehaviour::Elastic(elasticity)=>
strafesnet_common::gameplay_attributes::ContactingBehaviour::Elastic(elasticity),
}
}
impl Into<strafesnet_common::gameplay_attributes::ContactingBehaviour> for ContactingBehaviour {
fn into(self) -> strafesnet_common::gameplay_attributes::ContactingBehaviour {
match self {
ContactingBehaviour::Surf => {
strafesnet_common::gameplay_attributes::ContactingBehaviour::Surf
}
ContactingBehaviour::Ladder(contacting_ladder) => {
strafesnet_common::gameplay_attributes::ContactingBehaviour::Ladder(
contacting_ladder.into(),
)
}
ContactingBehaviour::NoJump => {
strafesnet_common::gameplay_attributes::ContactingBehaviour::NoJump
}
ContactingBehaviour::Cling => {
strafesnet_common::gameplay_attributes::ContactingBehaviour::Cling
}
ContactingBehaviour::Elastic(elasticity) => {
strafesnet_common::gameplay_attributes::ContactingBehaviour::Elastic(elasticity)
}
}
}
}
impl From<strafesnet_common::gameplay_attributes::ContactingBehaviour> for ContactingBehaviour{
fn from(value:strafesnet_common::gameplay_attributes::ContactingBehaviour)->Self{
match value{
strafesnet_common::gameplay_attributes::ContactingBehaviour::Surf=>
ContactingBehaviour::Surf,
strafesnet_common::gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder)=>
ContactingBehaviour::Ladder(
contacting_ladder.into()
),
strafesnet_common::gameplay_attributes::ContactingBehaviour::NoJump=>
ContactingBehaviour::NoJump,
strafesnet_common::gameplay_attributes::ContactingBehaviour::Cling=>
ContactingBehaviour::Cling,
strafesnet_common::gameplay_attributes::ContactingBehaviour::Elastic(elasticity)=>
ContactingBehaviour::Elastic(elasticity),
}
}
impl From<strafesnet_common::gameplay_attributes::ContactingBehaviour> for ContactingBehaviour {
fn from(value: strafesnet_common::gameplay_attributes::ContactingBehaviour) -> Self {
match value {
strafesnet_common::gameplay_attributes::ContactingBehaviour::Surf => {
ContactingBehaviour::Surf
}
strafesnet_common::gameplay_attributes::ContactingBehaviour::Ladder(
contacting_ladder,
) => ContactingBehaviour::Ladder(contacting_ladder.into()),
strafesnet_common::gameplay_attributes::ContactingBehaviour::NoJump => {
ContactingBehaviour::NoJump
}
strafesnet_common::gameplay_attributes::ContactingBehaviour::Cling => {
ContactingBehaviour::Cling
}
strafesnet_common::gameplay_attributes::ContactingBehaviour::Elastic(elasticity) => {
ContactingBehaviour::Elastic(elasticity)
}
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct IntersectingWater{
pub viscosity:Planar64,
pub density:Planar64,
pub velocity:Planar64Vec3,
pub struct IntersectingWater {
pub viscosity: Planar64,
pub density: Planar64,
pub velocity: Planar64Vec3,
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingWater> for IntersectingWater{
fn into(self)->strafesnet_common::gameplay_attributes::IntersectingWater{
strafesnet_common::gameplay_attributes::IntersectingWater{
viscosity:strafesnet_common::integer::Planar64::raw(self.viscosity),
density:strafesnet_common::integer::Planar64::raw(self.density),
velocity:strafesnet_common::integer::vec3::raw_array(self.velocity),
}
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingWater> for IntersectingWater {
fn into(self) -> strafesnet_common::gameplay_attributes::IntersectingWater {
strafesnet_common::gameplay_attributes::IntersectingWater {
viscosity: strafesnet_common::integer::Planar64::raw(self.viscosity),
density: strafesnet_common::integer::Planar64::raw(self.density),
velocity: strafesnet_common::integer::vec3::raw_array(self.velocity),
}
}
}
impl From<strafesnet_common::gameplay_attributes::IntersectingWater> for IntersectingWater{
fn from(value:strafesnet_common::gameplay_attributes::IntersectingWater)->Self{
Self{
viscosity:value.viscosity.to_raw(),
density:value.density.to_raw(),
velocity:value.velocity.map(|t|t.to_raw()).to_array(),
}
}
impl From<strafesnet_common::gameplay_attributes::IntersectingWater> for IntersectingWater {
fn from(value: strafesnet_common::gameplay_attributes::IntersectingWater) -> Self {
Self {
viscosity: value.viscosity.to_raw(),
density: value.density.to_raw(),
velocity: value.velocity.map(|t| t.to_raw()).to_array(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Accelerator{
pub acceleration:Planar64Vec3
pub struct Accelerator {
pub acceleration: Planar64Vec3,
}
impl Into<strafesnet_common::gameplay_attributes::Accelerator> for Accelerator{
fn into(self)->strafesnet_common::gameplay_attributes::Accelerator{
strafesnet_common::gameplay_attributes::Accelerator{
acceleration:strafesnet_common::integer::vec3::raw_array(self.acceleration)
}
}
impl Into<strafesnet_common::gameplay_attributes::Accelerator> for Accelerator {
fn into(self) -> strafesnet_common::gameplay_attributes::Accelerator {
strafesnet_common::gameplay_attributes::Accelerator {
acceleration: strafesnet_common::integer::vec3::raw_array(self.acceleration),
}
}
}
impl From<strafesnet_common::gameplay_attributes::Accelerator> for Accelerator{
fn from(value:strafesnet_common::gameplay_attributes::Accelerator)->Self{
Self{
acceleration:value.acceleration.map(|t|t.to_raw()).to_array(),
}
}
impl From<strafesnet_common::gameplay_attributes::Accelerator> for Accelerator {
fn from(value: strafesnet_common::gameplay_attributes::Accelerator) -> Self {
Self {
acceleration: value.acceleration.map(|t| t.to_raw()).to_array(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub enum Booster{
#[brw(magic=0u8)]
Velocity(Planar64Vec3),
#[brw(magic=1u8)]
Energy{direction:Planar64Vec3,energy:Planar64},
#[brw(magic=2u8)]
AirTime(Time),
#[brw(magic=3u8)]
Height(Planar64),
pub enum Booster {
#[brw(magic = 0u8)]
Velocity(Planar64Vec3),
#[brw(magic = 1u8)]
Energy {
direction: Planar64Vec3,
energy: Planar64,
},
#[brw(magic = 2u8)]
AirTime(Time),
#[brw(magic = 3u8)]
Height(Planar64),
}
impl Into<strafesnet_common::gameplay_attributes::Booster> for Booster{
fn into(self)->strafesnet_common::gameplay_attributes::Booster{
match self{
Booster::Velocity(velocity)=>
strafesnet_common::gameplay_attributes::Booster::Velocity(
strafesnet_common::integer::vec3::raw_array(velocity)
),
Booster::Energy{direction,energy}=>
strafesnet_common::gameplay_attributes::Booster::Energy{
direction:strafesnet_common::integer::vec3::raw_array(direction),
energy:strafesnet_common::integer::Planar64::raw(energy)
},
Booster::AirTime(time)=>
strafesnet_common::gameplay_attributes::Booster::AirTime(
strafesnet_common::integer::Time::raw(time)
),
Booster::Height(height)=>
strafesnet_common::gameplay_attributes::Booster::Height(
strafesnet_common::integer::Planar64::raw(height)
),
}
}
impl Into<strafesnet_common::gameplay_attributes::Booster> for Booster {
fn into(self) -> strafesnet_common::gameplay_attributes::Booster {
match self {
Booster::Velocity(velocity) => {
strafesnet_common::gameplay_attributes::Booster::Velocity(
strafesnet_common::integer::vec3::raw_array(velocity),
)
}
Booster::Energy { direction, energy } => {
strafesnet_common::gameplay_attributes::Booster::Energy {
direction: strafesnet_common::integer::vec3::raw_array(direction),
energy: strafesnet_common::integer::Planar64::raw(energy),
}
}
Booster::AirTime(time) => strafesnet_common::gameplay_attributes::Booster::AirTime(
strafesnet_common::integer::Time::raw(time),
),
Booster::Height(height) => strafesnet_common::gameplay_attributes::Booster::Height(
strafesnet_common::integer::Planar64::raw(height),
),
}
}
}
impl From<strafesnet_common::gameplay_attributes::Booster> for Booster{
fn from(value:strafesnet_common::gameplay_attributes::Booster)->Self{
match value{
strafesnet_common::gameplay_attributes::Booster::Velocity(velocity)=>
Booster::Velocity(velocity.map(|t|t.to_raw()).to_array()),
strafesnet_common::gameplay_attributes::Booster::Energy{direction,energy}=>
Booster::Energy{
direction:direction.map(|t|t.to_raw()).to_array(),
energy:energy.to_raw(),
},
strafesnet_common::gameplay_attributes::Booster::AirTime(time)=>
Booster::AirTime(time.get()),
strafesnet_common::gameplay_attributes::Booster::Height(height)=>
Booster::Height(height.to_raw()),
}
}
impl From<strafesnet_common::gameplay_attributes::Booster> for Booster {
fn from(value: strafesnet_common::gameplay_attributes::Booster) -> Self {
match value {
strafesnet_common::gameplay_attributes::Booster::Velocity(velocity) => {
Booster::Velocity(velocity.map(|t| t.to_raw()).to_array())
}
strafesnet_common::gameplay_attributes::Booster::Energy { direction, energy } => {
Booster::Energy {
direction: direction.map(|t| t.to_raw()).to_array(),
energy: energy.to_raw(),
}
}
strafesnet_common::gameplay_attributes::Booster::AirTime(time) => {
Booster::AirTime(time.get())
}
strafesnet_common::gameplay_attributes::Booster::Height(height) => {
Booster::Height(height.to_raw())
}
}
}
}
#[binrw::binrw]
#[brw(little,repr=u8)]
pub enum TrajectoryChoice{
HighArcLongDuration,
LowArcShortDuration,
pub enum TrajectoryChoice {
HighArcLongDuration,
LowArcShortDuration,
}
impl Into<strafesnet_common::gameplay_attributes::TrajectoryChoice> for TrajectoryChoice{
fn into(self)->strafesnet_common::gameplay_attributes::TrajectoryChoice{
match self{
TrajectoryChoice::HighArcLongDuration=>
strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration,
TrajectoryChoice::LowArcShortDuration=>
strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration,
}
}
impl Into<strafesnet_common::gameplay_attributes::TrajectoryChoice> for TrajectoryChoice {
fn into(self) -> strafesnet_common::gameplay_attributes::TrajectoryChoice {
match self {
TrajectoryChoice::HighArcLongDuration => {
strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration
}
TrajectoryChoice::LowArcShortDuration => {
strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration
}
}
}
}
impl From<strafesnet_common::gameplay_attributes::TrajectoryChoice> for TrajectoryChoice{
fn from(value:strafesnet_common::gameplay_attributes::TrajectoryChoice)->Self{
match value{
strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration=>
TrajectoryChoice::HighArcLongDuration,
strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration=>
TrajectoryChoice::LowArcShortDuration,
}
}
impl From<strafesnet_common::gameplay_attributes::TrajectoryChoice> for TrajectoryChoice {
fn from(value: strafesnet_common::gameplay_attributes::TrajectoryChoice) -> Self {
match value {
strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration => {
TrajectoryChoice::HighArcLongDuration
}
strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration => {
TrajectoryChoice::LowArcShortDuration
}
}
}
}
#[binrw::binrw]
#[brw(little)]
pub enum SetTrajectory{
#[brw(magic=0u8)]
AirTime(Time),
#[brw(magic=1u8)]
Height(Planar64),
#[brw(magic=2u8)]
DotVelocity{direction:Planar64Vec3,dot:Planar64},
#[brw(magic=3u8)]
TargetPointTime{
target_point:Planar64Vec3,
time:Time,
},
#[brw(magic=4u8)]
TargetPointSpeed{
target_point:Planar64Vec3,
speed:Planar64,
trajectory_choice:TrajectoryChoice,
},
#[brw(magic=5u8)]
Velocity(Planar64Vec3),
pub enum SetTrajectory {
#[brw(magic = 0u8)]
AirTime(Time),
#[brw(magic = 1u8)]
Height(Planar64),
#[brw(magic = 2u8)]
DotVelocity {
direction: Planar64Vec3,
dot: Planar64,
},
#[brw(magic = 3u8)]
TargetPointTime {
target_point: Planar64Vec3,
time: Time,
},
#[brw(magic = 4u8)]
TargetPointSpeed {
target_point: Planar64Vec3,
speed: Planar64,
trajectory_choice: TrajectoryChoice,
},
#[brw(magic = 5u8)]
Velocity(Planar64Vec3),
}
impl Into<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory{
fn into(self)->strafesnet_common::gameplay_attributes::SetTrajectory{
match self{
SetTrajectory::AirTime(time)=>
strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(
strafesnet_common::integer::Time::raw(time)
),
SetTrajectory::Height(height)=>
strafesnet_common::gameplay_attributes::SetTrajectory::Height(
strafesnet_common::integer::Planar64::raw(height)
),
SetTrajectory::DotVelocity{direction,dot}=>
strafesnet_common::gameplay_attributes::SetTrajectory::DotVelocity{
direction:strafesnet_common::integer::vec3::raw_array(direction),
dot:strafesnet_common::integer::Planar64::raw(dot),
},
SetTrajectory::TargetPointTime{target_point,time}=>
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointTime{
target_point:strafesnet_common::integer::vec3::raw_array(target_point),
time:strafesnet_common::integer::Time::raw(time),
},
SetTrajectory::TargetPointSpeed{target_point,speed,trajectory_choice}=>
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointSpeed{
target_point:strafesnet_common::integer::vec3::raw_array(target_point),
speed:strafesnet_common::integer::Planar64::raw(speed),
trajectory_choice:trajectory_choice.into(),
},
SetTrajectory::Velocity(velocity)=>
strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(
strafesnet_common::integer::vec3::raw_array(velocity)
),
}
}
impl Into<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory {
fn into(self) -> strafesnet_common::gameplay_attributes::SetTrajectory {
match self {
SetTrajectory::AirTime(time) => {
strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(
strafesnet_common::integer::Time::raw(time),
)
}
SetTrajectory::Height(height) => {
strafesnet_common::gameplay_attributes::SetTrajectory::Height(
strafesnet_common::integer::Planar64::raw(height),
)
}
SetTrajectory::DotVelocity { direction, dot } => {
strafesnet_common::gameplay_attributes::SetTrajectory::DotVelocity {
direction: strafesnet_common::integer::vec3::raw_array(direction),
dot: strafesnet_common::integer::Planar64::raw(dot),
}
}
SetTrajectory::TargetPointTime { target_point, time } => {
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointTime {
target_point: strafesnet_common::integer::vec3::raw_array(target_point),
time: strafesnet_common::integer::Time::raw(time),
}
}
SetTrajectory::TargetPointSpeed {
target_point,
speed,
trajectory_choice,
} => strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointSpeed {
target_point: strafesnet_common::integer::vec3::raw_array(target_point),
speed: strafesnet_common::integer::Planar64::raw(speed),
trajectory_choice: trajectory_choice.into(),
},
SetTrajectory::Velocity(velocity) => {
strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(
strafesnet_common::integer::vec3::raw_array(velocity),
)
}
}
}
}
impl From<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory{
fn from(value:strafesnet_common::gameplay_attributes::SetTrajectory)->Self{
match value{
strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(time)=>
SetTrajectory::AirTime(
time.get()
),
strafesnet_common::gameplay_attributes::SetTrajectory::Height(height)=>
SetTrajectory::Height(
height.to_raw()
),
strafesnet_common::gameplay_attributes::SetTrajectory::DotVelocity{direction,dot}=>
SetTrajectory::DotVelocity{
direction:direction.map(|t|t.to_raw()).to_array(),
dot:dot.to_raw(),
},
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointTime{target_point,time}=>
SetTrajectory::TargetPointTime{
target_point:target_point.map(|t|t.to_raw()).to_array(),
time:time.get(),
},
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointSpeed{target_point,speed,trajectory_choice}=>
SetTrajectory::TargetPointSpeed{
target_point:target_point.map(|t|t.to_raw()).to_array(),
speed:speed.to_raw(),
trajectory_choice:trajectory_choice.into(),
},
strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(velocity)=>
SetTrajectory::Velocity(
velocity.map(|t|t.to_raw()).to_array()
),
}
}
impl From<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory {
fn from(value: strafesnet_common::gameplay_attributes::SetTrajectory) -> Self {
match value {
strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(time) => {
SetTrajectory::AirTime(time.get())
}
strafesnet_common::gameplay_attributes::SetTrajectory::Height(height) => {
SetTrajectory::Height(height.to_raw())
}
strafesnet_common::gameplay_attributes::SetTrajectory::DotVelocity {
direction,
dot,
} => SetTrajectory::DotVelocity {
direction: direction.map(|t| t.to_raw()).to_array(),
dot: dot.to_raw(),
},
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointTime {
target_point,
time,
} => SetTrajectory::TargetPointTime {
target_point: target_point.map(|t| t.to_raw()).to_array(),
time: time.get(),
},
strafesnet_common::gameplay_attributes::SetTrajectory::TargetPointSpeed {
target_point,
speed,
trajectory_choice,
} => SetTrajectory::TargetPointSpeed {
target_point: target_point.map(|t| t.to_raw()).to_array(),
speed: speed.to_raw(),
trajectory_choice: trajectory_choice.into(),
},
strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(velocity) => {
SetTrajectory::Velocity(velocity.map(|t| t.to_raw()).to_array())
}
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Wormhole{
pub destination_model:u32,
pub struct Wormhole {
pub destination_model: u32,
}
impl Into<strafesnet_common::gameplay_attributes::Wormhole> for Wormhole{
fn into(self)->strafesnet_common::gameplay_attributes::Wormhole{
strafesnet_common::gameplay_attributes::Wormhole{
destination_model:strafesnet_common::model::ModelId::new(self.destination_model),
}
}
impl Into<strafesnet_common::gameplay_attributes::Wormhole> for Wormhole {
fn into(self) -> strafesnet_common::gameplay_attributes::Wormhole {
strafesnet_common::gameplay_attributes::Wormhole {
destination_model: strafesnet_common::model::ModelId::new(self.destination_model),
}
}
}
impl From<strafesnet_common::gameplay_attributes::Wormhole> for Wormhole{
fn from(value:strafesnet_common::gameplay_attributes::Wormhole)->Self{
Self{
destination_model:value.destination_model.get(),
}
}
impl From<strafesnet_common::gameplay_attributes::Wormhole> for Wormhole {
fn from(value: strafesnet_common::gameplay_attributes::Wormhole) -> Self {
Self {
destination_model: value.destination_model.get(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct GeneralAttributes{
pub header:u8,
#[br(if(header&Self::BOOSTER!=0))]
pub booster:Option<Booster>,
#[br(if(header&Self::TRAJECTORY!=0))]
pub trajectory:Option<SetTrajectory>,
#[br(if(header&Self::WORMHOLE!=0))]
pub wormhole:Option<Wormhole>,
#[br(if(header&Self::ACCELERATOR!=0))]
pub accelerator:Option<Accelerator>,
pub struct GeneralAttributes {
pub header: u8,
#[br(if(header&Self::BOOSTER!=0))]
pub booster: Option<Booster>,
#[br(if(header&Self::TRAJECTORY!=0))]
pub trajectory: Option<SetTrajectory>,
#[br(if(header&Self::WORMHOLE!=0))]
pub wormhole: Option<Wormhole>,
#[br(if(header&Self::ACCELERATOR!=0))]
pub accelerator: Option<Accelerator>,
}
impl GeneralAttributes{
const BOOSTER:u8=1<<0;
const TRAJECTORY:u8=1<<1;
const WORMHOLE:u8=1<<2;
const ACCELERATOR:u8=1<<3;
impl GeneralAttributes {
const BOOSTER: u8 = 1 << 0;
const TRAJECTORY: u8 = 1 << 1;
const WORMHOLE: u8 = 1 << 2;
const ACCELERATOR: u8 = 1 << 3;
}
impl Into<strafesnet_common::gameplay_attributes::GeneralAttributes> for GeneralAttributes{
fn into(self)->strafesnet_common::gameplay_attributes::GeneralAttributes{
strafesnet_common::gameplay_attributes::GeneralAttributes{
booster:self.booster.map(Into::into),
trajectory:self.trajectory.map(Into::into),
wormhole:self.wormhole.map(Into::into),
accelerator:self.accelerator.map(Into::into),
}
}
impl Into<strafesnet_common::gameplay_attributes::GeneralAttributes> for GeneralAttributes {
fn into(self) -> strafesnet_common::gameplay_attributes::GeneralAttributes {
strafesnet_common::gameplay_attributes::GeneralAttributes {
booster: self.booster.map(Into::into),
trajectory: self.trajectory.map(Into::into),
wormhole: self.wormhole.map(Into::into),
accelerator: self.accelerator.map(Into::into),
}
}
}
impl From<strafesnet_common::gameplay_attributes::GeneralAttributes> for GeneralAttributes{
fn from(value:strafesnet_common::gameplay_attributes::GeneralAttributes)->Self{
let header=
flag(value.booster.is_some(),GeneralAttributes::BOOSTER)
|flag(value.trajectory.is_some(),GeneralAttributes::TRAJECTORY)
|flag(value.wormhole.is_some(),GeneralAttributes::WORMHOLE)
|flag(value.accelerator.is_some(),GeneralAttributes::ACCELERATOR);
Self{
header,
booster:value.booster.map(Into::into),
trajectory:value.trajectory.map(Into::into),
wormhole:value.wormhole.map(Into::into),
accelerator:value.accelerator.map(Into::into),
}
}
impl From<strafesnet_common::gameplay_attributes::GeneralAttributes> for GeneralAttributes {
fn from(value: strafesnet_common::gameplay_attributes::GeneralAttributes) -> Self {
let header = flag(value.booster.is_some(), GeneralAttributes::BOOSTER)
| flag(value.trajectory.is_some(), GeneralAttributes::TRAJECTORY)
| flag(value.wormhole.is_some(), GeneralAttributes::WORMHOLE)
| flag(value.accelerator.is_some(), GeneralAttributes::ACCELERATOR);
Self {
header,
booster: value.booster.map(Into::into),
trajectory: value.trajectory.map(Into::into),
wormhole: value.wormhole.map(Into::into),
accelerator: value.accelerator.map(Into::into),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct ContactingAttributes{
pub header:u8,
#[br(if(header&Self::CONTACTING_BEHAVIOUR!=0))]
pub contact_behaviour:Option<ContactingBehaviour>,
pub struct ContactingAttributes {
pub header: u8,
#[br(if(header&Self::CONTACTING_BEHAVIOUR!=0))]
pub contact_behaviour: Option<ContactingBehaviour>,
}
impl ContactingAttributes{
const CONTACTING_BEHAVIOUR:u8=1<<0;
impl ContactingAttributes {
const CONTACTING_BEHAVIOUR: u8 = 1 << 0;
}
impl Into<strafesnet_common::gameplay_attributes::ContactingAttributes> for ContactingAttributes{
fn into(self)->strafesnet_common::gameplay_attributes::ContactingAttributes{
strafesnet_common::gameplay_attributes::ContactingAttributes{
contact_behaviour:self.contact_behaviour.map(Into::into),
}
}
impl Into<strafesnet_common::gameplay_attributes::ContactingAttributes> for ContactingAttributes {
fn into(self) -> strafesnet_common::gameplay_attributes::ContactingAttributes {
strafesnet_common::gameplay_attributes::ContactingAttributes {
contact_behaviour: self.contact_behaviour.map(Into::into),
}
}
}
impl From<strafesnet_common::gameplay_attributes::ContactingAttributes> for ContactingAttributes{
fn from(value:strafesnet_common::gameplay_attributes::ContactingAttributes)->Self{
Self{
header:flag(value.contact_behaviour.is_some(),ContactingAttributes::CONTACTING_BEHAVIOUR),
contact_behaviour:value.contact_behaviour.map(Into::into),
}
}
impl From<strafesnet_common::gameplay_attributes::ContactingAttributes> for ContactingAttributes {
fn from(value: strafesnet_common::gameplay_attributes::ContactingAttributes) -> Self {
Self {
header: flag(
value.contact_behaviour.is_some(),
ContactingAttributes::CONTACTING_BEHAVIOUR,
),
contact_behaviour: value.contact_behaviour.map(Into::into),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct IntersectingAttributes{
pub header:u8,
#[br(if(header&Self::INTERSECTING_WATER!=0))]
pub water:Option<IntersectingWater>,
pub struct IntersectingAttributes {
pub header: u8,
#[br(if(header&Self::INTERSECTING_WATER!=0))]
pub water: Option<IntersectingWater>,
}
impl IntersectingAttributes{
const INTERSECTING_WATER:u8=1<<0;
impl IntersectingAttributes {
const INTERSECTING_WATER: u8 = 1 << 0;
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingAttributes> for IntersectingAttributes{
fn into(self)->strafesnet_common::gameplay_attributes::IntersectingAttributes{
strafesnet_common::gameplay_attributes::IntersectingAttributes{
water:self.water.map(Into::into),
}
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingAttributes>
for IntersectingAttributes
{
fn into(self) -> strafesnet_common::gameplay_attributes::IntersectingAttributes {
strafesnet_common::gameplay_attributes::IntersectingAttributes {
water: self.water.map(Into::into),
}
}
}
impl From<strafesnet_common::gameplay_attributes::IntersectingAttributes> for IntersectingAttributes{
fn from(value:strafesnet_common::gameplay_attributes::IntersectingAttributes)->Self{
Self{
header:flag(value.water.is_some(),IntersectingAttributes::INTERSECTING_WATER),
water:value.water.map(Into::into),
}
}
impl From<strafesnet_common::gameplay_attributes::IntersectingAttributes>
for IntersectingAttributes
{
fn from(value: strafesnet_common::gameplay_attributes::IntersectingAttributes) -> Self {
Self {
header: flag(
value.water.is_some(),
IntersectingAttributes::INTERSECTING_WATER,
),
water: value.water.map(Into::into),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct ContactAttributes{
contacting:ContactingAttributes,
general:GeneralAttributes,
pub struct ContactAttributes {
contacting: ContactingAttributes,
general: GeneralAttributes,
}
impl Into<strafesnet_common::gameplay_attributes::ContactAttributes> for ContactAttributes{
fn into(self)->strafesnet_common::gameplay_attributes::ContactAttributes{
strafesnet_common::gameplay_attributes::ContactAttributes{
contacting:self.contacting.into(),
general:self.general.into(),
}
}
impl Into<strafesnet_common::gameplay_attributes::ContactAttributes> for ContactAttributes {
fn into(self) -> strafesnet_common::gameplay_attributes::ContactAttributes {
strafesnet_common::gameplay_attributes::ContactAttributes {
contacting: self.contacting.into(),
general: self.general.into(),
}
}
}
impl From<strafesnet_common::gameplay_attributes::ContactAttributes> for ContactAttributes{
fn from(value:strafesnet_common::gameplay_attributes::ContactAttributes)->Self{
Self{
contacting:value.contacting.into(),
general:value.general.into(),
}
}
impl From<strafesnet_common::gameplay_attributes::ContactAttributes> for ContactAttributes {
fn from(value: strafesnet_common::gameplay_attributes::ContactAttributes) -> Self {
Self {
contacting: value.contacting.into(),
general: value.general.into(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct IntersectAttributes{
intersecting:IntersectingAttributes,
general:GeneralAttributes,
pub struct IntersectAttributes {
intersecting: IntersectingAttributes,
general: GeneralAttributes,
}
impl Into<strafesnet_common::gameplay_attributes::IntersectAttributes> for IntersectAttributes{
fn into(self)->strafesnet_common::gameplay_attributes::IntersectAttributes{
strafesnet_common::gameplay_attributes::IntersectAttributes{
intersecting:self.intersecting.into(),
general:self.general.into(),
}
}
impl Into<strafesnet_common::gameplay_attributes::IntersectAttributes> for IntersectAttributes {
fn into(self) -> strafesnet_common::gameplay_attributes::IntersectAttributes {
strafesnet_common::gameplay_attributes::IntersectAttributes {
intersecting: self.intersecting.into(),
general: self.general.into(),
}
}
}
impl From<strafesnet_common::gameplay_attributes::IntersectAttributes> for IntersectAttributes{
fn from(value:strafesnet_common::gameplay_attributes::IntersectAttributes)->Self{
Self{
intersecting:value.intersecting.into(),
general:value.general.into(),
}
}
impl From<strafesnet_common::gameplay_attributes::IntersectAttributes> for IntersectAttributes {
fn from(value: strafesnet_common::gameplay_attributes::IntersectAttributes) -> Self {
Self {
intersecting: value.intersecting.into(),
general: value.general.into(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub enum CollisionAttributes{
#[brw(magic=0u8)]
Decoration,
#[brw(magic=1u8)]
Contact(ContactAttributes),
#[brw(magic=2u8)]
Intersect(IntersectAttributes),
pub enum CollisionAttributes {
#[brw(magic = 0u8)]
Decoration,
#[brw(magic = 1u8)]
Contact(ContactAttributes),
#[brw(magic = 2u8)]
Intersect(IntersectAttributes),
}
impl Into<strafesnet_common::gameplay_attributes::CollisionAttributes> for CollisionAttributes{
fn into(self)->strafesnet_common::gameplay_attributes::CollisionAttributes{
match self{
CollisionAttributes::Decoration=>
strafesnet_common::gameplay_attributes::CollisionAttributes::Decoration,
CollisionAttributes::Contact(attr)=>
strafesnet_common::gameplay_attributes::CollisionAttributes::Contact(attr.into()),
CollisionAttributes::Intersect(attr)=>
strafesnet_common::gameplay_attributes::CollisionAttributes::Intersect(attr.into()),
}
}
impl Into<strafesnet_common::gameplay_attributes::CollisionAttributes> for CollisionAttributes {
fn into(self) -> strafesnet_common::gameplay_attributes::CollisionAttributes {
match self {
CollisionAttributes::Decoration => {
strafesnet_common::gameplay_attributes::CollisionAttributes::Decoration
}
CollisionAttributes::Contact(attr) => {
strafesnet_common::gameplay_attributes::CollisionAttributes::Contact(attr.into())
}
CollisionAttributes::Intersect(attr) => {
strafesnet_common::gameplay_attributes::CollisionAttributes::Intersect(attr.into())
}
}
}
}
impl From<strafesnet_common::gameplay_attributes::CollisionAttributes> for CollisionAttributes{
fn from(value:strafesnet_common::gameplay_attributes::CollisionAttributes)->Self{
match value{
strafesnet_common::gameplay_attributes::CollisionAttributes::Decoration=>
CollisionAttributes::Decoration,
strafesnet_common::gameplay_attributes::CollisionAttributes::Contact(attr)=>
CollisionAttributes::Contact(attr.into()),
strafesnet_common::gameplay_attributes::CollisionAttributes::Intersect(attr)=>
CollisionAttributes::Intersect(attr.into()),
}
}
impl From<strafesnet_common::gameplay_attributes::CollisionAttributes> for CollisionAttributes {
fn from(value: strafesnet_common::gameplay_attributes::CollisionAttributes) -> Self {
match value {
strafesnet_common::gameplay_attributes::CollisionAttributes::Decoration => {
CollisionAttributes::Decoration
}
strafesnet_common::gameplay_attributes::CollisionAttributes::Contact(attr) => {
CollisionAttributes::Contact(attr.into())
}
strafesnet_common::gameplay_attributes::CollisionAttributes::Intersect(attr) => {
CollisionAttributes::Intersect(attr.into())
}
}
}
}

View File

@ -1,220 +1,241 @@
use super::common::flag;
pub type ModeId=u32;
pub type StageId=u32;
pub type ModeId = u32;
pub type StageId = u32;
#[binrw::binrw]
#[brw(little)]
pub struct StageElement{
pub header:u8,
pub stage_id:StageId,
#[br(if(header&Self::JUMP_LIMIT!=0))]
pub jump_limit:Option<u8>,
pub struct StageElement {
pub header: u8,
pub stage_id: StageId,
#[br(if(header&Self::JUMP_LIMIT!=0))]
pub jump_limit: Option<u8>,
}
impl StageElement{
const BEHAVIOUR:u8=0b00111;
const JUMP_LIMIT:u8=1<<3;
const FORCE:u8=1<<4;
const fn behaviour(&self)->Option<strafesnet_common::gameplay_modes::StageElementBehaviour>{
match self.header&Self::BEHAVIOUR{
0=>Some(strafesnet_common::gameplay_modes::StageElementBehaviour::SpawnAt),
1=>Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Trigger),
2=>Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Teleport),
3=>Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Platform),
4=>Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Check),
5=>Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Checkpoint),
_=>None,
}
}
const fn force(&self)->bool{
self.header&Self::FORCE!=0
}
impl StageElement {
const BEHAVIOUR: u8 = 0b00111;
const JUMP_LIMIT: u8 = 1 << 3;
const FORCE: u8 = 1 << 4;
const fn behaviour(&self) -> Option<strafesnet_common::gameplay_modes::StageElementBehaviour> {
match self.header & Self::BEHAVIOUR {
0 => Some(strafesnet_common::gameplay_modes::StageElementBehaviour::SpawnAt),
1 => Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Trigger),
2 => Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Teleport),
3 => Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Platform),
4 => Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Check),
5 => Some(strafesnet_common::gameplay_modes::StageElementBehaviour::Checkpoint),
_ => None,
}
}
const fn force(&self) -> bool {
self.header & Self::FORCE != 0
}
}
#[derive(Debug)]
pub enum StageElementError{
InvalidBehaviour,
pub enum StageElementError {
InvalidBehaviour,
}
impl std::fmt::Display for StageElementError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for StageElementError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for StageElementError{}
impl TryInto<strafesnet_common::gameplay_modes::StageElement> for StageElement{
type Error=StageElementError;
fn try_into(self)->Result<strafesnet_common::gameplay_modes::StageElement,Self::Error>{
Ok(strafesnet_common::gameplay_modes::StageElement::new(
strafesnet_common::gameplay_modes::StageId::new(self.stage_id),
self.force(),
self.behaviour().ok_or(StageElementError::InvalidBehaviour)?,
self.jump_limit,
))
}
impl std::error::Error for StageElementError {}
impl TryInto<strafesnet_common::gameplay_modes::StageElement> for StageElement {
type Error = StageElementError;
fn try_into(self) -> Result<strafesnet_common::gameplay_modes::StageElement, Self::Error> {
Ok(strafesnet_common::gameplay_modes::StageElement::new(
strafesnet_common::gameplay_modes::StageId::new(self.stage_id),
self.force(),
self.behaviour()
.ok_or(StageElementError::InvalidBehaviour)?,
self.jump_limit,
))
}
}
impl From<strafesnet_common::gameplay_modes::StageElement> for StageElement{
fn from(value:strafesnet_common::gameplay_modes::StageElement)->Self{
let behaviour=match value.behaviour(){
strafesnet_common::gameplay_modes::StageElementBehaviour::SpawnAt=>0,
strafesnet_common::gameplay_modes::StageElementBehaviour::Trigger=>1,
strafesnet_common::gameplay_modes::StageElementBehaviour::Teleport=>2,
strafesnet_common::gameplay_modes::StageElementBehaviour::Platform=>3,
strafesnet_common::gameplay_modes::StageElementBehaviour::Check=>4,
strafesnet_common::gameplay_modes::StageElementBehaviour::Checkpoint=>5,
};
let header=
behaviour
|flag(value.jump_limit().is_some(),StageElement::JUMP_LIMIT)
|flag(value.force(),StageElement::FORCE);
Self{
header,
stage_id:value.stage_id().get(),
jump_limit:value.jump_limit(),
}
}
impl From<strafesnet_common::gameplay_modes::StageElement> for StageElement {
fn from(value: strafesnet_common::gameplay_modes::StageElement) -> Self {
let behaviour = match value.behaviour() {
strafesnet_common::gameplay_modes::StageElementBehaviour::SpawnAt => 0,
strafesnet_common::gameplay_modes::StageElementBehaviour::Trigger => 1,
strafesnet_common::gameplay_modes::StageElementBehaviour::Teleport => 2,
strafesnet_common::gameplay_modes::StageElementBehaviour::Platform => 3,
strafesnet_common::gameplay_modes::StageElementBehaviour::Check => 4,
strafesnet_common::gameplay_modes::StageElementBehaviour::Checkpoint => 5,
};
let header = behaviour
| flag(value.jump_limit().is_some(), StageElement::JUMP_LIMIT)
| flag(value.force(), StageElement::FORCE);
Self {
header,
stage_id: value.stage_id().get(),
jump_limit: value.jump_limit(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Stage{
pub spawn:u32,
//open world support lol
pub ordered_checkpoints_count:u32,
pub unordered_checkpoints_count:u32,
//currently loaded checkpoint models
#[br(count=ordered_checkpoints_count)]
pub ordered_checkpoints:Vec<(u32,u32)>,
#[br(count=unordered_checkpoints_count)]
pub unordered_checkpoints:Vec<u32>,
pub struct Stage {
pub spawn: u32,
//open world support lol
pub ordered_checkpoints_count: u32,
pub unordered_checkpoints_count: u32,
//currently loaded checkpoint models
#[br(count=ordered_checkpoints_count)]
pub ordered_checkpoints: Vec<(u32, u32)>,
#[br(count=unordered_checkpoints_count)]
pub unordered_checkpoints: Vec<u32>,
}
impl Into<strafesnet_common::gameplay_modes::Stage> for Stage{
fn into(self)->strafesnet_common::gameplay_modes::Stage{
strafesnet_common::gameplay_modes::Stage::new(
strafesnet_common::model::ModelId::new(self.spawn),
self.ordered_checkpoints_count,
self.unordered_checkpoints_count,
self.ordered_checkpoints.into_iter().map(|(checkpoint_id,model_id)|(
strafesnet_common::gameplay_modes::CheckpointId::new(checkpoint_id),
strafesnet_common::model::ModelId::new(model_id),
)).collect(),
self.unordered_checkpoints.into_iter()
.map(strafesnet_common::model::ModelId::new)
.collect(),
)
}
impl Into<strafesnet_common::gameplay_modes::Stage> for Stage {
fn into(self) -> strafesnet_common::gameplay_modes::Stage {
strafesnet_common::gameplay_modes::Stage::new(
strafesnet_common::model::ModelId::new(self.spawn),
self.ordered_checkpoints_count,
self.unordered_checkpoints_count,
self.ordered_checkpoints
.into_iter()
.map(|(checkpoint_id, model_id)| {
(
strafesnet_common::gameplay_modes::CheckpointId::new(checkpoint_id),
strafesnet_common::model::ModelId::new(model_id),
)
})
.collect(),
self.unordered_checkpoints
.into_iter()
.map(strafesnet_common::model::ModelId::new)
.collect(),
)
}
}
impl From<strafesnet_common::gameplay_modes::Stage> for Stage{
fn from(value:strafesnet_common::gameplay_modes::Stage)->Self{
let spawn=value.spawn().get();
let ordered_checkpoints_count=value.ordered_checkpoints_count();
let unordered_checkpoints_count=value.unordered_checkpoints_count();
let (ordered_checkpoints,unordered_checkpoints)=value.into_inner();
Self{
spawn,
ordered_checkpoints_count,
unordered_checkpoints_count,
ordered_checkpoints:ordered_checkpoints.into_iter()
.map(|(checkpoint_id,model_id)|(checkpoint_id.get(),model_id.get()))
.collect(),
unordered_checkpoints:unordered_checkpoints.into_iter()
.map(|model_id|model_id.get())
.collect(),
}
}
impl From<strafesnet_common::gameplay_modes::Stage> for Stage {
fn from(value: strafesnet_common::gameplay_modes::Stage) -> Self {
let spawn = value.spawn().get();
let ordered_checkpoints_count = value.ordered_checkpoints_count();
let unordered_checkpoints_count = value.unordered_checkpoints_count();
let (ordered_checkpoints, unordered_checkpoints) = value.into_inner();
Self {
spawn,
ordered_checkpoints_count,
unordered_checkpoints_count,
ordered_checkpoints: ordered_checkpoints
.into_iter()
.map(|(checkpoint_id, model_id)| (checkpoint_id.get(), model_id.get()))
.collect(),
unordered_checkpoints: unordered_checkpoints
.into_iter()
.map(|model_id| model_id.get())
.collect(),
}
}
}
#[binrw::binrw]
#[brw(little,repr=u8)]
pub enum Zone{
Start,
Finish,
Anticheat,
pub enum Zone {
Start,
Finish,
Anticheat,
}
impl Into<strafesnet_common::gameplay_modes::Zone> for Zone{
fn into(self)->strafesnet_common::gameplay_modes::Zone{
match self{
Zone::Start=>strafesnet_common::gameplay_modes::Zone::Start,
Zone::Finish=>strafesnet_common::gameplay_modes::Zone::Finish,
Zone::Anticheat=>strafesnet_common::gameplay_modes::Zone::Anticheat,
}
}
impl Into<strafesnet_common::gameplay_modes::Zone> for Zone {
fn into(self) -> strafesnet_common::gameplay_modes::Zone {
match self {
Zone::Start => strafesnet_common::gameplay_modes::Zone::Start,
Zone::Finish => strafesnet_common::gameplay_modes::Zone::Finish,
Zone::Anticheat => strafesnet_common::gameplay_modes::Zone::Anticheat,
}
}
}
impl From<strafesnet_common::gameplay_modes::Zone> for Zone{
fn from(value:strafesnet_common::gameplay_modes::Zone)->Self{
match value{
strafesnet_common::gameplay_modes::Zone::Start=>Zone::Start,
strafesnet_common::gameplay_modes::Zone::Finish=>Zone::Finish,
strafesnet_common::gameplay_modes::Zone::Anticheat=>Zone::Anticheat,
}
}
impl From<strafesnet_common::gameplay_modes::Zone> for Zone {
fn from(value: strafesnet_common::gameplay_modes::Zone) -> Self {
match value {
strafesnet_common::gameplay_modes::Zone::Start => Zone::Start,
strafesnet_common::gameplay_modes::Zone::Finish => Zone::Finish,
strafesnet_common::gameplay_modes::Zone::Anticheat => Zone::Anticheat,
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct ModeHeader{
pub zones:u32,
pub stages:u32,
pub elements:u32,
pub struct ModeHeader {
pub zones: u32,
pub stages: u32,
pub elements: u32,
}
#[binrw::binrw]
#[brw(little)]
pub struct Mode{
pub header:ModeHeader,
pub style:super::gameplay_style::StyleModifiers,
pub start:u32,
#[br(count=header.zones)]
pub zones:Vec<(u32,Zone)>,
#[br(count=header.stages)]
pub stages:Vec<Stage>,
#[br(count=header.elements)]
pub elements:Vec<(u32,StageElement)>,
pub struct Mode {
pub header: ModeHeader,
pub style: super::gameplay_style::StyleModifiers,
pub start: u32,
#[br(count=header.zones)]
pub zones: Vec<(u32, Zone)>,
#[br(count=header.stages)]
pub stages: Vec<Stage>,
#[br(count=header.elements)]
pub elements: Vec<(u32, StageElement)>,
}
#[derive(Debug)]
pub enum ModeError{
StyleModifier(super::gameplay_style::StyleModifierError),
StageElement(StageElementError),
pub enum ModeError {
StyleModifier(super::gameplay_style::StyleModifierError),
StageElement(StageElementError),
}
impl std::fmt::Display for ModeError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for ModeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ModeError{}
impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
type Error=ModeError;
fn try_into(self)->Result<strafesnet_common::gameplay_modes::Mode,Self::Error>{
Ok(strafesnet_common::gameplay_modes::Mode::new(
self.style.try_into().map_err(ModeError::StyleModifier)?,
strafesnet_common::model::ModelId::new(self.start),
self.zones.into_iter().map(|(model_id,zone)|
(strafesnet_common::model::ModelId::new(model_id),zone.into())
).collect(),
self.stages.into_iter().map(Into::into).collect(),
self.elements.into_iter().map(|(model_id,stage_element)|
Ok((strafesnet_common::model::ModelId::new(model_id),stage_element.try_into()?))
).collect::<Result<_,_>>().map_err(ModeError::StageElement)?,
))
}
impl std::error::Error for ModeError {}
impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode {
type Error = ModeError;
fn try_into(self) -> Result<strafesnet_common::gameplay_modes::Mode, Self::Error> {
Ok(strafesnet_common::gameplay_modes::Mode::new(
self.style.try_into().map_err(ModeError::StyleModifier)?,
strafesnet_common::model::ModelId::new(self.start),
self.zones
.into_iter()
.map(|(model_id, zone)| {
(
strafesnet_common::model::ModelId::new(model_id),
zone.into(),
)
})
.collect(),
self.stages.into_iter().map(Into::into).collect(),
self.elements
.into_iter()
.map(|(model_id, stage_element)| {
Ok((
strafesnet_common::model::ModelId::new(model_id),
stage_element.try_into()?,
))
})
.collect::<Result<_, _>>()
.map_err(ModeError::StageElement)?,
))
}
}
impl From<strafesnet_common::gameplay_modes::Mode> for Mode{
fn from(value:strafesnet_common::gameplay_modes::Mode)->Self{
let (style,start,zones,stages,elements)=value.into_inner();
Self{
header:ModeHeader{
zones:zones.len() as u32,
stages:stages.len() as u32,
elements:elements.len() as u32,
},
style:style.into(),
start:start.get(),
zones:zones.into_iter()
.map(|(model_id,zone)|(model_id.get(),zone.into()))
.collect(),
stages:stages.into_iter()
.map(Into::into)
.collect(),
elements:elements.into_iter()
.map(|(model_id,stage_element)|(model_id.get(),stage_element.into()))
.collect(),
}
}
impl From<strafesnet_common::gameplay_modes::Mode> for Mode {
fn from(value: strafesnet_common::gameplay_modes::Mode) -> Self {
let (style, start, zones, stages, elements) = value.into_inner();
Self {
header: ModeHeader {
zones: zones.len() as u32,
stages: stages.len() as u32,
elements: elements.len() as u32,
},
style: style.into(),
start: start.get(),
zones: zones
.into_iter()
.map(|(model_id, zone)| (model_id.get(), zone.into()))
.collect(),
stages: stages.into_iter().map(Into::into).collect(),
elements: elements
.into_iter()
.map(|(model_id, stage_element)| (model_id.get(), stage_element.into()))
.collect(),
}
}
}

View File

@ -1,442 +1,514 @@
use super::common::flag;
use super::integer::{Time,Ratio64,Planar64,Planar64Vec3};
use super::integer::{Planar64, Planar64Vec3, Ratio64, Time};
pub type Controls=u32;
pub type Controls = u32;
#[derive(Debug)]
pub enum ControlsError{
UnknownBits,
pub enum ControlsError {
UnknownBits,
}
impl std::fmt::Display for ControlsError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for ControlsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ControlsError{}
impl std::error::Error for ControlsError {}
#[binrw::binrw]
#[brw(little)]
pub struct StyleModifiers{
pub header:u8,
pub controls_mask:Controls,
pub controls_mask_state:Controls,
#[br(if(header&Self::STRAFE!=0))]
pub strafe:Option<StrafeSettings>,
#[br(if(header&Self::ROCKET!=0))]
pub rocket:Option<PropulsionSettings>,
#[br(if(header&Self::JUMP!=0))]
pub jump:Option<JumpSettings>,
#[br(if(header&Self::WALK!=0))]
pub walk:Option<WalkSettings>,
#[br(if(header&Self::LADDER!=0))]
pub ladder:Option<LadderSettings>,
#[br(if(header&Self::SWIM!=0))]
pub swim:Option<PropulsionSettings>,
pub gravity:Planar64Vec3,
pub hitbox:Hitbox,
pub camera_offset:Planar64Vec3,
pub mass:Planar64,
pub struct StyleModifiers {
pub header: u8,
pub controls_mask: Controls,
pub controls_mask_state: Controls,
#[br(if(header&Self::STRAFE!=0))]
pub strafe: Option<StrafeSettings>,
#[br(if(header&Self::ROCKET!=0))]
pub rocket: Option<PropulsionSettings>,
#[br(if(header&Self::JUMP!=0))]
pub jump: Option<JumpSettings>,
#[br(if(header&Self::WALK!=0))]
pub walk: Option<WalkSettings>,
#[br(if(header&Self::LADDER!=0))]
pub ladder: Option<LadderSettings>,
#[br(if(header&Self::SWIM!=0))]
pub swim: Option<PropulsionSettings>,
pub gravity: Planar64Vec3,
pub hitbox: Hitbox,
pub camera_offset: Planar64Vec3,
pub mass: Planar64,
}
impl StyleModifiers{
const STRAFE:u8=1<<0;
const ROCKET:u8=1<<1;
const JUMP:u8=1<<2;
const WALK:u8=1<<3;
const LADDER:u8=1<<4;
const SWIM:u8=1<<5;
impl StyleModifiers {
const STRAFE: u8 = 1 << 0;
const ROCKET: u8 = 1 << 1;
const JUMP: u8 = 1 << 2;
const WALK: u8 = 1 << 3;
const LADDER: u8 = 1 << 4;
const SWIM: u8 = 1 << 5;
}
#[derive(Debug)]
pub enum StyleModifierError{
Controls(ControlsError),
JumpSettings(JumpSettingsError),
StrafeSettings(StrafeSettingsError),
pub enum StyleModifierError {
Controls(ControlsError),
JumpSettings(JumpSettingsError),
StrafeSettings(StrafeSettingsError),
}
impl std::fmt::Display for StyleModifierError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for StyleModifierError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for StyleModifierError{}
impl TryInto<strafesnet_common::gameplay_style::StyleModifiers> for StyleModifiers{
type Error=StyleModifierError;
fn try_into(self)->Result<strafesnet_common::gameplay_style::StyleModifiers,Self::Error>{
Ok(strafesnet_common::gameplay_style::StyleModifiers{
controls_mask:strafesnet_common::controls_bitflag::Controls::from_bits(self.controls_mask).ok_or(StyleModifierError::Controls(ControlsError::UnknownBits))?,
controls_mask_state:strafesnet_common::controls_bitflag::Controls::from_bits(self.controls_mask_state).ok_or(StyleModifierError::Controls(ControlsError::UnknownBits))?,
strafe:self.strafe.map(TryInto::try_into).transpose().map_err(StyleModifierError::StrafeSettings)?,
rocket:self.rocket.map(Into::into),
jump:self.jump.map(TryInto::try_into).transpose().map_err(StyleModifierError::JumpSettings)?,
walk:self.walk.map(Into::into),
ladder:self.ladder.map(Into::into),
swim:self.swim.map(Into::into),
gravity:strafesnet_common::integer::vec3::raw_array(self.gravity),
hitbox:self.hitbox.into(),
camera_offset:strafesnet_common::integer::vec3::raw_array(self.camera_offset),
mass:strafesnet_common::integer::Planar64::raw(self.mass),
})
}
impl std::error::Error for StyleModifierError {}
impl TryInto<strafesnet_common::gameplay_style::StyleModifiers> for StyleModifiers {
type Error = StyleModifierError;
fn try_into(self) -> Result<strafesnet_common::gameplay_style::StyleModifiers, Self::Error> {
Ok(strafesnet_common::gameplay_style::StyleModifiers {
controls_mask: strafesnet_common::controls_bitflag::Controls::from_bits(
self.controls_mask,
)
.ok_or(StyleModifierError::Controls(ControlsError::UnknownBits))?,
controls_mask_state: strafesnet_common::controls_bitflag::Controls::from_bits(
self.controls_mask_state,
)
.ok_or(StyleModifierError::Controls(ControlsError::UnknownBits))?,
strafe: self
.strafe
.map(TryInto::try_into)
.transpose()
.map_err(StyleModifierError::StrafeSettings)?,
rocket: self.rocket.map(Into::into),
jump: self
.jump
.map(TryInto::try_into)
.transpose()
.map_err(StyleModifierError::JumpSettings)?,
walk: self.walk.map(Into::into),
ladder: self.ladder.map(Into::into),
swim: self.swim.map(Into::into),
gravity: strafesnet_common::integer::vec3::raw_array(self.gravity),
hitbox: self.hitbox.into(),
camera_offset: strafesnet_common::integer::vec3::raw_array(self.camera_offset),
mass: strafesnet_common::integer::Planar64::raw(self.mass),
})
}
}
impl From<strafesnet_common::gameplay_style::StyleModifiers> for StyleModifiers{
fn from(value:strafesnet_common::gameplay_style::StyleModifiers)->Self{
let header=
flag(value.strafe.is_some(),StyleModifiers::STRAFE)
|flag(value.rocket.is_some(),StyleModifiers::ROCKET)
|flag(value.jump.is_some(),StyleModifiers::JUMP)
|flag(value.walk.is_some(),StyleModifiers::WALK)
|flag(value.ladder.is_some(),StyleModifiers::LADDER)
|flag(value.swim.is_some(),StyleModifiers::SWIM);
Self{
header,
controls_mask:value.controls_mask.bits(),
controls_mask_state:value.controls_mask_state.bits(),
strafe:value.strafe.map(Into::into),
rocket:value.rocket.map(Into::into),
jump:value.jump.map(Into::into),
walk:value.walk.map(Into::into),
ladder:value.ladder.map(Into::into),
swim:value.swim.map(Into::into),
gravity:value.gravity.map(|t|t.to_raw()).to_array(),
hitbox:value.hitbox.into(),
camera_offset:value.camera_offset.map(|t|t.to_raw()).to_array(),
mass:value.mass.to_raw(),
}
}
impl From<strafesnet_common::gameplay_style::StyleModifiers> for StyleModifiers {
fn from(value: strafesnet_common::gameplay_style::StyleModifiers) -> Self {
let header = flag(value.strafe.is_some(), StyleModifiers::STRAFE)
| flag(value.rocket.is_some(), StyleModifiers::ROCKET)
| flag(value.jump.is_some(), StyleModifiers::JUMP)
| flag(value.walk.is_some(), StyleModifiers::WALK)
| flag(value.ladder.is_some(), StyleModifiers::LADDER)
| flag(value.swim.is_some(), StyleModifiers::SWIM);
Self {
header,
controls_mask: value.controls_mask.bits(),
controls_mask_state: value.controls_mask_state.bits(),
strafe: value.strafe.map(Into::into),
rocket: value.rocket.map(Into::into),
jump: value.jump.map(Into::into),
walk: value.walk.map(Into::into),
ladder: value.ladder.map(Into::into),
swim: value.swim.map(Into::into),
gravity: value.gravity.map(|t| t.to_raw()).to_array(),
hitbox: value.hitbox.into(),
camera_offset: value.camera_offset.map(|t| t.to_raw()).to_array(),
mass: value.mass.to_raw(),
}
}
}
#[binrw::binrw]
#[brw(little,repr=u8)]
pub enum JumpCalculation{
Max,
BoostThenJump,
JumpThenBoost,
pub enum JumpCalculation {
Max,
BoostThenJump,
JumpThenBoost,
}
impl Into<strafesnet_common::gameplay_style::JumpCalculation> for JumpCalculation{
fn into(self)->strafesnet_common::gameplay_style::JumpCalculation{
match self{
JumpCalculation::Max=>strafesnet_common::gameplay_style::JumpCalculation::Max,
JumpCalculation::BoostThenJump=>strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump,
JumpCalculation::JumpThenBoost=>strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost,
}
}
impl Into<strafesnet_common::gameplay_style::JumpCalculation> for JumpCalculation {
fn into(self) -> strafesnet_common::gameplay_style::JumpCalculation {
match self {
JumpCalculation::Max => strafesnet_common::gameplay_style::JumpCalculation::Max,
JumpCalculation::BoostThenJump => {
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump
}
JumpCalculation::JumpThenBoost => {
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost
}
}
}
}
impl From<strafesnet_common::gameplay_style::JumpCalculation> for JumpCalculation{
fn from(value:strafesnet_common::gameplay_style::JumpCalculation)->Self{
match value{
strafesnet_common::gameplay_style::JumpCalculation::Max=>JumpCalculation::Max,
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump=>JumpCalculation::BoostThenJump,
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost=>JumpCalculation::JumpThenBoost,
}
}
impl From<strafesnet_common::gameplay_style::JumpCalculation> for JumpCalculation {
fn from(value: strafesnet_common::gameplay_style::JumpCalculation) -> Self {
match value {
strafesnet_common::gameplay_style::JumpCalculation::Max => JumpCalculation::Max,
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump => {
JumpCalculation::BoostThenJump
}
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost => {
JumpCalculation::JumpThenBoost
}
}
}
}
pub enum JumpImpulse{
Time(Time),
Height(Planar64),
Linear(Planar64),
Energy(Planar64),
pub enum JumpImpulse {
Time(Time),
Height(Planar64),
Linear(Planar64),
Energy(Planar64),
}
impl Into<strafesnet_common::gameplay_style::JumpImpulse> for JumpImpulse{
fn into(self)->strafesnet_common::gameplay_style::JumpImpulse{
match self{
JumpImpulse::Time(time)=>strafesnet_common::gameplay_style::JumpImpulse::Time(strafesnet_common::integer::Time::raw(time)),
JumpImpulse::Height(height)=>strafesnet_common::gameplay_style::JumpImpulse::Height(strafesnet_common::integer::Planar64::raw(height)),
JumpImpulse::Linear(deltav)=>strafesnet_common::gameplay_style::JumpImpulse::Linear(strafesnet_common::integer::Planar64::raw(deltav)),
JumpImpulse::Energy(energy)=>strafesnet_common::gameplay_style::JumpImpulse::Energy(strafesnet_common::integer::Planar64::raw(energy)),
}
}
impl Into<strafesnet_common::gameplay_style::JumpImpulse> for JumpImpulse {
fn into(self) -> strafesnet_common::gameplay_style::JumpImpulse {
match self {
JumpImpulse::Time(time) => strafesnet_common::gameplay_style::JumpImpulse::Time(
strafesnet_common::integer::Time::raw(time),
),
JumpImpulse::Height(height) => strafesnet_common::gameplay_style::JumpImpulse::Height(
strafesnet_common::integer::Planar64::raw(height),
),
JumpImpulse::Linear(deltav) => strafesnet_common::gameplay_style::JumpImpulse::Linear(
strafesnet_common::integer::Planar64::raw(deltav),
),
JumpImpulse::Energy(energy) => strafesnet_common::gameplay_style::JumpImpulse::Energy(
strafesnet_common::integer::Planar64::raw(energy),
),
}
}
}
impl From<strafesnet_common::gameplay_style::JumpImpulse> for JumpImpulse{
fn from(value:strafesnet_common::gameplay_style::JumpImpulse)->Self{
match value{
strafesnet_common::gameplay_style::JumpImpulse::Time(time)=>JumpImpulse::Time(time.get()),
strafesnet_common::gameplay_style::JumpImpulse::Height(height)=>JumpImpulse::Height(height.to_raw()),
strafesnet_common::gameplay_style::JumpImpulse::Linear(deltav)=>JumpImpulse::Linear(deltav.to_raw()),
strafesnet_common::gameplay_style::JumpImpulse::Energy(energy)=>JumpImpulse::Energy(energy.to_raw()),
}
}
impl From<strafesnet_common::gameplay_style::JumpImpulse> for JumpImpulse {
fn from(value: strafesnet_common::gameplay_style::JumpImpulse) -> Self {
match value {
strafesnet_common::gameplay_style::JumpImpulse::Time(time) => {
JumpImpulse::Time(time.get())
}
strafesnet_common::gameplay_style::JumpImpulse::Height(height) => {
JumpImpulse::Height(height.to_raw())
}
strafesnet_common::gameplay_style::JumpImpulse::Linear(deltav) => {
JumpImpulse::Linear(deltav.to_raw())
}
strafesnet_common::gameplay_style::JumpImpulse::Energy(energy) => {
JumpImpulse::Energy(energy.to_raw())
}
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct ControlsActivation{
controls_mask:Controls,
controls_intersects:Controls,
controls_contains:Controls,
pub struct ControlsActivation {
controls_mask: Controls,
controls_intersects: Controls,
controls_contains: Controls,
}
impl TryInto<strafesnet_common::gameplay_style::ControlsActivation> for ControlsActivation{
type Error=ControlsError;
fn try_into(self)->Result<strafesnet_common::gameplay_style::ControlsActivation,Self::Error>{
Ok(strafesnet_common::gameplay_style::ControlsActivation{
controls_mask:strafesnet_common::controls_bitflag::Controls::from_bits(self.controls_mask).ok_or(ControlsError::UnknownBits)?,
controls_intersects:strafesnet_common::controls_bitflag::Controls::from_bits(self.controls_intersects).ok_or(ControlsError::UnknownBits)?,
controls_contains:strafesnet_common::controls_bitflag::Controls::from_bits(self.controls_contains).ok_or(ControlsError::UnknownBits)?,
})
}
impl TryInto<strafesnet_common::gameplay_style::ControlsActivation> for ControlsActivation {
type Error = ControlsError;
fn try_into(
self,
) -> Result<strafesnet_common::gameplay_style::ControlsActivation, Self::Error> {
Ok(strafesnet_common::gameplay_style::ControlsActivation {
controls_mask: strafesnet_common::controls_bitflag::Controls::from_bits(
self.controls_mask,
)
.ok_or(ControlsError::UnknownBits)?,
controls_intersects: strafesnet_common::controls_bitflag::Controls::from_bits(
self.controls_intersects,
)
.ok_or(ControlsError::UnknownBits)?,
controls_contains: strafesnet_common::controls_bitflag::Controls::from_bits(
self.controls_contains,
)
.ok_or(ControlsError::UnknownBits)?,
})
}
}
impl From<strafesnet_common::gameplay_style::ControlsActivation> for ControlsActivation{
fn from(value:strafesnet_common::gameplay_style::ControlsActivation)->Self{
Self{
controls_mask:value.controls_mask.bits(),
controls_intersects:value.controls_intersects.bits(),
controls_contains:value.controls_contains.bits(),
}
}
impl From<strafesnet_common::gameplay_style::ControlsActivation> for ControlsActivation {
fn from(value: strafesnet_common::gameplay_style::ControlsActivation) -> Self {
Self {
controls_mask: value.controls_mask.bits(),
controls_intersects: value.controls_intersects.bits(),
controls_contains: value.controls_contains.bits(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct StrafeSettings{
header:u8,
enable:ControlsActivation,
mv:Planar64,
#[br(if(header&Self::AIR_ACCEL_LIMIT!=0))]
air_accel_limit:Option<Planar64>,
tick_rate:Ratio64,
pub struct StrafeSettings {
header: u8,
enable: ControlsActivation,
mv: Planar64,
#[br(if(header&Self::AIR_ACCEL_LIMIT!=0))]
air_accel_limit: Option<Planar64>,
tick_rate: Ratio64,
}
impl StrafeSettings{
const AIR_ACCEL_LIMIT:u8=1<<0;
impl StrafeSettings {
const AIR_ACCEL_LIMIT: u8 = 1 << 0;
}
#[derive(Debug)]
pub enum StrafeSettingsError{
Ratio(super::integer::RatioError),
Controls(ControlsError),
pub enum StrafeSettingsError {
Ratio(super::integer::RatioError),
Controls(ControlsError),
}
impl std::fmt::Display for StrafeSettingsError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for StrafeSettingsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for StrafeSettingsError{}
impl TryInto<strafesnet_common::gameplay_style::StrafeSettings> for StrafeSettings{
type Error=StrafeSettingsError;
fn try_into(self)->Result<strafesnet_common::gameplay_style::StrafeSettings,Self::Error>{
Ok(strafesnet_common::gameplay_style::StrafeSettings{
enable:self.enable.try_into().map_err(StrafeSettingsError::Controls)?,
mv:strafesnet_common::integer::Planar64::raw(self.mv),
air_accel_limit:self.air_accel_limit.map(strafesnet_common::integer::Planar64::raw),
tick_rate:self.tick_rate.try_into().map_err(StrafeSettingsError::Ratio)?,
})
}
impl std::error::Error for StrafeSettingsError {}
impl TryInto<strafesnet_common::gameplay_style::StrafeSettings> for StrafeSettings {
type Error = StrafeSettingsError;
fn try_into(self) -> Result<strafesnet_common::gameplay_style::StrafeSettings, Self::Error> {
Ok(strafesnet_common::gameplay_style::StrafeSettings {
enable: self
.enable
.try_into()
.map_err(StrafeSettingsError::Controls)?,
mv: strafesnet_common::integer::Planar64::raw(self.mv),
air_accel_limit: self
.air_accel_limit
.map(strafesnet_common::integer::Planar64::raw),
tick_rate: self
.tick_rate
.try_into()
.map_err(StrafeSettingsError::Ratio)?,
})
}
}
impl From<strafesnet_common::gameplay_style::StrafeSettings> for StrafeSettings{
fn from(value:strafesnet_common::gameplay_style::StrafeSettings)->Self{
let header=flag(value.air_accel_limit.is_some(),StrafeSettings::AIR_ACCEL_LIMIT);
Self{
header,
enable:value.enable.into(),
mv:value.mv.to_raw(),
air_accel_limit:value.air_accel_limit.map(|a|a.to_raw()),
tick_rate:value.tick_rate.into(),
}
}
impl From<strafesnet_common::gameplay_style::StrafeSettings> for StrafeSettings {
fn from(value: strafesnet_common::gameplay_style::StrafeSettings) -> Self {
let header = flag(
value.air_accel_limit.is_some(),
StrafeSettings::AIR_ACCEL_LIMIT,
);
Self {
header,
enable: value.enable.into(),
mv: value.mv.to_raw(),
air_accel_limit: value.air_accel_limit.map(|a| a.to_raw()),
tick_rate: value.tick_rate.into(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct PropulsionSettings{
magnitude:Planar64,
pub struct PropulsionSettings {
magnitude: Planar64,
}
impl Into<strafesnet_common::gameplay_style::PropulsionSettings> for PropulsionSettings{
fn into(self)->strafesnet_common::gameplay_style::PropulsionSettings{
strafesnet_common::gameplay_style::PropulsionSettings{
magnitude:strafesnet_common::integer::Planar64::raw(self.magnitude)
}
}
impl Into<strafesnet_common::gameplay_style::PropulsionSettings> for PropulsionSettings {
fn into(self) -> strafesnet_common::gameplay_style::PropulsionSettings {
strafesnet_common::gameplay_style::PropulsionSettings {
magnitude: strafesnet_common::integer::Planar64::raw(self.magnitude),
}
}
}
impl From<strafesnet_common::gameplay_style::PropulsionSettings> for PropulsionSettings{
fn from(value:strafesnet_common::gameplay_style::PropulsionSettings)->Self{
Self{
magnitude:value.magnitude.to_raw(),
}
}
impl From<strafesnet_common::gameplay_style::PropulsionSettings> for PropulsionSettings {
fn from(value: strafesnet_common::gameplay_style::PropulsionSettings) -> Self {
Self {
magnitude: value.magnitude.to_raw(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct JumpSettings{
header:u8,
impulse:i64,
pub struct JumpSettings {
header: u8,
impulse: i64,
}
impl JumpSettings{
const IMPULSE:u8=0b00011;
const CALCULATION:u8=0b01100;
const LIMIT_MINIMUM:u8=0b10000;
const fn impulse(&self)->Option<strafesnet_common::gameplay_style::JumpImpulse>{
match self.header&Self::IMPULSE{
0=>Some(strafesnet_common::gameplay_style::JumpImpulse::Time(strafesnet_common::integer::Time::raw(self.impulse))),
1=>Some(strafesnet_common::gameplay_style::JumpImpulse::Height(strafesnet_common::integer::Planar64::raw(self.impulse))),
2=>Some(strafesnet_common::gameplay_style::JumpImpulse::Linear(strafesnet_common::integer::Planar64::raw(self.impulse))),
3=>Some(strafesnet_common::gameplay_style::JumpImpulse::Energy(strafesnet_common::integer::Planar64::raw(self.impulse))),
_=>None,
}
}
const fn calculation(&self)->Option<strafesnet_common::gameplay_style::JumpCalculation>{
match (self.header&Self::CALCULATION)>>2{
0=>Some(strafesnet_common::gameplay_style::JumpCalculation::Max),
1=>Some(strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost),
2=>Some(strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump),
_=>None,
}
}
const fn limit_minimum(&self)->bool{
self.header&Self::LIMIT_MINIMUM!=0
}
impl JumpSettings {
const IMPULSE: u8 = 0b00011;
const CALCULATION: u8 = 0b01100;
const LIMIT_MINIMUM: u8 = 0b10000;
const fn impulse(&self) -> Option<strafesnet_common::gameplay_style::JumpImpulse> {
match self.header & Self::IMPULSE {
0 => Some(strafesnet_common::gameplay_style::JumpImpulse::Time(
strafesnet_common::integer::Time::raw(self.impulse),
)),
1 => Some(strafesnet_common::gameplay_style::JumpImpulse::Height(
strafesnet_common::integer::Planar64::raw(self.impulse),
)),
2 => Some(strafesnet_common::gameplay_style::JumpImpulse::Linear(
strafesnet_common::integer::Planar64::raw(self.impulse),
)),
3 => Some(strafesnet_common::gameplay_style::JumpImpulse::Energy(
strafesnet_common::integer::Planar64::raw(self.impulse),
)),
_ => None,
}
}
const fn calculation(&self) -> Option<strafesnet_common::gameplay_style::JumpCalculation> {
match (self.header & Self::CALCULATION) >> 2 {
0 => Some(strafesnet_common::gameplay_style::JumpCalculation::Max),
1 => Some(strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost),
2 => Some(strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump),
_ => None,
}
}
const fn limit_minimum(&self) -> bool {
self.header & Self::LIMIT_MINIMUM != 0
}
}
#[derive(Debug)]
pub enum JumpSettingsError{
InvalidImpulseDiscriminant,
InvalidCalculationDiscriminant,
pub enum JumpSettingsError {
InvalidImpulseDiscriminant,
InvalidCalculationDiscriminant,
}
impl TryInto<strafesnet_common::gameplay_style::JumpSettings> for JumpSettings{
type Error=JumpSettingsError;
fn try_into(self)->Result<strafesnet_common::gameplay_style::JumpSettings,Self::Error>{
Ok(strafesnet_common::gameplay_style::JumpSettings{
impulse:self.impulse().ok_or(JumpSettingsError::InvalidImpulseDiscriminant)?,
calculation:self.calculation().ok_or(JumpSettingsError::InvalidCalculationDiscriminant)?,
limit_minimum:self.limit_minimum(),
})
}
impl TryInto<strafesnet_common::gameplay_style::JumpSettings> for JumpSettings {
type Error = JumpSettingsError;
fn try_into(self) -> Result<strafesnet_common::gameplay_style::JumpSettings, Self::Error> {
Ok(strafesnet_common::gameplay_style::JumpSettings {
impulse: self
.impulse()
.ok_or(JumpSettingsError::InvalidImpulseDiscriminant)?,
calculation: self
.calculation()
.ok_or(JumpSettingsError::InvalidCalculationDiscriminant)?,
limit_minimum: self.limit_minimum(),
})
}
}
impl From<strafesnet_common::gameplay_style::JumpSettings> for JumpSettings{
fn from(value:strafesnet_common::gameplay_style::JumpSettings)->Self{
let (impulse,impulse_header)=match value.impulse{
strafesnet_common::gameplay_style::JumpImpulse::Time(impulse)=>(impulse.get(),0),
strafesnet_common::gameplay_style::JumpImpulse::Height(impulse)=>(impulse.to_raw(),1),
strafesnet_common::gameplay_style::JumpImpulse::Linear(impulse)=>(impulse.to_raw(),2),
strafesnet_common::gameplay_style::JumpImpulse::Energy(impulse)=>(impulse.to_raw(),3),
};
let calculation_header=match value.calculation{
strafesnet_common::gameplay_style::JumpCalculation::Max=>0,
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost=>1,
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump=>2,
};
let header=
impulse_header
|(calculation_header<<2)
|((value.limit_minimum as u8)<<4);
Self{
header,
impulse,
}
}
impl From<strafesnet_common::gameplay_style::JumpSettings> for JumpSettings {
fn from(value: strafesnet_common::gameplay_style::JumpSettings) -> Self {
let (impulse, impulse_header) = match value.impulse {
strafesnet_common::gameplay_style::JumpImpulse::Time(impulse) => (impulse.get(), 0),
strafesnet_common::gameplay_style::JumpImpulse::Height(impulse) => {
(impulse.to_raw(), 1)
}
strafesnet_common::gameplay_style::JumpImpulse::Linear(impulse) => {
(impulse.to_raw(), 2)
}
strafesnet_common::gameplay_style::JumpImpulse::Energy(impulse) => {
(impulse.to_raw(), 3)
}
};
let calculation_header = match value.calculation {
strafesnet_common::gameplay_style::JumpCalculation::Max => 0,
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost => 1,
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump => 2,
};
let header =
impulse_header | (calculation_header << 2) | ((value.limit_minimum as u8) << 4);
Self { header, impulse }
}
}
#[binrw::binrw]
#[brw(little)]
pub struct AccelerateSettings{
accel:Planar64,
topspeed:Planar64,
pub struct AccelerateSettings {
accel: Planar64,
topspeed: Planar64,
}
impl Into<strafesnet_common::gameplay_style::AccelerateSettings> for AccelerateSettings{
fn into(self)->strafesnet_common::gameplay_style::AccelerateSettings{
strafesnet_common::gameplay_style::AccelerateSettings{
accel:strafesnet_common::integer::Planar64::raw(self.accel),
topspeed:strafesnet_common::integer::Planar64::raw(self.topspeed),
}
}
impl Into<strafesnet_common::gameplay_style::AccelerateSettings> for AccelerateSettings {
fn into(self) -> strafesnet_common::gameplay_style::AccelerateSettings {
strafesnet_common::gameplay_style::AccelerateSettings {
accel: strafesnet_common::integer::Planar64::raw(self.accel),
topspeed: strafesnet_common::integer::Planar64::raw(self.topspeed),
}
}
}
impl From<strafesnet_common::gameplay_style::AccelerateSettings> for AccelerateSettings{
fn from(value:strafesnet_common::gameplay_style::AccelerateSettings)->Self{
Self{
accel:value.accel.to_raw(),
topspeed:value.topspeed.to_raw(),
}
}
impl From<strafesnet_common::gameplay_style::AccelerateSettings> for AccelerateSettings {
fn from(value: strafesnet_common::gameplay_style::AccelerateSettings) -> Self {
Self {
accel: value.accel.to_raw(),
topspeed: value.topspeed.to_raw(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct WalkSettings{
accelerate:AccelerateSettings,
static_friction:Planar64,
kinetic_friction:Planar64,
surf_dot:Planar64,
pub struct WalkSettings {
accelerate: AccelerateSettings,
static_friction: Planar64,
kinetic_friction: Planar64,
surf_dot: Planar64,
}
impl Into<strafesnet_common::gameplay_style::WalkSettings> for WalkSettings{
fn into(self)->strafesnet_common::gameplay_style::WalkSettings{
strafesnet_common::gameplay_style::WalkSettings{
accelerate:self.accelerate.into(),
static_friction:strafesnet_common::integer::Planar64::raw(self.static_friction),
kinetic_friction:strafesnet_common::integer::Planar64::raw(self.kinetic_friction),
surf_dot:strafesnet_common::integer::Planar64::raw(self.surf_dot),
}
}
impl Into<strafesnet_common::gameplay_style::WalkSettings> for WalkSettings {
fn into(self) -> strafesnet_common::gameplay_style::WalkSettings {
strafesnet_common::gameplay_style::WalkSettings {
accelerate: self.accelerate.into(),
static_friction: strafesnet_common::integer::Planar64::raw(self.static_friction),
kinetic_friction: strafesnet_common::integer::Planar64::raw(self.kinetic_friction),
surf_dot: strafesnet_common::integer::Planar64::raw(self.surf_dot),
}
}
}
impl From<strafesnet_common::gameplay_style::WalkSettings> for WalkSettings{
fn from(value:strafesnet_common::gameplay_style::WalkSettings)->Self{
Self{
accelerate:value.accelerate.into(),
static_friction:value.static_friction.to_raw(),
kinetic_friction:value.kinetic_friction.to_raw(),
surf_dot:value.surf_dot.to_raw(),
}
}
impl From<strafesnet_common::gameplay_style::WalkSettings> for WalkSettings {
fn from(value: strafesnet_common::gameplay_style::WalkSettings) -> Self {
Self {
accelerate: value.accelerate.into(),
static_friction: value.static_friction.to_raw(),
kinetic_friction: value.kinetic_friction.to_raw(),
surf_dot: value.surf_dot.to_raw(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct LadderSettings{
accelerate:AccelerateSettings,
dot:Planar64,
pub struct LadderSettings {
accelerate: AccelerateSettings,
dot: Planar64,
}
impl Into<strafesnet_common::gameplay_style::LadderSettings> for LadderSettings{
fn into(self)->strafesnet_common::gameplay_style::LadderSettings{
strafesnet_common::gameplay_style::LadderSettings{
accelerate:self.accelerate.into(),
dot:strafesnet_common::integer::Planar64::raw(self.dot),
}
}
impl Into<strafesnet_common::gameplay_style::LadderSettings> for LadderSettings {
fn into(self) -> strafesnet_common::gameplay_style::LadderSettings {
strafesnet_common::gameplay_style::LadderSettings {
accelerate: self.accelerate.into(),
dot: strafesnet_common::integer::Planar64::raw(self.dot),
}
}
}
impl From<strafesnet_common::gameplay_style::LadderSettings> for LadderSettings{
fn from(value:strafesnet_common::gameplay_style::LadderSettings)->Self{
Self{
accelerate:value.accelerate.into(),
dot:value.dot.to_raw(),
}
}
impl From<strafesnet_common::gameplay_style::LadderSettings> for LadderSettings {
fn from(value: strafesnet_common::gameplay_style::LadderSettings) -> Self {
Self {
accelerate: value.accelerate.into(),
dot: value.dot.to_raw(),
}
}
}
#[binrw::binrw]
#[brw(little,repr=u8)]
pub enum HitboxMesh{
Box,
Cylinder,
pub enum HitboxMesh {
Box,
Cylinder,
}
impl Into<strafesnet_common::gameplay_style::HitboxMesh> for HitboxMesh{
fn into(self)->strafesnet_common::gameplay_style::HitboxMesh{
match self{
HitboxMesh::Box=>strafesnet_common::gameplay_style::HitboxMesh::Box,
HitboxMesh::Cylinder=>strafesnet_common::gameplay_style::HitboxMesh::Cylinder,
}
}
impl Into<strafesnet_common::gameplay_style::HitboxMesh> for HitboxMesh {
fn into(self) -> strafesnet_common::gameplay_style::HitboxMesh {
match self {
HitboxMesh::Box => strafesnet_common::gameplay_style::HitboxMesh::Box,
HitboxMesh::Cylinder => strafesnet_common::gameplay_style::HitboxMesh::Cylinder,
}
}
}
impl From<strafesnet_common::gameplay_style::HitboxMesh> for HitboxMesh{
fn from(value:strafesnet_common::gameplay_style::HitboxMesh)->Self{
match value{
strafesnet_common::gameplay_style::HitboxMesh::Box=>HitboxMesh::Box,
strafesnet_common::gameplay_style::HitboxMesh::Cylinder=>HitboxMesh::Cylinder,
}
}
impl From<strafesnet_common::gameplay_style::HitboxMesh> for HitboxMesh {
fn from(value: strafesnet_common::gameplay_style::HitboxMesh) -> Self {
match value {
strafesnet_common::gameplay_style::HitboxMesh::Box => HitboxMesh::Box,
strafesnet_common::gameplay_style::HitboxMesh::Cylinder => HitboxMesh::Cylinder,
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Hitbox{
pub halfsize:Planar64Vec3,
pub mesh:HitboxMesh,
pub struct Hitbox {
pub halfsize: Planar64Vec3,
pub mesh: HitboxMesh,
}
impl Into<strafesnet_common::gameplay_style::Hitbox> for Hitbox{
fn into(self)->strafesnet_common::gameplay_style::Hitbox{
strafesnet_common::gameplay_style::Hitbox{
halfsize:strafesnet_common::integer::vec3::raw_array(self.halfsize),
mesh:self.mesh.into(),
}
}
impl Into<strafesnet_common::gameplay_style::Hitbox> for Hitbox {
fn into(self) -> strafesnet_common::gameplay_style::Hitbox {
strafesnet_common::gameplay_style::Hitbox {
halfsize: strafesnet_common::integer::vec3::raw_array(self.halfsize),
mesh: self.mesh.into(),
}
}
}
impl From<strafesnet_common::gameplay_style::Hitbox> for Hitbox{
fn from(value:strafesnet_common::gameplay_style::Hitbox)->Self{
Self{
halfsize:value.halfsize.map(|t|t.to_raw()).to_array(),
mesh:value.mesh.into(),
}
}
impl From<strafesnet_common::gameplay_style::Hitbox> for Hitbox {
fn from(value: strafesnet_common::gameplay_style::Hitbox) -> Self {
Self {
halfsize: value.halfsize.map(|t| t.to_raw()).to_array(),
mesh: value.mesh.into(),
}
}
}

View File

@ -1,46 +1,46 @@
pub type Time=i64;
pub type Time = i64;
#[binrw::binrw]
#[brw(little)]
pub struct Ratio64{
num:i64,
den:u64,
pub struct Ratio64 {
num: i64,
den: u64,
}
#[derive(Debug)]
pub enum RatioError{
ZeroDenominator,
pub enum RatioError {
ZeroDenominator,
}
impl std::fmt::Display for RatioError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for RatioError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for RatioError{}
impl TryInto<strafesnet_common::integer::Ratio64> for Ratio64{
type Error=RatioError;
fn try_into(self)->Result<strafesnet_common::integer::Ratio64,Self::Error>{
strafesnet_common::integer::Ratio64::new(self.num,self.den)
.ok_or(RatioError::ZeroDenominator)
}
impl std::error::Error for RatioError {}
impl TryInto<strafesnet_common::integer::Ratio64> for Ratio64 {
type Error = RatioError;
fn try_into(self) -> Result<strafesnet_common::integer::Ratio64, Self::Error> {
strafesnet_common::integer::Ratio64::new(self.num, self.den)
.ok_or(RatioError::ZeroDenominator)
}
}
impl From<strafesnet_common::integer::Ratio64> for Ratio64{
fn from(value:strafesnet_common::integer::Ratio64)->Self{
Self{
num:value.num(),
den:value.den(),
}
}
impl From<strafesnet_common::integer::Ratio64> for Ratio64 {
fn from(value: strafesnet_common::integer::Ratio64) -> Self {
Self {
num: value.num(),
den: value.den(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Ratio64Vec2{
pub x:Ratio64,
pub y:Ratio64,
pub struct Ratio64Vec2 {
pub x: Ratio64,
pub y: Ratio64,
}
pub type Angle32=i32;
pub type Planar64=i64;
pub type Planar64Vec3=[i64;3];
pub type Planar64Mat3=[i64;9];
pub type Planar64Affine3=[i64;12];
pub type Angle32 = i32;
pub type Planar64 = i64;
pub type Planar64Vec3 = [i64; 3];
pub type Planar64Mat3 = [i64; 9];
pub type Planar64Affine3 = [i64; 12];

View File

@ -1,276 +1,342 @@
use super::common::flag;
use strafesnet_common::model::PolygonIter;
use super::integer::{Planar64Vec3,Planar64Affine3};
use super::integer::{Planar64Affine3, Planar64Vec3};
pub type TextureCoordinate=[f32;2];
pub type Color4=[f32;4];
pub type TextureCoordinate = [f32; 2];
pub type Color4 = [f32; 4];
#[binrw::binrw]
#[brw(little)]
pub struct IndexedVertex{
pub pos:u32,
pub tex:u32,
pub normal:u32,
pub color:u32,
pub struct IndexedVertex {
pub pos: u32,
pub tex: u32,
pub normal: u32,
pub color: u32,
}
impl From<strafesnet_common::model::IndexedVertex> for IndexedVertex{
fn from(value:strafesnet_common::model::IndexedVertex)->Self{
Self{
pos:value.pos.get(),
tex:value.tex.get(),
normal:value.normal.get(),
color:value.color.get(),
}
}
impl From<strafesnet_common::model::IndexedVertex> for IndexedVertex {
fn from(value: strafesnet_common::model::IndexedVertex) -> Self {
Self {
pos: value.pos.get(),
tex: value.tex.get(),
normal: value.normal.get(),
color: value.color.get(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Polygon{
pub count:u32,
#[br(count=count)]
pub vertices:Vec<u32>,
pub struct Polygon {
pub count: u32,
#[br(count=count)]
pub vertices: Vec<u32>,
}
#[binrw::binrw]
#[brw(little)]
pub struct PolygonGroup{
pub count:u32,
#[br(count=count)]
pub polys:Vec<Polygon>,
pub struct PolygonGroup {
pub count: u32,
#[br(count=count)]
pub polys: Vec<Polygon>,
}
impl From<strafesnet_common::model::PolygonGroup> for PolygonGroup{
fn from(value:strafesnet_common::model::PolygonGroup)->Self{
match value{
strafesnet_common::model::PolygonGroup::PolygonList(polygon_list)=>{
let polys:Vec<Polygon>=polygon_list.polys().map(|poly|{
let vertices:Vec<u32>=poly.iter().map(|vert|vert.get()).collect();
Polygon{
count:vertices.len() as u32,
vertices,
}
}).collect();
Self{
count:polys.len() as u32,
polys,
}
}
}
}
impl From<strafesnet_common::model::PolygonGroup> for PolygonGroup {
fn from(value: strafesnet_common::model::PolygonGroup) -> Self {
match value {
strafesnet_common::model::PolygonGroup::PolygonList(polygon_list) => {
let polys: Vec<Polygon> = polygon_list
.polys()
.map(|poly| {
let vertices: Vec<u32> = poly.iter().map(|vert| vert.get()).collect();
Polygon {
count: vertices.len() as u32,
vertices,
}
})
.collect();
Self {
count: polys.len() as u32,
polys,
}
}
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct RenderConfig{
pub header:u8,
#[br(if(header&Self::TEXTURE!=0))]
pub texture:Option<u32>,
pub struct RenderConfig {
pub header: u8,
#[br(if(header&Self::TEXTURE!=0))]
pub texture: Option<u32>,
}
impl RenderConfig{
const TEXTURE:u8=1<<0;
impl RenderConfig {
const TEXTURE: u8 = 1 << 0;
}
impl Into<strafesnet_common::model::RenderConfig> for RenderConfig{
fn into(self)->strafesnet_common::model::RenderConfig{
strafesnet_common::model::RenderConfig{
texture:self.texture.map(strafesnet_common::model::TextureId::new),
}
}
impl Into<strafesnet_common::model::RenderConfig> for RenderConfig {
fn into(self) -> strafesnet_common::model::RenderConfig {
strafesnet_common::model::RenderConfig {
texture: self.texture.map(strafesnet_common::model::TextureId::new),
}
}
}
impl From<strafesnet_common::model::RenderConfig> for RenderConfig{
fn from(value:strafesnet_common::model::RenderConfig)->Self{
let header=flag(value.texture.is_some(),RenderConfig::TEXTURE);
Self{
header,
texture:value.texture.map(|texture_id|texture_id.get()),
}
}
impl From<strafesnet_common::model::RenderConfig> for RenderConfig {
fn from(value: strafesnet_common::model::RenderConfig) -> Self {
let header = flag(value.texture.is_some(), RenderConfig::TEXTURE);
Self {
header,
texture: value.texture.map(|texture_id| texture_id.get()),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct IndexedGraphicsGroup{
pub count:u32,
pub render:u32,
#[br(count=count)]
pub groups:Vec<u32>,
pub struct IndexedGraphicsGroup {
pub count: u32,
pub render: u32,
#[br(count=count)]
pub groups: Vec<u32>,
}
impl From<strafesnet_common::model::IndexedGraphicsGroup> for IndexedGraphicsGroup{
fn from(value:strafesnet_common::model::IndexedGraphicsGroup)->Self{
Self{
count:value.groups.len() as u32,
render:value.render.get(),
groups:value.groups.into_iter().map(|group_id|group_id.get()).collect(),
}
}
impl From<strafesnet_common::model::IndexedGraphicsGroup> for IndexedGraphicsGroup {
fn from(value: strafesnet_common::model::IndexedGraphicsGroup) -> Self {
Self {
count: value.groups.len() as u32,
render: value.render.get(),
groups: value
.groups
.into_iter()
.map(|group_id| group_id.get())
.collect(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct IndexedPhysicsGroup{
pub count:u32,
#[br(count=count)]
pub groups:Vec<u32>,
pub struct IndexedPhysicsGroup {
pub count: u32,
#[br(count=count)]
pub groups: Vec<u32>,
}
impl From<strafesnet_common::model::IndexedPhysicsGroup> for IndexedPhysicsGroup{
fn from(value:strafesnet_common::model::IndexedPhysicsGroup)->Self{
Self{
count:value.groups.len() as u32,
groups:value.groups.into_iter().map(|group_id|group_id.get()).collect(),
}
}
impl From<strafesnet_common::model::IndexedPhysicsGroup> for IndexedPhysicsGroup {
fn from(value: strafesnet_common::model::IndexedPhysicsGroup) -> Self {
Self {
count: value.groups.len() as u32,
groups: value
.groups
.into_iter()
.map(|group_id| group_id.get())
.collect(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct MeshHeader{
pub unique_pos:u32,
pub unique_normal:u32,
pub unique_tex:u32,
pub unique_color:u32,
pub unique_vertices:u32,
pub polygon_groups:u32,
pub graphics_groups:u32,
pub physics_groups:u32,
pub struct MeshHeader {
pub unique_pos: u32,
pub unique_normal: u32,
pub unique_tex: u32,
pub unique_color: u32,
pub unique_vertices: u32,
pub polygon_groups: u32,
pub graphics_groups: u32,
pub physics_groups: u32,
}
#[binrw::binrw]
#[brw(little)]
pub struct Mesh{
pub header:MeshHeader,
#[br(count=header.unique_pos)]
pub unique_pos:Vec<Planar64Vec3>,
#[br(count=header.unique_normal)]
pub unique_normal:Vec<Planar64Vec3>,
#[br(count=header.unique_tex)]
pub unique_tex:Vec<TextureCoordinate>,
#[br(count=header.unique_color)]
pub unique_color:Vec<Color4>,
#[br(count=header.unique_vertices)]
pub unique_vertices:Vec<IndexedVertex>,
#[br(count=header.polygon_groups)]
pub polygon_groups:Vec<PolygonGroup>,
#[br(count=header.graphics_groups)]
pub graphics_groups:Vec<IndexedGraphicsGroup>,
#[br(count=header.physics_groups)]
pub physics_groups:Vec<IndexedPhysicsGroup>,
pub struct Mesh {
pub header: MeshHeader,
#[br(count=header.unique_pos)]
pub unique_pos: Vec<Planar64Vec3>,
#[br(count=header.unique_normal)]
pub unique_normal: Vec<Planar64Vec3>,
#[br(count=header.unique_tex)]
pub unique_tex: Vec<TextureCoordinate>,
#[br(count=header.unique_color)]
pub unique_color: Vec<Color4>,
#[br(count=header.unique_vertices)]
pub unique_vertices: Vec<IndexedVertex>,
#[br(count=header.polygon_groups)]
pub polygon_groups: Vec<PolygonGroup>,
#[br(count=header.graphics_groups)]
pub graphics_groups: Vec<IndexedGraphicsGroup>,
#[br(count=header.physics_groups)]
pub physics_groups: Vec<IndexedPhysicsGroup>,
}
impl Into<strafesnet_common::model::Mesh> for Mesh{
fn into(self)->strafesnet_common::model::Mesh{
strafesnet_common::model::Mesh{
unique_pos:self.unique_pos.into_iter().map(strafesnet_common::integer::vec3::raw_array).collect(),
unique_normal:self.unique_normal.into_iter().map(strafesnet_common::integer::vec3::raw_array).collect(),
unique_tex:self.unique_tex.into_iter().map(strafesnet_common::model::TextureCoordinate::from_array).collect(),
unique_color:self.unique_color.into_iter().map(strafesnet_common::model::Color4::from_array).collect(),
unique_vertices:self.unique_vertices.into_iter().map(|vert|strafesnet_common::model::IndexedVertex{
pos:strafesnet_common::model::PositionId::new(vert.pos),
tex:strafesnet_common::model::TextureCoordinateId::new(vert.tex),
normal:strafesnet_common::model::NormalId::new(vert.normal),
color:strafesnet_common::model::ColorId::new(vert.color),
}).collect(),
polygon_groups:self.polygon_groups.into_iter().map(|group|
strafesnet_common::model::PolygonGroup::PolygonList(
strafesnet_common::model::PolygonList::new(
group.polys.into_iter().map(|vert|
vert.vertices.into_iter().map(strafesnet_common::model::VertexId::new).collect()
).collect()
)
)
).collect(),
graphics_groups:self.graphics_groups.into_iter().map(|group|
strafesnet_common::model::IndexedGraphicsGroup{
render:strafesnet_common::model::RenderConfigId::new(group.render),
groups:group.groups.into_iter().map(strafesnet_common::model::PolygonGroupId::new).collect(),
}
).collect(),
physics_groups:self.physics_groups.into_iter().map(|group|
strafesnet_common::model::IndexedPhysicsGroup{
groups:group.groups.into_iter().map(strafesnet_common::model::PolygonGroupId::new).collect(),
}
).collect(),
}
}
impl Into<strafesnet_common::model::Mesh> for Mesh {
fn into(self) -> strafesnet_common::model::Mesh {
strafesnet_common::model::Mesh {
unique_pos: self
.unique_pos
.into_iter()
.map(strafesnet_common::integer::vec3::raw_array)
.collect(),
unique_normal: self
.unique_normal
.into_iter()
.map(strafesnet_common::integer::vec3::raw_array)
.collect(),
unique_tex: self
.unique_tex
.into_iter()
.map(strafesnet_common::model::TextureCoordinate::from_array)
.collect(),
unique_color: self
.unique_color
.into_iter()
.map(strafesnet_common::model::Color4::from_array)
.collect(),
unique_vertices: self
.unique_vertices
.into_iter()
.map(|vert| strafesnet_common::model::IndexedVertex {
pos: strafesnet_common::model::PositionId::new(vert.pos),
tex: strafesnet_common::model::TextureCoordinateId::new(vert.tex),
normal: strafesnet_common::model::NormalId::new(vert.normal),
color: strafesnet_common::model::ColorId::new(vert.color),
})
.collect(),
polygon_groups: self
.polygon_groups
.into_iter()
.map(|group| {
strafesnet_common::model::PolygonGroup::PolygonList(
strafesnet_common::model::PolygonList::new(
group
.polys
.into_iter()
.map(|vert| {
vert.vertices
.into_iter()
.map(strafesnet_common::model::VertexId::new)
.collect()
})
.collect(),
),
)
})
.collect(),
graphics_groups: self
.graphics_groups
.into_iter()
.map(|group| strafesnet_common::model::IndexedGraphicsGroup {
render: strafesnet_common::model::RenderConfigId::new(group.render),
groups: group
.groups
.into_iter()
.map(strafesnet_common::model::PolygonGroupId::new)
.collect(),
})
.collect(),
physics_groups: self
.physics_groups
.into_iter()
.map(|group| strafesnet_common::model::IndexedPhysicsGroup {
groups: group
.groups
.into_iter()
.map(strafesnet_common::model::PolygonGroupId::new)
.collect(),
})
.collect(),
}
}
}
impl From<strafesnet_common::model::Mesh> for Mesh{
fn from(value:strafesnet_common::model::Mesh)->Self{
Self{
header:MeshHeader{
unique_pos:value.unique_pos.len() as u32,
unique_normal:value.unique_normal.len() as u32,
unique_tex:value.unique_tex.len() as u32,
unique_color:value.unique_color.len() as u32,
unique_vertices:value.unique_vertices.len() as u32,
polygon_groups:value.polygon_groups.len() as u32,
graphics_groups:value.graphics_groups.len() as u32,
physics_groups:value.physics_groups.len() as u32,
},
unique_pos:value.unique_pos.into_iter()
.map(|pos|pos.map(|t|t.to_raw()).to_array())
.collect(),
unique_normal:value.unique_normal.into_iter()
.map(|normal|normal.map(|t|t.to_raw()).to_array())
.collect(),
unique_tex:value.unique_tex.into_iter()
.map(|tex|tex.to_array())
.collect(),
unique_color:value.unique_color.into_iter()
.map(|color|color.to_array())
.collect(),
unique_vertices:value.unique_vertices.into_iter()
.map(Into::into)
.collect(),
polygon_groups:value.polygon_groups.into_iter()
.map(Into::into)
.collect(),
graphics_groups:value.graphics_groups.into_iter()
.map(Into::into)
.collect(),
physics_groups:value.physics_groups.into_iter()
.map(Into::into)
.collect(),
}
}
impl From<strafesnet_common::model::Mesh> for Mesh {
fn from(value: strafesnet_common::model::Mesh) -> Self {
Self {
header: MeshHeader {
unique_pos: value.unique_pos.len() as u32,
unique_normal: value.unique_normal.len() as u32,
unique_tex: value.unique_tex.len() as u32,
unique_color: value.unique_color.len() as u32,
unique_vertices: value.unique_vertices.len() as u32,
polygon_groups: value.polygon_groups.len() as u32,
graphics_groups: value.graphics_groups.len() as u32,
physics_groups: value.physics_groups.len() as u32,
},
unique_pos: value
.unique_pos
.into_iter()
.map(|pos| pos.map(|t| t.to_raw()).to_array())
.collect(),
unique_normal: value
.unique_normal
.into_iter()
.map(|normal| normal.map(|t| t.to_raw()).to_array())
.collect(),
unique_tex: value
.unique_tex
.into_iter()
.map(|tex| tex.to_array())
.collect(),
unique_color: value
.unique_color
.into_iter()
.map(|color| color.to_array())
.collect(),
unique_vertices: value.unique_vertices.into_iter().map(Into::into).collect(),
polygon_groups: value.polygon_groups.into_iter().map(Into::into).collect(),
graphics_groups: value.graphics_groups.into_iter().map(Into::into).collect(),
physics_groups: value.physics_groups.into_iter().map(Into::into).collect(),
}
}
}
#[binrw::binrw]
#[brw(little)]
pub struct Model{
pub mesh:u32,
pub attributes:u32,
pub color:Color4,
pub transform:Planar64Affine3,
pub struct Model {
pub mesh: u32,
pub attributes: u32,
pub color: Color4,
pub transform: Planar64Affine3,
}
impl Into<strafesnet_common::model::Model> for Model{
fn into(self)->strafesnet_common::model::Model{
let [_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b]=self.transform;
strafesnet_common::model::Model{
mesh:strafesnet_common::model::MeshId::new(self.mesh),
attributes:strafesnet_common::gameplay_attributes::CollisionAttributesId::new(self.attributes),
color:strafesnet_common::model::Color4::from_array(self.color),
transform:strafesnet_common::integer::Planar64Affine3::new(
strafesnet_common::integer::Planar64Mat3::from_cols([
strafesnet_common::integer::vec3::raw_xyz(_0,_1,_2),
strafesnet_common::integer::vec3::raw_xyz(_3,_4,_5),
strafesnet_common::integer::vec3::raw_xyz(_6,_7,_8)
]),
strafesnet_common::integer::vec3::raw_xyz(_9,_a,_b)
),
}
}
impl Into<strafesnet_common::model::Model> for Model {
fn into(self) -> strafesnet_common::model::Model {
let [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b] = self.transform;
strafesnet_common::model::Model {
mesh: strafesnet_common::model::MeshId::new(self.mesh),
attributes: strafesnet_common::gameplay_attributes::CollisionAttributesId::new(
self.attributes,
),
color: strafesnet_common::model::Color4::from_array(self.color),
transform: strafesnet_common::integer::Planar64Affine3::new(
strafesnet_common::integer::Planar64Mat3::from_cols([
strafesnet_common::integer::vec3::raw_xyz(_0, _1, _2),
strafesnet_common::integer::vec3::raw_xyz(_3, _4, _5),
strafesnet_common::integer::vec3::raw_xyz(_6, _7, _8),
]),
strafesnet_common::integer::vec3::raw_xyz(_9, _a, _b),
),
}
}
}
impl From<strafesnet_common::model::Model> for Model{
fn from(value:strafesnet_common::model::Model)->Self{
let (
[_0,_1,_2],
[_3,_4,_5],
[_6,_7,_8],
[_9,_a,_b]
)=(
value.transform.matrix3.x_axis.map(|t|t.to_raw()).to_array(),
value.transform.matrix3.y_axis.map(|t|t.to_raw()).to_array(),
value.transform.matrix3.z_axis.map(|t|t.to_raw()).to_array(),
value.transform.translation.map(|t|t.to_raw()).to_array()
);
Self{
mesh:value.mesh.get(),
attributes:value.attributes.get(),
color:value.color.to_array(),
transform:[_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b],
}
}
impl From<strafesnet_common::model::Model> for Model {
fn from(value: strafesnet_common::model::Model) -> Self {
let ([_0, _1, _2], [_3, _4, _5], [_6, _7, _8], [_9, _a, _b]) = (
value
.transform
.matrix3
.x_axis
.map(|t| t.to_raw())
.to_array(),
value
.transform
.matrix3
.y_axis
.map(|t| t.to_raw())
.to_array(),
value
.transform
.matrix3
.z_axis
.map(|t| t.to_raw())
.to_array(),
value.transform.translation.map(|t| t.to_raw()).to_array(),
);
Self {
mesh: value.mesh.get(),
attributes: value.attributes.get(),
color: value.color.to_array(),
transform: [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b],
}
}
}

View File

@ -1,160 +1,182 @@
use strafesnet_common::aabb;
use strafesnet_common::integer::{self,vec3,Time,Planar64,Planar64Vec3};
#[derive(Clone,Copy,Debug,Hash)]
pub struct Body<T>{
pub position:Planar64Vec3,//I64 where 2^32 = 1 u
pub velocity:Planar64Vec3,//I64 where 2^32 = 1 u/s
pub acceleration:Planar64Vec3,//I64 where 2^32 = 1 u/s/s
pub time:Time<T>,//nanoseconds x xxxxD!
use strafesnet_common::integer::{self, vec3, Planar64, Planar64Vec3, Time};
#[derive(Clone, Copy, Debug, Hash)]
pub struct Body<T> {
pub position: Planar64Vec3, //I64 where 2^32 = 1 u
pub velocity: Planar64Vec3, //I64 where 2^32 = 1 u/s
pub acceleration: Planar64Vec3, //I64 where 2^32 = 1 u/s/s
pub time: Time<T>, //nanoseconds x xxxxD!
}
impl<T> std::ops::Neg for Body<T>{
type Output=Self;
fn neg(self)->Self::Output{
Self{
position:self.position,
velocity:-self.velocity,
acceleration:self.acceleration,
time:-self.time,
}
}
impl<T> std::ops::Neg for Body<T> {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
position: self.position,
velocity: -self.velocity,
acceleration: self.acceleration,
time: -self.time,
}
}
}
impl<T> Body<T>
where Time<T>:Copy,
where
Time<T>: Copy,
{
pub const ZERO:Self=Self::new(vec3::ZERO,vec3::ZERO,vec3::ZERO,Time::ZERO);
pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3,time:Time<T>)->Self{
Self{
position,
velocity,
acceleration,
time,
}
}
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
let dt=time-self.time;
self.position
+(self.velocity*dt).map(|elem|elem.divide().fix_1())
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().fix_1())
}
pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{
let dt=time-self.time;
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().fix_1())
}
pub fn advance_time(&mut self,time:Time<T>){
self.position=self.extrapolated_position(time);
self.velocity=self.extrapolated_velocity(time);
self.time=time;
}
pub fn extrapolated_position_ratio_dt<Num,Den,N1,D1,N2,N3,D2,N4,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
where
// Why?
// All of this can be removed with const generics because the type can be specified as
// Ratio<Fixed<N,NF>,Fixed<D,DF>>
// which is known to implement all the necessary traits
Num:Copy,
Den:Copy+core::ops::Mul<i64,Output=D1>,
D1:Copy,
Num:core::ops::Mul<Planar64,Output=N1>,
Planar64:core::ops::Mul<D1,Output=N2>,
N1:core::ops::Add<N2,Output=N3>,
Num:core::ops::Mul<N3,Output=N4>,
Den:core::ops::Mul<D1,Output=D2>,
D2:Copy,
Planar64:core::ops::Mul<D2,Output=N4>,
N4:integer::Divide<D2,Output=T1>,
T1:integer::Fix<Planar64>,
{
// a*dt^2/2 + v*dt + p
// (a*dt/2+v)*dt+p
(self.acceleration.map(|elem|dt*elem/2)+self.velocity).map(|elem|dt.mul_ratio(elem))
.map(|elem|elem.divide().fix())+self.position
}
pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
where
Num:Copy,
Den:Copy,
Num:core::ops::Mul<Planar64,Output=N1>,
Planar64:core::ops::Mul<Den,Output=N1>,
N1:integer::Divide<Den,Output=T1>,
T1:integer::Fix<Planar64>,
{
// a*dt + v
self.acceleration.map(|elem|(dt*elem).divide().fix())+self.velocity
}
pub fn advance_time_ratio_dt(&mut self,dt:crate::model_physics::GigaTime){
self.position=self.extrapolated_position_ratio_dt(dt);
self.velocity=self.extrapolated_velocity_ratio_dt(dt);
self.time+=dt.into();
}
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
if self.velocity==vec3::ZERO{
if self.acceleration==vec3::ZERO{
None
}else{
Some(self.acceleration)
}
}else{
Some(self.velocity)
}
}
pub fn grow_aabb(&self,aabb:&mut aabb::Aabb,t0:Time<T>,t1:Time<T>){
aabb.grow(self.extrapolated_position(t0));
aabb.grow(self.extrapolated_position(t1));
//v+a*t==0
//goober code
if !self.acceleration.x.is_zero(){
let t=-self.velocity.x/self.acceleration.x;
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
aabb.grow(self.extrapolated_position_ratio_dt(t));
}
}
if !self.acceleration.y.is_zero(){
let t=-self.velocity.y/self.acceleration.y;
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
aabb.grow(self.extrapolated_position_ratio_dt(t));
}
}
if !self.acceleration.z.is_zero(){
let t=-self.velocity.z/self.acceleration.z;
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
aabb.grow(self.extrapolated_position_ratio_dt(t));
}
}
}
pub const ZERO: Self = Self::new(vec3::ZERO, vec3::ZERO, vec3::ZERO, Time::ZERO);
pub const fn new(
position: Planar64Vec3,
velocity: Planar64Vec3,
acceleration: Planar64Vec3,
time: Time<T>,
) -> Self {
Self {
position,
velocity,
acceleration,
time,
}
}
pub fn extrapolated_position(&self, time: Time<T>) -> Planar64Vec3 {
let dt = time - self.time;
self.position
+ (self.velocity * dt).map(|elem| elem.divide().fix_1())
+ self
.acceleration
.map(|elem| (dt * dt * elem / 2).divide().fix_1())
}
pub fn extrapolated_velocity(&self, time: Time<T>) -> Planar64Vec3 {
let dt = time - self.time;
self.velocity + (self.acceleration * dt).map(|elem| elem.divide().fix_1())
}
pub fn advance_time(&mut self, time: Time<T>) {
self.position = self.extrapolated_position(time);
self.velocity = self.extrapolated_velocity(time);
self.time = time;
}
pub fn extrapolated_position_ratio_dt<Num, Den, N1, D1, N2, N3, D2, N4, T1>(
&self,
dt: integer::Ratio<Num, Den>,
) -> Planar64Vec3
where
// Why?
// All of this can be removed with const generics because the type can be specified as
// Ratio<Fixed<N,NF>,Fixed<D,DF>>
// which is known to implement all the necessary traits
Num: Copy,
Den: Copy + core::ops::Mul<i64, Output = D1>,
D1: Copy,
Num: core::ops::Mul<Planar64, Output = N1>,
Planar64: core::ops::Mul<D1, Output = N2>,
N1: core::ops::Add<N2, Output = N3>,
Num: core::ops::Mul<N3, Output = N4>,
Den: core::ops::Mul<D1, Output = D2>,
D2: Copy,
Planar64: core::ops::Mul<D2, Output = N4>,
N4: integer::Divide<D2, Output = T1>,
T1: integer::Fix<Planar64>,
{
// a*dt^2/2 + v*dt + p
// (a*dt/2+v)*dt+p
(self.acceleration.map(|elem| dt * elem / 2) + self.velocity)
.map(|elem| dt.mul_ratio(elem))
.map(|elem| elem.divide().fix())
+ self.position
}
pub fn extrapolated_velocity_ratio_dt<Num, Den, N1, T1>(
&self,
dt: integer::Ratio<Num, Den>,
) -> Planar64Vec3
where
Num: Copy,
Den: Copy,
Num: core::ops::Mul<Planar64, Output = N1>,
Planar64: core::ops::Mul<Den, Output = N1>,
N1: integer::Divide<Den, Output = T1>,
T1: integer::Fix<Planar64>,
{
// a*dt + v
self.acceleration.map(|elem| (dt * elem).divide().fix()) + self.velocity
}
pub fn advance_time_ratio_dt(&mut self, dt: crate::model_physics::GigaTime) {
self.position = self.extrapolated_position_ratio_dt(dt);
self.velocity = self.extrapolated_velocity_ratio_dt(dt);
self.time += dt.into();
}
pub fn infinity_dir(&self) -> Option<Planar64Vec3> {
if self.velocity == vec3::ZERO {
if self.acceleration == vec3::ZERO {
None
} else {
Some(self.acceleration)
}
} else {
Some(self.velocity)
}
}
pub fn grow_aabb(&self, aabb: &mut aabb::Aabb, t0: Time<T>, t1: Time<T>) {
aabb.grow(self.extrapolated_position(t0));
aabb.grow(self.extrapolated_position(t1));
//v+a*t==0
//goober code
if !self.acceleration.x.is_zero() {
let t = -self.velocity.x / self.acceleration.x;
if t0.to_ratio().lt_ratio(t) && t.lt_ratio(t1.to_ratio()) {
aabb.grow(self.extrapolated_position_ratio_dt(t));
}
}
if !self.acceleration.y.is_zero() {
let t = -self.velocity.y / self.acceleration.y;
if t0.to_ratio().lt_ratio(t) && t.lt_ratio(t1.to_ratio()) {
aabb.grow(self.extrapolated_position_ratio_dt(t));
}
}
if !self.acceleration.z.is_zero() {
let t = -self.velocity.z / self.acceleration.z;
if t0.to_ratio().lt_ratio(t) && t.lt_ratio(t1.to_ratio()) {
aabb.grow(self.extrapolated_position_ratio_dt(t));
}
}
}
}
impl<T> std::fmt::Display for Body<T>{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"p({}) v({}) a({}) t({})",self.position,self.velocity,self.acceleration,self.time)
}
impl<T> std::fmt::Display for Body<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"p({}) v({}) a({}) t({})",
self.position, self.velocity, self.acceleration, self.time
)
}
}
pub struct VirtualBody<'a,T>{
body0:&'a Body<T>,
body1:&'a Body<T>,
pub struct VirtualBody<'a, T> {
body0: &'a Body<T>,
body1: &'a Body<T>,
}
impl<T> VirtualBody<'_,T>
where Time<T>:Copy,
impl<T> VirtualBody<'_, T>
where
Time<T>: Copy,
{
pub const fn relative<'a>(body0:&'a Body<T>,body1:&'a Body<T>)->VirtualBody<'a,T>{
//(p0,v0,a0,t0)
//(p1,v1,a1,t1)
VirtualBody{
body0,
body1,
}
}
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
self.body1.extrapolated_position(time)-self.body0.extrapolated_position(time)
}
pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{
self.body1.extrapolated_velocity(time)-self.body0.extrapolated_velocity(time)
}
pub fn acceleration(&self)->Planar64Vec3{
self.body1.acceleration-self.body0.acceleration
}
pub fn body(&self,time:Time<T>)->Body<T>{
Body::new(self.extrapolated_position(time),self.extrapolated_velocity(time),self.acceleration(),time)
}
pub const fn relative<'a>(body0: &'a Body<T>, body1: &'a Body<T>) -> VirtualBody<'a, T> {
//(p0,v0,a0,t0)
//(p1,v1,a1,t1)
VirtualBody { body0, body1 }
}
pub fn extrapolated_position(&self, time: Time<T>) -> Planar64Vec3 {
self.body1.extrapolated_position(time) - self.body0.extrapolated_position(time)
}
pub fn extrapolated_velocity(&self, time: Time<T>) -> Planar64Vec3 {
self.body1.extrapolated_velocity(time) - self.body0.extrapolated_velocity(time)
}
pub fn acceleration(&self) -> Planar64Vec3 {
self.body1.acceleration - self.body0.acceleration
}
pub fn body(&self, time: Time<T>) -> Body<T> {
Body::new(
self.extrapolated_position(time),
self.extrapolated_velocity(time),
self.acceleration(),
time,
)
}
}

View File

@ -1,21 +1,21 @@
pub type QNWorker<'a,Task>=CompatNWorker<'a,Task>;
pub type INWorker<'a,Task>=CompatNWorker<'a,Task>;
pub type QNWorker<'a, Task> = CompatNWorker<'a, Task>;
pub type INWorker<'a, Task> = CompatNWorker<'a, Task>;
pub struct CompatNWorker<'a,Task>{
data:std::marker::PhantomData<Task>,
f:Box<dyn FnMut(Task)+Send+'a>,
pub struct CompatNWorker<'a, Task> {
data: std::marker::PhantomData<Task>,
f: Box<dyn FnMut(Task) + Send + 'a>,
}
impl<'a,Task> CompatNWorker<'a,Task>{
pub fn new(f:impl FnMut(Task)+Send+'a)->CompatNWorker<'a,Task>{
Self{
data:std::marker::PhantomData,
f:Box::new(f),
}
}
impl<'a, Task> CompatNWorker<'a, Task> {
pub fn new(f: impl FnMut(Task) + Send + 'a) -> CompatNWorker<'a, Task> {
Self {
data: std::marker::PhantomData,
f: Box::new(f),
}
}
pub fn send(&mut self,task:Task)->Result<(),()>{
(self.f)(task);
Ok(())
}
pub fn send(&mut self, task: Task) -> Result<(), ()> {
(self.f)(task);
Ok(())
}
}

View File

@ -1,134 +1,185 @@
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge};
use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3};
use crate::physics::{Time,Body};
use crate::model_physics::{DirectedEdge, GigaTime, MeshQuery, FEV};
use crate::physics::{Body, Time};
use strafesnet_common::integer::{vec3::Vector3, Fixed, Ratio};
enum Transition<M:MeshQuery>{
Miss,
Next(FEV<M>,GigaTime),
Hit(M::Face,GigaTime),
enum Transition<M: MeshQuery> {
Miss,
Next(FEV<M>, GigaTime),
Hit(M::Face, GigaTime),
}
pub enum CrawlResult<M:MeshQuery>{
Miss(FEV<M>),
Hit(M::Face,GigaTime),
pub enum CrawlResult<M: MeshQuery> {
Miss(FEV<M>),
Hit(M::Face, GigaTime),
}
impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
where
// This is hardcoded for MinkowskiMesh lol
M::Face:Copy,
M::Edge:Copy,
M::Vert:Copy,
F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>,
<F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum,
<M as MeshQuery>::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>,
impl<F: Copy, M: MeshQuery<Normal = Vector3<F>, Offset = Fixed<4, 128>>> FEV<M>
where
// This is hardcoded for MinkowskiMesh lol
M::Face: Copy,
M::Edge: Copy,
M::Vert: Copy,
F: core::ops::Mul<Fixed<1, 32>, Output = Fixed<4, 128>>,
<F as core::ops::Mul<Fixed<1, 32>>>::Output: core::iter::Sum,
<M as MeshQuery>::Offset: core::ops::Sub<<F as std::ops::Mul<Fixed<1, 32>>>::Output>,
{
fn next_transition(&self,body_time:GigaTime,mesh:&M,body:&Body,mut best_time:GigaTime)->Transition<M>{
//conflicting derivative means it crosses in the wrong direction.
//if the transition time is equal to an already tested transition, do not replace the current best.
let mut best_transition=Transition::Miss;
match self{
&FEV::Face(face_id)=>{
//test own face collision time, ignoring roots with zero or conflicting derivative
//n=face.normal d=face.dot
//n.a t^2+n.v t+n.p-d==0
let (n,d)=mesh.face_nd(face_id);
//TODO: use higher precision d value?
//use the mesh transform translation instead of baking it into the d value.
for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=dt;
best_transition=Transition::Hit(face_id,dt);
break;
}
}
//test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.face_edges(face_id).iter(){
let edge_n=mesh.directed_edge_n(directed_edge_id);
let n=n.cross(edge_n);
let verts=mesh.edge_verts(directed_edge_id.as_undirected());
//WARNING: d is moved out of the *2 block because of adding two vertices!
//WARNING: precision is swept under the rug!
for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(verts[0])+mesh.vert(verts[1]))).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=dt;
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
break;
}
}
}
//if none:
},
&FEV::Edge(edge_id)=>{
//test each face collision time, ignoring roots with zero or conflicting derivative
let edge_n=mesh.edge_n(edge_id);
let edge_verts=mesh.edge_verts(edge_id);
let delta_pos=body.position*2-(mesh.vert(edge_verts[0])+mesh.vert(edge_verts[1]));
for (i,&edge_face_id) in mesh.edge_faces(edge_id).iter().enumerate(){
let face_n=mesh.face_nd(edge_face_id).0;
//edge_n gets parity from the order of edge_faces
let n=face_n.cross(edge_n)*((i as i64)*2-1);
//WARNING yada yada d *2
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=dt;
best_transition=Transition::Next(FEV::Face(edge_face_id),dt);
break;
}
}
}
//test each vertex collision time, ignoring roots with zero or conflicting derivative
for (i,&vert_id) in edge_verts.iter().enumerate(){
//vertex normal gets parity from vert index
let n=edge_n*(1-2*(i as i64));
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
best_time=dt;
best_transition=Transition::Next(FEV::Vert(vert_id),dt);
break;
}
}
}
//if none:
},
&FEV::Vert(vert_id)=>{
//test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.vert_edges(vert_id).iter(){
//edge is directed away from vertex, but we want the dot product to turn out negative
let n=-mesh.directed_edge_n(directed_edge_id);
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
best_time=dt;
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
break;
}
}
}
//if none:
},
}
best_transition
}
pub fn crawl(mut self,mesh:&M,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<M>{
let mut body_time={
let r=(start_time-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4())
};
let time_limit={
let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4())
};
for _ in 0..20{
match self.next_transition(body_time,mesh,relative_body,time_limit){
Transition::Miss=>return CrawlResult::Miss(self),
Transition::Next(next_fev,next_time)=>(self,body_time)=(next_fev,next_time),
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
}
}
//TODO: fix all bugs
//println!("Too many iterations! Using default behaviour instead of crashing...");
CrawlResult::Miss(self)
}
fn next_transition(
&self,
body_time: GigaTime,
mesh: &M,
body: &Body,
mut best_time: GigaTime,
) -> Transition<M> {
//conflicting derivative means it crosses in the wrong direction.
//if the transition time is equal to an already tested transition, do not replace the current best.
let mut best_transition = Transition::Miss;
match self {
&FEV::Face(face_id) => {
//test own face collision time, ignoring roots with zero or conflicting derivative
//n=face.normal d=face.dot
//n.a t^2+n.v t+n.p-d==0
let (n, d) = mesh.face_nd(face_id);
//TODO: use higher precision d value?
//use the mesh transform translation instead of baking it into the d value.
for dt in Fixed::<4, 128>::zeroes2(
(n.dot(body.position) - d) * 2,
n.dot(body.velocity) * 2,
n.dot(body.acceleration),
) {
if body_time.le_ratio(dt)
&& dt.lt_ratio(best_time)
&& n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative()
{
best_time = dt;
best_transition = Transition::Hit(face_id, dt);
break;
}
}
//test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.face_edges(face_id).iter() {
let edge_n = mesh.directed_edge_n(directed_edge_id);
let n = n.cross(edge_n);
let verts = mesh.edge_verts(directed_edge_id.as_undirected());
//WARNING: d is moved out of the *2 block because of adding two vertices!
//WARNING: precision is swept under the rug!
for dt in Fixed::<4, 128>::zeroes2(
n.dot(body.position * 2 - (mesh.vert(verts[0]) + mesh.vert(verts[1])))
.fix_4(),
n.dot(body.velocity).fix_4() * 2,
n.dot(body.acceleration).fix_4(),
) {
if body_time.le_ratio(dt)
&& dt.lt_ratio(best_time)
&& n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative()
{
best_time = dt;
best_transition =
Transition::Next(FEV::Edge(directed_edge_id.as_undirected()), dt);
break;
}
}
}
//if none:
}
&FEV::Edge(edge_id) => {
//test each face collision time, ignoring roots with zero or conflicting derivative
let edge_n = mesh.edge_n(edge_id);
let edge_verts = mesh.edge_verts(edge_id);
let delta_pos =
body.position * 2 - (mesh.vert(edge_verts[0]) + mesh.vert(edge_verts[1]));
for (i, &edge_face_id) in mesh.edge_faces(edge_id).iter().enumerate() {
let face_n = mesh.face_nd(edge_face_id).0;
//edge_n gets parity from the order of edge_faces
let n = face_n.cross(edge_n) * ((i as i64) * 2 - 1);
//WARNING yada yada d *2
for dt in Fixed::<4, 128>::zeroes2(
n.dot(delta_pos).fix_4(),
n.dot(body.velocity).fix_4() * 2,
n.dot(body.acceleration).fix_4(),
) {
if body_time.le_ratio(dt)
&& dt.lt_ratio(best_time)
&& n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative()
{
best_time = dt;
best_transition = Transition::Next(FEV::Face(edge_face_id), dt);
break;
}
}
}
//test each vertex collision time, ignoring roots with zero or conflicting derivative
for (i, &vert_id) in edge_verts.iter().enumerate() {
//vertex normal gets parity from vert index
let n = edge_n * (1 - 2 * (i as i64));
for dt in Fixed::<2, 64>::zeroes2(
(n.dot(body.position - mesh.vert(vert_id))) * 2,
n.dot(body.velocity) * 2,
n.dot(body.acceleration),
) {
if body_time.le_ratio(dt)
&& dt.lt_ratio(best_time)
&& n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative()
{
let dt = Ratio::new(dt.num.fix_4(), dt.den.fix_4());
best_time = dt;
best_transition = Transition::Next(FEV::Vert(vert_id), dt);
break;
}
}
}
//if none:
}
&FEV::Vert(vert_id) => {
//test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.vert_edges(vert_id).iter() {
//edge is directed away from vertex, but we want the dot product to turn out negative
let n = -mesh.directed_edge_n(directed_edge_id);
for dt in Fixed::<2, 64>::zeroes2(
(n.dot(body.position - mesh.vert(vert_id))) * 2,
n.dot(body.velocity) * 2,
n.dot(body.acceleration),
) {
if body_time.le_ratio(dt)
&& dt.lt_ratio(best_time)
&& n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative()
{
let dt = Ratio::new(dt.num.fix_4(), dt.den.fix_4());
best_time = dt;
best_transition =
Transition::Next(FEV::Edge(directed_edge_id.as_undirected()), dt);
break;
}
}
}
//if none:
}
}
best_transition
}
pub fn crawl(
mut self,
mesh: &M,
relative_body: &Body,
start_time: Time,
time_limit: Time,
) -> CrawlResult<M> {
let mut body_time = {
let r = (start_time - relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(), r.den.fix_4())
};
let time_limit = {
let r = (time_limit - relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(), r.den.fix_4())
};
for _ in 0..20 {
match self.next_transition(body_time, mesh, relative_body, time_limit) {
Transition::Miss => return CrawlResult::Miss(self),
Transition::Next(next_fev, next_time) => (self, body_time) = (next_fev, next_time),
Transition::Hit(face, time) => return CrawlResult::Hit(face, time),
}
}
//TODO: fix all bugs
//println!("Too many iterations! Using default behaviour instead of crashing...");
CrawlResult::Miss(self)
}
}

View File

@ -1,144 +1,170 @@
use std::io::Read;
#[derive(Debug)]
pub enum ReadError{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::ReadError),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::ReadError),
#[cfg(feature="snf")]
StrafesNET(strafesnet_snf::Error),
#[cfg(feature="snf")]
StrafesNETMap(strafesnet_snf::map::Error),
Io(std::io::Error),
UnknownFileFormat,
pub enum ReadError {
#[cfg(feature = "roblox")]
Roblox(strafesnet_rbx_loader::ReadError),
#[cfg(feature = "source")]
Source(strafesnet_bsp_loader::ReadError),
#[cfg(feature = "snf")]
StrafesNET(strafesnet_snf::Error),
#[cfg(feature = "snf")]
StrafesNETMap(strafesnet_snf::map::Error),
Io(std::io::Error),
UnknownFileFormat,
}
impl std::fmt::Display for ReadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for ReadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ReadError{}
impl std::error::Error for ReadError {}
pub enum DataStructure{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::Model),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature="snf")]
StrafesNET(strafesnet_common::map::CompleteMap),
pub enum DataStructure {
#[cfg(feature = "roblox")]
Roblox(strafesnet_rbx_loader::Model),
#[cfg(feature = "source")]
Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature = "snf")]
StrafesNET(strafesnet_common::map::CompleteMap),
}
pub fn read<R:Read+std::io::Seek>(input:R)->Result<DataStructure,ReadError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..4]{
#[cfg(feature="roblox")]
b"<rob"=>Ok(DataStructure::Roblox(strafesnet_rbx_loader::read(buf).map_err(ReadError::Roblox)?)),
#[cfg(feature="source")]
b"VBSP"=>Ok(DataStructure::Source(strafesnet_bsp_loader::read(buf).map_err(ReadError::Source)?)),
#[cfg(feature="snf")]
b"SNFM"=>Ok(DataStructure::StrafesNET(
strafesnet_snf::read_map(buf).map_err(ReadError::StrafesNET)?
.into_complete_map().map_err(ReadError::StrafesNETMap)?
)),
_=>Err(ReadError::UnknownFileFormat),
}
pub fn read<R: Read + std::io::Seek>(input: R) -> Result<DataStructure, ReadError> {
let mut buf = std::io::BufReader::new(input);
let peek = std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..4] {
#[cfg(feature = "roblox")]
b"<rob" => Ok(DataStructure::Roblox(
strafesnet_rbx_loader::read(buf).map_err(ReadError::Roblox)?,
)),
#[cfg(feature = "source")]
b"VBSP" => Ok(DataStructure::Source(
strafesnet_bsp_loader::read(buf).map_err(ReadError::Source)?,
)),
#[cfg(feature = "snf")]
b"SNFM" => Ok(DataStructure::StrafesNET(
strafesnet_snf::read_map(buf)
.map_err(ReadError::StrafesNET)?
.into_complete_map()
.map_err(ReadError::StrafesNETMap)?,
)),
_ => Err(ReadError::UnknownFileFormat),
}
}
#[derive(Debug)]
pub enum LoadError{
ReadError(ReadError),
File(std::io::Error),
Io(std::io::Error),
pub enum LoadError {
ReadError(ReadError),
File(std::io::Error),
Io(std::io::Error),
}
impl std::fmt::Display for LoadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
impl std::fmt::Display for LoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for LoadError{}
impl std::error::Error for LoadError {}
pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::CompleteMap,LoadError>{
//blocking because it's simpler...
let file=std::fs::File::open(path).map_err(LoadError::File)?;
match read(file).map_err(LoadError::ReadError)?{
#[cfg(feature="snf")]
DataStructure::StrafesNET(map)=>Ok(map),
#[cfg(feature="roblox")]
DataStructure::Roblox(model)=>{
let mut place=model.into_place();
place.run_scripts();
pub fn load<P: AsRef<std::path::Path>>(
path: P,
) -> Result<strafesnet_common::map::CompleteMap, LoadError> {
//blocking because it's simpler...
let file = std::fs::File::open(path).map_err(LoadError::File)?;
match read(file).map_err(LoadError::ReadError)? {
#[cfg(feature = "snf")]
DataStructure::StrafesNET(map) => Ok(map),
#[cfg(feature = "roblox")]
DataStructure::Roblox(model) => {
let mut place = model.into_place();
place.run_scripts();
let mut loader=strafesnet_deferred_loader::roblox_legacy();
let mut loader = strafesnet_deferred_loader::roblox_legacy();
let (texture_loader,mesh_loader)=loader.get_inner_mut();
let (texture_loader, mesh_loader) = loader.get_inner_mut();
let map_step1=strafesnet_rbx_loader::convert(
&place,
|name|texture_loader.acquire_render_config_id(name),
|name|mesh_loader.acquire_mesh_id(name),
);
let map_step1 = strafesnet_rbx_loader::convert(
&place,
|name| texture_loader.acquire_render_config_id(name),
|name| mesh_loader.acquire_mesh_id(name),
);
let meshpart_meshes=mesh_loader.load_meshes().map_err(LoadError::Io)?;
let meshpart_meshes = mesh_loader.load_meshes().map_err(LoadError::Io)?;
let map_step2=map_step1.add_meshpart_meshes_and_calculate_attributes(
meshpart_meshes.into_iter().map(|(mesh_id,loader_model)|
(mesh_id,strafesnet_rbx_loader::data::RobloxMeshBytes::new(loader_model.get()))
)
);
let map_step2 = map_step1.add_meshpart_meshes_and_calculate_attributes(
meshpart_meshes.into_iter().map(|(mesh_id, loader_model)| {
(
mesh_id,
strafesnet_rbx_loader::data::RobloxMeshBytes::new(loader_model.get()),
)
}),
);
let (textures,render_configs)=loader.into_render_configs().map_err(LoadError::Io)?.consume();
let (textures, render_configs) = loader
.into_render_configs()
.map_err(LoadError::Io)?
.consume();
let map=map_step2.add_render_configs_and_textures(
render_configs.into_iter(),
textures.into_iter().map(|(texture_id,texture)|
(texture_id,match texture{
strafesnet_deferred_loader::texture::Texture::ImageDDS(data)=>data,
})
)
);
let map = map_step2.add_render_configs_and_textures(
render_configs.into_iter(),
textures.into_iter().map(|(texture_id, texture)| {
(
texture_id,
match texture {
strafesnet_deferred_loader::texture::Texture::ImageDDS(data) => data,
},
)
}),
);
Ok(map)
},
#[cfg(feature="source")]
DataStructure::Source(bsp)=>{
let mut loader=strafesnet_deferred_loader::source_legacy();
Ok(map)
}
#[cfg(feature = "source")]
DataStructure::Source(bsp) => {
let mut loader = strafesnet_deferred_loader::source_legacy();
let (texture_loader,mesh_loader)=loader.get_inner_mut();
let (texture_loader, mesh_loader) = loader.get_inner_mut();
let map_step1=strafesnet_bsp_loader::convert(
&bsp,
|name|texture_loader.acquire_render_config_id(name),
|name|mesh_loader.acquire_mesh_id(name),
);
let map_step1 = strafesnet_bsp_loader::convert(
&bsp,
|name| texture_loader.acquire_render_config_id(name),
|name| mesh_loader.acquire_mesh_id(name),
);
let prop_meshes=mesh_loader.load_meshes(bsp.as_ref());
let prop_meshes = mesh_loader.load_meshes(bsp.as_ref());
let map_step2=map_step1.add_prop_meshes(
//the type conflagulator 9000
prop_meshes.into_iter().map(|(mesh_id,loader_model)|
(mesh_id,strafesnet_bsp_loader::data::ModelData{
mdl:strafesnet_bsp_loader::data::MdlData::new(loader_model.mdl.get()),
vtx:strafesnet_bsp_loader::data::VtxData::new(loader_model.vtx.get()),
vvd:strafesnet_bsp_loader::data::VvdData::new(loader_model.vvd.get()),
})
),
|name|texture_loader.acquire_render_config_id(name),
);
let map_step2 = map_step1.add_prop_meshes(
//the type conflagulator 9000
prop_meshes.into_iter().map(|(mesh_id, loader_model)| {
(
mesh_id,
strafesnet_bsp_loader::data::ModelData {
mdl: strafesnet_bsp_loader::data::MdlData::new(loader_model.mdl.get()),
vtx: strafesnet_bsp_loader::data::VtxData::new(loader_model.vtx.get()),
vvd: strafesnet_bsp_loader::data::VvdData::new(loader_model.vvd.get()),
},
)
}),
|name| texture_loader.acquire_render_config_id(name),
);
let (textures,render_configs)=loader.into_render_configs().map_err(LoadError::Io)?.consume();
let (textures, render_configs) = loader
.into_render_configs()
.map_err(LoadError::Io)?
.consume();
let map=map_step2.add_render_configs_and_textures(
render_configs.into_iter(),
textures.into_iter().map(|(texture_id,texture)|
(texture_id,match texture{
strafesnet_deferred_loader::texture::Texture::ImageDDS(data)=>data,
})
),
);
let map = map_step2.add_render_configs_and_textures(
render_configs.into_iter(),
textures.into_iter().map(|(texture_id, texture)| {
(
texture_id,
match texture {
strafesnet_deferred_loader::texture::Texture::ImageDDS(data) => data,
},
)
}),
);
Ok(map)
},
}
Ok(map)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,65 +1,65 @@
pub enum Instruction{
Render(crate::physics_worker::FrameState),
//UpdateModel(crate::graphics::GraphicsModelUpdate),
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
ChangeMap(strafesnet_common::map::CompleteMap),
pub enum Instruction {
Render(crate::physics_worker::FrameState),
//UpdateModel(crate::graphics::GraphicsModelUpdate),
Resize(winit::dpi::PhysicalSize<u32>, crate::settings::UserSettings),
ChangeMap(strafesnet_common::map::CompleteMap),
}
//Ideally the graphics thread worker description is:
/*
WorkerDescription{
input:Immediate,
output:Realtime(PoolOrdering::Ordered(3)),
input:Immediate,
output:Realtime(PoolOrdering::Ordered(3)),
}
*/
//up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order
pub fn new<'a>(
mut graphics:crate::graphics::GraphicsState,
mut config:wgpu::SurfaceConfiguration,
surface:wgpu::Surface<'a>,
device:wgpu::Device,
queue:wgpu::Queue,
)->crate::compat_worker::INWorker<'a,Instruction>{
let mut resize=None;
crate::compat_worker::INWorker::new(move |ins:Instruction|{
match ins{
Instruction::ChangeMap(map)=>{
graphics.clear();
graphics.generate_models(&device,&queue,&map);
},
Instruction::Resize(size,user_settings)=>{
resize=Some((size,user_settings));
}
Instruction::Render(frame_state)=>{
if let Some((size,user_settings))=resize.take(){
println!("Resizing to {:?}",size);
let t0=std::time::Instant::now();
config.width=size.width.max(1);
config.height=size.height.max(1);
surface.configure(&device,&config);
graphics.resize(&device,&config,&user_settings);
println!("Resize took {:?}",t0.elapsed());
}
//this has to go deeper somehow
let frame=match surface.get_current_texture(){
Ok(frame)=>frame,
Err(_)=>{
surface.configure(&device,&config);
surface
.get_current_texture()
.expect("Failed to acquire next surface texture!")
}
};
let view=frame.texture.create_view(&wgpu::TextureViewDescriptor{
format:Some(config.view_formats[0]),
..wgpu::TextureViewDescriptor::default()
});
mut graphics: crate::graphics::GraphicsState,
mut config: wgpu::SurfaceConfiguration,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
) -> crate::compat_worker::INWorker<'a, Instruction> {
let mut resize = None;
crate::compat_worker::INWorker::new(move |ins: Instruction| {
match ins {
Instruction::ChangeMap(map) => {
graphics.clear();
graphics.generate_models(&device, &queue, &map);
}
Instruction::Resize(size, user_settings) => {
resize = Some((size, user_settings));
}
Instruction::Render(frame_state) => {
if let Some((size, user_settings)) = resize.take() {
println!("Resizing to {:?}", size);
let t0 = std::time::Instant::now();
config.width = size.width.max(1);
config.height = size.height.max(1);
surface.configure(&device, &config);
graphics.resize(&device, &config, &user_settings);
println!("Resize took {:?}", t0.elapsed());
}
//this has to go deeper somehow
let frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(_) => {
surface.configure(&device, &config);
surface
.get_current_texture()
.expect("Failed to acquire next surface texture!")
}
};
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
format: Some(config.view_formats[0]),
..wgpu::TextureViewDescriptor::default()
});
graphics.render(&view,&device,&queue,frame_state);
graphics.render(&view, &device, &queue, frame_state);
frame.present();
}
}
})
frame.present();
}
}
})
}

View File

@ -1,21 +1,21 @@
mod body;
mod compat_worker;
mod face_crawler;
mod file;
mod graphics;
mod graphics_worker;
mod model_graphics;
mod model_physics;
mod physics;
mod physics_worker;
mod push_solve;
mod settings;
mod setup;
mod window;
mod worker;
mod physics;
mod graphics;
mod settings;
mod push_solve;
mod face_crawler;
mod compat_worker;
mod model_physics;
mod model_graphics;
mod physics_worker;
mod graphics_worker;
const TITLE:&'static str=concat!("Strafe Client v",env!("CARGO_PKG_VERSION"));
const TITLE: &'static str = concat!("Strafe Client v", env!("CARGO_PKG_VERSION"));
fn main(){
setup::setup_and_start(TITLE);
fn main() {
setup::setup_and_start(TITLE);
}

View File

@ -1,48 +1,48 @@
use bytemuck::{Pod,Zeroable};
use strafesnet_common::model::{IndexedVertex,PolygonGroup,RenderConfigId};
#[derive(Clone,Copy,Pod,Zeroable)]
use bytemuck::{Pod, Zeroable};
use strafesnet_common::model::{IndexedVertex, PolygonGroup, RenderConfigId};
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct GraphicsVertex{
pub pos:[f32;3],
pub tex:[f32;2],
pub normal:[f32;3],
pub color:[f32;4],
pub struct GraphicsVertex {
pub pos: [f32; 3],
pub tex: [f32; 2],
pub normal: [f32; 3],
pub color: [f32; 4],
}
#[derive(Clone,Copy,id::Id)]
#[derive(Clone, Copy, id::Id)]
pub struct IndexedGraphicsMeshOwnedRenderConfigId(u32);
pub struct IndexedGraphicsMeshOwnedRenderConfig{
pub unique_pos:Vec<[f32;3]>,
pub unique_tex:Vec<[f32;2]>,
pub unique_normal:Vec<[f32;3]>,
pub unique_color:Vec<[f32;4]>,
pub unique_vertices:Vec<IndexedVertex>,
pub render_config:RenderConfigId,
pub polys:PolygonGroup,
pub instances:Vec<GraphicsModelOwned>,
pub struct IndexedGraphicsMeshOwnedRenderConfig {
pub unique_pos: Vec<[f32; 3]>,
pub unique_tex: Vec<[f32; 2]>,
pub unique_normal: Vec<[f32; 3]>,
pub unique_color: Vec<[f32; 4]>,
pub unique_vertices: Vec<IndexedVertex>,
pub render_config: RenderConfigId,
pub polys: PolygonGroup,
pub instances: Vec<GraphicsModelOwned>,
}
pub enum Indices{
U32(Vec<u32>),
U16(Vec<u16>),
pub enum Indices {
U32(Vec<u32>),
U16(Vec<u16>),
}
pub struct GraphicsMeshOwnedRenderConfig{
pub vertices:Vec<GraphicsVertex>,
pub indices:Indices,
pub render_config:RenderConfigId,
pub instances:Vec<GraphicsModelOwned>,
pub struct GraphicsMeshOwnedRenderConfig {
pub vertices: Vec<GraphicsVertex>,
pub indices: Indices,
pub render_config: RenderConfigId,
pub instances: Vec<GraphicsModelOwned>,
}
#[derive(Clone,Copy,PartialEq,id::Id)]
#[derive(Clone, Copy, PartialEq, id::Id)]
pub struct GraphicsModelColor4(glam::Vec4);
impl std::hash::Hash for GraphicsModelColor4{
fn hash<H:std::hash::Hasher>(&self,state:&mut H) {
for &f in self.0.as_ref(){
bytemuck::cast::<f32,u32>(f).hash(state);
}
}
impl std::hash::Hash for GraphicsModelColor4 {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
for &f in self.0.as_ref() {
bytemuck::cast::<f32, u32>(f).hash(state);
}
}
}
impl Eq for GraphicsModelColor4{}
impl Eq for GraphicsModelColor4 {}
#[derive(Clone)]
pub struct GraphicsModelOwned{
pub transform:glam::Mat4,
pub normal_transform:glam::Mat3,
pub color:GraphicsModelColor4,
pub struct GraphicsModelOwned {
pub transform: glam::Mat4,
pub normal_transform: glam::Mat3,
pub color: GraphicsModelColor4,
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,249 +1,313 @@
use strafesnet_common::mouse::MouseState;
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner,Instruction as PhysicsInputInstruction};
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::timer::{Scaled,Timer,TimerState};
use mouse_interpolator::MouseInterpolator;
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::mouse::MouseState;
use strafesnet_common::physics::{
Instruction as PhysicsInputInstruction, Time as PhysicsTime, TimeInner as PhysicsTimeInner,
};
use strafesnet_common::session::{Time as SessionTime, TimeInner as SessionTimeInner};
use strafesnet_common::timer::{Scaled, Timer, TimerState};
pub struct FrameState{
pub body:crate::physics::Body,
pub camera:crate::physics::PhysicsCamera,
pub time:PhysicsTime,
pub struct FrameState {
pub body: crate::physics::Body,
pub camera: crate::physics::PhysicsCamera,
pub time: PhysicsTime,
}
#[derive(Debug)]
pub enum InputInstruction{
MoveMouse(glam::IVec2),
MoveRight(bool),
MoveUp(bool),
MoveBack(bool),
MoveLeft(bool),
MoveDown(bool),
MoveForward(bool),
Jump(bool),
Zoom(bool),
ResetAndRestart,
ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
PracticeFly,
pub enum InputInstruction {
MoveMouse(glam::IVec2),
MoveRight(bool),
MoveUp(bool),
MoveBack(bool),
MoveLeft(bool),
MoveDown(bool),
MoveForward(bool),
Jump(bool),
Zoom(bool),
ResetAndRestart,
ResetAndSpawn(
strafesnet_common::gameplay_modes::ModeId,
strafesnet_common::gameplay_modes::StageId,
),
PracticeFly,
}
pub enum Instruction{
Input(InputInstruction),
Render,
Resize(winit::dpi::PhysicalSize<u32>),
ChangeMap(strafesnet_common::map::CompleteMap),
//SetPaused is not an InputInstruction: the physics doesn't know that it's paused.
SetPaused(bool),
//Graphics(crate::graphics_worker::Instruction),
pub enum Instruction {
Input(InputInstruction),
Render,
Resize(winit::dpi::PhysicalSize<u32>),
ChangeMap(strafesnet_common::map::CompleteMap),
//SetPaused is not an InputInstruction: the physics doesn't know that it's paused.
SetPaused(bool),
//Graphics(crate::graphics_worker::Instruction),
}
mod mouse_interpolator{
use super::*;
//TODO: move this or tab
pub struct MouseInterpolator{
//"PlayerController"
user_settings:crate::settings::UserSettings,
//"MouseInterpolator"
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
last_mouse_time:PhysicsTime,
mouse_blocking:bool,
//"Simulation"
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext,
mod mouse_interpolator {
use super::*;
//TODO: move this or tab
pub struct MouseInterpolator {
//"PlayerController"
user_settings: crate::settings::UserSettings,
//"MouseInterpolator"
timeline:
std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction, PhysicsTimeInner>>,
last_mouse_time: PhysicsTime,
mouse_blocking: bool,
//"Simulation"
timer: Timer<Scaled<SessionTimeInner, PhysicsTimeInner>>,
physics: crate::physics::PhysicsContext,
}
impl MouseInterpolator {
pub fn new(
physics: crate::physics::PhysicsContext,
user_settings: crate::settings::UserSettings,
) -> MouseInterpolator {
MouseInterpolator {
mouse_blocking: true,
last_mouse_time: physics.get_next_mouse().time,
timeline: std::collections::VecDeque::new(),
timer: Timer::from_state(Scaled::identity(), false),
physics,
user_settings,
}
}
fn push_mouse_instruction(
&mut self,
ins: &TimedInstruction<Instruction, SessionTimeInner>,
m: glam::IVec2,
) {
if self.mouse_blocking {
//tell the game state which is living in the past about its future
self.timeline.push_front(TimedInstruction {
time: self.last_mouse_time,
instruction: PhysicsInputInstruction::SetNextMouse(MouseState {
time: self.timer.time(ins.time),
pos: m,
}),
});
} else {
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
self.timeline.push_front(TimedInstruction {
time: self.last_mouse_time,
instruction: PhysicsInputInstruction::ReplaceMouse(
MouseState {
time: self.last_mouse_time,
pos: self.physics.get_next_mouse().pos,
},
MouseState {
time: self.timer.time(ins.time),
pos: m,
},
),
});
//delay physics execution until we have an interpolation target
self.mouse_blocking = true;
}
self.last_mouse_time = self.timer.time(ins.time);
}
fn push(&mut self, time: SessionTime, phys_input: PhysicsInputInstruction) {
//This is always a non-mouse event
self.timeline.push_back(TimedInstruction {
time: self.timer.time(time),
instruction: phys_input,
});
}
/// returns should_empty_queue
/// may or may not mutate internal state XD!
fn map_instruction(
&mut self,
ins: &TimedInstruction<Instruction, SessionTimeInner>,
) -> bool {
let mut update_mouse_blocking = true;
match &ins.instruction {
Instruction::Input(input_instruction) => match input_instruction {
&InputInstruction::MoveMouse(m) => {
if !self.timer.is_paused() {
self.push_mouse_instruction(ins, m);
}
update_mouse_blocking = false;
}
&InputInstruction::MoveForward(s) => {
self.push(ins.time, PhysicsInputInstruction::SetMoveForward(s))
}
&InputInstruction::MoveLeft(s) => {
self.push(ins.time, PhysicsInputInstruction::SetMoveLeft(s))
}
&InputInstruction::MoveBack(s) => {
self.push(ins.time, PhysicsInputInstruction::SetMoveBack(s))
}
&InputInstruction::MoveRight(s) => {
self.push(ins.time, PhysicsInputInstruction::SetMoveRight(s))
}
&InputInstruction::MoveUp(s) => {
self.push(ins.time, PhysicsInputInstruction::SetMoveUp(s))
}
&InputInstruction::MoveDown(s) => {
self.push(ins.time, PhysicsInputInstruction::SetMoveDown(s))
}
&InputInstruction::Jump(s) => {
self.push(ins.time, PhysicsInputInstruction::SetJump(s))
}
&InputInstruction::Zoom(s) => {
self.push(ins.time, PhysicsInputInstruction::SetZoom(s))
}
&InputInstruction::ResetAndSpawn(mode_id, stage_id) => {
self.push(ins.time, PhysicsInputInstruction::Reset);
self.push(
ins.time,
PhysicsInputInstruction::SetSensitivity(
self.user_settings.calculate_sensitivity(),
),
);
self.push(ins.time, PhysicsInputInstruction::Spawn(mode_id, stage_id));
}
InputInstruction::ResetAndRestart => {
self.push(ins.time, PhysicsInputInstruction::Reset);
self.push(
ins.time,
PhysicsInputInstruction::SetSensitivity(
self.user_settings.calculate_sensitivity(),
),
);
self.push(ins.time, PhysicsInputInstruction::Restart);
}
InputInstruction::PracticeFly => {
self.push(ins.time, PhysicsInputInstruction::PracticeFly)
}
},
//do these really need to idle the physics?
//sending None dumps the instruction queue
Instruction::ChangeMap(_) => self.push(ins.time, PhysicsInputInstruction::Idle),
Instruction::Resize(_) => self.push(ins.time, PhysicsInputInstruction::Idle),
Instruction::Render => self.push(ins.time, PhysicsInputInstruction::Idle),
&Instruction::SetPaused(paused) => {
if let Err(e) = self.timer.set_paused(ins.time, paused) {
println!("Cannot SetPaused: {e}");
}
self.push(ins.time, PhysicsInputInstruction::Idle);
}
}
if update_mouse_blocking {
//this returns the bool for us
self.update_mouse_blocking(ins.time)
} else {
//do flush that queue
true
}
}
/// must check if self.mouse_blocking==true before calling!
fn unblock_mouse(&mut self, time: SessionTime) {
//push an event to extrapolate no movement from
self.timeline.push_front(TimedInstruction {
time: self.last_mouse_time,
instruction: PhysicsInputInstruction::SetNextMouse(MouseState {
time: self.timer.time(time),
pos: self.physics.get_next_mouse().pos,
}),
});
self.last_mouse_time = self.timer.time(time);
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
self.mouse_blocking = false;
}
fn update_mouse_blocking(&mut self, time: SessionTime) -> bool {
if self.mouse_blocking {
//assume the mouse has stopped moving after 10ms.
//shitty mice are 125Hz which is 8ms so this should cover that.
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if PhysicsTime::from_millis(10)
< self.timer.time(time) - self.physics.get_next_mouse().time
{
self.unblock_mouse(time);
true
} else {
false
}
} else {
//keep this up to date so that it can be used as a known-timestamp
//that the mouse was not moving when the mouse starts moving again
self.last_mouse_time = self.timer.time(time);
true
}
}
fn empty_queue(&mut self) {
while let Some(instruction) = self.timeline.pop_front() {
self.physics.run_input_instruction(instruction);
}
}
pub fn handle_instruction(
&mut self,
ins: &TimedInstruction<Instruction, SessionTimeInner>,
) {
let should_empty_queue = self.map_instruction(ins);
if should_empty_queue {
self.empty_queue();
}
}
pub fn get_frame_state(&self, time: SessionTime) -> FrameState {
FrameState {
body: self.physics.camera_body(),
camera: self.physics.camera(),
time: self.timer.time(time),
}
}
pub fn change_map(&mut self, time: SessionTime, map: &strafesnet_common::map::CompleteMap) {
//dump any pending interpolation state
if self.mouse_blocking {
self.unblock_mouse(time);
}
self.empty_queue();
}
impl MouseInterpolator{
pub fn new(
physics:crate::physics::PhysicsContext,
user_settings:crate::settings::UserSettings,
)->MouseInterpolator{
MouseInterpolator{
mouse_blocking:true,
last_mouse_time:physics.get_next_mouse().time,
timeline:std::collections::VecDeque::new(),
timer:Timer::from_state(Scaled::identity(),false),
physics,
user_settings,
}
}
fn push_mouse_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>,m:glam::IVec2){
if self.mouse_blocking{
//tell the game state which is living in the past about its future
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:m}),
});
}else{
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:self.last_mouse_time,pos:self.physics.get_next_mouse().pos},
MouseState{time:self.timer.time(ins.time),pos:m}
),
});
//delay physics execution until we have an interpolation target
self.mouse_blocking=true;
}
self.last_mouse_time=self.timer.time(ins.time);
}
fn push(&mut self,time:SessionTime,phys_input:PhysicsInputInstruction){
//This is always a non-mouse event
self.timeline.push_back(TimedInstruction{
time:self.timer.time(time),
instruction:phys_input,
});
}
/// returns should_empty_queue
/// may or may not mutate internal state XD!
fn map_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>)->bool{
let mut update_mouse_blocking=true;
match &ins.instruction{
Instruction::Input(input_instruction)=>match input_instruction{
&InputInstruction::MoveMouse(m)=>{
if !self.timer.is_paused(){
self.push_mouse_instruction(ins,m);
}
update_mouse_blocking=false;
},
&InputInstruction::MoveForward(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveForward(s)),
&InputInstruction::MoveLeft(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveLeft(s)),
&InputInstruction::MoveBack(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveRight(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveRight(s)),
&InputInstruction::MoveUp(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveUp(s)),
&InputInstruction::MoveDown(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveDown(s)),
&InputInstruction::Jump(s)=>self.push(ins.time,PhysicsInputInstruction::SetJump(s)),
&InputInstruction::Zoom(s)=>self.push(ins.time,PhysicsInputInstruction::SetZoom(s)),
&InputInstruction::ResetAndSpawn(mode_id,stage_id)=>{
self.push(ins.time,PhysicsInputInstruction::Reset);
self.push(ins.time,PhysicsInputInstruction::SetSensitivity(self.user_settings.calculate_sensitivity()));
self.push(ins.time,PhysicsInputInstruction::Spawn(mode_id,stage_id));
},
InputInstruction::ResetAndRestart=>{
self.push(ins.time,PhysicsInputInstruction::Reset);
self.push(ins.time,PhysicsInputInstruction::SetSensitivity(self.user_settings.calculate_sensitivity()));
self.push(ins.time,PhysicsInputInstruction::Restart);
},
InputInstruction::PracticeFly=>self.push(ins.time,PhysicsInputInstruction::PracticeFly),
},
//do these really need to idle the physics?
//sending None dumps the instruction queue
Instruction::ChangeMap(_)=>self.push(ins.time,PhysicsInputInstruction::Idle),
Instruction::Resize(_)=>self.push(ins.time,PhysicsInputInstruction::Idle),
Instruction::Render=>self.push(ins.time,PhysicsInputInstruction::Idle),
&Instruction::SetPaused(paused)=>{
if let Err(e)=self.timer.set_paused(ins.time,paused){
println!("Cannot SetPaused: {e}");
}
self.push(ins.time,PhysicsInputInstruction::Idle);
},
}
if update_mouse_blocking{
//this returns the bool for us
self.update_mouse_blocking(ins.time)
}else{
//do flush that queue
true
}
}
/// must check if self.mouse_blocking==true before calling!
fn unblock_mouse(&mut self,time:SessionTime){
//push an event to extrapolate no movement from
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(time),pos:self.physics.get_next_mouse().pos}),
});
self.last_mouse_time=self.timer.time(time);
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
self.mouse_blocking=false;
}
fn update_mouse_blocking(&mut self,time:SessionTime)->bool{
if self.mouse_blocking{
//assume the mouse has stopped moving after 10ms.
//shitty mice are 125Hz which is 8ms so this should cover that.
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if PhysicsTime::from_millis(10)<self.timer.time(time)-self.physics.get_next_mouse().time{
self.unblock_mouse(time);
true
}else{
false
}
}else{
//keep this up to date so that it can be used as a known-timestamp
//that the mouse was not moving when the mouse starts moving again
self.last_mouse_time=self.timer.time(time);
true
}
}
fn empty_queue(&mut self){
while let Some(instruction)=self.timeline.pop_front(){
self.physics.run_input_instruction(instruction);
}
}
pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>){
let should_empty_queue=self.map_instruction(ins);
if should_empty_queue{
self.empty_queue();
}
}
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
FrameState{
body:self.physics.camera_body(),
camera:self.physics.camera(),
time:self.timer.time(time),
}
}
pub fn change_map(&mut self,time:SessionTime,map:&strafesnet_common::map::CompleteMap){
//dump any pending interpolation state
if self.mouse_blocking{
self.unblock_mouse(time);
}
self.empty_queue();
//doing it like this to avoid doing PhysicsInstruction::ChangeMap(Rc<CompleteMap>)
self.physics.generate_models(&map);
//doing it like this to avoid doing PhysicsInstruction::ChangeMap(Rc<CompleteMap>)
self.physics.generate_models(&map);
//use the standard input interface so the instructions are written out to bots
self.handle_instruction(&TimedInstruction{
time,
instruction:Instruction::Input(InputInstruction::ResetAndSpawn(
strafesnet_common::gameplay_modes::ModeId::MAIN,
strafesnet_common::gameplay_modes::StageId::FIRST,
)),
});
}
pub const fn user_settings(&self)->&crate::settings::UserSettings{
&self.user_settings
}
}
//use the standard input interface so the instructions are written out to bots
self.handle_instruction(&TimedInstruction {
time,
instruction: Instruction::Input(InputInstruction::ResetAndSpawn(
strafesnet_common::gameplay_modes::ModeId::MAIN,
strafesnet_common::gameplay_modes::StageId::FIRST,
)),
});
}
pub const fn user_settings(&self) -> &crate::settings::UserSettings {
&self.user_settings
}
}
}
pub fn new<'a>(
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
user_settings:crate::settings::UserSettings,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
let physics=crate::physics::PhysicsContext::default();
let mut interpolator=MouseInterpolator::new(
physics,
user_settings
);
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{
interpolator.handle_instruction(&ins);
match ins.instruction{
Instruction::Render=>{
let frame_state=interpolator.get_frame_state(ins.time);
graphics_worker.send(crate::graphics_worker::Instruction::Render(frame_state)).unwrap();
},
Instruction::Resize(size)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,interpolator.user_settings().clone())).unwrap();
},
Instruction::ChangeMap(map)=>{
interpolator.change_map(ins.time,&map);
graphics_worker.send(crate::graphics_worker::Instruction::ChangeMap(map)).unwrap();
},
Instruction::Input(_)=>(),
Instruction::SetPaused(_)=>(),
}
})
mut graphics_worker: crate::compat_worker::INWorker<'a, crate::graphics_worker::Instruction>,
user_settings: crate::settings::UserSettings,
) -> crate::compat_worker::QNWorker<'a, TimedInstruction<Instruction, SessionTimeInner>> {
let physics = crate::physics::PhysicsContext::default();
let mut interpolator = MouseInterpolator::new(physics, user_settings);
crate::compat_worker::QNWorker::new(
move |ins: TimedInstruction<Instruction, SessionTimeInner>| {
interpolator.handle_instruction(&ins);
match ins.instruction {
Instruction::Render => {
let frame_state = interpolator.get_frame_state(ins.time);
graphics_worker
.send(crate::graphics_worker::Instruction::Render(frame_state))
.unwrap();
}
Instruction::Resize(size) => {
graphics_worker
.send(crate::graphics_worker::Instruction::Resize(
size,
interpolator.user_settings().clone(),
))
.unwrap();
}
Instruction::ChangeMap(map) => {
interpolator.change_map(ins.time, &map);
graphics_worker
.send(crate::graphics_worker::Instruction::ChangeMap(map))
.unwrap();
}
Instruction::Input(_) => (),
Instruction::SetPaused(_) => (),
}
},
)
}

View File

@ -1,4 +1,8 @@
use strafesnet_common::integer::{self,vec3::{self,Vector3},Fixed,Planar64,Planar64Vec3,Ratio};
use strafesnet_common::integer::{
self,
vec3::{self, Vector3},
Fixed, Planar64, Planar64Vec3, Ratio,
};
// This algorithm is based on Lua code
// written by Trey Reynolds in 2021
@ -7,347 +11,374 @@ use strafesnet_common::integer::{self,vec3::{self,Vector3},Fixed,Planar64,Planar
// A stack-allocated variable-size list that holds up to 4 elements
// Direct references are used instead of indices i0, i1, i2, i3
type Conts<'a>=arrayvec::ArrayVec<&'a Contact,4>;
type Conts<'a> = arrayvec::ArrayVec<&'a Contact, 4>;
// hack to allow comparing ratios to zero
const RATIO_ZERO:Ratio<Fixed<1,32>,Fixed<1,32>>=Ratio::new(Fixed::ZERO,Fixed::EPSILON);
const RATIO_ZERO: Ratio<Fixed<1, 32>, Fixed<1, 32>> = Ratio::new(Fixed::ZERO, Fixed::EPSILON);
struct Ray{
origin:Planar64Vec3,
direction:Planar64Vec3,
struct Ray {
origin: Planar64Vec3,
direction: Planar64Vec3,
}
impl Ray{
fn extrapolate<Num,Den,N1,T1>(&self,t:Ratio<Num,Den>)->Planar64Vec3
where
Num:Copy,
Den:Copy,
Num:core::ops::Mul<Planar64,Output=N1>,
Planar64:core::ops::Mul<Den,Output=N1>,
N1:integer::Divide<Den,Output=T1>,
T1:integer::Fix<Planar64>,
{
self.origin+self.direction.map(|elem|(t*elem).divide().fix())
}
impl Ray {
fn extrapolate<Num, Den, N1, T1>(&self, t: Ratio<Num, Den>) -> Planar64Vec3
where
Num: Copy,
Den: Copy,
Num: core::ops::Mul<Planar64, Output = N1>,
Planar64: core::ops::Mul<Den, Output = N1>,
N1: integer::Divide<Den, Output = T1>,
T1: integer::Fix<Planar64>,
{
self.origin + self.direction.map(|elem| (t * elem).divide().fix())
}
}
/// Information about a contact restriction
pub struct Contact{
pub position:Planar64Vec3,
pub velocity:Planar64Vec3,
pub normal:Planar64Vec3,
pub struct Contact {
pub position: Planar64Vec3,
pub velocity: Planar64Vec3,
pub normal: Planar64Vec3,
}
impl Contact{
fn relative_to(&self,point:Planar64Vec3)->Self{
Self{
position:self.position-point,
velocity:self.velocity,
normal:self.normal,
}
}
fn relative_dot(&self,direction:Planar64Vec3)->Fixed<2,64>{
(direction-self.velocity).dot(self.normal)
}
/// Calculate the time of intersection. (previously get_touch_time)
fn solve(&self,ray:&Ray)->Ratio<Fixed<2,64>,Fixed<2,64>>{
(self.position-ray.origin).dot(self.normal)/(ray.direction-self.velocity).dot(self.normal)
}
impl Contact {
fn relative_to(&self, point: Planar64Vec3) -> Self {
Self {
position: self.position - point,
velocity: self.velocity,
normal: self.normal,
}
}
fn relative_dot(&self, direction: Planar64Vec3) -> Fixed<2, 64> {
(direction - self.velocity).dot(self.normal)
}
/// Calculate the time of intersection. (previously get_touch_time)
fn solve(&self, ray: &Ray) -> Ratio<Fixed<2, 64>, Fixed<2, 64>> {
(self.position - ray.origin).dot(self.normal)
/ (ray.direction - self.velocity).dot(self.normal)
}
}
//note that this is horrible with fixed point arithmetic
fn solve1(c0:&Contact)->Option<Ratio<Vector3<Fixed<3,96>>,Fixed<2,64>>>{
const EPSILON:Fixed<2,64>=Fixed::from_bits(Fixed::<2,64>::ONE.to_bits().shr(10));
let det=c0.normal.dot(c0.velocity);
if det.abs()<EPSILON{
return None;
}
let d0=c0.normal.dot(c0.position);
Some(c0.normal*d0/det)
fn solve1(c0: &Contact) -> Option<Ratio<Vector3<Fixed<3, 96>>, Fixed<2, 64>>> {
const EPSILON: Fixed<2, 64> = Fixed::from_bits(Fixed::<2, 64>::ONE.to_bits().shr(10));
let det = c0.normal.dot(c0.velocity);
if det.abs() < EPSILON {
return None;
}
let d0 = c0.normal.dot(c0.position);
Some(c0.normal * d0 / det)
}
fn solve2(c0:&Contact,c1:&Contact)->Option<Ratio<Vector3<Fixed<5,160>>,Fixed<4,128>>>{
const EPSILON:Fixed<4,128>=Fixed::from_bits(Fixed::<4,128>::ONE.to_bits().shr(10));
let u0_u1=c0.velocity.cross(c1.velocity);
let n0_n1=c0.normal.cross(c1.normal);
let det=u0_u1.dot(n0_n1);
if det.abs()<EPSILON{
return None;
}
let d0=c0.normal.dot(c0.position);
let d1=c1.normal.dot(c1.position);
Some((c1.normal.cross(u0_u1)*d0+u0_u1.cross(c0.normal)*d1)/det)
fn solve2(c0: &Contact, c1: &Contact) -> Option<Ratio<Vector3<Fixed<5, 160>>, Fixed<4, 128>>> {
const EPSILON: Fixed<4, 128> = Fixed::from_bits(Fixed::<4, 128>::ONE.to_bits().shr(10));
let u0_u1 = c0.velocity.cross(c1.velocity);
let n0_n1 = c0.normal.cross(c1.normal);
let det = u0_u1.dot(n0_n1);
if det.abs() < EPSILON {
return None;
}
let d0 = c0.normal.dot(c0.position);
let d1 = c1.normal.dot(c1.position);
Some((c1.normal.cross(u0_u1) * d0 + u0_u1.cross(c0.normal) * d1) / det)
}
fn solve3(c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ratio<Vector3<Fixed<4,128>>,Fixed<3,96>>>{
const EPSILON:Fixed<3,96>=Fixed::from_bits(Fixed::<3,96>::ONE.to_bits().shr(10));
let n0_n1=c0.normal.cross(c1.normal);
let det=c2.normal.dot(n0_n1);
if det.abs()<EPSILON{
return None;
}
let d0=c0.normal.dot(c0.position);
let d1=c1.normal.dot(c1.position);
let d2=c2.normal.dot(c2.position);
Some((c1.normal.cross(c2.normal)*d0+c2.normal.cross(c0.normal)*d1+c0.normal.cross(c1.normal)*d2)/det)
fn solve3(
c0: &Contact,
c1: &Contact,
c2: &Contact,
) -> Option<Ratio<Vector3<Fixed<4, 128>>, Fixed<3, 96>>> {
const EPSILON: Fixed<3, 96> = Fixed::from_bits(Fixed::<3, 96>::ONE.to_bits().shr(10));
let n0_n1 = c0.normal.cross(c1.normal);
let det = c2.normal.dot(n0_n1);
if det.abs() < EPSILON {
return None;
}
let d0 = c0.normal.dot(c0.position);
let d1 = c1.normal.dot(c1.position);
let d2 = c2.normal.dot(c2.position);
Some(
(c1.normal.cross(c2.normal) * d0
+ c2.normal.cross(c0.normal) * d1
+ c0.normal.cross(c1.normal) * d2)
/ det,
)
}
fn decompose1(point:Planar64Vec3,u0:Planar64Vec3)->Option<[Ratio<Fixed<2,64>,Fixed<2,64>>;1]>{
let det=u0.dot(u0);
if det==Fixed::ZERO{
return None;
}
let s0=u0.dot(point)/det;
Some([s0])
fn decompose1(
point: Planar64Vec3,
u0: Planar64Vec3,
) -> Option<[Ratio<Fixed<2, 64>, Fixed<2, 64>>; 1]> {
let det = u0.dot(u0);
if det == Fixed::ZERO {
return None;
}
let s0 = u0.dot(point) / det;
Some([s0])
}
fn decompose2(point:Planar64Vec3,u0:Planar64Vec3,u1:Planar64Vec3)->Option<[Ratio<Fixed<4,128>,Fixed<4,128>>;2]>{
let u0_u1=u0.cross(u1);
let det=u0_u1.dot(u0_u1);
if det==Fixed::ZERO{
return None;
}
let s0=u0_u1.dot(point.cross(u1))/det;
let s1=u0_u1.dot(u0.cross(point))/det;
Some([s0,s1])
fn decompose2(
point: Planar64Vec3,
u0: Planar64Vec3,
u1: Planar64Vec3,
) -> Option<[Ratio<Fixed<4, 128>, Fixed<4, 128>>; 2]> {
let u0_u1 = u0.cross(u1);
let det = u0_u1.dot(u0_u1);
if det == Fixed::ZERO {
return None;
}
let s0 = u0_u1.dot(point.cross(u1)) / det;
let s1 = u0_u1.dot(u0.cross(point)) / det;
Some([s0, s1])
}
fn decompose3(point:Planar64Vec3,u0:Planar64Vec3,u1:Planar64Vec3,u2:Planar64Vec3)->Option<[Ratio<Fixed<3,96>,Fixed<3,96>>;3]>{
let det=u0.cross(u1).dot(u2);
if det==Fixed::ZERO{
return None;
}
let s0=point.cross(u1).dot(u2)/det;
let s1=u0.cross(point).dot(u2)/det;
let s2=u0.cross(u1).dot(point)/det;
Some([s0,s1,s2])
fn decompose3(
point: Planar64Vec3,
u0: Planar64Vec3,
u1: Planar64Vec3,
u2: Planar64Vec3,
) -> Option<[Ratio<Fixed<3, 96>, Fixed<3, 96>>; 3]> {
let det = u0.cross(u1).dot(u2);
if det == Fixed::ZERO {
return None;
}
let s0 = point.cross(u1).dot(u2) / det;
let s1 = u0.cross(point).dot(u2) / det;
let s2 = u0.cross(u1).dot(point) / det;
Some([s0, s1, s2])
}
fn is_space_enclosed_2(
a:Planar64Vec3,
b:Planar64Vec3,
)->bool{
a.cross(b)==Vector3::new([Fixed::ZERO;3])
&&a.dot(b).is_negative()
fn is_space_enclosed_2(a: Planar64Vec3, b: Planar64Vec3) -> bool {
a.cross(b) == Vector3::new([Fixed::ZERO; 3]) && a.dot(b).is_negative()
}
fn is_space_enclosed_3(
a:Planar64Vec3,
b:Planar64Vec3,
c:Planar64Vec3
)->bool{
a.cross(b).dot(c)==Fixed::ZERO
&&{
let det_abac=a.cross(b).dot(a.cross(c));
let det_abbc=a.cross(b).dot(b.cross(c));
let det_acbc=a.cross(c).dot(b.cross(c));
return!( det_abac*det_abbc).is_positive()
&&!( det_abbc*det_acbc).is_positive()
&&!(-det_acbc*det_abac).is_positive()
||is_space_enclosed_2(a,b)
||is_space_enclosed_2(a,c)
||is_space_enclosed_2(b,c)
}
fn is_space_enclosed_3(a: Planar64Vec3, b: Planar64Vec3, c: Planar64Vec3) -> bool {
a.cross(b).dot(c) == Fixed::ZERO && {
let det_abac = a.cross(b).dot(a.cross(c));
let det_abbc = a.cross(b).dot(b.cross(c));
let det_acbc = a.cross(c).dot(b.cross(c));
return !(det_abac * det_abbc).is_positive()
&& !(det_abbc * det_acbc).is_positive()
&& !(-det_acbc * det_abac).is_positive()
|| is_space_enclosed_2(a, b)
|| is_space_enclosed_2(a, c)
|| is_space_enclosed_2(b, c);
}
}
fn is_space_enclosed_4(
a:Planar64Vec3,
b:Planar64Vec3,
c:Planar64Vec3,
d:Planar64Vec3,
)->bool{
let det_abc=a.cross(b).dot(c);
let det_abd=a.cross(b).dot(d);
let det_acd=a.cross(c).dot(d);
let det_bcd=b.cross(c).dot(d);
return( det_abc*det_abd).is_negative()
&&(-det_abc*det_acd).is_negative()
&&( det_abd*det_acd).is_negative()
&&( det_abc*det_bcd).is_negative()
&&(-det_abd*det_bcd).is_negative()
&&( det_acd*det_bcd).is_negative()
||is_space_enclosed_3(a,b,c)
||is_space_enclosed_3(a,b,d)
||is_space_enclosed_3(a,c,d)
||is_space_enclosed_3(b,c,d)
fn is_space_enclosed_4(a: Planar64Vec3, b: Planar64Vec3, c: Planar64Vec3, d: Planar64Vec3) -> bool {
let det_abc = a.cross(b).dot(c);
let det_abd = a.cross(b).dot(d);
let det_acd = a.cross(c).dot(d);
let det_bcd = b.cross(c).dot(d);
return (det_abc * det_abd).is_negative()
&& (-det_abc * det_acd).is_negative()
&& (det_abd * det_acd).is_negative()
&& (det_abc * det_bcd).is_negative()
&& (-det_abd * det_bcd).is_negative()
&& (det_acd * det_bcd).is_negative()
|| is_space_enclosed_3(a, b, c)
|| is_space_enclosed_3(a, b, d)
|| is_space_enclosed_3(a, c, d)
|| is_space_enclosed_3(b, c, d);
}
const fn get_push_ray_0(point:Planar64Vec3)->Option<Ray>{
Some(Ray{origin:point,direction:vec3::ZERO})
const fn get_push_ray_0(point: Planar64Vec3) -> Option<Ray> {
Some(Ray {
origin: point,
direction: vec3::ZERO,
})
}
fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{
let direction=solve1(c0)?.divide().fix_1();
let [s0]=decompose1(direction,c0.velocity)?;
if s0.lt_ratio(RATIO_ZERO){
return None;
}
let origin=point+solve1(
&c0.relative_to(point),
)?.divide().fix_1();
Some(Ray{origin,direction})
fn get_push_ray_1(point: Planar64Vec3, c0: &Contact) -> Option<Ray> {
let direction = solve1(c0)?.divide().fix_1();
let [s0] = decompose1(direction, c0.velocity)?;
if s0.lt_ratio(RATIO_ZERO) {
return None;
}
let origin = point + solve1(&c0.relative_to(point))?.divide().fix_1();
Some(Ray { origin, direction })
}
fn get_push_ray_2(point:Planar64Vec3,c0:&Contact,c1:&Contact)->Option<Ray>{
let direction=solve2(c0,c1)?.divide().fix_1();
let [s0,s1]=decompose2(direction,c0.velocity,c1.velocity)?;
if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO){
return None;
}
let origin=point+solve2(
&c0.relative_to(point),
&c1.relative_to(point),
)?.divide().fix_1();
Some(Ray{origin,direction})
fn get_push_ray_2(point: Planar64Vec3, c0: &Contact, c1: &Contact) -> Option<Ray> {
let direction = solve2(c0, c1)?.divide().fix_1();
let [s0, s1] = decompose2(direction, c0.velocity, c1.velocity)?;
if s0.lt_ratio(RATIO_ZERO) || s1.lt_ratio(RATIO_ZERO) {
return None;
}
let origin = point
+ solve2(&c0.relative_to(point), &c1.relative_to(point))?
.divide()
.fix_1();
Some(Ray { origin, direction })
}
fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ray>{
let direction=solve3(c0,c1,c2)?.divide().fix_1();
let [s0,s1,s2]=decompose3(direction,c0.velocity,c1.velocity,c2.velocity)?;
if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO)||s2.lt_ratio(RATIO_ZERO){
return None;
}
let origin=point+solve3(
&c0.relative_to(point),
&c1.relative_to(point),
&c2.relative_to(point),
)?.divide().fix_1();
Some(Ray{origin,direction})
fn get_push_ray_3(point: Planar64Vec3, c0: &Contact, c1: &Contact, c2: &Contact) -> Option<Ray> {
let direction = solve3(c0, c1, c2)?.divide().fix_1();
let [s0, s1, s2] = decompose3(direction, c0.velocity, c1.velocity, c2.velocity)?;
if s0.lt_ratio(RATIO_ZERO) || s1.lt_ratio(RATIO_ZERO) || s2.lt_ratio(RATIO_ZERO) {
return None;
}
let origin = point
+ solve3(
&c0.relative_to(point),
&c1.relative_to(point),
&c2.relative_to(point),
)?
.divide()
.fix_1();
Some(Ray { origin, direction })
}
const fn get_best_push_ray_and_conts_0<'a>(point:Planar64Vec3)->Option<(Ray,Conts<'a>)>{
match get_push_ray_0(point){
Some(ray)=>Some((ray,Conts::new_const())),
None=>None,
}
const fn get_best_push_ray_and_conts_0<'a>(point: Planar64Vec3) -> Option<(Ray, Conts<'a>)> {
match get_push_ray_0(point) {
Some(ray) => Some((ray, Conts::new_const())),
None => None,
}
}
fn get_best_push_ray_and_conts_1(point:Planar64Vec3,c0:&Contact)->Option<(Ray,Conts)>{
get_push_ray_1(point,c0)
.map(|ray|(ray,Conts::from_iter([c0])))
fn get_best_push_ray_and_conts_1(point: Planar64Vec3, c0: &Contact) -> Option<(Ray, Conts)> {
get_push_ray_1(point, c0).map(|ray| (ray, Conts::from_iter([c0])))
}
fn get_best_push_ray_and_conts_2<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Contact)->Option<(Ray,Conts<'a>)>{
if is_space_enclosed_2(c0.normal,c1.normal){
return None;
}
if let Some(ray)=get_push_ray_2(point,c0,c1){
return Some((ray,Conts::from_iter([c0,c1])));
}
if let Some(ray)=get_push_ray_1(point,c0){
if !c1.relative_dot(ray.direction).is_negative(){
return Some((ray,Conts::from_iter([c0])));
}
}
return None;
fn get_best_push_ray_and_conts_2<'a>(
point: Planar64Vec3,
c0: &'a Contact,
c1: &'a Contact,
) -> Option<(Ray, Conts<'a>)> {
if is_space_enclosed_2(c0.normal, c1.normal) {
return None;
}
if let Some(ray) = get_push_ray_2(point, c0, c1) {
return Some((ray, Conts::from_iter([c0, c1])));
}
if let Some(ray) = get_push_ray_1(point, c0) {
if !c1.relative_dot(ray.direction).is_negative() {
return Some((ray, Conts::from_iter([c0])));
}
}
return None;
}
fn get_best_push_ray_and_conts_3<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Contact,c2:&'a Contact)->Option<(Ray,Conts<'a>)>{
if is_space_enclosed_3(c0.normal,c1.normal,c2.normal){
return None;
}
if let Some(ray)=get_push_ray_3(point,c0,c1,c2){
return Some((ray,Conts::from_iter([c0,c1,c2])));
}
if let Some(ray)=get_push_ray_2(point,c0,c1){
if !c2.relative_dot(ray.direction).is_negative(){
return Some((ray,Conts::from_iter([c0,c1])));
}
}
if let Some(ray)=get_push_ray_2(point,c0,c2){
if !c1.relative_dot(ray.direction).is_negative(){
return Some((ray,Conts::from_iter([c0,c2])));
}
}
if let Some(ray)=get_push_ray_1(point,c0){
if !c1.relative_dot(ray.direction).is_negative()
&&!c2.relative_dot(ray.direction).is_negative(){
return Some((ray,Conts::from_iter([c0])));
}
}
return None;
fn get_best_push_ray_and_conts_3<'a>(
point: Planar64Vec3,
c0: &'a Contact,
c1: &'a Contact,
c2: &'a Contact,
) -> Option<(Ray, Conts<'a>)> {
if is_space_enclosed_3(c0.normal, c1.normal, c2.normal) {
return None;
}
if let Some(ray) = get_push_ray_3(point, c0, c1, c2) {
return Some((ray, Conts::from_iter([c0, c1, c2])));
}
if let Some(ray) = get_push_ray_2(point, c0, c1) {
if !c2.relative_dot(ray.direction).is_negative() {
return Some((ray, Conts::from_iter([c0, c1])));
}
}
if let Some(ray) = get_push_ray_2(point, c0, c2) {
if !c1.relative_dot(ray.direction).is_negative() {
return Some((ray, Conts::from_iter([c0, c2])));
}
}
if let Some(ray) = get_push_ray_1(point, c0) {
if !c1.relative_dot(ray.direction).is_negative()
&& !c2.relative_dot(ray.direction).is_negative()
{
return Some((ray, Conts::from_iter([c0])));
}
}
return None;
}
fn get_best_push_ray_and_conts_4<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Contact,c2:&'a Contact,c3:&'a Contact)->Option<(Ray,Conts<'a>)>{
if is_space_enclosed_4(c0.normal,c1.normal,c2.normal,c3.normal){
return None;
}
fn get_best_push_ray_and_conts_4<'a>(
point: Planar64Vec3,
c0: &'a Contact,
c1: &'a Contact,
c2: &'a Contact,
c3: &'a Contact,
) -> Option<(Ray, Conts<'a>)> {
if is_space_enclosed_4(c0.normal, c1.normal, c2.normal, c3.normal) {
return None;
}
let (ray012,conts012)=get_best_push_ray_and_conts_3(point,c0,c1,c2)?;
let (ray013,conts013)=get_best_push_ray_and_conts_3(point,c0,c1,c3)?;
let (ray023,conts023)=get_best_push_ray_and_conts_3(point,c0,c2,c3)?;
let (ray012, conts012) = get_best_push_ray_and_conts_3(point, c0, c1, c2)?;
let (ray013, conts013) = get_best_push_ray_and_conts_3(point, c0, c1, c3)?;
let (ray023, conts023) = get_best_push_ray_and_conts_3(point, c0, c2, c3)?;
let err012=c3.relative_dot(ray012.direction);
let err013=c2.relative_dot(ray013.direction);
let err023=c1.relative_dot(ray023.direction);
let err012 = c3.relative_dot(ray012.direction);
let err013 = c2.relative_dot(ray013.direction);
let err023 = c1.relative_dot(ray023.direction);
let best_err=err012.max(err013).max(err023);
let best_err = err012.max(err013).max(err023);
if best_err==err012{
return Some((ray012,conts012))
}else if best_err==err013{
return Some((ray013,conts013))
}else if best_err==err023{
return Some((ray023,conts023))
}
unreachable!()
if best_err == err012 {
return Some((ray012, conts012));
} else if best_err == err013 {
return Some((ray013, conts013));
} else if best_err == err023 {
return Some((ray023, conts023));
}
unreachable!()
}
fn get_best_push_ray_and_conts<'a>(
point:Planar64Vec3,
conts:Conts<'a>,
)->Option<(Ray,Conts<'a>)>{
match conts.as_slice(){
&[c0,c1,c2,c3]=>get_best_push_ray_and_conts_4(point,c0,c1,c2,c3),
&[c0,c1,c2]=>get_best_push_ray_and_conts_3(point,c0,c1,c2),
&[c0,c1]=>get_best_push_ray_and_conts_2(point,c0,c1),
&[c0]=>get_best_push_ray_and_conts_1(point,c0),
&[]=>get_best_push_ray_and_conts_0(point),
_=>unreachable!(),
}
point: Planar64Vec3,
conts: Conts<'a>,
) -> Option<(Ray, Conts<'a>)> {
match conts.as_slice() {
&[c0, c1, c2, c3] => get_best_push_ray_and_conts_4(point, c0, c1, c2, c3),
&[c0, c1, c2] => get_best_push_ray_and_conts_3(point, c0, c1, c2),
&[c0, c1] => get_best_push_ray_and_conts_2(point, c0, c1),
&[c0] => get_best_push_ray_and_conts_1(point, c0),
&[] => get_best_push_ray_and_conts_0(point),
_ => unreachable!(),
}
}
fn get_first_touch<'a>(contacts:&'a Vec<Contact>,ray:&Ray,conts:&Conts)->Option<(Ratio<Fixed<2,64>,Fixed<2,64>>,&'a Contact)>{
contacts.iter()
.filter(|&contact|
!conts.iter().any(|&c|std::ptr::eq(c,contact))
&&contact.relative_dot(ray.direction).is_negative()
)
.map(|contact|(contact.solve(ray),contact))
.min_by_key(|&(t,_)|t)
fn get_first_touch<'a>(
contacts: &'a Vec<Contact>,
ray: &Ray,
conts: &Conts,
) -> Option<(Ratio<Fixed<2, 64>, Fixed<2, 64>>, &'a Contact)> {
contacts
.iter()
.filter(|&contact| {
!conts.iter().any(|&c| std::ptr::eq(c, contact))
&& contact.relative_dot(ray.direction).is_negative()
})
.map(|contact| (contact.solve(ray), contact))
.min_by_key(|&(t, _)| t)
}
pub fn push_solve(contacts:&Vec<Contact>,point:Planar64Vec3)->Option<Planar64Vec3>{
const ZERO:Ratio<Fixed<1,32>,Fixed<1,32>>=Ratio::new(Fixed::ZERO,Fixed::EPSILON);
let (mut ray,mut conts)=get_best_push_ray_and_conts_0(point)?;
loop{
let (next_t,next_cont)=match get_first_touch(contacts,&ray,&conts){
Some((t,conts))=>(t,conts),
None=>return Some(ray.origin),
};
pub fn push_solve(contacts: &Vec<Contact>, point: Planar64Vec3) -> Option<Planar64Vec3> {
const ZERO: Ratio<Fixed<1, 32>, Fixed<1, 32>> = Ratio::new(Fixed::ZERO, Fixed::EPSILON);
let (mut ray, mut conts) = get_best_push_ray_and_conts_0(point)?;
loop {
let (next_t, next_cont) = match get_first_touch(contacts, &ray, &conts) {
Some((t, conts)) => (t, conts),
None => return Some(ray.origin),
};
if ZERO.le_ratio(next_t){
return Some(ray.origin);
}
if ZERO.le_ratio(next_t) {
return Some(ray.origin);
}
//push_front
if conts.len()==conts.capacity(){
//this is a dead case, new_conts never has more than 3 elements
conts.rotate_right(1);
conts[0]=next_cont;
}else{
conts.push(next_cont);
conts.rotate_right(1);
}
//push_front
if conts.len() == conts.capacity() {
//this is a dead case, new_conts never has more than 3 elements
conts.rotate_right(1);
conts[0] = next_cont;
} else {
conts.push(next_cont);
conts.rotate_right(1);
}
let meet_point=ray.extrapolate(next_t);
match get_best_push_ray_and_conts(meet_point,conts){
Some((new_ray,new_conts))=>(ray,conts)=(new_ray,new_conts),
None=>return Some(meet_point),
}
}
let meet_point = ray.extrapolate(next_t);
match get_best_push_ray_and_conts(meet_point, conts) {
Some((new_ray, new_conts)) => (ray, conts) = (new_ray, new_conts),
None => return Some(meet_point),
}
}
}
#[cfg(test)]
mod tests{
use super::*;
#[test]
fn test_push_solve(){
let contacts=vec![
Contact{
position:vec3::ZERO,
velocity:vec3::Y,
normal:vec3::Y,
}
];
assert_eq!(
Some(vec3::ZERO),
push_solve(&contacts,vec3::NEG_Y)
);
}
mod tests {
use super::*;
#[test]
fn test_push_solve() {
let contacts = vec![Contact {
position: vec3::ZERO,
velocity: vec3::Y,
normal: vec3::Y,
}];
assert_eq!(Some(vec3::ZERO), push_solve(&contacts, vec3::NEG_Y));
}
}

View File

@ -1,70 +1,84 @@
use strafesnet_common::integer::{Ratio64,Ratio64Vec2};
use strafesnet_common::integer::{Ratio64, Ratio64Vec2};
#[derive(Clone)]
struct Ratio{
ratio:f64,
struct Ratio {
ratio: f64,
}
#[derive(Clone)]
enum DerivedFov{
FromScreenAspect,
FromAspect(Ratio),
enum DerivedFov {
FromScreenAspect,
FromAspect(Ratio),
}
#[derive(Clone)]
enum Fov{
Exactly{x:f64,y:f64},
SpecifyXDeriveY{x:f64,y:DerivedFov},
SpecifyYDeriveX{x:DerivedFov,y:f64},
enum Fov {
Exactly { x: f64, y: f64 },
SpecifyXDeriveY { x: f64, y: DerivedFov },
SpecifyYDeriveX { x: DerivedFov, y: f64 },
}
impl Default for Fov{
fn default()->Self{
Fov::SpecifyYDeriveX{x:DerivedFov::FromScreenAspect,y:1.0}
}
impl Default for Fov {
fn default() -> Self {
Fov::SpecifyYDeriveX {
x: DerivedFov::FromScreenAspect,
y: 1.0,
}
}
}
#[derive(Clone)]
enum DerivedSensitivity{
FromRatio(Ratio64),
enum DerivedSensitivity {
FromRatio(Ratio64),
}
#[derive(Clone)]
enum Sensitivity{
Exactly{x:Ratio64,y:Ratio64},
SpecifyXDeriveY{x:Ratio64,y:DerivedSensitivity},
SpecifyYDeriveX{x:DerivedSensitivity,y:Ratio64},
enum Sensitivity {
Exactly { x: Ratio64, y: Ratio64 },
SpecifyXDeriveY { x: Ratio64, y: DerivedSensitivity },
SpecifyYDeriveX { x: DerivedSensitivity, y: Ratio64 },
}
impl Default for Sensitivity{
fn default()->Self{
Sensitivity::SpecifyXDeriveY{x:Ratio64::ONE*524288,y:DerivedSensitivity::FromRatio(Ratio64::ONE)}
}
impl Default for Sensitivity {
fn default() -> Self {
Sensitivity::SpecifyXDeriveY {
x: Ratio64::ONE * 524288,
y: DerivedSensitivity::FromRatio(Ratio64::ONE),
}
}
}
#[derive(Default,Clone)]
pub struct UserSettings{
fov:Fov,
sensitivity:Sensitivity,
#[derive(Default, Clone)]
pub struct UserSettings {
fov: Fov,
sensitivity: Sensitivity,
}
impl UserSettings{
pub fn calculate_fov(&self,zoom:f64,screen_size:&glam::UVec2)->glam::DVec2{
zoom*match &self.fov{
&Fov::Exactly{x,y}=>glam::dvec2(x,y),
Fov::SpecifyXDeriveY{x,y}=>match y{
DerivedFov::FromScreenAspect=>glam::dvec2(*x,x*(screen_size.y as f64/screen_size.x as f64)),
DerivedFov::FromAspect(ratio)=>glam::dvec2(*x,x*ratio.ratio),
},
Fov::SpecifyYDeriveX{x,y}=>match x{
DerivedFov::FromScreenAspect=>glam::dvec2(y*(screen_size.x as f64/screen_size.y as f64),*y),
DerivedFov::FromAspect(ratio)=>glam::dvec2(y*ratio.ratio,*y),
},
}
}
pub fn calculate_sensitivity(&self)->Ratio64Vec2{
match &self.sensitivity{
Sensitivity::Exactly{x,y}=>Ratio64Vec2::new(x.clone(),y.clone()),
Sensitivity::SpecifyXDeriveY{x,y}=>match y{
DerivedSensitivity::FromRatio(ratio)=>Ratio64Vec2::new(x.clone(),x.mul_ref(ratio)),
}
Sensitivity::SpecifyYDeriveX{x,y}=>match x{
DerivedSensitivity::FromRatio(ratio)=>Ratio64Vec2::new(y.mul_ref(ratio),y.clone()),
}
}
}
impl UserSettings {
pub fn calculate_fov(&self, zoom: f64, screen_size: &glam::UVec2) -> glam::DVec2 {
zoom * match &self.fov {
&Fov::Exactly { x, y } => glam::dvec2(x, y),
Fov::SpecifyXDeriveY { x, y } => match y {
DerivedFov::FromScreenAspect => {
glam::dvec2(*x, x * (screen_size.y as f64 / screen_size.x as f64))
}
DerivedFov::FromAspect(ratio) => glam::dvec2(*x, x * ratio.ratio),
},
Fov::SpecifyYDeriveX { x, y } => match x {
DerivedFov::FromScreenAspect => {
glam::dvec2(y * (screen_size.x as f64 / screen_size.y as f64), *y)
}
DerivedFov::FromAspect(ratio) => glam::dvec2(y * ratio.ratio, *y),
},
}
}
pub fn calculate_sensitivity(&self) -> Ratio64Vec2 {
match &self.sensitivity {
Sensitivity::Exactly { x, y } => Ratio64Vec2::new(x.clone(), y.clone()),
Sensitivity::SpecifyXDeriveY { x, y } => match y {
DerivedSensitivity::FromRatio(ratio) => {
Ratio64Vec2::new(x.clone(), x.mul_ref(ratio))
}
},
Sensitivity::SpecifyYDeriveX { x, y } => match x {
DerivedSensitivity::FromRatio(ratio) => {
Ratio64Vec2::new(y.mul_ref(ratio), y.clone())
}
},
}
}
}
/*
@ -74,66 +88,78 @@ sensitivity_y_from_x_ratio=1
Sensitivity::DeriveY{x:0.0.001,y:DerivedSensitivity{ratio:1.0}}
*/
pub fn read_user_settings()->UserSettings{
let mut cfg=configparser::ini::Ini::new();
if let Ok(_)=cfg.load("settings.conf"){
let (cfg_fov_x,cfg_fov_y)=(cfg.getfloat("camera","fov_x"),cfg.getfloat("camera","fov_y"));
let fov=match(cfg_fov_x,cfg_fov_y){
(Ok(Some(fov_x)),Ok(Some(fov_y)))=>Fov::Exactly {
x:fov_x,
y:fov_y
},
(Ok(Some(fov_x)),Ok(None))=>Fov::SpecifyXDeriveY{
x:fov_x,
y:if let Ok(Some(fov_y_from_x_ratio))=cfg.getfloat("camera","fov_y_from_x_ratio"){
DerivedFov::FromAspect(Ratio{ratio:fov_y_from_x_ratio})
}else{
DerivedFov::FromScreenAspect
}
},
(Ok(None),Ok(Some(fov_y)))=>Fov::SpecifyYDeriveX{
x:if let Ok(Some(fov_x_from_y_ratio))=cfg.getfloat("camera","fov_x_from_y_ratio"){
DerivedFov::FromAspect(Ratio{ratio:fov_x_from_y_ratio})
}else{
DerivedFov::FromScreenAspect
},
y:fov_y,
},
_=>{
Fov::default()
},
};
let (cfg_sensitivity_x,cfg_sensitivity_y)=(cfg.getfloat("camera","sensitivity_x"),cfg.getfloat("camera","sensitivity_y"));
let sensitivity=match(cfg_sensitivity_x,cfg_sensitivity_y){
(Ok(Some(sensitivity_x)),Ok(Some(sensitivity_y)))=>Sensitivity::Exactly {
x:Ratio64::try_from(sensitivity_x).unwrap(),
y:Ratio64::try_from(sensitivity_y).unwrap(),
},
(Ok(Some(sensitivity_x)),Ok(None))=>Sensitivity::SpecifyXDeriveY{
x:Ratio64::try_from(sensitivity_x).unwrap(),
y:if let Ok(Some(sensitivity_y_from_x_ratio))=cfg.getfloat("camera","sensitivity_y_from_x_ratio"){
DerivedSensitivity::FromRatio(Ratio64::try_from(sensitivity_y_from_x_ratio).unwrap())
}else{
DerivedSensitivity::FromRatio(Ratio64::ONE)
},
},
(Ok(None),Ok(Some(sensitivity_y)))=>Sensitivity::SpecifyYDeriveX{
x:if let Ok(Some(sensitivity_x_from_y_ratio))=cfg.getfloat("camera","sensitivity_x_from_y_ratio"){
DerivedSensitivity::FromRatio(Ratio64::try_from(sensitivity_x_from_y_ratio).unwrap())
}else{
DerivedSensitivity::FromRatio(Ratio64::ONE)
},
y:Ratio64::try_from(sensitivity_y).unwrap(),
},
_=>{
Sensitivity::default()
},
};
UserSettings{
fov,
sensitivity,
}
}else{
UserSettings::default()
}
}
pub fn read_user_settings() -> UserSettings {
let mut cfg = configparser::ini::Ini::new();
if let Ok(_) = cfg.load("settings.conf") {
let (cfg_fov_x, cfg_fov_y) = (
cfg.getfloat("camera", "fov_x"),
cfg.getfloat("camera", "fov_y"),
);
let fov = match (cfg_fov_x, cfg_fov_y) {
(Ok(Some(fov_x)), Ok(Some(fov_y))) => Fov::Exactly { x: fov_x, y: fov_y },
(Ok(Some(fov_x)), Ok(None)) => Fov::SpecifyXDeriveY {
x: fov_x,
y: if let Ok(Some(fov_y_from_x_ratio)) =
cfg.getfloat("camera", "fov_y_from_x_ratio")
{
DerivedFov::FromAspect(Ratio {
ratio: fov_y_from_x_ratio,
})
} else {
DerivedFov::FromScreenAspect
},
},
(Ok(None), Ok(Some(fov_y))) => Fov::SpecifyYDeriveX {
x: if let Ok(Some(fov_x_from_y_ratio)) =
cfg.getfloat("camera", "fov_x_from_y_ratio")
{
DerivedFov::FromAspect(Ratio {
ratio: fov_x_from_y_ratio,
})
} else {
DerivedFov::FromScreenAspect
},
y: fov_y,
},
_ => Fov::default(),
};
let (cfg_sensitivity_x, cfg_sensitivity_y) = (
cfg.getfloat("camera", "sensitivity_x"),
cfg.getfloat("camera", "sensitivity_y"),
);
let sensitivity = match (cfg_sensitivity_x, cfg_sensitivity_y) {
(Ok(Some(sensitivity_x)), Ok(Some(sensitivity_y))) => Sensitivity::Exactly {
x: Ratio64::try_from(sensitivity_x).unwrap(),
y: Ratio64::try_from(sensitivity_y).unwrap(),
},
(Ok(Some(sensitivity_x)), Ok(None)) => Sensitivity::SpecifyXDeriveY {
x: Ratio64::try_from(sensitivity_x).unwrap(),
y: if let Ok(Some(sensitivity_y_from_x_ratio)) =
cfg.getfloat("camera", "sensitivity_y_from_x_ratio")
{
DerivedSensitivity::FromRatio(
Ratio64::try_from(sensitivity_y_from_x_ratio).unwrap(),
)
} else {
DerivedSensitivity::FromRatio(Ratio64::ONE)
},
},
(Ok(None), Ok(Some(sensitivity_y))) => Sensitivity::SpecifyYDeriveX {
x: if let Ok(Some(sensitivity_x_from_y_ratio)) =
cfg.getfloat("camera", "sensitivity_x_from_y_ratio")
{
DerivedSensitivity::FromRatio(
Ratio64::try_from(sensitivity_x_from_y_ratio).unwrap(),
)
} else {
DerivedSensitivity::FromRatio(Ratio64::ONE)
},
y: Ratio64::try_from(sensitivity_y).unwrap(),
},
_ => Sensitivity::default(),
};
UserSettings { fov, sensitivity }
} else {
UserSettings::default()
}
}

View File

@ -3,242 +3,250 @@ use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::integer;
use strafesnet_common::session::TimeInner as SessionTimeInner;
fn optional_features()->wgpu::Features{
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|wgpu::Features::TEXTURE_COMPRESSION_ETC2
fn optional_features() -> wgpu::Features {
wgpu::Features::TEXTURE_COMPRESSION_ASTC | wgpu::Features::TEXTURE_COMPRESSION_ETC2
}
fn required_features()->wgpu::Features{
wgpu::Features::TEXTURE_COMPRESSION_BC
fn required_features() -> wgpu::Features {
wgpu::Features::TEXTURE_COMPRESSION_BC
}
fn required_downlevel_capabilities()->wgpu::DownlevelCapabilities{
wgpu::DownlevelCapabilities{
flags:wgpu::DownlevelFlags::empty(),
shader_model:wgpu::ShaderModel::Sm5,
..wgpu::DownlevelCapabilities::default()
}
fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
wgpu::DownlevelCapabilities {
flags: wgpu::DownlevelFlags::empty(),
shader_model: wgpu::ShaderModel::Sm5,
..wgpu::DownlevelCapabilities::default()
}
}
pub fn required_limits()->wgpu::Limits{
wgpu::Limits::default()
pub fn required_limits() -> wgpu::Limits {
wgpu::Limits::default()
}
struct SetupContextPartial1{
backends:wgpu::Backends,
instance:wgpu::Instance,
struct SetupContextPartial1 {
backends: wgpu::Backends,
instance: wgpu::Instance,
}
fn create_window(title:&str,event_loop:&winit::event_loop::EventLoop<()>)->Result<winit::window::Window,winit::error::OsError>{
let mut attr=winit::window::WindowAttributes::default();
attr=attr.with_title(title);
#[cfg(windows_OFF)] // TODO
{
use winit::platform::windows::WindowBuilderExtWindows;
builder=builder.with_no_redirection_bitmap(true);
}
event_loop.create_window(attr)
fn create_window(
title: &str,
event_loop: &winit::event_loop::EventLoop<()>,
) -> Result<winit::window::Window, winit::error::OsError> {
let mut attr = winit::window::WindowAttributes::default();
attr = attr.with_title(title);
#[cfg(windows_OFF)] // TODO
{
use winit::platform::windows::WindowBuilderExtWindows;
builder = builder.with_no_redirection_bitmap(true);
}
event_loop.create_window(attr)
}
fn create_instance()->SetupContextPartial1{
let backends=wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all);
let dx12_shader_compiler=wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
SetupContextPartial1{
backends,
instance:wgpu::Instance::new(wgpu::InstanceDescriptor{
backends,
dx12_shader_compiler,
..Default::default()
}),
}
fn create_instance() -> SetupContextPartial1 {
let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all);
let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
SetupContextPartial1 {
backends,
instance: wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
dx12_shader_compiler,
..Default::default()
}),
}
}
impl SetupContextPartial1{
fn create_surface<'a>(self,window:&'a winit::window::Window)->Result<SetupContextPartial2<'a>,wgpu::CreateSurfaceError>{
Ok(SetupContextPartial2{
backends:self.backends,
surface:self.instance.create_surface(window)?,
instance:self.instance,
})
}
impl SetupContextPartial1 {
fn create_surface<'a>(
self,
window: &'a winit::window::Window,
) -> Result<SetupContextPartial2<'a>, wgpu::CreateSurfaceError> {
Ok(SetupContextPartial2 {
backends: self.backends,
surface: self.instance.create_surface(window)?,
instance: self.instance,
})
}
}
struct SetupContextPartial2<'a>{
backends:wgpu::Backends,
instance:wgpu::Instance,
surface:wgpu::Surface<'a>,
struct SetupContextPartial2<'a> {
backends: wgpu::Backends,
instance: wgpu::Instance,
surface: wgpu::Surface<'a>,
}
impl<'a> SetupContextPartial2<'a>{
fn pick_adapter(self)->SetupContextPartial3<'a>{
let adapter;
impl<'a> SetupContextPartial2<'a> {
fn pick_adapter(self) -> SetupContextPartial3<'a> {
let adapter;
//TODO: prefer adapter that implements optional features
//let optional_features=optional_features();
let required_features=required_features();
//TODO: prefer adapter that implements optional features
//let optional_features=optional_features();
let required_features = required_features();
//no helper function smh gotta write it myself
let adapters=self.instance.enumerate_adapters(self.backends);
//no helper function smh gotta write it myself
let adapters = self.instance.enumerate_adapters(self.backends);
let mut chosen_adapter=None;
let mut chosen_adapter_score=0;
for adapter in adapters {
if !adapter.is_surface_supported(&self.surface) {
continue;
}
let mut chosen_adapter = None;
let mut chosen_adapter_score = 0;
for adapter in adapters {
if !adapter.is_surface_supported(&self.surface) {
continue;
}
let score=match adapter.get_info().device_type{
wgpu::DeviceType::IntegratedGpu=>3,
wgpu::DeviceType::DiscreteGpu=>4,
wgpu::DeviceType::VirtualGpu=>2,
wgpu::DeviceType::Other|wgpu::DeviceType::Cpu=>1,
};
let score = match adapter.get_info().device_type {
wgpu::DeviceType::IntegratedGpu => 3,
wgpu::DeviceType::DiscreteGpu => 4,
wgpu::DeviceType::VirtualGpu => 2,
wgpu::DeviceType::Other | wgpu::DeviceType::Cpu => 1,
};
let adapter_features=adapter.features();
if chosen_adapter_score<score&&adapter_features.contains(required_features) {
chosen_adapter_score=score;
chosen_adapter=Some(adapter);
}
}
let adapter_features = adapter.features();
if chosen_adapter_score < score && adapter_features.contains(required_features) {
chosen_adapter_score = score;
chosen_adapter = Some(adapter);
}
}
if let Some(maybe_chosen_adapter)=chosen_adapter{
adapter=maybe_chosen_adapter;
}else{
panic!("No suitable GPU adapters found on the system!");
}
if let Some(maybe_chosen_adapter) = chosen_adapter {
adapter = maybe_chosen_adapter;
} else {
panic!("No suitable GPU adapters found on the system!");
}
let adapter_info = adapter.get_info();
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
let adapter_info=adapter.get_info();
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
let required_downlevel_capabilities=required_downlevel_capabilities();
let downlevel_capabilities=adapter.get_downlevel_capabilities();
assert!(
downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
"Adapter does not support the minimum shader model required to run this example: {:?}",
required_downlevel_capabilities.shader_model
);
assert!(
let required_downlevel_capabilities = required_downlevel_capabilities();
let downlevel_capabilities = adapter.get_downlevel_capabilities();
assert!(
downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
"Adapter does not support the minimum shader model required to run this example: {:?}",
required_downlevel_capabilities.shader_model
);
assert!(
downlevel_capabilities
.flags
.contains(required_downlevel_capabilities.flags),
"Adapter does not support the downlevel capabilities required to run this example: {:?}",
required_downlevel_capabilities.flags - downlevel_capabilities.flags
);
SetupContextPartial3{
instance:self.instance,
surface:self.surface,
adapter,
}
}
SetupContextPartial3 {
instance: self.instance,
surface: self.surface,
adapter,
}
}
}
struct SetupContextPartial3<'a>{
instance:wgpu::Instance,
surface:wgpu::Surface<'a>,
adapter:wgpu::Adapter,
struct SetupContextPartial3<'a> {
instance: wgpu::Instance,
surface: wgpu::Surface<'a>,
adapter: wgpu::Adapter,
}
impl<'a> SetupContextPartial3<'a>{
fn request_device(self)->SetupContextPartial4<'a>{
let optional_features=optional_features();
let required_features=required_features();
impl<'a> SetupContextPartial3<'a> {
fn request_device(self) -> SetupContextPartial4<'a> {
let optional_features = optional_features();
let required_features = required_features();
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
let needed_limits=required_limits().using_resolution(self.adapter.limits());
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
let needed_limits = required_limits().using_resolution(self.adapter.limits());
let trace_dir=std::env::var("WGPU_TRACE");
let (device, queue)=pollster::block_on(self.adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: (optional_features & self.adapter.features()) | required_features,
required_limits: needed_limits,
memory_hints:wgpu::MemoryHints::Performance,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
))
.expect("Unable to find a suitable GPU adapter!");
let trace_dir = std::env::var("WGPU_TRACE");
let (device, queue) = pollster::block_on(self.adapter.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: (optional_features & self.adapter.features())
| required_features,
required_limits: needed_limits,
memory_hints: wgpu::MemoryHints::Performance,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
))
.expect("Unable to find a suitable GPU adapter!");
SetupContextPartial4{
instance:self.instance,
surface:self.surface,
adapter:self.adapter,
device,
queue,
}
}
SetupContextPartial4 {
instance: self.instance,
surface: self.surface,
adapter: self.adapter,
device,
queue,
}
}
}
struct SetupContextPartial4<'a>{
instance:wgpu::Instance,
surface:wgpu::Surface<'a>,
adapter:wgpu::Adapter,
device:wgpu::Device,
queue:wgpu::Queue,
struct SetupContextPartial4<'a> {
instance: wgpu::Instance,
surface: wgpu::Surface<'a>,
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
}
impl<'a> SetupContextPartial4<'a>{
fn configure_surface(self,size:&'a winit::dpi::PhysicalSize<u32>)->SetupContext<'a>{
let mut config=self.surface
.get_default_config(&self.adapter, size.width, size.height)
.expect("Surface isn't supported by the adapter.");
let surface_view_format=config.format.add_srgb_suffix();
config.view_formats.push(surface_view_format);
config.present_mode=wgpu::PresentMode::AutoNoVsync;
self.surface.configure(&self.device, &config);
impl<'a> SetupContextPartial4<'a> {
fn configure_surface(self, size: &'a winit::dpi::PhysicalSize<u32>) -> SetupContext<'a> {
let mut config = self
.surface
.get_default_config(&self.adapter, size.width, size.height)
.expect("Surface isn't supported by the adapter.");
let surface_view_format = config.format.add_srgb_suffix();
config.view_formats.push(surface_view_format);
config.present_mode = wgpu::PresentMode::AutoNoVsync;
self.surface.configure(&self.device, &config);
SetupContext{
instance:self.instance,
surface:self.surface,
device:self.device,
queue:self.queue,
config,
}
}
SetupContext {
instance: self.instance,
surface: self.surface,
device: self.device,
queue: self.queue,
config,
}
}
}
pub struct SetupContext<'a>{
pub instance:wgpu::Instance,
pub surface:wgpu::Surface<'a>,
pub device:wgpu::Device,
pub queue:wgpu::Queue,
pub config:wgpu::SurfaceConfiguration,
pub struct SetupContext<'a> {
pub instance: wgpu::Instance,
pub surface: wgpu::Surface<'a>,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub config: wgpu::SurfaceConfiguration,
}
pub fn setup_and_start(title:&str){
let event_loop=winit::event_loop::EventLoop::new().unwrap();
pub fn setup_and_start(title: &str) {
let event_loop = winit::event_loop::EventLoop::new().unwrap();
println!("Initializing the surface...");
println!("Initializing the surface...");
let partial_1=create_instance();
let partial_1 = create_instance();
let window=create_window(title,&event_loop).unwrap();
let window = create_window(title, &event_loop).unwrap();
let partial_2=partial_1.create_surface(&window).unwrap();
let partial_2 = partial_1.create_surface(&window).unwrap();
let partial_3=partial_2.pick_adapter();
let partial_3 = partial_2.pick_adapter();
let partial_4=partial_3.request_device();
let partial_4 = partial_3.request_device();
let size=window.inner_size();
let size = window.inner_size();
let setup_context=partial_4.configure_surface(&size);
let setup_context = partial_4.configure_surface(&size);
//dedicated thread to ping request redraw back and resize the window doesn't seem logical
//dedicated thread to ping request redraw back and resize the window doesn't seem logical
//the thread that spawns the physics thread
let mut window_thread=crate::window::worker(
&window,
setup_context,
);
//the thread that spawns the physics thread
let mut window_thread = crate::window::worker(&window, setup_context);
if let Some(arg)=std::env::args().nth(1){
let path=std::path::PathBuf::from(arg);
window_thread.send(TimedInstruction{
time:integer::Time::ZERO,
instruction:WindowInstruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)),
}).unwrap();
};
if let Some(arg) = std::env::args().nth(1) {
let path = std::path::PathBuf::from(arg);
window_thread
.send(TimedInstruction {
time: integer::Time::ZERO,
instruction: WindowInstruction::WindowEvent(
winit::event::WindowEvent::DroppedFile(path),
),
})
.unwrap();
};
println!("Entering event loop...");
let root_time=std::time::Instant::now();
run_event_loop(event_loop,window_thread,root_time).unwrap();
println!("Entering event loop...");
let root_time = std::time::Instant::now();
run_event_loop(event_loop, window_thread, root_time).unwrap();
}
fn run_event_loop(
event_loop:winit::event_loop::EventLoop<()>,
mut window_thread:crate::compat_worker::QNWorker<TimedInstruction<WindowInstruction,SessionTimeInner>>,
root_time:std::time::Instant
)->Result<(),winit::error::EventLoopError>{
event_loop.run(move |event,elwt|{
event_loop: winit::event_loop::EventLoop<()>,
mut window_thread: crate::compat_worker::QNWorker<
TimedInstruction<WindowInstruction, SessionTimeInner>,
>,
root_time: std::time::Instant,
) -> Result<(), winit::error::EventLoopError> {
event_loop.run(move |event,elwt|{
let time=integer::Time::from_nanos(root_time.elapsed().as_nanos() as i64);
// *control_flow=if cfg!(feature="metal-auto-capture"){
// winit::event_loop::ControlFlow::Exit

View File

@ -1,217 +1,292 @@
use crate::physics_worker::InputInstruction;
use strafesnet_common::integer;
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::integer;
use strafesnet_common::session::{Time as SessionTime, TimeInner as SessionTimeInner};
pub enum WindowInstruction{
Resize(winit::dpi::PhysicalSize<u32>),
WindowEvent(winit::event::WindowEvent),
DeviceEvent(winit::event::DeviceEvent),
RequestRedraw,
Render,
pub enum WindowInstruction {
Resize(winit::dpi::PhysicalSize<u32>),
WindowEvent(winit::event::WindowEvent),
DeviceEvent(winit::event::DeviceEvent),
RequestRedraw,
Render,
}
//holds thread handles to dispatch to
struct WindowContext<'a>{
manual_mouse_lock:bool,
mouse:strafesnet_common::mouse::MouseState<SessionTimeInner>,//std::sync::Arc<std::sync::Mutex<>>
screen_size:glam::UVec2,
window:&'a winit::window::Window,
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<crate::physics_worker::Instruction,SessionTimeInner>>,
struct WindowContext<'a> {
manual_mouse_lock: bool,
mouse: strafesnet_common::mouse::MouseState<SessionTimeInner>, //std::sync::Arc<std::sync::Mutex<>>
screen_size: glam::UVec2,
window: &'a winit::window::Window,
physics_thread: crate::compat_worker::QNWorker<
'a,
TimedInstruction<crate::physics_worker::Instruction, SessionTimeInner>,
>,
}
impl WindowContext<'_>{
fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<u32>{
winit::dpi::PhysicalPosition::new(self.screen_size.x/2,self.screen_size.y/2)
}
fn window_event(&mut self,time:SessionTime,event:winit::event::WindowEvent){
match event{
winit::event::WindowEvent::DroppedFile(path)=>{
match crate::file::load(path.as_path()){
Ok(map)=>self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ChangeMap(map)}).unwrap(),
Err(e)=>println!("Failed to load map: {e}"),
}
},
winit::event::WindowEvent::Focused(state)=>{
//pause unpause
self.physics_thread.send(TimedInstruction{
time,
instruction:crate::physics_worker::Instruction::SetPaused(!state),
}).unwrap();
//recalculate pressed keys on focus
},
winit::event::WindowEvent::KeyboardInput{
event:winit::event::KeyEvent{state,logical_key,repeat:false,..},
..
}=>{
match (logical_key,state){
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Pressed)=>{
self.manual_mouse_lock=false;
match self.window.set_cursor_position(self.get_middle_of_screen()){
Ok(())=>(),
Err(e)=>println!("Could not set cursor position: {:?}",e),
}
match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){
Ok(())=>(),
Err(e)=>println!("Could not release cursor: {:?}",e),
}
self.window.set_cursor_visible(state.is_pressed());
},
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Released)=>{
//if cursor is outside window don't lock but apparently there's no get pos function
//let pos=window.get_cursor_pos();
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Locked){
Ok(())=>(),
Err(_)=>{
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
Ok(())=>(),
Err(e)=>{
self.manual_mouse_lock=true;
println!("Could not confine cursor: {:?}",e)
},
}
}
}
self.window.set_cursor_visible(state.is_pressed());
},
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11),winit::event::ElementState::Pressed)=>{
if self.window.fullscreen().is_some(){
self.window.set_fullscreen(None);
}else{
self.window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
}
},
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape),winit::event::ElementState::Pressed)=>{
self.manual_mouse_lock=false;
match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){
Ok(())=>(),
Err(e)=>println!("Could not release cursor: {:?}",e),
}
self.window.set_cursor_visible(true);
},
(keycode,state)=>{
let s=state.is_pressed();
if let Some(input_instruction)=match keycode{
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(InputInstruction::Jump(s)),
winit::keyboard::Key::Character(key)=>match key.as_str(){
"W"|"w"=>Some(InputInstruction::MoveForward(s)),
"A"|"a"=>Some(InputInstruction::MoveLeft(s)),
"S"|"s"=>Some(InputInstruction::MoveBack(s)),
"D"|"d"=>Some(InputInstruction::MoveRight(s)),
"E"|"e"=>Some(InputInstruction::MoveUp(s)),
"Q"|"q"=>Some(InputInstruction::MoveDown(s)),
"Z"|"z"=>Some(InputInstruction::Zoom(s)),
"R"|"r"=>if s{
//mouse needs to be reset since the position is absolute
self.mouse=strafesnet_common::mouse::MouseState::default();
Some(InputInstruction::ResetAndRestart)
}else{None},
"F"|"f"=>if s{Some(InputInstruction::PracticeFly)}else{None},
_=>None,
},
_=>None,
}{
self.physics_thread.send(TimedInstruction{
time,
instruction:crate::physics_worker::Instruction::Input(input_instruction),
}).unwrap();
}
},
}
},
_=>(),
}
}
impl WindowContext<'_> {
fn get_middle_of_screen(&self) -> winit::dpi::PhysicalPosition<u32> {
winit::dpi::PhysicalPosition::new(self.screen_size.x / 2, self.screen_size.y / 2)
}
fn window_event(&mut self, time: SessionTime, event: winit::event::WindowEvent) {
match event {
winit::event::WindowEvent::DroppedFile(path) => {
match crate::file::load(path.as_path()) {
Ok(map) => self
.physics_thread
.send(TimedInstruction {
time,
instruction: crate::physics_worker::Instruction::ChangeMap(map),
})
.unwrap(),
Err(e) => println!("Failed to load map: {e}"),
}
}
winit::event::WindowEvent::Focused(state) => {
//pause unpause
self.physics_thread
.send(TimedInstruction {
time,
instruction: crate::physics_worker::Instruction::SetPaused(!state),
})
.unwrap();
//recalculate pressed keys on focus
}
winit::event::WindowEvent::KeyboardInput {
event:
winit::event::KeyEvent {
state,
logical_key,
repeat: false,
..
},
..
} => {
match (logical_key, state) {
(
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),
winit::event::ElementState::Pressed,
) => {
self.manual_mouse_lock = false;
match self.window.set_cursor_position(self.get_middle_of_screen()) {
Ok(()) => (),
Err(e) => println!("Could not set cursor position: {:?}", e),
}
match self
.window
.set_cursor_grab(winit::window::CursorGrabMode::None)
{
Ok(()) => (),
Err(e) => println!("Could not release cursor: {:?}", e),
}
self.window.set_cursor_visible(state.is_pressed());
}
(
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),
winit::event::ElementState::Released,
) => {
//if cursor is outside window don't lock but apparently there's no get pos function
//let pos=window.get_cursor_pos();
match self
.window
.set_cursor_grab(winit::window::CursorGrabMode::Locked)
{
Ok(()) => (),
Err(_) => {
match self
.window
.set_cursor_grab(winit::window::CursorGrabMode::Confined)
{
Ok(()) => (),
Err(e) => {
self.manual_mouse_lock = true;
println!("Could not confine cursor: {:?}", e)
}
}
}
}
self.window.set_cursor_visible(state.is_pressed());
}
(
winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11),
winit::event::ElementState::Pressed,
) => {
if self.window.fullscreen().is_some() {
self.window.set_fullscreen(None);
} else {
self.window
.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
}
}
(
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape),
winit::event::ElementState::Pressed,
) => {
self.manual_mouse_lock = false;
match self
.window
.set_cursor_grab(winit::window::CursorGrabMode::None)
{
Ok(()) => (),
Err(e) => println!("Could not release cursor: {:?}", e),
}
self.window.set_cursor_visible(true);
}
(keycode, state) => {
let s = state.is_pressed();
if let Some(input_instruction) = match keycode {
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space) => {
Some(InputInstruction::Jump(s))
}
winit::keyboard::Key::Character(key) => match key.as_str() {
"W" | "w" => Some(InputInstruction::MoveForward(s)),
"A" | "a" => Some(InputInstruction::MoveLeft(s)),
"S" | "s" => Some(InputInstruction::MoveBack(s)),
"D" | "d" => Some(InputInstruction::MoveRight(s)),
"E" | "e" => Some(InputInstruction::MoveUp(s)),
"Q" | "q" => Some(InputInstruction::MoveDown(s)),
"Z" | "z" => Some(InputInstruction::Zoom(s)),
"R" | "r" => {
if s {
//mouse needs to be reset since the position is absolute
self.mouse =
strafesnet_common::mouse::MouseState::default();
Some(InputInstruction::ResetAndRestart)
} else {
None
}
}
"F" | "f" => {
if s {
Some(InputInstruction::PracticeFly)
} else {
None
}
}
_ => None,
},
_ => None,
} {
self.physics_thread
.send(TimedInstruction {
time,
instruction: crate::physics_worker::Instruction::Input(
input_instruction,
),
})
.unwrap();
}
}
}
}
_ => (),
}
}
fn device_event(&mut self,time:SessionTime,event: winit::event::DeviceEvent){
match event{
winit::event::DeviceEvent::MouseMotion{
delta,//these (f64,f64) are integers on my machine
}=>{
if self.manual_mouse_lock{
match self.window.set_cursor_position(self.get_middle_of_screen()){
Ok(())=>(),
Err(e)=>println!("Could not set cursor position: {:?}",e),
}
}
//do not step the physics because the mouse polling rate is higher than the physics can run.
//essentially the previous input will be overwritten until a true step runs
//which is fine because they run all the time.
let delta=glam::ivec2(delta.0 as i32,delta.1 as i32);
self.mouse.pos+=delta;
self.physics_thread.send(TimedInstruction{
time,
instruction:crate::physics_worker::Instruction::Input(InputInstruction::MoveMouse(self.mouse.pos)),
}).unwrap();
},
winit::event::DeviceEvent::MouseWheel {
delta,
}=>{
println!("mousewheel {:?}",delta);
if false{//self.physics.style.use_scroll{
self.physics_thread.send(TimedInstruction{
time,
instruction:crate::physics_worker::Instruction::Input(InputInstruction::Jump(true)),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump
}).unwrap();
}
},
_=>(),
}
}
fn device_event(&mut self, time: SessionTime, event: winit::event::DeviceEvent) {
match event {
winit::event::DeviceEvent::MouseMotion {
delta, //these (f64,f64) are integers on my machine
} => {
if self.manual_mouse_lock {
match self.window.set_cursor_position(self.get_middle_of_screen()) {
Ok(()) => (),
Err(e) => println!("Could not set cursor position: {:?}", e),
}
}
//do not step the physics because the mouse polling rate is higher than the physics can run.
//essentially the previous input will be overwritten until a true step runs
//which is fine because they run all the time.
let delta = glam::ivec2(delta.0 as i32, delta.1 as i32);
self.mouse.pos += delta;
self.physics_thread
.send(TimedInstruction {
time,
instruction: crate::physics_worker::Instruction::Input(
InputInstruction::MoveMouse(self.mouse.pos),
),
})
.unwrap();
}
winit::event::DeviceEvent::MouseWheel { delta } => {
println!("mousewheel {:?}", delta);
if false {
//self.physics.style.use_scroll{
self.physics_thread
.send(TimedInstruction {
time,
instruction: crate::physics_worker::Instruction::Input(
InputInstruction::Jump(true),
), //activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump
})
.unwrap();
}
}
_ => (),
}
}
}
pub fn worker<'a>(
window:&'a winit::window::Window,
setup_context:crate::setup::SetupContext<'a>,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction,SessionTimeInner>>{
// WindowContextSetup::new
let user_settings=crate::settings::read_user_settings();
window: &'a winit::window::Window,
setup_context: crate::setup::SetupContext<'a>,
) -> crate::compat_worker::QNWorker<'a, TimedInstruction<WindowInstruction, SessionTimeInner>> {
// WindowContextSetup::new
let user_settings = crate::settings::read_user_settings();
let mut graphics=crate::graphics::GraphicsState::new(&setup_context.device,&setup_context.queue,&setup_context.config);
graphics.load_user_settings(&user_settings);
let mut graphics = crate::graphics::GraphicsState::new(
&setup_context.device,
&setup_context.queue,
&setup_context.config,
);
graphics.load_user_settings(&user_settings);
//WindowContextSetup::into_context
let screen_size=glam::uvec2(setup_context.config.width,setup_context.config.height);
let graphics_thread=crate::graphics_worker::new(graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue);
let mut window_context=WindowContext{
manual_mouse_lock:false,
mouse:strafesnet_common::mouse::MouseState::default(),
//make sure to update this!!!!!
screen_size,
window,
physics_thread:crate::physics_worker::new(
graphics_thread,
user_settings,
),
};
//WindowContextSetup::into_context
let screen_size = glam::uvec2(setup_context.config.width, setup_context.config.height);
let graphics_thread = crate::graphics_worker::new(
graphics,
setup_context.config,
setup_context.surface,
setup_context.device,
setup_context.queue,
);
let mut window_context = WindowContext {
manual_mouse_lock: false,
mouse: strafesnet_common::mouse::MouseState::default(),
//make sure to update this!!!!!
screen_size,
window,
physics_thread: crate::physics_worker::new(graphics_thread, user_settings),
};
//WindowContextSetup::into_worker
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<WindowInstruction,SessionTimeInner>|{
match ins.instruction{
WindowInstruction::RequestRedraw=>{
window_context.window.request_redraw();
}
WindowInstruction::WindowEvent(window_event)=>{
window_context.window_event(ins.time,window_event);
},
WindowInstruction::DeviceEvent(device_event)=>{
window_context.device_event(ins.time,device_event);
},
WindowInstruction::Resize(size)=>{
window_context.physics_thread.send(
TimedInstruction{
time:ins.time,
instruction:crate::physics_worker::Instruction::Resize(size)
}
).unwrap();
}
WindowInstruction::Render=>{
window_context.physics_thread.send(
TimedInstruction{
time:ins.time,
instruction:crate::physics_worker::Instruction::Render
}
).unwrap();
}
}
})
//WindowContextSetup::into_worker
crate::compat_worker::QNWorker::new(
move |ins: TimedInstruction<WindowInstruction, SessionTimeInner>| match ins.instruction {
WindowInstruction::RequestRedraw => {
window_context.window.request_redraw();
}
WindowInstruction::WindowEvent(window_event) => {
window_context.window_event(ins.time, window_event);
}
WindowInstruction::DeviceEvent(device_event) => {
window_context.device_event(ins.time, device_event);
}
WindowInstruction::Resize(size) => {
window_context
.physics_thread
.send(TimedInstruction {
time: ins.time,
instruction: crate::physics_worker::Instruction::Resize(size),
})
.unwrap();
}
WindowInstruction::Render => {
window_context
.physics_thread
.send(TimedInstruction {
time: ins.time,
instruction: crate::physics_worker::Instruction::Render,
})
.unwrap();
}
},
)
}

View File

@ -1,34 +1,34 @@
use std::thread;
use std::sync::{mpsc,Arc};
use parking_lot::Mutex;
use std::sync::{mpsc, Arc};
use std::thread;
//WorkerPool
struct Pool(u32);
enum PoolOrdering{
Single,//single thread cannot get out of order
Ordered(u32),//order matters and should be buffered/dropped according to ControlFlow
Unordered(u32),//order does not matter
enum PoolOrdering {
Single, //single thread cannot get out of order
Ordered(u32), //order matters and should be buffered/dropped according to ControlFlow
Unordered(u32), //order does not matter
}
//WorkerInput
enum Input{
//no input, workers have everything needed at creation
None,
//Immediate input to any available worker, dropped if they are overflowing (all workers are busy)
Immediate,
//Queued input is ordered, but serial jobs that mutate state (such as running physics) can only be done with a single worker
Queued,//"Fifo"
//Query a function to get next input when a thread becomes available
//worker stops querying when Query function returns None and dies after all threads complete
//lifetimes sound crazy on this one
Query,
//Queue of length one, the input is replaced if it is submitted twice before the current work finishes
Mailbox,
enum Input {
//no input, workers have everything needed at creation
None,
//Immediate input to any available worker, dropped if they are overflowing (all workers are busy)
Immediate,
//Queued input is ordered, but serial jobs that mutate state (such as running physics) can only be done with a single worker
Queued, //"Fifo"
//Query a function to get next input when a thread becomes available
//worker stops querying when Query function returns None and dies after all threads complete
//lifetimes sound crazy on this one
Query,
//Queue of length one, the input is replaced if it is submitted twice before the current work finishes
Mailbox,
}
//WorkerOutput
enum Output{
None(Pool),
Realtime(PoolOrdering),//outputs are dropped if they are out of order and order is demanded
Buffered(PoolOrdering),//outputs are held back internally if they are out of order and order is demanded
enum Output {
None(Pool),
Realtime(PoolOrdering), //outputs are dropped if they are out of order and order is demanded
Buffered(PoolOrdering), //outputs are held back internally if they are out of order and order is demanded
}
//It would be possible to implement all variants
@ -42,9 +42,9 @@ enum Output{
//when ordering is requested, output is ordered by the order each thread is run
//which is the same as the order that the input data is processed except for Input::None which has no input data
//WorkerDescription
struct Description{
input:Input,
output:Output,
struct Description {
input: Input,
output: Output,
}
//The goal here is to have a worker thread that parks itself when it runs out of work.
@ -54,163 +54,169 @@ struct Description{
/*
QR = WorkerDescription{
input:Queued,
output:Realtime(Single),
input:Queued,
output:Realtime(Single),
}
*/
pub struct QRWorker<Task:Send,Value:Clone>{
sender: mpsc::Sender<Task>,
value:Arc<Mutex<Value>>,
pub struct QRWorker<Task: Send, Value: Clone> {
sender: mpsc::Sender<Task>,
value: Arc<Mutex<Value>>,
}
impl<Task:Send+'static,Value:Clone+Send+'static> QRWorker<Task,Value>{
pub fn new<F:FnMut(Task)->Value+Send+'static>(value:Value,mut f:F) -> Self {
let (sender, receiver) = mpsc::channel::<Task>();
let ret=Self {
sender,
value:Arc::new(Mutex::new(value)),
};
let value=ret.value.clone();
thread::spawn(move || {
loop {
match receiver.recv() {
Ok(task) => {
let v=f(task);//make sure function is evaluated before lock is acquired
*value.lock()=v;
}
Err(_) => {
println!("Worker stopping.",);
break;
}
}
}
});
ret
}
impl<Task: Send + 'static, Value: Clone + Send + 'static> QRWorker<Task, Value> {
pub fn new<F: FnMut(Task) -> Value + Send + 'static>(value: Value, mut f: F) -> Self {
let (sender, receiver) = mpsc::channel::<Task>();
let ret = Self {
sender,
value: Arc::new(Mutex::new(value)),
};
let value = ret.value.clone();
thread::spawn(move || {
loop {
match receiver.recv() {
Ok(task) => {
let v = f(task); //make sure function is evaluated before lock is acquired
*value.lock() = v;
}
Err(_) => {
println!("Worker stopping.",);
break;
}
}
}
});
ret
}
pub fn send(&self,task:Task)->Result<(), mpsc::SendError<Task>>{
self.sender.send(task)
}
pub fn send(&self, task: Task) -> Result<(), mpsc::SendError<Task>> {
self.sender.send(task)
}
pub fn grab_clone(&self)->Value{
self.value.lock().clone()
}
pub fn grab_clone(&self) -> Value {
self.value.lock().clone()
}
}
/*
QN = WorkerDescription{
input:Queued,
output:None(Single),
input:Queued,
output:None(Single),
}
*/
//None Output Worker does all its work internally from the perspective of the work submitter
pub struct QNWorker<'a,Task:Send>{
sender: mpsc::Sender<Task>,
handle:thread::ScopedJoinHandle<'a,()>,
pub struct QNWorker<'a, Task: Send> {
sender: mpsc::Sender<Task>,
handle: thread::ScopedJoinHandle<'a, ()>,
}
impl<'a,Task:Send+'a> QNWorker<'a,Task>{
pub fn new<F:FnMut(Task)+Send+'a>(scope:&'a thread::Scope<'a,'_>,mut f:F)->QNWorker<'a,Task>{
let (sender,receiver)=mpsc::channel::<Task>();
let handle=scope.spawn(move ||{
loop {
match receiver.recv() {
Ok(task)=>f(task),
Err(_)=>{
println!("Worker stopping.",);
break;
}
}
}
});
Self{
sender,
handle,
}
}
pub fn send(&self,task:Task)->Result<(),mpsc::SendError<Task>>{
self.sender.send(task)
}
impl<'a, Task: Send + 'a> QNWorker<'a, Task> {
pub fn new<F: FnMut(Task) + Send + 'a>(
scope: &'a thread::Scope<'a, '_>,
mut f: F,
) -> QNWorker<'a, Task> {
let (sender, receiver) = mpsc::channel::<Task>();
let handle = scope.spawn(move || loop {
match receiver.recv() {
Ok(task) => f(task),
Err(_) => {
println!("Worker stopping.",);
break;
}
}
});
Self { sender, handle }
}
pub fn send(&self, task: Task) -> Result<(), mpsc::SendError<Task>> {
self.sender.send(task)
}
}
/*
IN = WorkerDescription{
input:Immediate,
output:None(Single),
input:Immediate,
output:None(Single),
}
*/
//Inputs are dropped if the worker is busy
pub struct INWorker<'a,Task:Send>{
sender: mpsc::SyncSender<Task>,
handle:thread::ScopedJoinHandle<'a,()>,
pub struct INWorker<'a, Task: Send> {
sender: mpsc::SyncSender<Task>,
handle: thread::ScopedJoinHandle<'a, ()>,
}
impl<'a,Task:Send+'a> INWorker<'a,Task>{
pub fn new<F:FnMut(Task)+Send+'a>(scope:&'a thread::Scope<'a,'_>,mut f:F)->INWorker<'a,Task>{
let (sender,receiver)=mpsc::sync_channel::<Task>(1);
let handle=scope.spawn(move ||{
loop {
match receiver.recv() {
Ok(task)=>f(task),
Err(_)=>{
println!("Worker stopping.",);
break;
}
}
}
});
Self{
sender,
handle,
}
}
//blocking!
pub fn blocking_send(&self,task:Task)->Result<(), mpsc::SendError<Task>>{
self.sender.send(task)
}
pub fn send(&self,task:Task)->Result<(), mpsc::TrySendError<Task>>{
self.sender.try_send(task)
}
impl<'a, Task: Send + 'a> INWorker<'a, Task> {
pub fn new<F: FnMut(Task) + Send + 'a>(
scope: &'a thread::Scope<'a, '_>,
mut f: F,
) -> INWorker<'a, Task> {
let (sender, receiver) = mpsc::sync_channel::<Task>(1);
let handle = scope.spawn(move || loop {
match receiver.recv() {
Ok(task) => f(task),
Err(_) => {
println!("Worker stopping.",);
break;
}
}
});
Self { sender, handle }
}
//blocking!
pub fn blocking_send(&self, task: Task) -> Result<(), mpsc::SendError<Task>> {
self.sender.send(task)
}
pub fn send(&self, task: Task) -> Result<(), mpsc::TrySendError<Task>> {
self.sender.try_send(task)
}
}
#[cfg(test)]
mod test{
use super::{thread,QRWorker};
type Body=crate::physics::Body;
use strafesnet_common::{integer,instruction};
#[test]//How to run this test with printing: cargo test --release -- --nocapture
fn test_worker() {
// Create the worker thread
let test_body=Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO);
let worker=QRWorker::new(Body::ZERO,
|_|Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO)
);
mod test {
use super::{thread, QRWorker};
type Body = crate::physics::Body;
use strafesnet_common::{instruction, integer};
#[test] //How to run this test with printing: cargo test --release -- --nocapture
fn test_worker() {
// Create the worker thread
let test_body = Body::new(
integer::vec3::ONE,
integer::vec3::ONE,
integer::vec3::ONE,
integer::Time::ZERO,
);
let worker = QRWorker::new(Body::ZERO, |_| {
Body::new(
integer::vec3::ONE,
integer::vec3::ONE,
integer::vec3::ONE,
integer::Time::ZERO,
)
});
// Send tasks to the worker
for _ in 0..5 {
let task = instruction::TimedInstruction{
time:strafesnet_common::physics::Time::ZERO,
instruction:strafesnet_common::physics::Instruction::Idle,
};
worker.send(task).unwrap();
}
// Send tasks to the worker
for _ in 0..5 {
let task = instruction::TimedInstruction {
time: strafesnet_common::physics::Time::ZERO,
instruction: strafesnet_common::physics::Instruction::Idle,
};
worker.send(task).unwrap();
}
// Optional: Signal the worker to stop (in a real-world scenario)
// sender.send("STOP".to_string()).unwrap();
// Optional: Signal the worker to stop (in a real-world scenario)
// sender.send("STOP".to_string()).unwrap();
// Sleep to allow the worker thread to finish processing
thread::sleep(std::time::Duration::from_millis(10));
// Sleep to allow the worker thread to finish processing
thread::sleep(std::time::Duration::from_millis(10));
// Send a new task
let task = instruction::TimedInstruction{
time:integer::Time::ZERO,
instruction:strafesnet_common::physics::Instruction::Idle,
};
worker.send(task).unwrap();
// Send a new task
let task = instruction::TimedInstruction {
time: integer::Time::ZERO,
instruction: strafesnet_common::physics::Instruction::Idle,
};
worker.send(task).unwrap();
//assert_eq!(test_body,worker.grab_clone());
//assert_eq!(test_body,worker.grab_clone());
// wait long enough to see print from final task
thread::sleep(std::time::Duration::from_millis(10));
}
// wait long enough to see print from final task
thread::sleep(std::time::Duration::from_millis(10));
}
}