Compare commits

..

1 Commits

Author SHA1 Message Date
e9bf4db43e deduplicate models 2023-10-16 15:14:27 -07:00
12 changed files with 797 additions and 1372 deletions

691
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -22,7 +22,7 @@ rbx_dom_weak = "2.5.0"
rbx_reflection_database = "0.2.7"
rbx_xml = "0.13.1"
wgpu = "0.17.0"
winit = { version = "0.29.2", features = ["rwh_05"] }
winit = "0.28.6"
#[profile.release]
#lto = true

@ -12,12 +12,12 @@ use crate::aabb::Aabb;
#[derive(Default)]
pub struct BvhNode{
children:Vec<Self>,
models:Vec<usize>,
models:Vec<u32>,
aabb:Aabb,
}
impl BvhNode{
pub fn the_tester<F:FnMut(usize)>(&self,aabb:&Aabb,f:&mut F){
pub fn the_tester<F:FnMut(u32)>(&self,aabb:&Aabb,f:&mut F){
for &model in &self.models{
f(model);
}
@ -37,7 +37,7 @@ fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
let n=boxen.len();
if n<20{
let mut aabb=Aabb::default();
let models=boxen.into_iter().map(|b|{aabb.join(&b.1);b.0}).collect();
let models=boxen.into_iter().map(|b|{aabb.join(&b.1);b.0 as u32}).collect();
BvhNode{
children:Vec::new(),
models,

@ -5,7 +5,7 @@ use std::str::FromStr;
use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas};
use winit::{
event::{self, WindowEvent, DeviceEvent},
event_loop::EventLoop,
event_loop::{ControlFlow, EventLoop},
};
#[allow(dead_code)]
@ -88,7 +88,7 @@ async fn setup<E: Example>(title: &str) -> Setup {
env_logger::init();
};
let event_loop = EventLoop::new().unwrap();
let event_loop = EventLoop::new();
let mut builder = winit::window::WindowBuilder::new();
builder = builder.with_title(title);
#[cfg(windows_OFF)] // TODO
@ -302,15 +302,15 @@ fn start<E: Example>(
let mut example = E::init(&config, &adapter, &device, &queue);
log::info!("Entering render loop...");
event_loop.run(move |event, elwt| {
event_loop.run(move |event, _, control_flow| {
let _ = (&instance, &adapter); // force ownership by the closure
// *control_flow = if cfg!(feature = "metal-auto-capture") {
// ControlFlow::Exit
// } else {
// ControlFlow::Poll
// };
*control_flow = if cfg!(feature = "metal-auto-capture") {
ControlFlow::Exit
} else {
ControlFlow::Poll
};
match event {
event::Event::AboutToWait => {
event::Event::RedrawEventsCleared => {
#[cfg(not(target_arch = "wasm32"))]
spawner.run_until_stalled();
@ -318,44 +318,48 @@ fn start<E: Example>(
}
event::Event::WindowEvent {
event:
// WindowEvent::Resized(size)
// | WindowEvent::ScaleFactorChanged {
// new_inner_size: &mut size,
// ..
// },
WindowEvent::Resized(size),//ignoring scale factor changed for now because mutex bruh
WindowEvent::Resized(size)
| WindowEvent::ScaleFactorChanged {
new_inner_size: &mut size,
..
},
..
} => {
log::info!("Resizing to {:?}", size);
config.width = size.width.max(1);
config.height = size.height.max(1);
example.resize(&config, &device, &queue);
surface.configure(&device, &config);
// Once winit is fixed, the detection conditions here can be removed.
// https://github.com/rust-windowing/winit/issues/2876
let max_dimension = adapter.limits().max_texture_dimension_2d;
if size.width > max_dimension || size.height > max_dimension {
log::warn!(
"The resizing size {:?} exceeds the limit of {}.",
size,
max_dimension
);
} else {
log::info!("Resizing to {:?}", size);
config.width = size.width.max(1);
config.height = size.height.max(1);
example.resize(&config, &device, &queue);
surface.configure(&device, &config);
}
}
event::Event::DeviceEvent {
event,
..
} => {
example.device_event(&window,event);
},
event::Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
event:
event::KeyEvent {
logical_key: winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape),
input:
event::KeyboardInput {
virtual_keycode: Some(event::VirtualKeyCode::Escape),
state: event::ElementState::Pressed,
..
},
..
}
| WindowEvent::CloseRequested => {
elwt.exit();
*control_flow = ControlFlow::Exit;
}
#[cfg(not(target_arch = "wasm32"))]
WindowEvent::KeyboardInput {
event:
event::KeyEvent {
logical_key: winit::keyboard::Key::Named(winit::keyboard::NamedKey::ScrollLock),
input:
event::KeyboardInput {
virtual_keycode: Some(event::VirtualKeyCode::Scroll),
state: event::ElementState::Pressed,
..
},
@ -363,47 +367,54 @@ fn start<E: Example>(
} => {
println!("{:#?}", instance.generate_report());
}
WindowEvent::RedrawRequested => {
let frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(_) => {
surface.configure(&device, &config);
surface
.get_current_texture()
.expect("Failed to acquire next surface texture!")
}
};
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
format: Some(surface_view_format),
..wgpu::TextureViewDescriptor::default()
});
example.render(&view, &device, &queue, &spawner);
frame.present();
#[cfg(target_arch = "wasm32")]
{
if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup {
let image_bitmap = offscreen_canvas_setup
.offscreen_canvas
.transfer_to_image_bitmap()
.expect("couldn't transfer offscreen canvas to image bitmap.");
offscreen_canvas_setup
.bitmap_renderer
.transfer_from_image_bitmap(&image_bitmap);
log::info!("Transferring OffscreenCanvas to ImageBitmapRenderer");
}
}
}
_ => {
example.update(&window,&device,&queue,event);
}
},
event::Event::DeviceEvent {
event,
..
} => {
example.device_event(&window,event);
},
event::Event::RedrawRequested(_) => {
let frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(_) => {
surface.configure(&device, &config);
surface
.get_current_texture()
.expect("Failed to acquire next surface texture!")
}
};
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
format: Some(surface_view_format),
..wgpu::TextureViewDescriptor::default()
});
example.render(&view, &device, &queue, &spawner);
frame.present();
#[cfg(target_arch = "wasm32")]
{
if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup {
let image_bitmap = offscreen_canvas_setup
.offscreen_canvas
.transfer_to_image_bitmap()
.expect("couldn't transfer offscreen canvas to image bitmap.");
offscreen_canvas_setup
.bitmap_renderer
.transfer_from_image_bitmap(&image_bitmap);
log::info!("Transferring OffscreenCanvas to ImageBitmapRenderer");
}
}
}
_ => {}
}
}).unwrap();
});
}
#[cfg(not(target_arch = "wasm32"))]

