Unquatification

This commit is contained in:
rhpidfyre 2025-01-09 02:11:29 -05:00
parent 08f419f931
commit 88dcfe40ed
88 changed files with 18282 additions and 14619 deletions

View File

@ -1,4 +1,4 @@
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 {
@ -7,7 +7,7 @@ fn valve_transform([x,y,z]:[f32;3])->integer::Planar64Vec3{
pub fn convert_bsp<AcquireRenderConfigId, AcquireMeshId>(
bsp: &vbsp::Bsp,
mut acquire_render_config_id: AcquireRenderConfigId,
mut acquire_mesh_id:AcquireMeshId
mut acquire_mesh_id: AcquireMeshId,
) -> PartialMap1
where
AcquireRenderConfigId: FnMut(Option<&str>) -> model::RenderConfigId,
@ -16,11 +16,14 @@ where
//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);
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|{
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
@ -32,22 +35,27 @@ where
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))
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(),
*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();
})
.collect();
//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|{
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();
@ -55,13 +63,20 @@ where
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_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);
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
@ -79,7 +94,8 @@ where
//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 =
glam::vec2(texture_transform_u.dot(pos), texture_transform_v.dot(pos));
let tex_idx = spam_tex.len() as u32;
spam_tex.push(tex);
@ -93,12 +109,17 @@ where
vertex_id
});
let polygon_list = std::iter::from_fn(move || {
match (polygon_iter.next(),polygon_iter.next(),polygon_iter.next()){
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();
})
.collect();
if face.is_visible() {
//TODO: deduplicate graphics groups by render id
graphics_groups.push(model::IndexedGraphicsGroup {
@ -108,7 +129,8 @@ where
}
physics_group.groups.push(polygon_group_id);
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
}).collect();
})
.collect();
model::Mesh {
unique_pos: spam_pos,
unique_tex: spam_tex,
@ -119,7 +141,8 @@ where
graphics_groups,
physics_groups: vec![physics_group],
}
}).collect();
})
.collect();
let world_models:Vec<model::Model>=
//one instance of the main world mesh
@ -220,33 +243,58 @@ impl PartialMap2{
//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();
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|
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()
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)|{
.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.texture = render_config
.texture
.and_then(|texture_id| texture_id_map.get(&texture_id).copied());
render_config
}).collect();
})
.collect();
map::CompleteMap {
modes: self.modes,
attributes: self.attributes,
@ -289,10 +337,15 @@ where
}
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_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 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())
@ -309,17 +362,24 @@ where
});
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|
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()),
(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(),
))
}).collect();
})
.collect();
Ok(model::Mesh {
unique_pos: spam_pos,
unique_normal: spam_normal,

View File

@ -21,13 +21,15 @@ pub fn read<R:std::io::Read>(mut input:R)->Result<Bsp,ReadError>{
//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
acquire_mesh_id: AcquireMeshId,
) -> bsp::PartialMap1
where
AcquireRenderConfigId: FnMut(Option<&str>) -> strafesnet_common::model::RenderConfigId,

View File

@ -8,7 +8,10 @@ pub struct Aabb{
impl Default for Aabb {
fn default() -> Self {
Self{min:vec3::MAX,max:vec3::MIN}
Self {
min: vec3::MAX,
max: vec3::MIN,
}
}
}

View File

@ -41,7 +41,8 @@ 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{
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,
@ -49,18 +50,24 @@ impl<T> BvhNode<T>{
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{
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>{
}
}
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),
@ -68,15 +75,16 @@ impl<T> BvhNode<T>{
aabb: self.aabb,
},
RecursiveContent::Branch(children) => {
let branch:Vec<BvhWeightNode<W,T>>=children.into_iter().map(|child|
child.weigh_contents(f)
).collect();
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,
}
},
}
}
}
}
@ -94,9 +102,11 @@ impl <W,T> BvhWeightNode<W,T>{
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{
RecursiveContent::Branch(children) => {
for child in children {
child.into_visitor(f)
},
}
}
}
}
}
@ -109,13 +119,16 @@ 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|{
let nodes = boxen
.into_iter()
.map(|b| {
aabb.join(&b.1);
BvhNode {
content: RecursiveContent::Leaf(b.0),
aabb: b.1,
}
}).collect();
})
.collect();
BvhNode {
content: RecursiveContent::Branch(nodes),
aabb,
@ -146,9 +159,24 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
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};
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..] {
@ -165,7 +193,8 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
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){
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();
@ -181,11 +210,14 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
} else {
BvhNode {
content: RecursiveContent::Branch(
list_list.into_iter().map(|b|{
list_list
.into_iter()
.map(|b| {
let node = generate_bvh_node(b, false);
aabb.join(&node.aabb);
node
}).collect()
})
.collect(),
),
aabb,
}

View File

@ -1,10 +1,10 @@
use crate::model;
use crate::integer::{AbsoluteTime, Planar64, Planar64Vec3};
use crate::model;
//you have this effect while in contact
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ContactingLadder {
pub sticky:bool
pub sticky: bool,
}
#[derive(Clone, Hash, Eq, PartialEq)]
pub enum ContactingBehaviour {
@ -24,13 +24,16 @@ pub struct IntersectingWater{
//All models can be given these attributes
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Accelerator {
pub acceleration:Planar64Vec3
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
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
}
@ -43,7 +46,7 @@ impl Booster{
//let d=direction.dot(velocity);
//TODO: think about negative
//velocity+direction.with_length((d*d+energy).sqrt()-d)
},
}
Booster::AirTime(_) => todo!(),
Booster::Height(_) => todo!(),
}
@ -59,13 +62,18 @@ 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
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
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
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,
@ -77,9 +85,19 @@ impl SetTrajectory{
match self {
SetTrajectory::AirTime(_)
| SetTrajectory::Height(_)
|SetTrajectory::DotVelocity{direction:_,dot:_}=>false,
SetTrajectory::TargetPointTime{target_point:_,time:_}
|SetTrajectory::TargetPointSpeed{target_point:_,speed:_,trajectory_choice:_}
| SetTrajectory::DotVelocity {
direction: _,
dot: _,
} => false,
SetTrajectory::TargetPointTime {
target_point: _,
time: _,
}
| SetTrajectory::TargetPointSpeed {
target_point: _,
speed: _,
trajectory_choice: _,
}
| SetTrajectory::Velocity(_) => true,
}
}

View File

@ -1,7 +1,7 @@
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 {
@ -12,7 +12,12 @@ pub struct StageElement{
}
impl StageElement {
#[inline]
pub const fn new(stage_id:StageId,force:bool,behaviour:StageElementBehaviour,jump_limit:Option<u8>)->Self{
pub const fn new(
stage_id: StageId,
force: bool,
behaviour: StageElementBehaviour,
jump_limit: Option<u8>,
) -> Self {
Self {
stage_id,
force,
@ -116,12 +121,23 @@ impl Stage{
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
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)
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 {
@ -137,7 +153,8 @@ pub struct StageUpdate{
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);
self.unordered_checkpoints
.extend(update.unordered_checkpoints);
}
}
@ -187,7 +204,9 @@ impl Mode{
elements: HashMap::new(),
}
}
pub fn into_inner(self)->(
pub fn into_inner(
self,
) -> (
gameplay_style::StyleModifiers,
ModelId,
HashMap<ModelId, Zone>,
@ -231,27 +250,36 @@ impl Mode{
//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{
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{
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{
self.elements.insert(
model,
StageElement {
stage_id: StageId(stage_id as u32),
force: false,
behaviour: StageElementBehaviour::Checkpoint,
jump_limit: None,
});
},
);
}
}
}
@ -304,9 +332,7 @@ pub struct Modes{
}
impl Modes {
pub const fn new(modes: Vec<Mode>) -> Self {
Self{
modes,
}
Self { modes }
}
pub fn into_inner(self) -> Vec<Mode> {
self.modes

View File

@ -1,7 +1,7 @@
const VALVE_SCALE: Planar64 = Planar64::raw(1 << 28); // 1/16
use crate::integer::{int,vec3::int as int3,AbsoluteTime,Ratio64,Planar64,Planar64Vec3};
use crate::controls_bitflag::Controls;
use crate::integer::{int, vec3::int as int3, AbsoluteTime, Planar64, Planar64Vec3, Ratio64};
use crate::physics::Time as PhysicsTime;
#[derive(Clone, Debug)]
@ -75,16 +75,21 @@ impl JumpImpulse{
let v_g = gravity.dot(velocity);
//do it backwards
let radicand = v_g * v_g + (g * height * 2).fix_4();
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1()
},
&JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(),
velocity
- (*gravity * (radicand.sqrt().fix_2() + v_g) / gg)
.divide()
.fix_1()
}
&JumpImpulse::Linear(jump_speed) => {
velocity + (jump_dir * jump_speed / jump_dir.length()).divide().fix_1()
}
&JumpImpulse::Energy(energy) => {
//calculate energy
//let e=gravity.dot(velocity);
//add
//you get the idea
todo!()
},
}
}
}
//TODO: remove this and implement JumpCalculation properly
@ -134,7 +139,7 @@ impl JumpSettings{
//activate booster normally, jump does nothing
boost_vel
}
},
}
(true, _) => {
//the source calculation (?)
let boost_vel = match booster {
@ -167,7 +172,7 @@ impl JumpSettings{
//activate booster normally, jump does nothing
boost_vel
}
},
}
//the strafe client calculation
(false, _) => {
let boost_vel = match booster {
@ -175,7 +180,7 @@ impl JumpSettings{
None => rel_velocity,
};
boost_vel + jump_dir.with_length(jump_speed).divide().fix_1()
},
}
}
}
}
@ -225,7 +230,9 @@ impl ControlsActivation{
//Half-Sideways
pub const fn half_sideways() -> Self {
Self {
controls_mask:Controls::MoveForward.union(Controls::MoveLeft).union(Controls::MoveRight),
controls_mask: Controls::MoveForward
.union(Controls::MoveLeft)
.union(Controls::MoveRight),
controls_intersects: Controls::MoveLeft.union(Controls::MoveRight),
controls_contains: Controls::MoveForward,
}
@ -233,7 +240,10 @@ impl ControlsActivation{
//Surf Half-Sideways
pub const fn surf_half_sideways() -> Self {
Self {
controls_mask:Controls::MoveForward.union(Controls::MoveBackward).union(Controls::MoveLeft).union(Controls::MoveRight),
controls_mask: Controls::MoveForward
.union(Controls::MoveBackward)
.union(Controls::MoveLeft)
.union(Controls::MoveRight),
controls_intersects: Controls::MoveForward.union(Controls::MoveBackward),
controls_contains: Controls::empty(),
}
@ -265,16 +275,30 @@ pub struct StrafeSettings{
pub tick_rate: Ratio64,
}
impl StrafeSettings {
pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{
pub fn tick_velocity(
&self,
velocity: Planar64Vec3,
control_dir: Planar64Vec3,
) -> Option<Planar64Vec3> {
let d = velocity.dot(control_dir);
let mv = self.mv.fix_2();
match d < mv {
true=>Some(velocity+(control_dir*self.air_accel_limit.map_or(mv-d,|limit|limit.fix_2().min(mv-d))).fix_1()),
true => Some(
velocity
+ (control_dir
* self
.air_accel_limit
.map_or(mv - d, |limit| limit.fix_2().min(mv - d)))
.fix_1(),
),
false => None,
}
}
pub fn next_tick(&self, time: PhysicsTime) -> PhysicsTime {
PhysicsTime::from_nanos(self.tick_rate.rhs_div_int(self.tick_rate.mul_int(time.nanos())+1))
PhysicsTime::from_nanos(
self.tick_rate
.rhs_div_int(self.tick_rate.mul_int(time.nanos()) + 1),
)
}
pub const fn activates(&self, controls: Controls) -> bool {
self.enable.activates(controls)
@ -318,7 +342,11 @@ impl WalkSettings{
};
self.accelerate.accel.min((-gravity.y * friction).fix_1())
}
pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
pub fn get_walk_target_velocity(
&self,
control_dir: Planar64Vec3,
normal: Planar64Vec3,
) -> Planar64Vec3 {
if control_dir == crate::integer::vec3::ZERO {
return control_dir;
}
@ -332,7 +360,9 @@ impl WalkSettings{
if cr == crate::integer::vec3::ZERO_2 {
crate::integer::vec3::ZERO
} else {
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().fix_1()
(cr.cross(normal) * self.accelerate.topspeed / ((nn * (nnmm - dd)).sqrt()))
.divide()
.fix_1()
}
} else {
crate::integer::vec3::ZERO
@ -359,7 +389,11 @@ impl LadderSettings{
//TODO: fallible ladder accel
self.accelerate.accel
}
pub fn get_ladder_target_velocity(&self,mut control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
pub fn get_ladder_target_velocity(
&self,
mut control_dir: Planar64Vec3,
normal: Planar64Vec3,
) -> Planar64Vec3 {
if control_dir == crate::integer::vec3::ZERO {
return control_dir;
}
@ -385,7 +419,9 @@ impl LadderSettings{
if cr == crate::integer::vec3::ZERO_2 {
crate::integer::vec3::ZERO
} else {
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().fix_1()
(cr.cross(normal) * self.accelerate.topspeed / ((nn * (nnmm - dd)).sqrt()))
.divide()
.fix_1()
}
} else {
crate::integer::vec3::ZERO
@ -462,9 +498,7 @@ impl StyleModifiers{
},
dot: (int(1) / 2).sqrt(),
}),
swim:Some(PropulsionSettings{
magnitude:int(12),
}),
swim: Some(PropulsionSettings { magnitude: int(12) }),
hitbox: Hitbox::roblox(),
camera_offset: int3(0, 2, 0), //4.5-2.5=2
}
@ -504,9 +538,7 @@ impl StyleModifiers{
},
dot: (int(1) / 2).sqrt(),
}),
swim:Some(PropulsionSettings{
magnitude:int(12),
}),
swim: Some(PropulsionSettings { magnitude: int(12) }),
hitbox: Hitbox::roblox(),
camera_offset: int3(0, 2, 0), //4.5-2.5=2
}

View File

@ -9,12 +9,18 @@ pub struct TimedInstruction<I,T>{
pub trait InstructionEmitter {
type Instruction;
type TimeInner;
fn next_instruction(&self,time_limit:Time<Self::TimeInner>)->Option<TimedInstruction<Self::Instruction,Self::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>);
fn process_instruction(
&mut self,
instruction: TimedInstruction<Self::Instruction, Self::TimeInner>,
);
}
//PROPER PRIVATE FIELDS!!!
@ -23,12 +29,13 @@ pub struct InstructionCollector<I,T>{
instruction: Option<I>,
}
impl<I, T> InstructionCollector<I, T>
where Time<T>:Copy+PartialOrd,
where
Time<T>: Copy + PartialOrd,
{
pub const fn new(time: Time<T>) -> Self {
Self {
time,
instruction:None
instruction: None,
}
}
#[inline]
@ -42,7 +49,7 @@ impl<I,T> InstructionCollector<I,T>
self.time = unwrap_instruction.time;
self.instruction = Some(unwrap_instruction.instruction);
}
},
}
None => (),
}
}
@ -51,7 +58,7 @@ impl<I,T> InstructionCollector<I,T>
match self.instruction {
Some(instruction) => Some(TimedInstruction {
time: self.time,
instruction
instruction,
}),
None => None,
}

View File

@ -1,5 +1,5 @@
pub use fixed_wide::fixed::{Fixed,Fix};
pub use ratio_ops::ratio::{Ratio,Divide};
pub use fixed_wide::fixed::{Fix, Fixed};
pub use ratio_ops::ratio::{Divide, Ratio};
//integer units
@ -70,13 +70,23 @@ impl<T,Num,Den,N1,T1> From<Ratio<Num,Den>> for Time<T>
{
#[inline]
fn from(value: Ratio<Num, Den>) -> Self {
Self::raw((value*Planar64::raw(1_000_000_000)).divide().fix().to_raw())
Self::raw(
(value * Planar64::raw(1_000_000_000))
.divide()
.fix()
.to_raw(),
)
}
}
impl<T> std::fmt::Display for Time<T> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0)
write!(
f,
"{}s+{:09}ns",
self.0 / Self::ONE_SECOND.0,
self.0 % Self::ONE_SECOND.0
)
}
}
impl<T> std::default::Default for Time<T> {
@ -122,7 +132,10 @@ impl<T> std::ops::Mul for Time<T>{
type Output = Ratio<fixed_wide::fixed::Fixed<2, 64>, fixed_wide::fixed::Fixed<2, 64>>;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Ratio::new(Fixed::raw(self.0)*Fixed::raw(rhs.0),Fixed::raw_digit(1_000_000_000i64.pow(2)))
Ratio::new(
Fixed::raw(self.0) * Fixed::raw(rhs.0),
Fixed::raw_digit(1_000_000_000i64.pow(2)),
)
}
}
impl<T> std::ops::Div<i64> for Time<T> {
@ -162,13 +175,25 @@ mod test_time{
#[test]
fn time_squared() {
let a = Time::from_secs(2);
assert_eq!(a*a,Ratio::new(Fixed::<2,64>::raw_digit(1_000_000_000i64.pow(2))*4,Fixed::<2,64>::raw_digit(1_000_000_000i64.pow(2))));
assert_eq!(
a * a,
Ratio::new(
Fixed::<2, 64>::raw_digit(1_000_000_000i64.pow(2)) * 4,
Fixed::<2, 64>::raw_digit(1_000_000_000i64.pow(2))
)
);
}
#[test]
fn time_times_planar64() {
let a = Time::from_secs(2);
let b = Planar64::from(2);
assert_eq!(b*a,Ratio::new(Fixed::<2,64>::raw_digit(1_000_000_000*(1<<32))<<2,Fixed::<1,32>::raw_digit(1_000_000_000)));
assert_eq!(
b * a,
Ratio::new(
Fixed::<2, 64>::raw_digit(1_000_000_000 * (1 << 32)) << 2,
Fixed::<1, 32>::raw_digit(1_000_000_000)
)
);
}
}
@ -176,7 +201,7 @@ mod test_time{
const fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
(a, b) = (b, a.rem_euclid(b));
};
}
a
}
#[derive(Clone, Copy, Debug, Hash)]
@ -193,7 +218,10 @@ impl Ratio64{
None
} else {
let d = gcd(num.unsigned_abs(), den);
Some(Self{num:num/(d as i64),den:den/d})
Some(Self {
num: num / (d as i64),
den: den / d,
})
}
}
#[inline]
@ -305,8 +333,9 @@ impl TryFrom<f32> for Ratio64{
std::num::FpCategory::Nan => Err(Self::Error::Nan),
std::num::FpCategory::Infinite => Err(Self::Error::Infinite),
std::num::FpCategory::Zero => Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f32(value)),
std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => {
ratio64_from_mes(integer_decode_f32(value))
}
}
}
}
@ -318,8 +347,9 @@ impl TryFrom<f64> for Ratio64{
std::num::FpCategory::Nan => Err(Self::Error::Nan),
std::num::FpCategory::Infinite => Err(Self::Error::Infinite),
std::num::FpCategory::Zero => Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f64(value)),
std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => {
ratio64_from_mes(integer_decode_f64(value))
}
}
}
}
@ -361,17 +391,17 @@ pub struct Ratio64Vec2{
pub y: Ratio64,
}
impl Ratio64Vec2 {
pub const ONE:Self=Self{x:Ratio64::ONE,y:Ratio64::ONE};
pub const ONE: Self = Self {
x: Ratio64::ONE,
y: Ratio64::ONE,
};
#[inline]
pub const fn new(x: Ratio64, y: Ratio64) -> Self {
Self { x, y }
}
#[inline]
pub const fn mul_int(&self, rhs: glam::I64Vec2) -> glam::I64Vec2 {
glam::i64vec2(
self.x.mul_int(rhs.x),
self.y.mul_int(rhs.y),
)
glam::i64vec2(self.x.mul_int(rhs.x), self.y.mul_int(rhs.y))
}
}
impl std::ops::Mul<i64> for Ratio64Vec2 {
@ -397,7 +427,9 @@ impl Angle32{
pub const fn wrap_from_i64(theta: i64) -> Self {
//take lower bits
//note: this was checked on compiler explorer and compiles to 1 instruction!
Self(i32::from_ne_bytes(((theta&((1<<32)-1)) as u32).to_ne_bytes()))
Self(i32::from_ne_bytes(
((theta & ((1 << 32) - 1)) as u32).to_ne_bytes(),
))
}
#[inline]
pub fn clamp_from_i64(theta: i64) -> Self {
@ -414,18 +446,15 @@ impl Angle32{
#[inline]
pub fn clamp(&self, theta_min: Self, theta_max: Self) -> Self {
//((max-min as u32)/2 as i32)+min
let midpoint=((
(theta_max.0 as u32)
.wrapping_sub(theta_min.0 as u32)
/2
) as i32)//(u32::MAX/2) as i32 ALWAYS works
let midpoint = (((theta_max.0 as u32).wrapping_sub(theta_min.0 as u32) / 2) as i32) //(u32::MAX/2) as i32 ALWAYS works
.wrapping_add(theta_min.0);
//(theta-mid).clamp(max-mid,min-mid)+mid
Self(
self.0.wrapping_sub(midpoint)
self.0
.wrapping_sub(midpoint)
.max(theta_min.0.wrapping_sub(midpoint))
.min(theta_max.0.wrapping_sub(midpoint))
.wrapping_add(midpoint)
.wrapping_add(midpoint),
)
}
#[inline]
@ -451,7 +480,10 @@ impl Angle32{
(Planar64::raw(x),Planar64::raw(y))
*/
let (s, c) = (self.0 as f64 * Self::ANGLE32_TO_FLOAT64_RADIANS).sin_cos();
(Planar64::raw((c*((1u64<<32) as f64)) as i64),Planar64::raw((s*((1u64<<32) as f64)) as i64))
(
Planar64::raw((c * ((1u64 << 32) as f64)) as i64),
Planar64::raw((s * ((1u64 << 32) as f64)) as i64),
)
}
}
impl Into<f32> for Angle32 {
@ -508,8 +540,14 @@ fn angle_sin_cos(){
println!("cordic s={} c={}", (s / h).divide(), (c / h).divide());
let (fs, fc) = f.sin_cos();
println!("float s={} c={}", fs, fc);
assert!(close_enough((c/h).divide().fix_1(),Planar64::raw((fc*((1u64<<32) as f64)) as i64)));
assert!(close_enough((s/h).divide().fix_1(),Planar64::raw((fs*((1u64<<32) as f64)) as i64)));
assert!(close_enough(
(c / h).divide().fix_1(),
Planar64::raw((fc * ((1u64 << 32) as f64)) as i64)
));
assert!(close_enough(
(s / h).divide().fix_1(),
Planar64::raw((fs * ((1u64 << 32) as f64)) as i64)
));
}
test_angle(1.0);
test_angle(std::f64::consts::PI / 4.0);
@ -550,18 +588,27 @@ pub mod vec3{
pub const MIN: Planar64Vec3 = Planar64Vec3::new([Planar64::MIN; 3]);
pub const MAX: Planar64Vec3 = Planar64Vec3::new([Planar64::MAX; 3]);
pub const ZERO: Planar64Vec3 = Planar64Vec3::new([Planar64::ZERO; 3]);
pub const ZERO_2:linear_ops::types::Vector3<Fixed::<2,64>>=linear_ops::types::Vector3::new([Fixed::<2,64>::ZERO;3]);
pub const ZERO_2: linear_ops::types::Vector3<Fixed<2, 64>> =
linear_ops::types::Vector3::new([Fixed::<2, 64>::ZERO; 3]);
pub const X: Planar64Vec3 = Planar64Vec3::new([Planar64::ONE, Planar64::ZERO, Planar64::ZERO]);
pub const Y: Planar64Vec3 = Planar64Vec3::new([Planar64::ZERO, Planar64::ONE, Planar64::ZERO]);
pub const Z: Planar64Vec3 = Planar64Vec3::new([Planar64::ZERO, Planar64::ZERO, Planar64::ONE]);
pub const ONE: Planar64Vec3 = Planar64Vec3::new([Planar64::ONE, Planar64::ONE, Planar64::ONE]);
pub const NEG_X:Planar64Vec3=Planar64Vec3::new([Planar64::NEG_ONE,Planar64::ZERO,Planar64::ZERO]);
pub const NEG_Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::NEG_ONE,Planar64::ZERO]);
pub const NEG_Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::NEG_ONE]);
pub const NEG_ONE:Planar64Vec3=Planar64Vec3::new([Planar64::NEG_ONE,Planar64::NEG_ONE,Planar64::NEG_ONE]);
pub const NEG_X: Planar64Vec3 =
Planar64Vec3::new([Planar64::NEG_ONE, Planar64::ZERO, Planar64::ZERO]);
pub const NEG_Y: Planar64Vec3 =
Planar64Vec3::new([Planar64::ZERO, Planar64::NEG_ONE, Planar64::ZERO]);
pub const NEG_Z: Planar64Vec3 =
Planar64Vec3::new([Planar64::ZERO, Planar64::ZERO, Planar64::NEG_ONE]);
pub const NEG_ONE: Planar64Vec3 =
Planar64Vec3::new([Planar64::NEG_ONE, Planar64::NEG_ONE, Planar64::NEG_ONE]);
#[inline]
pub const fn int(x: i32, y: i32, z: i32) -> Planar64Vec3 {
Planar64Vec3::new([Planar64::raw((x as i64)<<32),Planar64::raw((y as i64)<<32),Planar64::raw((z as i64)<<32)])
Planar64Vec3::new([
Planar64::raw((x as i64) << 32),
Planar64::raw((y as i64) << 32),
Planar64::raw((z as i64) << 32),
])
}
#[inline]
pub fn raw_array(array: [i64; 3]) -> Planar64Vec3 {
@ -572,7 +619,9 @@ pub mod vec3{
Planar64Vec3::new([Planar64::raw(x), Planar64::raw(y), Planar64::raw(z)])
}
#[inline]
pub fn try_from_f32_array([x,y,z]:[f32;3])->Result<Planar64Vec3,Planar64TryFromFloatError>{
pub fn try_from_f32_array(
[x, y, z]: [f32; 3],
) -> Result<Planar64Vec3, Planar64TryFromFloatError> {
Ok(Planar64Vec3::new([
try_from_f32(x)?,
try_from_f32(y)?,
@ -632,7 +681,9 @@ pub mod mat3{
])
}
#[inline]
pub fn try_from_f32_array_2d([x_axis,y_axis,z_axis]:[[f32;3];3])->Result<Planar64Mat3,Planar64TryFromFloatError>{
pub fn try_from_f32_array_2d(
[x_axis, y_axis, z_axis]: [[f32; 3]; 3],
) -> Result<Planar64Mat3, Planar64TryFromFloatError> {
Ok(Planar64Mat3::new([
vec3::try_from_f32_array(x_axis)?.to_array(),
vec3::try_from_f32_array(y_axis)?.to_array(),
@ -649,7 +700,10 @@ pub struct Planar64Affine3{
impl Planar64Affine3 {
#[inline]
pub const fn new(matrix3: Planar64Mat3, translation: Planar64Vec3) -> Self {
Self{matrix3,translation}
Self {
matrix3,
translation,
}
}
#[inline]
pub fn transform_point3(&self, point: Planar64Vec3) -> vec3::Vector3<Fixed<2, 64>> {
@ -659,13 +713,28 @@ impl Planar64Affine3{
impl Into<glam::Mat4> for Planar64Affine3 {
#[inline]
fn into(self) -> glam::Mat4 {
let matrix3=self.matrix3.to_array().map(|row|row.map(Into::<f32>::into));
let matrix3 = self
.matrix3
.to_array()
.map(|row| row.map(Into::<f32>::into));
let translation = self.translation.to_array().map(Into::<f32>::into);
glam::Mat4::from_cols_array(&[
matrix3[0][0],matrix3[0][1],matrix3[0][2],0.0,
matrix3[1][0],matrix3[1][1],matrix3[1][2],0.0,
matrix3[2][0],matrix3[2][1],matrix3[2][2],0.0,
translation[0],translation[1],translation[2],1.0
matrix3[0][0],
matrix3[0][1],
matrix3[0][2],
0.0,
matrix3[1][0],
matrix3[1][1],
matrix3[1][2],
0.0,
matrix3[2][0],
matrix3[2][1],
matrix3[2][2],
0.0,
translation[0],
translation[1],
translation[2],
1.0,
])
}
}

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,6 +1,6 @@
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 {

View File

@ -1,5 +1,5 @@
use crate::integer::{Planar64Vec3,Planar64Affine3};
use crate::gameplay_attributes;
use crate::integer::{Planar64Affine3, Planar64Vec3};
pub type TextureCoordinate = glam::Vec2;
pub type Color4 = glam::Vec4;
@ -44,7 +44,12 @@ impl PolygonIter for PolygonList{
}
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())
Self(
self.0
.into_iter()
.map(|ivl| ivl.into_iter().map(&f).collect())
.collect(),
)
}
}
// pub struct TriangleStrip(IndexedVertexList);

View File

@ -14,7 +14,8 @@ impl<T> Default for MouseState<T>{
}
}
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();

View File

@ -4,7 +4,10 @@ pub type Time=crate::integer::Time<TimeInner>;
#[derive(Clone, Debug)]
pub enum Instruction {
ReplaceMouse(crate::mouse::MouseState<TimeInner>,crate::mouse::MouseState<TimeInner>),
ReplaceMouse(
crate::mouse::MouseState<TimeInner>,
crate::mouse::MouseState<TimeInner>,
),
SetNextMouse(crate::mouse::MouseState<TimeInner>),
SetMoveRight(bool),
SetMoveUp(bool),
@ -21,7 +24,10 @@ pub enum Instruction{
Restart,
/// Spawn: Teleport to a specific mode's spawn
/// Sets current mode & spawn
Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId),
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

View File

@ -1,6 +1,6 @@
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 {}
@ -55,8 +55,12 @@ 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>},
Started {
timer: TimerFixed<Realtime<PhysicsTimeInner, TimeInner>, Unpaused>,
},
Finished {
timer: TimerFixed<Realtime<PhysicsTimeInner, TimeInner>, Paused>,
},
}
#[derive(Clone, Copy, Debug)]
@ -86,7 +90,7 @@ impl Run{
timer: TimerFixed::new(time, Time::ZERO),
};
Ok(())
},
}
RunState::Started { .. } => Err(Error::AlreadyStarted),
RunState::Finished { .. } => Err(Error::AlreadyFinished),
}
@ -100,7 +104,7 @@ impl Run{
timer: timer.into_paused(time),
};
Ok(())
},
}
RunState::Finished { .. } => Err(Error::AlreadyFinished),
}
}

View File

@ -1,4 +1,4 @@
use crate::integer::{Time,Ratio64};
use crate::integer::{Ratio64, Time};
#[derive(Clone, Copy, Debug)]
pub struct Paused;
@ -50,7 +50,8 @@ pub struct Scaled<In,Out>{
_out: core::marker::PhantomData<Out>,
}
impl<In, Out> Scaled<In, Out>
where Time<In>:Copy,
where
Time<In>: Copy,
{
pub const fn new(scale: Ratio64, offset: InnerTime) -> Self {
Self {
@ -105,7 +106,8 @@ impl<In,Out> TimerState for Realtime<In,Out>{
}
}
impl<In, Out> TimerState for Scaled<In, Out>
where Time<In>:Copy,
where
Time<In>: Copy,
{
type In = In;
type Out = Out;
@ -134,7 +136,8 @@ pub struct TimerFixed<T:TimerState,P:PauseState>{
//scaled timer methods are generic across PauseState
impl<P: PauseState, In, Out> TimerFixed<Scaled<In, Out>, P>
where Time<In>:Copy,
where
Time<In>: Copy,
{
pub fn scaled(time: Time<In>, new_time: Time<Out>, scale: Ratio64) -> Self {
let mut timer = Self {
@ -154,7 +157,8 @@ impl<P:PauseState,In,Out> TimerFixed<Scaled<In,Out>,P>
//pause and unpause is generic across TimerState
impl<T: TimerState> TimerFixed<T, Paused>
where Time<T::In>:Copy,
where
Time<T::In>: Copy,
{
pub fn into_unpaused(self, time: Time<T::In>) -> TimerFixed<T, Unpaused> {
let new_time = self.time(time);
@ -167,7 +171,8 @@ impl<T:TimerState> TimerFixed<T,Paused>
}
}
impl<T: TimerState> TimerFixed<T, Unpaused>
where Time<T::In>:Copy,
where
Time<T::In>: Copy,
{
pub fn into_paused(self, time: Time<T::In>) -> TimerFixed<T, Paused> {
let new_time = self.time(time);
@ -295,7 +300,8 @@ impl<T:TimerState> Timer<T>
}
//scaled timer methods are generic across PauseState
impl<In, Out> Timer<Scaled<In, Out>>
where Time<In>:Copy,
where
Time<In>: Copy,
{
pub const fn get_scale(&self) -> Ratio64 {
match self {
@ -327,7 +333,10 @@ mod test{
#[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)));
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));

View File

@ -18,7 +18,7 @@ struct Outer{
enum Update<I, U> {
Insert(I),
Update(U),
Remove
Remove,
}
struct InnerUpdate {

View File

@ -1,4 +1,4 @@
use bnum::{BInt,cast::As};
use bnum::{cast::As, BInt};
#[derive(Clone, Copy, Debug, Default, Hash)]
/// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled)
@ -23,10 +23,8 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
}
impl<const N: usize, const F: usize> Fixed<N, F> {
#[inline]
pub const fn from_bits(bits:BInt::<N>)->Self{
Self{
bits,
}
pub const fn from_bits(bits: BInt<N>) -> Self {
Self { bits }
}
#[inline]
pub const fn to_bits(self) -> BInt<N> {
@ -82,10 +80,7 @@ macro_rules! impl_from {
)*
};
}
impl_from!(
u8,u16,u32,u64,u128,usize,
i8,i16,i32,i64,i128,isize
);
impl_from!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
impl<const N: usize, const F: usize> PartialEq for Fixed<N, F> {
#[inline]
@ -96,7 +91,7 @@ impl<const N:usize,const F:usize> PartialEq for Fixed<N,F>{
impl<const N: usize, const F: usize, T> PartialEq<T> for Fixed<N, F>
where
T: Copy,
BInt::<N>:From<T>,
BInt<N>: From<T>,
{
#[inline]
fn eq(&self, &other: &T) -> bool {
@ -114,7 +109,7 @@ impl<const N:usize,const F:usize> PartialOrd for Fixed<N,F>{
impl<const N: usize, const F: usize, T> PartialOrd<T> for Fixed<N, F>
where
T: Copy,
BInt::<N>:From<T>,
BInt<N>: From<T>,
{
#[inline]
fn partial_cmp(&self, &other: &T) -> Option<std::cmp::Ordering> {
@ -161,7 +156,11 @@ macro_rules! impl_into_float {
const DIGIT_SHIFT: u32 = 6; //Log2[64]
// SBBB BBBB
// 1001 1110 0000 0000
let sign=if self.bits.is_negative(){(1 as $unsigned)<<(<$unsigned>::BITS-1)}else{0};
let sign = if self.bits.is_negative() {
(1 as $unsigned) << (<$unsigned>::BITS - 1)
} else {
0
};
let unsigned = self.bits.unsigned_abs();
let most_significant_bit = unsigned.bits();
let exp = if unsigned.is_zero() {
@ -192,7 +191,7 @@ macro_rules! impl_into_float {
<$output>::from_bits(bits)
}
}
}
};
}
impl_into_float!(f32, u32, 8, 24);
impl_into_float!(f64, u64, 11, 53);
@ -251,9 +250,7 @@ macro_rules! impl_from_float {
std::num::FpCategory::Nan => Err(FixedFromFloatError::Nan),
std::num::FpCategory::Infinite => Err(FixedFromFloatError::Infinite),
std::num::FpCategory::Zero => Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal
=>{
std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => {
let (m, e, s) = $decode(value);
let mut digits = [0u64; N];
let most_significant_bit = e as i32 + $mantissa_bits as i32 + F as i32;
@ -261,15 +258,19 @@ macro_rules! impl_from_float {
return Err(FixedFromFloatError::Underflow);
}
let digit_index = most_significant_bit >> DIGIT_SHIFT;
let digit=digits.get_mut(digit_index as usize).ok_or(FixedFromFloatError::Overflow)?;
let digit = digits
.get_mut(digit_index as usize)
.ok_or(FixedFromFloatError::Overflow)?;
let take_bits = most_significant_bit - (digit_index << DIGIT_SHIFT);
let rest_of_mantissa = -($mantissa_bits as i32 - (take_bits as i32));
*digit = signed_shift(m, rest_of_mantissa);
if rest_of_mantissa < 0 && digit_index != 0 {
//we don't care if some float bits are partially truncated
if let Some(digit) = digits.get_mut((digit_index - 1) as usize) {
let take_bits=most_significant_bit-((digit_index-1)<<DIGIT_SHIFT);
let rest_of_mantissa=-($mantissa_bits as i32-(take_bits as i32));
let take_bits =
most_significant_bit - ((digit_index - 1) << DIGIT_SHIFT);
let rest_of_mantissa =
-($mantissa_bits as i32 - (take_bits as i32));
*digit = signed_shift(m, rest_of_mantissa);
}
}
@ -279,11 +280,11 @@ macro_rules! impl_from_float {
} else {
Self::from_bits(bits)
})
},
}
}
}
}
};
}
impl_from_float!(integer_decode_f32, f32, 24);
impl_from_float!(integer_decode_f64, f64, 53);
@ -313,7 +314,7 @@ macro_rules! impl_additive_operator {
}
impl<const N: usize, const F: usize, U> core::ops::$trait<U> for $struct<N, F>
where
BInt::<N>:From<U>,
BInt<N>: From<U>,
{
type Output = $output;
#[inline]
@ -333,7 +334,7 @@ macro_rules! impl_additive_assign_operator {
}
impl<const N: usize, const F: usize, U> core::ops::$trait<U> for $struct<N, F>
where
BInt::<N>:From<U>,
BInt<N>: From<U>,
{
#[inline]
fn $method(&mut self, other: U) {
@ -406,7 +407,10 @@ macro_rules! impl_multiply_operator_not_const_generic {
}
}
#[cfg(not(feature = "wide-mul"))]
impl_multiplicative_operator_not_const_generic!(($struct, $trait, $method, $output ), $width);
impl_multiplicative_operator_not_const_generic!(
($struct, $trait, $method, $output),
$width
);
#[cfg(feature = "deferred-division")]
impl ratio_ops::ratio::Divide<i64> for Fixed<$width, { $width * 32 }> {
type Output = Self;
@ -415,7 +419,7 @@ macro_rules! impl_multiply_operator_not_const_generic {
Self::from_bits(self.bits.div_euclid(BInt::from(other)))
}
}
}
};
}
macro_rules! impl_divide_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => {
@ -431,7 +435,10 @@ macro_rules! impl_divide_operator_not_const_generic {
}
}
#[cfg(all(not(feature = "wide-mul"), not(feature = "deferred-division")))]
impl_multiplicative_operator_not_const_generic!(($struct, $trait, $method, $output ), $width);
impl_multiplicative_operator_not_const_generic!(
($struct, $trait, $method, $output),
$width
);
#[cfg(all(not(feature = "wide-mul"), feature = "deferred-division"))]
impl<const F: usize> ratio_ops::ratio::Divide for $struct<$width, F> {
type Output = $output;
@ -449,7 +456,7 @@ macro_rules! impl_multiplicative_operator {
( $struct: ident, $trait: ident, $method: ident, $inner_method: ident, $output: ty ) => {
impl<const N: usize, const F: usize, U> core::ops::$trait<U> for $struct<N, F>
where
BInt::<N>:From<U>+core::ops::$trait,
BInt<N>: From<U> + core::ops::$trait,
{
type Output = $output;
#[inline]
@ -463,7 +470,7 @@ macro_rules! impl_multiplicative_assign_operator {
( $struct: ident, $trait: ident, $method: ident, $not_assign_method: ident ) => {
impl<const N: usize, const F: usize, U> core::ops::$trait<U> for $struct<N, F>
where
BInt::<N>:From<U>+core::ops::$trait,
BInt<N>: From<U> + core::ops::$trait,
{
#[inline]
fn $method(&mut self, other: U) {
@ -488,19 +495,33 @@ macro_rules! macro_repeated{
macro_rules! macro_16 {
( $macro: ident, $any:tt ) => {
macro_repeated!($macro, $any, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
}
};
}
macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, MulAssign, mul_assign, mul) );
macro_16!( impl_multiply_operator_not_const_generic, (Fixed, Mul, mul, Self) );
macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, DivAssign, div_assign, div) );
macro_16!( impl_divide_operator_not_const_generic, (Fixed, Div, div, Self) );
macro_16!(
impl_multiplicative_assign_operator_not_const_generic,
(Fixed, MulAssign, mul_assign, mul)
);
macro_16!(
impl_multiply_operator_not_const_generic,
(Fixed, Mul, mul, Self)
);
macro_16!(
impl_multiplicative_assign_operator_not_const_generic,
(Fixed, DivAssign, div_assign, div)
);
macro_16!(
impl_divide_operator_not_const_generic,
(Fixed, Div, div, Self)
);
impl_multiplicative_assign_operator!(Fixed, MulAssign, mul_assign, mul);
impl_multiplicative_operator!(Fixed, Mul, mul, mul, Self);
impl_multiplicative_assign_operator!(Fixed, DivAssign, div_assign, div_euclid);
impl_multiplicative_operator!(Fixed, Div, div, div_euclid, Self);
#[cfg(feature = "deferred-division")]
impl<const LHS_N:usize,const LHS_F:usize,const RHS_N:usize,const RHS_F:usize> core::ops::Div<Fixed<RHS_N,RHS_F>> for Fixed<LHS_N,LHS_F>{
impl<const LHS_N: usize, const LHS_F: usize, const RHS_N: usize, const RHS_F: usize>
core::ops::Div<Fixed<RHS_N, RHS_F>> for Fixed<LHS_N, LHS_F>
{
type Output = ratio_ops::ratio::Ratio<Fixed<LHS_N, LHS_F>, Fixed<RHS_N, RHS_F>>;
#[inline]
fn div(self, other: Fixed<RHS_N, RHS_F>) -> Self::Output {
@ -573,7 +594,7 @@ macro_rules! impl_wide_operators{
}
}
}
}
};
}
// WIDE MUL: multiply into a wider type
@ -648,26 +669,133 @@ macro_rules! impl_wide_same_size_not_const_generic{
//const generics sidestepped wahoo
macro_repeated!(
impl_wide_not_const_generic,(),
(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),
(1,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),
(1,3),(2,3), (4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),
(1,4),(2,4),(3,4), (5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),
(1,5),(2,5),(3,5),(4,5), (6,5),(7,5),(8,5),(9,5),(10,5),(11,5),
(1,6),(2,6),(3,6),(4,6),(5,6), (7,6),(8,6),(9,6),(10,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7), (8,7),(9,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8), (9,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),
(1,11),(2,11),(3,11),(4,11),(5,11),
(1,12),(2,12),(3,12),(4,12),
(1,13),(2,13),(3,13),
(1,14),(2,14),
impl_wide_not_const_generic,
(),
(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),
(1, 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),
(1, 3),
(2, 3),
(4, 3),
(5, 3),
(6, 3),
(7, 3),
(8, 3),
(9, 3),
(10, 3),
(11, 3),
(12, 3),
(13, 3),
(1, 4),
(2, 4),
(3, 4),
(5, 4),
(6, 4),
(7, 4),
(8, 4),
(9, 4),
(10, 4),
(11, 4),
(12, 4),
(1, 5),
(2, 5),
(3, 5),
(4, 5),
(6, 5),
(7, 5),
(8, 5),
(9, 5),
(10, 5),
(11, 5),
(1, 6),
(2, 6),
(3, 6),
(4, 6),
(5, 6),
(7, 6),
(8, 6),
(9, 6),
(10, 6),
(1, 7),
(2, 7),
(3, 7),
(4, 7),
(5, 7),
(6, 7),
(8, 7),
(9, 7),
(1, 8),
(2, 8),
(3, 8),
(4, 8),
(5, 8),
(6, 8),
(7, 8),
(9, 8),
(1, 9),
(2, 9),
(3, 9),
(4, 9),
(5, 9),
(6, 9),
(7, 9),
(1, 10),
(2, 10),
(3, 10),
(4, 10),
(5, 10),
(6, 10),
(1, 11),
(2, 11),
(3, 11),
(4, 11),
(5, 11),
(1, 12),
(2, 12),
(3, 12),
(4, 12),
(1, 13),
(2, 13),
(3, 13),
(1, 14),
(2, 14),
(1, 15)
);
macro_repeated!(
impl_wide_same_size_not_const_generic,(),
1,2,3,4,5,6,7,8
impl_wide_same_size_not_const_generic,
(),
1,
2,
3,
4,
5,
6,
7,
8
);
pub trait Fix<Out> {
@ -725,8 +853,7 @@ macro_rules! impl_fix_lhs_eq_rhs_not_const_generic{
(),
($lhs:expr,$rhs:expr)
) => {
impl Fixed<$lhs,{$lhs*32}>
{
impl Fixed<$lhs, { $lhs * 32 }> {
paste::item! {
#[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
@ -741,50 +868,279 @@ macro_rules! impl_fix_lhs_eq_rhs_not_const_generic{
}
}
}
}
};
}
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
macro_repeated!(
impl_fix_rhs_lt_lhs_not_const_generic,(),
(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),(17,1),
(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),
(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),
(5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(13,4),(14,4),(15,4),(16,4),
(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5),
(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6),
(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),
(9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8),
(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),
(11,10),(12,10),(13,10),(14,10),(15,10),(16,10),
(12,11),(13,11),(14,11),(15,11),(16,11),
(13,12),(14,12),(15,12),(16,12),
(14,13),(15,13),(16,13),
(15,14),(16,14),
impl_fix_rhs_lt_lhs_not_const_generic,
(),
(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),
(17, 1),
(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),
(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),
(5, 4),
(6, 4),
(7, 4),
(8, 4),
(9, 4),
(10, 4),
(11, 4),
(12, 4),
(13, 4),
(14, 4),
(15, 4),
(16, 4),
(6, 5),
(7, 5),
(8, 5),
(9, 5),
(10, 5),
(11, 5),
(12, 5),
(13, 5),
(14, 5),
(15, 5),
(16, 5),
(7, 6),
(8, 6),
(9, 6),
(10, 6),
(11, 6),
(12, 6),
(13, 6),
(14, 6),
(15, 6),
(16, 6),
(8, 7),
(9, 7),
(10, 7),
(11, 7),
(12, 7),
(13, 7),
(14, 7),
(15, 7),
(16, 7),
(9, 8),
(10, 8),
(11, 8),
(12, 8),
(13, 8),
(14, 8),
(15, 8),
(16, 8),
(10, 9),
(11, 9),
(12, 9),
(13, 9),
(14, 9),
(15, 9),
(16, 9),
(11, 10),
(12, 10),
(13, 10),
(14, 10),
(15, 10),
(16, 10),
(12, 11),
(13, 11),
(14, 11),
(15, 11),
(16, 11),
(13, 12),
(14, 12),
(15, 12),
(16, 12),
(14, 13),
(15, 13),
(16, 13),
(15, 14),
(16, 14),
(16, 15)
);
macro_repeated!(
impl_fix_lhs_lt_rhs_not_const_generic,(),
impl_fix_lhs_lt_rhs_not_const_generic,
(),
(1, 2),
(1,3),(2,3),
(1,4),(2,4),(3,4),
(1,5),(2,5),(3,5),(4,5),
(1,6),(2,6),(3,6),(4,6),(5,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),
(1,11),(2,11),(3,11),(4,11),(5,11),(6,11),(7,11),(8,11),(9,11),(10,11),
(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(10,12),(11,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),
(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),
(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),
(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)
(1, 3),
(2, 3),
(1, 4),
(2, 4),
(3, 4),
(1, 5),
(2, 5),
(3, 5),
(4, 5),
(1, 6),
(2, 6),
(3, 6),
(4, 6),
(5, 6),
(1, 7),
(2, 7),
(3, 7),
(4, 7),
(5, 7),
(6, 7),
(1, 8),
(2, 8),
(3, 8),
(4, 8),
(5, 8),
(6, 8),
(7, 8),
(1, 9),
(2, 9),
(3, 9),
(4, 9),
(5, 9),
(6, 9),
(7, 9),
(8, 9),
(1, 10),
(2, 10),
(3, 10),
(4, 10),
(5, 10),
(6, 10),
(7, 10),
(8, 10),
(9, 10),
(1, 11),
(2, 11),
(3, 11),
(4, 11),
(5, 11),
(6, 11),
(7, 11),
(8, 11),
(9, 11),
(10, 11),
(1, 12),
(2, 12),
(3, 12),
(4, 12),
(5, 12),
(6, 12),
(7, 12),
(8, 12),
(9, 12),
(10, 12),
(11, 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),
(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),
(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),
(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)
);
macro_repeated!(
impl_fix_lhs_eq_rhs_not_const_generic,(),
(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10),(11,11),(12,12),(13,13),(14,14),(15,15),(16,16)
impl_fix_lhs_eq_rhs_not_const_generic,
(),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8),
(9, 9),
(10, 10),
(11, 11),
(12, 12),
(13, 13),
(14, 14),
(15, 15),
(16, 16)
);
macro_rules! impl_not_const_generic {
@ -836,7 +1192,7 @@ macro_rules! impl_not_const_generic{
}
}
}
}
};
}
impl_not_const_generic!(1, 2);
impl_not_const_generic!(2, 4);

View File

@ -1,5 +1,5 @@
use crate::types::I32F32;
use crate::types::I256F256;
use crate::types::I32F32;
#[test]
fn you_can_add_numbers() {
@ -49,7 +49,9 @@ fn from_f32(){
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();
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);
@ -67,7 +69,9 @@ fn from_f32(){
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 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));
@ -86,7 +90,9 @@ fn from_f64(){
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();
let b: Result<I256F256, _> = (0b101011110101001010101010000000000000000000000000000u64 as f64
* 2.0f64.powi(16))
.try_into();
assert_eq!(b, Ok(a));
}
@ -198,9 +204,15 @@ 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]));
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]));
assert_eq!(
zeroes,
arrayvec::ArrayVec::from_iter([I32F32::NEG_ONE * 3, I32F32::ONE])
);
}
#[test]
#[cfg(all(feature = "zeroes", feature = "deferred-division"))]

View File

@ -6,10 +6,16 @@ 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>{
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::Equal => {
return ArrayVec::from_iter(Self::zeroes1(a0, a1).into_iter())
}
Ordering::Less => false,
};
let radicand = a1 * a1 - a2 * a0 * 4;
@ -20,13 +26,25 @@ macro_rules! impl_zeroes{
}
//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)],
(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(),
}

View File

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

View File

@ -7,15 +7,24 @@ macro_rules! impl_fixed_wide_vector_not_const_generic {
) => {
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{
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
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>,
V: core::ops::Div<
<fixed_wide::fixed::Fixed<$n, { $n * 32 }> as core::ops::Mul>::Output,
>,
{
self * length / self.length()
}
@ -28,7 +37,7 @@ macro_rules! impl_fixed_wide_vector_not_const_generic {
macro_rules! macro_4 {
( $macro: ident, $any:tt ) => {
$crate::macro_repeated!($macro, $any, 1, 2, 3, 4);
}
};
}
#[doc(hidden)]
@ -38,23 +47,264 @@ 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)
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)
);
};
}
@ -66,8 +316,7 @@ macro_rules! impl_fix_not_const_generic{
(),
($lhs:expr,$rhs:expr)
) => {
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>
{
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}>>{
@ -75,5 +324,5 @@ macro_rules! impl_fix_not_const_generic{
}
}
}
}
};
}

View File

@ -12,32 +12,25 @@ macro_rules! impl_matrix {
self.array
}
#[inline]
pub fn from_cols(cols:[Vector<Y,T>;X])->Self
{
Matrix::new(
cols.map(|col|col.array),
)
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
F: Fn(T) -> U,
{
Matrix::new(
self.array.map(|inner|inner.map(&f)),
)
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()
)
)
)
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>
@ -50,16 +43,15 @@ macro_rules! impl_matrix {
{
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: 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()
)
)
.map(|(lhs_iter, &rhs_value)| lhs_iter.next().unwrap() * rhs_value)
.sum()
})
}),
}
}
#[inline]
@ -71,21 +63,18 @@ macro_rules! impl_matrix {
U: Copy,
{
let mut array_of_iterators = self.array.map(|axis| axis.into_iter());
Vector::new(
core::array::from_fn(|_|
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()
)
)
.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
T: Copy,
{
#[inline(always)]
pub const fn from_value(value: T) -> Self {
@ -96,13 +85,15 @@ macro_rules! impl_matrix {
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()))
)
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>{
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] {
@ -117,7 +108,8 @@ macro_rules! impl_matrix {
}
}
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>
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,
@ -129,7 +121,8 @@ macro_rules! impl_matrix {
self.dot(rhs)
}
}
impl<const X:usize,const Y:usize,T,U,V> core::ops::Mul<Vector<X,U>> for Matrix<X,Y,T>
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,
@ -143,14 +136,21 @@ macro_rules! impl_matrix {
}
#[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>{
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 {
@ -164,7 +164,7 @@ macro_rules! impl_matrix_deferred_division {
ratio_ops::ratio::Ratio::new(self, rhs)
}
}
}
};
}
#[doc(hidden)]
@ -175,22 +175,20 @@ macro_rules! impl_matrix_extend {
#[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()),
)
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()));
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)]
@ -213,7 +211,7 @@ macro_rules! impl_matrix_named_fields_shape {
unsafe { core::mem::transmute(&mut self.array) }
}
}
}
};
}
#[doc(hidden)]
@ -262,11 +260,23 @@ macro_rules! impl_matrix_3x3 {
{
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],
[
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,6 +1,6 @@
pub mod common;
pub mod vector;
pub mod matrix;
pub mod vector;
#[cfg(feature = "fixed-wide")]
pub mod fixed_wide;

View File

@ -14,11 +14,9 @@ macro_rules! impl_vector {
#[inline]
pub fn map<F, U>(self, f: F) -> Vector<N, U>
where
F:Fn(T)->U
F: Fn(T) -> U,
{
Vector::new(
self.array.map(f)
)
Vector::new(self.array.map(f))
}
#[inline]
pub fn map_zip<F, U, V>(self, other: Vector<N, U>, f: F) -> Vector<N, V>
@ -26,9 +24,7 @@ macro_rules! impl_vector {
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())),
)
Vector::new(core::array::from_fn(|_| f(iter.next().unwrap())))
}
}
impl<const N: usize, T: Copy> Vector<N, T> {
@ -41,9 +37,7 @@ macro_rules! impl_vector {
impl<const N: usize, T: Default> Default for Vector<N, T> {
#[inline]
fn default() -> Self {
Self::new(
core::array::from_fn(|_|Default::default())
)
Self::new(core::array::from_fn(|_| Default::default()))
}
}
@ -104,21 +98,22 @@ macro_rules! impl_vector {
type Output = Vector<N, V>;
#[inline]
fn neg(self) -> Self::Output {
Vector::new(
self.array.map(|t|-t)
)
Vector::new(self.array.map(|t| -t))
}
}
impl<const N:usize,T> Vector<N,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()
self.array
.into_iter()
.zip(rhs.array)
.map(|(a, b)| a * b)
.sum()
}
}
@ -168,13 +163,15 @@ macro_rules! impl_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>{
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 {
@ -188,113 +185,130 @@ macro_rules! impl_vector_deferred_division {
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>{
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>{
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>{
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>{
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))
}
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>{
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)
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))
}
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>{
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>{
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>{
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)
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))
}
self.array.iter_mut().for_each(|t| t.$method(rhs))
}
}
};
}
#[doc(hidden)]
@ -305,12 +319,10 @@ macro_rules! impl_vector_extend {
#[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()),
)
}
Vector::new(core::array::from_fn(|_| iter.next().unwrap()))
}
}
};
}
#[doc(hidden)]
@ -330,15 +342,14 @@ macro_rules! impl_vector_named_fields {
unsafe { core::mem::transmute(&mut self.array) }
}
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_vector_3 {
() => {
impl<T> Vector<3,T>
{
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
@ -353,5 +364,5 @@ macro_rules! impl_vector_3 {
])
}
}
}
};
}

