Compare commits

..

8 Commits

Author SHA1 Message Date
e9bf4db43e deduplicate models 2023-10-16 15:14:27 -07:00
7e1cf7041a GameMechanics: make invalid states unrepresentable 2023-10-14 18:14:27 -07:00
50543ffcea implement additional attribute populating 2023-10-14 18:14:27 -07:00
54498f20f9 improve constant names 2023-10-14 16:20:57 -07:00
2240b80656 sqrt + test 2023-10-14 16:20:57 -07:00
d18f2168e4 fix tests 2023-10-14 16:20:57 -07:00
381b7b3c2f put jump in style 2023-10-14 14:51:13 -07:00
0d6741a81c integer physics 2023-10-14 12:34:20 -07:00
7 changed files with 268 additions and 35 deletions

@ -421,14 +421,17 @@ impl Planar64{
pub const fn get(&self)->i64{
self.0
}
pub fn sqrt(&self)->Self{
Planar64(unsafe{(((self.0 as i128)<<32) as f64).sqrt().to_int_unchecked()})
}
}
const PLANAR64_FLOAT32_ONE:f32=(1u64<<32) as f32;
const PLANAR64_FLOAT32_MUL:f32=1.0/PLANAR64_FLOAT32_ONE;
const PLANAR64_FLOAT64_ONE:f64=(1u64<<32) as f64;
const PLANAR64_ONE_FLOAT32:f32=(1u64<<32) as f32;
const PLANAR64_CONVERT_TO_FLOAT32:f32=1.0/PLANAR64_ONE_FLOAT32;
const PLANAR64_ONE_FLOAT64:f64=(1u64<<32) as f64;
impl Into<f32> for Planar64{
#[inline]
fn into(self)->f32{
self.0 as f32*PLANAR64_FLOAT32_MUL
self.0 as f32*PLANAR64_CONVERT_TO_FLOAT32
}
}
impl From<Ratio64> for Planar64{
@ -640,7 +643,7 @@ impl Into<glam::Vec3> for Planar64Vec3{
self.0.x as f32,
self.0.y as f32,
self.0.z as f32,
)*PLANAR64_FLOAT32_MUL
)*PLANAR64_CONVERT_TO_FLOAT32
}
}
impl TryFrom<[f32;3]> for Planar64Vec3{
@ -808,7 +811,7 @@ impl Planar64Mat3{
pub fn from_rotation_y(angle:Angle32)->Self{
let theta=angle.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (s,c)=theta.sin_cos();
let (c,s)=(c*PLANAR64_FLOAT64_ONE,s*PLANAR64_FLOAT64_ONE);
let (c,s)=(c*PLANAR64_ONE_FLOAT64,s*PLANAR64_ONE_FLOAT64);
//TODO: fix this rounding towards 0
let (c,s):(i64,i64)=(unsafe{c.to_int_unchecked()},unsafe{s.to_int_unchecked()});
Self::from_cols(
@ -889,8 +892,8 @@ impl Into<glam::Mat4> for Planar64Affine3{
self.matrix3.x_axis.0.x as f32,self.matrix3.x_axis.0.y as f32,self.matrix3.x_axis.0.z as f32,0.0,
self.matrix3.y_axis.0.x as f32,self.matrix3.y_axis.0.y as f32,self.matrix3.y_axis.0.z as f32,0.0,
self.matrix3.z_axis.0.x as f32,self.matrix3.z_axis.0.y as f32,self.matrix3.z_axis.0.z as f32,0.0,
self.translation.0.x as f32,self.translation.0.y as f32,self.translation.0.z as f32,PLANAR64_FLOAT32_ONE
])*PLANAR64_FLOAT32_MUL
self.translation.0.x as f32,self.translation.0.y as f32,self.translation.0.z as f32,PLANAR64_ONE_FLOAT32
])*PLANAR64_CONVERT_TO_FLOAT32
}
}
impl TryFrom<glam::Affine3A> for Planar64Affine3{
@ -911,4 +914,12 @@ impl std::fmt::Display for Planar64Affine3{
Into::<f32>::into(self.matrix3.z_axis.x()),Into::<f32>::into(self.matrix3.z_axis.y()),Into::<f32>::into(self.matrix3.z_axis.z()),
)
}
}
#[test]
fn test_sqrt(){
let r=Planar64::int(400);
println!("r{}",r.get());
let s=r.sqrt();
println!("s{}",s.get());
}

