Unquatification #8
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -18,7 +18,7 @@ struct Outer{
|
||||
enum Update<I, U> {
|
||||
Insert(I),
|
||||
Update(U),
|
||||
Remove
|
||||
Remove,
|
||||
}
|
||||
|
||||
struct InnerUpdate {
|
||||
|
@ -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);
|
||||
|
@ -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"))]
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod macros;
|
||||
pub mod matrix;
|
||||
pub mod types;
|
||||
pub mod vector;
|
||||
pub mod matrix;
|
||||
|
||||
#[cfg(feature = "named-fields")]
|
||||
mod named;
|
||||
|
@ -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{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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,
|
||||
],
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub mod common;
|
||||
pub mod vector;
|
||||
pub mod matrix;
|
||||
pub mod vector;
|
||||
|
||||
#[cfg(feature = "fixed-wide")]
|
||||
pub mod fixed_wide;
|
||||
|
@ -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 {
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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))
|
||||
);
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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>;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod runner;
|
||||
pub mod context;
|
||||
pub mod runner;
|
||||
#[cfg(feature = "run-service")]
|
||||
pub(crate) mod scheduler;
|
||||
|
||||
|
@ -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,
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)?;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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)?;
|
||||
|
@ -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)
|
||||
|
@ -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>(())?;
|
||||
|
@ -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,
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>(())?;
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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() {}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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"));
|
||||
|
||||
|
@ -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
@ -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(_) => (),
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user