View File

@ -1,5 +1,5 @@
use crate::vector::Vector;
use crate::matrix::Matrix;
use crate::vector::Vector;
#[repr(C)]
pub struct Vector2<T> {
@ -45,15 +45,7 @@ pub struct Matrix4<T> {
crate::impl_matrix_named_fields!(
//outer struct
(
(Matrix2, 2),
(Matrix3, 3),
(Matrix4, 4)
),
((Matrix2, 2), (Matrix3, 3), (Matrix4, 4)),
//inner struct
(
(2),
(3),
(4)
)
((2), (3), (4))
);

View File

@ -12,7 +12,10 @@ fn wide_vec3(){
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]
@ -38,16 +41,33 @@ fn wide_vec3_length_squared(){
#[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();
[
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();
])
.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}}
@ -58,7 +78,9 @@ fn wide_matrix_dot(){
[Planar64Wide1::from(50), Planar64Wide1::from(60)],
[Planar64Wide1::from(114), Planar64Wide1::from(140)],
[Planar64Wide1::from(178), Planar64Wide1::from(220)],
]).transpose().array
])
.transpose()
.array
);
}
@ -88,9 +110,22 @@ fn wide_matrix_adjugate(){
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
[
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,4 +1,4 @@
use crate::types::{Vector3,Matrix3};
use crate::types::{Matrix3, Vector3};
#[test]
fn test_vector() {
@ -14,7 +14,6 @@ fn test_vector(){
assert_eq!(v.y, 10);
}
#[test]
fn test_matrix() {
let mut v = Matrix3::from_value(2);

View File

@ -1,4 +1,4 @@
use crate::types::{Vector2,Vector3,Matrix3x4,Matrix4x2,Matrix3x2,Matrix2x3};
use crate::types::{Matrix2x3, Matrix3x2, Matrix3x4, Matrix4x2, Vector2, Vector3};
#[test]
fn test_bool() {
@ -21,10 +21,7 @@ fn test_arithmetic(){
#[test]
fn matrix_transform_vector() {
let m=Matrix2x3::new([
[1,2,3],
[4,5,6],
]).transpose();
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);
@ -33,27 +30,22 @@ fn matrix_transform_vector(){
#[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([ // | | |
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();
])
.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
Matrix3x2::new([[50.0, 60.0], [114.0, 140.0], [178.0, 220.0],])
.transpose()
.array
);
}

View File

@ -1,5 +1,5 @@
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>;

View File

@ -30,7 +30,10 @@ impl<Num,Den> Ratio<Num,Den>
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>
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>,
@ -38,7 +41,10 @@ impl<LhsNum,LhsDen> Ratio<LhsNum,LhsDen>{
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>
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>,
@ -50,7 +56,13 @@ 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>
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>,
@ -59,7 +71,10 @@ macro_rules! impl_ratio_method {
RhsDen: Copy,
LhsCrossMul: core::ops::$trait<RhsCrossMul>,
{
Ratio::new((self.num*rhs.den).$method(self.den*rhs.num),self.den*rhs.den)
Ratio::new(
(self.num * rhs.den).$method(self.den * rhs.num),
self.den * rhs.den,
)
}
}
};
@ -115,7 +130,10 @@ 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
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>,
@ -127,7 +145,7 @@ macro_rules! impl_ratio_ord_method{
}
}
}
}
};
}
//PartialEq
impl_ratio_ord_method!(eq, eq_ratio, bool);
@ -251,7 +269,8 @@ 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>
impl<LhsNum, LhsDen, RhsNum, RhsDen, T, U> PartialEq<Ratio<RhsNum, RhsDen>>
for Ratio<LhsNum, LhsDen>
where
LhsNum: Copy,
LhsDen: Copy,
@ -268,7 +287,8 @@ impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<
}
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>
impl<LhsNum, LhsDen, RhsNum, RhsDen, T, U> PartialOrd<Ratio<RhsNum, RhsDen>>
for Ratio<LhsNum, LhsDen>
where
LhsNum: Copy,
LhsDen: Copy,