@ -50,20 +50,20 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse
let mut contacting=crate::model::ContactingAttributes::default();
let mut force_can_collide=can_collide;
match name{
//"Water"=>intersecting.water=Some(crate::model::IntersectingWater{density:1.0,drag:1.0}),
"Water"=>intersecting.water=Some(crate::model::IntersectingWater{density:Planar64::ONE,viscosity:Planar64::ONE/10,current:velocity}),
"Accelerator"=>{force_can_collide=false;intersecting.accelerator=Some(crate::model::IntersectingAccelerator{acceleration:velocity})},
"MapFinish"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Finish})},
"MapAnticheat"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Anitcheat})},
"Platform"=>general.stage_element=Some(crate::model::GameMechanicStageElement{
"Platform"=>general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
mode_id:0,
stage_id:0,
force:false,
behaviour:crate::model::StageElementBehaviour::Platform,
}),
})),
other=>{
if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
.captures(other){
general.stage_element=Some(crate::model::GameMechanicStageElement{
general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
mode_id:0,
stage_id:captures[3].parse::<u32>().unwrap(),
force:match captures.get(1){
@ -77,7 +77,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse
"Platform"=>crate::model::StageElementBehaviour::Platform,
_=>panic!("regex1[2] messed up bad"),
}
})
}));
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
.captures(other){
force_can_collide=false;
@ -96,13 +96,18 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse
match force_can_collide{
true=>{
match name{
//"Bounce"=>(),
"Bounce"=>contacting.elasticity=Some(u32::MAX),
"Surf"=>contacting.surf=Some(crate::model::ContactingSurf{}),
"Ladder"=>contacting.ladder=Some(crate::model::ContactingLadder{sticky:true}),
other=>{
//REGEX!!!!
//Jump#
//WormholeIn#
if let Some(captures)=lazy_regex::regex!(r"^(Jump|WormholeIn)(\d+)$")
.captures(other){
match &captures[1]{
"Jump"=>general.jump_limit=Some(crate::model::GameMechanicJumpLimit{count:captures[2].parse::<u32>().unwrap()}),
"WormholeIn"=>general.teleport_behaviour=Some(crate::model::TeleportBehaviour::Wormhole(crate::model::GameMechanicWormhole{destination_model_id:captures[2].parse::<u32>().unwrap()})),
_=>panic!("regex3[1] messed up bad"),
}
}
}
}
crate::model::CollisionAttributes::Contact{contacting,general}
@ -111,8 +116,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse
||general.jump_limit.is_some()
||general.booster.is_some()
||general.zone.is_some()
||general.stage_element.is_some()
||general.wormhole.is_some()
||general.teleport_behaviour.is_some()
||intersecting.water.is_some()
||intersecting.accelerator.is_some()
{

@ -230,7 +230,7 @@ impl GlobalState{
Some(ModelGraphicsInstance{
transform: instance.transform.into(),
normal_transform: Into::<glam::Mat3>::into(instance.transform.matrix3).inverse().transpose(),
color: instance.color,
color:model_graphics::ModelGraphicsColor4::from(instance.color),
})
}
}).collect();
@ -262,9 +262,182 @@ impl GlobalState{
});
}
}
//check every model to see if it's using the same (texture,color) but has few instances, if it is combine it into one model
//1. collect unique instances of texture and color, note model id
//2. for each model id, check if removing it from the pool decreases both the model count and instance count by more than one
//3. transpose all models that stay in the set
//best plan: benchmark set_bind_group, set_vertex_buffer, set_index_buffer and draw_indexed
//check if the estimated render performance is better by transposing multiple model instances into one model instance
//for now: just deduplicate single models...
let mut deduplicated_models=Vec::with_capacity(indexed_models_len);//use indexed_models_len because the list will likely get smaller instead of bigger
let mut unique_texture_color=std::collections::HashMap::new();//texture->color->vec![(model_id,instance_id)]
for (model_id,model) in unique_texture_models.iter().enumerate(){
//for now: filter out models with more than one instance
if 1<model.instances.len(){
continue;
}
//populate hashmap
let unique_color=if let Some(unique_color)=unique_texture_color.get_mut(&model.texture){
unique_color
}else{
//make new hashmap
let unique_color=std::collections::HashMap::new();
unique_texture_color.insert(model.texture,unique_color);
unique_texture_color.get_mut(&model.texture).unwrap()
};
//separate instances by color
for (instance_id,instance) in model.instances.iter().enumerate(){
let model_instance_list=if let Some(model_instance_list)=unique_color.get_mut(&instance.color){
model_instance_list
}else{
//make new hashmap
let model_instance_list=Vec::new();
unique_color.insert(instance.color.clone(),model_instance_list);
unique_color.get_mut(&instance.color).unwrap()
};
//add model instance to list
model_instance_list.push((model_id,instance_id));
}
}
//populate a hashmap of models selected for transposition
let mut selected_model_instances=std::collections::HashSet::new();
for (texture,unique_color) in unique_texture_color.into_iter(){
for (color,model_instance_list) in unique_color.into_iter(){
if 1<model_instance_list.len(){
//create model
let mut unique_pos=Vec::new();
let mut pos_id_from=std::collections::HashMap::new();
let mut map_pos_id=std::collections::HashMap::new();
let mut unique_tex=Vec::new();
let mut tex_id_from=std::collections::HashMap::new();
let mut map_tex_id=std::collections::HashMap::new();
let mut unique_normal=Vec::new();
let mut normal_id_from=std::collections::HashMap::new();
let mut map_normal_id=std::collections::HashMap::new();
let mut unique_color=Vec::new();
let mut color_id_from=std::collections::HashMap::new();
let mut map_color_id=std::collections::HashMap::new();
let mut unique_vertices=Vec::new();
let mut vertex_id_from=std::collections::HashMap::new();
let mut map_vertex_id=std::collections::HashMap::new();
let mut polys=Vec::new();
//transform instance vertices
for (model_id,instance_id) in model_instance_list.into_iter(){
//populate hashset to prevent these models from being copied
selected_model_instances.insert(model_id);
//there is only one instance per model
let model=&unique_texture_models[model_id];
let instance=&model.instances[instance_id];
//just hash word slices LOL
for (old_pos_id,untransformed_pos) in model.unique_pos.iter().enumerate(){
let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array();
let h=pos.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
let pos_id=if let Some(&pos_id)=pos_id_from.get(&h){
pos_id
}else{
let pos_id=unique_pos.len();
unique_pos.push(pos.clone());
pos_id_from.insert(h,pos_id);
pos_id
};
map_pos_id.insert(old_pos_id,pos_id);
}
for (old_tex_id,tex) in model.unique_tex.iter().enumerate(){
let h=tex.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
let tex_id=if let Some(&tex_id)=tex_id_from.get(&h){
tex_id
}else{
let tex_id=unique_tex.len();
unique_tex.push(tex.clone());
tex_id_from.insert(h,tex_id);
tex_id
};
map_tex_id.insert(old_tex_id,tex_id);
}
for (old_normal_id,untransformed_normal) in model.unique_normal.iter().enumerate(){
let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array();
let h=normal.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
let normal_id=if let Some(&normal_id)=normal_id_from.get(&h){
normal_id
}else{
let normal_id=unique_normal.len();
unique_normal.push(normal.clone());
normal_id_from.insert(h,normal_id);
normal_id
};
map_normal_id.insert(old_normal_id,normal_id);
}
for (old_color_id,color) in model.unique_color.iter().enumerate(){
let h=color.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
let color_id=if let Some(&color_id)=color_id_from.get(&h){
color_id
}else{
let color_id=unique_color.len();
unique_color.push(color.clone());
color_id_from.insert(h,color_id);
color_id
};
map_color_id.insert(old_color_id,color_id);
}
//map the indexed vertices onto new indices
//creating the vertex map is slightly different because the vertices are directly hashable
for (old_vertex_id,unmapped_vertex) in model.unique_vertices.iter().enumerate(){
let vertex=model::IndexedVertex{
pos:map_pos_id[&(unmapped_vertex.pos as usize)] as u32,
tex:map_tex_id[&(unmapped_vertex.tex as usize)] as u32,
normal:map_normal_id[&(unmapped_vertex.normal as usize)] as u32,
color:map_color_id[&(unmapped_vertex.color as usize)] as u32,
};
let vertex_id=if let Some(&vertex_id)=vertex_id_from.get(&vertex){
vertex_id
}else{
let vertex_id=unique_vertices.len();
unique_vertices.push(vertex.clone());
vertex_id_from.insert(vertex,vertex_id);
vertex_id
};
map_vertex_id.insert(old_vertex_id as u32,vertex_id as u32);
}
for group in &model.groups{
for poly in &group.polys{
polys.push(model::IndexedPolygon{vertices:poly.vertices.iter().map(|vertex_id|map_vertex_id[vertex_id]).collect()});
}
}
}
//push model into dedup
deduplicated_models.push(model_graphics::IndexedModelGraphicsSingleTexture{
unique_pos,
unique_tex,
unique_normal,
unique_color,
unique_vertices,
texture,
groups:vec![model_graphics::IndexedGroupFixedTexture{
polys
}],
instances:vec![model_graphics::ModelGraphicsInstance{
transform:glam::Mat4::IDENTITY,
normal_transform:glam::Mat3::IDENTITY,
color
}],
});
}
}
}
//construct transposed models
//fill untouched models
for (model_id,model) in unique_texture_models.into_iter().enumerate(){
if !selected_model_instances.contains(&model_id){
deduplicated_models.push(model);
}
}
//de-index models
let mut models=Vec::with_capacity(unique_texture_models.len());
for model in unique_texture_models.into_iter(){
let mut models=Vec::with_capacity(indexed_models_len);
for model in deduplicated_models.into_iter(){
let mut vertices = Vec::new();
let mut index_from_vertex = std::collections::HashMap::new();//::<IndexedVertex,usize>
let mut entities = Vec::new();
@ -393,7 +566,7 @@ fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec<f32> {
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis));
raw.extend_from_slice(&[0.0]);
//color
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color));
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get()));
raw.append(&mut v);
}
raw

