Compare commits

..

46 Commits

Author SHA1 Message Date
087e95b1f7 delete TogglePaused 2025-12-22 13:54:35 -08:00
e46a51319f delete unused models 2025-12-20 16:31:05 -08:00
a3b0306430 rbx_loader: fix regex 2025-12-19 13:10:04 -08:00
e024f37843 update deps 2025-12-18 10:58:50 -08:00
4059cfa527 update wgpu 2025-12-18 10:57:19 -08:00
e4f3418bc6 document PhysicsMesh 2025-12-11 09:36:21 -08:00
6ca6d5e484 expect dead code 2025-12-10 18:05:16 -08:00
0668ac2def use allow instead of expect 2025-12-09 14:39:42 -08:00
73e3181d0c roblox_emulator: v0.5.2 2025-11-27 16:42:01 -08:00
19ba8f2445 update deps 2025-11-27 15:50:19 -08:00
0495d07e26 update rbx-dom 2025-11-27 15:48:17 -08:00
0ea353b27d common: fixed_wide: min max 2025-11-24 13:04:44 -08:00
99706079d9 common: fixed_wide: add mul_sign div_sign 2025-11-24 13:04:44 -08:00
730c5fb7dd common: integer: generic zero 2025-11-22 08:47:16 -08:00
d1b61bb997 push_solve: remove epsilon 2025-11-21 10:52:34 -08:00
0343ad19cf MeshQuery::hint_point returns any point inside the mesh 2025-11-20 10:59:08 -08:00
43210b1417 less access to TouchingState private fields 2025-11-19 13:39:07 -08:00
e9d28cf15f document jank 2025-11-19 13:15:31 -08:00
452bac4049 change collision_end_contact & collision_end_intersect fn signatures 2025-11-19 10:57:44 -08:00
48aad78f59 change contact_normal function signature to reduce copies 2025-11-19 10:20:33 -08:00
d45a42f5aa change ContactCollision struct layout
Match TouchingState contacts HashMap K,V layout to try to get lucky with compiler optimization.
2025-11-19 10:20:33 -08:00
c219fec3bc specialize touching member access 2025-11-19 10:08:40 -08:00
2a05d50abb check touching before testing collision 2025-11-19 10:08:40 -08:00
fbb047f8d4 combine call chain 2025-11-19 09:01:51 -08:00
c4d837a552 Fix infinite loop with intersects when allowing 0s collisions 2025-11-19 09:01:51 -08:00
a08bd44789 Generic ConvexMeshId 2025-11-19 09:01:51 -08:00
4ae5359046 rename not_spawn_at to is_not_spawn_at 2025-11-19 09:01:27 -08:00
15ecaf602a deep match 2025-11-18 12:29:46 -08:00
1e0511a7ba remove intermediate allocation 2025-11-18 12:23:05 -08:00
a9e4705d89 remove (some) fixed point implicit conversion
They may be convenient, but they cannot be done at compile-time.
TODO: remove more of them i.e. impl_multiplicative_operator
2025-11-18 11:53:52 -08:00
98069859b5 Gracefully handle 0 acceleration for walking targets 2025-11-18 19:47:04 +00:00
64d3996fa9 use From instead of Into 2025-11-18 11:46:32 -08:00
49c0c16e35 Use a From implementation instead of manual conversion
If the contacts and intersects map ever change in the future to not be 1:1 with gaps but instead something else, this guarantees that this implicit use of the relationship will flag at a compiler level
2025-11-18 19:25:44 +00:00
255bed4803 Ensure the PhysicsData's bvh respects the original model ordering
There's no importance in worrying about the core HashMap ordering since it's not used as an iterator except for outside of this very function for bvh purposes
2025-11-18 19:25:44 +00:00
128e137829 remove redundant code 2025-11-17 13:22:46 -08:00
1e19f804cc custom hex Debug print for Fixed 2025-11-17 12:45:55 -08:00
f6f35c5f54 fix lints 2025-11-17 12:41:34 -08:00
4e7d580918 add lints to workspace 2025-11-16 14:53:23 -08:00
8d5a100a2e update deps 2025-11-09 05:48:31 -08:00
91208db706 drop lazy_regex dep 2025-11-09 05:47:33 -08:00
5a320b514e fix style 2025-11-07 16:52:50 -08:00
661d706a22 common: aabb: area_weight fn 2025-10-17 15:48:59 +01:00
5550d5771e common: bvh: reduce variable scope 2025-10-17 15:03:54 +01:00
c834d1d1ca common: bvh: name constant 2025-10-17 14:57:53 +01:00
ca9d2238a7 common: bvh: tweak code style 2025-10-17 14:57:53 +01:00
f3bb8dd067 update deps 2025-10-01 23:37:07 -07:00
61 changed files with 1483 additions and 13350 deletions

1712
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,3 +24,14 @@ resolver = "2"
#lto = true #lto = true
strip = true strip = true
codegen-units = 1 codegen-units = 1
[workspace.lints.rust]
# unsafe_code = "forbid"
# missing_docs = "warn"
# missing_debug_implementations = "warn"
single_use_lifetimes = "warn"
trivial_casts = "warn"
unused_lifetimes = "warn"
unused_qualifications = "warn"
# variant_size_differences = "warn"
unexpected_cfgs = "warn"

View File

@@ -1,10 +0,0 @@
FROM docker.io/joseluisq/rust-linux-darwin-builder:2.0.0-beta.1-amd64 as build
COPY .cargo/config.toml .cargo/config.toml
COPY . .
RUN cargo build --bin strafe-client --release --target aarch64-apple-darwin
# RUN cargo build --release --target x86_64-apple-darwin
# isolate the binary
FROM scratch
COPY --from=build /root/src/target/aarch64-apple-darwin/release/strafe-client /
ENTRYPOINT ["/strafe-client"]

View File

@@ -1 +0,0 @@
docker build -f docker/aarch64-apple-darwin.Dockerfile . -o target/aarch64-apple-darwin/release/

View File

@@ -11,4 +11,7 @@ id = { version = "0.1.0", registry = "strafesnet" }
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" } strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
strafesnet_session = { path = "../session", registry = "strafesnet" } strafesnet_session = { path = "../session", registry = "strafesnet" }
strafesnet_settings = { path = "../settings", registry = "strafesnet" } strafesnet_settings = { path = "../settings", registry = "strafesnet" }
wgpu = "26.0.1" wgpu = "28.0.0"
[lints]
workspace = true

View File

@@ -94,7 +94,7 @@ impl GraphicsCamera{
raw raw
} }
} }
impl std::default::Default for GraphicsCamera{ impl Default for GraphicsCamera{
fn default()->Self{ fn default()->Self{
Self{ Self{
screen_size:glam::UVec2::ONE, screen_size:glam::UVec2::ONE,
@@ -167,7 +167,7 @@ impl GraphicsState{
} }
pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){ pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){
//generate texture view per texture //generate texture view per texture
let texture_views:HashMap<strafesnet_common::model::TextureId,wgpu::TextureView>=map.textures.iter().enumerate().filter_map(|(texture_id,texture_data)|{ let texture_views:HashMap<model::TextureId,wgpu::TextureView>=map.textures.iter().enumerate().filter_map(|(texture_id,texture_data)|{
let texture_id=model::TextureId::new(texture_id as u32); let texture_id=model::TextureId::new(texture_id as u32);
let image=match ddsfile::Dds::read(std::io::Cursor::new(texture_data)){ let image=match ddsfile::Dds::read(std::io::Cursor::new(texture_data)){
Ok(image)=>image, Ok(image)=>image,
@@ -616,7 +616,7 @@ impl GraphicsState{
address_mode_w:wgpu::AddressMode::ClampToEdge, address_mode_w:wgpu::AddressMode::ClampToEdge,
mag_filter:wgpu::FilterMode::Linear, mag_filter:wgpu::FilterMode::Linear,
min_filter:wgpu::FilterMode::Linear, min_filter:wgpu::FilterMode::Linear,
mipmap_filter:wgpu::FilterMode::Linear, mipmap_filter:wgpu::MipmapFilterMode::Linear,
..Default::default() ..Default::default()
}); });
let repeat_sampler=device.create_sampler(&wgpu::SamplerDescriptor{ let repeat_sampler=device.create_sampler(&wgpu::SamplerDescriptor{
@@ -626,7 +626,7 @@ impl GraphicsState{
address_mode_w:wgpu::AddressMode::Repeat, address_mode_w:wgpu::AddressMode::Repeat,
mag_filter:wgpu::FilterMode::Linear, mag_filter:wgpu::FilterMode::Linear,
min_filter:wgpu::FilterMode::Linear, min_filter:wgpu::FilterMode::Linear,
mipmap_filter:wgpu::FilterMode::Linear, mipmap_filter:wgpu::MipmapFilterMode::Linear,
anisotropy_clamp:16, anisotropy_clamp:16,
..Default::default() ..Default::default()
}); });
@@ -754,7 +754,7 @@ impl GraphicsState{
&skybox_texture_bind_group_layout, &skybox_texture_bind_group_layout,
&model_bind_group_layout, &model_bind_group_layout,
], ],
push_constant_ranges:&[], immediate_size:0,
}); });
let sky_pipeline_layout=device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor{ let sky_pipeline_layout=device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor{
label:None, label:None,
@@ -762,7 +762,7 @@ impl GraphicsState{
&camera_bind_group_layout, &camera_bind_group_layout,
&skybox_texture_bind_group_layout, &skybox_texture_bind_group_layout,
], ],
push_constant_ranges:&[], immediate_size:0,
}); });
// Create the render pipelines // Create the render pipelines
@@ -793,7 +793,7 @@ impl GraphicsState{
bias:wgpu::DepthBiasState::default(), bias:wgpu::DepthBiasState::default(),
}), }),
multisample:wgpu::MultisampleState::default(), multisample:wgpu::MultisampleState::default(),
multiview:None, multiview_mask:None,
cache:None, cache:None,
}); });
let model_pipeline=device.create_render_pipeline(&wgpu::RenderPipelineDescriptor{ let model_pipeline=device.create_render_pipeline(&wgpu::RenderPipelineDescriptor{
@@ -803,7 +803,7 @@ impl GraphicsState{
module:&shader, module:&shader,
entry_point:Some("vs_entity_texture"), entry_point:Some("vs_entity_texture"),
buffers:&[wgpu::VertexBufferLayout{ buffers:&[wgpu::VertexBufferLayout{
array_stride:std::mem::size_of::<GraphicsVertex>() as wgpu::BufferAddress, array_stride:size_of::<GraphicsVertex>() as wgpu::BufferAddress,
step_mode:wgpu::VertexStepMode::Vertex, step_mode:wgpu::VertexStepMode::Vertex,
attributes:&wgpu::vertex_attr_array![0=>Float32x3,1=>Float32x2,2=>Float32x3,3=>Float32x4], attributes:&wgpu::vertex_attr_array![0=>Float32x3,1=>Float32x2,2=>Float32x3,3=>Float32x4],
}], }],
@@ -828,7 +828,7 @@ impl GraphicsState{
bias:wgpu::DepthBiasState::default(), bias:wgpu::DepthBiasState::default(),
}), }),
multisample:wgpu::MultisampleState::default(), multisample:wgpu::MultisampleState::default(),
multiview:None, multiview_mask:None,
cache:None, cache:None,
}); });
@@ -880,7 +880,7 @@ impl GraphicsState{
camera_buf, camera_buf,
models:Vec::new(), models:Vec::new(),
depth_view, depth_view,
staging_belt:wgpu::util::StagingBelt::new(0x100), staging_belt:wgpu::util::StagingBelt::new(device.clone(),0x100),
bind_group_layouts:GraphicsBindGroupLayouts{model:model_bind_group_layout}, bind_group_layouts:GraphicsBindGroupLayouts{model:model_bind_group_layout},
samplers:GraphicsSamplers{repeat:repeat_sampler}, samplers:GraphicsSamplers{repeat:repeat_sampler},
temp_squid_texture_view:squid_texture_view, temp_squid_texture_view:squid_texture_view,
@@ -918,7 +918,6 @@ impl GraphicsState{
&self.camera_buf, &self.camera_buf,
0, 0,
wgpu::BufferSize::new((camera_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(), wgpu::BufferSize::new((camera_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(),
device,
) )
.copy_from_slice(bytemuck::cast_slice(&camera_uniforms)); .copy_from_slice(bytemuck::cast_slice(&camera_uniforms));
//This code only needs to run when the uniforms change //This code only needs to run when the uniforms change
@@ -965,6 +964,7 @@ impl GraphicsState{
}), }),
timestamp_writes:Default::default(), timestamp_writes:Default::default(),
occlusion_query_set:Default::default(), occlusion_query_set:Default::default(),
multiview_mask:None,
}); });
rpass.set_bind_group(0,&self.bind_groups.camera,&[]); rpass.set_bind_group(0,&self.bind_groups.camera,&[]);

View File