View File

@ -1,9 +1,9 @@
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>);
@ -54,7 +54,9 @@ impl Place{
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();
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}");
@ -86,8 +88,12 @@ 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),
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),
}
}
@ -97,7 +103,7 @@ pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
pub fn convert<AcquireRenderConfigId, AcquireMeshId>(
dom: impl AsRef<WeakDom>,
acquire_render_config_id: AcquireRenderConfigId,
acquire_mesh_id:AcquireMeshId
acquire_mesh_id: AcquireMeshId,
) -> rbx::PartialMap1
where
AcquireRenderConfigId: FnMut(Option<&str>) -> strafesnet_common::model::RenderConfigId,

View File

@ -1,12 +1,18 @@
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)
RbxMesh(rbx_mesh::mesh::Error),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -15,13 +21,7 @@ impl std::fmt::Display for Error{
}
impl std::error::Error for Error {}
fn ingest_vertices2<
AcquirePosId,
AcquireTexId,
AcquireNormalId,
AcquireColorId,
AcquireVertexId,
>(
fn ingest_vertices2<AcquirePosId, AcquireTexId, AcquireNormalId, AcquireColorId, AcquireVertexId>(
vertices: Vec<Vertex2>,
acquire_pos_id: &mut AcquirePosId,
acquire_tex_id: &mut AcquireTexId,
@ -38,22 +38,23 @@ where
{
//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((
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))
color: acquire_color_id(vertex.color.map(|f| f as f32 / 255.0f32)),
}),
))).collect::<Result<_,_>>()?)
))
})
.collect::<Result<_, _>>()?)
}
fn ingest_vertices_truncated2<
AcquirePosId,
AcquireTexId,
AcquireNormalId,
AcquireVertexId,
>(
fn ingest_vertices_truncated2<AcquirePosId, AcquireTexId, AcquireNormalId, AcquireVertexId>(
vertices: Vec<Vertex2Truncated>,
acquire_pos_id: &mut AcquirePosId,
acquire_tex_id: &mut AcquireTexId,
@ -69,29 +70,44 @@ where
{
//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((
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
color: static_color_id,
}),
))).collect::<Result<_,_>>()?)
))
})
.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>
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.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> {
@ -149,52 +165,110 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<model::Me
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{
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<_,_>>()?)));
},
}))
};
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),
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())));
},
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_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
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
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,

View File

@ -1,5 +1,9 @@
use strafesnet_common::model::{Color4,TextureCoordinate,Mesh,IndexedGraphicsGroup,IndexedPhysicsGroup,IndexedVertex,PolygonGroupId,PolygonGroup,PolygonList,IndexedVertexList,PositionId,TextureCoordinateId,NormalId,ColorId,VertexId,RenderConfigId};
use strafesnet_common::integer::{vec3, Planar64Vec3};
use strafesnet_common::model::{
Color4, ColorId, IndexedGraphicsGroup, IndexedPhysicsGroup, IndexedVertex, IndexedVertexList,
Mesh, NormalId, PolygonGroup, PolygonGroupId, PolygonList, PositionId, RenderConfigId,
TextureCoordinate, TextureCoordinateId, VertexId,
};
#[derive(Debug)]
pub enum Primitives {
@ -51,40 +55,15 @@ const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[
[1, 3, 0],
],
// top (0, 1, 0)
[
[5,3,1],
[4,2,1],
[3,1,1],
[2,0,1],
],
[[5, 3, 1], [4, 2, 1], [3, 1, 1], [2, 0, 1]],
// back (0, 0, 1)
[
[0,3,2],
[1,2,2],
[2,1,2],
[3,0,2],
],
[[0, 3, 2], [1, 2, 2], [2, 1, 2], [3, 0, 2]],
// left (-1, 0, 0)
[
[0,2,3],
[3,1,3],
[4,0,3],
[7,3,3],
],
[[0, 2, 3], [3, 1, 3], [4, 0, 3], [7, 3, 3]],
// bottom (0,-1, 0)
[
[1,1,4],
[0,0,4],
[7,3,4],
[6,2,4],
],
[[1, 1, 4], [0, 0, 4], [7, 3, 4], [6, 2, 4]],
// front (0, 0,-1)
[
[4,1,5],
[5,0,5],
[6,3,5],
[7,2,5],
],
[[4, 1, 5], [5, 0, 5], [6, 3, 5], [7, 2, 5]],
];
#[derive(Hash, PartialEq, Eq)]
@ -135,8 +114,16 @@ impl CubeFaceDescription{
pub fn insert(&mut self, index: CubeFace, value: FaceDescription) {
self.0[index as usize] = Some(value);
}
pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,6>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{
self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u)))
pub fn pairs(
self,
) -> std::iter::FilterMap<
std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>, 6>>,
impl FnMut((usize, Option<FaceDescription>)) -> Option<(usize, FaceDescription)>,
> {
self.0
.into_iter()
.enumerate()
.filter_map(|v| v.1.map(|u| (v.0, u)))
}
}
pub fn unit_cube(render: RenderConfigId) -> Mesh {
@ -145,7 +132,10 @@ pub fn unit_cube(render:RenderConfigId)->Mesh{
t.insert(CubeFace::Top, FaceDescription::new_with_render_id(render));
t.insert(CubeFace::Back, FaceDescription::new_with_render_id(render));
t.insert(CubeFace::Left, FaceDescription::new_with_render_id(render));
t.insert(CubeFace::Bottom,FaceDescription::new_with_render_id(render));
t.insert(
CubeFace::Bottom,
FaceDescription::new_with_render_id(render),
);
t.insert(CubeFace::Front, FaceDescription::new_with_render_id(render));
generate_partial_unit_cube(t)
}
@ -159,17 +149,34 @@ impl WedgeFaceDescription{
pub fn insert(&mut self, index: WedgeFace, value: FaceDescription) {
self.0[index as usize] = Some(value);
}
pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,5>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{
self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u)))
pub fn pairs(
self,
) -> std::iter::FilterMap<
std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>, 5>>,
impl FnMut((usize, Option<FaceDescription>)) -> Option<(usize, FaceDescription)>,
> {
self.0
.into_iter()
.enumerate()
.filter_map(|v| v.1.map(|u| (v.0, u)))
}
}
pub fn unit_wedge(render: RenderConfigId) -> Mesh {
let mut t = WedgeFaceDescription::default();
t.insert(WedgeFace::Right,FaceDescription::new_with_render_id(render));
t.insert(WedgeFace::TopFront,FaceDescription::new_with_render_id(render));
t.insert(
WedgeFace::Right,
FaceDescription::new_with_render_id(render),
);
t.insert(
WedgeFace::TopFront,
FaceDescription::new_with_render_id(render),
);
t.insert(WedgeFace::Back, FaceDescription::new_with_render_id(render));
t.insert(WedgeFace::Left, FaceDescription::new_with_render_id(render));
t.insert(WedgeFace::Bottom,FaceDescription::new_with_render_id(render));
t.insert(
WedgeFace::Bottom,
FaceDescription::new_with_render_id(render),
);
generate_partial_unit_wedge(t)
}
#[derive(Default)]
@ -178,17 +185,40 @@ impl CornerWedgeFaceDescription{
pub fn insert(&mut self, index: CornerWedgeFace, value: FaceDescription) {
self.0[index as usize] = Some(value);
}
pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,5>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{
self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u)))
pub fn pairs(
self,
) -> std::iter::FilterMap<
std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>, 5>>,
impl FnMut((usize, Option<FaceDescription>)) -> Option<(usize, FaceDescription)>,
> {
self.0
.into_iter()
.enumerate()
.filter_map(|v| v.1.map(|u| (v.0, u)))
}
}
pub fn unit_cornerwedge(render: RenderConfigId) -> Mesh {
let mut t = CornerWedgeFaceDescription::default();
t.insert(CornerWedgeFace::Right,FaceDescription::new_with_render_id(render));
t.insert(CornerWedgeFace::TopBack,FaceDescription::new_with_render_id(render));
t.insert(CornerWedgeFace::TopLeft,FaceDescription::new_with_render_id(render));
t.insert(CornerWedgeFace::Bottom,FaceDescription::new_with_render_id(render));
t.insert(CornerWedgeFace::Front,FaceDescription::new_with_render_id(render));
t.insert(
CornerWedgeFace::Right,
FaceDescription::new_with_render_id(render),
);
t.insert(
CornerWedgeFace::TopBack,
FaceDescription::new_with_render_id(render),
);
t.insert(
CornerWedgeFace::TopLeft,
FaceDescription::new_with_render_id(render),
);
t.insert(
CornerWedgeFace::Bottom,
FaceDescription::new_with_render_id(render),
);
t.insert(
CornerWedgeFace::Front,
FaceDescription::new_with_render_id(render),
);
generate_partial_unit_cornerwedge(t)
}
@ -220,18 +250,25 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
for (face_id, face_description) in face_descriptions.pairs() {
//assume that scanning short lists is faster than hashing.
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
let transform_index = if let Some(transform_index) = transforms
.iter()
.position(|&transform| transform == face_description.transform)
{
transform_index
} else {
//create new transform_index
let transform_index = transforms.len();
transforms.push(face_description.transform);
generated_tex.extend(CUBE_DEFAULT_TEXTURE_COORDS.map(|tex|
face_description.transform.transform_point2(tex)
));
generated_tex.extend(
CUBE_DEFAULT_TEXTURE_COORDS
.map(|tex| face_description.transform.transform_point2(tex)),
);
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
let color_index = if let Some(color_index) = generated_color
.iter()
.position(|&color| color == face_description.color)
{
color_index
} else {
//create new color_index
@ -245,9 +282,11 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
//push vertices as they are needed
let group_id = PolygonGroupId::new(polygon_groups.len() as u32);
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(vec![
CUBE_DEFAULT_POLYS[face_id].map(|tup|{
CUBE_DEFAULT_POLYS[face_id]
.map(|tup| {
let pos = CUBE_DEFAULT_VERTICES[tup[0] as usize];
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
let pos_index =
if let Some(pos_index) = generated_pos.iter().position(|&p| p == pos) {
pos_index
} else {
//create new pos_index
@ -265,7 +304,8 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
let vert_index = generated_vertices.len();
generated_vertices.push(vertex);
VertexId::new(vert_index as u32)
}).to_vec(),
})
.to_vec(),
])));
graphics_groups.push(IndexedGraphicsGroup {
render: face_description.render,
@ -294,32 +334,13 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
[1, 3, 0],
],
// FrontTop (0, 1, -1)
vec![
[3,1,1],
[2,0,1],
[6,3,1],
[7,2,1],
],
vec![[3, 1, 1], [2, 0, 1], [6, 3, 1], [7, 2, 1]],
// back (0, 0, 1)
vec![
[0,3,2],
[1,2,2],
[2,1,2],
[3,0,2],
],
vec![[0, 3, 2], [1, 2, 2], [2, 1, 2], [3, 0, 2]],
// left (-1, 0, 0)
vec![
[0,2,3],
[3,1,3],
[7,3,3],
],
vec![[0, 2, 3], [3, 1, 3], [7, 3, 3]],
// bottom (0,-1, 0)
vec![
[1,1,4],
[0,0,4],
[7,3,4],
[6,2,4],
],
vec![[1, 1, 4], [0, 0, 4], [7, 3, 4], [6, 2, 4]],
];
let mut generated_pos = Vec::new();
let mut generated_tex = Vec::new();
@ -333,18 +354,25 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
for (face_id, face_description) in face_descriptions.pairs() {
//assume that scanning short lists is faster than hashing.
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
let transform_index = if let Some(transform_index) = transforms
.iter()
.position(|&transform| transform == face_description.transform)
{
transform_index
} else {
//create new transform_index
let transform_index = transforms.len();
transforms.push(face_description.transform);
generated_tex.extend(CUBE_DEFAULT_TEXTURE_COORDS.map(|tex|
face_description.transform.transform_point2(tex)
));
generated_tex.extend(
CUBE_DEFAULT_TEXTURE_COORDS
.map(|tex| face_description.transform.transform_point2(tex)),
);
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
let color_index = if let Some(color_index) = generated_color
.iter()
.position(|&color| color == face_description.color)
{
color_index
} else {
//create new color_index
@ -358,9 +386,12 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
//push vertices as they are needed
let group_id = PolygonGroupId::new(polygon_groups.len() as u32);
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(vec![
wedge_default_polys[face_id].iter().map(|tup|{
wedge_default_polys[face_id]
.iter()
.map(|tup| {
let pos = CUBE_DEFAULT_VERTICES[tup[0] as usize];
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
let pos_index =
if let Some(pos_index) = generated_pos.iter().position(|&p| p == pos) {
pos_index
} else {
//create new pos_index
@ -378,7 +409,8 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
let vert_index = generated_vertices.len();
generated_vertices.push(vertex);
VertexId::new(vert_index as u32)
}).collect()
})
.collect(),
])));
graphics_groups.push(IndexedGraphicsGroup {
render: face_description.render,
@ -407,30 +439,13 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
[1, 3, 0],
],
// BackTop (0, 1, 1)
vec![
[5,3,1],
[0,1,1],
[1,0,1],
],
vec![[5, 3, 1], [0, 1, 1], [1, 0, 1]],
// LeftTop (-1, 1, 0)
vec![
[5,3,2],
[7,2,2],
[0,1,2],
],
vec![[5, 3, 2], [7, 2, 2], [0, 1, 2]],
// bottom (0,-1, 0)
vec![
[1,1,3],
[0,0,3],
[7,3,3],
[6,2,3],
],
vec![[1, 1, 3], [0, 0, 3], [7, 3, 3], [6, 2, 3]],
// front (0, 0,-1)
vec![
[5,0,4],
[6,3,4],
[7,2,4],
],
vec![[5, 0, 4], [6, 3, 4], [7, 2, 4]],
];
let mut generated_pos = Vec::new();
let mut generated_tex = Vec::new();
@ -444,18 +459,25 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
for (face_id, face_description) in face_descriptions.pairs() {
//assume that scanning short lists is faster than hashing.
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
let transform_index = if let Some(transform_index) = transforms
.iter()
.position(|&transform| transform == face_description.transform)
{
transform_index
} else {
//create new transform_index
let transform_index = transforms.len();
transforms.push(face_description.transform);
generated_tex.extend(CUBE_DEFAULT_TEXTURE_COORDS.map(|tex|
face_description.transform.transform_point2(tex)
));
generated_tex.extend(
CUBE_DEFAULT_TEXTURE_COORDS
.map(|tex| face_description.transform.transform_point2(tex)),
);
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
let color_index = if let Some(color_index) = generated_color
.iter()
.position(|&color| color == face_description.color)
{
color_index
} else {
//create new color_index
@ -469,9 +491,12 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
//push vertices as they are needed
let group_id = PolygonGroupId::new(polygon_groups.len() as u32);
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(vec![
cornerwedge_default_polys[face_id].iter().map(|tup|{
cornerwedge_default_polys[face_id]
.iter()
.map(|tup| {
let pos = CUBE_DEFAULT_VERTICES[tup[0] as usize];
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
let pos_index =
if let Some(pos_index) = generated_pos.iter().position(|&p| p == pos) {
pos_index
} else {
//create new pos_index
@ -489,7 +514,8 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
let vert_index = generated_vertices.len();
generated_vertices.push(vertex);
VertexId::new(vert_index as u32)
}).collect(),
})
.collect(),
])));
graphics_groups.push(IndexedGraphicsGroup {
render: face_description.render,

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,15 @@ 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)
)
)
|| 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)]
@ -19,16 +22,21 @@ impl Context{
pub const fn new(dom: WeakDom) -> Self {
Self { dom }
}
pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance,Services){
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)
InstanceBuilder::new("DataModel").with_child(script),
));
let services = context.convert_into_place();
(context,crate::runner::instance::Instance::new(script_ref),services)
(
context,
crate::runner::instance::Instance::new(script_ref),
services,
)
}
pub fn from_ref(dom: &WeakDom) -> &Context {
unsafe { &*(dom as *const WeakDom as *const Context) }
@ -38,19 +46,24 @@ impl 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())
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()
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")
)?,
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(),
})
}
@ -61,17 +74,20 @@ impl Context{
//insert services
let game = self.dom.root_ref();
let terrain_bldr = InstanceBuilder::new("Terrain");
let workspace=self.dom.insert(game,
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)
.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));
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"));
@ -80,10 +96,7 @@ impl Context{
self.dom.transfer_within(instance, workspace);
}
Services{
game,
workspace,
}
Services { game, workspace }
}
}