@ -152,16 +152,24 @@ pub struct GameMechanicStageElement{
pub behaviour:StageElementBehaviour
}
#[derive(Clone)]
pub struct GameMechanicWormhole{//(position,angles)*=origin.transform.inverse()*destination.transform
pub model_id:u32,
pub struct GameMechanicWormhole{
//destination does not need to be another wormhole
//this defines a one way portal to a destination model transform
//two of these can create a two way wormhole
pub destination_model_id:u32,
//(position,angles)*=origin.transform.inverse()*destination.transform
}
#[derive(Clone)]
pub enum TeleportBehaviour{
StageElement(GameMechanicStageElement),
Wormhole(GameMechanicWormhole),
}
#[derive(Default,Clone)]
pub struct GameMechanicAttributes{
pub jump_limit:Option<GameMechanicJumpLimit>,
pub booster:Option<GameMechanicBooster>,
pub zone:Option<GameMechanicZone>,
pub stage_element:Option<GameMechanicStageElement>,
pub wormhole:Option<GameMechanicWormhole>,//stage_element and wormhole are in conflict
pub teleport_behaviour:Option<TeleportBehaviour>,
}
#[derive(Default,Clone)]
pub struct ContactingAttributes{

@ -27,9 +27,29 @@ pub struct ModelGraphicsSingleTexture{
pub entities: Vec<Vec<u16>>,
pub texture: Option<u32>,
}
#[derive(Clone,PartialEq)]
pub struct ModelGraphicsColor4(glam::Vec4);
impl ModelGraphicsColor4{
pub const fn get(&self)->glam::Vec4{
self.0
}
}
impl From<glam::Vec4> for ModelGraphicsColor4{
fn from(value:glam::Vec4)->Self{
Self(value)
}
}
impl std::hash::Hash for ModelGraphicsColor4{
fn hash<H: std::hash::Hasher>(&self,state:&mut H) {
for &f in self.0.as_ref(){
u32::from_ne_bytes(f.to_ne_bytes()).hash(state);
}
}
}
impl Eq for ModelGraphicsColor4{}
#[derive(Clone)]
pub struct ModelGraphicsInstance{
pub transform:glam::Mat4,
pub normal_transform:glam::Mat3,
pub color:glam::Vec4,
pub color:ModelGraphicsColor4,
}

@ -205,12 +205,13 @@ pub struct WorldState{}
pub struct StyleModifiers{
pub controls_mask:u32,//controls which are unable to be activated
pub controls_held:u32,//controls which must be active to be able to strafe
pub strafe_tick_rate:Ratio64,
pub jump_time:Time,
pub mv:Planar64,
pub walkspeed:Planar64,
pub friction:Planar64,
pub walk_accel:Planar64,
pub gravity:Planar64Vec3,
pub strafe_tick_rate:Ratio64,
pub hitbox_halfsize:Planar64Vec3,
}
impl std::default::Default for StyleModifiers{
@ -219,6 +220,7 @@ impl std::default::Default for StyleModifiers{
controls_mask: !0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
controls_held: 0,
strafe_tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
jump_time: Time::from_nanos(715_588_000/2*100),//0.715588/2.0*100.0
gravity: Planar64Vec3::int(0,-100,0),
friction: Planar64::int(12)/10,
walk_accel: Planar64::int(90),
@ -275,6 +277,10 @@ impl StyleModifiers{
}
return control_dir
}
fn get_jump_power(&self)->Planar64Vec3{
Planar64Vec3::int(0,715588,0)/(2*1000000/100)
}
}
pub struct PhysicsState{
@ -412,6 +418,11 @@ impl Body {
self.time=time;
}
}
impl std::fmt::Display for Body{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"p({}) v({}) a({}) t({})",self.position,self.velocity,self.acceleration,self.time)
}
}
impl Default for PhysicsState{
fn default() -> Self {
@ -643,7 +654,7 @@ impl PhysicsState {
}
fn jump(&mut self){
self.grounded=false;//do I need this?
let mut v=self.body.velocity+Planar64Vec3::int(0,715588,0)/(2*1000000/100);//0.715588/2.0*100.0
let mut v=self.body.velocity+self.style.get_jump_power();
self.contact_constrain_velocity(&mut v);
self.body.velocity=v;
}
@ -1095,8 +1106,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
}
//check ground
self.contacts.insert(c.model,c);
match &general.stage_element{
Some(stage_element)=>{
match &general.teleport_behaviour{
Some(crate::model::TeleportBehaviour::StageElement(stage_element))=>{
if stage_element.force||self.game.stage_id<stage_element.stage_id{
self.game.stage_id=stage_element.stage_id;
}
@ -1122,6 +1133,9 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
crate::model::StageElementBehaviour::Platform=>(),
}
},
Some(crate::model::TeleportBehaviour::Wormhole(wormhole))=>{
//telefart
}
None=>(),
}
//flatten v
@ -1143,8 +1157,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
PhysicsCollisionAttributes::Intersect{intersecting,general}=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
self.intersects.insert(c.model,c);
match &general.stage_element{
Some(stage_element)=>{
match &general.teleport_behaviour{
Some(crate::model::TeleportBehaviour::StageElement(stage_element))=>{
if stage_element.force||self.game.stage_id<stage_element.stage_id{
self.game.stage_id=stage_element.stage_id;
}
@ -1170,6 +1184,9 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
crate::model::StageElementBehaviour::Platform=>(),
}
},
Some(crate::model::TeleportBehaviour::Wormhole(wormhole))=>{
//telefart
}
None=>(),
}
},

@ -100,7 +100,7 @@ fn test_worker() {
};
worker.send(task).unwrap();
println!("value={:?}",worker.grab_clone());
println!("value={}",worker.grab_clone());
// wait long enough to see print from final task
thread::sleep(std::time::Duration::from_secs(1));