Compare commits

...

13 Commits

7 changed files with 300 additions and 99 deletions

1
Cargo.lock generated
View File

@ -1692,6 +1692,7 @@ dependencies = [
"lazy-regex", "lazy-regex",
"log", "log",
"obj", "obj",
"parking_lot",
"pollster", "pollster",
"rbx_binary", "rbx_binary",
"rbx_dom_weak", "rbx_dom_weak",

View File

@ -14,6 +14,7 @@ glam = "0.24.1"
lazy-regex = "3.0.2" lazy-regex = "3.0.2"
log = "0.4.20" log = "0.4.20"
obj = "0.10.2" obj = "0.10.2"
parking_lot = "0.12.1"
pollster = "0.3.0" pollster = "0.3.0"
rbx_binary = "0.7.1" rbx_binary = "0.7.1"
rbx_dom_weak = "2.5.0" rbx_dom_weak = "2.5.0"

View File

@ -34,7 +34,7 @@ pub enum InputInstruction {
//for interpolation / networking / playback reasons, most playback heads will always want //for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation. //to be 1 instruction ahead to generate the next state for interpolation.
} }
#[derive(Clone,Debug)]
pub struct Body { pub struct Body {
position: glam::Vec3,//I64 where 2^32 = 1 u position: glam::Vec3,//I64 where 2^32 = 1 u
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
@ -195,7 +195,7 @@ impl Camera {
offset, offset,
angles: glam::DVec2::ZERO, angles: glam::DVec2::ZERO,
fov: glam::vec2(aspect,1.0), fov: glam::vec2(aspect,1.0),
sensitivity: glam::dvec2(1.0/6144.0,1.0/6144.0), sensitivity: glam::dvec2(1.0/16384.0,1.0/16384.0),
time: 0, time: 0,
} }
} }
@ -221,13 +221,13 @@ impl Camera {
} }
pub struct GameMechanicsState{ pub struct GameMechanicsState{
pub spawn_id:u32, pub stage_id:u32,
//jump_counts:HashMap<u32,u32>, //jump_counts:HashMap<u32,u32>,
} }
impl std::default::Default for GameMechanicsState{ impl std::default::Default for GameMechanicsState{
fn default() -> Self { fn default() -> Self {
Self{ Self{
spawn_id:0, stage_id:0,
} }
} }
} }
@ -317,7 +317,8 @@ pub struct PhysicsState{
pub world:WorldState,//currently there is only one state the world can be in pub world:WorldState,//currently there is only one state the world can be in
pub game:GameMechanicsState, pub game:GameMechanicsState,
pub style:StyleModifiers, pub style:StyleModifiers,
pub contacts:std::collections::HashSet::<RelativeCollision>, pub contacts:std::collections::HashMap::<u32,RelativeCollision>,
pub intersects:std::collections::HashMap::<u32,RelativeCollision>,
//pub intersections: Vec<ModelId>, //pub intersections: Vec<ModelId>,
//camera must exist in state because wormholes modify the camera, also camera punch //camera must exist in state because wormholes modify the camera, also camera punch
pub camera:Camera, pub camera:Camera,
@ -329,6 +330,7 @@ pub struct PhysicsState{
pub models:Vec<ModelPhysics>, pub models:Vec<ModelPhysics>,
pub modes:Vec<crate::model::ModeDescription>, pub modes:Vec<crate::model::ModeDescription>,
pub mode_from_mode_id:std::collections::HashMap::<u32,usize>,
//the spawn point is where you spawn when you load into the map. //the spawn point is where you spawn when you load into the map.
//This is not the same as Reset which teleports you to Spawn0 //This is not the same as Reset which teleports you to Spawn0
pub spawn_point:glam::Vec3, pub spawn_point:glam::Vec3,
@ -458,6 +460,7 @@ pub struct ModelPhysics {
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es //A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
//in this iteration, all it needs is extents. //in this iteration, all it needs is extents.
mesh: TreyMesh, mesh: TreyMesh,
transform:glam::Affine3A,
attributes:PhysicsCollisionAttributes, attributes:PhysicsCollisionAttributes,
} }
@ -470,13 +473,14 @@ impl ModelPhysics {
Self{ Self{
mesh:aabb, mesh:aabb,
attributes, attributes,
transform:transform.clone(),
} }
} }
pub fn from_model(model:&crate::model::IndexedModel,instance:&crate::model::ModelInstance) -> Option<Self> { pub fn from_model(model:&crate::model::IndexedModel,instance:&crate::model::ModelInstance) -> Option<Self> {
match &instance.attributes{ match &instance.attributes{
crate::model::CollisionAttributes::Decoration=>None,
crate::model::CollisionAttributes::Contact{contacting,general}=>Some(ModelPhysics::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Contact{contacting:contacting.clone(),general:general.clone()})), crate::model::CollisionAttributes::Contact{contacting,general}=>Some(ModelPhysics::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Contact{contacting:contacting.clone(),general:general.clone()})),
crate::model::CollisionAttributes::Intersect{intersecting,general}=>None,//Some(ModelPhysics::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Intersecting{intersecting,general})), crate::model::CollisionAttributes::Intersect{intersecting,general}=>Some(ModelPhysics::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Intersect{intersecting:intersecting.clone(),general:general.clone()})),
crate::model::CollisionAttributes::Decoration=>None,
} }
} }
pub fn unit_vertices(&self) -> [glam::Vec3;8] { pub fn unit_vertices(&self) -> [glam::Vec3;8] {
@ -556,6 +560,15 @@ impl PhysicsState {
pub fn clear(&mut self){ pub fn clear(&mut self){
self.models.clear(); self.models.clear();
self.modes.clear(); self.modes.clear();
self.contacts.clear();
self.intersects.clear();
}
pub fn get_mode(&self,mode_id:u32)->Option<&crate::model::ModeDescription>{
if let Some(&mode)=self.mode_from_mode_id.get(&mode_id){
self.modes.get(mode)
}else{
None
}
} }
//tickless gaming //tickless gaming
pub fn run(&mut self, time_limit:TIME){ pub fn run(&mut self, time_limit:TIME){
@ -583,7 +596,7 @@ impl PhysicsState {
} }
fn contact_constrain_velocity(&self,velocity:&mut glam::Vec3){ fn contact_constrain_velocity(&self,velocity:&mut glam::Vec3){
for contact in self.contacts.iter() { for (_,contact) in &self.contacts {
let n=contact.normal(&self.models); let n=contact.normal(&self.models);
let d=velocity.dot(n); let d=velocity.dot(n);
if d<0f32{ if d<0f32{
@ -592,7 +605,7 @@ impl PhysicsState {
} }
} }
fn contact_constrain_acceleration(&self,acceleration:&mut glam::Vec3){ fn contact_constrain_acceleration(&self,acceleration:&mut glam::Vec3){
for contact in self.contacts.iter() { for (_,contact) in &self.contacts {
let n=contact.normal(&self.models); let n=contact.normal(&self.models);
let d=acceleration.dot(n); let d=acceleration.dot(n);
if d<0f32{ if d<0f32{
@ -965,12 +978,19 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
//JUST POLLING!!! NO MUTATION //JUST POLLING!!! NO MUTATION
let mut collector = crate::instruction::InstructionCollector::new(time_limit); let mut collector = crate::instruction::InstructionCollector::new(time_limit);
//check for collision stop instructions with curent contacts //check for collision stop instructions with curent contacts
for collision_data in self.contacts.iter() { for (_,collision_data) in &self.contacts {
collector.collect(self.predict_collision_end(self.time,time_limit,collision_data)); collector.collect(self.predict_collision_end(self.time,time_limit,collision_data));
} }
// for collision_data in &self.intersects{
// collector.collect(self.predict_collision_end2(self.time,time_limit,collision_data));
// }
//check for collision start instructions (against every part in the game with no optimization!!) //check for collision start instructions (against every part in the game with no optimization!!)
for i in 0..self.models.len() { for i in 0..self.models.len() {
collector.collect(self.predict_collision_start(self.time,time_limit,i as u32)); let i=i as u32;
if self.contacts.contains_key(&i)||self.intersects.contains_key(&i){
continue;
}
collector.collect(self.predict_collision_start(self.time,time_limit,i));
} }
if self.grounded { if self.grounded {
//walk maintenance //walk maintenance
@ -1005,15 +1025,54 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
self.spawn_point=position; self.spawn_point=position;
} }
PhysicsInstruction::CollisionStart(c) => { PhysicsInstruction::CollisionStart(c) => {
//check ground let model=c.model(&self.models).unwrap();
match &c.face { match &model.attributes{
PhysicsCollisionAttributes::Contact{contacting,general}=>{
match &contacting.surf{
Some(surf)=>println!("I'm surfing!"),
None=>match &c.face {
AabbFace::Top => { AabbFace::Top => {
//ground //ground
self.grounded=true; self.grounded=true;
}, },
_ => (), _ => (),
},
} }
self.contacts.insert(c); match &general.booster{
Some(booster)=>self.body.velocity+=booster.velocity,
None=>(),
}
match &general.stage_element{
Some(stage_element)=>{
if stage_element.force||self.game.stage_id<stage_element.stage_id{
self.game.stage_id=stage_element.stage_id;
}
match stage_element.behaviour{
crate::model::StageElementBehaviour::SpawnAt=>(),
crate::model::StageElementBehaviour::Trigger
|crate::model::StageElementBehaviour::Teleport=>{
//TODO make good
if let Some(mode)=self.get_mode(stage_element.mode_id){
if let Some(&spawn)=mode.get_spawn_model_id(self.game.stage_id){
if let Some(model)=self.models.get(spawn as usize){
self.body.position=model.transform.transform_point3(glam::Vec3::Y)+glam::Vec3::Y*(self.style.hitbox_halfsize.y+0.1);
//manual clear //for c in self.contacts{process_instruction(CollisionEnd(c))}
self.contacts.clear();
self.intersects.clear();
self.body.acceleration=self.style.gravity;
self.walk.state=WalkEnum::Reached;
self.grounded=false;
}else{println!("bad1");}
}else{println!("bad2");}
}else{println!("bad3");}
},
crate::model::StageElementBehaviour::Platform=>(),
}
},
None=>(),
}
//check ground
self.contacts.insert(c.model,c);
//flatten v //flatten v
let mut v=self.body.velocity; let mut v=self.body.velocity;
self.contact_constrain_velocity(&mut v); self.contact_constrain_velocity(&mut v);
@ -1023,8 +1082,17 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
} }
self.refresh_walk_target(); self.refresh_walk_target();
}, },
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);
},
}
},
PhysicsInstruction::CollisionEnd(c) => { PhysicsInstruction::CollisionEnd(c) => {
self.contacts.remove(&c);//remove contact before calling contact_constrain_acceleration let model=c.model(&self.models).unwrap();
match &model.attributes{
PhysicsCollisionAttributes::Contact{contacting,general}=>{
self.contacts.remove(&c.model);//remove contact before calling contact_constrain_acceleration
let mut a=self.style.gravity; let mut a=self.style.gravity;
self.contact_constrain_acceleration(&mut a); self.contact_constrain_acceleration(&mut a);
self.body.acceleration=a; self.body.acceleration=a;
@ -1037,6 +1105,11 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
} }
self.refresh_walk_target(); self.refresh_walk_target();
}, },
PhysicsCollisionAttributes::Intersect{intersecting,general}=>{
self.intersects.remove(&c.model);
},
}
},
PhysicsInstruction::StrafeTick => { PhysicsInstruction::StrafeTick => {
let camera_mat=self.camera.simulate_move_rotation_y(self.mouse_interpolation.interpolated_position(self.time).x-self.mouse_interpolation.mouse0.x); let camera_mat=self.camera.simulate_move_rotation_y(self.mouse_interpolation.interpolated_position(self.time).x-self.mouse_interpolation.mouse0.x);
let control_dir=camera_mat*self.style.get_control_dir(self.controls); let control_dir=camera_mat*self.style.get_control_dir(self.controls);

View File

@ -30,7 +30,7 @@ fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>
//next class //next class
objects objects
} }
fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3)->crate::model::CollisionAttributes{ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersecting:bool)->crate::model::CollisionAttributes{
let mut general=crate::model::GameMechanicAttributes::default(); let mut general=crate::model::GameMechanicAttributes::default();
let mut intersecting=crate::model::IntersectingAttributes::default(); let mut intersecting=crate::model::IntersectingAttributes::default();
let mut contacting=crate::model::ContactingAttributes::default(); let mut contacting=crate::model::ContactingAttributes::default();
@ -46,7 +46,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3)->crate::model:
behaviour:crate::model::StageElementBehaviour::Platform, behaviour:crate::model::StageElementBehaviour::Platform,
}), }),
other=>{ other=>{
if let Some(captures)=lazy_regex::regex!(r"^(Force)?(SpawnAt|Trigger|Teleport|Platform)(\d+)$") if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
.captures(other){ .captures(other){
general.stage_element=Some(crate::model::GameMechanicStageElement{ general.stage_element=Some(crate::model::GameMechanicStageElement{
mode_id:0, mode_id:0,
@ -56,7 +56,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3)->crate::model:
None=>false, None=>false,
}, },
behaviour:match &captures[2]{ behaviour:match &captures[2]{
"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt, "Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
"Trigger"=>crate::model::StageElementBehaviour::Trigger, "Trigger"=>crate::model::StageElementBehaviour::Trigger,
"Teleport"=>crate::model::StageElementBehaviour::Teleport, "Teleport"=>crate::model::StageElementBehaviour::Teleport,
"Platform"=>crate::model::StageElementBehaviour::Platform, "Platform"=>crate::model::StageElementBehaviour::Platform,
@ -89,9 +89,21 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3)->crate::model:
//WormholeIn# //WormholeIn#
} }
} }
return crate::model::CollisionAttributes::Contact{contacting,general}; crate::model::CollisionAttributes::Contact{contacting,general}
},
false=>if force_intersecting
||general.jump_limit.is_some()
||general.booster.is_some()
||general.zone.is_some()
||general.stage_element.is_some()
||general.wormhole.is_some()
||intersecting.water.is_some()
||intersecting.accelerator.is_some()
{
crate::model::CollisionAttributes::Intersect{intersecting,general}
}else{
crate::model::CollisionAttributes::Decoration
}, },
false=>return crate::model::CollisionAttributes::Decoration,
} }
} }
@ -219,6 +231,7 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
); );
//push TempIndexedAttributes //push TempIndexedAttributes
let mut force_intersecting=false;
let mut temp_indexing_attributes=Vec::new(); let mut temp_indexing_attributes=Vec::new();
if let Some(attr)=match &object.name[..]{ if let Some(attr)=match &object.name[..]{
"MapStart"=>{ "MapStart"=>{
@ -227,11 +240,11 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
}, },
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}), "UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}),
other=>{ other=>{
let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|OrderedCheckpoint)(\d+)$"); let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|OrderedCheckpoint)(\d+)$");
if let Some(captures) = regman.captures(other) { if let Some(captures) = regman.captures(other) {
match &captures[1]{ match &captures[1]{
"BonusStart"=>Some(crate::model::TempIndexedAttributes::Start{mode_id:captures[2].parse::<u32>().unwrap()}), "BonusStart"=>Some(crate::model::TempIndexedAttributes::Start{mode_id:captures[2].parse::<u32>().unwrap()}),
"Spawn"=>Some(crate::model::TempIndexedAttributes::Spawn{mode_id:0,stage_id:captures[2].parse::<u32>().unwrap()}), "Spawn"|"ForceSpawn"=>Some(crate::model::TempIndexedAttributes::Spawn{mode_id:0,stage_id:captures[2].parse::<u32>().unwrap()}),
"OrderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::OrderedCheckpoint{mode_id:0,checkpoint_id:captures[2].parse::<u32>().unwrap()}), "OrderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::OrderedCheckpoint{mode_id:0,checkpoint_id:captures[2].parse::<u32>().unwrap()}),
_=>None, _=>None,
} }
@ -240,6 +253,7 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
} }
} }
}{ }{
force_intersecting=true;
temp_indexing_attributes.push(attr); temp_indexing_attributes.push(attr);
} }
@ -354,17 +368,17 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder, primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder,
//use front face texture first and use top face texture as a fallback //use front face texture first and use top face texture as a fallback
primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([ primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([
f0,//Wedge::Right f0,//Cube::Right->Wedge::Right
if f5.is_some(){f5}else{f1},//Wedge::TopFront if f5.is_some(){f5}else{f1},//Cube::Front|Cube::Top->Wedge::TopFront
f2,//Wedge::Back f2,//Cube::Back->Wedge::Back
f3,//Wedge::Left f3,//Cube::Left->Wedge::Left
f4,//Wedge::Bottom f4,//Cube::Bottom->Wedge::Bottom
]), ]),
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([ primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([
f0,//CornerWedge::Right f0,//Cube::Right->CornerWedge::Right
f1,//CornerWedge::Top f1,//Cube::Top->CornerWedge::Top
f4,//CornerWedge::Bottom f4,//Cube::Bottom->CornerWedge::Bottom
f5,//CornerWedge::Front f5,//Cube::Front->CornerWedge::Front
]), ]),
}; };
//make new model if unit cube has not been created before //make new model if unit cube has not been created before
@ -440,7 +454,7 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
indexed_models[model_id].instances.push(crate::model::ModelInstance { indexed_models[model_id].instances.push(crate::model::ModelInstance {
transform:model_transform, transform:model_transform,
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency), color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
attributes:get_attributes(&object.name,*can_collide,glam::vec3(velocity.x,velocity.y,velocity.z)), attributes:get_attributes(&object.name,*can_collide,glam::vec3(velocity.x,velocity.y,velocity.z),force_intersecting),
temp_indexing:temp_indexing_attributes, temp_indexing:temp_indexing_attributes,
}); });
} }