@@ -8,3 +8,6 @@ arrayvec = "0.7.6"
glam = "0.30.0" glam = "0.30.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" } strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -33,7 +33,7 @@ impl<T:Copy> std::ops::Neg for &Body<T>{
impl<T> Body<T> impl<T> Body<T>
where Time<T>:Copy, where Time<T>:Copy,
{ {
pub const ZERO:Self=Self::new(vec3::ZERO,vec3::ZERO,vec3::ZERO,Time::ZERO); pub const ZERO:Self=Self::new(vec3::zero(),vec3::zero(),vec3::zero(),Time::ZERO);
pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3,time:Time<T>)->Self{ pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3,time:Time<T>)->Self{
Self{ Self{
position, position,
@@ -107,8 +107,8 @@ impl<T> Body<T>
self.time+=dt.into(); self.time+=dt.into();
} }
pub fn infinity_dir(&self)->Option<Planar64Vec3>{ pub fn infinity_dir(&self)->Option<Planar64Vec3>{
if self.velocity==vec3::ZERO{ if self.velocity==vec3::zero(){
if self.acceleration==vec3::ZERO{ if self.acceleration==vec3::zero(){
None None
}else{ }else{
Some(self.acceleration) Some(self.acceleration)

View File

@@ -90,6 +90,9 @@ pub trait MeshQuery{
let &[v0,v1]=self.edge_verts(directed_edge_id.as_undirected()).as_ref(); let &[v0,v1]=self.edge_verts(directed_edge_id.as_undirected()).as_ref();
(self.vert(v1)-self.vert(v0))*((directed_edge_id.parity() as i64)*2-1) (self.vert(v1)-self.vert(v0))*((directed_edge_id.parity() as i64)*2-1)
} }
/// This must return a point inside the mesh.
#[expect(dead_code)]
fn hint_point(&self)->Planar64Vec3;
fn vert(&self,vert_id:Self::Vert)->Planar64Vec3; fn vert(&self,vert_id:Self::Vert)->Planar64Vec3;
fn face_nd(&self,face_id:Self::Face)->(Self::Normal,Self::Offset); fn face_nd(&self,face_id:Self::Face)->(Self::Normal,Self::Offset);
fn face_edges(&self,face_id:Self::Face)->impl AsRef<[Self::Edge]>; fn face_edges(&self,face_id:Self::Face)->impl AsRef<[Self::Edge]>;
@@ -100,17 +103,21 @@ pub trait MeshQuery{
} }
#[derive(Debug)] #[derive(Debug)]
struct FaceRefs{ struct FaceRefs{
// I didn't write it down, but I assume the edges are directed
// clockwise when looking towards the face normal, i.e. right hand rule.
edges:Vec<SubmeshDirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
//verts are redundant, use edge[i].verts[0]
//verts:Vec<VertId>, //verts:Vec<VertId>,
} }
#[derive(Debug)] #[derive(Debug)]
struct EdgeRefs{ struct EdgeRefs{
faces:[SubmeshFaceId;2],//left, right faces:[SubmeshFaceId;2],//left, right
verts:[SubmeshVertId;2],//bottom, top verts:[SubmeshVertId;2],//start, end
} }
#[derive(Debug)] #[derive(Debug)]
struct VertRefs{ struct VertRefs{
faces:Vec<SubmeshFaceId>, faces:Vec<SubmeshFaceId>,
// edges are always directed away from the vert
edges:Vec<SubmeshDirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -447,6 +454,10 @@ impl MeshQuery for PhysicsMeshView<'_>{
let face_idx=self.topology.faces[face_id.get() as usize].get() as usize; let face_idx=self.topology.faces[face_id.get() as usize].get() as usize;
(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot) (self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
} }
fn hint_point(&self)->Planar64Vec3{
// invariant: meshes always encompass the origin
vec3::zero()
}
//ideally I never calculate the vertex position, but I have to for the graphical meshes... //ideally I never calculate the vertex position, but I have to for the graphical meshes...
fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{ fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
let vert_idx=self.topology.verts[vert_id.get() as usize].get() as usize; let vert_idx=self.topology.verts[vert_id.get() as usize].get() as usize;
@@ -500,7 +511,7 @@ impl TransformedMesh<'_>{
transform, transform,
} }
} }
pub fn verts<'a>(&'a self)->impl Iterator<Item=vec3::Vector3<Fixed<2,64>>>+'a{ pub fn verts<'a>(&'a self)->impl Iterator<Item=Vector3<Fixed<2,64>>>+'a{
self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos)) self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos))
} }
fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{ fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{
@@ -532,6 +543,9 @@ impl MeshQuery for TransformedMesh<'_>{
// wrap for speed // wrap for speed
self.transform.vertex.transform_point3(self.view.vert(vert_id)).wrap_1() self.transform.vertex.transform_point3(self.view.vert(vert_id)).wrap_1()
} }
fn hint_point(&self)->Planar64Vec3{
self.transform.vertex.translation
}
#[inline] #[inline]
fn face_edges(&self,face_id:SubmeshFaceId)->impl AsRef<[SubmeshDirectedEdgeId]>{ fn face_edges(&self,face_id:SubmeshFaceId)->impl AsRef<[SubmeshDirectedEdgeId]>{
self.view.face_edges(face_id) self.view.face_edges(face_id)
@@ -750,7 +764,7 @@ impl MinkowskiMesh<'_>{
let infinity_fev=self.infinity_fev(-dir,infinity_body.position); let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
//a line is simpler to solve than a parabola //a line is simpler to solve than a parabola
infinity_body.velocity=dir; infinity_body.velocity=dir;
infinity_body.acceleration=vec3::ZERO; infinity_body.acceleration=vec3::zero();
//crawl in from negative infinity along a tangent line to get the closest fev //crawl in from negative infinity along a tangent line to get the closest fev
infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,start_time).miss() infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,start_time).miss()
}) })
@@ -778,10 +792,7 @@ impl MinkowskiMesh<'_>{
use crate::face_crawler::{low,upp}; use crate::face_crawler::{low,upp};
//no algorithm needed, there is only one state and two cases (Edge,None) //no algorithm needed, there is only one state and two cases (Edge,None)
//determine when it passes an edge ("sliding off" case) //determine when it passes an edge ("sliding off" case)
let start_time=range.start_bound().map(|&t|{ let start_time=range.start_bound().map(|&t|(t-relative_body.time).to_ratio());
let r=(t-relative_body.time).to_ratio();
Ratio::new(r.num,r.den)
});
let mut best_time=range.end_bound().map(|&t|into_giga_time(t,relative_body.time)); let mut best_time=range.end_bound().map(|&t|into_giga_time(t,relative_body.time));
let mut best_edge=None; let mut best_edge=None;
let face_n=self.face_nd(contact_face_id).0; let face_n=self.face_nd(contact_face_id).0;
@@ -810,7 +821,7 @@ impl MinkowskiMesh<'_>{
infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,Bound::Included(&infinity_body.time)).hit() infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,Bound::Included(&infinity_body.time)).hit()
} }
pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{ pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{
let infinity_body=Body::new(point,vec3::Y,vec3::ZERO,Time::ZERO); let infinity_body=Body::new(point,vec3::Y,vec3::zero(),Time::ZERO);
//movement must escape the mesh forwards and backwards in time, //movement must escape the mesh forwards and backwards in time,
//otherwise the point is not inside the mesh //otherwise the point is not inside the mesh
self.infinity_in(infinity_body) self.infinity_in(infinity_body)
@@ -856,6 +867,10 @@ impl MeshQuery for MinkowskiMesh<'_>{
}, },
} }
} }
fn hint_point(&self)->Planar64Vec3{
self.mesh1.transform.vertex.translation-
self.mesh0.transform.vertex.translation
}
fn face_edges(&self,face_id:MinkowskiFace)->impl AsRef<[MinkowskiDirectedEdge]>{ fn face_edges(&self,face_id:MinkowskiFace)->impl AsRef<[MinkowskiDirectedEdge]>{
match face_id{ match face_id{
MinkowskiFace::VertFace(v0,f1)=>{ MinkowskiFace::VertFace(v0,f1)=>{

View File

@@ -36,7 +36,7 @@ pub enum InternalInstruction{
pub struct InputState{ pub struct InputState{
mouse:MouseState, mouse:MouseState,
next_mouse:MouseState, next_mouse:MouseState,
controls:strafesnet_common::controls_bitflag::Controls, controls:Controls,
} }
impl InputState{ impl InputState{
fn set_next_mouse(&mut self,next_mouse:MouseState){ fn set_next_mouse(&mut self,next_mouse:MouseState){
@@ -87,7 +87,7 @@ enum JumpDirection{
impl JumpDirection{ impl JumpDirection{
fn direction(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{ fn direction(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{
match self{ match self{
JumpDirection::FromContactNormal=>contact_normal(models,hitbox_mesh,contact), JumpDirection::FromContactNormal=>contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id),
&JumpDirection::Exactly(dir)=>dir, &JumpDirection::Exactly(dir)=>dir,
} }
} }
@@ -100,7 +100,6 @@ enum TransientAcceleration{
time:Time, time:Time,
}, },
//walk target will never be reached //walk target will never be reached
#[expect(dead_code)]
Unreachable{ Unreachable{
acceleration:Planar64Vec3, acceleration:Planar64Vec3,
} }
@@ -114,8 +113,12 @@ struct ContactMoveState{
} }
impl TransientAcceleration{ impl TransientAcceleration{
fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{ fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{
if target_diff==vec3::ZERO{ if target_diff==vec3::zero(){
TransientAcceleration::Reached TransientAcceleration::Reached
}else if accel==Planar64::ZERO{
TransientAcceleration::Unreachable{
acceleration:vec3::zero()
}
}else{ }else{
//normal friction acceleration is clippedAcceleration.dot(normal)*friction //normal friction acceleration is clippedAcceleration.dot(normal)*friction
TransientAcceleration::Reachable{ TransientAcceleration::Reachable{
@@ -137,7 +140,7 @@ impl TransientAcceleration{
} }
fn acceleration(&self)->Planar64Vec3{ fn acceleration(&self)->Planar64Vec3{
match self{ match self{
TransientAcceleration::Reached=>vec3::ZERO, TransientAcceleration::Reached=>vec3::zero(),
&TransientAcceleration::Reachable{acceleration,time:_}=>acceleration, &TransientAcceleration::Reachable{acceleration,time:_}=>acceleration,
&TransientAcceleration::Unreachable{acceleration}=>acceleration, &TransientAcceleration::Unreachable{acceleration}=>acceleration,
} }
@@ -160,7 +163,7 @@ impl ContactMoveState{
} }
} }
fn ground_things(walk_settings:&gameplay_style::WalkSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){ fn ground_things(walk_settings:&gameplay_style::WalkSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){
let normal=contact_normal(models,hitbox_mesh,contact); let normal=contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id);
let gravity=touching.base_acceleration(models,style,camera,input_state); let gravity=touching.base_acceleration(models,style,camera,input_state);
let control_dir=style.get_y_control_dir(camera,input_state.controls); let control_dir=style.get_y_control_dir(camera,input_state.controls);
let target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal); let target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal);
@@ -168,7 +171,7 @@ fn ground_things(walk_settings:&gameplay_style::WalkSettings,contact:&ContactCol
(gravity,target_velocity_clipped) (gravity,target_velocity_clipped)
} }
fn ladder_things(ladder_settings:&gameplay_style::LadderSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){ fn ladder_things(ladder_settings:&gameplay_style::LadderSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){
let normal=contact_normal(models,hitbox_mesh,contact); let normal=contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id);
let gravity=touching.base_acceleration(models,style,camera,input_state); let gravity=touching.base_acceleration(models,style,camera,input_state);
let control_dir=style.get_y_control_dir(camera,input_state.controls); let control_dir=style.get_y_control_dir(camera,input_state.controls);
let target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal); let target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal);
@@ -193,7 +196,7 @@ impl PhysicsModels{
self.contact_attributes.clear(); self.contact_attributes.clear();
self.intersect_attributes.clear(); self.intersect_attributes.clear();
} }
fn mesh(&self,convex_mesh_id:ConvexMeshId)->TransformedMesh<'_>{ fn mesh(&self,convex_mesh_id:ConvexMeshId<PhysicsModelId>)->TransformedMesh<'_>{
let (mesh_id,transform)=match convex_mesh_id.model_id{ let (mesh_id,transform)=match convex_mesh_id.model_id{
PhysicsModelId::Contact(model_id)=>{ PhysicsModelId::Contact(model_id)=>{
let model=&self.contact_models[&model_id]; let model=&self.contact_models[&model_id];
@@ -210,25 +213,25 @@ impl PhysicsModels{
) )
} }
//it's a bit weird to have three functions, but it's always going to be one of these //it's a bit weird to have three functions, but it's always going to be one of these
fn contact_mesh(&self,contact:&ContactCollision)->TransformedMesh<'_>{ fn contact_mesh(&self,convex_mesh_id:&ConvexMeshId<ContactModelId>)->TransformedMesh<'_>{
let model=&self.contact_models[&contact.model_id]; let model=&self.contact_models[&convex_mesh_id.model_id];
TransformedMesh::new( TransformedMesh::new(
self.meshes[&model.mesh_id].submesh_view(contact.submesh_id), self.meshes[&model.mesh_id].submesh_view(convex_mesh_id.submesh_id),
&model.transform &model.transform
) )
} }
fn intersect_mesh(&self,intersect:&IntersectCollision)->TransformedMesh<'_>{ fn intersect_mesh(&self,convex_mesh_id:&ConvexMeshId<IntersectModelId>)->TransformedMesh<'_>{
let model=&self.intersect_models[&intersect.model_id]; let model=&self.intersect_models[&convex_mesh_id.model_id];
TransformedMesh::new( TransformedMesh::new(
self.meshes[&model.mesh_id].submesh_view(intersect.submesh_id), self.meshes[&model.mesh_id].submesh_view(convex_mesh_id.submesh_id),
&model.transform &model.transform
) )
} }
fn get_model_transform(&self,model_id:ModelId)->Option<&PhysicsMeshTransform>{ fn get_model_transform(&self,model_id:ModelId)->Option<&PhysicsMeshTransform>{
//ModelId can possibly be a decoration //ModelId can possibly be a decoration
match self.contact_models.get(&ContactModelId::new(model_id.get())){ match self.contact_models.get(&model_id.into()){
Some(model)=>Some(&model.transform), Some(model)=>Some(&model.transform),
None=>self.intersect_models.get(&IntersectModelId::new(model_id.get())) None=>self.intersect_models.get(&model_id.into())
.map(|model|&model.transform), .map(|model|&model.transform),
} }
} }
@@ -302,7 +305,7 @@ impl PhysicsCamera{
} }
} }
impl std::default::Default for PhysicsCamera{ impl Default for PhysicsCamera{
fn default()->Self{ fn default()->Self{
Self{ Self{
sensitivity:Ratio64Vec2::ONE*200_000, sensitivity:Ratio64Vec2::ONE*200_000,
@@ -385,7 +388,7 @@ mod gameplay{
self.unordered_checkpoints.clear(); self.unordered_checkpoints.clear();
} }
} }
impl std::default::Default for ModeState{ impl Default for ModeState{
fn default()->Self{ fn default()->Self{
Self{ Self{
mode_id:gameplay_modes::ModeId::MAIN, mode_id:gameplay_modes::ModeId::MAIN,
@@ -440,7 +443,7 @@ impl StyleHelper for StyleModifiers{
fn get_control_dir(&self,controls:Controls)->Planar64Vec3{ fn get_control_dir(&self,controls:Controls)->Planar64Vec3{
//don't get fancy just do it //don't get fancy just do it
let mut control_dir:Planar64Vec3=vec3::ZERO; let mut control_dir:Planar64Vec3=vec3::zero();
//Apply mask after held check so you can require non-allowed keys to be held for some reason //Apply mask after held check so you can require non-allowed keys to be held for some reason
let controls=controls.intersection(self.controls_mask); let controls=controls.intersection(self.controls_mask);
if controls.contains(Controls::MoveForward){ if controls.contains(Controls::MoveForward){
@@ -479,7 +482,7 @@ impl StyleHelper for StyleModifiers{
}; };
let transform=integer::Planar64Affine3::new( let transform=integer::Planar64Affine3::new(
mat3::from_diagonal(self.hitbox.halfsize), mat3::from_diagonal(self.hitbox.halfsize),
vec3::ZERO vec3::zero()
); );
HitboxMesh::new(mesh,transform) HitboxMesh::new(mesh,transform)
} }
@@ -497,7 +500,7 @@ impl MoveState{
//call this after state.move_state is changed //call this after state.move_state is changed
fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
match self{ match self{
MoveState::Fly=>body.acceleration=vec3::ZERO, MoveState::Fly=>body.acceleration=vec3::zero(),
MoveState::Air=>{ MoveState::Air=>{
//calculate base acceleration //calculate base acceleration
let a=touching.base_acceleration(models,style,camera,input_state); let a=touching.base_acceleration(models,style,camera,input_state);
@@ -611,7 +614,7 @@ impl MoveState{
// TODO: unduplicate this code // TODO: unduplicate this code
match self.get_walk_state(){ match self.get_walk_state(){
// did you stop touching the thing you were walking on? // did you stop touching the thing you were walking on?
Some(walk_state)=>if !touching.contacts.contains(&walk_state.contact){ Some(walk_state)=>if !touching.contains_contact(&walk_state.contact.convex_mesh_id){
self.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state); self.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state);
}else{ }else{
// stopped touching something else while walking // stopped touching something else while walking
@@ -642,9 +645,9 @@ impl TryFrom<&gameplay_attributes::CollisionAttributes> for PhysicsCollisionAttr
} }
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] #[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
struct ContactAttributesId(u32); struct ContactAttributesId(u32);
impl Into<CollisionAttributesId> for ContactAttributesId{ impl From<ContactAttributesId> for CollisionAttributesId{
fn into(self)->CollisionAttributesId{ fn from(value:ContactAttributesId)->CollisionAttributesId{
CollisionAttributesId::new(self.0) CollisionAttributesId::new(value.0)
} }
} }
impl From<CollisionAttributesId> for ContactAttributesId{ impl From<CollisionAttributesId> for ContactAttributesId{
@@ -654,9 +657,9 @@ impl From<CollisionAttributesId> for ContactAttributesId{
} }
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] #[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
struct IntersectAttributesId(u32); struct IntersectAttributesId(u32);
impl Into<CollisionAttributesId> for IntersectAttributesId{ impl From<IntersectAttributesId> for CollisionAttributesId{
fn into(self)->CollisionAttributesId{ fn from(value:IntersectAttributesId)->CollisionAttributesId{
CollisionAttributesId::new(self.0) CollisionAttributesId::new(value.0)
} }
} }
impl From<CollisionAttributesId> for IntersectAttributesId{ impl From<CollisionAttributesId> for IntersectAttributesId{
@@ -666,16 +669,26 @@ impl From<CollisionAttributesId> for IntersectAttributesId{
} }
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
struct ContactModelId(u32); struct ContactModelId(u32);
impl Into<ModelId> for ContactModelId{ impl From<ContactModelId> for ModelId{
fn into(self)->ModelId{ fn from(value:ContactModelId)->ModelId{
ModelId::new(self.get()) ModelId::new(value.get())
}
}
impl From<ModelId> for ContactModelId{
fn from(other: ModelId)->Self{
Self::new(other.get())
} }
} }
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
struct IntersectModelId(u32); struct IntersectModelId(u32);
impl Into<ModelId> for IntersectModelId{ impl From<IntersectModelId> for ModelId{
fn into(self)->ModelId{ fn from(value:IntersectModelId)->ModelId{
ModelId::new(self.get()) ModelId::new(value.get())
}
}
impl From<ModelId> for IntersectModelId{
fn from(other: ModelId)->Self{
Self::new(other.get())
} }
} }
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
@@ -683,9 +696,9 @@ enum PhysicsModelId{
Contact(ContactModelId), Contact(ContactModelId),
Intersect(IntersectModelId), Intersect(IntersectModelId),
} }
impl Into<ModelId> for PhysicsModelId{ impl From<PhysicsModelId> for ModelId{
fn into(self)->ModelId{ fn from(value:PhysicsModelId)->ModelId{
ModelId::new(match self{ ModelId::new(match value{
PhysicsModelId::Contact(model_id)=>model_id.get(), PhysicsModelId::Contact(model_id)=>model_id.get(),
PhysicsModelId::Intersect(model_id)=>model_id.get(), PhysicsModelId::Intersect(model_id)=>model_id.get(),
}) })
@@ -693,10 +706,18 @@ impl Into<ModelId> for PhysicsModelId{
} }
//unique physics meshes indexed by this //unique physics meshes indexed by this
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
struct ConvexMeshId{ struct ConvexMeshId<Id>{
model_id:PhysicsModelId, model_id:Id,
submesh_id:PhysicsSubmeshId, submesh_id:PhysicsSubmeshId,
} }
impl<Id> ConvexMeshId<Id>{
fn map<NewId>(self,model_id:NewId)->ConvexMeshId<NewId>{
ConvexMeshId{
model_id,
submesh_id:self.submesh_id,
}
}
}
struct ContactModel{ struct ContactModel{
mesh_id:PhysicsMeshId, mesh_id:PhysicsMeshId,
attr_id:ContactAttributesId, attr_id:ContactAttributesId,
@@ -710,14 +731,12 @@ struct IntersectModel{
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
pub struct ContactCollision{ pub struct ContactCollision{
convex_mesh_id:ConvexMeshId<ContactModelId>,
face_id:model_physics::MinkowskiFace, face_id:model_physics::MinkowskiFace,
model_id:ContactModelId,
submesh_id:PhysicsSubmeshId,
} }
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
pub struct IntersectCollision{ pub struct IntersectCollision{
model_id:IntersectModelId, convex_mesh_id:ConvexMeshId<IntersectModelId>,
submesh_id:PhysicsSubmeshId,
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
pub enum Collision{ pub enum Collision{
@@ -725,33 +744,47 @@ pub enum Collision{
Intersect(IntersectCollision), Intersect(IntersectCollision),
} }
impl Collision{ impl Collision{
const fn new(convex_mesh_id:ConvexMeshId,face_id:model_physics::MinkowskiFace)->Self{ fn new(convex_mesh_id:ConvexMeshId<PhysicsModelId>,face_id:model_physics::MinkowskiFace)->Self{
match convex_mesh_id.model_id{ match convex_mesh_id.model_id{
PhysicsModelId::Contact(model_id)=>Collision::Contact(ContactCollision{model_id,submesh_id:convex_mesh_id.submesh_id,face_id}), PhysicsModelId::Contact(model_id)=>Collision::Contact(ContactCollision{convex_mesh_id:convex_mesh_id.map(model_id),face_id}),
PhysicsModelId::Intersect(model_id)=>Collision::Intersect(IntersectCollision{model_id,submesh_id:convex_mesh_id.submesh_id}), PhysicsModelId::Intersect(model_id)=>Collision::Intersect(IntersectCollision{convex_mesh_id:convex_mesh_id.map(model_id)}),
} }
} }
} }
#[derive(Clone,Debug,Default)] #[derive(Clone,Debug,Default)]
struct TouchingState{ struct TouchingState{
contacts:HashSet<ContactCollision>, // This is kind of jank, it's a ContactCollision
intersects:HashSet<IntersectCollision>, // but split over the Key and Value of the HashMap.
contacts:HashMap<ConvexMeshId<ContactModelId>,model_physics::MinkowskiFace>,
intersects:HashSet<ConvexMeshId<IntersectModelId>>,
} }
impl TouchingState{ impl TouchingState{
fn clear(&mut self){ fn clear(&mut self){
self.contacts.clear(); self.contacts.clear();
self.intersects.clear(); self.intersects.clear();
} }
fn insert(&mut self,collision:Collision)->bool{ fn insert_contact(&mut self,contact:ContactCollision)->Option<model_physics::MinkowskiFace>{
match collision{ self.contacts.insert(contact.convex_mesh_id,contact.face_id)
Collision::Contact(collision)=>self.contacts.insert(collision),
Collision::Intersect(collision)=>self.intersects.insert(collision),
}
} }
fn remove(&mut self,collision:&Collision)->bool{ fn insert_intersect(&mut self,intersect:IntersectCollision)->bool{
match collision{ self.intersects.insert(intersect.convex_mesh_id)
Collision::Contact(collision)=>self.contacts.remove(collision), }
Collision::Intersect(collision)=>self.intersects.remove(collision), fn remove_contact(&mut self,convex_mesh_id:&ConvexMeshId<ContactModelId>)->Option<model_physics::MinkowskiFace>{
self.contacts.remove(convex_mesh_id)
}
fn remove_intersect(&mut self,convex_mesh_id:&ConvexMeshId<IntersectModelId>)->bool{
self.intersects.remove(convex_mesh_id)
}
fn contains_contact(&self,convex_mesh_id:&ConvexMeshId<ContactModelId>)->bool{
self.contacts.contains_key(convex_mesh_id)
}
fn contains_intersect(&self,convex_mesh_id:&ConvexMeshId<IntersectModelId>)->bool{
self.intersects.contains(convex_mesh_id)
}
fn contains(&self,convex_mesh_id:&ConvexMeshId<PhysicsModelId>)->bool{
match convex_mesh_id.model_id{
PhysicsModelId::Contact(contact_model_id)=>self.contains_contact(&convex_mesh_id.map(contact_model_id)),
PhysicsModelId::Intersect(intersect_model_id)=>self.contains_intersect(&convex_mesh_id.map(intersect_model_id)),
} }
} }
fn base_acceleration(&self,models:&PhysicsModels,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->Planar64Vec3{ fn base_acceleration(&self,models:&PhysicsModels,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->Planar64Vec3{
@@ -760,7 +793,7 @@ impl TouchingState{
a+=rocket_settings.acceleration(style.get_propulsion_control_dir(camera,input_state.controls)); a+=rocket_settings.acceleration(style.get_propulsion_control_dir(camera,input_state.controls));
} }
//add accelerators //add accelerators
for contact in &self.contacts{ for contact in self.contacts.keys(){
if let Some(accelerator)=&models.contact_attr(contact.model_id).general.accelerator{ if let Some(accelerator)=&models.contact_attr(contact.model_id).general.accelerator{
a+=accelerator.acceleration; a+=accelerator.acceleration;
} }
@@ -774,10 +807,10 @@ impl TouchingState{
a a
} }
fn constrain_velocity(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,velocity:Planar64Vec3)->Planar64Vec3{ fn constrain_velocity(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,velocity:Planar64Vec3)->Planar64Vec3{
let contacts:Vec<_>=self.contacts.iter().map(|contact|{ let contacts:Vec<_>=self.contacts.iter().map(|(convex_mesh_id,face_id)|{
let n=contact_normal(models,hitbox_mesh,contact); let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id);
crate::push_solve::Contact{ crate::push_solve::Contact{
position:vec3::ZERO, position:vec3::zero(),
velocity:n, velocity:n,
normal:n, normal:n,
} }
@@ -785,10 +818,10 @@ impl TouchingState{
crate::push_solve::push_solve(&contacts,velocity) crate::push_solve::push_solve(&contacts,velocity)
} }
fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:Planar64Vec3)->Planar64Vec3{ fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:Planar64Vec3)->Planar64Vec3{
let contacts:Vec<_>=self.contacts.iter().map(|contact|{ let contacts:Vec<_>=self.contacts.iter().map(|(convex_mesh_id,face_id)|{
let n=contact_normal(models,hitbox_mesh,contact); let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id);
crate::push_solve::Contact{ crate::push_solve::Contact{
position:vec3::ZERO, position:vec3::zero(),
velocity:n, velocity:n,
normal:n, normal:n,
} }
@@ -798,29 +831,29 @@ impl TouchingState{
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,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.relative_to(&Body::ZERO);
let relative_body=body; let relative_body=body;
for contact in &self.contacts{ for (convex_mesh_id,face_id) in &self.contacts{
//detect face slide off //detect face slide off
let model_mesh=models.contact_mesh(contact); let model_mesh=models.contact_mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),*face_id).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time:relative_body.time+time.into(), time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionEnd( instruction:InternalInstruction::CollisionEnd(
Collision::Contact(*contact), Collision::Contact(ContactCollision{face_id:*face_id,convex_mesh_id:*convex_mesh_id}),
time time
), ),
} }
})); }));
} }
for intersect in &self.intersects{ for convex_mesh_id in &self.intersects{
//detect model collision in reverse //detect model collision in reverse
let model_mesh=models.intersect_mesh(intersect); let model_mesh=models.intersect_mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_out(&relative_body,start_time..collector.time()).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_out(&relative_body,start_time..collector.time()).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time:relative_body.time+time.into(), time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionEnd( instruction:InternalInstruction::CollisionEnd(
Collision::Intersect(*intersect), Collision::Intersect(IntersectCollision{convex_mesh_id:*convex_mesh_id}),
time time
), ),
} }
@@ -947,7 +980,7 @@ impl PhysicsState{
// shared geometry for simulations // shared geometry for simulations
pub struct PhysicsData{ pub struct PhysicsData{
//permanent map data //permanent map data
bvh:bvh::BvhNode<ConvexMeshId>, bvh:bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
//transient map/environment data (open world loads/unloads parts of this data) //transient map/environment data (open world loads/unloads parts of this data)
models:PhysicsModels, models:PhysicsModels,
//semi-transient data //semi-transient data
@@ -1033,14 +1066,16 @@ impl PhysicsData{
let transform=PhysicsMeshTransform::new(model.transform); let transform=PhysicsMeshTransform::new(model.transform);
match attr_id{ match attr_id{
PhysicsAttributesId::Contact(attr_id)=>{ PhysicsAttributesId::Contact(attr_id)=>{
contact_models.insert(ContactModelId::new(model_id as u32),ContactModel{ let contact_model_id=ContactModelId::new(model_id as u32);
contact_models.insert(contact_model_id,ContactModel{
mesh_id, mesh_id,
attr_id, attr_id,
transform, transform,
}); });
}, },
PhysicsAttributesId::Intersect(attr_id)=>{ PhysicsAttributesId::Intersect(attr_id)=>{
intersect_models.insert(IntersectModelId::new(model_id as u32),IntersectModel{ let intersect_model_id=IntersectModelId::new(model_id as u32);
intersect_models.insert(intersect_model_id,IntersectModel{
mesh_id, mesh_id,
attr_id, attr_id,
transform, transform,
@@ -1054,12 +1089,23 @@ impl PhysicsData{
(PhysicsMeshId::new(mesh_id as u32),mesh) (PhysicsMeshId::new(mesh_id as u32),mesh)
).collect(); ).collect();
let convex_mesh_aabb_list= let convex_mesh_aabb_list=
//map the two lists into a single type so they can be processed with one closure // use the map models iteration order to ensure that the
contact_models.iter().map(|(&model_id,model)| // order that the models are passed into bvh::generate_bvh is consistent
(PhysicsModelId::Contact(model_id),&model.mesh_id,&model.transform) map.models.iter().enumerate().filter_map(|(model_id,model)|{
).chain(intersect_models.iter().map(|(&model_id,model)| match map.attributes.get(model.attributes.get() as usize){
(PhysicsModelId::Intersect(model_id),&model.mesh_id,&model.transform) None|Some(gameplay_attributes::CollisionAttributes::Decoration)=>None,
)) Some(gameplay_attributes::CollisionAttributes::Contact(_))=>{
let model_id=ContactModelId::new(model_id as u32);
let model=contact_models.get(&model_id)?;
Some((PhysicsModelId::Contact(model_id),&model.mesh_id,&model.transform))
},
Some(gameplay_attributes::CollisionAttributes::Intersect(_))=>{
let model_id=IntersectModelId::new(model_id as u32);
let model=intersect_models.get(&model_id)?;
Some((PhysicsModelId::Intersect(model_id),&model.mesh_id,&model.transform))
},
}
})
.flat_map(|(model_id,mesh_id,transform)|{ .flat_map(|(model_id,mesh_id,transform)|{
meshes[mesh_id].submesh_views() meshes[mesh_id].submesh_views()
.enumerate().map(move|(submesh_id,view)|{ .enumerate().map(move|(submesh_id,view)|{
@@ -1161,21 +1207,19 @@ impl<'a> PhysicsContext<'a>{
//relative to moving platforms //relative to moving platforms
//let relative_body=state.body.relative_to(&Body::ZERO); //let relative_body=state.body.relative_to(&Body::ZERO);
let relative_body=&state.body; let relative_body=&state.body;
data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{ data.bvh.sample_aabb(&aabb,&mut |convex_mesh_id|{
if state.touching.contains(convex_mesh_id){
return;
}
//no checks are needed because of the time limits. //no checks are needed because of the time limits.
let model_mesh=data.models.mesh(convex_mesh_id); let model_mesh=data.models.mesh(*convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_in(relative_body,state.time..collector.time()) collector.collect(minkowski.predict_collision_in(relative_body,state.time..collector.time())
//temp (?) code to avoid collision loops .map(|(face,dt)|
.and_then(|(face,dt)|{
// this must be rounded to avoid the infinite loop when hitting the start zone
let time=relative_body.time+dt.into();
(state.time<time).then_some((time,face,dt))
}).map(|(time,face,dt)|
TimedInstruction{ TimedInstruction{
time, time:relative_body.time+dt.into(),
instruction:InternalInstruction::CollisionStart( instruction:InternalInstruction::CollisionStart(
Collision::new(convex_mesh_id,face), Collision::new(*convex_mesh_id,face),
dt dt
) )
} }
@@ -1186,12 +1230,17 @@ impl<'a> PhysicsContext<'a>{
} }
fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{ fn contact_normal(
let model_mesh=models.contact_mesh(contact); models:&PhysicsModels,
hitbox_mesh:&HitboxMesh,
convex_mesh_id:&ConvexMeshId<ContactModelId>,
face_id:model_physics::MinkowskiFace,
)->Planar64Vec3{
let model_mesh=models.contact_mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
// TODO: normalize to i64::MAX>>1 // TODO: normalize to i64::MAX>>1
// wrap for speed // wrap for speed
minkowski.face_nd(contact.face_id).0.wrap_1() minkowski.face_nd(face_id).0.wrap_1()
} }
fn recalculate_touching( fn recalculate_touching(
@@ -1203,7 +1252,7 @@ fn recalculate_touching(
mode:Option<&gameplay_modes::Mode>, mode:Option<&gameplay_modes::Mode>,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1212,11 +1261,11 @@ fn recalculate_touching(
//collision_end all existing contacts //collision_end all existing contacts
//I would have preferred while let Some(contact)=contacts.pop() //I would have preferred while let Some(contact)=contacts.pop()
//but there is no such method //but there is no such method
while let Some(&contact)=touching.contacts.iter().next(){ while let Some((&convex_mesh_id,_face_id))=touching.contacts.iter().next(){
collision_end_contact(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,models.contact_attr(contact.model_id),contact) collision_end_contact(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,models.contact_attr(convex_mesh_id.model_id),&convex_mesh_id)
} }
while let Some(&intersect)=touching.intersects.iter().next(){ while let Some(&convex_mesh_id)=touching.intersects.iter().next(){
collision_end_intersect(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,mode,run,models.intersect_attr(intersect.model_id),intersect,time); collision_end_intersect(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,mode,run,models.intersect_attr(convex_mesh_id.model_id),&convex_mesh_id,time);
} }
//find all models in the teleport region //find all models in the teleport region
let mut aabb=aabb::Aabb::default(); let mut aabb=aabb::Aabb::default();
@@ -1238,8 +1287,7 @@ fn recalculate_touching(
collision_start_intersect(move_state,body,mode_state,touching,mode,run,models,hitbox_mesh,bvh,style,camera,input_state, collision_start_intersect(move_state,body,mode_state,touching,mode,run,models,hitbox_mesh,bvh,style,camera,input_state,
models.intersect_attr(model_id), models.intersect_attr(model_id),
IntersectCollision{ IntersectCollision{
model_id, convex_mesh_id:convex_mesh_id.map(model_id),
submesh_id:convex_mesh_id.submesh_id,
}, },
time, time,
), ),
@@ -1257,7 +1305,7 @@ fn set_position(
mode:Option<&gameplay_modes::Mode>, mode:Option<&gameplay_modes::Mode>,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1273,8 +1321,8 @@ fn set_position(
fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,v:Planar64Vec3)->bool{ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,v:Planar64Vec3)->bool{
//This is not correct but is better than what I have //This is not correct but is better than what I have
let mut culled=false; let mut culled=false;
touching.contacts.retain(|contact|{ touching.contacts.retain(|convex_mesh_id,face_id|{
let n=contact_normal(models,hitbox_mesh,contact); let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id);
let r=n.dot(v).is_positive(); let r=n.dot(v).is_positive();
if r{ if r{
culled=true; culled=true;
@@ -1291,8 +1339,8 @@ fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hit
fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3)->bool{ fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3)->bool{
//This is not correct but is better than what I have //This is not correct but is better than what I have
let mut culled=false; let mut culled=false;
touching.contacts.retain(|contact|{ touching.contacts.retain(|convex_mesh_id,face_id|{
let n=contact_normal(models,hitbox_mesh,contact); let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id);
let r=n.dot(a).is_positive(); let r=n.dot(a).is_positive();
if r{ if r{
culled=true; culled=true;
@@ -1316,7 +1364,7 @@ fn teleport(
mode:Option<&gameplay_modes::Mode>, mode:Option<&gameplay_modes::Mode>,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1338,7 +1386,7 @@ fn teleport_to_spawn(
mode:&gameplay_modes::Mode, mode:&gameplay_modes::Mode,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1427,7 +1475,7 @@ fn run_teleport_behaviour(
mode_state:&mut ModeState, mode_state:&mut ModeState,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1474,7 +1522,7 @@ fn run_teleport_behaviour(
} }
} }
fn not_spawn_at( fn is_not_spawn_at(
mode:Option<&gameplay_modes::Mode>, mode:Option<&gameplay_modes::Mode>,
model_id:ModelId, model_id:ModelId,
)->bool{ )->bool{
@@ -1495,7 +1543,7 @@ fn collision_start_contact(
mode:Option<&gameplay_modes::Mode>, mode:Option<&gameplay_modes::Mode>,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1505,12 +1553,12 @@ fn collision_start_contact(
){ ){
let incident_velocity=body.velocity; let incident_velocity=body.velocity;
//add to touching //add to touching
touching.insert(Collision::Contact(contact)); touching.insert_contact(contact);
//clip v //clip v
set_velocity(body,touching,models,hitbox_mesh,incident_velocity); set_velocity(body,touching,models,hitbox_mesh,incident_velocity);
let mut allow_jump=true; let mut allow_jump=true;
let model_id=contact.model_id.into(); let model_id=contact.convex_mesh_id.model_id.into();
let mut allow_run_teleport_behaviour=not_spawn_at(mode,model_id); let mut allow_run_teleport_behaviour=is_not_spawn_at(mode,model_id);
match &attr.contacting.contact_behaviour{ match &attr.contacting.contact_behaviour{
Some(gameplay_attributes::ContactingBehaviour::Surf)=>(), Some(gameplay_attributes::ContactingBehaviour::Surf)=>(),
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"), Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
@@ -1524,7 +1572,7 @@ fn collision_start_contact(
//kill v //kill v
//actually you could do this with a booster attribute :thinking: //actually you could do this with a booster attribute :thinking:
//it's a little bit different because maybe you want to chain ladders together //it's a little bit different because maybe you want to chain ladders together
set_velocity(body,touching,models,hitbox_mesh,vec3::ZERO);//model.velocity set_velocity(body,touching,models,hitbox_mesh,vec3::zero());//model.velocity
} }
//ladder walkstate //ladder walkstate
let (gravity,target_velocity)=ladder_things(ladder_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state); let (gravity,target_velocity)=ladder_things(ladder_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
@@ -1533,7 +1581,7 @@ fn collision_start_contact(
}, },
Some(gameplay_attributes::ContactingBehaviour::NoJump)=>allow_jump=false, Some(gameplay_attributes::ContactingBehaviour::NoJump)=>allow_jump=false,
None=>if let Some(walk_settings)=&style.walk{ None=>if let Some(walk_settings)=&style.walk{
if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact),vec3::Y){ if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id),vec3::Y){
allow_run_teleport_behaviour=true; allow_run_teleport_behaviour=true;
//ground //ground
let (gravity,target_velocity)=ground_things(walk_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state); let (gravity,target_velocity)=ground_things(walk_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
@@ -1598,7 +1646,7 @@ fn collision_start_intersect(
run:&mut run::Run, run:&mut run::Run,
models:&PhysicsModels, models:&PhysicsModels,
hitbox_mesh:&HitboxMesh, hitbox_mesh:&HitboxMesh,
bvh:&bvh::BvhNode<ConvexMeshId>, bvh:&bvh::BvhNode<ConvexMeshId<PhysicsModelId>>,
style:&StyleModifiers, style:&StyleModifiers,
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
@@ -1607,13 +1655,13 @@ fn collision_start_intersect(
time:Time, time:Time,
){ ){
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
touching.insert(Collision::Intersect(intersect)); touching.insert_intersect(intersect);
//insta booster! //insta booster!
if let Some(booster)=&attr.general.booster{ if let Some(booster)=&attr.general.booster{
move_state.cull_velocity(booster.boost(body.velocity),body,touching,models,hitbox_mesh,style,camera,input_state); move_state.cull_velocity(booster.boost(body.velocity),body,touching,models,hitbox_mesh,style,camera,input_state);
} }
if let Some(mode)=mode{ if let Some(mode)=mode{
let zone=mode.get_zone(intersect.model_id.into()); let zone=mode.get_zone(intersect.convex_mesh_id.model_id.into());
match zone{ match zone{
Some(gameplay_modes::Zone::Start)=>{ Some(gameplay_modes::Zone::Start)=>{
println!("@@@@ Starting new run!"); println!("@@@@ Starting new run!");
@@ -1630,7 +1678,7 @@ fn collision_start_intersect(
} }
} }
move_state.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state); move_state.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state);
run_teleport_behaviour(intersect.model_id.into(),attr.general.wormhole.as_ref(),mode,move_state,body,touching,run,mode_state,models,hitbox_mesh,bvh,style,camera,input_state,time); run_teleport_behaviour(intersect.convex_mesh_id.model_id.into(),attr.general.wormhole.as_ref(),mode,move_state,body,touching,run,mode_state,models,hitbox_mesh,bvh,style,camera,input_state,time);
} }
fn collision_end_contact( fn collision_end_contact(
@@ -1643,15 +1691,17 @@ fn collision_end_contact(
camera:&PhysicsCamera, camera:&PhysicsCamera,
input_state:&InputState, input_state:&InputState,
_attr:&gameplay_attributes::ContactAttributes, _attr:&gameplay_attributes::ContactAttributes,
contact:ContactCollision, convex_mesh_id:&ConvexMeshId<ContactModelId>,
){ ){
touching.remove(&Collision::Contact(contact));//remove contact before calling contact_constrain_acceleration touching.remove_contact(convex_mesh_id);//remove contact before calling contact_constrain_acceleration
//check ground //check ground
//TODO do better //TODO do better
//this is inner code from move_state.cull_velocity //this is inner code from move_state.cull_velocity
match move_state.get_walk_state(){ match move_state.get_walk_state(){
// did you stop touching the thing you were walking on? // did you stop touching the thing you were walking on?
Some(walk_state)=>if walk_state.contact==contact{ // This does not check the face! Is that a bad thing? It should be
// impossible to stop touching a different face than you started touching...
Some(walk_state)=>if &walk_state.contact.convex_mesh_id==convex_mesh_id{
move_state.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state); move_state.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state);
}else{ }else{
// stopped touching something else while walking // stopped touching something else while walking
@@ -1673,13 +1723,13 @@ fn collision_end_intersect(
mode:Option<&gameplay_modes::Mode>, mode:Option<&gameplay_modes::Mode>,
run:&mut run::Run, run:&mut run::Run,
_attr:&gameplay_attributes::IntersectAttributes, _attr:&gameplay_attributes::IntersectAttributes,
intersect:IntersectCollision, convex_mesh_id:&ConvexMeshId<IntersectModelId>,
time:Time, time:Time,
){ ){
touching.remove(&Collision::Intersect(intersect)); touching.remove_intersect(convex_mesh_id);
move_state.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state); move_state.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state);
if let Some(mode)=mode{ if let Some(mode)=mode{
let zone=mode.get_zone(intersect.model_id.into()); let zone=mode.get_zone(convex_mesh_id.model_id.into());
match zone{ match zone{
Some(gameplay_modes::Zone::Start)=>{ Some(gameplay_modes::Zone::Start)=>{
match run.start(time){ match run.start(time){
@@ -1713,7 +1763,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
&mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,&mut state.run, &mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,&mut state.run,
mode, mode,
&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state, &data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,
data.models.contact_attr(contact.model_id), data.models.contact_attr(contact.convex_mesh_id.model_id),
contact, contact,
state.time, state.time,
), ),
@@ -1721,7 +1771,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
&mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching, &mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,
mode, mode,
&mut state.run,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state, &mut state.run,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,
data.models.intersect_attr(intersect.model_id), data.models.intersect_attr(intersect.convex_mesh_id.model_id),
intersect, intersect,
state.time, state.time,
), ),
@@ -1730,15 +1780,15 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
InternalInstruction::CollisionEnd(collision,_)=>match collision{ InternalInstruction::CollisionEnd(collision,_)=>match collision{
Collision::Contact(contact)=>collision_end_contact( Collision::Contact(contact)=>collision_end_contact(
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
data.models.contact_attr(contact.model_id), data.models.contact_attr(contact.convex_mesh_id.model_id),
contact &contact.convex_mesh_id
), ),
Collision::Intersect(intersect)=>collision_end_intersect( Collision::Intersect(intersect)=>collision_end_intersect(
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
data.modes.get_mode(state.mode_state.get_mode_id()), data.modes.get_mode(state.mode_state.get_mode_id()),
&mut state.run, &mut state.run,
data.models.intersect_attr(intersect.model_id), data.models.intersect_attr(intersect.convex_mesh_id.model_id),
intersect, &intersect.convex_mesh_id,
state.time state.time
), ),
}, },
@@ -1749,7 +1799,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
if strafe_settings.activates(controls){ if strafe_settings.activates(controls){
let masked_controls=strafe_settings.mask(controls); let masked_controls=strafe_settings.mask(controls);
let control_dir=state.style.get_control_dir(masked_controls); let control_dir=state.style.get_control_dir(masked_controls);
if control_dir!=vec3::ZERO{ if control_dir!=vec3::zero(){
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x); let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){ if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
//this is wrong but will work ig //this is wrong but will work ig
@@ -1772,7 +1822,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
//which means that gravity can be fully cancelled //which means that gravity can be fully cancelled
//ignore moving platforms for now //ignore moving platforms for now
let target=core::mem::replace(&mut walk_state.target,TransientAcceleration::Reached); let target=core::mem::replace(&mut walk_state.target,TransientAcceleration::Reached);
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO); set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::zero());
// check what the target was to see if it was invalid // check what the target was to see if it was invalid
match target{ match target{
//you are not supposed to reach a walk target which is already reached! //you are not supposed to reach a walk target which is already reached!
@@ -1836,7 +1886,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
if let Some(walk_state)=state.move_state.get_walk_state(){ if let Some(walk_state)=state.move_state.get_walk_state(){
if let Some(jump_settings)=&state.style.jump{ if let Some(jump_settings)=&state.style.jump{
let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact); let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact);
let booster_option=data.models.contact_attr(walk_state.contact.model_id).general.booster.as_ref(); let booster_option=data.models.contact_attr(walk_state.contact.convex_mesh_id.model_id).general.booster.as_ref();
let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,booster_option); let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,booster_option);
state.cull_velocity(data,jumped_velocity); state.cull_velocity(data,jumped_velocity);
} }
@@ -1854,7 +1904,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}, },
Instruction::Mode(ModeInstruction::Restart(mode_id))=>{ Instruction::Mode(ModeInstruction::Restart(mode_id))=>{
//teleport to mode start zone //teleport to mode start zone
let mut spawn_point=vec3::ZERO; let mut spawn_point=vec3::zero();
let mode=data.modes.get_mode(mode_id); let mode=data.modes.get_mode(mode_id);
if let Some(mode)=mode{ if let Some(mode)=mode{
// set style // set style
@@ -1867,7 +1917,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
} }
set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time); set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time);
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO); set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::zero());
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::Air);
b_refresh_walk_target=false; b_refresh_walk_target=false;
} }
@@ -1917,7 +1967,7 @@ mod test{
use strafesnet_common::integer::{vec3::{self,int as int3},mat3}; use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
use super::*; use super::*;
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){ fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(mat3::from_diagonal(int3(5,1,5)>>1),vec3::ZERO)); let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(mat3::from_diagonal(int3(5,1,5)>>1),vec3::zero()));
let h1=StyleModifiers::roblox_bhop().calculate_mesh(); let h1=StyleModifiers::roblox_bhop().calculate_mesh();
let hitbox_mesh=h1.transformed_mesh(); let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh(); let platform_mesh=h0.transformed_mesh();
@@ -1928,12 +1978,12 @@ mod test{
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){ fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(), let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
integer::Planar64Affine3::new( integer::Planar64Affine3::new(
integer::Planar64Mat3::from_cols([ Planar64Mat3::from_cols([
int3(5,0,1)>>1, int3(5,0,1)>>1,
int3(0,1,0)>>1, int3(0,1,0)>>1,
int3(-1,0,5)>>1, int3(-1,0,5)>>1,
]), ]),
vec3::ZERO vec3::zero()
), ),
); );
let h1=StyleModifiers::roblox_bhop().calculate_mesh(); let h1=StyleModifiers::roblox_bhop().calculate_mesh();
@@ -1952,7 +2002,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(0,5,0), int3(0,5,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -1961,7 +2011,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(0,5,0), int3(0,5,0),
int3(0,-1,0)+(vec3::X>>32), int3(0,-1,0)+(vec3::X>>32),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -1970,7 +2020,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(3,5,0), int3(3,5,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -1979,7 +2029,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(0,5,3), int3(0,5,3),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -1988,7 +2038,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(-3,5,0), int3(-3,5,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -1997,7 +2047,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(0,5,-3), int3(0,5,-3),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -2114,7 +2164,7 @@ mod test{
test_collision(Body::new( test_collision(Body::new(
int3(0,5,0), int3(0,5,0),
int3(1,-64,2)>>6,// /64 int3(1,-64,2)>>6,// /64
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
@@ -2130,7 +2180,7 @@ mod test{
#[test] #[test]
fn already_inside_hit_nothing(){ fn already_inside_hit_nothing(){
test_collision(Body::new( test_collision(Body::new(
vec3::ZERO, vec3::zero(),
int3(1,0,0), int3(1,0,0),
int3(0,1,0), int3(0,1,0),
Time::ZERO Time::ZERO
@@ -2142,7 +2192,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(0,10,-7)>>1)+vec3::raw_xyz(0,0,1), (int3(0,10,-7)>>1)+vec3::raw_xyz(0,0,1),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))) ),Some(Time::from_secs(2)))
} }
@@ -2151,7 +2201,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(7,10,0)>>1)+vec3::raw_xyz(-1,0,0), (int3(7,10,0)>>1)+vec3::raw_xyz(-1,0,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))) ),Some(Time::from_secs(2)))
} }
@@ -2160,7 +2210,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(0,10,7)>>1)+vec3::raw_xyz(0,0,-1), (int3(0,10,7)>>1)+vec3::raw_xyz(0,0,-1),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))) ),Some(Time::from_secs(2)))
} }
@@ -2169,7 +2219,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(-7,10,0)>>1)+vec3::raw_xyz(1,0,0), (int3(-7,10,0)>>1)+vec3::raw_xyz(1,0,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),Some(Time::from_secs(2))) ),Some(Time::from_secs(2)))
} }
@@ -2179,7 +2229,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
int3(0,10,-7)>>1, int3(0,10,-7)>>1,
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2188,7 +2238,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
int3(7,10,0)>>1, int3(7,10,0)>>1,
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2197,7 +2247,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
int3(0,10,7)>>1, int3(0,10,7)>>1,
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2206,7 +2256,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
int3(-7,10,0)>>1, int3(-7,10,0)>>1,
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2216,7 +2266,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(0,10,-7)>>1)-vec3::raw_xyz(0,0,1), (int3(0,10,-7)>>1)-vec3::raw_xyz(0,0,1),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2225,7 +2275,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(7,10,0)>>1)-vec3::raw_xyz(-1,0,0), (int3(7,10,0)>>1)-vec3::raw_xyz(-1,0,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2234,7 +2284,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(0,10,7)>>1)-vec3::raw_xyz(0,0,-1), (int3(0,10,7)>>1)-vec3::raw_xyz(0,0,-1),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }
@@ -2243,7 +2293,7 @@ mod test{
test_collision_axis_aligned(Body::new( test_collision_axis_aligned(Body::new(
(int3(-7,10,0)>>1)-vec3::raw_xyz(1,0,0), (int3(-7,10,0)>>1)-vec3::raw_xyz(1,0,0),
int3(0,-1,0), int3(0,-1,0),
vec3::ZERO, vec3::zero(),
Time::ZERO Time::ZERO
),None) ),None)
} }

View File

@@ -39,20 +39,18 @@ impl Contact{
//note that this is horrible with fixed point arithmetic //note that this is horrible with fixed point arithmetic
fn solve1(c0:&Contact)->Option<Ratio<Vector3<Fixed<3,96>>,Fixed<2,64>>>{ fn solve1(c0:&Contact)->Option<Ratio<Vector3<Fixed<3,96>>,Fixed<2,64>>>{
const EPSILON:Fixed<2,64>=Fixed::from_bits(Fixed::<2,64>::ONE.to_bits().shr(10));
let det=c0.normal.dot(c0.velocity); let det=c0.normal.dot(c0.velocity);
if det.abs()<EPSILON{ if det.abs()==Fixed::ZERO{
return None; return None;
} }
let d0=c0.normal.dot(c0.position); let d0=c0.normal.dot(c0.position);
Some(c0.normal*d0/det) Some(c0.normal*d0/det)
} }
fn solve2(c0:&Contact,c1:&Contact)->Option<Ratio<Vector3<Fixed<5,160>>,Fixed<4,128>>>{ fn solve2(c0:&Contact,c1:&Contact)->Option<Ratio<Vector3<Fixed<5,160>>,Fixed<4,128>>>{
const EPSILON:Fixed<4,128>=Fixed::from_bits(Fixed::<4,128>::ONE.to_bits().shr(10));
let u0_u1=c0.velocity.cross(c1.velocity); let u0_u1=c0.velocity.cross(c1.velocity);
let n0_n1=c0.normal.cross(c1.normal); let n0_n1=c0.normal.cross(c1.normal);
let det=u0_u1.dot(n0_n1); let det=u0_u1.dot(n0_n1);
if det.abs()<EPSILON{ if det.abs()==Fixed::ZERO{
return None; return None;
} }
let d0=c0.normal.dot(c0.position); let d0=c0.normal.dot(c0.position);
@@ -60,10 +58,9 @@ fn solve2(c0:&Contact,c1:&Contact)->Option<Ratio<Vector3<Fixed<5,160>>,Fixed<4,1
Some((c1.normal.cross(u0_u1)*d0+u0_u1.cross(c0.normal)*d1)/det) Some((c1.normal.cross(u0_u1)*d0+u0_u1.cross(c0.normal)*d1)/det)
} }
fn solve3(c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ratio<Vector3<Fixed<4,128>>,Fixed<3,96>>>{ fn solve3(c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ratio<Vector3<Fixed<4,128>>,Fixed<3,96>>>{
const EPSILON:Fixed<3,96>=Fixed::from_bits(Fixed::<3,96>::ONE.to_bits().shr(10));
let n0_n1=c0.normal.cross(c1.normal); let n0_n1=c0.normal.cross(c1.normal);
let det=c2.normal.dot(n0_n1); let det=c2.normal.dot(n0_n1);
if det.abs()<EPSILON{ if det.abs()==Fixed::ZERO{
return None; return None;
} }
let d0=c0.normal.dot(c0.position); let d0=c0.normal.dot(c0.position);
@@ -149,7 +146,7 @@ fn is_space_enclosed_4(
} }
const fn get_push_ray_0(point:Planar64Vec3)->Ray{ const fn get_push_ray_0(point:Planar64Vec3)->Ray{
Ray{origin:point,direction:vec3::ZERO} Ray{origin:point,direction:vec3::zero()}
} }
fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{ fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{
//wrap for speed //wrap for speed
@@ -321,13 +318,13 @@ mod tests{
fn test_push_solve(){ fn test_push_solve(){
let contacts=vec![ let contacts=vec![
Contact{ Contact{
position:vec3::ZERO, position:vec3::zero(),
velocity:vec3::Y, velocity:vec3::Y,
normal:vec3::Y, normal:vec3::Y,
} }
]; ];
assert_eq!( assert_eq!(
vec3::ZERO, vec3::zero(),
push_solve(&contacts,vec3::NEG_Y) push_solve(&contacts,vec3::NEG_Y)
); );
} }

View File

@@ -10,3 +10,6 @@ strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
strafesnet_physics = { path = "../physics", registry = "strafesnet" } strafesnet_physics = { path = "../physics", registry = "strafesnet" }
strafesnet_settings = { path = "../settings", registry = "strafesnet" } strafesnet_settings = { path = "../settings", registry = "strafesnet" }
strafesnet_snf = { path = "../../lib/snf", registry = "strafesnet" } strafesnet_snf = { path = "../../lib/snf", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -31,7 +31,7 @@ pub enum SessionInputInstruction{
Mouse(glam::IVec2), Mouse(glam::IVec2),
SetControl(strafesnet_common::physics::SetControlInstruction), SetControl(strafesnet_common::physics::SetControlInstruction),
Mode(ImplicitModeInstruction), Mode(ImplicitModeInstruction),
Misc(strafesnet_common::physics::MiscInstruction), Misc(MiscInstruction),
} }
/// Implicit mode instruction are fed separately to session. /// Implicit mode instruction are fed separately to session.
/// Session generates the explicit mode instructions interlaced with a SetSensitivity instruction /// Session generates the explicit mode instructions interlaced with a SetSensitivity instruction
@@ -52,7 +52,6 @@ pub enum SessionControlInstruction{
pub enum SessionPlaybackInstruction{ pub enum SessionPlaybackInstruction{
SkipForward, SkipForward,
SkipBack, SkipBack,
TogglePaused,
DecreaseTimescale, DecreaseTimescale,
IncreaseTimescale, IncreaseTimescale,
} }
@@ -152,10 +151,10 @@ enum ViewState{
pub struct Session{ pub struct Session{
directories:Directories, directories:Directories,
user_settings:UserSettings, user_settings:UserSettings,
mouse_interpolator:crate::mouse_interpolator::MouseInterpolator, mouse_interpolator:MouseInterpolator,
view_state:ViewState, view_state:ViewState,
//gui:GuiState //gui:GuiState
geometry_shared:physics::PhysicsData, geometry_shared:PhysicsData,
simulation:Simulation, simulation:Simulation,
// below fields not included in lite session // below fields not included in lite session
recording:Recording, recording:Recording,
@@ -253,7 +252,14 @@ impl InstructionConsumer<Instruction<'_>> for Session{
// don't flush the buffered instructions in the mouse interpolator // don't flush the buffered instructions in the mouse interpolator
// until the mouse is confirmed to be not moving at a later time // until the mouse is confirmed to be not moving at a later time
// what if they pause for 5ms lmao // what if they pause for 5ms lmao
_=self.simulation.timer.set_paused(ins.time,paused); match &self.view_state{
ViewState::Play=>{
_=self.simulation.timer.set_paused(ins.time,paused);
},
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
_=replay.simulation.timer.set_paused(ins.time,paused);
},
}
}, },
Instruction::Control(SessionControlInstruction::CopyRecordingIntoReplayAndSpectate)=> if let ViewState::Play=self.view_state{ Instruction::Control(SessionControlInstruction::CopyRecordingIntoReplayAndSpectate)=> if let ViewState::Play=self.view_state{
// Bind: B // Bind: B
@@ -374,14 +380,6 @@ impl InstructionConsumer<Instruction<'_>> for Session{
}, },
} }
}, },
Instruction::Playback(SessionPlaybackInstruction::TogglePaused)=>{
match &self.view_state{
ViewState::Play=>(),
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
_=replay.simulation.timer.set_paused(ins.time,!replay.simulation.timer.is_paused());
},
}
}
Instruction::ChangeMap(complete_map)=>{ Instruction::ChangeMap(complete_map)=>{
self.clear_recording(); self.clear_recording();
self.change_map(complete_map); self.change_map(complete_map);

View File

@@ -8,3 +8,6 @@ configparser = "3.0.2"
directories = "6.0.0" directories = "6.0.0"
glam = "0.30.0" glam = "0.30.0"
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" } strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -10,3 +10,6 @@ strafesnet_physics = { path = "../engine/physics", registry = "strafesnet" }
strafesnet_snf = { path = "../lib/snf", registry = "strafesnet" } strafesnet_snf = { path = "../lib/snf", registry = "strafesnet" }
# this is just for the primitive constructor # this is just for the primitive constructor
strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet" } strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -77,7 +77,7 @@ fn simultaneous_collision(){
Time::ZERO, Time::ZERO,
); );
let mut physics=PhysicsState::new_with_body(body); let mut physics=PhysicsState::new_with_body(body);
physics.style_mut().gravity=vec3::ZERO; physics.style_mut().gravity=vec3::zero();
let mut phys_iter=PhysicsContext::iter_internal(&mut physics,&physics_data,Time::from_secs(2)) let mut phys_iter=PhysicsContext::iter_internal(&mut physics,&physics_data,Time::from_secs(2))
.filter(|ins|!matches!(ins.instruction,InternalInstruction::StrafeTick)); .filter(|ins|!matches!(ins.instruction,InternalInstruction::StrafeTick));
// the order that they hit does matter, but we aren't currently worrying about that. // the order that they hit does matter, but we aren't currently worrying about that.
@@ -101,7 +101,7 @@ fn bug_3(){
Time::ZERO, Time::ZERO,
); );
let mut physics=PhysicsState::new_with_body(body); let mut physics=PhysicsState::new_with_body(body);
physics.style_mut().gravity=vec3::ZERO; physics.style_mut().gravity=vec3::zero();
let mut phys_iter=PhysicsContext::iter_internal(&mut physics,&physics_data,Time::from_secs(3)) let mut phys_iter=PhysicsContext::iter_internal(&mut physics,&physics_data,Time::from_secs(3))
.filter(|ins|!matches!(ins.instruction,InternalInstruction::StrafeTick)); .filter(|ins|!matches!(ins.instruction,InternalInstruction::StrafeTick));
// touch side of part at 0,0,0 // touch side of part at 0,0,0

View File

@@ -29,7 +29,7 @@ fn physics_bug_2()->Result<(),ReplayError>{
// wait one second to activate the bug // wait one second to activate the bug
// hit=Some(ModelId(2262)) // hit=Some(ModelId(2262))
PhysicsContext::run_input_instruction(&mut physics,&physics_data,strafesnet_common::instruction::TimedInstruction{ PhysicsContext::run_input_instruction(&mut physics,&physics_data,strafesnet_common::instruction::TimedInstruction{
time:strafesnet_common::integer::Time::from_millis(500), time:Time::from_millis(500),
instruction:strafesnet_common::physics::Instruction::Idle, instruction:strafesnet_common::physics::Instruction::Idle,
}); });
@@ -68,7 +68,7 @@ fn physics_bug_3()->Result<(),ReplayError>{
let mut physics=PhysicsState::new_with_body(body); let mut physics=PhysicsState::new_with_body(body);
// wait one second to activate the bug // wait one second to activate the bug
PhysicsContext::run_input_instruction(&mut physics,&physics_data,strafesnet_common::instruction::TimedInstruction{ PhysicsContext::run_input_instruction(&mut physics,&physics_data,strafesnet_common::instruction::TimedInstruction{
time:strafesnet_common::integer::Time::from_millis(500), time:Time::from_millis(500),
instruction:strafesnet_common::physics::Instruction::Idle, instruction:strafesnet_common::physics::Instruction::Idle,
}); });

View File

@@ -17,3 +17,6 @@ vbsp = "0.9.1"
vbsp-entities-css = "0.6.0" vbsp-entities-css = "0.6.0"
vmdl = "0.2.0" vmdl = "0.2.0"
vpk = "0.3.0" vpk = "0.3.0"
[lints]
workspace = true

View File

@@ -7,7 +7,7 @@ use crate::{valve_transform_normal,valve_transform_dist};
#[derive(Hash,Eq,PartialEq)] #[derive(Hash,Eq,PartialEq)]
struct Face{ struct Face{
normal:integer::Planar64Vec3, normal:integer::Planar64Vec3,
dot:integer::Planar64, dot:Planar64,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -210,7 +210,7 @@ pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{
let color=mb.acquire_color_id(glam::Vec4::ONE); let color=mb.acquire_color_id(glam::Vec4::ONE);
let tex=mb.acquire_tex_id(glam::Vec2::ZERO); let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
// normals are ignored by physics // normals are ignored by physics
let normal=mb.acquire_normal_id(integer::vec3::ZERO); let normal=mb.acquire_normal_id(integer::vec3::zero());
let polygon_list=faces.into_iter().map(|face|{ let polygon_list=faces.into_iter().map(|face|{
face.into_iter().map(|pos|{ face.into_iter().map(|pos|{

View File

@@ -105,7 +105,7 @@ pub fn convert<'a>(
water:Some(attr::IntersectingWater{ water:Some(attr::IntersectingWater{
viscosity:integer::Planar64::ONE, viscosity:integer::Planar64::ONE,
density:integer::Planar64::ONE, density:integer::Planar64::ONE,
velocity:integer::vec3::ZERO, velocity:integer::vec3::zero(),
}), }),
}, },
general:attr::GeneralAttributes::default(), general:attr::GeneralAttributes::default(),
@@ -295,7 +295,7 @@ pub fn convert<'a>(
attributes, attributes,
transform:integer::Planar64Affine3::new( transform:integer::Planar64Affine3::new(
integer::mat3::identity(), integer::mat3::identity(),
integer::vec3::ZERO, integer::vec3::zero(),
), ),
color:glam::Vec4::ONE, color:glam::Vec4::ONE,
}); });
@@ -347,7 +347,7 @@ pub struct PartialMap1{
modes:NormalizedModes, modes:NormalizedModes,
} }
impl PartialMap1{ impl PartialMap1{
pub fn add_prop_meshes<'a>( pub fn add_prop_meshes(
self, self,
prop_meshes:Meshes<model::Mesh>, prop_meshes:Meshes<model::Mesh>,
)->PartialMap2{ )->PartialMap2{

View File

@@ -31,7 +31,7 @@ impl Loader for TextureLoader{
type Error=TextureError; type Error=TextureError;
type Index<'a>=Cow<'a,str>; type Index<'a>=Cow<'a,str>;
type Resource=Texture; type Resource=Texture;
fn load<'a>(&mut self,index:Self::Index<'a>)->Result<Self::Resource,Self::Error>{ fn load(&mut self,index:Self::Index<'_>)->Result<Self::Resource,Self::Error>{
let file_name=format!("textures/{}.dds",index); let file_name=format!("textures/{}.dds",index);
let mut file=std::fs::File::open(file_name)?; let mut file=std::fs::File::open(file_name)?;
let mut data=Vec::new(); let mut data=Vec::new();
@@ -111,7 +111,7 @@ impl ModelLoader<'_,'_>{
} }
} }
} }
impl<'bsp,'vpk> Loader for ModelLoader<'bsp,'vpk>{ impl Loader for ModelLoader<'_,'_>{
type Error=MeshError; type Error=MeshError;
type Index<'a>=&'a str where Self:'a; type Index<'a>=&'a str where Self:'a;
type Resource=vmdl::Model; type Resource=vmdl::Model;
@@ -151,7 +151,7 @@ impl MeshLoader<'_,'_,'_,'_>{
} }
} }
} }
impl<'str,'bsp,'vpk,'load> Loader for MeshLoader<'bsp,'vpk,'load,'str>{ impl Loader for MeshLoader<'_,'_,'_,'_>{
type Error=MeshError; type Error=MeshError;
type Index<'a>=&'a str where Self:'a; type Index<'a>=&'a str where Self:'a;
type Resource=Mesh; type Resource=Mesh;

View File

@@ -17,3 +17,6 @@ linear_ops = { version = "0.1.1", path = "../linear_ops", registry = "strafesnet
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet" } ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet" }
glam = "0.30.0" glam = "0.30.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -61,11 +61,11 @@ impl Aabb{
pub fn center(&self)->Planar64Vec3{ pub fn center(&self)->Planar64Vec3{
self.min.map_zip(self.max,|(min,max)|min.midpoint(max)) self.min.map_zip(self.max,|(min,max)|min.midpoint(max))
} }
//probably use floats for area & volume because we don't care about precision #[inline]
// pub fn area_weight(&self)->f32{ pub fn area_weight(&self)->fixed_wide::fixed::Fixed<2,64>{
// let d=self.max-self.min; let d=self.max-self.min;
// d.x*d.y+d.y*d.z+d.z*d.x d.x*d.y+d.y*d.z+d.z*d.x
// } }
// pub fn volume(&self)->f32{ // pub fn volume(&self)->f32{
// let d=self.max-self.min; // let d=self.max-self.min;
// d.x*d.y*d.z // d.x*d.y*d.z

View File

@@ -245,18 +245,19 @@ pub fn generate_bvh<T>(boxen:Vec<(T,Aabb)>)->BvhNode<T>{
fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
let n=boxen.len(); let n=boxen.len();
if force||n<20{ const MAX_TERMINAL_BRANCH_LEAF_NODES:usize=20;
let mut aabb=Aabb::default(); if force||n<MAX_TERMINAL_BRANCH_LEAF_NODES{
let nodes=boxen.into_iter().map(|b|{ let mut aabb_outer=Aabb::default();
aabb.join(&b.1); let nodes=boxen.into_iter().map(|(data,aabb)|{
aabb_outer.join(&aabb);
BvhNode{ BvhNode{
content:RecursiveContent::Leaf(b.0), content:RecursiveContent::Leaf(data),
aabb:b.1, aabb,
} }
}).collect(); }).collect();
BvhNode{ BvhNode{
content:RecursiveContent::Branch(nodes), content:RecursiveContent::Branch(nodes),
aabb, aabb:aabb_outer,
} }
}else{ }else{
let mut sort_x=Vec::with_capacity(n); let mut sort_x=Vec::with_capacity(n);
@@ -272,9 +273,9 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
sort_y.sort_by_key(|&(_,c)|c); sort_y.sort_by_key(|&(_,c)|c);
sort_z.sort_by_key(|&(_,c)|c); sort_z.sort_by_key(|&(_,c)|c);
let h=n/2; let h=n/2;
let median_x=sort_x[h].1; let (_,median_x)=sort_x[h];
let median_y=sort_y[h].1; let (_,median_y)=sort_y[h];
let median_z=sort_z[h].1; let (_,median_z)=sort_z[h];
//locate a run of values equal to the median //locate a run of values equal to the median
//partition point gives the first index for which the predicate evaluates to false //partition point gives the first index for which the predicate evaluates to false
let first_index_eq_median_x=sort_x.partition_point(|&(_,x)|x<median_x); let first_index_eq_median_x=sort_x.partition_point(|&(_,x)|x<median_x);
@@ -313,10 +314,10 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
}; };
list_list[list_id].push((data,aabb)); list_list[list_id].push((data,aabb));
} }
let mut aabb=Aabb::default();
if list_list.len()==1{ if list_list.len()==1{
generate_bvh_node(list_list.remove(0),true) generate_bvh_node(list_list.remove(0),true)
}else{ }else{
let mut aabb=Aabb::default();
BvhNode{ BvhNode{
content:RecursiveContent::Branch( content:RecursiveContent::Branch(
list_list.into_iter().map(|b|{ list_list.into_iter().map(|b|{

View File

@@ -34,7 +34,7 @@ pub struct StyleModifiers{
//unused //unused
pub mass:Planar64, pub mass:Planar64,
} }
impl std::default::Default for StyleModifiers{ impl Default for StyleModifiers{
fn default()->Self{ fn default()->Self{
Self::roblox_bhop() Self::roblox_bhop()
} }
@@ -319,7 +319,7 @@ impl WalkSettings{
self.accelerate.accel.min((-gravity.y*friction).clamp_1()) self.accelerate.accel.min((-gravity.y*friction).clamp_1())
} }
pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{ pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
if control_dir==crate::integer::vec3::ZERO{ if control_dir==crate::integer::vec3::zero(){
return control_dir; return control_dir;
} }
let nn=normal.length_squared(); let nn=normal.length_squared();
@@ -329,13 +329,13 @@ impl WalkSettings{
let dd=d*d; let dd=d*d;
if dd<nnmm{ if dd<nnmm{
let cr=normal.cross(control_dir); let cr=normal.cross(control_dir);
if cr==crate::integer::vec3::ZERO_2{ if cr==crate::integer::vec3::zero(){
crate::integer::vec3::ZERO crate::integer::vec3::zero()
}else{ }else{
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().clamp_1() (cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().clamp_1()
} }
}else{ }else{
crate::integer::vec3::ZERO crate::integer::vec3::zero()
} }
} }
pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{ pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{
@@ -360,7 +360,7 @@ impl LadderSettings{
self.accelerate.accel self.accelerate.accel
} }
pub fn get_ladder_target_velocity(&self,mut control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{ pub fn get_ladder_target_velocity(&self,mut control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
if control_dir==crate::integer::vec3::ZERO{ if control_dir==crate::integer::vec3::zero(){
return control_dir; return control_dir;
} }
let nn=normal.length_squared(); let nn=normal.length_squared();
@@ -382,13 +382,13 @@ impl LadderSettings{
//- fix the underlying issue //- fix the underlying issue
if dd<nnmm{ if dd<nnmm{
let cr=normal.cross(control_dir); let cr=normal.cross(control_dir);
if cr==crate::integer::vec3::ZERO_2{ if cr==crate::integer::vec3::zero(){
crate::integer::vec3::ZERO crate::integer::vec3::zero()
}else{ }else{
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().clamp_1() (cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().clamp_1()
} }
}else{ }else{
crate::integer::vec3::ZERO crate::integer::vec3::zero()
} }
} }
} }

View File

@@ -86,7 +86,7 @@ impl<T> std::fmt::Display for Time<T>{
write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0) write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0)
} }
} }
impl<T> std::default::Default for Time<T>{ impl<T> Default for Time<T>{
fn default()->Self{ fn default()->Self{
Self::raw(0) Self::raw(0)
} }
@@ -126,7 +126,7 @@ impl_time_additive_assign_operator!(core::ops::AddAssign,add_assign);
impl_time_additive_assign_operator!(core::ops::SubAssign,sub_assign); impl_time_additive_assign_operator!(core::ops::SubAssign,sub_assign);
impl_time_additive_assign_operator!(core::ops::RemAssign,rem_assign); impl_time_additive_assign_operator!(core::ops::RemAssign,rem_assign);
impl<T> std::ops::Mul for Time<T>{ impl<T> std::ops::Mul for Time<T>{
type Output=Ratio<fixed_wide::fixed::Fixed<2,64>,fixed_wide::fixed::Fixed<2,64>>; type Output=Ratio<Fixed<2,64>,Fixed<2,64>>;
#[inline] #[inline]
fn mul(self,rhs:Self)->Self::Output{ fn mul(self,rhs:Self)->Self::Output{
Ratio::new(Fixed::raw(self.0)*Fixed::raw(rhs.0),Fixed::raw_digit(1_000_000_000i64.pow(2))) Ratio::new(Fixed::raw(self.0)*Fixed::raw(rhs.0),Fixed::raw_digit(1_000_000_000i64.pow(2)))
@@ -156,7 +156,7 @@ impl<T> core::ops::Mul<Time<T>> for Planar64{
#[cfg(test)] #[cfg(test)]
mod test_time{ mod test_time{
use super::*; use super::*;
type Time=super::AbsoluteTime; type Time=AbsoluteTime;
#[test] #[test]
fn time_from_planar64(){ fn time_from_planar64(){
let a:Time=Planar64::from(1).into(); let a:Time=Planar64::from(1).into();
@@ -552,7 +552,7 @@ impl TryFrom<[f32;3]> for Unit32Vec3{
} }
*/ */
pub type Planar64TryFromFloatError=fixed_wide::fixed::FixedFromFloatError; pub type Planar64TryFromFloatError=FixedFromFloatError;
pub type Planar64=fixed_wide::types::I32F32; pub type Planar64=fixed_wide::types::I32F32;
pub type Planar64Vec3=linear_ops::types::Vector3<Planar64>; pub type Planar64Vec3=linear_ops::types::Vector3<Planar64>;
pub type Planar64Mat3=linear_ops::types::Matrix3<Planar64>; pub type Planar64Mat3=linear_ops::types::Matrix3<Planar64>;
@@ -561,12 +561,6 @@ pub mod vec3{
pub use linear_ops::types::Vector3; pub use linear_ops::types::Vector3;
pub const MIN:Planar64Vec3=Planar64Vec3::new([Planar64::MIN;3]); pub const MIN:Planar64Vec3=Planar64Vec3::new([Planar64::MIN;3]);
pub const MAX:Planar64Vec3=Planar64Vec3::new([Planar64::MAX;3]); pub const MAX:Planar64Vec3=Planar64Vec3::new([Planar64::MAX;3]);
pub const ZERO:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO;3]);
pub const ZERO_2:linear_ops::types::Vector3<Fixed::<2,64>>=linear_ops::types::Vector3::new([Fixed::<2,64>::ZERO;3]);
pub const ZERO_3:linear_ops::types::Vector3<Fixed::<3,96>>=linear_ops::types::Vector3::new([Fixed::<3,96>::ZERO;3]);
pub const ZERO_4:linear_ops::types::Vector3<Fixed::<4,128>>=linear_ops::types::Vector3::new([Fixed::<4,128>::ZERO;3]);
pub const ZERO_5:linear_ops::types::Vector3<Fixed::<5,160>>=linear_ops::types::Vector3::new([Fixed::<5,160>::ZERO;3]);
pub const ZERO_6:linear_ops::types::Vector3<Fixed::<6,192>>=linear_ops::types::Vector3::new([Fixed::<6,192>::ZERO;3]);
pub const X:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ZERO,Planar64::ZERO]); pub const X:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ZERO,Planar64::ZERO]);
pub const Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]); pub const Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]);
pub const Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::ONE]); pub const Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::ONE]);
@@ -575,6 +569,10 @@ pub mod vec3{
pub const NEG_Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::NEG_ONE,Planar64::ZERO]); pub const NEG_Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::NEG_ONE,Planar64::ZERO]);
pub const NEG_Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::NEG_ONE]); pub const NEG_Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::NEG_ONE]);
pub const NEG_ONE:Planar64Vec3=Planar64Vec3::new([Planar64::NEG_ONE,Planar64::NEG_ONE,Planar64::NEG_ONE]); pub const NEG_ONE:Planar64Vec3=Planar64Vec3::new([Planar64::NEG_ONE,Planar64::NEG_ONE,Planar64::NEG_ONE]);
// TODO: use #![feature(generic_const_items)] when stabilized https://github.com/rust-lang/rust/issues/113521
pub const fn zero<const N:usize,const F:usize>()->Vector3<Fixed<N,F>>{
Vector3::new([Fixed::ZERO;3])
}
#[inline] #[inline]
pub const fn int(x:i32,y:i32,z:i32)->Planar64Vec3{ pub const fn int(x:i32,y:i32,z:i32)->Planar64Vec3{
Planar64Vec3::new([Planar64::raw((x as i64)<<32),Planar64::raw((y as i64)<<32),Planar64::raw((z as i64)<<32)]) Planar64Vec3::new([Planar64::raw((x as i64)<<32),Planar64::raw((y as i64)<<32),Planar64::raw((z as i64)<<32)])
@@ -663,7 +661,7 @@ pub struct Planar64Affine3{
pub translation:Planar64Vec3, pub translation:Planar64Vec3,
} }
impl Planar64Affine3{ impl Planar64Affine3{
pub const IDENTITY:Self=Self::new(mat3::identity(),vec3::ZERO); pub const IDENTITY:Self=Self::new(mat3::identity(),vec3::zero());
#[inline] #[inline]
pub const fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{ pub const fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{
Self{matrix3,translation} Self{matrix3,translation}

View File

@@ -11,3 +11,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[dependencies] [dependencies]
strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -4,5 +4,5 @@ pub trait Loader{
type Error:Error; type Error:Error;
type Index<'a> where Self:'a; type Index<'a> where Self:'a;
type Resource; type Resource;
fn load<'a>(&mut self,index:Self::Index<'a>)->Result<Self::Resource,Self::Error>; fn load(&mut self,index:Self::Index<'_>)->Result<Self::Resource,Self::Error>;
} }

View File

@@ -18,3 +18,6 @@ bnum = "0.13.0"
arrayvec = { version = "0.7.6", optional = true } arrayvec = { version = "0.7.6", optional = true }
paste = "1.0.15" paste = "1.0.15"
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true } ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }
[lints]
workspace = true

View File

@@ -1,6 +1,8 @@
use bnum::{BInt,cast::As}; use bnum::{BInt,cast::As};
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,Eq,PartialOrd,Ord)] const BNUM_DIGIT_WIDTH:usize=64;
#[derive(Clone,Copy,Default,Hash,PartialEq,Eq,PartialOrd,Ord)]
/// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled) /// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled)
/// N is the number of u64s to use /// N is the number of u64s to use
/// F is the number of fractional bits (always N*32 lol) /// F is the number of fractional bits (always N*32 lol)
@@ -68,6 +70,34 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
pub const fn midpoint(self,other:Self)->Self{ pub const fn midpoint(self,other:Self)->Self{
Self::from_bits(self.bits.midpoint(other.bits)) Self::from_bits(self.bits.midpoint(other.bits))
} }
#[inline]
pub const fn min(self,other:Self)->Self{
Self::from_bits(self.bits.min(other.bits))
}
#[inline]
pub const fn max(self,other:Self)->Self{
Self::from_bits(self.bits.max(other.bits))
}
/// return the result of self*sign(other)
#[inline]
pub const fn mul_sign<const N1:usize,const F1:usize>(self,other:Fixed<N1,F1>)->Self{
if other.is_negative(){
Self::from_bits(self.bits.neg())
}else if other.is_zero(){
Fixed::ZERO
}else{
self
}
}
/// return the result of self/sign(other) (divide by zero does not change the sign)
#[inline]
pub const fn div_sign<const N1:usize,const F1:usize>(self,other:Fixed<N1,F1>)->Self{
if other.is_negative(){
Self::from_bits(self.bits.neg())
}else{
self
}
}
} }
impl<const F:usize> Fixed<1,F>{ impl<const F:usize> Fixed<1,F>{
/// My old code called this function everywhere so let's provide it /// My old code called this function everywhere so let's provide it
@@ -99,28 +129,6 @@ impl_from!(
i8,i16,i32,i64,i128,isize i8,i16,i32,i64,i128,isize
); );
impl<const N:usize,const F:usize,T> PartialEq<T> for Fixed<N,F>
where
T:Copy,
BInt::<N>:From<T>,
{
#[inline]
fn eq(&self,&other:&T)->bool{
self.bits.eq(&other.into())
}
}
impl<const N:usize,const F:usize,T> PartialOrd<T> for Fixed<N,F>
where
T:Copy,
BInt::<N>:From<T>,
{
#[inline]
fn partial_cmp(&self,&other:&T)->Option<std::cmp::Ordering>{
self.bits.partial_cmp(&other.into())
}
}
impl<const N:usize,const F:usize> std::ops::Neg for Fixed<N,F>{ impl<const N:usize,const F:usize> std::ops::Neg for Fixed<N,F>{
type Output=Self; type Output=Self;
#[inline] #[inline]
@@ -286,6 +294,23 @@ macro_rules! impl_from_float {
impl_from_float!(integer_decode_f32,f32,24); impl_from_float!(integer_decode_f32,f32,24);
impl_from_float!(integer_decode_f64,f64,53); impl_from_float!(integer_decode_f64,f64,53);
impl<const N:usize,const F:usize> core::fmt::Debug for Fixed<N,F>{
#[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
let integral=self.as_bits().unsigned_abs()>>F;
let fractional=self.as_bits().unsigned_abs()&((bnum::BUint::<N>::ONE<<F)-bnum::BUint::<N>::ONE);
let leading_zeroes=(fractional.leading_zeros() as usize).saturating_sub(N*BNUM_DIGIT_WIDTH-F)>>2;
if self.is_negative(){
core::write!(f,"-")?;
}
if fractional.is_zero(){
core::write!(f,"{integral:x}.{}","0".repeat(leading_zeroes))
}else{
core::write!(f,"{integral:x}.{}{fractional:x}","0".repeat(leading_zeroes))
}
}
}
impl<const N:usize,const F:usize> core::fmt::Display for Fixed<N,F>{ impl<const N:usize,const F:usize> core::fmt::Display for Fixed<N,F>{
#[inline] #[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{ fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
@@ -309,16 +334,6 @@ macro_rules! impl_additive_operator {
self.$method(other) self.$method(other)
} }
} }
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
where
BInt::<N>:From<U>,
{
type Output = $output;
#[inline]
fn $method(self, other: U) -> Self::Output {
Self::from_bits(self.bits.$method(BInt::<N>::from(other).shl(F as u32)))
}
}
}; };
} }
macro_rules! impl_additive_assign_operator { macro_rules! impl_additive_assign_operator {
@@ -329,15 +344,6 @@ macro_rules! impl_additive_assign_operator {
self.bits.$method(other.bits); self.bits.$method(other.bits);
} }
} }
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
where
BInt::<N>:From<U>,
{
#[inline]
fn $method(&mut self, other: U) {
self.bits.$method(BInt::<N>::from(other).shl(F as u32));
}
}
}; };
} }
@@ -360,7 +366,7 @@ impl_additive_operator!( Fixed, BitXor, bitxor, Self );
// non-wide operators. The result is the same width as the inputs. // non-wide operators. The result is the same width as the inputs.
// This macro is not used in the default configuration. // This macro is not used in the default configuration.
#[allow(unused_macros)] #[expect(unused_macros)]
macro_rules! impl_multiplicative_operator_not_const_generic { macro_rules! impl_multiplicative_operator_not_const_generic {
( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => { ( ($struct: ident, $trait: ident, $method: ident, $output: ty ), $width:expr ) => {
impl<const F:usize> core::ops::$trait for $struct<$width,F>{ impl<const F:usize> core::ops::$trait for $struct<$width,F>{
@@ -539,7 +545,7 @@ impl_shift_operator!( Fixed, Shr, shr, Self );
// wide operators. The result width is the sum of the input widths, i.e. none of the multiplication // wide operators. The result width is the sum of the input widths, i.e. none of the multiplication
#[allow(unused_macros)] #[expect(unused_macros)]
macro_rules! impl_wide_operators{ macro_rules! impl_wide_operators{
($lhs:expr,$rhs:expr)=>{ ($lhs:expr,$rhs:expr)=>{
impl core::ops::Mul<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{ impl core::ops::Mul<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{

View File

@@ -229,3 +229,16 @@ fn test_zeroes_deferred_division(){
]) ])
); );
} }
#[test]
fn test_debug(){
assert_eq!(format!("{:?}",I32F32::EPSILON),"0.00000001");
assert_eq!(format!("{:?}",I32F32::ONE),"1.00000000");
assert_eq!(format!("{:?}",I32F32::TWO),"2.00000000");
assert_eq!(format!("{:?}",I32F32::MAX),"7fffffff.ffffffff");
assert_eq!(format!("{:?}",I32F32::try_from(core::f64::consts::PI).unwrap()),"3.243f6a88");
assert_eq!(format!("{:?}",I32F32::NEG_EPSILON),"-0.00000001");
assert_eq!(format!("{:?}",I32F32::NEG_ONE),"-1.00000000");
assert_eq!(format!("{:?}",I32F32::NEG_TWO),"-2.00000000");
assert_eq!(format!("{:?}",I32F32::MIN),"-80000000.00000000");
}

View File

@@ -20,3 +20,6 @@ paste = { version = "1.0.15", optional = true }
[dev-dependencies] [dev-dependencies]
fixed_wide = { path = "../fixed_wide", registry = "strafesnet", features = ["wide-mul"] } fixed_wide = { path = "../fixed_wide", registry = "strafesnet", features = ["wide-mul"] }
[lints]
workspace = true

View File

@@ -205,7 +205,8 @@ macro_rules! impl_matrix_named_fields_shape {
#[inline] #[inline]
fn deref(&self)->&Self::Target{ fn deref(&self)->&Self::Target{
// This cast is valid because Matrix has #[repr(transparent)] // This cast is valid because Matrix has #[repr(transparent)]
let ptr=&self.array as *const [[T;$size_inner];$size_outer] as *const Self::Target; let ptr:*const [[T;$size_inner];$size_outer]=&self.array;
let ptr=ptr as *const Self::Target;
// SAFETY: this pointer is non-null because it comes from a reference // SAFETY: this pointer is non-null because it comes from a reference
unsafe{&*ptr} unsafe{&*ptr}
} }
@@ -214,7 +215,8 @@ macro_rules! impl_matrix_named_fields_shape {
#[inline] #[inline]
fn deref_mut(&mut self)->&mut Self::Target{ fn deref_mut(&mut self)->&mut Self::Target{
// This cast is valid because Matrix has #[repr(transparent)] // This cast is valid because Matrix has #[repr(transparent)]
let ptr=&mut self.array as *mut [[T;$size_inner];$size_outer] as *mut Self::Target; let ptr:*mut [[T;$size_inner];$size_outer]=&mut self.array;
let ptr=ptr as *mut Self::Target;
// SAFETY: this pointer is non-null because it comes from a reference // SAFETY: this pointer is non-null because it comes from a reference
unsafe{&mut*ptr} unsafe{&mut*ptr}
} }

View File

@@ -331,7 +331,8 @@ macro_rules! impl_vector_named_fields {
#[inline] #[inline]
fn deref(&self)->&Self::Target{ fn deref(&self)->&Self::Target{
// This cast is valid because Vector has #[repr(transparent)] // This cast is valid because Vector has #[repr(transparent)]
let ptr=&self.array as *const [T;$size] as *const Self::Target; let ptr:*const [T;$size]=&self.array;
let ptr=ptr as *const Self::Target;
// SAFETY: this pointer is non-null because it comes from a reference // SAFETY: this pointer is non-null because it comes from a reference
unsafe{&*ptr} unsafe{&*ptr}
} }
@@ -340,7 +341,8 @@ macro_rules! impl_vector_named_fields {
#[inline] #[inline]
fn deref_mut(&mut self)->&mut Self::Target{ fn deref_mut(&mut self)->&mut Self::Target{
// This cast is valid because Vector has #[repr(transparent)] // This cast is valid because Vector has #[repr(transparent)]
let ptr=&mut self.array as *mut [T;$size] as *mut Self::Target; let ptr:*mut [T;$size]=&mut self.array;
let ptr=ptr as *mut Self::Target;
// SAFETY: this pointer is non-null because it comes from a reference // SAFETY: this pointer is non-null because it comes from a reference
unsafe{&mut*ptr} unsafe{&mut*ptr}
} }

View File

@@ -8,3 +8,6 @@ description = "Ratio operations using trait bounds for avoiding division like th
authors = ["Rhys Lloyd <krakow20@gmail.com>"] authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[dependencies] [dependencies]
[lints]
workspace = true

View File

@@ -12,14 +12,17 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[dependencies] [dependencies]
bytemuck = "1.14.3" bytemuck = "1.14.3"
glam = "0.30.0" glam = "0.30.0"
lazy-regex = "3.1.0" regex = { version = "1.11.3", default-features = false, features = ["unicode-perl"] }
rbx_binary = { version = "1.0.1-sn5", registry = "strafesnet" }
rbx_dom_weak = { version = "3.0.1-sn5", registry = "strafesnet" }
rbx_mesh = "0.5.0" rbx_mesh = "0.5.0"
rbx_reflection = "5.0.0"
rbx_reflection_database = "1.0.0"
rbx_xml = { version = "1.0.1-sn5", registry = "strafesnet" }
rbxassetid = { version = "0.1.0", path = "../rbxassetid", registry = "strafesnet" } rbxassetid = { version = "0.1.0", path = "../rbxassetid", registry = "strafesnet" }
roblox_emulator = { version = "0.5.1", path = "../roblox_emulator", default-features = false, registry = "strafesnet" } roblox_emulator = { version = "0.5.1", path = "../roblox_emulator", default-features = false, registry = "strafesnet" }
strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.1", path = "../deferred_loader", registry = "strafesnet" } strafesnet_deferred_loader = { version = "0.5.1", path = "../deferred_loader", registry = "strafesnet" }
rbx_binary = "2.0.1"
rbx_dom_weak = "4.1.0"
rbx_reflection = "6.1.0"
rbx_reflection_database = "2.0.2"
rbx_xml = "2.0.1"
[lints]
workspace = true

View File

@@ -50,7 +50,7 @@ impl Loader for TextureLoader{
type Error=TextureError; type Error=TextureError;
type Index<'a>=&'a str; type Index<'a>=&'a str;
type Resource=Texture; type Resource=Texture;
fn load<'a>(&mut self,index:Self::Index<'a>)->Result<Self::Resource,Self::Error>{ fn load(&mut self,index:Self::Index<'_>)->Result<Self::Resource,Self::Error>{
let RobloxAssetId(asset_id)=index.parse()?; let RobloxAssetId(asset_id)=index.parse()?;
let file_name=format!("textures/{}.dds",asset_id); let file_name=format!("textures/{}.dds",asset_id);
let data=read_entire_file(file_name)?; let data=read_entire_file(file_name)?;
@@ -157,7 +157,7 @@ impl Loader for MeshLoader{
type Error=MeshError; type Error=MeshError;
type Index<'a>=MeshIndex<'a>; type Index<'a>=MeshIndex<'a>;
type Resource=MeshWithSize; type Resource=MeshWithSize;
fn load<'a>(&mut self,index:Self::Index<'a>)->Result<Self::Resource,Self::Error>{ fn load(&mut self,index:Self::Index<'_>)->Result<Self::Resource,Self::Error>{
let mesh=match index.mesh_type{ let mesh=match index.mesh_type{
MeshType::FileMesh=>{ MeshType::FileMesh=>{
let RobloxAssetId(asset_id)=index.content.parse()?; let RobloxAssetId(asset_id)=index.content.parse()?;

View File

@@ -18,6 +18,15 @@ fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s) rbx_dom_weak::ustr(s)
} }
macro_rules! lazy_regex{
($r:literal)=>{{
use regex::Regex;
use std::sync::LazyLock;
static RE:LazyLock<Regex>=LazyLock::new(||Regex::new($r).unwrap());
&RE
}};
}
fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Result<Planar64Affine3,Planar64TryFromFloatError>{ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Result<Planar64Affine3,Planar64TryFromFloatError>{
Ok(Planar64Affine3::new( Ok(Planar64Affine3::new(
Planar64Mat3::from_cols([ Planar64Mat3::from_cols([
@@ -118,7 +127,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
); );
}, },
other=>{ other=>{
let regman=lazy_regex::regex!(r"^(BonusStart|WormholeOut)(\d+)$"); let regman=lazy_regex!(r"^(BonusStart|WormholeOut)(\d+)$");
if let Some(captures)=regman.captures(other){ if let Some(captures)=regman.captures(other){
match &captures[1]{ match &captures[1]{
"BonusStart"=>{ "BonusStart"=>{
@@ -144,7 +153,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
}, },
_=>(), _=>(),
} }
}else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$") }else if let Some(captures)=lazy_regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
.captures(other){ .captures(other){
force_intersecting=true; force_intersecting=true;
let stage_id=StageId::new(ParseIntContext::parse(&captures[3]).map_err(GetAttributesError::StageIdParseInt)?); let stage_id=StageId::new(ParseIntContext::parse(&captures[3]).map_err(GetAttributesError::StageIdParseInt)?);
@@ -185,7 +194,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
stage_element, stage_element,
), ),
); );
}else if let Some(captures)=lazy_regex::regex!(r"^(Jump|WormholeIn)(\d+)$") }else if let Some(captures)=lazy_regex!(r"^(Jump|WormholeIn)(\d+)$")
.captures(other){ .captures(other){
match &captures[1]{ match &captures[1]{
"Jump"=>modes_builder.push_mode_update( "Jump"=>modes_builder.push_mode_update(
@@ -210,7 +219,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
}, },
_=>unreachable!("regex2[1] messed up bad"), _=>unreachable!("regex2[1] messed up bad"),
} }
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$") }else if let Some(captures)=lazy_regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
.captures(other){ .captures(other){
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
@@ -242,7 +251,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
} }
} }
//need some way to skip this //need some way to skip this
if allow_booster&&velocity!=vec3::ZERO{ if allow_booster&&velocity!=vec3::zero(){
general.booster=Some(attr::Booster::Velocity(velocity)); general.booster=Some(attr::Booster::Velocity(velocity));
} }
Ok(match force_can_collide{ Ok(match force_can_collide{
@@ -335,12 +344,12 @@ pub struct RobloxFaceTextureDescription{
pub color:glam::Vec4, pub color:glam::Vec4,
pub transform:RobloxTextureTransform, pub transform:RobloxTextureTransform,
} }
impl core::cmp::PartialEq for RobloxFaceTextureDescription{ impl PartialEq for RobloxFaceTextureDescription{
fn eq(&self,other:&Self)->bool{ fn eq(&self,other:&Self)->bool{
self.to_bits().eq(&other.to_bits()) self.to_bits().eq(&other.to_bits())
} }
} }
impl core::cmp::Eq for RobloxFaceTextureDescription{} impl Eq for RobloxFaceTextureDescription{}
impl core::hash::Hash for RobloxFaceTextureDescription{ impl core::hash::Hash for RobloxFaceTextureDescription{
fn hash<H:core::hash::Hasher>(&self,state:&mut H){ fn hash<H:core::hash::Hasher>(&self,state:&mut H){
self.to_bits().hash(state); self.to_bits().hash(state);
@@ -550,7 +559,7 @@ pub fn convert<'a>(
//just going to leave it like this for now instead of reworking the data structures for this whole thing //just going to leave it like this for now instead of reworking the data structures for this whole thing
let textureless_render_group=render_config_deferred_loader.acquire_render_config_id(None); let textureless_render_group=render_config_deferred_loader.acquire_render_config_id(None);
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let basepart=&db.classes["BasePart"]; let basepart=&db.classes["BasePart"];
let baseparts=dom.descendants().filter(|&instance| let baseparts=dom.descendants().filter(|&instance|
db.classes.get(instance.class.as_str()).is_some_and(|class| db.classes.get(instance.class.as_str()).is_some_and(|class|
@@ -766,10 +775,10 @@ struct MeshIdWithSize{
mesh:model::MeshId, mesh:model::MeshId,
size:Planar64Vec3, size:Planar64Vec3,
} }
fn acquire_mesh_id_from_render_config_id<'a>( fn acquire_mesh_id_from_render_config_id(
primitive_meshes:&mut Vec<model::Mesh>, primitive_meshes:&mut Vec<model::Mesh>,
mesh_id_from_render_config_id:&mut HashMap<model::MeshId,HashMap<RenderConfigId,model::MeshId>>, mesh_id_from_render_config_id:&mut HashMap<model::MeshId,HashMap<RenderConfigId,model::MeshId>>,
loaded_meshes:&'a HashMap<model::MeshId,MeshWithSize>, loaded_meshes:&HashMap<model::MeshId,MeshWithSize>,
old_mesh_id:model::MeshId, old_mesh_id:model::MeshId,
render:RenderConfigId, render:RenderConfigId,
)->Option<MeshIdWithSize>{ )->Option<MeshIdWithSize>{
@@ -789,10 +798,10 @@ fn acquire_mesh_id_from_render_config_id<'a>(
size, size,
}) })
} }
fn acquire_union_id_from_render_config_id<'a>( fn acquire_union_id_from_render_config_id(
primitive_meshes:&mut Vec<model::Mesh>, primitive_meshes:&mut Vec<model::Mesh>,
union_id_from_render_config_id:&mut HashMap<model::MeshId,HashMap<RobloxPartDescription,model::MeshId>>, union_id_from_render_config_id:&mut HashMap<model::MeshId,HashMap<RobloxPartDescription,model::MeshId>>,
loaded_meshes:&'a HashMap<model::MeshId,MeshWithSize>, loaded_meshes:&HashMap<model::MeshId,MeshWithSize>,
old_union_id:model::MeshId, old_union_id:model::MeshId,
part_texture_description:RobloxPartDescription, part_texture_description:RobloxPartDescription,
)->Option<MeshIdWithSize>{ )->Option<MeshIdWithSize>{

View File

@@ -250,7 +250,7 @@ pub fn convert(
// generate a unit cube as default physics // generate a unit cube as default physics
let pos_list=CUBE_DEFAULT_VERTICES.map(|pos|mb.acquire_pos_id(pos>>1)); let pos_list=CUBE_DEFAULT_VERTICES.map(|pos|mb.acquire_pos_id(pos>>1));
let tex=mb.acquire_tex_id(glam::Vec2::ZERO); let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
let normal=mb.acquire_normal_id(vec3::ZERO); let normal=mb.acquire_normal_id(vec3::zero());
let color=mb.acquire_color_id(glam::Vec4::ONE); let color=mb.acquire_color_id(glam::Vec4::ONE);
let polygon_group=PolygonGroup::PolygonList(PolygonList::new(CUBE_DEFAULT_POLYS.map(|poly|poly.map(|[pos_id,_]| let polygon_group=PolygonGroup::PolygonList(PolygonList::new(CUBE_DEFAULT_POLYS.map(|poly|poly.map(|[pos_id,_]|
mb.acquire_vertex_id(IndexedVertex{pos:pos_list[pos_id as usize],tex,normal,color}) mb.acquire_vertex_id(IndexedVertex{pos:pos_list[pos_id as usize],tex,normal,color})

View File

@@ -9,3 +9,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[dependencies] [dependencies]
url = "2.5.4" url = "2.5.4"
[lints]
workspace = true

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "roblox_emulator" name = "roblox_emulator"
version = "0.5.1" version = "0.5.2"
edition = "2024" edition = "2024"
repository = "https://git.itzana.me/StrafesNET/strafe-project" repository = "https://git.itzana.me/StrafesNET/strafe-project"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@@ -15,7 +15,10 @@ run-service=[]
glam = "0.30.0" glam = "0.30.0"
mlua = { version = "0.11.3", features = ["luau"] } mlua = { version = "0.11.3", features = ["luau"] }
phf = { version = "0.13.1", features = ["macros"] } phf = { version = "0.13.1", features = ["macros"] }
rbx_dom_weak = { version = "3.0.1-sn5", registry = "strafesnet" } rbx_dom_weak = "4.1.0"
rbx_reflection = "5.0.0" rbx_reflection = "6.1.0"
rbx_reflection_database = "1.0.0" rbx_reflection_database = "2.0.2"
rbx_types = "2.0.0" rbx_types = "3.1.0"
[lints]
workspace = true

View File

@@ -52,7 +52,7 @@ impl Context{
} }
/// Creates an iterator over all items of a particular class. /// Creates an iterator over all items of a particular class.
pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{ pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let Some(superclass)=db.classes.get(superclass)else{ let Some(superclass)=db.classes.get(superclass)else{
panic!("Invalid class"); panic!("Invalid class");
}; };

View File

@@ -8,7 +8,7 @@ impl<'a> EnumItem<'a>{
Self{name:Some(name.as_ref()),value} Self{name:Some(name.as_ref()),value}
} }
} }
impl<'a> From<rbx_types::Enum> for EnumItem<'a>{ impl From<rbx_types::Enum> for EnumItem<'_>{
fn from(e:rbx_types::Enum)->Self{ fn from(e:rbx_types::Enum)->Self{
EnumItem{ EnumItem{
name:None, name:None,
@@ -37,7 +37,7 @@ impl PartialEq for EnumItem<'_>{
pub struct Enums; pub struct Enums;
impl Enums{ impl Enums{
pub fn get(&self,index:&str)->Option<EnumItems<'static>>{ pub fn get(&self,index:&str)->Option<EnumItems<'static>>{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
db.enums.get(index).map(|ed|EnumItems{ed}) db.enums.get(index).map(|ed|EnumItems{ed})
} }
} }

View File

@@ -5,7 +5,6 @@ use rbx_types::Ref;
use rbx_dom_weak::{Ustr,InstanceBuilder,WeakDom}; use rbx_dom_weak::{Ustr,InstanceBuilder,WeakDom};
use crate::util::static_ustr; use crate::util::static_ustr;
use crate::runner::vector3::Vector3;
use crate::runner::number::Number; use crate::runner::number::Number;
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
@@ -38,13 +37,13 @@ pub fn dom_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>
} }
pub fn class_is_a(class:&str,superclass:&str)->bool{ pub fn class_is_a(class:&str,superclass:&str)->bool{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let (Some(class),Some(superclass))=(db.classes.get(class),db.classes.get(superclass))else{ let (Some(class),Some(superclass))=(db.classes.get(class),db.classes.get(superclass))else{
return false; return false;
}; };
db.has_superclass(class,superclass) db.has_superclass(class,superclass)
} }
fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{ fn get_full_name(dom:&WeakDom,instance:&rbx_dom_weak::Instance)->String{
let mut full_name=instance.name.clone(); let mut full_name=instance.name.clone();
let mut pref=instance.parent(); let mut pref=instance.parent();
while let Some(parent)=dom.get_by_ref(pref){ while let Some(parent)=dom.get_by_ref(pref){
@@ -66,29 +65,29 @@ pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),m
}) })
} }
pub fn find_first_child<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{ pub fn find_first_child<'a>(dom:&'a WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name) instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name)
} }
pub fn find_first_descendant<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{ pub fn find_first_descendant<'a>(dom:&'a WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
dom.descendants_of(instance.referent()).find(|&inst|inst.name==name) dom.descendants_of(instance.referent()).find(|&inst|inst.name==name)
} }
pub fn find_first_child_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{ pub fn find_first_child_of_class<'a>(dom:&'a WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class) instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class)
} }
pub fn find_first_descendant_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{ pub fn find_first_descendant_of_class<'a>(dom:&'a WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class) dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
} }
pub fn find_first_child_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{ pub fn find_first_child_which_is_a<'a>(dom:&'a WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let superclass_descriptor=db.classes.get(superclass)?; let superclass_descriptor=db.classes.get(superclass)?;
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|{ instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|{
db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor)) db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
}) })
} }
pub fn find_first_descendant_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{ pub fn find_first_descendant_which_is_a<'a>(dom:&'a WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let superclass_descriptor=db.classes.get(superclass)?; let superclass_descriptor=db.classes.get(superclass)?;
dom.descendants_of(instance.referent()).find(|inst|{ dom.descendants_of(instance.referent()).find(|inst|{
db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor)) db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
@@ -283,7 +282,7 @@ impl mlua::UserData for Instance{
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?;
//println!("__index t={} i={index:?}",instance.name); //println!("__index t={} i={index:?}",instance.name);
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?; let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
// Find existing property // Find existing property
// Interestingly, ustr can know ahead of time if // Interestingly, ustr can know ahead of time if
@@ -345,7 +344,7 @@ impl mlua::UserData for Instance{
let index_str=&*index.to_str()?; let index_str=&*index.to_str()?;
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get_mut(dom)?; let instance=this.get_mut(dom)?;
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get().unwrap();
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?; let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
let property=db.superclasses_iter(class).find_map(|cls| let property=db.superclasses_iter(class).find_map(|cls|
cls.properties.get(index_str) cls.properties.get(index_str)
@@ -488,8 +487,8 @@ static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
"GetService"=>GET_SERVICE, "GetService"=>GET_SERVICE,
}, },
"Terrain"=>phf::phf_map!{ "Terrain"=>phf::phf_map!{
"FillBall"=>cf!(|_lua,_,_:(Vector3,Number,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())), "FillBall"=>cf!(|_lua,_,_:(crate::runner::vector3::Vector3,Number,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())),
"FillBlock"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,Vector3,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())), "FillBlock"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,crate::runner::vector3::Vector3,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())),
"FillCylinder"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,Number,Number,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())), "FillCylinder"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,Number,Number,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())),
"SetMaterialColor"=>cf!(|_lua,_,_:(crate::runner::r#enum::CoerceEnum,crate::runner::color3::Color3)|mlua::Result::Ok(())), "SetMaterialColor"=>cf!(|_lua,_,_:(crate::runner::r#enum::CoerceEnum,crate::runner::color3::Color3)|mlua::Result::Ok(())),
}, },

View File

@@ -48,6 +48,11 @@ fn init(lua:&mlua::Lua)->mlua::Result<()>{
Ok(()) Ok(())
} }
unsafe fn extend_lifetime_mut<'a,T>(src:&mut T)->&'a mut T{
let ptr:*mut T=src;
unsafe{&mut*ptr}
}
impl Runner{ impl Runner{
pub fn new()->Result<Self,Error>{ pub fn new()->Result<Self,Error>{
let runner=Self{ let runner=Self{
@@ -65,8 +70,7 @@ impl Runner{
// SAFETY: This is not a &'static mut WeakDom, // SAFETY: This is not a &'static mut WeakDom,
// but as long as Runnable<'a> holds the lifetime of &'a mut Context // but as long as Runnable<'a> holds the lifetime of &'a mut Context
// it is a valid unique reference. // it is a valid unique reference.
let ptr=&mut context.dom as *mut rbx_dom_weak::WeakDom; self.lua.set_app_data::<crate::context::LuaAppData>(unsafe{extend_lifetime_mut(&mut context.dom)});
self.lua.set_app_data::<crate::context::LuaAppData>(unsafe{&mut*ptr});
#[cfg(feature="run-service")] #[cfg(feature="run-service")]
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default()); self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
Ok(Runnable{ Ok(Runnable{

View File

@@ -117,7 +117,7 @@ impl mlua::FromLua for ScriptSignal{
} }
impl mlua::UserData for ScriptConnection{ impl mlua::UserData for ScriptConnection{
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){ fn add_fields<F:UserDataFields<Self>>(fields:&mut F){
fields.add_field_method_get("Connected",|_,this|{ fields.add_field_method_get("Connected",|_,this|{
Ok(this.position().is_some()) Ok(this.position().is_some())
}); });

View File

@@ -46,8 +46,8 @@ impl Scheduler{
} }
} }
pub fn scheduler_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut crate::scheduler::Scheduler)->mlua::Result<T>)->mlua::Result<T>{ pub fn scheduler_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut Scheduler)->mlua::Result<T>)->mlua::Result<T>{
let mut scheduler=lua.app_data_mut::<crate::scheduler::Scheduler>().ok_or_else(||mlua::Error::runtime("Scheduler missing"))?; let mut scheduler=lua.app_data_mut::<Scheduler>().ok_or_else(||mlua::Error::runtime("Scheduler missing"))?;
f(&mut *scheduler) f(&mut *scheduler)
} }

View File

@@ -9,3 +9,6 @@ edition = "2024"
binrw = "0.15.0" binrw = "0.15.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }
strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" }
[lints]
workspace = true

View File

@@ -6,7 +6,7 @@ use strafesnet_common::physics::Time;
const VERSION:u32=0; const VERSION:u32=0;
type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,strafesnet_common::physics::Time>; type TimedPhysicsInstruction=strafesnet_common::instruction::TimedInstruction<strafesnet_common::physics::Instruction,Time>;
#[derive(Debug)] #[derive(Debug)]
pub enum Error{ pub enum Error{
@@ -274,7 +274,7 @@ pub fn write_bot<W:BinWriterExt>(mut writer:W,physics_version:u32,instructions:i
//probe header length //probe header length
let mut bot_header_data=Vec::new(); let mut bot_header_data=Vec::new();
binrw::BinWrite::write_le(&header,&mut std::io::Cursor::new(&mut bot_header_data)).map_err(Error::InvalidData)?; header.write_le(&mut std::io::Cursor::new(&mut bot_header_data)).map_err(Error::InvalidData)?;
// the first block location is the map header // the first block location is the map header
block_location.push(offset); block_location.push(offset);

View File

@@ -97,8 +97,8 @@ enum ResourceType{
} }
struct ResourceMap<T>{ struct ResourceMap<T>{
meshes:HashMap<strafesnet_common::model::MeshId,T>, meshes:HashMap<model::MeshId,T>,
textures:HashMap<strafesnet_common::model::TextureId,T>, textures:HashMap<model::TextureId,T>,
} }
impl<T> Default for ResourceMap<T>{ impl<T> Default for ResourceMap<T>{
fn default()->Self{ fn default()->Self{
@@ -185,7 +185,7 @@ pub struct StreamableMap<R:BinReaderExt>{
//this is every possible attribute... need some sort of streaming system //this is every possible attribute... need some sort of streaming system
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>, attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
//this is every possible render configuration... shaders and such... need streaming //this is every possible render configuration... shaders and such... need streaming
render_configs:Vec<strafesnet_common::model::RenderConfig>, render_configs:Vec<model::RenderConfig>,
//this makes sense to keep in memory for streaming, a map of which blocks occupy what space //this makes sense to keep in memory for streaming, a map of which blocks occupy what space
bvh:BvhNode<BlockId>, bvh:BvhNode<BlockId>,
//something something resources hashmaps //something something resources hashmaps
@@ -223,7 +223,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
} }
Ok(Self{ Ok(Self{
file, file,
modes:strafesnet_common::gameplay_modes::NormalizedModes::new(modes), modes:gameplay_modes::NormalizedModes::new(modes),
attributes, attributes,
render_configs, render_configs,
bvh:strafesnet_common::bvh::generate_bvh(bvh), bvh:strafesnet_common::bvh::generate_bvh(bvh),
@@ -366,12 +366,12 @@ fn collect_spacial_blocks(
block_location.push(sequential_block_data.position()); block_location.push(sequential_block_data.position());
}else{ }else{
match bvh_node.into_content(){ match bvh_node.into_content(){
strafesnet_common::bvh::RecursiveContent::Branch(bvh_node_list)=>{ RecursiveContent::Branch(bvh_node_list)=>{
for bvh_node in bvh_node_list{ for bvh_node in bvh_node_list{
collect_spacial_blocks(block_location,block_headers,sequential_block_data,bvh_node)?; collect_spacial_blocks(block_location,block_headers,sequential_block_data,bvh_node)?;
} }
}, },
strafesnet_common::bvh::RecursiveContent::Leaf(_)=>panic!(),//bvh branches are 20 leaves minimum RecursiveContent::Leaf(_)=>panic!(),//bvh branches are 20 leaves minimum
} }
} }
Ok(()) Ok(())
@@ -384,13 +384,13 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
let boxen=map.models.into_iter().enumerate().map(|(model_id,model)|{ let boxen=map.models.into_iter().enumerate().map(|(model_id,model)|{
//grow your own aabb //grow your own aabb
let mesh=map.meshes.get(model.mesh.get() as usize).ok_or(Error::InvalidMeshId(model.mesh))?; let mesh=map.meshes.get(model.mesh.get() as usize).ok_or(Error::InvalidMeshId(model.mesh))?;
let mut aabb=strafesnet_common::aabb::Aabb::default(); let mut aabb=Aabb::default();
for &pos in &mesh.unique_pos{ for &pos in &mesh.unique_pos{
aabb.grow(model.transform.transform_point3(pos).narrow_1().unwrap()); aabb.grow(model.transform.transform_point3(pos).narrow_1().unwrap());
} }
Ok(((model::ModelId::new(model_id as u32),model.into()),aabb)) Ok(((model::ModelId::new(model_id as u32),model.into()),aabb))
}).collect::<Result<Vec<_>,_>>()?; }).collect::<Result<Vec<_>,_>>()?;
let bvh=weigh_contents(strafesnet_common::bvh::generate_bvh(boxen),&|_|std::mem::size_of::<newtypes::model::Model>()); let bvh=weigh_contents(strafesnet_common::bvh::generate_bvh(boxen),&|_|size_of::<newtypes::model::Model>());
//build blocks //build blocks
//block location is initialized with two values //block location is initialized with two values
//the first value represents the location of the first byte after the file header //the first value represents the location of the first byte after the file header

View File

@@ -20,7 +20,7 @@ impl TryInto<TimedPhysicsInstruction> for TimedInstruction{
} }
} }
impl TryFrom<TimedPhysicsInstruction> for TimedInstruction{ impl TryFrom<TimedPhysicsInstruction> for TimedInstruction{
type Error=super::physics::InstructionConvert; type Error=InstructionConvert;
fn try_from(value:TimedPhysicsInstruction)->Result<Self,Self::Error>{ fn try_from(value:TimedPhysicsInstruction)->Result<Self,Self::Error>{
Ok(Self{ Ok(Self{
time:value.time.get(), time:value.time.get(),

View File

@@ -12,12 +12,11 @@ flate2 = "1.0.27"
futures = "0.3.31" futures = "0.3.31"
image = "0.25.2" image = "0.25.2"
image_dds = "0.7.1" image_dds = "0.7.1"
lazy-regex = "3.1.0"
rbx_asset = { version = "0.5.0", registry = "strafesnet" } rbx_asset = { version = "0.5.0", registry = "strafesnet" }
rbx_binary = { version = "1.0.1-sn5", registry = "strafesnet" } rbx_binary = "2.0.1"
rbx_dom_weak = { version = "3.0.1-sn5", registry = "strafesnet" } rbx_dom_weak = "4.1.0"
rbx_reflection_database = "1.0.0" rbx_reflection_database = "2.0.2"
rbx_xml = { version = "1.0.1-sn5", registry = "strafesnet" } rbx_xml = "2.0.1"
rbxassetid = { version = "0.1.0", registry = "strafesnet" } rbxassetid = { version = "0.1.0", registry = "strafesnet" }
strafesnet_bsp_loader = { version = "0.3.1", path = "../lib/bsp_loader", registry = "strafesnet" } strafesnet_bsp_loader = { version = "0.3.1", path = "../lib/bsp_loader", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.1", path = "../lib/deferred_loader", registry = "strafesnet" } strafesnet_deferred_loader = { version = "0.5.1", path = "../lib/deferred_loader", registry = "strafesnet" }
@@ -36,3 +35,6 @@ vtf = "0.3.0"
#lto = true #lto = true
#strip = true #strip = true
#codegen-units = 1 #codegen-units = 1
[lints]
workspace = true

View File

@@ -465,7 +465,7 @@ fn convert_to_snf(path:&Path,output_folder:PathBuf)->Result<Errors,ConvertError>
}) })
} }
async fn roblox_to_snf(paths:Vec<std::path::PathBuf>,output_folder:PathBuf)->AResult<()>{ async fn roblox_to_snf(paths:Vec<PathBuf>,output_folder:PathBuf)->AResult<()>{
let start=std::time::Instant::now(); let start=std::time::Instant::now();
let thread_limit=std::thread::available_parallelism()?.get(); let thread_limit=std::thread::available_parallelism()?.get();

View File

@@ -484,7 +484,7 @@ async fn convert_to_snf(path:&Path,vpk_list:&[strafesnet_bsp_loader::Vpk],output
Ok(()) Ok(())
} }
async fn source_to_snf(paths:Vec<std::path::PathBuf>,output_folder:PathBuf,vpk_paths:Vec<PathBuf>)->AResult<()>{ async fn source_to_snf(paths:Vec<PathBuf>,output_folder:PathBuf,vpk_paths:Vec<PathBuf>)->AResult<()>{
let start=std::time::Instant::now(); let start=std::time::Instant::now();
let thread_limit=std::thread::available_parallelism()?.get(); let thread_limit=std::thread::available_parallelism()?.get();

View File

@@ -28,9 +28,12 @@ strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet", o
strafesnet_session = { path = "../engine/session", registry = "strafesnet" } strafesnet_session = { path = "../engine/session", registry = "strafesnet" }
strafesnet_settings = { path = "../engine/settings", registry = "strafesnet" } strafesnet_settings = { path = "../engine/settings", registry = "strafesnet" }
strafesnet_snf = { path = "../lib/snf", registry = "strafesnet", optional = true } strafesnet_snf = { path = "../lib/snf", registry = "strafesnet", optional = true }
wgpu = "26.0.1" wgpu = "28.0.0"
winit = "0.30.7" winit = "0.30.7"
[profile.dev] [profile.dev]
strip = false strip = false
opt-level = 3 opt-level = 3
[lints]
workspace = true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,62 +0,0 @@
# Blender MTL File: 'teslacyberv3.0.blend'
# Material Count: 6
newmtl Material
Ns 65.476285
Ka 1.000000 1.000000 1.000000
Kd 0.411568 0.411568 0.411568
Ks 0.614679 0.614679 0.614679
Ke 0.000000 0.000000 0.000000
Ni 36.750000
d 1.000000
illum 3
newmtl Материал
Ns 323.999994
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
newmtl Материал.001
Ns 900.000000
Ka 1.000000 1.000000 1.000000
Kd 0.026240 0.026240 0.026240
Ks 0.000000 0.000000 0.000000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 1
newmtl Материал.002
Ns 0.000000
Ka 1.000000 1.000000 1.000000
Kd 0.031837 0.032429 0.029425
Ks 0.169725 0.169725 0.169725
Ke 0.000000 0.000000 0.000000
Ni 0.000000
d 1.000000
illum 2
newmtl Материал.003
Ns 900.000000
Ka 1.000000 1.000000 1.000000
Kd 0.023585 0.083235 0.095923
Ks 1.000000 1.000000 1.000000
Ke 0.000000 0.000000 0.000000
Ni 45.049999
d 1.000000
illum 3
newmtl Материал.004
Ns 323.999994
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@ impl<'a> SetupContextPartial2<'a>{
let required_features=required_features(); let required_features=required_features();
//no helper function smh gotta write it myself //no helper function smh gotta write it myself
let adapters=self.instance.enumerate_adapters(self.backends); let adapters=pollster::block_on(self.instance.enumerate_adapters(self.backends));
let mut chosen_adapter=None; let mut chosen_adapter=None;
let mut chosen_adapter_score=0; let mut chosen_adapter_score=0;
@@ -119,12 +119,13 @@ impl<'a> SetupContextPartial3<'a>{
let (device, queue)=pollster::block_on(self.adapter let (device, queue)=pollster::block_on(self.adapter
.request_device( .request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor{
label: None, label:None,
required_features: (optional_features & self.adapter.features()) | required_features, required_features:(optional_features&self.adapter.features())|required_features,
required_limits: needed_limits, required_limits:needed_limits,
memory_hints:wgpu::MemoryHints::Performance, memory_hints:wgpu::MemoryHints::Performance,
trace: wgpu::Trace::Off, trace:wgpu::Trace::Off,
experimental_features:wgpu::ExperimentalFeatures::disabled(),
}, },
)) ))
.expect("Unable to find a suitable GPU adapter!"); .expect("Unable to find a suitable GPU adapter!");

View File

@@ -15,6 +15,7 @@ pub enum Instruction{
struct WindowContext<'a>{ struct WindowContext<'a>{
manual_mouse_lock:bool, manual_mouse_lock:bool,
mouse_pos:glam::DVec2, mouse_pos:glam::DVec2,
simulation_paused:bool,
screen_size:glam::UVec2, screen_size:glam::UVec2,
window:&'a winit::window::Window, window:&'a winit::window::Window,
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<PhysicsWorkerInstruction,SessionTime>>, physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<PhysicsWorkerInstruction,SessionTime>>,
@@ -24,6 +25,35 @@ impl WindowContext<'_>{
fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<u32>{ fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<u32>{
winit::dpi::PhysicalPosition::new(self.screen_size.x/2,self.screen_size.y/2) winit::dpi::PhysicalPosition::new(self.screen_size.x/2,self.screen_size.y/2)
} }
fn free_mouse(&mut self){
self.manual_mouse_lock=false;
match self.window.set_cursor_position(self.get_middle_of_screen()){
Ok(())=>(),
Err(e)=>println!("Could not set cursor position: {:?}",e),
}
match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){
Ok(())=>(),
Err(e)=>println!("Could not release cursor: {:?}",e),
}
self.window.set_cursor_visible(true);
}
fn lock_mouse(&mut self){
//if cursor is outside window don't lock but apparently there's no get pos function
//let pos=window.get_cursor_pos();
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Locked){
Ok(())=>(),
Err(_)=>{
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
Ok(())=>(),
Err(e)=>{
self.manual_mouse_lock=true;
println!("Could not confine cursor: {:?}",e)
},
}
}
}
self.window.set_cursor_visible(false);
}
fn window_event(&mut self,time:SessionTime,event:winit::event::WindowEvent){ fn window_event(&mut self,time:SessionTime,event:winit::event::WindowEvent){
match event{ match event{
winit::event::WindowEvent::DroppedFile(path)=>{ winit::event::WindowEvent::DroppedFile(path)=>{
@@ -34,6 +64,10 @@ impl WindowContext<'_>{
} }
}, },
winit::event::WindowEvent::Focused(state)=>{ winit::event::WindowEvent::Focused(state)=>{
// don't unpause if manually paused
if self.simulation_paused{
return;
}
//pause unpause //pause unpause
self.physics_thread.send(TimedInstruction{ self.physics_thread.send(TimedInstruction{
time, time,
@@ -46,35 +80,8 @@ impl WindowContext<'_>{
.. ..
}=>{ }=>{
match (logical_key,state){ match (logical_key,state){
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Pressed)=>{ (winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Pressed)=>self.free_mouse(),
self.manual_mouse_lock=false; (winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Released)=>self.lock_mouse(),
match self.window.set_cursor_position(self.get_middle_of_screen()){
Ok(())=>(),
Err(e)=>println!("Could not set cursor position: {:?}",e),
}
match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){
Ok(())=>(),
Err(e)=>println!("Could not release cursor: {:?}",e),
}
self.window.set_cursor_visible(state.is_pressed());
},
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Released)=>{
//if cursor is outside window don't lock but apparently there's no get pos function
//let pos=window.get_cursor_pos();
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Locked){
Ok(())=>(),
Err(_)=>{
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
Ok(())=>(),
Err(e)=>{
self.manual_mouse_lock=true;
println!("Could not confine cursor: {:?}",e)
},
}
}
}
self.window.set_cursor_visible(state.is_pressed());
},
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11),winit::event::ElementState::Pressed)=>{ (winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11),winit::event::ElementState::Pressed)=>{
if self.window.fullscreen().is_some(){ if self.window.fullscreen().is_some(){
self.window.set_fullscreen(None); self.window.set_fullscreen(None);
@@ -132,7 +139,16 @@ impl WindowContext<'_>{
if let Some(session_instruction)=match keycode{ if let Some(session_instruction)=match keycode{
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>input_ctrl!(SetJump,s), winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>input_ctrl!(SetJump,s),
// TODO: bind system so playback pausing can use spacebar // TODO: bind system so playback pausing can use spacebar
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Enter)=>session_playback!(TogglePaused,s), winit::keyboard::Key::Named(winit::keyboard::NamedKey::Enter)=>if s{
let paused=!self.simulation_paused;
self.simulation_paused=paused;
if paused{
self.free_mouse();
}else{
self.lock_mouse();
}
Some(SessionInstructionSubset::Control(SessionControlInstruction::SetPaused(paused)))
}else{None},
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp)=>session_playback!(IncreaseTimescale,s), winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp)=>session_playback!(IncreaseTimescale,s),
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown)=>session_playback!(DecreaseTimescale,s), winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown)=>session_playback!(DecreaseTimescale,s),
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft)=>session_playback!(SkipBack,s), winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft)=>session_playback!(SkipBack,s),
@@ -188,7 +204,7 @@ impl WindowContext<'_>{
} }
} }
fn device_event(&mut self,time:SessionTime,event: winit::event::DeviceEvent){ fn device_event(&mut self,time:SessionTime,event:winit::event::DeviceEvent){
match event{ match event{
winit::event::DeviceEvent::MouseMotion{ winit::event::DeviceEvent::MouseMotion{
delta, delta,
@@ -241,6 +257,7 @@ pub fn worker<'a>(
let mut window_context=WindowContext{ let mut window_context=WindowContext{
manual_mouse_lock:false, manual_mouse_lock:false,
mouse_pos:glam::DVec2::ZERO, mouse_pos:glam::DVec2::ZERO,
simulation_paused:false,
//make sure to update this!!!!! //make sure to update this!!!!!
screen_size, screen_size,
window, window,