forked from StrafesNET/strafe-project
349 lines
12 KiB
Rust
349 lines
12 KiB
Rust
use std::time::Instant;
|
|
use physics::{InputInstruction, PhysicsInstruction};
|
|
use instruction::{TimedInstruction, InstructionConsumer};
|
|
|
|
mod bvh;
|
|
mod aabb;
|
|
mod model;
|
|
mod model_graphics;
|
|
mod zeroes;
|
|
mod worker;
|
|
mod integer;
|
|
mod physics;
|
|
mod graphics;
|
|
mod settings;
|
|
mod framework;
|
|
mod primitives;
|
|
mod instruction;
|
|
mod load_roblox;
|
|
|
|
|
|
pub struct GlobalState{
|
|
start_time: std::time::Instant,
|
|
manual_mouse_lock:bool,
|
|
mouse:std::sync::Arc<std::sync::Mutex<physics::MouseState>>,
|
|
user_settings:settings::UserSettings,
|
|
//Ideally the graphics thread worker description is:
|
|
/*
|
|
WorkerDescription{
|
|
input:Immediate,
|
|
output:Realtime(PoolOrdering::Ordered(3)),
|
|
}
|
|
*/
|
|
//up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order
|
|
graphics_thread:worker::INWorker<graphics::GraphicsInstruction>,
|
|
physics_thread:worker::QNWorker<TimedInstruction<InputInstruction>>,
|
|
}
|
|
|
|
impl framework::Example for GlobalState {
|
|
fn optional_features() -> wgpu::Features {
|
|
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|
|
| wgpu::Features::TEXTURE_COMPRESSION_ETC2
|
|
}
|
|
fn required_features() -> wgpu::Features {
|
|
wgpu::Features::TEXTURE_COMPRESSION_BC
|
|
}
|
|
fn required_limits() -> wgpu::Limits {
|
|
wgpu::Limits::default() //framework.rs was using goofy limits that caused me a multi-day headache
|
|
}
|
|
fn init(
|
|
config: &wgpu::SurfaceConfiguration,
|
|
_adapter: &wgpu::Adapter,
|
|
device: &wgpu::Device,
|
|
queue: &wgpu::Queue,
|
|
) -> Self {
|
|
//wee
|
|
let user_settings=settings::read_user_settings();
|
|
let mut indexed_models = Vec::new();
|
|
indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teslacyberv3.0.obj")[..]).unwrap(),glam::Vec4::ONE));
|
|
indexed_models.push(primitives::unit_sphere());
|
|
indexed_models.push(primitives::unit_cylinder());
|
|
indexed_models.push(primitives::unit_cube());
|
|
println!("models.len = {:?}", indexed_models.len());
|
|
indexed_models[0].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.))).unwrap(),
|
|
..Default::default()
|
|
});
|
|
//quad monkeys
|
|
indexed_models[1].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,10.))).unwrap(),
|
|
..Default::default()
|
|
});
|
|
indexed_models[1].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,10.))).unwrap(),
|
|
color:glam::vec4(1.0,0.0,0.0,1.0),
|
|
..Default::default()
|
|
});
|
|
indexed_models[1].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,20.))).unwrap(),
|
|
color:glam::vec4(0.0,1.0,0.0,1.0),
|
|
..Default::default()
|
|
});
|
|
indexed_models[1].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,20.))).unwrap(),
|
|
color:glam::vec4(0.0,0.0,1.0,1.0),
|
|
..Default::default()
|
|
});
|
|
//decorative monkey
|
|
indexed_models[1].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(15.,10.,15.))).unwrap(),
|
|
color:glam::vec4(0.5,0.5,0.5,0.5),
|
|
attributes:model::CollisionAttributes::Decoration,
|
|
..Default::default()
|
|
});
|
|
//teapot
|
|
indexed_models[2].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_scale_rotation_translation(glam::vec3(0.5, 1.0, 0.2),glam::quat(-0.22248298016985793,-0.839457167990537,-0.05603504040830783,-0.49261857546227916),glam::vec3(-10.,7.,10.))).unwrap(),
|
|
..Default::default()
|
|
});
|
|
//ground
|
|
indexed_models[3].instances.push(model::ModelInstance{
|
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(0.,0.,0.))*glam::Affine3A::from_scale(glam::vec3(160.0, 1.0, 160.0))).unwrap(),
|
|
..Default::default()
|
|
});
|
|
|
|
let mut graphics=GraphicsState::new();
|
|
|
|
graphics.load_user_settings(&user_settings);
|
|
|
|
let indexed_model_instances=model::IndexedModelInstances{
|
|
textures:Vec::new(),
|
|
models:indexed_models,
|
|
spawn_point:integer::Planar64Vec3::Y*50,
|
|
modes:Vec::new(),
|
|
};
|
|
|
|
//how to multithread
|
|
|
|
//1. build
|
|
physics.generate_models(&indexed_model_instances);
|
|
|
|
//2. move
|
|
let physics_thread=physics.into_worker();
|
|
|
|
//3. forget
|
|
|
|
let mut state=GlobalState{
|
|
start_time:Instant::now(),
|
|
manual_mouse_lock:false,
|
|
mouse:physics::MouseState::default(),
|
|
user_settings,
|
|
graphics,
|
|
physics_thread,
|
|
};
|
|
state.generate_model_graphics(&device,&queue,indexed_model_instances);
|
|
|
|
let args:Vec<String>=std::env::args().collect();
|
|
if args.len()==2{
|
|
state.load_file(std::path::PathBuf::from(&args[1]), device, queue);
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
fn load_file(&mut self,path: std::path::PathBuf, device: &wgpu::Device, queue: &wgpu::Queue){
|
|
println!("Loading file: {:?}", &path);
|
|
//oh boy! let's load the map!
|
|
if let Ok(file)=std::fs::File::open(path){
|
|
let mut input = std::io::BufReader::new(file);
|
|
let mut first_8=[0u8;8];
|
|
//.rbxm roblox binary = "<roblox!"
|
|
//.rbxmx roblox xml = "<roblox "
|
|
//.bsp = "VBSP"
|
|
//.vmf =
|
|
//.snf = "SNMF"
|
|
//.snf = "SNBF"
|
|
if let (Ok(()),Ok(()))=(std::io::Read::read_exact(&mut input, &mut first_8),std::io::Seek::rewind(&mut input)){
|
|
if let Some(indexed_model_instances)={
|
|
match &first_8[0..4]{
|
|
b"<rob"=>{
|
|
match match &first_8[4..8]{
|
|
b"lox!"=>rbx_binary::from_reader(input).map_err(|e|format!("{:?}",e)),
|
|
b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(|e|format!("{:?}",e)),
|
|
other=>Err(format!("Unknown Roblox file type {:?}",other)),
|
|
}{
|
|
Ok(dom)=>Some(load_roblox::generate_indexed_models(dom)),
|
|
Err(e)=>{
|
|
println!("Error loading roblox file:{:?}",e);
|
|
None
|
|
},
|
|
}
|
|
},
|
|
//b"VBSP"=>Some(load_bsp::generate_indexed_models(input)),
|
|
//b"SNFM"=>Some(sniffer::generate_indexed_models(input)),
|
|
//b"SNFB"=>Some(sniffer::load_bot(input)),
|
|
other=>{
|
|
println!("loser file {:?}",other);
|
|
None
|
|
},
|
|
}
|
|
}{
|
|
let spawn_point=indexed_model_instances.spawn_point;
|
|
//if generate_indexed_models succeeds, clear the previous ones
|
|
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{
|
|
time:physics.time,
|
|
instruction: PhysicsInstruction::Input(physics::PhysicsInputInstruction::Reset),
|
|
});
|
|
physics.load_user_settings(&self.user_settings);
|
|
physics.generate_models(&indexed_model_instances);
|
|
self.physics_thread=physics.into_worker();
|
|
|
|
//graphics.load_user_settings(&self.user_settings);
|
|
self.generate_model_graphics(device,queue,indexed_model_instances);
|
|
//manual reset
|
|
}else{
|
|
println!("No modeldatas were generated");
|
|
}
|
|
}else{
|
|
println!("Failed to read first 8 bytes and seek back to beginning of file.");
|
|
}
|
|
}else{
|
|
println!("Could not open file");
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::single_match)]
|
|
fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: winit::event::WindowEvent) {
|
|
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)=>{
|
|
//pause unpause
|
|
//recalculate pressed keys on focus
|
|
},
|
|
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 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)){
|
|
Ok(())=>(),
|
|
Err(e)=>println!("Could not set cursor position: {:?}",e),
|
|
}
|
|
match window.set_cursor_grab(winit::window::CursorGrabMode::None){
|
|
Ok(())=>(),
|
|
Err(e)=>println!("Could not release cursor: {:?}",e),
|
|
}
|
|
}else{
|
|
//if cursor is outside window don't lock but apparently there's no get pos function
|
|
//let pos=window.get_cursor_pos();
|
|
match window.set_cursor_grab(winit::window::CursorGrabMode::Locked){
|
|
Ok(())=>(),
|
|
Err(_)=>{
|
|
match window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
|
|
Ok(())=>(),
|
|
Err(e)=>{
|
|
self.manual_mouse_lock=true;
|
|
println!("Could not confine cursor: {:?}",e)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
window.set_cursor_visible(s);
|
|
},
|
|
Some(winit::event::VirtualKeyCode::F11)=>{
|
|
if s{
|
|
if window.fullscreen().is_some(){
|
|
window.set_fullscreen(None);
|
|
}else{
|
|
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
|
|
}
|
|
}
|
|
},
|
|
Some(winit::event::VirtualKeyCode::Escape)=>{
|
|
if s{
|
|
self.manual_mouse_lock=false;
|
|
match window.set_cursor_grab(winit::window::CursorGrabMode::None){
|
|
Ok(())=>(),
|
|
Err(e)=>println!("Could not release cursor: {:?}",e),
|
|
}
|
|
window.set_cursor_visible(true);
|
|
}
|
|
},
|
|
Some(keycode)=>{
|
|
if let Some(input_instruction)=match keycode {
|
|
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,
|
|
instruction:input_instruction,
|
|
}).unwrap();
|
|
}
|
|
},
|
|
_=>(),
|
|
}
|
|
},
|
|
_=>(),
|
|
}
|
|
}
|
|
|
|
fn device_event(&mut self, window: &winit::window::Window, event: winit::event::DeviceEvent) {
|
|
//there's no way this is the best way get a timestamp.
|
|
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
|
|
match event {
|
|
winit::event::DeviceEvent::MouseMotion {
|
|
delta,//these (f64,f64) are integers on my machine
|
|
} => {
|
|
if self.manual_mouse_lock{
|
|
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)){
|
|
Ok(())=>(),
|
|
Err(e)=>println!("Could not set cursor position: {:?}",e),
|
|
}
|
|
}
|
|
//do not step the physics because the mouse polling rate is higher than the physics can run.
|
|
//essentially the previous input will be overwritten until a true step runs
|
|
//which is fine because they run all the time.
|
|
let delta=glam::ivec2(delta.0 as i32,delta.1 as i32);
|
|
self.mouse.pos+=delta;
|
|
self.physics_thread.send(TimedInstruction{
|
|
time,
|
|
instruction:InputInstruction::MoveMouse(self.mouse.pos),
|
|
}).unwrap();
|
|
},
|
|
winit::event::DeviceEvent::MouseWheel {
|
|
delta,
|
|
} => {
|
|
println!("mousewheel {:?}",delta);
|
|
if false{//self.physics.style.use_scroll{
|
|
self.physics_thread.send(TimedInstruction{
|
|
time,
|
|
instruction:InputInstruction::Jump(true),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump
|
|
}).unwrap();
|
|
}
|
|
}
|
|
_=>(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
framework::run::<GlobalState>(
|
|
format!("Strafe Client v{}",
|
|
env!("CARGO_PKG_VERSION")
|
|
).as_str()
|
|
);
|
|
}
|