View File

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

View File

@ -5,25 +5,38 @@ 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,
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(xz, yz, zz),
),
glam::vec3(x,y,z)
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)))
Self(glam::Affine3A::from_mat3(glam::Mat3::from_euler(
glam::EulerRot::YXZ,
y,
x,
z,
)))
}
}
@ -42,7 +55,7 @@ impl Into<rbx_types::CFrame> for CFrame{
vec3_to_glam(self.0.matrix3.x_axis),
vec3_to_glam(self.0.matrix3.y_axis),
vec3_to_glam(self.0.matrix3.z_axis),
)
),
)
}
}
@ -54,7 +67,7 @@ impl From<rbx_types::CFrame> for CFrame{
vec3_from_glam(value.orientation.y),
vec3_from_glam(value.orientation.z),
),
translation:vec3_from_glam(value.position)
translation: vec3_from_glam(value.position),
})
}
}
@ -63,61 +76,102 @@ 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>,
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,
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,
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,
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,
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"))
})?
_ => 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_table.raw_set(
"Angles",
lua.create_function(|_, (x, y, z): (f32, f32, f32)| Ok(CFrame::angles(x, y, z)))?,
)?;
globals.set("CFrame", cframe_table)?;
@ -132,28 +186,8 @@ impl mlua::UserData for CFrame{
}
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({},{},{},{},{},{},{},{},{},{},{},{})",
methods.add_method("components", |_, this, ()| {
Ok((
this.0.translation.x,
this.0.translation.y,
this.0.translation.z,
@ -167,7 +201,32 @@ impl mlua::UserData for CFrame{
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,
))
});
}
}

View File

@ -18,15 +18,19 @@ impl Into<rbx_types::Color3> for Color3{
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(
"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(
"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)?;
@ -56,13 +60,13 @@ impl mlua::UserData for Color3{
});
}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("Lerp",|_,this,(other,t):(Self,f32)|
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

@ -8,7 +8,7 @@ impl ColorSequence{
impl Into<rbx_types::ColorSequence> for ColorSequence {
fn into(self) -> rbx_types::ColorSequence {
rbx_types::ColorSequence {
keypoints:Vec::new()
keypoints: Vec::new(),
}
}
}
@ -16,10 +16,9 @@ impl Into<rbx_types::ColorSequence> for ColorSequence{
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)?;

View File

@ -26,38 +26,42 @@ pub fn set_globals(_lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>
}
impl mlua::UserData for EnumItem<'_> {
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
}
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()?){
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_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)|{
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){
}
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);

View File

@ -1,8 +1,8 @@
use std::collections::{hash_map::Entry, HashMap};
use mlua::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use rbx_types::Ref;
use rbx_dom_weak::{InstanceBuilder, WeakDom};
use rbx_types::Ref;
use crate::runner::vector3::Vector3;
@ -14,15 +14,22 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
let instance_table = lua.create_table()?;
//Instance.new
instance_table.raw_set("new",
lua.create_function(|lua,(class_name,parent):(mlua::String,Option<Instance>)|{
instance_table.raw_set(
"new",
lua.create_function(
|lua, (class_name, parent): (mlua::String, Option<Instance>)| {
let class_name_str = &*class_name.to_str()?;
let parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
let parent =
parent.ok_or_else(|| mlua::Error::runtime("Nil Parent not yet supported"))?;
dom_mut(lua, |dom| {
//TODO: Nil instances
Ok(Instance::new(dom.insert(parent.referent,InstanceBuilder::new(class_name_str))))
Ok(Instance::new(dom.insert(
parent.referent,
InstanceBuilder::new(class_name_str),
)))
})
})?
},
)?,
)?;
globals.set("Instance", instance_table)?;
@ -31,8 +38,13 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
}
// LMAO look at this function!
pub fn dom_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>)->mlua::Result<T>{
let mut dom=lua.app_data_mut::<&'static mut WeakDom>().ok_or_else(||mlua::Error::runtime("DataModel missing"))?;
pub fn dom_mut<T>(
lua: &mlua::Lua,
mut f: impl FnMut(&mut WeakDom) -> mlua::Result<T>,
) -> mlua::Result<T> {
let mut dom = lua
.app_data_mut::<&'static mut WeakDom>()
.ok_or_else(|| mlua::Error::runtime("DataModel missing"))?;
f(&mut *dom)
}
@ -66,18 +78,44 @@ pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),m
})
}
pub fn find_first_child<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name)
pub fn find_first_child<'a>(
dom: &'a rbx_dom_weak::WeakDom,
instance: &rbx_dom_weak::Instance,
name: &str,
) -> Option<&'a rbx_dom_weak::Instance> {
instance
.children()
.iter()
.filter_map(|&r| dom.get_by_ref(r))
.find(|inst| inst.name == name)
}
pub fn find_first_descendant<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
dom.descendants_of(instance.referent()).find(|&inst|inst.name==name)
pub fn find_first_descendant<'a>(
dom: &'a rbx_dom_weak::WeakDom,
instance: &rbx_dom_weak::Instance,
name: &str,
) -> Option<&'a rbx_dom_weak::Instance> {
dom.descendants_of(instance.referent())
.find(|&inst| inst.name == name)
}
pub fn find_first_child_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class)
pub fn find_first_child_of_class<'a>(
dom: &'a rbx_dom_weak::WeakDom,
instance: &rbx_dom_weak::Instance,
class: &str,
) -> Option<&'a rbx_dom_weak::Instance> {
instance
.children()
.iter()
.filter_map(|&r| dom.get_by_ref(r))
.find(|inst| inst.class == class)
}
pub fn find_first_descendant_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
pub fn find_first_descendant_of_class<'a>(
dom: &'a rbx_dom_weak::WeakDom,
instance: &rbx_dom_weak::Instance,
class: &str,
) -> Option<&'a rbx_dom_weak::Instance> {
dom.descendants_of(instance.referent())
.find(|&inst| inst.class == class)
}
#[derive(Clone, Copy)]
@ -89,10 +127,15 @@ impl Instance{
Self { referent }
}
pub fn get<'a>(&self, dom: &'a WeakDom) -> mlua::Result<&'a rbx_dom_weak::Instance> {
dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
dom.get_by_ref(self.referent)
.ok_or_else(|| mlua::Error::runtime("Instance missing"))
}
pub fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{
dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
pub fn get_mut<'a>(
&self,
dom: &'a mut WeakDom,
) -> mlua::Result<&'a mut rbx_dom_weak::Instance> {
dom.get_by_ref_mut(self.referent)
.ok_or_else(|| mlua::Error::runtime("Instance missing"))
}
}
type_from_lua_userdata!(Instance);
@ -153,7 +196,7 @@ impl mlua::UserData for Instance{
});
}
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("GetChildren",|lua,this,_:()|
methods.add_method("GetChildren", |lua, this, _: ()| {
dom_mut(lua, |dom| {
let instance = this.get(dom)?;
let children: Vec<_> = instance
@ -164,61 +207,61 @@ impl mlua::UserData for Instance{
.collect();
Ok(children)
})
);
fn ffc(lua:&mlua::Lua,this:&Instance,(name,search_descendants):(mlua::String,Option<bool>))->mlua::Result<Option<Instance>>{
});
fn ffc(
lua: &mlua::Lua,
this: &Instance,
(name, search_descendants): (mlua::String, Option<bool>),
) -> mlua::Result<Option<Instance>> {
let name_str = &*name.to_str()?;
dom_mut(lua, |dom| {
let instance = this.get(dom)?;
Ok(
match search_descendants.unwrap_or(false){
Ok(match search_descendants.unwrap_or(false) {
true => find_first_descendant(dom, instance, name_str),
false => find_first_child(dom, instance, name_str),
}
.map(|instance|
Instance::new(instance.referent())
)
)
.map(|instance| Instance::new(instance.referent())))
})
}
methods.add_method("FindFirstChild", ffc);
methods.add_method("WaitForChild", ffc);
methods.add_method("FindFirstChildOfClass",|lua,this,(class,search_descendants):(mlua::String,Option<bool>)|{
methods.add_method(
"FindFirstChildOfClass",
|lua, this, (class, search_descendants): (mlua::String, Option<bool>)| {
let class_str = &*class.to_str()?;
dom_mut(lua, |dom| {
Ok(
match search_descendants.unwrap_or(false){
Ok(match search_descendants.unwrap_or(false) {
true => find_first_descendant_of_class(dom, this.get(dom)?, class_str),
false => find_first_child_of_class(dom, this.get(dom)?, class_str),
}
.map(|instance|
Instance::new(instance.referent())
)
)
.map(|instance| Instance::new(instance.referent())))
})
});
methods.add_method("GetDescendants",|lua,this,_:()|
},
);
methods.add_method("GetDescendants", |lua, this, _: ()| {
dom_mut(lua, |dom| {
let children: Vec<_> = dom
.descendants_of(this.referent)
.map(|instance|
Instance::new(instance.referent())
)
.map(|instance| Instance::new(instance.referent()))
.collect();
Ok(children)
})
);
methods.add_method("IsA",|lua,this,classname:mlua::String|
});
methods.add_method("IsA", |lua, this, classname: mlua::String| {
dom_mut(lua, |dom| {
let instance = this.get(dom)?;
Ok(crate::context::class_is_a(instance.class.as_str(),&*classname.to_str()?))
Ok(crate::context::class_is_a(
instance.class.as_str(),
&*classname.to_str()?,
))
})
);
methods.add_method("Destroy",|lua,this,()|
});
methods.add_method("Destroy", |lua, this, ()| {
dom_mut(lua, |dom| {
dom.destroy(this.referent);
Ok(())
})
);
});
methods.add_meta_function(mlua::MetaMethod::ToString, |lua, this: Instance| {
dom_mut(lua, |dom| {
let instance = this.get(dom)?;
@ -289,68 +332,139 @@ impl mlua::UserData for Instance{
.into_lua(lua)
})
});
methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Instance,mlua::String,mlua::Value)|{
methods.add_meta_function(
mlua::MetaMethod::NewIndex,
|lua, (this, index, value): (Instance, mlua::String, mlua::Value)| {
dom_mut(lua, |dom| {
let instance = this.get_mut(dom)?;
//println!("__newindex t={} i={index:?} v={value:?}",instance.name);
let index_str = &*index.to_str()?;
let db = rbx_reflection_database::get();
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
let class = db
.classes
.get(instance.class.as_str())
.ok_or_else(|| mlua::Error::runtime("Class missing"))?;
let mut iter = SuperClassIter {
database: db,
descriptor: Some(class),
};
let property=iter.find_map(|cls|cls.properties.get(index_str)).ok_or_else(||mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name)))?;
let property = iter
.find_map(|cls| cls.properties.get(index_str))
.ok_or_else(|| {
mlua::Error::runtime(format!(
"Property '{index_str}' missing on class '{}'",
class.name
))
})?;
match &property.data_type {
rbx_reflection::DataType::Value(rbx_types::VariantType::Vector3) => {
let typed_value:Vector3=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Userdata"))?.borrow()?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Vector3(typed_value.into()));
},
let typed_value: Vector3 = *value
.as_userdata()
.ok_or_else(|| mlua::Error::runtime("Expected Userdata"))?
.borrow()?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::Vector3(typed_value.into()),
);
}
rbx_reflection::DataType::Value(rbx_types::VariantType::Float32) => {
let typed_value:f32=coerce_float32(&value).ok_or_else(||mlua::Error::runtime("Expected f32"))?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Float32(typed_value));
},
let typed_value: f32 = coerce_float32(&value)
.ok_or_else(|| mlua::Error::runtime("Expected f32"))?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::Float32(typed_value),
);
}
rbx_reflection::DataType::Enum(enum_name) => {
let typed_value = match &value {
&mlua::Value::Integer(int)=>Ok(rbx_types::Enum::from_u32(int as u32)),
&mlua::Value::Number(num)=>Ok(rbx_types::Enum::from_u32(num as u32)),
&mlua::Value::Integer(int) => {
Ok(rbx_types::Enum::from_u32(int as u32))
}
&mlua::Value::Number(num) => {
Ok(rbx_types::Enum::from_u32(num as u32))
}
mlua::Value::String(s) => {
let e=db.enums.get(enum_name).ok_or_else(||mlua::Error::runtime("Database DataType Enum name does not exist"))?;
Ok(rbx_types::Enum::from_u32(*e.items.get(&*s.to_str()?).ok_or_else(||mlua::Error::runtime("Invalid enum item"))?))
},
let e = db.enums.get(enum_name).ok_or_else(|| {
mlua::Error::runtime(
"Database DataType Enum name does not exist",
)
})?;
Ok(rbx_types::Enum::from_u32(
*e.items.get(&*s.to_str()?).ok_or_else(|| {
mlua::Error::runtime("Invalid enum item")
})?,
))
}
mlua::Value::UserData(any_user_data) => {
let e: crate::runner::r#enum::Enum = *any_user_data.borrow()?;
Ok(e.into())
},
}
_ => Err(mlua::Error::runtime("Expected Enum")),
}?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Enum(typed_value));
},
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::Enum(typed_value),
);
}
rbx_reflection::DataType::Value(rbx_types::VariantType::Color3) => {
let typed_value:crate::runner::color3::Color3=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Color3"))?.borrow()?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Color3(typed_value.into()));
},
let typed_value: crate::runner::color3::Color3 = *value
.as_userdata()
.ok_or_else(|| mlua::Error::runtime("Expected Color3"))?
.borrow()?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::Color3(typed_value.into()),
);
}
rbx_reflection::DataType::Value(rbx_types::VariantType::Bool) => {
let typed_value=value.as_boolean().ok_or_else(||mlua::Error::runtime("Expected boolean"))?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Bool(typed_value));
},
let typed_value = value
.as_boolean()
.ok_or_else(|| mlua::Error::runtime("Expected boolean"))?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::Bool(typed_value),
);
}
rbx_reflection::DataType::Value(rbx_types::VariantType::String) => {
let typed_value=value.as_str().ok_or_else(||mlua::Error::runtime("Expected boolean"))?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::String(typed_value.to_owned()));
},
let typed_value = value
.as_str()
.ok_or_else(|| mlua::Error::runtime("Expected boolean"))?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::String(typed_value.to_owned()),
);
}
rbx_reflection::DataType::Value(rbx_types::VariantType::NumberSequence) => {
let typed_value:crate::runner::number_sequence::NumberSequence=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected NumberSequence"))?.borrow()?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::NumberSequence(typed_value.into()));
},
let typed_value: crate::runner::number_sequence::NumberSequence =
*value
.as_userdata()
.ok_or_else(|| mlua::Error::runtime("Expected NumberSequence"))?
.borrow()?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::NumberSequence(typed_value.into()),
);
}
rbx_reflection::DataType::Value(rbx_types::VariantType::ColorSequence) => {
let typed_value:crate::runner::color_sequence::ColorSequence=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected ColorSequence"))?.borrow()?;
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::ColorSequence(typed_value.into()));
},
other=>return Err(mlua::Error::runtime(format!("Unimplemented property type: {other:?}"))),
let typed_value: crate::runner::color_sequence::ColorSequence = *value
.as_userdata()
.ok_or_else(|| mlua::Error::runtime("Expected ColorSequence"))?
.borrow()?;
instance.properties.insert(
index_str.to_owned(),
rbx_types::Variant::ColorSequence(typed_value.into()),
);
}
other => {
return Err(mlua::Error::runtime(format!(
"Unimplemented property type: {other:?}"
)))
}
}
Ok(())
})
});
},
);
}
}
@ -367,10 +481,12 @@ type ClassFunctionPointer=fn(&mlua::Lua,mlua::MultiValue)->mlua::Result<mlua::Mu
// TODO: use macros to define these with better organization
/// A double hash map of function pointers.
/// The class tree is walked by the Instance.__index metamethod to find available class methods.
type CFD=phf::Map<&'static str,// Class name
phf::Map<&'static str,// Method name
ClassFunctionPointer
>
type CFD = phf::Map<
&'static str, // Class name
phf::Map<
&'static str, // Method name
ClassFunctionPointer,
>,
>;
static CLASS_FUNCTION_DATABASE: CFD = phf::phf_map! {
"DataModel"=>phf::phf_map!{
@ -401,25 +517,28 @@ static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
/// Functions are created the first time they are accessed and stored in this data structure.
#[derive(Default)]
struct ClassMethodsStore {
classes:HashMap<&'static str,//ClassName
HashMap<&'static str,//Method name
mlua::Function
>
>
classes: HashMap<
&'static str, //ClassName
HashMap<
&'static str, //Method name
mlua::Function,
>,
>,
}
impl ClassMethodsStore {
/// return self.classes[class] or create the ClassMethods and then return it
fn get_or_create_class_methods(&mut self, class: &str) -> Option<ClassMethods> {
// Use get_entry to get the &'static str keys of the database
// and use it as a key for the classes hashmap
CLASS_FUNCTION_DATABASE.get_entry(class)
.map(|(&static_class_str,method_pointers)|
ClassMethods{
CLASS_FUNCTION_DATABASE
.get_entry(class)
.map(|(&static_class_str, method_pointers)| ClassMethods {
method_pointers,
methods:self.classes.entry(static_class_str)
methods: self
.classes
.entry(static_class_str)
.or_insert_with(|| HashMap::new()),
}
)
})
}
}
struct ClassMethods<'a> {
@ -428,44 +547,49 @@ struct ClassMethods<'a>{
}
impl ClassMethods<'_> {
/// return self.methods[index] or create the function in the hashmap and then return it
fn get_or_create_function(&mut self,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::Function>>{
fn get_or_create_function(
&mut self,
lua: &mlua::Lua,
index: &str,
) -> mlua::Result<Option<mlua::Function>> {
Ok(match self.method_pointers.get_entry(index) {
Some((&static_index_str,&function_pointer))=>Some(
match self.methods.entry(static_index_str){
Some((&static_index_str, &function_pointer)) => {
Some(match self.methods.entry(static_index_str) {
Entry::Occupied(entry) => entry.get().clone(),
Entry::Vacant(entry)=>entry.insert(
lua.create_function(function_pointer)?
).clone(),
Entry::Vacant(entry) => {
entry.insert(lua.create_function(function_pointer)?).clone()
}
})
}
),
None => None,
})
}
}
fn class_methods_store_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut ClassMethodsStore)->mlua::Result<T>)->mlua::Result<T>{
let mut cf=lua.app_data_mut::<ClassMethodsStore>().ok_or_else(||mlua::Error::runtime("ClassMethodsStore missing"))?;
fn class_methods_store_mut<T>(
lua: &mlua::Lua,
mut f: impl FnMut(&mut ClassMethodsStore) -> mlua::Result<T>,
) -> mlua::Result<T> {
let mut cf = lua
.app_data_mut::<ClassMethodsStore>()
.ok_or_else(|| mlua::Error::runtime("ClassMethodsStore missing"))?;
f(&mut *cf)
}
/// A virtual property pointer definition shorthand.
type VirtualPropertyFunctionPointer = fn(&rbx_types::Variant) -> Option<rbx_types::Variant>;
const fn vpp(
property:&'static str,
pointer:VirtualPropertyFunctionPointer,
)->VirtualProperty{
VirtualProperty{
property,
pointer,
}
const fn vpp(property: &'static str, pointer: VirtualPropertyFunctionPointer) -> VirtualProperty {
VirtualProperty { property, pointer }
}
struct VirtualProperty {
property: &'static str, // Source property name
pointer: VirtualPropertyFunctionPointer,
}
type VPD=phf::Map<&'static str,// Class name
phf::Map<&'static str,// Virtual property name
VirtualProperty
>
type VPD = phf::Map<
&'static str, // Class name
phf::Map<
&'static str, // Virtual property name
VirtualProperty,
>,
>;
static VIRTUAL_PROPERTY_DATABASE: VPD = phf::phf_map! {
"BasePart"=>phf::phf_map!{
@ -482,7 +606,7 @@ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
fn find_virtual_property(
properties: &HashMap<String, rbx_types::Variant>,
class: &rbx_reflection::ClassDescriptor,
index:&str
index: &str,
) -> Option<rbx_types::Variant> {
//Find virtual property
let class_virtual_properties = VIRTUAL_PROPERTY_DATABASE.get(&class.name)?;
@ -499,10 +623,12 @@ fn find_virtual_property(
// This whole thing is a bad idea and a garbage collection nightmare.
// TODO: recreate rbx_dom_weak with my own instance type that owns this data.
type CreateUserData = fn(&mlua::Lua) -> mlua::Result<mlua::AnyUserData>;
type LUD=phf::Map<&'static str,// Class name
phf::Map<&'static str,// Value name
CreateUserData
>
type LUD = phf::Map<
&'static str, // Class name
phf::Map<
&'static str, // Value name
CreateUserData,
>,
>;
static LAZY_USER_DATA: LUD = phf::phf_map! {
"RunService"=>phf::phf_map!{
@ -513,45 +639,52 @@ static LAZY_USER_DATA:LUD=phf::phf_map!{
};
#[derive(Default)]
pub struct InstanceValueStore {
values:HashMap<Ref,
HashMap<&'static str,
mlua::AnyUserData
>
>,
values: HashMap<Ref, HashMap<&'static str, mlua::AnyUserData>>,
}
pub struct InstanceValues<'a> {
named_values: &'static phf::Map<&'static str, CreateUserData>,
values: &'a mut HashMap<&'static str, mlua::AnyUserData>,
}
impl InstanceValueStore {
pub fn get_or_create_instance_values(&mut self,instance:&rbx_dom_weak::Instance)->Option<InstanceValues>{
LAZY_USER_DATA.get(instance.class.as_str())
.map(|named_values|
InstanceValues{
pub fn get_or_create_instance_values(
&mut self,
instance: &rbx_dom_weak::Instance,
) -> Option<InstanceValues> {
LAZY_USER_DATA
.get(instance.class.as_str())
.map(|named_values| InstanceValues {
named_values,
values:self.values.entry(instance.referent())
values: self
.values
.entry(instance.referent())
.or_insert_with(|| HashMap::new()),
}
)
})
}
}
impl InstanceValues<'_> {
pub fn get_or_create_value(&mut self,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
pub fn get_or_create_value(
&mut self,
lua: &mlua::Lua,
index: &str,
) -> mlua::Result<Option<mlua::AnyUserData>> {
Ok(match self.named_values.get_entry(index) {
Some((&static_index_str,&function_pointer))=>Some(
match self.values.entry(static_index_str){
Some((&static_index_str, &function_pointer)) => {
Some(match self.values.entry(static_index_str) {
Entry::Occupied(entry) => entry.get().clone(),
Entry::Vacant(entry)=>entry.insert(
function_pointer(lua)?
).clone(),
Entry::Vacant(entry) => entry.insert(function_pointer(lua)?).clone(),
})
}
),
None => None,
})
}
}
pub fn instance_value_store_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut InstanceValueStore)->mlua::Result<T>)->mlua::Result<T>{
let mut cf=lua.app_data_mut::<InstanceValueStore>().ok_or_else(||mlua::Error::runtime("InstanceValueStore missing"))?;
pub fn instance_value_store_mut<T>(
lua: &mlua::Lua,
mut f: impl FnMut(&mut InstanceValueStore) -> mlua::Result<T>,
) -> mlua::Result<T> {
let mut cf = lua
.app_data_mut::<InstanceValueStore>()
.ok_or_else(|| mlua::Error::runtime("InstanceValueStore missing"))?;
f(&mut *cf)
}

View File

@ -4,7 +4,11 @@ macro_rules! type_from_lua_userdata{
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))),
other => Err(mlua::Error::runtime(format!(
"Expected {} got {:?}",
stringify!($asd),
other
))),
}
}
}
@ -16,7 +20,11 @@ macro_rules! type_from_lua_userdata_lua_lifetime{
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))),
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

@ -8,7 +8,7 @@ impl NumberSequence{
impl Into<rbx_types::NumberSequence> for NumberSequence {
fn into(self) -> rbx_types::NumberSequence {
rbx_types::NumberSequence {
keypoints:Vec::new()
keypoints: Vec::new(),
}
}
}
@ -16,10 +16,9 @@ impl Into<rbx_types::NumberSequence> for NumberSequence{
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)?;

View File

@ -7,10 +7,7 @@ pub struct Runner{
}
#[derive(Debug)]
pub enum Error {
Lua{
source:String,
error:mlua::Error
},
Lua { source: String, error: mlua::Error },
RustLua(mlua::Error),
NoServices,
}
@ -57,19 +54,34 @@ impl Runner{
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>{
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)?;
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)});
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());
self.lua
.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
Ok(Runnable {
lua: self.lua,
_lifetime:&std::marker::PhantomData
_lifetime: &std::marker::PhantomData,
})
}
}
@ -77,42 +89,53 @@ impl Runner{
//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<()>
_lifetime: &'a std::marker::PhantomData<()>,
}
impl Runnable<'_> {
pub fn drop_context(self) -> Runner {
self.lua.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
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,
}
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)?;
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})?;
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())
)
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});
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}"),
@ -124,11 +147,16 @@ impl Runnable<'_>{
#[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"))?;
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 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)

