Compare commits
13 Commits
master
...
physics-th
Author | SHA1 | Date | |
---|---|---|---|
ede287acd6 | |||
9df4da9d26 | |||
77d060b90c | |||
f320520292 | |||
6fc585e01e | |||
928654c7df | |||
8000d1db29 | |||
194b9b9a11 | |||
4933543dbf | |||
285a19a73a | |||
f3f624c44c | |||
e547940f2b | |||
51aac7b214 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1692,6 +1692,7 @@ dependencies = [
|
||||
"lazy-regex",
|
||||
"log",
|
||||
"obj",
|
||||
"parking_lot",
|
||||
"pollster",
|
||||
"rbx_binary",
|
||||
"rbx_dom_weak",
|
||||
|
@ -14,6 +14,7 @@ glam = "0.24.1"
|
||||
lazy-regex = "3.0.2"
|
||||
log = "0.4.20"
|
||||
obj = "0.10.2"
|
||||
parking_lot = "0.12.1"
|
||||
pollster = "0.3.0"
|
||||
rbx_binary = "0.7.1"
|
||||
rbx_dom_weak = "2.5.0"
|
||||
|
221
src/body.rs
221
src/body.rs
@ -34,7 +34,7 @@ pub enum InputInstruction {
|
||||
//for interpolation / networking / playback reasons, most playback heads will always want
|
||||
//to be 1 instruction ahead to generate the next state for interpolation.
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Body {
|
||||
position: glam::Vec3,//I64 where 2^32 = 1 u
|
||||
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
|
||||
@ -45,20 +45,20 @@ trait MyHash{
|
||||
fn hash(&self) -> u64;
|
||||
}
|
||||
impl MyHash for Body {
|
||||
fn hash(&self) -> u64 {
|
||||
fn hash(&self) -> u64 {
|
||||
let mut hasher=std::collections::hash_map::DefaultHasher::new();
|
||||
for &el in self.position.as_ref().iter() {
|
||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
||||
}
|
||||
for &el in self.velocity.as_ref().iter() {
|
||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
||||
}
|
||||
for &el in self.acceleration.as_ref().iter() {
|
||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
||||
}
|
||||
std::hash::Hasher::write(&mut hasher, self.time.to_ne_bytes().as_slice());
|
||||
for &el in self.position.as_ref().iter() {
|
||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
||||
}
|
||||
for &el in self.velocity.as_ref().iter() {
|
||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
||||
}
|
||||
for &el in self.acceleration.as_ref().iter() {
|
||||
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
|
||||
}
|
||||
std::hash::Hasher::write(&mut hasher, self.time.to_ne_bytes().as_slice());
|
||||
return std::hash::Hasher::finish(&hasher);//hash check to see if walk target is valid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MoveRestriction {
|
||||
@ -80,9 +80,9 @@ impl InputState {
|
||||
}
|
||||
impl crate::instruction::InstructionEmitter<InputInstruction> for InputState{
|
||||
fn next_instruction(&self, time_limit:crate::body::TIME) -> Option<TimedInstruction<InputInstruction>> {
|
||||
//this is polled by PhysicsState for actions like Jump
|
||||
//no, it has to be the other way around. physics is run up until the jump instruction, and then the jump instruction is pushed.
|
||||
self.queue.get(0)
|
||||
//this is polled by PhysicsState for actions like Jump
|
||||
//no, it has to be the other way around. physics is run up until the jump instruction, and then the jump instruction is pushed.
|
||||
self.queue.get(0)
|
||||
}
|
||||
}
|
||||
impl crate::instruction::InstructionConsumer<InputInstruction> for InputState{
|
||||
@ -171,12 +171,12 @@ pub struct Camera {
|
||||
|
||||
#[inline]
|
||||
fn mat3_from_rotation_y_f64(angle: f64) -> glam::Mat3 {
|
||||
let (sina, cosa) = angle.sin_cos();
|
||||
glam::Mat3::from_cols(
|
||||
glam::Vec3::new(cosa as f32, 0.0, -sina as f32),
|
||||
glam::Vec3::Y,
|
||||
glam::Vec3::new(sina as f32, 0.0, cosa as f32),
|
||||
)
|
||||
let (sina, cosa) = angle.sin_cos();
|
||||
glam::Mat3::from_cols(
|
||||
glam::Vec3::new(cosa as f32, 0.0, -sina as f32),
|
||||
glam::Vec3::Y,
|
||||
glam::Vec3::new(sina as f32, 0.0, cosa as f32),
|
||||
)
|
||||
}
|
||||
#[inline]
|
||||
fn perspective_rh(fov_x_slope: f32, fov_y_slope: f32, z_near: f32, z_far: f32) -> glam::Mat4 {
|
||||
@ -192,11 +192,11 @@ fn perspective_rh(fov_x_slope: f32, fov_y_slope: f32, z_near: f32, z_far: f32) -
|
||||
impl Camera {
|
||||
pub fn from_offset(offset:glam::Vec3,aspect:f32) -> Self {
|
||||
Self{
|
||||
offset,
|
||||
angles: glam::DVec2::ZERO,
|
||||
fov: glam::vec2(aspect,1.0),
|
||||
sensitivity: glam::dvec2(1.0/6144.0,1.0/6144.0),
|
||||
time: 0,
|
||||
offset,
|
||||
angles: glam::DVec2::ZERO,
|
||||
fov: glam::vec2(aspect,1.0),
|
||||
sensitivity: glam::dvec2(1.0/16384.0,1.0/16384.0),
|
||||
time: 0,
|
||||
}
|
||||
}
|
||||
fn simulate_move_angles(&self, delta: glam::IVec2) -> glam::DVec2 {
|
||||
@ -221,13 +221,13 @@ impl Camera {
|
||||
}
|
||||
|
||||
pub struct GameMechanicsState{
|
||||
pub spawn_id:u32,
|
||||
pub stage_id:u32,
|
||||
//jump_counts:HashMap<u32,u32>,
|
||||
}
|
||||
impl std::default::Default for GameMechanicsState{
|
||||
fn default() -> 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 game:GameMechanicsState,
|
||||
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>,
|
||||
//camera must exist in state because wormholes modify the camera, also camera punch
|
||||
pub camera:Camera,
|
||||
@ -329,6 +330,7 @@ pub struct PhysicsState{
|
||||
pub models:Vec<ModelPhysics>,
|
||||
|
||||
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.
|
||||
//This is not the same as Reset which teleports you to Spawn0
|
||||
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
|
||||
//in this iteration, all it needs is extents.
|
||||
mesh: TreyMesh,
|
||||
transform:glam::Affine3A,
|
||||
attributes:PhysicsCollisionAttributes,
|
||||
}
|
||||
|
||||
@ -470,13 +473,14 @@ impl ModelPhysics {
|
||||
Self{
|
||||
mesh:aabb,
|
||||
attributes,
|
||||
transform:transform.clone(),
|
||||
}
|
||||
}
|
||||
pub fn from_model(model:&crate::model::IndexedModel,instance:&crate::model::ModelInstance) -> Option<Self> {
|
||||
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::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] {
|
||||
@ -556,6 +560,15 @@ impl PhysicsState {
|
||||
pub fn clear(&mut self){
|
||||
self.models.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
|
||||
pub fn run(&mut self, time_limit:TIME){
|
||||
@ -583,7 +596,7 @@ impl PhysicsState {
|
||||
}
|
||||
|
||||
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 d=velocity.dot(n);
|
||||
if d<0f32{
|
||||
@ -592,7 +605,7 @@ impl PhysicsState {
|
||||
}
|
||||
}
|
||||
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 d=acceleration.dot(n);
|
||||
if d<0f32{
|
||||
@ -965,12 +978,19 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
|
||||
//JUST POLLING!!! NO MUTATION
|
||||
let mut collector = crate::instruction::InstructionCollector::new(time_limit);
|
||||
//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));
|
||||
}
|
||||
// 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!!)
|
||||
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 {
|
||||
//walk maintenance
|
||||
@ -986,56 +1006,109 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
|
||||
impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
|
||||
fn process_instruction(&mut self, ins:TimedInstruction<PhysicsInstruction>) {
|
||||
match &ins.instruction {
|
||||
PhysicsInstruction::StrafeTick => (),
|
||||
PhysicsInstruction::Input(InputInstruction::MoveMouse(_)) => (),
|
||||
_=>println!("{:?}",ins),
|
||||
PhysicsInstruction::StrafeTick => (),
|
||||
PhysicsInstruction::Input(InputInstruction::MoveMouse(_)) => (),
|
||||
_=>println!("{:?}",ins),
|
||||
}
|
||||
//selectively update body
|
||||
match &ins.instruction {
|
||||
PhysicsInstruction::Input(InputInstruction::MoveMouse(_)) => (),//dodge time for mouse movement
|
||||
PhysicsInstruction::Input(_)
|
||||
|PhysicsInstruction::SetSpawnPosition(_)
|
||||
|PhysicsInstruction::ReachWalkTargetVelocity
|
||||
|PhysicsInstruction::CollisionStart(_)
|
||||
|PhysicsInstruction::CollisionEnd(_)
|
||||
|PhysicsInstruction::StrafeTick => self.advance_time(ins.time),
|
||||
PhysicsInstruction::Input(InputInstruction::MoveMouse(_)) => (),//dodge time for mouse movement
|
||||
PhysicsInstruction::Input(_)
|
||||
|PhysicsInstruction::SetSpawnPosition(_)
|
||||
|PhysicsInstruction::ReachWalkTargetVelocity
|
||||
|PhysicsInstruction::CollisionStart(_)
|
||||
|PhysicsInstruction::CollisionEnd(_)
|
||||
|PhysicsInstruction::StrafeTick => self.advance_time(ins.time),
|
||||
}
|
||||
match ins.instruction {
|
||||
PhysicsInstruction::SetSpawnPosition(position)=>{
|
||||
self.spawn_point=position;
|
||||
}
|
||||
PhysicsInstruction::CollisionStart(c) => {
|
||||
//check ground
|
||||
match &c.face {
|
||||
AabbFace::Top => {
|
||||
//ground
|
||||
self.grounded=true;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
self.contacts.insert(c);
|
||||
//flatten v
|
||||
let mut v=self.body.velocity;
|
||||
self.contact_constrain_velocity(&mut v);
|
||||
self.body.velocity=v;
|
||||
if self.grounded&&self.style.get_control(StyleModifiers::CONTROL_JUMP,self.controls){
|
||||
self.jump();
|
||||
let model=c.model(&self.models).unwrap();
|
||||
match &model.attributes{
|
||||
PhysicsCollisionAttributes::Contact{contacting,general}=>{
|
||||
match &contacting.surf{
|
||||
Some(surf)=>println!("I'm surfing!"),
|
||||
None=>match &c.face {
|
||||
AabbFace::Top => {
|
||||
//ground
|
||||
self.grounded=true;
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
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
|
||||
let mut v=self.body.velocity;
|
||||
self.contact_constrain_velocity(&mut v);
|
||||
self.body.velocity=v;
|
||||
if self.grounded&&self.style.get_control(StyleModifiers::CONTROL_JUMP,self.controls){
|
||||
self.jump();
|
||||
}
|
||||
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);
|
||||
},
|
||||
}
|
||||
self.refresh_walk_target();
|
||||
},
|
||||
PhysicsInstruction::CollisionEnd(c) => {
|
||||
self.contacts.remove(&c);//remove contact before calling contact_constrain_acceleration
|
||||
let mut a=self.style.gravity;
|
||||
self.contact_constrain_acceleration(&mut a);
|
||||
self.body.acceleration=a;
|
||||
//check ground
|
||||
match &c.face {
|
||||
AabbFace::Top => {
|
||||
self.grounded=false;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
self.refresh_walk_target();
|
||||
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;
|
||||
self.contact_constrain_acceleration(&mut a);
|
||||
self.body.acceleration=a;
|
||||
//check ground
|
||||
match &c.face {
|
||||
AabbFace::Top => {
|
||||
self.grounded=false;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
self.refresh_walk_target();
|
||||
},
|
||||
PhysicsCollisionAttributes::Intersect{intersecting,general}=>{
|
||||
self.intersects.remove(&c.model);
|
||||
},
|
||||
}
|
||||
},
|
||||
PhysicsInstruction::StrafeTick => {
|
||||
let camera_mat=self.camera.simulate_move_rotation_y(self.mouse_interpolation.interpolated_position(self.time).x-self.mouse_interpolation.mouse0.x);
|
||||
|
@ -30,7 +30,7 @@ fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>
|
||||
//next class
|
||||
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 intersecting=crate::model::IntersectingAttributes::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,
|
||||
}),
|
||||
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){
|
||||
general.stage_element=Some(crate::model::GameMechanicStageElement{
|
||||
mode_id:0,
|
||||
@ -56,7 +56,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3)->crate::model:
|
||||
None=>false,
|
||||
},
|
||||
behaviour:match &captures[2]{
|
||||
"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
|
||||
"Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
|
||||
"Trigger"=>crate::model::StageElementBehaviour::Trigger,
|
||||
"Teleport"=>crate::model::StageElementBehaviour::Teleport,
|
||||
"Platform"=>crate::model::StageElementBehaviour::Platform,
|
||||
@ -89,9 +89,21 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3)->crate::model:
|
||||
//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
|
||||
let mut force_intersecting=false;
|
||||
let mut temp_indexing_attributes=Vec::new();
|
||||
if let Some(attr)=match &object.name[..]{
|
||||
"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}),
|
||||
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) {
|
||||
match &captures[1]{
|
||||
"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()}),
|
||||
_=>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);
|
||||
}
|
||||
|
||||
@ -354,17 +368,17 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
||||
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder,
|
||||
//use front face texture first and use top face texture as a fallback
|
||||
primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([
|
||||
f0,//Wedge::Right
|
||||
if f5.is_some(){f5}else{f1},//Wedge::TopFront
|
||||
f2,//Wedge::Back
|
||||
f3,//Wedge::Left
|
||||
f4,//Wedge::Bottom
|
||||
f0,//Cube::Right->Wedge::Right
|
||||
if f5.is_some(){f5}else{f1},//Cube::Front|Cube::Top->Wedge::TopFront
|
||||
f2,//Cube::Back->Wedge::Back
|
||||
f3,//Cube::Left->Wedge::Left
|
||||
f4,//Cube::Bottom->Wedge::Bottom
|
||||
]),
|
||||
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([
|
||||
f0,//CornerWedge::Right
|
||||
f1,//CornerWedge::Top
|
||||
f4,//CornerWedge::Bottom
|
||||
f5,//CornerWedge::Front
|
||||
f0,//Cube::Right->CornerWedge::Right
|
||||
f1,//Cube::Top->CornerWedge::Top
|
||||
f4,//Cube::Bottom->CornerWedge::Bottom
|
||||
f5,//Cube::Front->CornerWedge::Front
|
||||
]),
|
||||
};
|
||||
//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 {
|
||||
transform:model_transform,
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
26
src/main.rs
26
src/main.rs
@ -11,6 +11,7 @@ mod framework;
|
||||
mod primitives;
|
||||
mod instruction;
|
||||
mod load_roblox;
|
||||
mod worker;
|
||||
|
||||
struct Entity {
|
||||
index_count: u32,
|
||||
@ -104,8 +105,8 @@ impl GlobalState{
|
||||
for model_instance in &model.instances{
|
||||
if let Some(model_physics)=body::ModelPhysics::from_model(model,model_instance){
|
||||
let model_id=self.physics.models.len() as u32;
|
||||
//snoop it before it gets stolen
|
||||
for attr in model_instance.temp_indexing.iter(){
|
||||
self.physics.models.push(model_physics);
|
||||
for attr in &model_instance.temp_indexing{
|
||||
match attr{
|
||||
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)),
|
||||
@ -113,8 +114,6 @@ impl GlobalState{
|
||||
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|{
|
||||
tup.1.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{
|
||||
start:tup.0,
|
||||
spawns:tup.1.into_iter().map(|tup|tup.1).collect(),
|
||||
ordered_checkpoints:tup.2.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().enumerate().map(|(i,tup)|{eshmep2.insert(tup.0,i);tup.1}).collect(),
|
||||
unordered_checkpoints:tup.3,
|
||||
spawn_from_stage_id:eshmep1,
|
||||
ordered_checkpoint_from_checkpoint_id:eshmep2,
|
||||
}
|
||||
}).collect());
|
||||
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.
|
||||
let indexed_models_len=indexed_models.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
|
||||
let instances:Vec<ModelGraphicsInstance>=model.instances.into_iter().filter_map(|instance|{
|
||||
if instance.color.w==0.0{
|
||||
@ -581,7 +588,8 @@ impl framework::Example for GlobalState {
|
||||
time: 0,
|
||||
style:body::StyleModifiers::default(),
|
||||
grounded: false,
|
||||
contacts: std::collections::HashSet::new(),
|
||||
contacts: std::collections::HashMap::new(),
|
||||
intersects: std::collections::HashMap::new(),
|
||||
models: Vec::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)),
|
||||
@ -590,6 +598,7 @@ impl framework::Example for GlobalState {
|
||||
world:body::WorldState{},
|
||||
game:body::GameMechanicsState::default(),
|
||||
modes:Vec::new(),
|
||||
mode_from_mode_id:std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
//load textures
|
||||
@ -904,6 +913,7 @@ impl framework::Example for GlobalState {
|
||||
//if generate_indexed_models succeeds, clear the previous ones
|
||||
self.physics.clear();
|
||||
self.graphics.clear();
|
||||
self.physics.game.stage_id=0;
|
||||
self.generate_model_physics(&indexed_model_instances);
|
||||
self.generate_model_graphics(device,queue,indexed_model_instances);
|
||||
//manual reset
|
||||
|
19
src/model.rs
19
src/model.rs
@ -85,7 +85,26 @@ pub struct ModeDescription{
|
||||
pub spawns:Vec<u32>,//spawns[spawn_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 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{
|
||||
Start{
|
||||
mode_id:u32,
|
||||
|
83
src/worker.rs
Normal file
83
src/worker.rs
Normal 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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user