Compare commits
44 Commits
bsp-brush
...
sussy-tips
| Author | SHA1 | Date | |
|---|---|---|---|
| 96e311ef99 | |||
| 71d717a9d8 | |||
| 4d17e50883 | |||
| 1024d7564f | |||
| ddedb56a3b | |||
| 10ca30db2e | |||
| eec6a1fa72 | |||
| d384744d2d | |||
| 24c01962bf | |||
| dd4f81a73c | |||
| f46a7ba458 | |||
| 97ddf94df4 | |||
| d654f79748 | |||
| 091e8a4453 | |||
| b7a86467d8 | |||
| c9c6ddbf74 | |||
| 96af3fd1a1 | |||
| b6c40c3470 | |||
| 0418b83eaf | |||
| d138be2acd | |||
| 42caa785ce | |||
| 5b5c347c7a | |||
| a8853cb1a7 | |||
| 2c4ec76c96 | |||
| af3f9cf1fe | |||
| 067b5eee3c | |||
| 0b63e06ad2 | |||
| 6c40a51cd2 | |||
| 80f28e8bf7 | |||
| d6d1c5365b | |||
| 0542fad5dd | |||
| c7901d58d8 | |||
| 4506add978 | |||
| 5cf2272849 | |||
| 347b1176d2 | |||
| 7c0ad5b601 | |||
| 62851bbd60 | |||
| b3a6d08656 | |||
| 5409548348 | |||
| 57552c1a6a | |||
| aace3bb2a3 | |||
| 4899003766 | |||
| 0c991715ab | |||
| 72e0caa84a |
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -1867,7 +1867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4370,9 +4370,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vbsp"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f14a5685e0bb386aac9b9c6046a05152a46a0bc58d53afb3fbe577f1a1c2bb05"
|
||||
version = "0.7.0-codegen1"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "ff3887880f15dc530860d683aa6cf33f2e03e5a1c2783e3dc8252bffbf889c3c"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"arrayvec",
|
||||
|
||||
@@ -556,7 +556,7 @@ impl MoveState{
|
||||
=>None,
|
||||
}
|
||||
}
|
||||
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
|
||||
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<InternalInstruction,Time>>{
|
||||
//check if you have a valid walk state and create an instruction
|
||||
match self{
|
||||
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
|
||||
@@ -784,7 +784,7 @@ impl TouchingState{
|
||||
}).collect();
|
||||
crate::push_solve::push_solve(&contacts,acceleration)
|
||||
}
|
||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){
|
||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){
|
||||
// let relative_body=body.relative_to(&Body::ZERO);
|
||||
let relative_body=body;
|
||||
for contact in &self.contacts{
|
||||
@@ -878,7 +878,7 @@ impl PhysicsState{
|
||||
fn reset_to_default(&mut self){
|
||||
*self=Self::default();
|
||||
}
|
||||
fn next_move_instruction(&self)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
|
||||
fn next_move_instruction(&self)->Option<TimedInstruction<InternalInstruction,Time>>{
|
||||
self.move_state.next_move_instruction(&self.style.strafe,self.time)
|
||||
}
|
||||
fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){
|
||||
@@ -935,7 +935,7 @@ pub struct PhysicsData{
|
||||
impl Default for PhysicsData{
|
||||
fn default()->Self{
|
||||
Self{
|
||||
bvh:bvh::BvhNode::default(),
|
||||
bvh:bvh::BvhNode::empty(),
|
||||
models:Default::default(),
|
||||
modes:Default::default(),
|
||||
hitbox_mesh:StyleModifiers::default().calculate_mesh(),
|
||||
@@ -950,21 +950,21 @@ pub struct PhysicsContext<'a>{
|
||||
// the physics consumes both Instruction and PhysicsInternalInstruction,
|
||||
// but can only emit PhysicsInternalInstruction
|
||||
impl InstructionConsumer<InternalInstruction> for PhysicsContext<'_>{
|
||||
type TimeInner=TimeInner;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<InternalInstruction,TimeInner>){
|
||||
type Time=Time;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<InternalInstruction,Time>){
|
||||
atomic_internal_instruction(&mut self.state,&self.data,ins)
|
||||
}
|
||||
}
|
||||
impl InstructionConsumer<Instruction> for PhysicsContext<'_>{
|
||||
type TimeInner=TimeInner;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,TimeInner>){
|
||||
type Time=Time;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,Time>){
|
||||
atomic_input_instruction(&mut self.state,&self.data,ins)
|
||||
}
|
||||
}
|
||||
impl InstructionEmitter<InternalInstruction> for PhysicsContext<'_>{
|
||||
type TimeInner=TimeInner;
|
||||
type Time=Time;
|
||||
//this little next instruction function could cache its return value and invalidate the cached value by watching the State.
|
||||
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
|
||||
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<InternalInstruction,Time>>{
|
||||
next_instruction_internal(&self.state,&self.data,time_limit)
|
||||
}
|
||||
}
|
||||
@@ -972,7 +972,7 @@ impl PhysicsContext<'_>{
|
||||
pub fn run_input_instruction(
|
||||
state:&mut PhysicsState,
|
||||
data:&PhysicsData,
|
||||
instruction:TimedInstruction<Instruction,TimeInner>
|
||||
instruction:TimedInstruction<Instruction,Time>
|
||||
){
|
||||
let mut context=PhysicsContext{state,data};
|
||||
context.process_exhaustive(instruction.time);
|
||||
@@ -1121,7 +1121,7 @@ impl PhysicsData{
|
||||
}
|
||||
|
||||
//this is the one who asks
|
||||
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
|
||||
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<InternalInstruction,Time>>{
|
||||
//JUST POLLING!!! NO MUTATION
|
||||
let mut collector=instruction::InstructionCollector::new(time_limit);
|
||||
|
||||
@@ -1136,7 +1136,7 @@ impl PhysicsData{
|
||||
//relative to moving platforms
|
||||
//let relative_body=state.body.relative_to(&Body::ZERO);
|
||||
let relative_body=&state.body;
|
||||
data.bvh.the_tester(&aabb,&mut |&convex_mesh_id|{
|
||||
data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{
|
||||
//no checks are needed because of the time limits.
|
||||
let model_mesh=data.models.mesh(convex_mesh_id);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
||||
@@ -1198,7 +1198,7 @@ fn recalculate_touching(
|
||||
aabb.inflate(hitbox_mesh.halfsize);
|
||||
//relative to moving platforms
|
||||
//let relative_body=state.body.relative_to(&Body::ZERO);
|
||||
bvh.the_tester(&aabb,&mut |&convex_mesh_id|{
|
||||
bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{
|
||||
//no checks are needed because of the time limits.
|
||||
let model_mesh=models.mesh(convex_mesh_id);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
@@ -1651,7 +1651,7 @@ fn collision_end_intersect(
|
||||
}
|
||||
}
|
||||
}
|
||||
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,TimeInner>){
|
||||
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,Time>){
|
||||
state.time=ins.time;
|
||||
let (should_advance_body,goober_time)=match ins.instruction{
|
||||
InternalInstruction::CollisionStart(_,dt)
|
||||
@@ -1747,7 +1747,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
||||
}
|
||||
}
|
||||
|
||||
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<Instruction,TimeInner>){
|
||||
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<Instruction,Time>){
|
||||
state.time=ins.time;
|
||||
let should_advance_body=match ins.instruction{
|
||||
//the body may as well be a quantum wave function
|
||||
|
||||
@@ -5,13 +5,13 @@ use strafesnet_common::physics::{
|
||||
TimeInner as PhysicsTimeInner,
|
||||
Time as PhysicsTime,
|
||||
};
|
||||
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||
use strafesnet_common::session::Time as SessionTime;
|
||||
use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,TimedInstruction};
|
||||
|
||||
type TimedSelfInstruction=TimedInstruction<Instruction,PhysicsTimeInner>;
|
||||
type DoubleTimedSelfInstruction=TimedInstruction<TimedSelfInstruction,SessionTimeInner>;
|
||||
type TimedSelfInstruction=TimedInstruction<Instruction,PhysicsTime>;
|
||||
type DoubleTimedSelfInstruction=TimedInstruction<TimedSelfInstruction,SessionTime>;
|
||||
|
||||
type TimedPhysicsInstruction=TimedInstruction<PhysicsInstruction,PhysicsTimeInner>;
|
||||
type TimedPhysicsInstruction=TimedInstruction<PhysicsInstruction,PhysicsTime>;
|
||||
|
||||
const MOUSE_TIMEOUT:SessionTime=SessionTime::from_millis(10);
|
||||
|
||||
@@ -89,14 +89,14 @@ pub struct MouseInterpolator{
|
||||
// Maybe MouseInterpolator manipulation is better expressed using impls
|
||||
// and called from Instruction trait impls in session
|
||||
impl InstructionConsumer<TimedSelfInstruction> for MouseInterpolator{
|
||||
type TimeInner=SessionTimeInner;
|
||||
type Time=SessionTime;
|
||||
fn process_instruction(&mut self,ins:DoubleTimedSelfInstruction){
|
||||
self.push_unbuffered_input(ins.time,ins.instruction.time,ins.instruction.instruction.into())
|
||||
}
|
||||
}
|
||||
impl InstructionEmitter<StepInstruction> for MouseInterpolator{
|
||||
type TimeInner=SessionTimeInner;
|
||||
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::TimeInner>>{
|
||||
type Time=SessionTime;
|
||||
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::Time>>{
|
||||
self.buffered_instruction_with_timeout(time_limit)
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ impl MouseInterpolator{
|
||||
output:std::collections::VecDeque::new(),
|
||||
}
|
||||
}
|
||||
fn push_mouse_and_flush_buffer(&mut self,ins:TimedInstruction<MouseInstruction,PhysicsTimeInner>){
|
||||
fn push_mouse_and_flush_buffer(&mut self,ins:TimedInstruction<MouseInstruction,PhysicsTime>){
|
||||
self.buffer.push_front(TimedInstruction{
|
||||
time:ins.time,
|
||||
instruction:BufferedInstruction::Mouse(ins.instruction).into(),
|
||||
@@ -219,7 +219,7 @@ impl MouseInterpolator{
|
||||
}
|
||||
}
|
||||
}
|
||||
fn buffered_instruction_with_timeout(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,SessionTimeInner>>{
|
||||
fn buffered_instruction_with_timeout(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,SessionTime>>{
|
||||
match self.get_mouse_timedout_at(time_limit){
|
||||
Some(timeout)=>Some(TimedInstruction{
|
||||
time:timeout,
|
||||
@@ -232,7 +232,7 @@ impl MouseInterpolator{
|
||||
}),
|
||||
}
|
||||
}
|
||||
pub fn pop_buffered_instruction(&mut self,ins:TimedInstruction<StepInstruction,PhysicsTimeInner>)->Option<TimedPhysicsInstruction>{
|
||||
pub fn pop_buffered_instruction(&mut self,ins:TimedInstruction<StepInstruction,PhysicsTime>)->Option<TimedPhysicsInstruction>{
|
||||
match ins.instruction{
|
||||
StepInstruction::Pop=>(),
|
||||
StepInstruction::Timeout=>self.timeout_mouse(ins.time),
|
||||
@@ -244,6 +244,7 @@ impl MouseInterpolator{
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use super::*;
|
||||
use strafesnet_common::session::TimeInner as SessionTimeInner;
|
||||
#[test]
|
||||
fn test(){
|
||||
let mut interpolator=MouseInterpolator::new();
|
||||
|
||||
@@ -88,11 +88,11 @@ impl Simulation{
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Recording{
|
||||
instructions:Vec<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
|
||||
instructions:Vec<TimedInstruction<PhysicsInputInstruction,PhysicsTime>>,
|
||||
}
|
||||
impl Recording{
|
||||
pub fn new(
|
||||
instructions:Vec<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
|
||||
instructions:Vec<TimedInstruction<PhysicsInputInstruction,PhysicsTime>>,
|
||||
)->Self{
|
||||
Self{instructions}
|
||||
}
|
||||
@@ -207,8 +207,8 @@ impl Session{
|
||||
// Session emits DoStep
|
||||
|
||||
impl InstructionConsumer<Instruction<'_>> for Session{
|
||||
type TimeInner=SessionTimeInner;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,Self::TimeInner>){
|
||||
type Time=SessionTime;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,Self::Time>){
|
||||
// repetitive procedure macro
|
||||
macro_rules! run_mouse_interpolator_instruction{
|
||||
($instruction:expr)=>{
|
||||
@@ -425,8 +425,8 @@ impl InstructionConsumer<Instruction<'_>> for Session{
|
||||
}
|
||||
}
|
||||
impl InstructionConsumer<StepInstruction> for Session{
|
||||
type TimeInner=SessionTimeInner;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<StepInstruction,Self::TimeInner>){
|
||||
type Time=SessionTime;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<StepInstruction,Self::Time>){
|
||||
let time=self.simulation.timer.time(ins.time);
|
||||
if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins.set_time(time)){
|
||||
//record
|
||||
@@ -436,8 +436,8 @@ impl InstructionConsumer<StepInstruction> for Session{
|
||||
}
|
||||
}
|
||||
impl InstructionEmitter<StepInstruction> for Session{
|
||||
type TimeInner=SessionTimeInner;
|
||||
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::TimeInner>>{
|
||||
type Time=SessionTime;
|
||||
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::Time>>{
|
||||
self.mouse_interpolator.next_instruction(time_limit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||
glam = "0.29.0"
|
||||
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }
|
||||
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }
|
||||
vbsp = "0.6.0"
|
||||
vbsp = { version = "0.7.0-codegen1", registry = "strafesnet" }
|
||||
vmdl = "0.2.0"
|
||||
vpk = "0.2.0"
|
||||
|
||||
@@ -204,6 +204,7 @@ pub enum BrushToMeshError{
|
||||
count:usize,
|
||||
},
|
||||
InvalidPlanes(PlanesToFacesError),
|
||||
SkipBecauseTexture,
|
||||
}
|
||||
impl std::fmt::Display for BrushToMeshError{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
@@ -212,24 +213,7 @@ impl std::fmt::Display for BrushToMeshError{
|
||||
}
|
||||
impl core::error::Error for BrushToMeshError{}
|
||||
|
||||
pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,BrushToMeshError>{
|
||||
let brush_start_idx=brush.brush_side as usize;
|
||||
let sides_range=brush_start_idx..brush_start_idx+brush.num_brush_sides as usize;
|
||||
let sides=bsp.brush_sides.get(sides_range).ok_or(BrushToMeshError::SliceBrushSides)?;
|
||||
let face_list=sides.iter().map(|side|{
|
||||
let plane=bsp.plane(side.plane as usize)?;
|
||||
Some(Face{
|
||||
normal:valve_transform_normal(plane.normal.into()),
|
||||
dot:valve_transform_dist(plane.dist.into()),
|
||||
})
|
||||
}).collect::<Option<std::collections::HashSet<_>>>().ok_or(BrushToMeshError::MissingPlane)?;
|
||||
|
||||
if face_list.len()<4{
|
||||
return Err(BrushToMeshError::InvalidFaceCount{count:face_list.len()});
|
||||
}
|
||||
|
||||
let faces=planes_to_faces(face_list).map_err(BrushToMeshError::InvalidPlanes)?;
|
||||
|
||||
pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{
|
||||
// generate the mesh
|
||||
let mut mb=model::MeshBuilder::new();
|
||||
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
||||
@@ -237,7 +221,7 @@ pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,Brus
|
||||
// normals are ignored by physics
|
||||
let normal=mb.acquire_normal_id(integer::vec3::ZERO);
|
||||
|
||||
let polygon_list=faces.faces.into_iter().map(|face|{
|
||||
let polygon_list=faces.into_iter().map(|face|{
|
||||
face.into_iter().map(|pos|{
|
||||
let pos=mb.acquire_pos_id(pos);
|
||||
mb.acquire_vertex_id(model::IndexedVertex{
|
||||
@@ -253,12 +237,55 @@ pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,Brus
|
||||
let physics_groups=vec![model::IndexedPhysicsGroup{
|
||||
groups:vec![model::PolygonGroupId::new(0)],
|
||||
}];
|
||||
let graphics_groups=vec![model::IndexedGraphicsGroup{
|
||||
render:model::RenderConfigId::new(0),
|
||||
groups:vec![model::PolygonGroupId::new(0)],
|
||||
}];
|
||||
let graphics_groups=vec![];
|
||||
|
||||
Ok(mb.build(polygon_groups,graphics_groups,physics_groups))
|
||||
mb.build(polygon_groups,graphics_groups,physics_groups)
|
||||
}
|
||||
|
||||
pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,BrushToMeshError>{
|
||||
let brush_start_idx=brush.brush_side as usize;
|
||||
let sides_range=brush_start_idx..brush_start_idx+brush.num_brush_sides as usize;
|
||||
let sides=bsp.brush_sides.get(sides_range).ok_or(BrushToMeshError::SliceBrushSides)?;
|
||||
for side in sides{
|
||||
if let Some(texture_info)=bsp.textures_info.get(side.texture_info as usize){
|
||||
let texture_info=vbsp::Handle::new(bsp,texture_info);
|
||||
let s=texture_info.name();
|
||||
if s.starts_with("tools/")||s.starts_with("TOOLS/"){
|
||||
return Err(BrushToMeshError::SkipBecauseTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
let face_list=sides.iter().filter(|side|side.bevel==0).map(|side|{
|
||||
let plane=bsp.plane(side.plane as usize)?;
|
||||
Some(Face{
|
||||
normal:valve_transform_normal(plane.normal.into()),
|
||||
dot:valve_transform_dist(plane.dist.into()),
|
||||
})
|
||||
}).collect::<Option<std::collections::HashSet<_>>>().ok_or(BrushToMeshError::MissingPlane)?;
|
||||
|
||||
if face_list.len()<4{
|
||||
return Err(BrushToMeshError::InvalidFaceCount{count:face_list.len()});
|
||||
}
|
||||
|
||||
let faces=planes_to_faces(face_list).map_err(BrushToMeshError::InvalidPlanes)?;
|
||||
|
||||
let mesh=faces_to_mesh(faces.faces);
|
||||
|
||||
Ok(mesh)
|
||||
}
|
||||
|
||||
pub fn unit_cube()->model::Mesh{
|
||||
let face_list=[
|
||||
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||
].into_iter().collect();
|
||||
let faces=planes_to_faces(face_list).unwrap();
|
||||
let mesh=faces_to_mesh(faces.faces);
|
||||
mesh
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -42,29 +42,30 @@ pub fn convert<'a>(
|
||||
let mut unique_attributes=Vec::new();
|
||||
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
|
||||
unique_attributes.push(gameplay_attributes::CollisionAttributes::contact_default());
|
||||
unique_attributes.push(gameplay_attributes::CollisionAttributes::intersect_default());
|
||||
const ATTRIBUTE_DECORATION:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0);
|
||||
const ATTRIBUTE_CONTACT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(1);
|
||||
const ATTRIBUTE_INTERSECT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(2);
|
||||
|
||||
//declare all prop models to Loader
|
||||
let prop_models=Vec::new();
|
||||
// let prop_models=bsp.static_props().map(|prop|{
|
||||
// //get or create mesh_id
|
||||
// let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
||||
// let placement=prop.as_prop_placement();
|
||||
// model::Model{
|
||||
// mesh:mesh_id,
|
||||
// attributes:ATTRIBUTE_DECORATION,
|
||||
// transform:integer::Planar64Affine3::new(
|
||||
// integer::mat3::try_from_f32_array_2d((
|
||||
// glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
|
||||
// //TODO: figure this out
|
||||
// *glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
|
||||
// ).to_cols_array_2d()).unwrap(),
|
||||
// valve_transform(placement.origin.into()),
|
||||
// ),
|
||||
// color:glam::Vec4::ONE,
|
||||
// }
|
||||
// }).collect();
|
||||
let prop_models=bsp.static_props().map(|prop|{
|
||||
//get or create mesh_id
|
||||
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
||||
let placement=prop.as_prop_placement();
|
||||
model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:ATTRIBUTE_DECORATION,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::try_from_f32_array_2d((
|
||||
glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
|
||||
//TODO: figure this out
|
||||
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
|
||||
).to_cols_array_2d()).unwrap(),
|
||||
valve_transform(placement.origin.into()),
|
||||
),
|
||||
color:glam::Vec4::ONE,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
//TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups
|
||||
|
||||
@@ -75,7 +76,7 @@ pub fn convert<'a>(
|
||||
|
||||
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
||||
let mut graphics_groups=Vec::new();
|
||||
//let mut render_id_to_graphics_group_id=std::collections::HashMap::new();
|
||||
let mut render_id_to_graphics_group_id=std::collections::HashMap::new();
|
||||
let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{
|
||||
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
|
||||
let face_texture=face.texture();
|
||||
@@ -105,15 +106,15 @@ pub fn convert<'a>(
|
||||
//a render config for it, and then returns the id to that render config
|
||||
let render_id=render_config_deferred_loader.acquire_render_config_id(Some(Cow::Borrowed(face_texture_data.name())));
|
||||
//deduplicate graphics groups by render id
|
||||
// let graphics_group_id=*render_id_to_graphics_group_id.entry(render_id).or_insert_with(||{
|
||||
// let graphics_group_id=graphics_groups.len();
|
||||
// graphics_groups.push(model::IndexedGraphicsGroup{
|
||||
// render:render_id,
|
||||
// groups:vec![],
|
||||
// });
|
||||
// graphics_group_id
|
||||
// });
|
||||
// graphics_groups[graphics_group_id].groups.push(polygon_group_id);
|
||||
let graphics_group_id=*render_id_to_graphics_group_id.entry(render_id).or_insert_with(||{
|
||||
let graphics_group_id=graphics_groups.len();
|
||||
graphics_groups.push(model::IndexedGraphicsGroup{
|
||||
render:render_id,
|
||||
groups:vec![],
|
||||
});
|
||||
graphics_group_id
|
||||
});
|
||||
graphics_groups[graphics_group_id].groups.push(polygon_group_id);
|
||||
}
|
||||
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
|
||||
}).collect();
|
||||
@@ -121,69 +122,114 @@ pub fn convert<'a>(
|
||||
mb.build(polygon_groups,graphics_groups,vec![])
|
||||
}).collect();
|
||||
|
||||
let brush_mesh_start_idx=world_meshes.len();
|
||||
let mut found_spawn=None;
|
||||
|
||||
let mut world_models=Vec::new();
|
||||
|
||||
// the one and only world model 0
|
||||
world_models.push(model::Model{
|
||||
mesh:model::MeshId::new(0),
|
||||
attributes:ATTRIBUTE_DECORATION,
|
||||
transform:integer::Planar64Affine3::IDENTITY,
|
||||
color:glam::Vec4::W,
|
||||
});
|
||||
|
||||
for raw_ent in bsp.entities.iter(){
|
||||
match raw_ent.parse(){
|
||||
Ok(vbsp::basic::Entity::Brush(brush))
|
||||
|Ok(vbsp::basic::Entity::BrushIllusionary(brush))
|
||||
|Ok(vbsp::basic::Entity::BrushWall(brush))
|
||||
|Ok(vbsp::basic::Entity::BrushWallToggle(brush))=>{
|
||||
//The first character of brush.model is '*'
|
||||
match brush.model[1..].parse(){
|
||||
Ok(mesh_id)=>{
|
||||
world_models.push(model::Model{
|
||||
mesh:model::MeshId::new(mesh_id),
|
||||
attributes:ATTRIBUTE_DECORATION,
|
||||
transform:integer::Planar64Affine3::from_translation(
|
||||
valve_transform(brush.origin.into())
|
||||
),
|
||||
color:(glam::Vec3::from_array([
|
||||
brush.color.r as f32,
|
||||
brush.color.g as f32,
|
||||
brush.color.b as f32
|
||||
])/255.0).extend(1.0),
|
||||
});
|
||||
},
|
||||
Err(e)=>{
|
||||
println!("Brush model int parse error: {e}");
|
||||
},
|
||||
}
|
||||
},
|
||||
_=>(),
|
||||
}
|
||||
|
||||
match raw_ent.parse(){
|
||||
Ok(vbsp::css::Entity::InfoPlayerCounterterrorist(spawn))=>{
|
||||
found_spawn=Some(valve_transform(spawn.origin.into()));
|
||||
},
|
||||
Err(e)=>{
|
||||
println!("Bsp Entity parse error: {e}");
|
||||
},
|
||||
_=>(),
|
||||
}
|
||||
}
|
||||
|
||||
// physics models
|
||||
for brush in &bsp.brushes{
|
||||
if !brush.flags.contains(vbsp::BrushFlags::SOLID){
|
||||
continue;
|
||||
}
|
||||
let mesh_result=crate::brush::brush_to_mesh(bsp,brush);
|
||||
match mesh_result{
|
||||
Ok(mesh)=>world_meshes.push(mesh),
|
||||
Ok(mesh)=>{
|
||||
let mesh_id=model::MeshId::new(world_meshes.len() as u32);
|
||||
world_meshes.push(mesh);
|
||||
world_models.push(model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:ATTRIBUTE_CONTACT_DEFAULT,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::identity(),
|
||||
integer::vec3::ZERO,
|
||||
),
|
||||
color:glam::Vec4::ONE,
|
||||
});
|
||||
},
|
||||
Err(e)=>println!("Brush mesh error: {e}"),
|
||||
}
|
||||
}
|
||||
println!("How many brush: {}",bsp.brushes.len());
|
||||
println!("Generated brush models: {}",world_meshes.len()-brush_mesh_start_idx);
|
||||
|
||||
let world_models:Vec<model::Model>=
|
||||
//one instance of the main world mesh
|
||||
std::iter::once((
|
||||
//world_model
|
||||
model::MeshId::new(0),
|
||||
//model_origin
|
||||
vbsp::Vector::from([0.0,0.0,0.0]),
|
||||
//model_color
|
||||
vbsp::Color{r:255,g:255,b:255},
|
||||
)).chain(
|
||||
//entities sprinkle instances of the other meshes around
|
||||
bsp.entities.iter()
|
||||
.flat_map(|ent|ent.parse())//ignore entity parsing errors
|
||||
.filter_map(|ent|match ent{
|
||||
vbsp::Entity::Brush(brush)=>Some(brush),
|
||||
vbsp::Entity::BrushIllusionary(brush)=>Some(brush),
|
||||
vbsp::Entity::BrushWall(brush)=>Some(brush),
|
||||
vbsp::Entity::BrushWallToggle(brush)=>Some(brush),
|
||||
_=>None,
|
||||
}).flat_map(|brush|
|
||||
//The first character of brush.model is '*'
|
||||
brush.model[1..].parse().map(|mesh_id|//ignore parse int errors
|
||||
(model::MeshId::new(mesh_id),brush.origin,brush.color)
|
||||
)
|
||||
)
|
||||
).map(|(mesh_id,model_origin,vbsp::Color{r,g,b})|model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:ATTRIBUTE_DECORATION,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::identity(),
|
||||
valve_transform(model_origin.into())
|
||||
),
|
||||
color:(glam::Vec3::from_array([r as f32,g as f32,b as f32])/255.0).extend(1.0),
|
||||
}).chain(
|
||||
// physics models
|
||||
(brush_mesh_start_idx..world_meshes.len()).map(|mesh_id|model::Model{
|
||||
mesh:model::MeshId::new(mesh_id as u32),
|
||||
attributes:ATTRIBUTE_CONTACT_DEFAULT,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::identity(),
|
||||
integer::vec3::ZERO,
|
||||
),
|
||||
color:glam::Vec4::ONE,
|
||||
})
|
||||
).collect();
|
||||
let mut modes_list=Vec::new();
|
||||
if let Some(spawn_point)=found_spawn{
|
||||
// create a new mesh
|
||||
let mesh_id=model::MeshId::new(world_meshes.len() as u32);
|
||||
world_meshes.push(crate::brush::unit_cube());
|
||||
// create a new model
|
||||
let model_id=model::ModelId::new(world_models.len() as u32);
|
||||
world_models.push(model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:ATTRIBUTE_INTERSECT_DEFAULT,
|
||||
transform:integer::Planar64Affine3::from_translation(spawn_point),
|
||||
color:glam::Vec4::W,
|
||||
});
|
||||
|
||||
let first_stage=strafesnet_common::gameplay_modes::Stage::empty(model_id);
|
||||
let main_mode=strafesnet_common::gameplay_modes::Mode::new(
|
||||
strafesnet_common::gameplay_style::StyleModifiers::source_bhop(),
|
||||
model_id,
|
||||
std::collections::HashMap::new(),
|
||||
vec![first_stage],
|
||||
std::collections::HashMap::new(),
|
||||
);
|
||||
modes_list.push(main_mode);
|
||||
}
|
||||
|
||||
PartialMap1{
|
||||
attributes:unique_attributes,
|
||||
world_meshes,
|
||||
prop_models,
|
||||
world_models,
|
||||
modes:strafesnet_common::gameplay_modes::Modes::new(Vec::new()),
|
||||
modes:strafesnet_common::gameplay_modes::Modes::new(modes_list),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,35 +10,27 @@ use crate::aabb::Aabb;
|
||||
//sort the centerpoints on each axis (3 lists)
|
||||
//bv is put into octant based on whether it is upper or lower in each list
|
||||
|
||||
pub enum RecursiveContent<R,T>{
|
||||
Branch(Vec<R>),
|
||||
Leaf(T),
|
||||
pub enum RecursiveContent<N,L>{
|
||||
Branch(Vec<N>),
|
||||
Leaf(L),
|
||||
}
|
||||
impl<R,T> Default for RecursiveContent<R,T>{
|
||||
fn default()->Self{
|
||||
impl<N,L> RecursiveContent<N,L>{
|
||||
pub fn empty()->Self{
|
||||
Self::Branch(Vec::new())
|
||||
}
|
||||
}
|
||||
pub struct BvhNode<T>{
|
||||
content:RecursiveContent<BvhNode<T>,T>,
|
||||
pub struct BvhNode<L>{
|
||||
content:RecursiveContent<BvhNode<L>,L>,
|
||||
aabb:Aabb,
|
||||
}
|
||||
impl<T> Default for BvhNode<T>{
|
||||
fn default()->Self{
|
||||
impl<L> BvhNode<L>{
|
||||
pub fn empty()->Self{
|
||||
Self{
|
||||
content:Default::default(),
|
||||
content:RecursiveContent::empty(),
|
||||
aabb:Aabb::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct BvhWeightNode<W,T>{
|
||||
content:RecursiveContent<BvhWeightNode<W,T>,T>,
|
||||
weight:W,
|
||||
aabb:Aabb,
|
||||
}
|
||||
|
||||
impl<T> BvhNode<T>{
|
||||
pub fn the_tester<F:FnMut(&T)>(&self,aabb:&Aabb,f:&mut F){
|
||||
pub fn sample_aabb<F:FnMut(&L)>(&self,aabb:&Aabb,f:&mut F){
|
||||
match &self.content{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
@@ -47,51 +39,15 @@ impl<T> BvhNode<T>{
|
||||
//you're probably not going to spend a lot of time outside the map,
|
||||
//so the test is extra work for nothing
|
||||
if aabb.intersects(&child.aabb){
|
||||
child.the_tester(aabb,f);
|
||||
child.sample_aabb(aabb,f);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
child.into_visitor(f)
|
||||
},
|
||||
}
|
||||
pub fn into_inner(self)->(RecursiveContent<BvhNode<L>,L>,Aabb){
|
||||
(self.content,self.aabb)
|
||||
}
|
||||
pub fn weigh_contents<W:Copy+std::iter::Sum<W>,F:Fn(&T)->W>(self,f:&F)->BvhWeightNode<W,T>{
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>BvhWeightNode{
|
||||
weight:f(&model),
|
||||
content:RecursiveContent::Leaf(model),
|
||||
aabb:self.aabb,
|
||||
},
|
||||
RecursiveContent::Branch(children)=>{
|
||||
let branch:Vec<BvhWeightNode<W,T>>=children.into_iter().map(|child|
|
||||
child.weigh_contents(f)
|
||||
).collect();
|
||||
BvhWeightNode{
|
||||
weight:branch.iter().map(|node|node.weight).sum(),
|
||||
content:RecursiveContent::Branch(branch),
|
||||
aabb:self.aabb,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <W,T> BvhWeightNode<W,T>{
|
||||
pub const fn weight(&self)->&W{
|
||||
&self.weight
|
||||
}
|
||||
pub const fn aabb(&self)->&Aabb{
|
||||
&self.aabb
|
||||
}
|
||||
pub fn into_content(self)->RecursiveContent<BvhWeightNode<W,T>,T>{
|
||||
self.content
|
||||
}
|
||||
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
|
||||
pub fn into_visitor<F:FnMut(L)>(self,f:&mut F){
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
@@ -130,9 +86,9 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
|
||||
sort_y.push((i,center.y));
|
||||
sort_z.push((i,center.z));
|
||||
}
|
||||
sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
|
||||
sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
|
||||
sort_z.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
|
||||
sort_x.sort_by_key(|&(_,c)|c);
|
||||
sort_y.sort_by_key(|&(_,c)|c);
|
||||
sort_z.sort_by_key(|&(_,c)|c);
|
||||
let h=n/2;
|
||||
let median_x=sort_x[h].1;
|
||||
let median_y=sort_y[h].1;
|
||||
|
||||
@@ -171,4 +171,7 @@ impl CollisionAttributes{
|
||||
pub fn contact_default()->Self{
|
||||
Self::Contact(ContactAttributes::default())
|
||||
}
|
||||
pub fn intersect_default()->Self{
|
||||
Self::Intersect(IntersectAttributes::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::integer::Time;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct TimedInstruction<I,T>{
|
||||
pub time:Time<T>,
|
||||
pub time:T,
|
||||
pub instruction:I,
|
||||
}
|
||||
impl<I,T> TimedInstruction<I,T>{
|
||||
#[inline]
|
||||
pub fn set_time<TimeInner>(self,new_time:Time<TimeInner>)->TimedInstruction<I,TimeInner>{
|
||||
pub fn set_time<T2>(self,new_time:T2)->TimedInstruction<I,T2>{
|
||||
TimedInstruction{
|
||||
time:new_time,
|
||||
instruction:self.instruction,
|
||||
@@ -17,21 +15,21 @@ impl<I,T> TimedInstruction<I,T>{
|
||||
|
||||
/// Ensure all emitted instructions are processed before consuming external instructions
|
||||
pub trait InstructionEmitter<I>{
|
||||
type TimeInner;
|
||||
fn next_instruction(&self,time_limit:Time<Self::TimeInner>)->Option<TimedInstruction<I,Self::TimeInner>>;
|
||||
type Time;
|
||||
fn next_instruction(&self,time_limit:Self::Time)->Option<TimedInstruction<I,Self::Time>>;
|
||||
}
|
||||
/// Apply an atomic state update
|
||||
pub trait InstructionConsumer<I>{
|
||||
type TimeInner;
|
||||
fn process_instruction(&mut self,instruction:TimedInstruction<I,Self::TimeInner>);
|
||||
type Time;
|
||||
fn process_instruction(&mut self,instruction:TimedInstruction<I,Self::Time>);
|
||||
}
|
||||
/// If the object produces its own instructions, allow exhaustively feeding them back in
|
||||
pub trait InstructionFeedback<I,T>:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>
|
||||
pub trait InstructionFeedback<I,T>:InstructionEmitter<I,Time=T>+InstructionConsumer<I,Time=T>
|
||||
where
|
||||
Time<T>:Copy,
|
||||
T:Copy,
|
||||
{
|
||||
#[inline]
|
||||
fn process_exhaustive(&mut self,time_limit:Time<T>){
|
||||
fn process_exhaustive(&mut self,time_limit:T){
|
||||
while let Some(instruction)=self.next_instruction(time_limit){
|
||||
self.process_instruction(instruction);
|
||||
}
|
||||
@@ -39,39 +37,24 @@ pub trait InstructionFeedback<I,T>:InstructionEmitter<I,TimeInner=T>+Instruction
|
||||
}
|
||||
impl<I,T,X> InstructionFeedback<I,T> for X
|
||||
where
|
||||
Time<T>:Copy,
|
||||
X:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>,
|
||||
T:Copy,
|
||||
X:InstructionEmitter<I,Time=T>+InstructionConsumer<I,Time=T>,
|
||||
{}
|
||||
|
||||
//PROPER PRIVATE FIELDS!!!
|
||||
pub struct InstructionCollector<I,T>{
|
||||
time:Time<T>,
|
||||
time:T,
|
||||
instruction:Option<I>,
|
||||
}
|
||||
impl<I,T> InstructionCollector<I,T>
|
||||
where Time<T>:Copy+PartialOrd,
|
||||
{
|
||||
impl<I,T> InstructionCollector<I,T>{
|
||||
#[inline]
|
||||
pub const fn new(time:Time<T>)->Self{
|
||||
pub const fn new(time:T)->Self{
|
||||
Self{
|
||||
time,
|
||||
instruction:None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn time(&self)->Time<T>{
|
||||
self.time
|
||||
}
|
||||
#[inline]
|
||||
pub fn collect(&mut self,instruction:Option<TimedInstruction<I,T>>){
|
||||
if let Some(ins)=instruction{
|
||||
if ins.time<self.time{
|
||||
self.time=ins.time;
|
||||
self.instruction=Some(ins.instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn take(self)->Option<TimedInstruction<I,T>>{
|
||||
//STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR
|
||||
self.instruction.map(|instruction|TimedInstruction{
|
||||
@@ -80,3 +63,20 @@ impl<I,T> InstructionCollector<I,T>
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<I,T:Copy> InstructionCollector<I,T>{
|
||||
#[inline]
|
||||
pub const fn time(&self)->T{
|
||||
self.time
|
||||
}
|
||||
}
|
||||
impl<I,T:PartialOrd> InstructionCollector<I,T>{
|
||||
#[inline]
|
||||
pub fn collect(&mut self,instruction:Option<TimedInstruction<I,T>>){
|
||||
if let Some(ins)=instruction{
|
||||
if ins.time<self.time{
|
||||
self.time=ins.time;
|
||||
self.instruction=Some(ins.instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,12 @@ impl<T> From<Planar64> for Time<T>{
|
||||
Self::raw((value*Planar64::raw(1_000_000_000)).fix_1().to_raw())
|
||||
}
|
||||
}
|
||||
impl<T> From<Time<T>> for Ratio<Planar64,Planar64>{
|
||||
#[inline]
|
||||
fn from(value:Time<T>)->Self{
|
||||
value.to_ratio()
|
||||
}
|
||||
}
|
||||
impl<T,Num,Den,N1,T1> From<Ratio<Num,Den>> for Time<T>
|
||||
where
|
||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||
@@ -648,11 +654,19 @@ pub struct Planar64Affine3{
|
||||
pub translation:Planar64Vec3,
|
||||
}
|
||||
impl Planar64Affine3{
|
||||
pub const IDENTITY:Self=Self::new(mat3::identity(),vec3::ZERO);
|
||||
#[inline]
|
||||
pub const fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{
|
||||
Self{matrix3,translation}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn from_translation(translation:Planar64Vec3)->Self{
|
||||
Self{
|
||||
matrix3:mat3::identity(),
|
||||
translation,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn transform_point3(&self,point:Planar64Vec3)->vec3::Vector3<Fixed<2,64>>{
|
||||
self.translation.fix_2()+self.matrix3*point
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use strafesnet_common::physics::Time;
|
||||
|
||||
const VERSION:u32=0;
|
||||
|
||||
type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,strafesnet_common::physics::TimeInner>;
|
||||
type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,strafesnet_common::physics::Time>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error{
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::file::BlockId;
|
||||
use binrw::{binrw,BinReaderExt,BinWriterExt};
|
||||
use strafesnet_common::model;
|
||||
use strafesnet_common::aabb::Aabb;
|
||||
use strafesnet_common::bvh::BvhNode;
|
||||
use strafesnet_common::bvh::{BvhNode,RecursiveContent};
|
||||
use strafesnet_common::gameplay_modes;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -233,7 +233,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
}
|
||||
pub fn get_intersecting_region_block_ids(&self,aabb:&Aabb)->Vec<BlockId>{
|
||||
let mut block_ids=Vec::new();
|
||||
self.bvh.the_tester(aabb,&mut |&block_id|block_ids.push(block_id));
|
||||
self.bvh.sample_aabb(aabb,&mut |&block_id|block_ids.push(block_id));
|
||||
block_ids
|
||||
}
|
||||
pub fn load_region(&mut self,block_id:BlockId)->Result<Vec<(model::ModelId,model::Model)>,Error>{
|
||||
@@ -287,12 +287,60 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
}
|
||||
}
|
||||
|
||||
// silly redefinition of Bvh for determining the size of subnodes
|
||||
// without duplicating work by running weight calculation recursion top down on every node
|
||||
pub struct BvhWeightNode<W,T>{
|
||||
content:RecursiveContent<BvhWeightNode<W,T>,T>,
|
||||
weight:W,
|
||||
aabb:Aabb,
|
||||
}
|
||||
impl <W,T> BvhWeightNode<W,T>{
|
||||
pub const fn weight(&self)->&W{
|
||||
&self.weight
|
||||
}
|
||||
pub const fn aabb(&self)->&Aabb{
|
||||
&self.aabb
|
||||
}
|
||||
pub fn into_content(self)->RecursiveContent<BvhWeightNode<W,T>,T>{
|
||||
self.content
|
||||
}
|
||||
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
child.into_visitor(f)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn weigh_contents<T,W:Copy+std::iter::Sum<W>,F:Fn(&T)->W>(node:BvhNode<T>,f:&F)->BvhWeightNode<W,T>{
|
||||
let (content,aabb)=node.into_inner();
|
||||
match content{
|
||||
RecursiveContent::Leaf(model)=>BvhWeightNode{
|
||||
weight:f(&model),
|
||||
content:RecursiveContent::Leaf(model),
|
||||
aabb,
|
||||
},
|
||||
RecursiveContent::Branch(children)=>{
|
||||
let branch:Vec<BvhWeightNode<W,T>>=children.into_iter().map(|child|
|
||||
weigh_contents(child,f)
|
||||
).collect();
|
||||
BvhWeightNode{
|
||||
weight:branch.iter().map(|node|node.weight).sum(),
|
||||
content:RecursiveContent::Branch(branch),
|
||||
aabb,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const BVH_NODE_MAX_WEIGHT:usize=64*1024;//64 kB
|
||||
fn collect_spacial_blocks(
|
||||
block_location:&mut Vec<u64>,
|
||||
block_headers:&mut Vec<SpacialBlockHeader>,
|
||||
sequential_block_data:&mut std::io::Cursor<&mut Vec<u8>>,
|
||||
bvh_node:strafesnet_common::bvh::BvhWeightNode<usize,(model::ModelId,newtypes::model::Model)>
|
||||
bvh_node:BvhWeightNode<usize,(model::ModelId,newtypes::model::Model)>
|
||||
)->Result<(),Error>{
|
||||
//inspect the node weights top-down.
|
||||
//When a node weighs less than the limit,
|
||||
@@ -342,7 +390,7 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
|
||||
}
|
||||
Ok(((model::ModelId::new(model_id as u32),model.into()),aabb))
|
||||
}).collect::<Result<Vec<_>,_>>()?;
|
||||
let bvh=strafesnet_common::bvh::generate_bvh(boxen).weigh_contents(&|_|std::mem::size_of::<newtypes::model::Model>());
|
||||
let bvh=weigh_contents(strafesnet_common::bvh::generate_bvh(boxen),&|_|std::mem::size_of::<newtypes::model::Model>());
|
||||
//build blocks
|
||||
//block location is initialized with two values
|
||||
//the first value represents the location of the first byte after the file header
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::integer::Time;
|
||||
use super::common::{bool_from_u8,bool_into_u8};
|
||||
|
||||
type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,strafesnet_common::physics::TimeInner>;
|
||||
type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,strafesnet_common::physics::Time>;
|
||||
|
||||
#[binrw::binrw]
|
||||
#[brw(little)]
|
||||
|
||||
@@ -25,7 +25,7 @@ strafesnet_rbx_loader = { version = "0.6.0", path = "../lib/rbx_loader", registr
|
||||
strafesnet_snf = { version = "0.3.0", path = "../lib/snf", registry = "strafesnet" }
|
||||
thiserror = "2.0.11"
|
||||
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] }
|
||||
vbsp = "0.6.0"
|
||||
vbsp = { version = "0.7.0-codegen1", registry = "strafesnet" }
|
||||
vmdl = "0.2.0"
|
||||
vmt-parser = "0.2.0"
|
||||
vpk = "0.2.0"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use crate::window::Instruction;
|
||||
use strafesnet_common::integer;
|
||||
use strafesnet_common::instruction::TimedInstruction;
|
||||
use strafesnet_common::session::TimeInner as SessionTimeInner;
|
||||
use strafesnet_common::session::Time as SessionTime;
|
||||
|
||||
pub struct App<'a>{
|
||||
root_time:std::time::Instant,
|
||||
window_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>,
|
||||
window_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTime>>,
|
||||
}
|
||||
impl<'a> App<'a>{
|
||||
pub fn new(
|
||||
root_time:std::time::Instant,
|
||||
window_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>,
|
||||
window_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTime>>,
|
||||
)->App<'a>{
|
||||
Self{
|
||||
root_time,
|
||||
|
||||
@@ -2,7 +2,6 @@ mod app;
|
||||
mod file;
|
||||
mod setup;
|
||||
mod window;
|
||||
mod worker;
|
||||
mod compat_worker;
|
||||
mod physics_worker;
|
||||
mod graphics_worker;
|
||||
|
||||
@@ -6,7 +6,7 @@ use strafesnet_session::session::{
|
||||
};
|
||||
use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer};
|
||||
use strafesnet_common::physics::Time as PhysicsTime;
|
||||
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||
use strafesnet_common::session::Time as SessionTime;
|
||||
use strafesnet_common::timer::Timer;
|
||||
|
||||
pub enum Instruction{
|
||||
@@ -23,7 +23,7 @@ pub fn new<'a>(
|
||||
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
|
||||
directories:Directories,
|
||||
user_settings:settings::UserSettings,
|
||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
|
||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTime>>{
|
||||
let physics=strafesnet_physics::physics::PhysicsState::default();
|
||||
let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO);
|
||||
let simulation=Simulation::new(timer,physics);
|
||||
@@ -32,7 +32,7 @@ pub fn new<'a>(
|
||||
directories,
|
||||
simulation,
|
||||
);
|
||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{
|
||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTime>|{
|
||||
// excruciating pain
|
||||
macro_rules! run_session_instruction{
|
||||
($time:expr,$instruction:expr)=>{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use strafesnet_common::instruction::TimedInstruction;
|
||||
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||
use strafesnet_common::session::Time as SessionTime;
|
||||
use strafesnet_common::physics::{MiscInstruction,SetControlInstruction};
|
||||
use crate::file::LoadFormat;
|
||||
use crate::physics_worker::Instruction as PhysicsWorkerInstruction;
|
||||
@@ -17,7 +17,7 @@ struct WindowContext<'a>{
|
||||
mouse_pos:glam::DVec2,
|
||||
screen_size:glam::UVec2,
|
||||
window:&'a winit::window::Window,
|
||||
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<PhysicsWorkerInstruction,SessionTimeInner>>,
|
||||
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<PhysicsWorkerInstruction,SessionTime>>,
|
||||
}
|
||||
|
||||
impl WindowContext<'_>{
|
||||
@@ -223,7 +223,7 @@ impl WindowContext<'_>{
|
||||
pub fn worker<'a>(
|
||||
window:&'a winit::window::Window,
|
||||
setup_context:crate::setup::SetupContext<'a>,
|
||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
|
||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTime>>{
|
||||
// WindowContextSetup::new
|
||||
#[cfg(feature="user-install")]
|
||||
let directories=Directories::user().unwrap();
|
||||
@@ -252,7 +252,7 @@ pub fn worker<'a>(
|
||||
};
|
||||
|
||||
//WindowContextSetup::into_worker
|
||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{
|
||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTime>|{
|
||||
match ins.instruction{
|
||||
Instruction::WindowEvent(window_event)=>{
|
||||
window_context.window_event(ins.time,window_event);
|
||||
|
||||
@@ -1 +1 @@
|
||||
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/bhop_snfm
|
||||
/run/media/quat/Files/Documents/map-files/strafesnet/maps/bhop_snfm
|
||||
@@ -1 +1 @@
|
||||
/run/media/quat/Files/Documents/map-files/verify-scripts/meshes
|
||||
/run/media/quat/Files/Documents/map-files/strafesnet/meshes
|
||||
@@ -1 +1 @@
|
||||
/run/media/quat/Files/Documents/map-files/verify-scripts/replays
|
||||
/run/media/quat/Files/Documents/map-files/strafesnet/replays
|
||||
@@ -1 +1 @@
|
||||
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/surf_snfm
|
||||
/run/media/quat/Files/Documents/map-files/strafesnet/maps/surf_snfm
|
||||
@@ -1 +1 @@
|
||||
/run/media/quat/Files/Documents/map-files/verify-scripts/textures
|
||||
/run/media/quat/Files/Documents/map-files/strafesnet/textures
|
||||
@@ -1 +1 @@
|
||||
/run/media/quat/Files/Documents/map-files/verify-scripts/unions
|
||||
/run/media/quat/Files/Documents/map-files/strafesnet/unions
|
||||
Reference in New Issue
Block a user