View File

@ -73,14 +73,22 @@ impl ScriptSignal{
}
}
pub fn connect(&self, function: mlua::Function) -> ScriptConnection {
self.connections.functions.borrow_mut().functions.push(function.clone());
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());
self.once
.functions
.borrow_mut()
.functions
.push(function.clone());
ScriptConnection {
connection: self.once.clone(),
function,
@ -92,18 +100,21 @@ impl ScriptSignal{
}
impl ScriptConnection {
pub fn position(&self) -> Option<usize> {
self.connection.functions.borrow().functions.iter().position(|function|function==&self.function)
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|
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))
);
});
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))
@ -114,21 +125,27 @@ 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))),
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())
});
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);
this.connection
.functions
.borrow_mut()
.functions
.remove(index);
}
Ok(())
});
@ -140,8 +157,7 @@ fn wait_thread(lua:&mlua::Lua,this:ScriptSignal)->Result<(),mlua::Error>{
}
// 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)
@ -158,7 +174,8 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
wait_env.raw_set("wait_thread", wait_thread)?;
//construct wait function from Lua code
let wait=lua.load(LUA_WAIT)
let wait = lua
.load(LUA_WAIT)
.set_name("wait")
.set_environment(wait_env)
.call::<mlua::Function>(())?;

View File

@ -11,10 +11,9 @@ 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_table.raw_set(
"new",
lua.create_function(|_, (x, y, z): (f32, f32, f32)| Ok(Vector3::new(x, y, z)))?,
)?;
globals.set("Vector3", vector3_table)?;
@ -57,25 +56,29 @@ impl mlua::UserData for Vector3{
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{
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,
))
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,
))
});
}
}

View File

@ -36,7 +36,8 @@ impl Scheduler{
!self.schedule.is_empty()
}
pub fn schedule_thread(&mut self, delay: u64, thread: mlua::Thread) {
self.schedule.entry(self.tick+delay.max(1))
self.schedule
.entry(self.tick + delay.max(1))
.or_insert(Vec::new())
.push(thread);
}
@ -46,8 +47,13 @@ impl 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"))?;
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)
}
@ -60,13 +66,15 @@ fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{
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{
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"))?,
};
@ -77,8 +85,7 @@ fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{
}
// 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)
@ -95,7 +102,8 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
wait_env.raw_set("schedule_thread", schedule_thread)?;
//construct wait function from Lua code
let wait=lua.load(LUA_WAIT)
let wait = lua
.load(LUA_WAIT)
.set_name("wait")
.set_environment(wait_env)
.call::<mlua::Function>(())?;

View File

@ -0,0 +1 @@

View File

@ -1,4 +1,4 @@
use binrw::{BinReaderExt, binrw};
use binrw::{binrw, BinReaderExt};
#[derive(Debug)]
pub enum Error {
@ -90,7 +90,10 @@ impl<R:BinReaderExt> StreamableBot<R>{
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 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,6 +1,6 @@
//file format "sniff"
use binrw::{binrw,BinReaderExt,io::TakeSeekExt};
use binrw::{binrw, io::TakeSeekExt, BinReaderExt};
#[derive(Debug)]
pub enum Error {
@ -95,13 +95,18 @@ impl<R:BinReaderExt> File<R>{
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>{
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))
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)?;
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 {

View File

@ -2,10 +2,10 @@ 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 {
@ -40,21 +40,21 @@ 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)
_ => 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)
_ => 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)
_ => Err(Error::UnexpectedFourCC),
}
}
@ -63,6 +63,5 @@ mod tests {
//use super::*;
#[test]
fn test() {
}
fn test() {}
}

View File

@ -1,13 +1,13 @@
use std::io::Read;
use std::collections::HashMap;
use std::io::Read;
use crate::newtypes;
use crate::file::BlockId;
use crate::newtypes;
use binrw::{binrw, BinReaderExt, BinWriterExt};
use strafesnet_common::model;
use strafesnet_common::aabb::Aabb;
use strafesnet_common::bvh::BvhNode;
use strafesnet_common::gameplay_modes;
use strafesnet_common::model;
#[derive(Debug)]
pub enum Error {
@ -174,23 +174,34 @@ struct Region{
}
//code deduplication reused in into_complete_map
fn read_region<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<(model::ModelId,model::Model)>,Error>{
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())
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>{
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>{
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)?;
@ -215,12 +226,19 @@ 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 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();
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 {
@ -229,15 +247,15 @@ impl<R:BinReaderExt> StreamableMap<R>{
resource_blocks.meshes.insert(
//generate the id from the current length
model::MeshId::new(resource_blocks.meshes.len() as u32),
resource_block_header.id
resource_block_header.id,
);
},
}
ResourceType::Texture => {
resource_blocks.textures.insert(
model::TextureId::new(resource_blocks.textures.len() as u32),
resource_block_header.id
resource_block_header.id,
);
},
}
}
}
Ok(Self {
@ -252,23 +270,36 @@ impl<R:BinReaderExt> StreamableMap<R>{
}
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));
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>{
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))?;
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))?;
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));
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
@ -279,7 +310,11 @@ impl<R:BinReaderExt> StreamableMap<R>{
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))?);
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());
@ -287,7 +322,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
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 {
@ -311,7 +346,10 @@ 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)>
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,
@ -325,10 +363,7 @@ fn collect_spacial_blocks(
models.push((model_id.get(), model));
});
let id = BlockId::new(block_headers.len() as u32 + 1);
block_headers.push(SpacialBlockHeader{
id,
extents,
});
block_headers.push(SpacialBlockHeader { id, extents });
let region = Region {
model_count,
models,
@ -339,9 +374,14 @@ fn collect_spacial_blocks(
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)?;
collect_spacial_blocks(
block_location,
block_headers,
sequential_block_data,
bvh_node,
)?;
}
}
},
strafesnet_common::bvh::RecursiveContent::Leaf(_) => panic!(), //bvh branches are 20 leaves minimum
}
}
@ -350,18 +390,30 @@ fn collect_spacial_blocks(
/// 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>{
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)|{
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 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>());
})
.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
@ -372,7 +424,12 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
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)?;
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
@ -383,7 +440,8 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
});
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)?;
binrw::BinWrite::write_le(&serializable_mesh, &mut cursor_to_data)
.map_err(Error::InvalidData)?;
block_location.push(cursor_to_data.position());
}
//textures
@ -421,9 +479,14 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
};
//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)?;
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)?;
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;

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,5 +1,5 @@
use super::common::flag;
use super::integer::{Time,Planar64,Planar64Vec3};
use super::integer::{Planar64, Planar64Vec3, Time};
#[binrw::binrw]
#[brw(little)]
@ -40,36 +40,44 @@ pub enum ContactingBehaviour{
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)=>
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),
)
}
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),
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)
}
}
}
}
@ -103,12 +111,12 @@ impl From<strafesnet_common::gameplay_attributes::IntersectingWater> for Interse
#[binrw::binrw]
#[brw(little)]
pub struct Accelerator {
pub acceleration:Planar64Vec3
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)
acceleration: strafesnet_common::integer::vec3::raw_array(self.acceleration),
}
}
}
@ -126,32 +134,34 @@ pub enum Booster{
#[brw(magic = 0u8)]
Velocity(Planar64Vec3),
#[brw(magic = 1u8)]
Energy{direction:Planar64Vec3,energy:Planar64},
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)=>
Booster::Velocity(velocity) => {
strafesnet_common::gameplay_attributes::Booster::Velocity(
strafesnet_common::integer::vec3::raw_array(velocity)
),
Booster::Energy{direction,energy}=>
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)
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)
Booster::Height(height) => strafesnet_common::gameplay_attributes::Booster::Height(
strafesnet_common::integer::Planar64::raw(height),
),
}
}
@ -159,17 +169,21 @@ impl Into<strafesnet_common::gameplay_attributes::Booster> for Booster{
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}=>
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()),
}
}
strafesnet_common::gameplay_attributes::Booster::AirTime(time) => {
Booster::AirTime(time.get())
}
strafesnet_common::gameplay_attributes::Booster::Height(height) => {
Booster::Height(height.to_raw())
}
}
}
}
@ -183,20 +197,24 @@ pub enum TrajectoryChoice{
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,
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,
strafesnet_common::gameplay_attributes::TrajectoryChoice::HighArcLongDuration => {
TrajectoryChoice::HighArcLongDuration
}
strafesnet_common::gameplay_attributes::TrajectoryChoice::LowArcShortDuration => {
TrajectoryChoice::LowArcShortDuration
}
}
}
}
@ -209,7 +227,10 @@ pub enum SetTrajectory{
#[brw(magic = 1u8)]
Height(Planar64),
#[brw(magic = 2u8)]
DotVelocity{direction:Planar64Vec3,dot:Planar64},
DotVelocity {
direction: Planar64Vec3,
dot: Planar64,
},
#[brw(magic = 3u8)]
TargetPointTime {
target_point: Planar64Vec3,
@ -227,68 +248,80 @@ pub enum SetTrajectory{
impl Into<strafesnet_common::gameplay_attributes::SetTrajectory> for SetTrajectory {
fn into(self) -> strafesnet_common::gameplay_attributes::SetTrajectory {
match self {
SetTrajectory::AirTime(time)=>
SetTrajectory::AirTime(time) => {
strafesnet_common::gameplay_attributes::SetTrajectory::AirTime(
strafesnet_common::integer::Time::raw(time)
),
SetTrajectory::Height(height)=>
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::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}=>
}
}
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{
}
}
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)=>
SetTrajectory::Velocity(velocity) => {
strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(
strafesnet_common::integer::vec3::raw_array(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{
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{
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{
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()
),
strafesnet_common::gameplay_attributes::SetTrajectory::Velocity(velocity) => {
SetTrajectory::Velocity(velocity.map(|t| t.to_raw()).to_array())
}
}
}
}
@ -344,8 +377,7 @@ impl Into<strafesnet_common::gameplay_attributes::GeneralAttributes> for General
}
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)
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);
@ -379,7 +411,10 @@ impl Into<strafesnet_common::gameplay_attributes::ContactingAttributes> for Cont
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),
header: flag(
value.contact_behaviour.is_some(),
ContactingAttributes::CONTACTING_BEHAVIOUR,
),
contact_behaviour: value.contact_behaviour.map(Into::into),
}
}
@ -395,17 +430,24 @@ pub struct IntersectingAttributes{
impl IntersectingAttributes {
const INTERSECTING_WATER: u8 = 1 << 0;
}
impl Into<strafesnet_common::gameplay_attributes::IntersectingAttributes> for IntersectingAttributes{
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{
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),
header: flag(
value.water.is_some(),
IntersectingAttributes::INTERSECTING_WATER,
),
water: value.water.map(Into::into),
}
}
@ -470,24 +512,30 @@ pub enum CollisionAttributes{
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()),
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()),
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

@ -46,7 +46,8 @@ impl TryInto<strafesnet_common::gameplay_modes::StageElement> for StageElement{
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.behaviour()
.ok_or(StageElementError::InvalidBehaviour)?,
self.jump_limit,
))
}
@ -61,8 +62,7 @@ impl From<strafesnet_common::gameplay_modes::StageElement> for StageElement{
strafesnet_common::gameplay_modes::StageElementBehaviour::Check => 4,
strafesnet_common::gameplay_modes::StageElementBehaviour::Checkpoint => 5,
};
let header=
behaviour
let header = behaviour
| flag(value.jump_limit().is_some(), StageElement::JUMP_LIMIT)
| flag(value.force(), StageElement::FORCE);
Self {
@ -92,11 +92,17 @@ impl Into<strafesnet_common::gameplay_modes::Stage> for Stage{
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)|(
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()
)
})
.collect(),
self.unordered_checkpoints
.into_iter()
.map(strafesnet_common::model::ModelId::new)
.collect(),
)
@ -112,10 +118,12 @@ impl From<strafesnet_common::gameplay_modes::Stage> for Stage{
spawn,
ordered_checkpoints_count,
unordered_checkpoints_count,
ordered_checkpoints:ordered_checkpoints.into_iter()
ordered_checkpoints: ordered_checkpoints
.into_iter()
.map(|(checkpoint_id, model_id)| (checkpoint_id.get(), model_id.get()))
.collect(),
unordered_checkpoints:unordered_checkpoints.into_iter()
unordered_checkpoints: unordered_checkpoints
.into_iter()
.map(|model_id| model_id.get())
.collect(),
}
@ -185,13 +193,26 @@ impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
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.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)?,
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)?,
))
}
}
@ -206,13 +227,13 @@ impl From<strafesnet_common::gameplay_modes::Mode> for Mode{
},
style: style.into(),
start: start.get(),
zones:zones.into_iter()
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()
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,5 +1,5 @@
use super::common::flag;
use super::integer::{Time,Ratio64,Planar64,Planar64Vec3};
use super::integer::{Planar64, Planar64Vec3, Ratio64, Time};
pub type Controls = u32;
#[derive(Debug)]
@ -60,11 +60,25 @@ impl TryInto<strafesnet_common::gameplay_style::StyleModifiers> for StyleModifie
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)?,
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)?,
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),
@ -77,8 +91,7 @@ impl TryInto<strafesnet_common::gameplay_style::StyleModifiers> for StyleModifie
}
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)
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)
@ -113,8 +126,12 @@ impl Into<strafesnet_common::gameplay_style::JumpCalculation> for JumpCalculatio
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,
JumpCalculation::BoostThenJump => {
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump
}
JumpCalculation::JumpThenBoost => {
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost
}
}
}
}
@ -122,8 +139,12 @@ impl From<strafesnet_common::gameplay_style::JumpCalculation> for JumpCalculatio
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,
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump => {
JumpCalculation::BoostThenJump
}
strafesnet_common::gameplay_style::JumpCalculation::JumpThenBoost => {
JumpCalculation::JumpThenBoost
}
}
}
}
@ -137,20 +158,36 @@ pub enum JumpImpulse{
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)),
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()),
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())
}
}
}
}
@ -164,11 +201,22 @@ pub struct ControlsActivation{
}
impl TryInto<strafesnet_common::gameplay_style::ControlsActivation> for ControlsActivation {
type Error = ControlsError;
fn try_into(self)->Result<strafesnet_common::gameplay_style::ControlsActivation,Self::Error>{
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)?,
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)?,
})
}
}
@ -210,16 +258,27 @@ impl TryInto<strafesnet_common::gameplay_style::StrafeSettings> for StrafeSettin
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)?,
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)?,
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);
let header = flag(
value.air_accel_limit.is_some(),
StrafeSettings::AIR_ACCEL_LIMIT,
);
Self {
header,
enable: value.enable.into(),
@ -238,7 +297,7 @@ pub struct PropulsionSettings{
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)
magnitude: strafesnet_common::integer::Planar64::raw(self.magnitude),
}
}
}
@ -262,10 +321,18 @@ impl JumpSettings{
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))),
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,
}
}
@ -290,8 +357,12 @@ 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)?,
impulse: self
.impulse()
.ok_or(JumpSettingsError::InvalidImpulseDiscriminant)?,
calculation: self
.calculation()
.ok_or(JumpSettingsError::InvalidCalculationDiscriminant)?,
limit_minimum: self.limit_minimum(),
})
}
@ -300,9 +371,15 @@ 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),
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,
@ -310,13 +387,8 @@ impl From<strafesnet_common::gameplay_style::JumpSettings> for JumpSettings{
strafesnet_common::gameplay_style::JumpCalculation::BoostThenJump => 2,
};
let header =
impulse_header
|(calculation_header<<2)
|((value.limit_minimum as u8)<<4);
Self{
header,
impulse,
}
impulse_header | (calculation_header << 2) | ((value.limit_minimum as u8) << 4);
Self { header, impulse }
}
}

View File

@ -1,7 +1,7 @@
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];
@ -43,13 +43,16 @@ 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 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();
})
.collect();
Self {
count: polys.len() as u32,
polys,
@ -97,7 +100,11 @@ impl From<strafesnet_common::model::IndexedGraphicsGroup> for IndexedGraphicsGro
Self {
count: value.groups.len() as u32,
render: value.render.get(),
groups:value.groups.into_iter().map(|group_id|group_id.get()).collect(),
groups: value
.groups
.into_iter()
.map(|group_id| group_id.get())
.collect(),
}
}
}
@ -112,7 +119,11 @@ 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(),
groups: value
.groups
.into_iter()
.map(|group_id| group_id.get())
.collect(),
}
}
}
@ -153,36 +164,79 @@ pub struct Mesh{
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{
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|
})
.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()
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{
})
.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(),
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(),
}
}
}
@ -199,30 +253,30 @@ impl From<strafesnet_common::model::Mesh> for Mesh{
graphics_groups: value.graphics_groups.len() as u32,
physics_groups: value.physics_groups.len() as u32,
},
unique_pos:value.unique_pos.into_iter()
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()
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()
unique_tex: value
.unique_tex
.into_iter()
.map(|tex| tex.to_array())
.collect(),
unique_color:value.unique_color.into_iter()
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(),
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(),
}
}
}
@ -240,31 +294,43 @@ impl Into<strafesnet_common::model::Model> for 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),
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(_6, _7, _8),
]),
strafesnet_common::integer::vec3::raw_xyz(_9,_a,_b)
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()
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(),

View File

@ -1,5 +1,5 @@
use strafesnet_common::aabb;
use strafesnet_common::integer::{self,vec3,Time,Planar64,Planar64Vec3};
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
@ -20,10 +20,16 @@ impl<T> std::ops::Neg for Body<T>{
}
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{
pub const fn new(
position: Planar64Vec3,
velocity: Planar64Vec3,
acceleration: Planar64Vec3,
time: Time<T>,
) -> Self {
Self {
position,
velocity,
@ -35,7 +41,9 @@ impl<T> Body<T>
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())
+ 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;
@ -46,7 +54,10 @@ impl<T> Body<T>
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
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
@ -67,10 +78,15 @@ impl<T> Body<T>
{
// 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
(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
pub fn extrapolated_velocity_ratio_dt<Num, Den, N1, T1>(
&self,
dt: integer::Ratio<Num, Den>,
) -> Planar64Vec3
where
Num: Copy,
Den: Copy,
@ -122,11 +138,14 @@ impl<T> Body<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)
write!(
f,
"p({}) v({}) a({}) t({})",
self.position, self.velocity, self.acceleration, self.time
)
}
}
@ -135,15 +154,13 @@ pub struct VirtualBody<'a,T>{
body1: &'a Body<T>,
}
impl<T> VirtualBody<'_, T>
where Time<T>:Copy,
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,
}
VirtualBody { body0, body1 }
}
pub fn extrapolated_position(&self, time: Time<T>) -> Planar64Vec3 {
self.body1.extrapolated_position(time) - self.body0.extrapolated_position(time)
@ -155,6 +172,11 @@ impl<T> VirtualBody<'_,T>
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)
Body::new(
self.extrapolated_position(time),
self.extrapolated_velocity(time),
self.acceleration(),
time,
)
}
}

View File

@ -1,6 +1,6 @@
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,
@ -23,7 +23,13 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
<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>{
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;
@ -35,8 +41,15 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
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(){
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;
@ -49,28 +62,45 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
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(){
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);
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]));
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(){
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;
@ -81,8 +111,15 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
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(){
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);
@ -91,27 +128,41 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
}
}
//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(){
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);
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>{
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())

View File