@ -531,14 +531,7 @@ impl std::ops::Mul<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Planar64(((self.0 as i128*rhs.0 as i128)>>32) as i64)
}
}
impl std::ops::Mul<Time> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self,rhs:Time)->Self::Output{
Planar64(((self.0 as i128*rhs.0 as i128)/1_000_000_000) as i64)
Planar64((((self.0 as i128)*(rhs.0 as i128))>>32) as i64)
}
}
impl std::ops::Div<i64> for Planar64{
@ -581,10 +574,6 @@ impl Planar64Vec3{
Self(glam::i64vec3((x as i64)<<32,(y as i64)<<32,(z as i64)<<32))
}
#[inline]
pub const fn raw(x:i64,y:i64,z:i64)->Self{
Self(glam::i64vec3(x,y,z))
}
#[inline]
pub fn x(&self)->Planar64{
Planar64(self.0.x)
}
@ -819,23 +808,6 @@ impl Planar64Mat3{
}
}
#[inline]
pub fn from_rotation_yx(yaw:Angle32,pitch:Angle32)->Self{
let xtheta=yaw.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (xs,xc)=xtheta.sin_cos();
let (xc,xs)=(xc*PLANAR64_ONE_FLOAT64,xs*PLANAR64_ONE_FLOAT64);
let ytheta=pitch.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (ys,yc)=ytheta.sin_cos();
let (yc,ys)=(yc*PLANAR64_ONE_FLOAT64,ys*PLANAR64_ONE_FLOAT64);
//TODO: fix this rounding towards 0
let (xc,xs):(i64,i64)=(unsafe{xc.to_int_unchecked()},unsafe{xs.to_int_unchecked()});
let (yc,ys):(i64,i64)=(unsafe{yc.to_int_unchecked()},unsafe{ys.to_int_unchecked()});
Self::from_cols(
Planar64Vec3(glam::i64vec3(xc,0,-xs)),
Planar64Vec3(glam::i64vec3(((xs as i128*ys as i128)>>32) as i64,yc,((xc as i128*ys as i128)>>32) as i64)),
Planar64Vec3(glam::i64vec3(((xs as i128*yc as i128)>>32) as i64,-ys,((xc as i128*yc as i128)>>32) as i64)),
)
}
#[inline]
pub fn from_rotation_y(angle:Angle32)->Self{
let theta=angle.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (s,c)=theta.sin_cos();

@ -50,17 +50,8 @@ 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"=>{
force_can_collide=false;
//TODO: read stupid CustomPhysicalProperties
intersecting.water=Some(crate::model::IntersectingWater{density:Planar64::ONE,viscosity:Planar64::ONE/10,current:velocity});
},
"Accelerator"=>{
//although the new game supports collidable accelerators, this is a roblox compatability map loader
force_can_collide=false;
general.accelerator=Some(crate::model::GameMechanicAccelerator{acceleration:velocity});
},
"SetVelocity"=>general.trajectory=Some(crate::model::GameMechanicSetTrajectory::Velocity(velocity)),
"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.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
@ -81,28 +72,12 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse
},
behaviour:match &captures[2]{
"Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
//cancollide false so you don't hit the side
//NOT a decoration
"Trigger"=>{force_can_collide=false;crate::model::StageElementBehaviour::Trigger},
"Teleport"=>{force_can_collide=false;crate::model::StageElementBehaviour::Teleport},
"Platform"=>crate::model::StageElementBehaviour::Platform,
_=>panic!("regex1[2] messed up bad"),
}
}));
}else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Jump)(\d+)$")
.captures(other){
general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
mode_id:0,
stage_id:0,
force:match captures.get(1){
Some(m)=>m.as_str()=="Force",
None=>false,
},
behaviour:match &captures[2]{
"Jump"=>crate::model::StageElementBehaviour::JumpLimit(captures[3].parse::<u32>().unwrap()),
_=>panic!("regex4[1] messed up bad"),
}
}));
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
.captures(other){
force_can_collide=false;
@ -111,33 +86,39 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse
"Anticheat"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Anitcheat}),
_=>panic!("regex2[1] messed up bad"),
}
}else if let Some(captures)=lazy_regex::regex!(r"^(WormholeIn)(\d+)$")
.captures(other){
force_can_collide=false;
match &captures[1]{
"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"),
}
}
}
}
//need some way to skip this
if velocity!=Planar64Vec3::ZERO{
general.booster=Some(crate::model::GameMechanicBooster::Velocity(velocity));
general.booster=Some(crate::model::GameMechanicBooster{velocity});
}
match force_can_collide{
true=>{
match name{
"Bounce"=>contacting.contact_behaviour=Some(crate::model::ContactingBehaviour::Elastic(u32::MAX)),
"Surf"=>contacting.contact_behaviour=Some(crate::model::ContactingBehaviour::Surf),
"Ladder"=>contacting.contact_behaviour=Some(crate::model::ContactingBehaviour::Ladder(crate::model::ContactingLadder{sticky:true})),
_=>(),
"Bounce"=>contacting.elasticity=Some(u32::MAX),
"Surf"=>contacting.surf=Some(crate::model::ContactingSurf{}),
"Ladder"=>contacting.ladder=Some(crate::model::ContactingLadder{sticky:true}),
other=>{
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}
},
false=>if force_intersecting
||general.any()
||intersecting.any()
||general.jump_limit.is_some()
||general.booster.is_some()
||general.zone.is_some()
||general.teleport_behaviour.is_some()
||intersecting.water.is_some()
||intersecting.accelerator.is_some()
{
crate::model::CollisionAttributes::Intersect{intersecting,general}
}else{
@ -263,17 +244,16 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
if let Some(attr)=match &object.name[..]{
"MapStart"=>{
spawn_point=model_transform.transform_point3(Planar64Vec3::ZERO)+Planar64Vec3::Y*5/2;
Some(crate::model::TempIndexedAttributes::Start(crate::model::TempAttrStart{mode_id:0}))
Some(crate::model::TempIndexedAttributes::Start{mode_id:0})
},
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint(crate::model::TempAttrUnorderedCheckpoint{mode_id:0})),
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}),
other=>{
let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|OrderedCheckpoint|WormholeOut)(\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(crate::model::TempAttrStart{mode_id:captures[2].parse::<u32>().unwrap()})),
"Spawn"|"ForceSpawn"=>Some(crate::model::TempIndexedAttributes::Spawn(crate::model::TempAttrSpawn{mode_id:0,stage_id:captures[2].parse::<u32>().unwrap()})),
"OrderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::OrderedCheckpoint(crate::model::TempAttrOrderedCheckpoint{mode_id:0,checkpoint_id:captures[2].parse::<u32>().unwrap()})),
"WormholeOut"=>Some(crate::model::TempIndexedAttributes::Wormhole(crate::model::TempAttrWormhole{wormhole_id:captures[2].parse::<u32>().unwrap()})),
"BonusStart"=>Some(crate::model::TempIndexedAttributes::Start{mode_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,
}
}else{

@ -234,10 +234,6 @@ impl GlobalState{
})
}
}).collect();
//skip pushing a model if all instances are invisible
if instances.len()==0{
continue;
}
//check each group, if it's using a new texture then make a new clone of the model
let id=unique_texture_models.len();
let mut unique_textures=Vec::new();
@ -305,24 +301,27 @@ impl GlobalState{
model_instance_list.push((model_id,instance_id));
}
}
//populate a hashset of models selected for transposition
//construct transposed models
//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(){
//world transforming one model does not meet the definition of deduplicaiton
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
@ -333,73 +332,78 @@ impl GlobalState{
let model=&unique_texture_models[model_id];
let instance=&model.instances[instance_id];
//just hash word slices LOL
let map_pos_id:Vec<u32>=model.unique_pos.iter().map(|untransformed_pos|{
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|bytemuck::cast::<f32,u32>(v));
(if let Some(&pos_id)=pos_id_from.get(&h){
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
}) as u32
}).collect();
let map_tex_id:Vec<u32>=model.unique_tex.iter().map(|tex|{
let h=tex.map(|v|bytemuck::cast::<f32,u32>(v));
(if let Some(&tex_id)=tex_id_from.get(&h){
};
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
}) as u32
}).collect();
let map_normal_id:Vec<u32>=model.unique_normal.iter().map(|untransformed_normal|{
};
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|bytemuck::cast::<f32,u32>(v));
(if let Some(&normal_id)=normal_id_from.get(&h){
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
}) as u32
}).collect();
let map_color_id:Vec<u32>=model.unique_color.iter().map(|color|{
let h=color.map(|v|bytemuck::cast::<f32,u32>(v));
(if let Some(&color_id)=color_id_from.get(&h){
};
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
}) as u32
}).collect();
};
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
let map_vertex_id:Vec<u32>=model.unique_vertices.iter().map(|unmapped_vertex|{
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,
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,
};
(if let Some(&vertex_id)=vertex_id_from.get(&vertex){
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
}) as u32
}).collect();
};
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 as usize]).collect()});
polys.push(model::IndexedPolygon{vertices:poly.vertices.iter().map(|vertex_id|map_vertex_id[vertex_id]).collect()});
}
}
}
@ -423,6 +427,7 @@ impl GlobalState{
}
}
}
//construct transposed models
//fill untouched models
for (model_id,model) in unique_texture_models.into_iter().enumerate(){
if !selected_model_instances.contains(&model_id){
@ -431,8 +436,8 @@ impl GlobalState{
}
//de-index models
let deduplicated_models_len=deduplicated_models.len();
let models:Vec<model_graphics::ModelGraphicsSingleTexture>=deduplicated_models.into_iter().map(|model|{
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();
@ -462,13 +467,13 @@ impl GlobalState{
}
}
entities.push(indices);
model_graphics::ModelGraphicsSingleTexture{
models.push(model_graphics::ModelGraphicsSingleTexture{
instances:model.instances,
vertices,
entities,
texture:model.texture,
}
}).collect();
});
}
//.into_iter() the modeldata vec so entities can be /moved/ to models.entities
let mut model_count=0;
let mut instance_count=0;
@ -540,7 +545,6 @@ impl GlobalState{
println!("Texture References={}",num_textures);
println!("Textures Loaded={}",texture_views.len());
println!("Indexed Models={}",indexed_models_len);
println!("Deduplicated Models={}",deduplicated_models_len);
println!("Graphics Objects: {}",self.graphics.models.len());
println!("Graphics Instances: {}",instance_count);
}
@ -931,7 +935,7 @@ impl framework::Example for GlobalState {
let screen_size=glam::uvec2(config.width,config.height);
let camera=GraphicsCamera::new(screen_size,user_settings.calculate_fov(1.0,&screen_size).as_vec2());
let camera_uniforms = camera.to_uniform_data(physics.output().adjust_mouse(&physics::MouseState::default()));
let camera_uniforms = camera.to_uniform_data(physics.output().adjust_mouse(&physics.next_mouse));
let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera"),
contents: bytemuck::cast_slice(&camera_uniforms),
@ -1063,7 +1067,6 @@ impl framework::Example for GlobalState {
self.graphics.clear();
let mut physics=physics::PhysicsState::default();
//physics.spawn()
physics.game.stage_id=0;
physics.spawn_point=spawn_point;
physics.process_instruction(instruction::TimedInstruction{
@ -1093,20 +1096,20 @@ impl framework::Example for GlobalState {
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
match event {
winit::event::WindowEvent::DroppedFile(path) => self.load_file(path,device,queue),
winit::event::WindowEvent::Focused(_state)=>{
winit::event::WindowEvent::Focused(state)=>{
//pause unpause
//recalculate pressed keys on focus
},
winit::event::WindowEvent::KeyboardInput{
event:winit::event::KeyEvent{state,logical_key,repeat:false,..},
winit::event::WindowEvent::KeyboardInput {
input:winit::event::KeyboardInput{state, virtual_keycode,..},
..
}=>{
let s=match state {
winit::event::ElementState::Pressed => true,
winit::event::ElementState::Released => false,
};
match logical_key{
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab)=>{
match virtual_keycode{
Some(winit::event::VirtualKeyCode::Tab)=>{
if s{
self.manual_mouse_lock=false;
match window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.graphics.camera.screen_size.x as f32/2.0, self.graphics.camera.screen_size.y as f32/2.0)){
@ -1135,7 +1138,7 @@ impl framework::Example for GlobalState {
}
window.set_cursor_visible(s);
},
winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11)=>{
Some(winit::event::VirtualKeyCode::F11)=>{
if s{
if window.fullscreen().is_some(){
window.set_fullscreen(None);
@ -1144,7 +1147,7 @@ impl framework::Example for GlobalState {
}
}
},
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape)=>{
Some(winit::event::VirtualKeyCode::Escape)=>{
if s{
self.manual_mouse_lock=false;
match window.set_cursor_grab(winit::window::CursorGrabMode::None){
@ -1154,21 +1157,18 @@ impl framework::Example for GlobalState {
window.set_cursor_visible(true);
}
},
keycode=>{
Some(keycode)=>{
if let Some(input_instruction)=match keycode {
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(InputInstruction::Jump(s)),
winit::keyboard::Key::Character(c)=>match c.as_str(){
"w"=>Some(InputInstruction::MoveForward(s)),
"a"=>Some(InputInstruction::MoveLeft(s)),
"s"=>Some(InputInstruction::MoveBack(s)),
"d"=>Some(InputInstruction::MoveRight(s)),
"e"=>Some(InputInstruction::MoveUp(s)),
"q"=>Some(InputInstruction::MoveDown(s)),
"z"=>Some(InputInstruction::Zoom(s)),
"r"=>if s{Some(InputInstruction::Reset)}else{None},
_=>None,
}
_=>None,
winit::event::VirtualKeyCode::W => Some(InputInstruction::MoveForward(s)),
winit::event::VirtualKeyCode::A => Some(InputInstruction::MoveLeft(s)),
winit::event::VirtualKeyCode::S => Some(InputInstruction::MoveBack(s)),
winit::event::VirtualKeyCode::D => Some(InputInstruction::MoveRight(s)),
winit::event::VirtualKeyCode::E => Some(InputInstruction::MoveUp(s)),
winit::event::VirtualKeyCode::Q => Some(InputInstruction::MoveDown(s)),
winit::event::VirtualKeyCode::Space => Some(InputInstruction::Jump(s)),
winit::event::VirtualKeyCode::Z => Some(InputInstruction::Zoom(s)),
winit::event::VirtualKeyCode::R => if s{Some(InputInstruction::Reset)}else{None},
_ => None,
}{
self.physics_thread.send(TimedInstruction{
time,
@ -1176,6 +1176,7 @@ impl framework::Example for GlobalState {
}).unwrap();
}
},
_=>(),
}
},
_=>(),

@ -1,4 +1,4 @@
use crate::integer::{Time,Planar64,Planar64Vec3,Planar64Affine3};
use crate::integer::{Planar64,Planar64Vec3,Planar64Affine3};
pub type TextureCoordinate=glam::Vec2;
pub type Color4=glam::Vec4;
#[derive(Clone,Hash,PartialEq,Eq)]
@ -50,63 +50,53 @@ pub struct IndexedModelInstances{
}
//stage description referencing flattened ids is spooky, but the map loading is meant to be deterministic.
pub struct ModeDescription{
pub start:usize,//start=model_id
pub spawns:Vec<usize>,//spawns[spawn_id]=model_id
pub ordered_checkpoints:Vec<usize>,//ordered_checkpoints[checkpoint_id]=model_id
pub unordered_checkpoints:Vec<usize>,//unordered_checkpoints[checkpoint_id]=model_id
pub start:u32,//start=model_id
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<&usize>{
self.spawns.get(*self.spawn_from_stage_id.get(&stage_id)?)
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<&usize>{
self.ordered_checkpoints.get(*self.ordered_checkpoint_from_checkpoint_id.get(&checkpoint_id)?)
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
}
}
}
//I don't want this code to exist!
#[derive(Clone)]
pub struct TempAttrStart{
pub mode_id:u32,
}
#[derive(Clone)]
pub struct TempAttrSpawn{
pub mode_id:u32,
pub stage_id:u32,
}
#[derive(Clone)]
pub struct TempAttrOrderedCheckpoint{
pub mode_id:u32,
pub checkpoint_id:u32,
}
#[derive(Clone)]
pub struct TempAttrUnorderedCheckpoint{
pub mode_id:u32,
}
#[derive(Clone)]
pub struct TempAttrWormhole{
pub wormhole_id:u32,
}
pub enum TempIndexedAttributes{
Start(TempAttrStart),
Spawn(TempAttrSpawn),
OrderedCheckpoint(TempAttrOrderedCheckpoint),
UnorderedCheckpoint(TempAttrUnorderedCheckpoint),
Wormhole(TempAttrWormhole),
Start{
mode_id:u32,
},
Spawn{
mode_id:u32,
stage_id:u32,
},
OrderedCheckpoint{
mode_id:u32,
checkpoint_id:u32,
},
UnorderedCheckpoint{
mode_id:u32,
},
}
//you have this effect while in contact
#[derive(Clone)]
pub struct ContactingSurf{}
#[derive(Clone)]
pub struct ContactingLadder{
pub sticky:bool
}
#[derive(Clone)]
pub enum ContactingBehaviour{
Surf,
Ladder(ContactingLadder),
Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32
}
//you have this effect while intersecting
#[derive(Clone)]
pub struct IntersectingWater{
@ -114,37 +104,18 @@ pub struct IntersectingWater{
pub density:Planar64,
pub current:Planar64Vec3,
}
//All models can be given these attributes
#[derive(Clone)]
pub struct GameMechanicAccelerator{
pub struct IntersectingAccelerator{
pub acceleration:Planar64Vec3
}
//All models can be given these attributes
#[derive(Clone)]
pub enum GameMechanicBooster{
Affine(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
pub struct GameMechanicJumpLimit{
pub count:u32,
}
#[derive(Clone)]
pub enum TrajectoryChoice{
HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time
LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time
}
#[derive(Clone)]
pub enum GameMechanicSetTrajectory{
AirTime(Time),//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
TargetPointTime{//launch on a trajectory that will land at a target point in a set amount of time
target_point:Planar64Vec3,
time:Time,//short time = fast and direct, long time = launch high in the air, negative time = wrong way
},
TrajectoryTargetPoint{//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,
},
Velocity(Planar64Vec3),//SetVelocity
DotVelocity{direction:Planar64Vec3,dot:Planar64},//set your velocity in a specific direction without touching other directions
pub struct GameMechanicBooster{
pub velocity:Planar64Vec3,
}
#[derive(Clone)]
pub enum ZoneBehaviour{
@ -159,10 +130,10 @@ pub struct GameMechanicZone{
pub behaviour:ZoneBehaviour,
}
// enum TrapCondition{
// FasterThan(Planar64),
// SlowerThan(Planar64),
// InRange(Planar64,Planar64),
// OutsideRange(Planar64,Planar64),
// FasterThan(i64),
// SlowerThan(i64),
// InRange(i64,i64),
// OutsideRange(i64,i64),
// }
#[derive(Clone)]
pub enum StageElementBehaviour{
@ -171,7 +142,6 @@ pub enum StageElementBehaviour{
Trigger,
Teleport,
Platform,
JumpLimit(u32),
//Speedtrap(TrapCondition),//Acts as a trigger with a speed condition
}
#[derive(Clone)]
@ -194,42 +164,24 @@ pub enum TeleportBehaviour{
StageElement(GameMechanicStageElement),
Wormhole(GameMechanicWormhole),
}
//attributes listed in order of handling
#[derive(Default,Clone)]
pub struct GameMechanicAttributes{
pub zone:Option<GameMechanicZone>,
pub jump_limit:Option<GameMechanicJumpLimit>,
pub booster:Option<GameMechanicBooster>,
pub trajectory:Option<GameMechanicSetTrajectory>,
pub zone:Option<GameMechanicZone>,
pub teleport_behaviour:Option<TeleportBehaviour>,
pub accelerator:Option<GameMechanicAccelerator>,
}
impl GameMechanicAttributes{
pub fn any(&self)->bool{
self.booster.is_some()
||self.trajectory.is_some()
||self.zone.is_some()
||self.teleport_behaviour.is_some()
||self.accelerator.is_some()
}
}
#[derive(Default,Clone)]
pub struct ContactingAttributes{
pub elasticity:Option<u32>,//[1/2^32,1] 0=None (elasticity+1)/2^32
//friction?
pub contact_behaviour:Option<ContactingBehaviour>,
}
impl ContactingAttributes{
pub fn any(&self)->bool{
self.contact_behaviour.is_some()
}
pub surf:Option<ContactingSurf>,
pub ladder:Option<ContactingLadder>,
}
#[derive(Default,Clone)]
pub struct IntersectingAttributes{
pub water:Option<IntersectingWater>,
}
impl IntersectingAttributes{
pub fn any(&self)->bool{
self.water.is_some()
}
pub accelerator:Option<IntersectingAccelerator>,
}
//Spawn(u32) NO! spawns are indexed in the map header instead of marked with attibutes
pub enum CollisionAttributes{

@ -42,7 +42,7 @@ impl From<glam::Vec4> for ModelGraphicsColor4{
impl std::hash::Hash for ModelGraphicsColor4{
fn hash<H: std::hash::Hasher>(&self,state:&mut H) {
for &f in self.0.as_ref(){
bytemuck::cast::<f32,u32>(f).hash(state);
u32::from_ne_bytes(f.to_ne_bytes()).hash(state);
}
}
}

File diff suppressed because it is too large Load Diff

@ -174,7 +174,7 @@ pub fn unit_cornerwedge()->crate::model::IndexedModel{
generate_partial_unit_cornerwedge(t)
}
#[derive(Clone)]
#[derive(Copy,Clone)]
pub struct FaceDescription{
pub texture:Option<u32>,
pub transform:glam::Affine2,

@ -6,7 +6,7 @@ pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
if a2==Planar64::ZERO{
return zeroes1(a0, a1);
}
let radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
let mut radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
if 0<radicand {
//start with f64 sqrt
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});