View File

@ -11,6 +11,7 @@ mod framework;
mod primitives; mod primitives;
mod instruction; mod instruction;
mod load_roblox; mod load_roblox;
mod worker;
struct Entity { struct Entity {
index_count: u32, index_count: u32,
@ -104,8 +105,8 @@ impl GlobalState{
for model_instance in &model.instances{ for model_instance in &model.instances{
if let Some(model_physics)=body::ModelPhysics::from_model(model,model_instance){ if let Some(model_physics)=body::ModelPhysics::from_model(model,model_instance){
let model_id=self.physics.models.len() as u32; let model_id=self.physics.models.len() as u32;
//snoop it before it gets stolen self.physics.models.push(model_physics);
for attr in model_instance.temp_indexing.iter(){ for attr in &model_instance.temp_indexing{
match attr{ match attr{
model::TempIndexedAttributes::Start{mode_id}=>starts.push((*mode_id,model_id)), model::TempIndexedAttributes::Start{mode_id}=>starts.push((*mode_id,model_id)),
model::TempIndexedAttributes::Spawn{mode_id,stage_id}=>spawns.push((*mode_id,model_id,*stage_id)), model::TempIndexedAttributes::Spawn{mode_id,stage_id}=>spawns.push((*mode_id,model_id,*stage_id)),
@ -113,8 +114,6 @@ impl GlobalState{
model::TempIndexedAttributes::UnorderedCheckpoint{mode_id}=>unordered_checkpoints.push((*mode_id,model_id)), model::TempIndexedAttributes::UnorderedCheckpoint{mode_id}=>unordered_checkpoints.push((*mode_id,model_id)),
} }
} }
//steal it
self.physics.models.push(model_physics);
} }
} }
} }
@ -147,14 +146,22 @@ impl GlobalState{
} }
} }
} }
let num_modes=self.physics.modes.len();
for (mode_id,mode) in eshmep{
self.physics.mode_from_mode_id.insert(mode_id,num_modes+mode);
}
self.physics.modes.append(&mut modedatas.into_iter().map(|mut tup|{ self.physics.modes.append(&mut modedatas.into_iter().map(|mut tup|{
tup.1.sort_by_key(|tup|tup.0); tup.1.sort_by_key(|tup|tup.0);
tup.2.sort_by_key(|tup|tup.0); tup.2.sort_by_key(|tup|tup.0);
let mut eshmep1=std::collections::HashMap::new();
let mut eshmep2=std::collections::HashMap::new();
model::ModeDescription{ model::ModeDescription{
start:tup.0, start:tup.0,
spawns:tup.1.into_iter().map(|tup|tup.1).collect(), spawns:tup.1.into_iter().enumerate().map(|(i,tup)|{eshmep1.insert(tup.0,i);tup.1}).collect(),
ordered_checkpoints:tup.2.into_iter().map(|tup|tup.1).collect(), ordered_checkpoints:tup.2.into_iter().enumerate().map(|(i,tup)|{eshmep2.insert(tup.0,i);tup.1}).collect(),
unordered_checkpoints:tup.3, unordered_checkpoints:tup.3,
spawn_from_stage_id:eshmep1,
ordered_checkpoint_from_checkpoint_id:eshmep2,
} }
}).collect()); }).collect());
println!("Physics Objects: {}",self.physics.models.len()); println!("Physics Objects: {}",self.physics.models.len());
@ -228,7 +235,7 @@ impl GlobalState{
//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. //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=indexed_models.models.len(); let indexed_models_len=indexed_models.models.len();
let mut unique_texture_models=Vec::with_capacity(indexed_models_len); let mut unique_texture_models=Vec::with_capacity(indexed_models_len);
for mut model in indexed_models.models.into_iter(){ for model in indexed_models.models.into_iter(){
//convert ModelInstance into ModelGraphicsInstance //convert ModelInstance into ModelGraphicsInstance
let instances:Vec<ModelGraphicsInstance>=model.instances.into_iter().filter_map(|instance|{ let instances:Vec<ModelGraphicsInstance>=model.instances.into_iter().filter_map(|instance|{
if instance.color.w==0.0{ if instance.color.w==0.0{
@ -581,7 +588,8 @@ impl framework::Example for GlobalState {
time: 0, time: 0,
style:body::StyleModifiers::default(), style:body::StyleModifiers::default(),
grounded: false, grounded: false,
contacts: std::collections::HashSet::new(), contacts: std::collections::HashMap::new(),
intersects: std::collections::HashMap::new(),
models: Vec::new(), models: Vec::new(),
walk: body::WalkState::new(), walk: body::WalkState::new(),
camera: body::Camera::from_offset(glam::vec3(0.0,4.5-2.5,0.0),(config.width as f32)/(config.height as f32)), camera: body::Camera::from_offset(glam::vec3(0.0,4.5-2.5,0.0),(config.width as f32)/(config.height as f32)),
@ -590,6 +598,7 @@ impl framework::Example for GlobalState {
world:body::WorldState{}, world:body::WorldState{},
game:body::GameMechanicsState::default(), game:body::GameMechanicsState::default(),
modes:Vec::new(), modes:Vec::new(),
mode_from_mode_id:std::collections::HashMap::new(),
}; };
//load textures //load textures
@ -904,6 +913,7 @@ impl framework::Example for GlobalState {
//if generate_indexed_models succeeds, clear the previous ones //if generate_indexed_models succeeds, clear the previous ones
self.physics.clear(); self.physics.clear();
self.graphics.clear(); self.graphics.clear();
self.physics.game.stage_id=0;
self.generate_model_physics(&indexed_model_instances); self.generate_model_physics(&indexed_model_instances);
self.generate_model_graphics(device,queue,indexed_model_instances); self.generate_model_graphics(device,queue,indexed_model_instances);
//manual reset //manual reset

View File

@ -85,7 +85,26 @@ pub struct ModeDescription{
pub spawns:Vec<u32>,//spawns[spawn_id]=model_id pub spawns:Vec<u32>,//spawns[spawn_id]=model_id
pub ordered_checkpoints:Vec<u32>,//ordered_checkpoints[checkpoint_id]=model_id pub ordered_checkpoints:Vec<u32>,//ordered_checkpoints[checkpoint_id]=model_id
pub unordered_checkpoints:Vec<u32>,//unordered_checkpoints[checkpoint_id]=model_id pub unordered_checkpoints:Vec<u32>,//unordered_checkpoints[checkpoint_id]=model_id
pub spawn_from_stage_id:std::collections::HashMap::<u32,usize>,
pub ordered_checkpoint_from_checkpoint_id:std::collections::HashMap::<u32,usize>,
} }
impl ModeDescription{
pub fn get_spawn_model_id(&self,stage_id:u32)->Option<&u32>{
if let Some(&spawn)=self.spawn_from_stage_id.get(&stage_id){
self.spawns.get(spawn)
}else{
None
}
}
pub fn get_ordered_checkpoint_model_id(&self,checkpoint_id:u32)->Option<&u32>{
if let Some(&checkpoint)=self.ordered_checkpoint_from_checkpoint_id.get(&checkpoint_id){
self.ordered_checkpoints.get(checkpoint)
}else{
None
}
}
}
#[derive(Debug)]
pub enum TempIndexedAttributes{ pub enum TempIndexedAttributes{
Start{ Start{
mode_id:u32, mode_id:u32,

83
src/worker.rs Normal file
View File

@ -0,0 +1,83 @@
use std::thread;
use std::sync::{mpsc,Arc};
use parking_lot::Mutex;
//The goal here is to have a worker thread that parks itself when it runs out of work.
//The worker thread publishes the result of its work back to the worker object for every item in the work queue.
//The physics (target use case) knows when it has not changed the body, so not updating the value is also an option.
struct Worker<Task:Send,Value:Clone> {
sender: mpsc::Sender<Task>,
value:Arc<Mutex<Value>>,
}
impl<Task:Send+'static,Value:Clone+Send+'static> Worker<Task,Value> {
fn new<F:Fn(Task)->Value+Send+'static>(value:Value,f:F) -> Self {
let (sender, receiver) = mpsc::channel::<Task>();
let ret=Self {
sender,
value:Arc::new(Mutex::new(value)),
};
let value=ret.value.clone();
thread::spawn(move || {
loop {
match receiver.recv() {
Ok(task) => {
println!("Worker got a task");
// Process the task
*value.lock()=f(task);
}
Err(_) => {
println!("Worker stopping.",);
break;
}
}
}
});
ret
}
fn send(&self,task:Task)->Result<(), mpsc::SendError<Task>>{
self.sender.send(task)
}
fn grab_clone(&self)->Value{
self.value.lock().clone()
}
}
#[test]//How to run this test with printing: cargo test --release -- --nocapture
fn test_worker() {
println!("hiiiii");
// Create the worker thread
let worker = Worker::new(crate::body::Body::with_pva(glam::Vec3::ZERO,glam::Vec3::ZERO,glam::Vec3::ZERO),
|_|crate::body::Body::with_pva(glam::Vec3::ONE,glam::Vec3::ONE,glam::Vec3::ONE)
);
// Send tasks to the worker
for i in 0..5 {
let task = crate::instruction::TimedInstruction{
time:0,
instruction:crate::body::PhysicsInstruction::StrafeTick,
};
worker.send(task).unwrap();
}
// Optional: Signal the worker to stop (in a real-world scenario)
// sender.send("STOP".to_string()).unwrap();
// Sleep to allow the worker thread to finish processing
thread::sleep(std::time::Duration::from_secs(2));
// Send a new task
let task = crate::instruction::TimedInstruction{
time:0,
instruction:crate::body::PhysicsInstruction::StrafeTick,
};
worker.send(task).unwrap();
println!("value={:?}",worker.grab_clone());
// wait long enough to see print from final task
thread::sleep(std::time::Duration::from_secs(1));
}