@ -34,13 +34,19 @@ pub fn read<R:Read+std::io::Seek>(input:R)->Result<DataStructure,ReadError>{
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)?)),
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)?)),
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)?
strafesnet_snf::read_map(buf)
.map_err(ReadError::StrafesNET)?
.into_complete_map()
.map_err(ReadError::StrafesNETMap)?,
)),
_ => Err(ReadError::UnknownFileFormat),
}
@ -59,7 +65,9 @@ impl std::fmt::Display for LoadError{
}
impl std::error::Error for LoadError {}
pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::CompleteMap,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)? {
@ -83,24 +91,33 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
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()))
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{
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();
@ -117,28 +134,37 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
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{
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{
textures.into_iter().map(|(texture_id, texture)| {
(
texture_id,
match texture {
strafesnet_deferred_loader::texture::Texture::ImageDDS(data) => data,
})
),
},
)
}),
);
Ok(map)
},
}
}
}

View File

@ -1,10 +1,15 @@
use crate::model_graphics::{
self, GraphicsMeshOwnedRenderConfig, GraphicsModelColor4, GraphicsModelOwned, GraphicsVertex,
IndexedGraphicsMeshOwnedRenderConfig, IndexedGraphicsMeshOwnedRenderConfigId,
};
use std::borrow::Cow;
use std::collections::{HashSet,HashMap};
use strafesnet_common::map;
use std::collections::{HashMap, HashSet};
use strafesnet_common::integer;
use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId};
use strafesnet_common::map;
use strafesnet_common::model::{
self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId,
};
use wgpu::{util::DeviceExt, AstcBlock, AstcChannel};
use crate::model_graphics::{self,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex};
struct Indices {
count: u32,
@ -12,7 +17,11 @@ struct Indices{
format: wgpu::IndexFormat,
}
impl Indices {
fn new<T:bytemuck::Pod>(device:&wgpu::Device,indices:&Vec<T>,format:wgpu::IndexFormat)->Self{
fn new<T: bytemuck::Pod>(
device: &wgpu::Device,
indices: &Vec<T>,
format: wgpu::IndexFormat,
) -> Self {
Self {
buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index"),
@ -72,7 +81,8 @@ impl GraphicsCamera{
}
pub fn world(&self, pos: glam::Vec3, angles: glam::Vec2) -> glam::Mat4 {
//f32 good enough for view matrix
glam::Mat4::from_translation(pos)*glam::Mat4::from_euler(glam::EulerRot::YXZ,angles.x,angles.y,0f32)
glam::Mat4::from_translation(pos)
* glam::Mat4::from_euler(glam::EulerRot::YXZ, angles.x, angles.y, 0f32)
}
pub fn to_uniform_data(&self, pos: glam::Vec3, angles: glam::Vec2) -> [f32; 16 * 4] {
@ -138,18 +148,29 @@ impl GraphicsState{
self.models.clear();
}
pub fn load_user_settings(&mut self, user_settings: &crate::settings::UserSettings) {
self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2();
self.camera.fov = user_settings
.calculate_fov(1.0, &self.camera.screen_size)
.as_vec2();
}
pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){
pub fn generate_models(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
map: &map::CompleteMap,
) {
//generate texture view per texture
let texture_views:HashMap<strafesnet_common::model::TextureId,wgpu::TextureView>=map.textures.iter().enumerate().filter_map(|(texture_id,texture_data)|{
let texture_views: HashMap<strafesnet_common::model::TextureId, wgpu::TextureView> = map
.textures
.iter()
.enumerate()
.filter_map(|(texture_id, texture_data)| {
let texture_id = model::TextureId::new(texture_id as u32);
let image = match ddsfile::Dds::read(std::io::Cursor::new(texture_data)) {
Ok(image) => image,
Err(e) => {
println!("Error loading texture: {e}");
return None;
},
}
};
let (mut width, mut height) = (image.get_width(), image.get_height());
@ -161,11 +182,11 @@ impl GraphicsState{
width = width / 4 * 4;
height = height / 4 * 4;
wgpu::TextureFormat::Bc7RgbaUnormSrgb
},
}
other => {
println!("unsupported texture format{:?}", other);
return None;
},
}
};
let size = wgpu::Extent3d {
@ -195,30 +216,47 @@ impl GraphicsState{
wgpu::util::TextureDataOrder::LayerMajor,
&image.data,
);
Some((texture_id,texture.create_view(&wgpu::TextureViewDescriptor{
Some((
texture_id,
texture.create_view(&wgpu::TextureViewDescriptor {
label: Some(format!("Texture{} View", texture_id.get()).as_str()),
dimension: Some(wgpu::TextureViewDimension::D2),
..wgpu::TextureViewDescriptor::default()
})))
}).collect();
}),
))
})
.collect();
let num_textures = texture_views.len();
//split groups with different textures into separate models
//the models received here are supposed to be tightly packed,i.e. no code needs to check if two models are using the same groups.
let indexed_models_len = map.models.len();
//models split into graphics_group.RenderConfigId
let mut owned_mesh_id_from_mesh_id_render_config_id:HashMap<model::MeshId,HashMap<RenderConfigId,IndexedGraphicsMeshOwnedRenderConfigId>>=HashMap::new();
let mut unique_render_config_models:Vec<IndexedGraphicsMeshOwnedRenderConfig>=Vec::with_capacity(indexed_models_len);
let mut owned_mesh_id_from_mesh_id_render_config_id: HashMap<
model::MeshId,
HashMap<RenderConfigId, IndexedGraphicsMeshOwnedRenderConfigId>,
> = HashMap::new();
let mut unique_render_config_models: Vec<IndexedGraphicsMeshOwnedRenderConfig> =
Vec::with_capacity(indexed_models_len);
for model in &map.models {
//wow
let instance = GraphicsModelOwned {
transform: model.transform.into(),
normal_transform:glam::Mat3::from_cols_array_2d(&model.transform.matrix3.to_array().map(|row|row.map(Into::into))).inverse().transpose(),
normal_transform: glam::Mat3::from_cols_array_2d(
&model
.transform
.matrix3
.to_array()
.map(|row| row.map(Into::into)),
)
.inverse()
.transpose(),
color: GraphicsModelColor4::new(model.color),
};
//get or create owned mesh map
let owned_mesh_map = owned_mesh_id_from_mesh_id_render_config_id
.entry(model.mesh).or_insert_with(||{
.entry(model.mesh)
.or_insert_with(|| {
let mut owned_mesh_map = HashMap::new();
//add mesh if renderid never before seen for this model
//add instance
@ -228,30 +266,58 @@ impl GraphicsState{
for graphics_group in mesh.graphics_groups.iter() {
//get or create owned mesh
let owned_mesh_id = owned_mesh_map
.entry(graphics_group.render).or_insert_with(||{
.entry(graphics_group.render)
.or_insert_with(|| {
//create
let owned_mesh_id=IndexedGraphicsMeshOwnedRenderConfigId::new(unique_render_config_models.len() as u32);
unique_render_config_models.push(IndexedGraphicsMeshOwnedRenderConfig{
unique_pos:mesh.unique_pos.iter().map(|v|v.to_array().map(Into::into)).collect(),
unique_tex:mesh.unique_tex.iter().map(|v|*v.as_ref()).collect(),
unique_normal:mesh.unique_normal.iter().map(|v|v.to_array().map(Into::into)).collect(),
unique_color:mesh.unique_color.iter().map(|v|*v.as_ref()).collect(),
let owned_mesh_id = IndexedGraphicsMeshOwnedRenderConfigId::new(
unique_render_config_models.len() as u32,
);
unique_render_config_models.push(
IndexedGraphicsMeshOwnedRenderConfig {
unique_pos: mesh
.unique_pos
.iter()
.map(|v| v.to_array().map(Into::into))
.collect(),
unique_tex: mesh
.unique_tex
.iter()
.map(|v| *v.as_ref())
.collect(),
unique_normal: mesh
.unique_normal
.iter()
.map(|v| v.to_array().map(Into::into))
.collect(),
unique_color: mesh
.unique_color
.iter()
.map(|v| *v.as_ref())
.collect(),
unique_vertices: mesh.unique_vertices.clone(),
render_config: graphics_group.render,
polys:model::PolygonGroup::PolygonList(model::PolygonList::new(Vec::new())),
polys: model::PolygonGroup::PolygonList(
model::PolygonList::new(Vec::new()),
),
instances: Vec::new(),
});
},
);
owned_mesh_id
});
let owned_mesh=unique_render_config_models.get_mut(owned_mesh_id.get() as usize).unwrap();
let owned_mesh = unique_render_config_models
.get_mut(owned_mesh_id.get() as usize)
.unwrap();
match &mut owned_mesh.polys {
model::PolygonGroup::PolygonList(polygon_list)=>polygon_list.extend(
graphics_group.groups.iter().flat_map(|polygon_group_id|{
mesh.polygon_groups[polygon_group_id.get() as usize].polys()
model::PolygonGroup::PolygonList(polygon_list) => polygon_list
.extend(
graphics_group
.groups
.iter()
.flat_map(|polygon_group_id| {
mesh.polygon_groups[polygon_group_id.get() as usize]
.polys()
})
.map(|vertex_id_slice|
vertex_id_slice.to_vec()
)
.map(|vertex_id_slice| vertex_id_slice.to_vec()),
),
}
}
@ -259,7 +325,9 @@ impl GraphicsState{
owned_mesh_map
});
for owned_mesh_id in owned_mesh_map.values() {
let owned_mesh=unique_render_config_models.get_mut(owned_mesh_id.get() as usize).unwrap();
let owned_mesh = unique_render_config_models
.get_mut(owned_mesh_id.get() as usize)
.unwrap();
let render_config = &map.render_configs[owned_mesh.render_config.get() as usize];
if model.color.w == 0.0 && render_config.texture.is_none() {
continue;
@ -324,60 +392,91 @@ impl GraphicsState{
let model = &unique_render_config_models[model_id];
let instance = &model.instances[instance_id];
//just hash word slices LOL
let map_pos_id:Vec<PositionId>=model.unique_pos.iter().map(|untransformed_pos|{
let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array();
let map_pos_id: Vec<PositionId> = model
.unique_pos
.iter()
.map(|untransformed_pos| {
let pos = instance
.transform
.transform_point3(glam::Vec3::from_array(
untransformed_pos.clone(),
))
.to_array();
let h = bytemuck::cast::<[f32; 3], [u32; 3]>(pos);
PositionId::new(*pos_id_from.entry(h).or_insert_with(|| {
let pos_id = unique_pos.len();
unique_pos.push(pos);
pos_id
}) as u32)
}).collect();
let map_tex_id:Vec<TextureCoordinateId>=model.unique_tex.iter().map(|&tex|{
})
.collect();
let map_tex_id: Vec<TextureCoordinateId> = model
.unique_tex
.iter()
.map(|&tex| {
let h = bytemuck::cast::<[f32; 2], [u32; 2]>(tex);
TextureCoordinateId::new(*tex_id_from.entry(h).or_insert_with(||{
TextureCoordinateId::new(*tex_id_from.entry(h).or_insert_with(
|| {
let tex_id = unique_tex.len();
unique_tex.push(tex);
tex_id
}) as u32)
}).collect();
let map_normal_id:Vec<NormalId>=model.unique_normal.iter().map(|untransformed_normal|{
let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array();
},
) as u32)
})
.collect();
let map_normal_id: Vec<NormalId> = model
.unique_normal
.iter()
.map(|untransformed_normal| {
let normal = (instance.normal_transform
* glam::Vec3::from_array(untransformed_normal.clone()))
.to_array();
let h = bytemuck::cast::<[f32; 3], [u32; 3]>(normal);
NormalId::new(*normal_id_from.entry(h).or_insert_with(|| {
let normal_id = unique_normal.len();
unique_normal.push(normal);
normal_id
}) as u32)
}).collect();
let map_color_id:Vec<ColorId>=model.unique_color.iter().map(|&color|{
})
.collect();
let map_color_id: Vec<ColorId> = model
.unique_color
.iter()
.map(|&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(color);
color_id
}) as u32)
}).collect();
})
.collect();
//map the indexed vertices onto new indices
//creating the vertex map is slightly different because the vertices are directly hashable
let map_vertex_id:Vec<VertexId>=model.unique_vertices.iter().map(|unmapped_vertex|{
let map_vertex_id: Vec<VertexId> = model
.unique_vertices
.iter()
.map(|unmapped_vertex| {
let vertex = model::IndexedVertex {
pos: map_pos_id[unmapped_vertex.pos.get() as usize],
tex: map_tex_id[unmapped_vertex.tex.get() as usize],
normal: map_normal_id[unmapped_vertex.normal.get() as usize],
color: map_color_id[unmapped_vertex.color.get() as usize],
};
VertexId::new(*vertex_id_from.entry(vertex.clone()).or_insert_with(||{
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)
}).collect();
polys.extend(model.polys.polys().map(|poly|
poly.iter().map(|vertex_id|
map_vertex_id[vertex_id.get() as usize]
).collect()
));
},
) as u32)
})
.collect();
polys.extend(model.polys.polys().map(|poly| {
poly.iter()
.map(|vertex_id| map_vertex_id[vertex_id.get() as usize])
.collect()
}));
}
//push model into dedup
deduplicated_models.push(IndexedGraphicsMeshOwnedRenderConfig {
@ -391,7 +490,7 @@ impl GraphicsState{
instances: vec![GraphicsModelOwned {
transform: glam::Mat4::IDENTITY,
normal_transform: glam::Mat3::IDENTITY,
color
color,
}],
});
}
@ -406,14 +505,16 @@ impl GraphicsState{
//de-index models
let deduplicated_models_len = deduplicated_models.len();
let models:Vec<GraphicsMeshOwnedRenderConfig>=deduplicated_models.into_iter().map(|model|{
let models: Vec<GraphicsMeshOwnedRenderConfig> = deduplicated_models
.into_iter()
.map(|model| {
let mut vertices = Vec::new();
let mut index_from_vertex = HashMap::new(); //::<IndexedVertex,usize>
//this mut be combined in a more complex way if the models use different render patterns per group
let mut indices = Vec::new();
for poly in model.polys.polys() {
let mut poly_vertices=poly.iter()
.map(|&vertex_index|*index_from_vertex.entry(vertex_index).or_insert_with(||{
let mut poly_vertices = poly.iter().map(|&vertex_index| {
*index_from_vertex.entry(vertex_index).or_insert_with(|| {
let i = vertices.len();
let vertex = &model.unique_vertices[vertex_index.get() as usize];
vertices.push(GraphicsVertex {
@ -423,7 +524,8 @@ impl GraphicsState{
color: model.unique_color[vertex.color.get() as usize],
});
i
}));
})
});
let a = poly_vertices.next().unwrap();
let mut b = poly_vertices.next().unwrap();
@ -438,18 +540,30 @@ impl GraphicsState{
indices: if (u32::MAX as usize) < vertices.len() {
panic!("Model has too many vertices!")
} else if (u16::MAX as usize) < vertices.len() {
model_graphics::Indices::U32(indices.into_iter().map(|vertex_idx|vertex_idx as u32).collect())
model_graphics::Indices::U32(
indices
.into_iter()
.map(|vertex_idx| vertex_idx as u32)
.collect(),
)
} else {
model_graphics::Indices::U16(indices.into_iter().map(|vertex_idx|vertex_idx as u16).collect())
model_graphics::Indices::U16(
indices
.into_iter()
.map(|vertex_idx| vertex_idx as u16)
.collect(),
)
},
vertices,
render_config: model.render_config,
}
}).collect();
})
.collect();
//.into_iter() the modeldata vec so entities can be /moved/ to models.entities
let mut model_count = 0;
let mut instance_count = 0;
let uniform_buffer_binding_size=crate::setup::required_limits().max_uniform_buffer_binding_size as usize;
let uniform_buffer_binding_size =
crate::setup::required_limits().max_uniform_buffer_binding_size as usize;
let chunk_size = uniform_buffer_binding_size / MODEL_BUFFER_SIZE_BYTES;
self.models.reserve(models.len());
for model in models.into_iter() {
@ -465,9 +579,10 @@ impl GraphicsState{
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let render_config = &map.render_configs[model.render_config.get() as usize];
let texture_view=render_config.texture.and_then(|texture_id|
texture_views.get(&texture_id)
).unwrap_or(&self.temp_squid_texture_view);
let texture_view = render_config
.texture
.and_then(|texture_id| texture_views.get(&texture_id))
.unwrap_or(&self.temp_squid_texture_view);
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.bind_group_layouts.model,
entries: &[
@ -496,8 +611,12 @@ impl GraphicsState{
instance_count: instances_chunk.len() as u32,
vertex_buf,
indices: match &model.indices {
model_graphics::Indices::U32(indices)=>Indices::new(device,indices,wgpu::IndexFormat::Uint32),
model_graphics::Indices::U16(indices)=>Indices::new(device,indices,wgpu::IndexFormat::Uint16),
model_graphics::Indices::U32(indices) => {
Indices::new(device, indices, wgpu::IndexFormat::Uint32)
}
model_graphics::Indices::U16(indices) => {
Indices::new(device, indices, wgpu::IndexFormat::Uint16)
}
},
bind_group,
});
@ -516,10 +635,10 @@ impl GraphicsState{
queue: &wgpu::Queue,
config: &wgpu::SurfaceConfiguration,
) -> Self {
let camera_bind_group_layout=device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor{
let camera_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries:&[
wgpu::BindGroupLayoutEntry{
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
@ -528,10 +647,10 @@ impl GraphicsState{
min_binding_size: None,
},
count: None,
},
],
}],
});
let skybox_texture_bind_group_layout=device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor{
let skybox_texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Skybox Texture Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
@ -552,7 +671,8 @@ impl GraphicsState{
},
],
});
let model_bind_group_layout=device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor{
let model_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Model Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
@ -616,7 +736,8 @@ impl GraphicsState{
let device_features = device.features();
let skybox_texture_view = {
let skybox_format=if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ASTC){
let skybox_format =
if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ASTC) {
println!("Using ASTC");
wgpu::TextureFormat::Astc {
block: AstcBlock::B4x4,
@ -722,7 +843,8 @@ impl GraphicsState{
})
};
let model_pipeline_layout=device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor{
let model_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[
&camera_bind_group_layout,
@ -733,10 +855,7 @@ impl GraphicsState{
});
let sky_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts:&[
&camera_bind_group_layout,
&skybox_texture_bind_group_layout,
],
bind_group_layouts: &[&camera_bind_group_layout, &skybox_texture_bind_group_layout],
push_constant_ranges: &[],
});
@ -816,12 +935,10 @@ impl GraphicsState{
});
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &camera_bind_group_layout,
entries:&[
wgpu::BindGroupEntry{
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: camera_buf.as_entire_binding(),
},
],
}],
label: Some("Camera"),
});
@ -845,7 +962,7 @@ impl GraphicsState{
Self {
pipelines: GraphicsPipelines {
skybox: sky_pipeline,
model:model_pipeline
model: model_pipeline,
},
bind_groups: GraphicsBindGroups {
camera: camera_bind_group,
@ -856,8 +973,12 @@ impl GraphicsState{
models: Vec::new(),
depth_view,
staging_belt: wgpu::util::StagingBelt::new(0x100),
bind_group_layouts:GraphicsBindGroupLayouts{model:model_bind_group_layout},
samplers:GraphicsSamplers{repeat:repeat_sampler},
bind_group_layouts: GraphicsBindGroupLayouts {
model: model_bind_group_layout,
},
samplers: GraphicsSamplers {
repeat: repeat_sampler,
},
temp_squid_texture_view: squid_texture_view,
}
}
@ -880,12 +1001,18 @@ impl GraphicsState{
) {
//TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input
let mut encoder=device.create_command_encoder(&wgpu::CommandEncoderDescriptor{label:None});
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
// update rotation
let camera_uniforms = self.camera.to_uniform_data(
frame_state.body.extrapolated_position(frame_state.time).map(Into::<f32>::into).to_array().into(),
frame_state.camera.simulate_move_angles(glam::IVec2::ZERO)
frame_state
.body
.extrapolated_position(frame_state.time)
.map(Into::<f32>::into)
.to_array()
.into(),
frame_state.camera.simulate_move_angles(glam::IVec2::ZERO),
);
self.staging_belt
.write_buffer(

View File

@ -27,7 +27,7 @@ pub fn new<'a>(
Instruction::ChangeMap(map) => {
graphics.clear();
graphics.generate_models(&device, &queue, &map);
},
}
Instruction::Resize(size, user_settings) => {
resize = Some((size, user_settings));
}

View File

@ -1,18 +1,18 @@
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"));

View File

@ -1,8 +1,8 @@
use std::borrow::{Borrow, Cow};
use std::collections::{HashSet,HashMap};
use std::collections::{HashMap, HashSet};
use strafesnet_common::integer::vec3::Vector3;
use strafesnet_common::model::{self,MeshId,PolygonIter};
use strafesnet_common::integer::{self, vec3, Fixed, Planar64, Planar64Vec3, Ratio};
use strafesnet_common::model::{self, MeshId, PolygonIter};
use strafesnet_common::physics::Time;
type Body = crate::body::Body<strafesnet_common::physics::TimeInner>;
@ -80,13 +80,20 @@ pub trait MeshQuery{
}
fn directed_edge_n(&self, directed_edge_id: Self::Edge) -> Planar64Vec3 {
let verts = self.edge_verts(directed_edge_id.as_undirected());
(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1)
(self.vert(verts[1].clone()) - self.vert(verts[0].clone()))
* ((directed_edge_id.parity() as i64) * 2 - 1)
}
fn vert(&self, vert_id: Self::Vert) -> Planar64Vec3;
fn face_nd(&self, face_id: Self::Face) -> (Self::Normal, Self::Offset);
fn face_edges(&self, face_id: Self::Face) -> Cow<Vec<Self::Edge>>;
fn edge_faces(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Face;2]>;
fn edge_verts(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Vert;2]>;
fn edge_faces(
&self,
edge_id: <Self::Edge as DirectedEdge>::UndirectedEdge,
) -> Cow<[Self::Face; 2]>;
fn edge_verts(
&self,
edge_id: <Self::Edge as DirectedEdge>::UndirectedEdge,
) -> Cow<[Self::Vert; 2]>;
fn vert_edges(&self, vert_id: Self::Vert) -> Cow<Vec<Self::Edge>>;
fn vert_faces(&self, vert_id: Self::Vert) -> Cow<Vec<Self::Face>>;
}
@ -147,12 +154,30 @@ impl PhysicsMesh{
//go go gadget debug print mesh
let data = PhysicsMeshData {
faces: vec![
Face{normal:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
Face {
normal: vec3::raw_xyz(4294967296, 0, 0),
dot: Planar64::raw(4294967296),
},
Face {
normal: vec3::raw_xyz(0, 4294967296, 0),
dot: Planar64::raw(4294967296),
},
Face {
normal: vec3::raw_xyz(0, 0, 4294967296),
dot: Planar64::raw(4294967296),
},
Face {
normal: vec3::raw_xyz(-4294967296, 0, 0),
dot: Planar64::raw(4294967296),
},
Face {
normal: vec3::raw_xyz(0, -4294967296, 0),
dot: Planar64::raw(4294967296),
},
Face {
normal: vec3::raw_xyz(0, 0, -4294967296),
dot: Planar64::raw(4294967296),
},
],
verts: vec![
Vert(vec3::raw_xyz(4294967296, -4294967296, -4294967296)),
@ -162,44 +187,226 @@ impl PhysicsMesh{
Vert(vec3::raw_xyz(-4294967296, 4294967296, -4294967296)),
Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296, -4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
]
Vert(vec3::raw_xyz(-4294967296, -4294967296, -4294967296)),
],
};
let mesh_topology = PhysicsMeshTopology {
faces: (0..data.faces.len() as u32).map(MeshFaceId::new).collect(),
verts: (0..data.verts.len() as u32).map(MeshVertId::new).collect(),
face_topology: vec![
FaceRefs{edges:vec![SubmeshDirectedEdgeId((9223372036854775808u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775809u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775810u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(3)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId((9223372036854775812u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775813u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId(1)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId((9223372036854775814u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775816u64-(1<<63)+(1<<31)) as u32)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId((9223372036854775817u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(10)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId((9223372036854775815u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775818u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId((9223372036854775811u64-(1<<63)+(1<<31)) as u32)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(9)]}
FaceRefs {
edges: vec![
SubmeshDirectedEdgeId(
(9223372036854775808u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775809u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775810u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(3),
],
},
FaceRefs {
edges: vec![
SubmeshDirectedEdgeId(
(9223372036854775812u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775813u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(6),
SubmeshDirectedEdgeId(1),
],
},
FaceRefs {
edges: vec![
SubmeshDirectedEdgeId(7),
SubmeshDirectedEdgeId(2),
SubmeshDirectedEdgeId(
(9223372036854775814u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775816u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
FaceRefs {
edges: vec![
SubmeshDirectedEdgeId(8),
SubmeshDirectedEdgeId(5),
SubmeshDirectedEdgeId(
(9223372036854775817u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(10),
],
},
FaceRefs {
edges: vec![
SubmeshDirectedEdgeId(
(9223372036854775815u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775818u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(11),
SubmeshDirectedEdgeId(
(9223372036854775811u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
FaceRefs {
edges: vec![
SubmeshDirectedEdgeId(4),
SubmeshDirectedEdgeId(0),
SubmeshDirectedEdgeId(
(9223372036854775819u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(9),
],
},
],
edge_topology: vec![
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(5)],verts:[SubmeshVertId(0),SubmeshVertId(1)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(1)],verts:[SubmeshVertId(1),SubmeshVertId(2)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(2)],verts:[SubmeshVertId(2),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(0)],verts:[SubmeshVertId(0),SubmeshVertId(3)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(5)],verts:[SubmeshVertId(1),SubmeshVertId(4)]},
EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(3)],verts:[SubmeshVertId(4),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(1)],verts:[SubmeshVertId(2),SubmeshVertId(5)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(2)],verts:[SubmeshVertId(3),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(3)],verts:[SubmeshVertId(5),SubmeshVertId(6)]},
EdgeRefs{faces:[SubmeshFaceId(3),SubmeshFaceId(5)],verts:[SubmeshVertId(4),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(5),SubmeshFaceId(4)],verts:[SubmeshVertId(0),SubmeshVertId(7)]}
EdgeRefs {
faces: [SubmeshFaceId(0), SubmeshFaceId(5)],
verts: [SubmeshVertId(0), SubmeshVertId(1)],
},
EdgeRefs {
faces: [SubmeshFaceId(0), SubmeshFaceId(1)],
verts: [SubmeshVertId(1), SubmeshVertId(2)],
},
EdgeRefs {
faces: [SubmeshFaceId(0), SubmeshFaceId(2)],
verts: [SubmeshVertId(2), SubmeshVertId(3)],
},
EdgeRefs {
faces: [SubmeshFaceId(4), SubmeshFaceId(0)],
verts: [SubmeshVertId(0), SubmeshVertId(3)],
},
EdgeRefs {
faces: [SubmeshFaceId(1), SubmeshFaceId(5)],
verts: [SubmeshVertId(1), SubmeshVertId(4)],
},
EdgeRefs {
faces: [SubmeshFaceId(1), SubmeshFaceId(3)],
verts: [SubmeshVertId(4), SubmeshVertId(5)],
},
EdgeRefs {
faces: [SubmeshFaceId(2), SubmeshFaceId(1)],
verts: [SubmeshVertId(2), SubmeshVertId(5)],
},
EdgeRefs {
faces: [SubmeshFaceId(4), SubmeshFaceId(2)],
verts: [SubmeshVertId(3), SubmeshVertId(6)],
},
EdgeRefs {
faces: [SubmeshFaceId(2), SubmeshFaceId(3)],
verts: [SubmeshVertId(5), SubmeshVertId(6)],
},
EdgeRefs {
faces: [SubmeshFaceId(3), SubmeshFaceId(5)],
verts: [SubmeshVertId(4), SubmeshVertId(7)],
},
EdgeRefs {
faces: [SubmeshFaceId(4), SubmeshFaceId(3)],
verts: [SubmeshVertId(6), SubmeshVertId(7)],
},
EdgeRefs {
faces: [SubmeshFaceId(5), SubmeshFaceId(4)],
verts: [SubmeshVertId(0), SubmeshVertId(7)],
},
],
vert_topology: vec![
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(4),SubmeshFaceId(5)],edges:vec![SubmeshDirectedEdgeId((9223372036854775811u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775808u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(5),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId((9223372036854775812u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775809u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(1),SubmeshDirectedEdgeId((9223372036854775810u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775814u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(4)],edges:vec![SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId(3),SubmeshDirectedEdgeId((9223372036854775815u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(3),SubmeshFaceId(5),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId((9223372036854775817u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775813u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId((9223372036854775816u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(4)],edges:vec![SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId((9223372036854775818u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(4),SubmeshFaceId(3),SubmeshFaceId(5)],edges:vec![SubmeshDirectedEdgeId(10),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId(9)]}
]
VertRefs {
faces: vec![SubmeshFaceId(0), SubmeshFaceId(4), SubmeshFaceId(5)],
edges: vec![
SubmeshDirectedEdgeId(
(9223372036854775811u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775819u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775808u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(0), SubmeshFaceId(5), SubmeshFaceId(1)],
edges: vec![
SubmeshDirectedEdgeId(
(9223372036854775812u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(0),
SubmeshDirectedEdgeId(
(9223372036854775809u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(0), SubmeshFaceId(2), SubmeshFaceId(1)],
edges: vec![
SubmeshDirectedEdgeId(1),
SubmeshDirectedEdgeId(
(9223372036854775810u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775814u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(0), SubmeshFaceId(2), SubmeshFaceId(4)],
edges: vec![
SubmeshDirectedEdgeId(2),
SubmeshDirectedEdgeId(3),
SubmeshDirectedEdgeId(
(9223372036854775815u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(3), SubmeshFaceId(5), SubmeshFaceId(1)],
edges: vec![
SubmeshDirectedEdgeId(4),
SubmeshDirectedEdgeId(
(9223372036854775817u64 - (1 << 63) + (1 << 31)) as u32,
),
SubmeshDirectedEdgeId(
(9223372036854775813u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(2), SubmeshFaceId(3), SubmeshFaceId(1)],
edges: vec![
SubmeshDirectedEdgeId(5),
SubmeshDirectedEdgeId(6),
SubmeshDirectedEdgeId(
(9223372036854775816u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(2), SubmeshFaceId(3), SubmeshFaceId(4)],
edges: vec![
SubmeshDirectedEdgeId(7),
SubmeshDirectedEdgeId(8),
SubmeshDirectedEdgeId(
(9223372036854775818u64 - (1 << 63) + (1 << 31)) as u32,
),
],
},
VertRefs {
faces: vec![SubmeshFaceId(4), SubmeshFaceId(3), SubmeshFaceId(5)],
edges: vec![
SubmeshDirectedEdgeId(10),
SubmeshDirectedEdgeId(11),
SubmeshDirectedEdgeId(9),
],
},
],
};
Self {
data,
@ -255,11 +462,14 @@ struct VertRefGuy{
struct EdgeRefVerts([SubmeshVertId; 2]);
impl EdgeRefVerts {
const fn new(v0: SubmeshVertId, v1: SubmeshVertId) -> (Self, bool) {
(if v0.0<v1.0{
(
if v0.0 < v1.0 {
Self([v0, v1])
} else {
Self([v1, v0])
},v0.0<v1.0)
},
v0.0 < v1.0,
)
}
}
struct EdgeRefFaces([SubmeshFaceId; 2]);
@ -283,11 +493,15 @@ impl EdgePool{
edge_id
} else {
let edge_id = SubmeshEdgeId::new(self.edge_guys.len() as u32);
self.edge_guys.push((edge_ref_verts.clone(),EdgeRefFaces::new()));
self.edge_guys
.push((edge_ref_verts.clone(), EdgeRefFaces::new()));
self.edge_id_from_guy.insert(edge_ref_verts, edge_id);
edge_id
};
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id.get() as usize)}.1,edge_id)
(
&mut unsafe { self.edge_guys.get_unchecked_mut(edge_id.get() as usize) }.1,
edge_id,
)
}
}
@ -316,14 +530,19 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
//the same face is not allowed to be in multiple polygon groups
let mut faces = Vec::new();
let mut face_id_from_face = HashMap::new();
let mut mesh_topologies:Vec<PhysicsMeshTopology>=mesh.physics_groups.iter().map(|physics_group|{
let mut mesh_topologies: Vec<PhysicsMeshTopology> = mesh
.physics_groups
.iter()
.map(|physics_group| {
//construct submesh
let mut submesh_faces = Vec::new(); //these contain a map from submeshId->meshId
let mut submesh_verts = Vec::new();
let mut submesh_vert_id_from_mesh_vert_id=HashMap::<MeshVertId,SubmeshVertId>::new();
let mut submesh_vert_id_from_mesh_vert_id =
HashMap::<MeshVertId, SubmeshVertId>::new();
//lazy closure
let mut get_submesh_vert_id = |vert_id: MeshVertId| {
if let Some(&submesh_vert_id)=submesh_vert_id_from_mesh_vert_id.get(&vert_id){
if let Some(&submesh_vert_id) = submesh_vert_id_from_mesh_vert_id.get(&vert_id)
{
submesh_vert_id
} else {
let submesh_vert_id = SubmeshVertId::new(submesh_verts.len() as u32);
@ -342,9 +561,19 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
//one face per poly
let mut normal = Vector3::new([Fixed::ZERO, Fixed::ZERO, Fixed::ZERO]);
let len = poly_vertices.len();
let face_edges=poly_vertices.into_iter().enumerate().map(|(i,vert_id)|{
let vert0_id=MeshVertId::new(mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32);
let vert1_id=MeshVertId::new(mesh.unique_vertices[poly_vertices[(i+1)%len].get() as usize].pos.get() as u32);
let face_edges = poly_vertices
.into_iter()
.enumerate()
.map(|(i, vert_id)| {
let vert0_id = MeshVertId::new(
mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32,
);
let vert1_id = MeshVertId::new(
mesh.unique_vertices
[poly_vertices[(i + 1) % len].get() as usize]
.pos
.get() as u32,
);
//index submesh verts
let submesh_vert0_id = get_submesh_vert_id(vert0_id);
let submesh_vert1_id = get_submesh_vert_id(vert1_id);
@ -357,25 +586,38 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
(v0.x - v1.x) * (v0.y + v1.y),
]);
//get/create edge and push face into it
let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(submesh_vert0_id,submesh_vert1_id);
let (edge_ref_verts, is_sorted) =
EdgeRefVerts::new(submesh_vert0_id, submesh_vert1_id);
let (edge_ref_faces, edge_id) = edge_pool.push(edge_ref_verts);
//polygon vertices as assumed to be listed clockwise
//populate the edge face on the left or right depending on how the edge vertices got sorted
edge_ref_faces.push(!is_sorted as usize, submesh_face_id);
//index edges & face into vertices
{
let vert_ref_guy=unsafe{vert_ref_guys.get_unchecked_mut(submesh_vert0_id.get() as usize)};
let vert_ref_guy = unsafe {
vert_ref_guys
.get_unchecked_mut(submesh_vert0_id.get() as usize)
};
vert_ref_guy.edges.insert(edge_id.as_directed(is_sorted));
vert_ref_guy.faces.insert(submesh_face_id);
unsafe{vert_ref_guys.get_unchecked_mut(submesh_vert1_id.get() as usize)}.edges.insert(edge_id.as_directed(!is_sorted));
unsafe {
vert_ref_guys
.get_unchecked_mut(submesh_vert1_id.get() as usize)
}
.edges
.insert(edge_id.as_directed(!is_sorted));
}
//return directed_edge_id
edge_id.as_directed(is_sorted)
}).collect();
})
.collect();
let mut dot = Fixed::ZERO;
// find the average dot
for &v in poly_vertices {
dot+=normal.dot(mesh.unique_pos[mesh.unique_vertices[v.get() as usize].pos.get() as usize]);
dot += normal.dot(
mesh.unique_pos
[mesh.unique_vertices[v.get() as usize].pos.get() as usize],
);
}
//assume face hash is stable, and there are no flush faces...
let face = Face {
@ -398,26 +640,35 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
PhysicsMeshTopology {
faces: submesh_faces,
verts: submesh_verts,
face_topology:face_ref_guys.into_iter().map(|face_ref_guy|{
FaceRefs{edges:face_ref_guy.0}
}).collect(),
edge_topology:edge_pool.edge_guys.into_iter().map(|(edge_ref_verts,edge_ref_faces)|
EdgeRefs{faces:edge_ref_faces.0,verts:edge_ref_verts.0}
).collect(),
vert_topology:vert_ref_guys.into_iter().map(|vert_ref_guy|
VertRefs{
face_topology: face_ref_guys
.into_iter()
.map(|face_ref_guy| FaceRefs {
edges: face_ref_guy.0,
})
.collect(),
edge_topology: edge_pool
.edge_guys
.into_iter()
.map(|(edge_ref_verts, edge_ref_faces)| EdgeRefs {
faces: edge_ref_faces.0,
verts: edge_ref_verts.0,
})
.collect(),
vert_topology: vert_ref_guys
.into_iter()
.map(|vert_ref_guy| VertRefs {
edges: vert_ref_guy.edges.into_iter().collect(),
faces: vert_ref_guy.faces.into_iter().collect(),
})
.collect(),
}
).collect(),
}
}).collect();
})
.collect();
Ok(Self {
data:PhysicsMeshData{
faces,
verts,
},
complete_mesh:mesh_topologies.pop().ok_or(PhysicsMeshError::NoPhysicsGroups)?,
data: PhysicsMeshData { faces, verts },
complete_mesh: mesh_topologies
.pop()
.ok_or(PhysicsMeshError::NoPhysicsGroups)?,
submeshes: mesh_topologies,
})
}
@ -435,7 +686,10 @@ impl MeshQuery for PhysicsMeshView<'_>{
type Offset = Planar64;
fn face_nd(&self, face_id: SubmeshFaceId) -> (Planar64Vec3, Planar64) {
let face_idx = self.topology.faces[face_id.get() as usize].get() as usize;
(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
(
self.data.faces[face_idx].normal,
self.data.faces[face_idx].dot,
)
}
//ideally I never calculate the vertex position, but I have to for the graphical meshes...
fn vert(&self, vert_id: SubmeshVertId) -> Planar64Vec3 {
@ -483,24 +737,33 @@ impl TransformedMesh<'_>{
view: PhysicsMeshView<'a>,
transform: &'a PhysicsMeshTransform,
) -> TransformedMesh<'a> {
TransformedMesh{
view,
transform,
}
TransformedMesh { view, transform }
}
pub fn verts<'a>(&'a self) -> impl Iterator<Item = vec3::Vector3<Fixed<2, 64>>> + 'a {
self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos))
self.view
.data
.verts
.iter()
.map(|&Vert(pos)| self.transform.vertex.transform_point3(pos))
}
fn farthest_vert(&self, dir: Planar64Vec3) -> SubmeshVertId {
//this happens to be well-defined. there are no virtual virtices
SubmeshVertId::new(
self.view.topology.verts.iter()
self.view
.topology
.verts
.iter()
.enumerate()
.max_by_key(|(_,&vert_id)|
dir.dot(self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0))
.max_by_key(|(_, &vert_id)| {
dir.dot(
self.transform
.vertex
.transform_point3(self.view.data.verts[vert_id.get() as usize].0),
)
})
//assume there is more than zero vertices.
.unwrap().0 as u32
.unwrap()
.0 as u32,
)
}
}
@ -513,11 +776,15 @@ impl MeshQuery for TransformedMesh<'_>{
fn face_nd(&self, face_id: SubmeshFaceId) -> (Self::Normal, Self::Offset) {
let (n, d) = self.view.face_nd(face_id);
let transformed_n = self.transform.normal * n;
let transformed_d=d*self.transform.det+transformed_n.dot(self.transform.vertex.translation);
let transformed_d =
d * self.transform.det + transformed_n.dot(self.transform.vertex.translation);
(transformed_n, transformed_d)
}
fn vert(&self, vert_id: SubmeshVertId) -> Planar64Vec3 {
self.transform.vertex.transform_point3(self.view.vert(vert_id)).fix_1()
self.transform
.vertex
.transform_point3(self.view.vert(vert_id))
.fix_1()
}
#[inline]
fn face_edges(&self, face_id: SubmeshFaceId) -> Cow<Vec<SubmeshDirectedEdgeId>> {
@ -559,8 +826,12 @@ impl UndirectedEdge for MinkowskiEdge{
type DirectedEdge = MinkowskiDirectedEdge;
fn as_directed(&self, parity: bool) -> Self::DirectedEdge {
match self {
MinkowskiEdge::VertEdge(v0,e1)=>MinkowskiDirectedEdge::VertEdge(*v0,e1.as_directed(parity)),
MinkowskiEdge::EdgeVert(e0,v1)=>MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),*v1),
MinkowskiEdge::VertEdge(v0, e1) => {
MinkowskiDirectedEdge::VertEdge(*v0, e1.as_directed(parity))
}
MinkowskiEdge::EdgeVert(e0, v1) => {
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity), *v1)
}
}
}
}
@ -574,14 +845,19 @@ impl DirectedEdge for MinkowskiDirectedEdge{
type UndirectedEdge = MinkowskiEdge;
fn as_undirected(&self) -> Self::UndirectedEdge {
match self {
MinkowskiDirectedEdge::VertEdge(v0,e1)=>MinkowskiEdge::VertEdge(*v0,e1.as_undirected()),
MinkowskiDirectedEdge::EdgeVert(e0,v1)=>MinkowskiEdge::EdgeVert(e0.as_undirected(),*v1),
MinkowskiDirectedEdge::VertEdge(v0, e1) => {
MinkowskiEdge::VertEdge(*v0, e1.as_undirected())
}
MinkowskiDirectedEdge::EdgeVert(e0, v1) => {
MinkowskiEdge::EdgeVert(e0.as_undirected(), *v1)
}
}
}
fn parity(&self) -> bool {
match self {
MinkowskiDirectedEdge::VertEdge(_,e)
|MinkowskiDirectedEdge::EdgeVert(e,_)=>e.parity(),
MinkowskiDirectedEdge::VertEdge(_, e) | MinkowskiDirectedEdge::EdgeVert(e, _) => {
e.parity()
}
}
}
}
@ -614,16 +890,25 @@ enum EV{
pub type GigaTime = Ratio<Fixed<4, 128>, Fixed<4, 128>>;
impl MinkowskiMesh<'_> {
pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{
MinkowskiMesh{
mesh0,
mesh1,
}
pub fn minkowski_sum<'a>(
mesh0: TransformedMesh<'a>,
mesh1: TransformedMesh<'a>,
) -> MinkowskiMesh<'a> {
MinkowskiMesh { mesh0, mesh1 }
}
fn farthest_vert(&self, dir: Planar64Vec3) -> MinkowskiVert {
MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir))
MinkowskiVert::VertVert(
self.mesh0.farthest_vert(dir),
self.mesh1.farthest_vert(-dir),
)
}
fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{
fn next_transition_vert(
&self,
vert_id: MinkowskiVert,
best_distance_squared: &mut Fixed<2, 64>,
infinity_dir: Planar64Vec3,
point: Planar64Vec3,
) -> Transition {
let mut best_transition = Transition::Done;
for &directed_edge_id in self.vert_edges(vert_id).iter() {
let edge_n = self.directed_edge_n(directed_edge_id);
@ -643,7 +928,13 @@ impl MinkowskiMesh<'_>{
}
best_transition
}
fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{
fn final_ev(
&self,
vert_id: MinkowskiVert,
best_distance_squared: &mut Fixed<2, 64>,
infinity_dir: Planar64Vec3,
point: Planar64Vec3,
) -> EV {
let mut best_transition = EV::Vert(vert_id);
let diff = point - self.vert(vert_id);
for &directed_edge_id in self.vert_edges(vert_id).iter() {
@ -668,20 +959,32 @@ impl MinkowskiMesh<'_>{
}
best_transition
}
fn crawl_boundaries(&self,mut vert_id:MinkowskiVert,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{
fn crawl_boundaries(
&self,
mut vert_id: MinkowskiVert,
infinity_dir: Planar64Vec3,
point: Planar64Vec3,
) -> EV {
let mut best_distance_squared = {
let diff = point - self.vert(vert_id);
diff.dot(diff)
};
loop {
match self.next_transition_vert(vert_id,&mut best_distance_squared,infinity_dir,point){
Transition::Done=>return self.final_ev(vert_id,&mut best_distance_squared,infinity_dir,point),
match self.next_transition_vert(
vert_id,
&mut best_distance_squared,
infinity_dir,
point,
) {
Transition::Done => {
return self.final_ev(vert_id, &mut best_distance_squared, infinity_dir, point)
}
Transition::Vert(new_vert_id) => vert_id = new_vert_id,
}
}
}
/// This function drops a vertex down to an edge or a face if the path from infinity did not cross any vertex-edge boundaries but the point is supposed to have already crossed a boundary down from a vertex
fn infinity_fev(&self,infinity_dir:Planar64Vec3,point:Planar64Vec3)->FEV::<MinkowskiMesh>{
fn infinity_fev(&self, infinity_dir: Planar64Vec3, point: Planar64Vec3) -> FEV<MinkowskiMesh> {
//start on any vertex
//cross uncrossable vertex-edge boundaries until you find the closest vertex or edge
//cross edge-face boundary if it's uncrossable
@ -709,7 +1012,7 @@ impl MinkowskiMesh<'_>{
}
}
FEV::Edge(edge_id)
},
}
}
}
fn closest_fev_not_inside(&self, mut infinity_body: Body) -> Option<FEV<MinkowskiMesh>> {
@ -726,8 +1029,13 @@ impl MinkowskiMesh<'_>{
}
})
}
pub fn predict_collision_in(&self,relative_body:&Body,time_limit:Time)->Option<(MinkowskiFace,GigaTime)>{
self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{
pub fn predict_collision_in(
&self,
relative_body: &Body,
time_limit: Time,
) -> Option<(MinkowskiFace, GigaTime)> {
self.closest_fev_not_inside(relative_body.clone())
.map_or(None, |fev| {
//continue forwards along the body parabola
match fev.crawl(self, relative_body, relative_body.time, time_limit) {
crate::face_crawler::CrawlResult::Miss(_) => None,
@ -735,7 +1043,11 @@ impl MinkowskiMesh<'_>{
}
})
}
pub fn predict_collision_out(&self,relative_body:&Body,time_limit:Time)->Option<(MinkowskiFace,GigaTime)>{
pub fn predict_collision_out(
&self,
relative_body: &Body,
time_limit: Time,
) -> Option<(MinkowskiFace, GigaTime)> {
//create an extrapolated body at time_limit
let infinity_body = Body::new(
relative_body.extrapolated_position(time_limit),
@ -743,15 +1055,26 @@ impl MinkowskiMesh<'_>{
relative_body.acceleration,
-time_limit,
);
self.closest_fev_not_inside(infinity_body).map_or(None,|fev|{
self.closest_fev_not_inside(infinity_body)
.map_or(None, |fev| {
//continue backwards along the body parabola
match fev.crawl(self,&-relative_body.clone(),-time_limit,-relative_body.time){
match fev.crawl(
self,
&-relative_body.clone(),
-time_limit,
-relative_body.time,
) {
crate::face_crawler::CrawlResult::Miss(_) => None,
crate::face_crawler::CrawlResult::Hit(face, time) => Some((face, -time)), //no need to test -time<time_limit because of the first step
}
})
}
pub fn predict_collision_face_out(&self,relative_body:&Body,time_limit:Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
pub fn predict_collision_face_out(
&self,
relative_body: &Body,
time_limit: Time,
contact_face_id: MinkowskiFace,
) -> Option<(MinkowskiEdge, GigaTime)> {
//no algorithm needed, there is only one state and two cases (Edge,None)
//determine when it passes an edge ("sliding off" case)
let mut best_time = {
@ -768,8 +1091,16 @@ impl MinkowskiMesh<'_>{
let d = n.dot(self.vert(verts[0]) + self.vert(verts[1]));
//WARNING! d outside of *2
//WARNING: truncated precision
for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).fix_4(),n.dot(relative_body.velocity).fix_4()*2,n.dot(relative_body.acceleration).fix_4()){
if Ratio::new(Planar64::ZERO,Planar64::EPSILON).le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
for dt in Fixed::<4, 128>::zeroes2(
((n.dot(relative_body.position)) * 2 - d).fix_4(),
n.dot(relative_body.velocity).fix_4() * 2,
n.dot(relative_body.acceleration).fix_4(),
) {
if Ratio::new(Planar64::ZERO, Planar64::EPSILON).le_ratio(dt)
&& dt.lt_ratio(best_time)
&& n.dot(relative_body.extrapolated_velocity_ratio_dt(dt))
.is_negative()
{
best_time = dt;
best_edge = Some(directed_edge_id);
break;
@ -790,10 +1121,7 @@ impl MinkowskiMesh<'_>{
//movement must escape the mesh forwards and backwards in time,
//otherwise the point is not inside the mesh
self.infinity_in(infinity_body)
.is_some_and(|_|
self.infinity_in(-infinity_body)
.is_some()
)
.is_some_and(|_| self.infinity_in(-infinity_body).is_some())
}
}
impl MeshQuery for MinkowskiMesh<'_> {
@ -808,7 +1136,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
MinkowskiFace::VertFace(v0, f1) => {
let (n, d) = self.mesh1.face_nd(f1);
(-n, d - n.dot(self.mesh0.vert(v0)))
},
}
MinkowskiFace::EdgeEdge(e0, e1, parity) => {
let edge0_n = self.mesh0.edge_n(e0);
let edge1_n = self.mesh1.edge_n(e1);
@ -817,28 +1145,31 @@ impl MeshQuery for MinkowskiMesh<'_>{
let n = edge0_n.cross(edge1_n);
let e0d = n.dot(self.mesh0.vert(e0v0) + self.mesh0.vert(e0v1));
let e1d = n.dot(self.mesh1.vert(e1v0) + self.mesh1.vert(e1v1));
((n*(parity as i64*4-2)).fix_3(),((e0d-e1d)*(parity as i64*2-1)).fix_4())
},
(
(n * (parity as i64 * 4 - 2)).fix_3(),
((e0d - e1d) * (parity as i64 * 2 - 1)).fix_4(),
)
}
MinkowskiFace::FaceVert(f0, v1) => {
let (n, d) = self.mesh0.face_nd(f0);
(n, d - n.dot(self.mesh1.vert(v1)))
},
}
}
}
fn vert(&self, vert_id: MinkowskiVert) -> Planar64Vec3 {
match vert_id {
MinkowskiVert::VertVert(v0,v1)=>{
self.mesh0.vert(v0)-self.mesh1.vert(v1)
},
MinkowskiVert::VertVert(v0, v1) => self.mesh0.vert(v0) - self.mesh1.vert(v1),
}
}
fn face_edges(&self, face_id: MinkowskiFace) -> Cow<Vec<MinkowskiDirectedEdge>> {
match face_id {
MinkowskiFace::VertFace(v0,f1)=>{
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&edge_id1|{
MinkowskiDirectedEdge::VertEdge(v0,edge_id1.reverse())
}).collect())
},
MinkowskiFace::VertFace(v0, f1) => Cow::Owned(
self.mesh1
.face_edges(f1)
.iter()
.map(|&edge_id1| MinkowskiDirectedEdge::VertEdge(v0, edge_id1.reverse()))
.collect(),
),
MinkowskiFace::EdgeEdge(e0, e1, parity) => {
let e0v = self.mesh0.edge_verts(e0);
let e1v = self.mesh1.edge_verts(e1);
@ -850,12 +1181,14 @@ impl MeshQuery for MinkowskiMesh<'_>{
MinkowskiDirectedEdge::VertEdge(e0v[1], e1.as_directed(!parity)),
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity), e1v[1]),
])
},
MinkowskiFace::FaceVert(f0,v1)=>{
Cow::Owned(self.mesh0.face_edges(f0).iter().map(|&edge_id0|{
MinkowskiDirectedEdge::EdgeVert(edge_id0,v1)
}).collect())
},
}
MinkowskiFace::FaceVert(f0, v1) => Cow::Owned(
self.mesh0
.face_edges(f0)
.iter()
.map(|&edge_id0| MinkowskiDirectedEdge::EdgeVert(edge_id0, v1))
.collect(),
),
}
}
fn edge_faces(&self, edge_id: MinkowskiEdge) -> Cow<[MinkowskiFace; 2]> {
@ -864,9 +1197,11 @@ impl MeshQuery for MinkowskiMesh<'_>{
//faces are listed backwards from the minkowski mesh
let v0e = self.mesh0.vert_edges(v0);
let &[e1f0, e1f1] = self.mesh1.edge_faces(e1).borrow();
Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
Cow::Owned(
[(e1f1, false), (e1f0, true)].map(|(edge_face_id1, face_parity)| {
let mut best_edge = None;
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
let mut best_d: Ratio<Fixed<8, 256>, Fixed<8, 256>> =
Ratio::new(Fixed::ZERO, Fixed::ONE);
let edge_face1_n = self.mesh1.face_nd(edge_face_id1).0;
let edge_face1_nn = edge_face1_n.dot(edge_face1_n);
for &directed_edge_id0 in v0e.iter() {
@ -886,17 +1221,26 @@ impl MeshQuery for MinkowskiMesh<'_>{
}
best_edge.map_or(
MinkowskiFace::VertFace(v0, edge_face_id1),
|directed_edge_id0|MinkowskiFace::EdgeEdge(directed_edge_id0.as_undirected(),e1,directed_edge_id0.parity()^face_parity)
|directed_edge_id0| {
MinkowskiFace::EdgeEdge(
directed_edge_id0.as_undirected(),
e1,
directed_edge_id0.parity() ^ face_parity,
)
}))
},
)
}),
)
}
MinkowskiEdge::EdgeVert(e0, v1) => {
//tracking index with an external variable because .enumerate() is not available
let v1e = self.mesh1.vert_edges(v1);
let &[e0f0, e0f1] = self.mesh0.edge_faces(e0).borrow();
Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
Cow::Owned(
[(e0f0, true), (e0f1, false)].map(|(edge_face_id0, face_parity)| {
let mut best_edge = None;
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
let mut best_d: Ratio<Fixed<8, 256>, Fixed<8, 256>> =
Ratio::new(Fixed::ZERO, Fixed::ONE);
let edge_face0_n = self.mesh0.face_nd(edge_face_id0).0;
let edge_face0_nn = edge_face0_n.dot(edge_face0_n);
for &directed_edge_id1 in v1e.iter() {
@ -913,24 +1257,31 @@ impl MeshQuery for MinkowskiMesh<'_>{
}
best_edge.map_or(
MinkowskiFace::FaceVert(edge_face_id0, v1),
|directed_edge_id1|MinkowskiFace::EdgeEdge(e0,directed_edge_id1.as_undirected(),directed_edge_id1.parity()^face_parity)
|directed_edge_id1| {
MinkowskiFace::EdgeEdge(
e0,
directed_edge_id1.as_undirected(),
directed_edge_id1.parity() ^ face_parity,
)
}))
},
)
}),
)
}
}
}
fn edge_verts(&self, edge_id: MinkowskiEdge) -> Cow<[MinkowskiVert; 2]> {
match edge_id {
MinkowskiEdge::VertEdge(v0,e1)=>{
Cow::Owned(self.mesh1.edge_verts(e1).map(|vert_id1|{
MinkowskiVert::VertVert(v0,vert_id1)
}))
},
MinkowskiEdge::EdgeVert(e0,v1)=>{
Cow::Owned(self.mesh0.edge_verts(e0).map(|vert_id0|{
MinkowskiVert::VertVert(vert_id0,v1)
}))
},
MinkowskiEdge::VertEdge(v0, e1) => Cow::Owned(
self.mesh1
.edge_verts(e1)
.map(|vert_id1| MinkowskiVert::VertVert(v0, vert_id1)),
),
MinkowskiEdge::EdgeVert(e0, v1) => Cow::Owned(
self.mesh0
.edge_verts(e0)
.map(|vert_id0| MinkowskiVert::VertVert(vert_id0, v1)),
),
}
}
fn vert_edges(&self, vert_id: MinkowskiVert) -> Cow<Vec<MinkowskiDirectedEdge>> {
@ -940,8 +1291,14 @@ impl MeshQuery for MinkowskiMesh<'_>{
//detect shared volume when the other mesh is mirrored along a test edge dir
let v0f = self.mesh0.vert_faces(v0);
let v1f = self.mesh1.vert_faces(v1);
let v0f_n:Vec<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
let v0f_n: Vec<_> = v0f
.iter()
.map(|&face_id| self.mesh0.face_nd(face_id).0)
.collect();
let v1f_n: Vec<_> = v1f
.iter()
.map(|&face_id| self.mesh1.face_nd(face_id).0)
.collect();
let the_len = v0f.len() + v1f.len();
for &directed_edge_id in self.mesh0.vert_edges(v0).iter() {
let n = self.mesh0.directed_edge_n(directed_edge_id);
@ -972,7 +1329,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
}
}
Cow::Owned(edges)
},
}
}
}
fn vert_faces(&self, _vert_id: MinkowskiVert) -> Cow<Vec<MinkowskiFace>> {
@ -1006,8 +1363,18 @@ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
#[test]
fn test_is_empty_volume() {
assert!(!is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3()].to_vec()));
assert!(is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3(),vec3::NEG_X.fix_3()].to_vec()));
assert!(!is_empty_volume(
[vec3::X.fix_3(), vec3::Y.fix_3(), vec3::Z.fix_3()].to_vec()
));
assert!(is_empty_volume(
[
vec3::X.fix_3(),
vec3::Y.fix_3(),
vec3::Z.fix_3(),
vec3::NEG_X.fix_3()
]
.to_vec()
));
}
#[test]

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
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,
@ -24,7 +25,10 @@ pub enum InputInstruction{
Jump(bool),
Zoom(bool),
ResetAndRestart,
ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
ResetAndSpawn(
strafesnet_common::gameplay_modes::ModeId,
strafesnet_common::gameplay_modes::StageId,
),
PracticeFly,
}
pub enum Instruction {
@ -43,13 +47,13 @@ pub struct MouseInterpolator{
//"PlayerController"
user_settings: crate::settings::UserSettings,
//"MouseInterpolator"
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
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(
@ -65,12 +69,19 @@ impl MouseInterpolator{
user_settings,
}
}
fn push_mouse_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>,m:glam::IVec2){
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}),
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.
@ -78,8 +89,14 @@ impl MouseInterpolator{
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}
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
@ -96,7 +113,10 @@ impl MouseInterpolator{
}
/// returns should_empty_queue
/// may or may not mutate internal state XD!
fn map_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>)->bool{
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 {
@ -105,26 +125,54 @@ impl MouseInterpolator{
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::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::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::SetSensitivity(
self.user_settings.calculate_sensitivity(),
),
);
self.push(ins.time, PhysicsInputInstruction::Restart);
},
InputInstruction::PracticeFly=>self.push(ins.time,PhysicsInputInstruction::PracticeFly),
}
InputInstruction::PracticeFly => {
self.push(ins.time, PhysicsInputInstruction::PracticeFly)
}
},
//do these really need to idle the physics?
//sending None dumps the instruction queue
@ -136,7 +184,7 @@ impl MouseInterpolator{
println!("Cannot SetPaused: {e}");
}
self.push(ins.time, PhysicsInputInstruction::Idle);
},
}
}
if update_mouse_blocking {
//this returns the bool for us
@ -151,7 +199,10 @@ impl MouseInterpolator{
//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}),
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.
@ -163,7 +214,9 @@ impl MouseInterpolator{
//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{
if PhysicsTime::from_millis(10)
< self.timer.time(time) - self.physics.get_next_mouse().time
{
self.unblock_mouse(time);
true
} else {
@ -181,7 +234,10 @@ impl MouseInterpolator{
self.physics.run_input_instruction(instruction);
}
}
pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>){
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();
@ -224,26 +280,34 @@ pub fn new<'a>(
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>|{
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();
},
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();
},
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();
},
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
@ -49,7 +53,8 @@ impl Contact{
}
/// 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)
(self.position - ray.origin).dot(self.normal)
/ (ray.direction - self.velocity).dot(self.normal)
}
}
@ -75,7 +80,11 @@ fn solve2(c0:&Contact,c1:&Contact)->Option<Ratio<Vector3<Fixed<5,160>>,Fixed<4,1
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>>>{
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);
@ -85,10 +94,18 @@ fn solve3(c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ratio<Vector3<Fixed<4,128
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)
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]>{
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;
@ -96,7 +113,11 @@ fn decompose1(point:Planar64Vec3,u0:Planar64Vec3)->Option<[Ratio<Fixed<2,64>,Fix
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]>{
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 {
@ -106,7 +127,12 @@ fn decompose2(point:Planar64Vec3,u0:Planar64Vec3,u1:Planar64Vec3)->Option<[Ratio
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]>{
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;
@ -117,20 +143,11 @@ fn decompose3(point:Planar64Vec3,u0:Planar64Vec3,u1:Planar64Vec3,u2:Planar64Vec3
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
&&{
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));
@ -139,15 +156,10 @@ fn is_space_enclosed_3(
&& !(-det_acbc * det_abac).is_positive()
|| is_space_enclosed_2(a, b)
|| is_space_enclosed_2(a, c)
||is_space_enclosed_2(b,c)
|| is_space_enclosed_2(b, c);
}
}
fn is_space_enclosed_4(
a:Planar64Vec3,
b:Planar64Vec3,
c:Planar64Vec3,
d:Planar64Vec3,
)->bool{
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);
@ -161,11 +173,14 @@ fn is_space_enclosed_4(
|| 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)
|| is_space_enclosed_3(b, c, d);
}
const fn get_push_ray_0(point: Planar64Vec3) -> Option<Ray> {
Some(Ray{origin:point,direction:vec3::ZERO})
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();
@ -173,9 +188,7 @@ fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{
if s0.lt_ratio(RATIO_ZERO) {
return None;
}
let origin=point+solve1(
&c0.relative_to(point),
)?.divide().fix_1();
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> {
@ -184,10 +197,10 @@ fn get_push_ray_2(point:Planar64Vec3,c0:&Contact,c1:&Contact)->Option<Ray>{
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();
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> {
@ -196,11 +209,14 @@ fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Optio
if s0.lt_ratio(RATIO_ZERO) || s1.lt_ratio(RATIO_ZERO) || s2.lt_ratio(RATIO_ZERO) {
return None;
}
let origin=point+solve3(
let origin = point
+ solve3(
&c0.relative_to(point),
&c1.relative_to(point),
&c2.relative_to(point),
)?.divide().fix_1();
)?
.divide()
.fix_1();
Some(Ray { origin, direction })
}
@ -211,10 +227,13 @@ const fn get_best_push_ray_and_conts_0<'a>(point:Planar64Vec3)->Option<(Ray,Cont
}
}
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])))
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>)>{
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;
}
@ -228,7 +247,12 @@ fn get_best_push_ray_and_conts_2<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Co
}
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>)>{
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;
}
@ -247,13 +271,20 @@ fn get_best_push_ray_and_conts_3<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Co
}
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(){
&& !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>)>{
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;
}
@ -269,11 +300,11 @@ fn get_best_push_ray_and_conts_4<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Co
let best_err = err012.max(err013).max(err023);
if best_err == err012 {
return Some((ray012,conts012))
return Some((ray012, conts012));
} else if best_err == err013 {
return Some((ray013,conts013))
return Some((ray013, conts013));
} else if best_err == err023 {
return Some((ray023,conts023))
return Some((ray023, conts023));
}
unreachable!()
}
@ -292,12 +323,17 @@ fn get_best_push_ray_and_conts<'a>(
}
}
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|
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)
}
@ -338,16 +374,11 @@ mod tests{
use super::*;
#[test]
fn test_push_solve() {
let contacts=vec![
Contact{
let contacts = vec![Contact {
position: vec3::ZERO,
velocity: vec3::Y,
normal: vec3::Y,
}
];
assert_eq!(
Some(vec3::ZERO),
push_solve(&contacts,vec3::NEG_Y)
);
}];
assert_eq!(Some(vec3::ZERO), push_solve(&contacts, vec3::NEG_Y));
}
}

View File

@ -16,7 +16,10 @@ enum Fov{
}
impl Default for Fov {
fn default() -> Self {
Fov::SpecifyYDeriveX{x:DerivedFov::FromScreenAspect,y:1.0}
Fov::SpecifyYDeriveX {
x: DerivedFov::FromScreenAspect,
y: 1.0,
}
}
}
#[derive(Clone)]
@ -31,7 +34,10 @@ enum Sensitivity{
}
impl Default for Sensitivity {
fn default() -> Self {
Sensitivity::SpecifyXDeriveY{x:Ratio64::ONE*524288,y:DerivedSensitivity::FromRatio(Ratio64::ONE)}
Sensitivity::SpecifyXDeriveY {
x: Ratio64::ONE * 524288,
y: DerivedSensitivity::FromRatio(Ratio64::ONE),
}
}
}
@ -45,11 +51,15 @@ impl UserSettings{
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::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::FromScreenAspect => {
glam::dvec2(y * (screen_size.x as f64 / screen_size.y as f64), *y)
}
DerivedFov::FromAspect(ratio) => glam::dvec2(y * ratio.ratio, *y),
},
}
@ -58,11 +68,15 @@ impl UserSettings{
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)),
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()),
DerivedSensitivity::FromRatio(ratio) => {
Ratio64Vec2::new(y.mul_ref(ratio), y.clone())
}
},
}
}
}
@ -77,33 +91,42 @@ 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 (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(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})
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})
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()
},
_ => Fov::default(),
};
let (cfg_sensitivity_x,cfg_sensitivity_y)=(cfg.getfloat("camera","sensitivity_x"),cfg.getfloat("camera","sensitivity_y"));
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(),
@ -111,28 +134,31 @@ pub fn read_user_settings()->UserSettings{
},
(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())
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())
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()
},
_ => Sensitivity::default(),
};
UserSettings{
fov,
sensitivity,
}
UserSettings { fov, sensitivity }
} else {
UserSettings::default()
}

View File

@ -4,8 +4,7 @@ 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
wgpu::Features::TEXTURE_COMPRESSION_ASTC | wgpu::Features::TEXTURE_COMPRESSION_ETC2
}
fn required_features() -> wgpu::Features {
wgpu::Features::TEXTURE_COMPRESSION_BC
@ -25,7 +24,10 @@ 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>{
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
@ -48,7 +50,10 @@ fn create_instance()->SetupContextPartial1{
}
}
impl SetupContextPartial1 {
fn create_surface<'a>(self,window:&'a winit::window::Window)->Result<SetupContextPartial2<'a>,wgpu::CreateSurfaceError>{
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)?,
@ -99,7 +104,6 @@ impl<'a> SetupContextPartial2<'a>{
panic!("No suitable GPU adapters found on the system!");
}
let adapter_info = adapter.get_info();
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
@ -138,11 +142,11 @@ impl<'a> SetupContextPartial3<'a>{
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(
let (device, queue) = pollster::block_on(self.adapter.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: (optional_features & self.adapter.features()) | required_features,
required_features: (optional_features & self.adapter.features())
| required_features,
required_limits: needed_limits,
memory_hints: wgpu::MemoryHints::Performance,
},
@ -168,7 +172,8 @@ struct SetupContextPartial4<'a>{
}
impl<'a> SetupContextPartial4<'a> {
fn configure_surface(self, size: &'a winit::dpi::PhysicalSize<u32>) -> SetupContext<'a> {
let mut config=self.surface
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();
@ -215,17 +220,18 @@ pub fn setup_and_start(title:&str){
//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,
);
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{
window_thread
.send(TimedInstruction {
time: integer::Time::ZERO,
instruction:WindowInstruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)),
}).unwrap();
instruction: WindowInstruction::WindowEvent(
winit::event::WindowEvent::DroppedFile(path),
),
})
.unwrap();
};
println!("Entering event loop...");
@ -235,8 +241,10 @@ pub fn setup_and_start(title:&str){
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
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);

View File

@ -1,6 +1,6 @@
use crate::physics_worker::InputInstruction;
use strafesnet_common::integer;
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::integer;
use strafesnet_common::session::{Time as SessionTime, TimeInner as SessionTimeInner};
pub enum WindowInstruction {
@ -17,7 +17,10 @@ struct WindowContext<'a>{
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>>,
physics_thread: crate::compat_worker::QNWorker<
'a,
TimedInstruction<crate::physics_worker::Instruction, SessionTimeInner>,
>,
}
impl WindowContext<'_> {
@ -28,71 +31,112 @@ impl WindowContext<'_>{
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(),
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{
self.physics_thread
.send(TimedInstruction {
time,
instruction: crate::physics_worker::Instruction::SetPaused(!state),
}).unwrap();
})
.unwrap();
//recalculate pressed keys on focus
},
}
winit::event::WindowEvent::KeyboardInput {
event:winit::event::KeyEvent{state,logical_key,repeat:false,..},
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)=>{
(
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){
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)=>{
}
(
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){
match self
.window
.set_cursor_grab(winit::window::CursorGrabMode::Locked)
{
Ok(()) => (),
Err(_) => {
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
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)=>{
}
(
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)));
self.window
.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
}
},
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape),winit::event::ElementState::Pressed)=>{
}
(
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){
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::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)),
@ -101,24 +145,39 @@ impl WindowContext<'_>{
"E" | "e" => Some(InputInstruction::MoveUp(s)),
"Q" | "q" => Some(InputInstruction::MoveDown(s)),
"Z" | "z" => Some(InputInstruction::Zoom(s)),
"R"|"r"=>if s{
"R" | "r" => {
if s {
//mouse needs to be reset since the position is absolute
self.mouse=strafesnet_common::mouse::MouseState::default();
self.mouse =
strafesnet_common::mouse::MouseState::default();
Some(InputInstruction::ResetAndRestart)
}else{None},
"F"|"f"=>if s{Some(InputInstruction::PracticeFly)}else{None},
} else {
None
}
}
"F" | "f" => {
if s {
Some(InputInstruction::PracticeFly)
} else {
None
}
}
_ => None,
},
_ => None,
} {
self.physics_thread.send(TimedInstruction{
self.physics_thread
.send(TimedInstruction {
time,
instruction:crate::physics_worker::Instruction::Input(input_instruction),
}).unwrap();
instruction: crate::physics_worker::Instruction::Input(
input_instruction,
),
})
.unwrap();
}
}
}
},
}
},
_ => (),
}
}
@ -139,22 +198,29 @@ impl WindowContext<'_>{
//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{
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();
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();
}
}
},
_ => (),
}
}
@ -166,52 +232,61 @@ pub fn worker<'a>(
// 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);
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 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,
),
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{
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::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{
window_context
.physics_thread
.send(TimedInstruction {
time: ins.time,
instruction:crate::physics_worker::Instruction::Render
}
).unwrap();
}
}
instruction: crate::physics_worker::Instruction::Render,
})
.unwrap();
}
},
)
}

View File

@ -1,6 +1,6 @@
use std::thread;
use std::sync::{mpsc,Arc};
use parking_lot::Mutex;
use std::sync::{mpsc, Arc};
use std::thread;
//WorkerPool
struct Pool(u32);
@ -110,10 +110,12 @@ pub struct QNWorker<'a,Task:Send>{
}
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>{
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 {
let handle = scope.spawn(move || loop {
match receiver.recv() {
Ok(task) => f(task),
Err(_) => {
@ -121,12 +123,8 @@ impl<'a,Task:Send+'a> QNWorker<'a,Task>{
break;
}
}
}
});
Self{
sender,
handle,
}
Self { sender, handle }
}
pub fn send(&self, task: Task) -> Result<(), mpsc::SendError<Task>> {
self.sender.send(task)
@ -146,10 +144,12 @@ pub struct INWorker<'a,Task:Send>{
}
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>{
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 {
let handle = scope.spawn(move || loop {
match receiver.recv() {
Ok(task) => f(task),
Err(_) => {
@ -157,12 +157,8 @@ impl<'a,Task:Send+'a> INWorker<'a,Task>{
break;
}
}
}
});
Self{
sender,
handle,
}
Self { sender, handle }
}
//blocking!
pub fn blocking_send(&self, task: Task) -> Result<(), mpsc::SendError<Task>> {
@ -177,14 +173,24 @@ impl<'a,Task:Send+'a> INWorker<'a,Task>{
mod test {
use super::{thread, QRWorker};
type Body = crate::physics::Body;
use strafesnet_common::{integer,instruction};
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)
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 {