Compare commits

..

3 Commits

Author SHA1 Message Date
e832fccacb wip 2025-05-03 22:12:15 -07:00
8ab910e18f wip: hash_str 2025-05-03 22:12:15 -07:00
b46fb04a5d directly use rbx-dom by path 2025-05-03 22:12:15 -07:00
23 changed files with 365 additions and 501 deletions

72
Cargo.lock generated
View File

@@ -46,6 +46,18 @@ dependencies = [
"zerocopy 0.7.35", "zerocopy 0.7.35",
] ]
[[package]]
name = "ahash_macro"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e73c78bacfd5857793b9b6813ab94605bb1699de9915f3285472f6633748e773"
dependencies = [
"ahash",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@@ -61,6 +73,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]] [[package]]
name = "android-activity" name = "android-activity"
version = "0.6.0" version = "0.6.0"
@@ -1251,12 +1269,27 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "hash_str"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb4f3e9ad2b19e6290ea4c648770a69ac4687c692a2a6175aaef2a398552da4"
dependencies = [
"ahash",
"ahash_macro",
"bumpalo",
"hashbrown",
"serde",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [ dependencies = [
"allocator-api2",
"equivalent",
"foldhash", "foldhash",
] ]
@@ -3098,11 +3131,10 @@ dependencies = [
[[package]] [[package]]
name = "rbx_binary" name = "rbx_binary"
version = "1.1.0-sn4" version = "1.0.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "dc304e66f18316b8ee8729cec639528c9c5182aacbef1f8967619bd8b2363be4"
dependencies = [ dependencies = [
"ahash", "ahash",
"hash_str",
"log", "log",
"lz4", "lz4",
"profiling", "profiling",
@@ -3115,15 +3147,12 @@ dependencies = [
[[package]] [[package]]
name = "rbx_dom_weak" name = "rbx_dom_weak"
version = "3.1.0-sn4" version = "3.0.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "5fcb44ffa83385055a42e5e499be3a4cce04b2ceb9af2b9825f70dcc4d2578ce"
dependencies = [ dependencies = [
"ahash", "ahash",
"mlua", "hash_str",
"rbx_types", "rbx_types",
"serde", "serde",
"ustr",
] ]
[[package]] [[package]]
@@ -3139,9 +3168,8 @@ dependencies = [
[[package]] [[package]]
name = "rbx_reflection" name = "rbx_reflection"
version = "5.0.0" version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e"
dependencies = [ dependencies = [
"hash_str",
"rbx_types", "rbx_types",
"serde", "serde",
"thiserror 1.0.69", "thiserror 1.0.69",
@@ -3149,9 +3177,7 @@ dependencies = [
[[package]] [[package]]
name = "rbx_reflection_database" name = "rbx_reflection_database"
version = "1.0.2+roblox-670" version = "1.0.3+roblox-670"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5349b19e5e94fbcaba7a52175263ab64011e0a13f17ff57729f2f560ccdec615"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"rbx_reflection", "rbx_reflection",
@@ -3162,8 +3188,6 @@ dependencies = [
[[package]] [[package]]
name = "rbx_types" name = "rbx_types"
version = "2.0.0" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78e4fdde46493def107e5f923d82e813dec9b3eef52c2f75fbad3a716023eda2"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"bitflags 1.3.2", "bitflags 1.3.2",
@@ -3176,12 +3200,11 @@ dependencies = [
[[package]] [[package]]
name = "rbx_xml" name = "rbx_xml"
version = "1.1.0-sn4" version = "1.0.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "08750f2f23366cda4a71d05c11c40b55bfeccd4bbc22037264b510c90809f7bd"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.13.1", "base64 0.13.1",
"hash_str",
"log", "log",
"rbx_dom_weak", "rbx_dom_weak",
"rbx_reflection", "rbx_reflection",
@@ -4315,19 +4338,6 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "ustr"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208"
dependencies = [
"ahash",
"byteorder 1.5.0",
"lazy_static",
"parking_lot",
"serde",
]
[[package]] [[package]]
name = "utf16_iter" name = "utf16_iter"
version = "1.0.5" version = "1.0.5"

View File

@@ -1,10 +1,7 @@
use crate::model::{into_giga_time,GigaTime,FEV,MeshQuery,DirectedEdge}; use crate::model::{GigaTime,FEV,MeshQuery,DirectedEdge};
use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3}; use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3};
use crate::physics::{Time,Body}; use crate::physics::{Time,Body};
use core::ops::Bound;
#[derive(Debug)]
enum Transition<M:MeshQuery>{ enum Transition<M:MeshQuery>{
Miss, Miss,
Next(FEV<M>,GigaTime), Next(FEV<M>,GigaTime),
@@ -30,60 +27,17 @@ impl<M:MeshQuery> CrawlResult<M>{
} }
} }
// TODO: move predict_collision_face_out algorithm in here or something
/// check_lower_bound
pub fn low<LhsNum,LhsDen,RhsNum,RhsDen,T>(lower_bound:&Bound<Ratio<LhsNum,LhsDen>>,dt:&Ratio<RhsNum,RhsDen>)->bool
where
RhsNum:Copy,
RhsDen:Copy,
LhsNum:Copy,
LhsDen:Copy,
LhsDen:strafesnet_common::integer::Parity,
RhsDen:strafesnet_common::integer::Parity,
LhsNum:core::ops::Mul<RhsDen,Output=T>,
LhsDen:core::ops::Mul<RhsNum,Output=T>,
T:Ord+Copy,
{
match lower_bound{
Bound::Included(time)=>time.le_ratio(*dt),
Bound::Excluded(time)=>time.lt_ratio(*dt),
Bound::Unbounded=>true,
}
}
/// check_upper_bound
pub fn upp<LhsNum,LhsDen,RhsNum,RhsDen,T>(dt:&Ratio<LhsNum,LhsDen>,upper_bound:&Bound<Ratio<RhsNum,RhsDen>>)->bool
where
RhsNum:Copy,
RhsDen:Copy,
LhsNum:Copy,
LhsDen:Copy,
LhsDen:strafesnet_common::integer::Parity,
RhsDen:strafesnet_common::integer::Parity,
LhsNum:core::ops::Mul<RhsDen,Output=T>,
LhsDen:core::ops::Mul<RhsNum,Output=T>,
T:Ord+Copy,
{
match upper_bound{
Bound::Included(time)=>dt.le_ratio(*time),
Bound::Excluded(time)=>dt.lt_ratio(*time),
Bound::Unbounded=>true,
}
}
impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M> impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
where where
// This is hardcoded for MinkowskiMesh lol // This is hardcoded for MinkowskiMesh lol
M::Face:Copy, M::Face:Copy,
M::Edge:Copy, M::Edge:Copy,
M::Vert:Copy, M::Vert:Copy,
M:std::fmt::Debug,
F:std::fmt::Display,
F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>, F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>,
<F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum, <F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum,
M::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>, <M as MeshQuery>::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>,
{ {
fn next_transition(&self,mesh:&M,body:&Body,lower_bound:Bound<GigaTime>,mut upper_bound:Bound<GigaTime>)->Transition<M>{ fn next_transition(&self,body_time:GigaTime,mesh:&M,body:&Body,mut best_time:GigaTime)->Transition<M>{
//conflicting derivative means it crosses in the wrong direction. //conflicting derivative means it crosses in the wrong direction.
//if the transition time is equal to an already tested transition, do not replace the current best. //if the transition time is equal to an already tested transition, do not replace the current best.
let mut best_transition=Transition::Miss; let mut best_transition=Transition::Miss;
@@ -93,16 +47,11 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
//n=face.normal d=face.dot //n=face.normal d=face.dot
//n.a t^2+n.v t+n.p-d==0 //n.a t^2+n.v t+n.p-d==0
let (n,d)=mesh.face_nd(face_id); let (n,d)=mesh.face_nd(face_id);
println!("Face n={} d={}",n,d);
//TODO: use higher precision d value? //TODO: use higher precision d value?
//use the mesh transform translation instead of baking it into the d value. //use the mesh transform translation instead of baking it into the d value.
for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let low=low(&lower_bound,&dt); if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
let upp=upp(&dt,&upper_bound); best_time=dt;
let into=n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative();
println!("dt={} low={low} upp={upp} into={into}",dt.divide());
if low&&upp&&into{
upper_bound=Bound::Included(dt);
best_transition=Transition::Hit(face_id,dt); best_transition=Transition::Hit(face_id,dt);
break; break;
} }
@@ -116,8 +65,8 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
//WARNING: precision is swept under the rug! //WARNING: precision is swept under the rug!
//wrap for speed //wrap for speed
for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(v0)+mesh.vert(v1))).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){ for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(v0)+mesh.vert(v1))).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
upper_bound=Bound::Included(dt); best_time=dt;
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt); best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
break; break;
} }
@@ -135,17 +84,11 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
let face_n=mesh.face_nd(edge_face_id).0; let face_n=mesh.face_nd(edge_face_id).0;
//edge_n gets parity from the order of edge_faces //edge_n gets parity from the order of edge_faces
let n=face_n.cross(edge_n)*((i as i64)*2-1); let n=face_n.cross(edge_n)*((i as i64)*2-1);
let d=n.dot(delta_pos).wrap_4();
println!("Edge Face={:?} boundary_n={} boundary_d={}",edge_face_id,n,d>>1);
//WARNING yada yada d *2 //WARNING yada yada d *2
//wrap for speed //wrap for speed
for dt in Fixed::<4,128>::zeroes2(d,n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){ for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
let low=low(&lower_bound,&dt); if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
let upp=upp(&dt,&upper_bound); best_time=dt;
let into=n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative();
println!("dt={} low={low} upp={upp} into={into}",dt.divide());
if low&&upp&&into{
upper_bound=Bound::Included(dt);
best_transition=Transition::Next(FEV::Face(edge_face_id),dt); best_transition=Transition::Next(FEV::Face(edge_face_id),dt);
break; break;
} }
@@ -156,9 +99,9 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
//vertex normal gets parity from vert index //vertex normal gets parity from vert index
let n=edge_n*(1-2*(i as i64)); let n=edge_n*(1-2*(i as i64));
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4()); let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4());
upper_bound=Bound::Included(dt); best_time=dt;
best_transition=Transition::Next(FEV::Vert(vert_id),dt); best_transition=Transition::Next(FEV::Vert(vert_id),dt);
break; break;
} }
@@ -172,9 +115,9 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
//edge is directed away from vertex, but we want the dot product to turn out negative //edge is directed away from vertex, but we want the dot product to turn out negative
let n=-mesh.directed_edge_n(directed_edge_id); let n=-mesh.directed_edge_n(directed_edge_id);
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4()); let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4());
upper_bound=Bound::Included(dt); best_time=dt;
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt); best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
break; break;
} }
@@ -185,16 +128,19 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
} }
best_transition best_transition
} }
pub fn crawl(mut self,mesh:&M,relative_body:&Body,lower_bound:Bound<&Time>,upper_bound:Bound<&Time>)->CrawlResult<M>{ pub fn crawl(mut self,mesh:&M,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<M>{
let mut lower_bound=lower_bound.map(|&t|into_giga_time(t,relative_body.time)); let mut body_time={
let upper_bound=upper_bound.map(|&t|into_giga_time(t,relative_body.time)); let r=(start_time-relative_body.time).to_ratio();
println!("crawl begin={self:?}"); Ratio::new(r.num.widen_4(),r.den.widen_4())
};
let time_limit={
let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.widen_4(),r.den.widen_4())
};
for _ in 0..20{ for _ in 0..20{
let transition=self.next_transition(mesh,relative_body,lower_bound,upper_bound); match self.next_transition(body_time,mesh,relative_body,time_limit){
println!("transition={transition:?}");
match transition{
Transition::Miss=>return CrawlResult::Miss(self), Transition::Miss=>return CrawlResult::Miss(self),
Transition::Next(next_fev,next_time)=>(self,lower_bound)=(next_fev,Bound::Included(next_time)), Transition::Next(next_fev,next_time)=>(self,body_time)=(next_fev,next_time),
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time), Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
} }
} }

View File

@@ -1,5 +1,5 @@
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use core::ops::{Bound,RangeBounds}; use core::ops::Range;
use strafesnet_common::integer::vec3::Vector3; use strafesnet_common::integer::vec3::Vector3;
use strafesnet_common::model::{self,MeshId,PolygonIter}; use strafesnet_common::model::{self,MeshId,PolygonIter};
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio}; use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
@@ -68,17 +68,16 @@ pub enum FEV<M:MeshQuery>{
} }
//use Unit32 #[repr(C)] for map files //use Unit32 #[repr(C)] for map files
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)] #[derive(Clone,Hash,Eq,PartialEq)]
struct Face{ struct Face{
normal:Planar64Vec3, normal:Planar64Vec3,
dot:Planar64, dot:Planar64,
} }
#[derive(Debug)]
struct Vert(Planar64Vec3); struct Vert(Planar64Vec3);
pub trait MeshQuery{ pub trait MeshQuery{
type Face:Copy+std::fmt::Debug; type Face:Copy;
type Edge:Copy+DirectedEdge+std::fmt::Debug; type Edge:Copy+DirectedEdge;
type Vert:Copy+std::fmt::Debug; type Vert:Copy;
// Vertex must be Planar64Vec3 because it represents an actual position // Vertex must be Planar64Vec3 because it represents an actual position
type Normal; type Normal;
type Offset; type Offset;
@@ -98,22 +97,18 @@ pub trait MeshQuery{
fn vert_edges(&self,vert_id:Self::Vert)->impl AsRef<[Self::Edge]>; fn vert_edges(&self,vert_id:Self::Vert)->impl AsRef<[Self::Edge]>;
fn vert_faces(&self,vert_id:Self::Vert)->impl AsRef<[Self::Face]>; fn vert_faces(&self,vert_id:Self::Vert)->impl AsRef<[Self::Face]>;
} }
#[derive(Debug)]
struct FaceRefs{ struct FaceRefs{
edges:Vec<SubmeshDirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
//verts:Vec<VertId>, //verts:Vec<VertId>,
} }
#[derive(Debug)]
struct EdgeRefs{ struct EdgeRefs{
faces:[SubmeshFaceId;2],//left, right faces:[SubmeshFaceId;2],//left, right
verts:[SubmeshVertId;2],//bottom, top verts:[SubmeshVertId;2],//bottom, top
} }
#[derive(Debug)]
struct VertRefs{ struct VertRefs{
faces:Vec<SubmeshFaceId>, faces:Vec<SubmeshFaceId>,
edges:Vec<SubmeshDirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
} }
#[derive(Debug)]
pub struct PhysicsMeshData{ pub struct PhysicsMeshData{
//this contains all real and virtual faces used in both the complete mesh and convex submeshes //this contains all real and virtual faces used in both the complete mesh and convex submeshes
//faces are sorted such that all faces that belong to the complete mesh appear first, and then //faces are sorted such that all faces that belong to the complete mesh appear first, and then
@@ -123,7 +118,6 @@ pub struct PhysicsMeshData{
faces:Vec<Face>,//MeshFaceId indexes this list faces:Vec<Face>,//MeshFaceId indexes this list
verts:Vec<Vert>,//MeshVertId indexes this list verts:Vec<Vert>,//MeshVertId indexes this list
} }
#[derive(Debug)]
pub struct PhysicsMeshTopology{ pub struct PhysicsMeshTopology{
//mapping of local ids to PhysicsMeshData ids //mapping of local ids to PhysicsMeshData ids
faces:Vec<MeshFaceId>,//SubmeshFaceId indexes this list faces:Vec<MeshFaceId>,//SubmeshFaceId indexes this list
@@ -149,12 +143,10 @@ impl From<MeshId> for PhysicsMeshId{
pub struct PhysicsSubmeshId(u32); pub struct PhysicsSubmeshId(u32);
pub struct PhysicsMesh{ pub struct PhysicsMesh{
data:PhysicsMeshData, data:PhysicsMeshData,
// The complete mesh is unused at this time. complete_mesh:PhysicsMeshTopology,
// complete_mesh:PhysicsMeshTopology, //Most objects in roblox maps are already convex, so the list length is 0
// Submeshes are guaranteed to be convex and may contain //as soon as the mesh is divided into 2 submeshes, the list length jumps to 2.
// "virtual" faces which are not part of the complete mesh. //length 1 is unnecessary since the complete mesh would be a duplicate of the only submesh, but would still function properly
// Physics calculations should never resolve to hitting
// a virtual face.
submeshes:Vec<PhysicsMeshTopology>, submeshes:Vec<PhysicsMeshTopology>,
} }
impl PhysicsMesh{ impl PhysicsMesh{
@@ -218,24 +210,19 @@ impl PhysicsMesh{
}; };
Self{ Self{
data, data,
// complete_mesh:mesh_topology.clone(), complete_mesh:mesh_topology,
submeshes:vec![mesh_topology], submeshes:Vec::new(),
} }
} }
pub fn unit_cylinder()->Self{ pub fn unit_cylinder()->Self{
Self::unit_cube() Self::unit_cube()
} }
#[inline] #[inline]
pub fn complete_mesh(&self)->&PhysicsMeshTopology{ pub const fn complete_mesh(&self)->&PhysicsMeshTopology{
// If there is exactly one submesh, then the complete mesh is identical to it. &self.complete_mesh
if self.submeshes.len()==1{
self.submeshes.first().unwrap()
}else{
panic!("PhysicsMesh complete mesh is not known");
}
} }
#[inline] #[inline]
pub fn complete_mesh_view(&self)->PhysicsMeshView{ pub const fn complete_mesh_view(&self)->PhysicsMeshView{
PhysicsMeshView{ PhysicsMeshView{
data:&self.data, data:&self.data,
topology:self.complete_mesh(), topology:self.complete_mesh(),
@@ -243,7 +230,12 @@ impl PhysicsMesh{
} }
#[inline] #[inline]
pub fn submeshes(&self)->&[PhysicsMeshTopology]{ pub fn submeshes(&self)->&[PhysicsMeshTopology]{
&self.submeshes //the complete mesh is already a convex mesh when len()==0, len()==1 is invalid but will still work
if self.submeshes.len()==0{
std::slice::from_ref(&self.complete_mesh)
}else{
&self.submeshes.as_slice()
}
} }
#[inline] #[inline]
pub fn submesh_view(&self,submesh_id:PhysicsSubmeshId)->PhysicsMeshView{ pub fn submesh_view(&self,submesh_id:PhysicsSubmeshId)->PhysicsMeshView{
@@ -321,20 +313,14 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
if mesh.unique_pos.len()==0{ if mesh.unique_pos.len()==0{
return Err(PhysicsMeshError::ZeroVertices); return Err(PhysicsMeshError::ZeroVertices);
} }
// An empty physics mesh is a waste of resources
if mesh.physics_groups.len()==0{
return Err(PhysicsMeshError::NoPhysicsGroups);
}
let verts=mesh.unique_pos.iter().copied().map(Vert).collect(); let verts=mesh.unique_pos.iter().copied().map(Vert).collect();
//TODO: fix submeshes //TODO: fix submeshes
//flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id //flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id
//lower face_id points to upper face_id //lower face_id points to upper face_id
//the same face is not allowed to be in multiple polygon groups //the same face is not allowed to be in multiple polygon groups
// because SubmeshFaceId -> CompleteMeshFaceId -> SubmeshFaceId is ambiguous
// when multiple SubmeshFaceId point to one MeshFaceId
let mut faces=Vec::new(); let mut faces=Vec::new();
let mut face_id_from_face=HashMap::new(); let mut face_id_from_face=HashMap::new();
let mesh_topologies:Vec<PhysicsMeshTopology>=mesh.physics_groups.iter().map(|physics_group|{ let mut mesh_topologies:Vec<PhysicsMeshTopology>=mesh.physics_groups.iter().map(|physics_group|{
//construct submesh //construct submesh
let mut submesh_faces=Vec::new();//these contain a map from submeshId->meshId let mut submesh_faces=Vec::new();//these contain a map from submeshId->meshId
let mut submesh_verts=Vec::new(); let mut submesh_verts=Vec::new();
@@ -395,11 +381,15 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
normal:(normal/len as i64).divide().narrow_1().unwrap(), normal:(normal/len as i64).divide().narrow_1().unwrap(),
dot:(dot/(len*len) as i64).narrow_1().unwrap(), dot:(dot/(len*len) as i64).narrow_1().unwrap(),
}; };
let face_id=*face_id_from_face.entry(face).or_insert_with(||{ let face_id=match face_id_from_face.get(&face){
let face_id=MeshFaceId::new(faces.len() as u32); Some(&face_id)=>face_id,
faces.push(face); None=>{
face_id let face_id=MeshFaceId::new(faces.len() as u32);
}); face_id_from_face.insert(face.clone(),face_id);
faces.push(face);
face_id
}
};
submesh_faces.push(face_id); submesh_faces.push(face_id);
face_ref_guys.push(FaceRefEdges(face_edges)); face_ref_guys.push(FaceRefEdges(face_edges));
} }
@@ -407,16 +397,16 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
PhysicsMeshTopology{ PhysicsMeshTopology{
faces:submesh_faces, faces:submesh_faces,
verts:submesh_verts, verts:submesh_verts,
face_topology:face_ref_guys.into_iter().map(|FaceRefEdges(edges)|{ face_topology:face_ref_guys.into_iter().map(|face_ref_guy|{
FaceRefs{edges} FaceRefs{edges:face_ref_guy.0}
}).collect(), }).collect(),
edge_topology:edge_pool.edge_guys.into_iter().map(|(EdgeRefVerts(verts),EdgeRefFaces(faces))| edge_topology:edge_pool.edge_guys.into_iter().map(|(edge_ref_verts,edge_ref_faces)|
EdgeRefs{faces,verts} EdgeRefs{faces:edge_ref_faces.0,verts:edge_ref_verts.0}
).collect(), ).collect(),
vert_topology:vert_ref_guys.into_iter().map(|VertRefGuy{edges,faces}| vert_topology:vert_ref_guys.into_iter().map(|vert_ref_guy|
VertRefs{ VertRefs{
edges:edges.into_iter().collect(), edges:vert_ref_guy.edges.into_iter().collect(),
faces:faces.into_iter().collect(), faces:vert_ref_guy.faces.into_iter().collect(),
} }
).collect(), ).collect(),
} }
@@ -426,13 +416,12 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
faces, faces,
verts, verts,
}, },
// complete_mesh:None, complete_mesh:mesh_topologies.pop().ok_or(PhysicsMeshError::NoPhysicsGroups)?,
submeshes:mesh_topologies, submeshes:mesh_topologies,
}) })
} }
} }
#[derive(Debug)]
pub struct PhysicsMeshView<'a>{ pub struct PhysicsMeshView<'a>{
data:&'a PhysicsMeshData, data:&'a PhysicsMeshData,
topology:&'a PhysicsMeshTopology, topology:&'a PhysicsMeshTopology,
@@ -469,7 +458,6 @@ impl MeshQuery for PhysicsMeshView<'_>{
} }
} }
#[derive(Debug)]
pub struct PhysicsMeshTransform{ pub struct PhysicsMeshTransform{
pub vertex:integer::Planar64Affine3, pub vertex:integer::Planar64Affine3,
pub normal:integer::mat3::Matrix3<Fixed<2,64>>, pub normal:integer::mat3::Matrix3<Fixed<2,64>>,
@@ -485,7 +473,6 @@ impl PhysicsMeshTransform{
} }
} }
#[derive(Debug)]
pub struct TransformedMesh<'a>{ pub struct TransformedMesh<'a>{
view:PhysicsMeshView<'a>, view:PhysicsMeshView<'a>,
transform:&'a PhysicsMeshTransform, transform:&'a PhysicsMeshTransform,
@@ -611,7 +598,6 @@ pub enum MinkowskiFace{
//FaceFace //FaceFace
} }
#[derive(Debug)]
pub struct MinkowskiMesh<'a>{ pub struct MinkowskiMesh<'a>{
mesh0:TransformedMesh<'a>, mesh0:TransformedMesh<'a>,
mesh1:TransformedMesh<'a>, mesh1:TransformedMesh<'a>,
@@ -629,10 +615,6 @@ enum EV{
} }
pub type GigaTime=Ratio<Fixed<4,128>,Fixed<4,128>>; pub type GigaTime=Ratio<Fixed<4,128>,Fixed<4,128>>;
pub fn into_giga_time(time:Time,relative_to:Time)->GigaTime{
let r=(time-relative_to).to_ratio();
Ratio::new(r.num.widen_4(),r.den.widen_4())
}
impl MinkowskiMesh<'_>{ impl MinkowskiMesh<'_>{
pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{ pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{
@@ -748,46 +730,44 @@ impl MinkowskiMesh<'_>{
// //
// Most of the calculation time is just calculating the starting point // Most of the calculation time is just calculating the starting point
// for the "actual" crawling algorithm below (predict_collision_{in|out}). // for the "actual" crawling algorithm below (predict_collision_{in|out}).
fn closest_fev_not_inside(&self,mut infinity_body:Body,start_time:Bound<&Time>)->Option<FEV<MinkowskiMesh>>{ fn closest_fev_not_inside(&self,mut infinity_body:Body,start_time:Time)->Option<FEV<MinkowskiMesh>>{
infinity_body.infinity_dir().and_then(|dir|{ infinity_body.infinity_dir().and_then(|dir|{
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() // TODO: change crawl_fev args to delta time? Optional values?
infinity_fev.crawl(self,&infinity_body,Time::MIN/4,start_time).miss()
}) })
} }
pub fn predict_collision_in(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{ pub fn predict_collision_in(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>)->Option<(MinkowskiFace,GigaTime)>{
println!("@@@BEGIN SETUP@@@"); self.closest_fev_not_inside(relative_body.clone(),start_time).and_then(|fev|{
self.closest_fev_not_inside(relative_body.clone(),range.start_bound()).and_then(|fev|{
println!("@@@BEGIN REAL CRAWL@@@");
//continue forwards along the body parabola //continue forwards along the body parabola
fev.crawl(self,relative_body,range.start_bound(),range.end_bound()).hit() fev.crawl(self,relative_body,start_time,time_limit).hit()
}) })
} }
pub fn predict_collision_out(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{ pub fn predict_collision_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>)->Option<(MinkowskiFace,GigaTime)>{
let (lower_bound,upper_bound)=(range.start_bound(),range.end_bound()); //create an extrapolated body at time_limit
// swap and negate bounds to do a time inversion
let (lower_bound,upper_bound)=(upper_bound.map(|&t|-t),lower_bound.map(|&t|-t));
let infinity_body=-relative_body.clone(); let infinity_body=-relative_body.clone();
self.closest_fev_not_inside(infinity_body,lower_bound.as_ref()).and_then(|fev|{ self.closest_fev_not_inside(infinity_body,-time_limit).and_then(|fev|{
//continue backwards along the body parabola //continue backwards along the body parabola
fev.crawl(self,&infinity_body,lower_bound.as_ref(),upper_bound.as_ref()).hit() fev.crawl(self,&infinity_body,-time_limit,-start_time).hit()
//no need to test -time<time_limit because of the first step //no need to test -time<time_limit because of the first step
.map(|(face,time)|(face,-time)) .map(|(face,time)|(face,-time))
}) })
} }
pub fn predict_collision_face_out(&self,relative_body:&Body,range:impl RangeBounds<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiDirectedEdge,GigaTime)>{ pub fn predict_collision_face_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
// TODO: make better
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={
let r=(t-relative_body.time).to_ratio(); let r=(start_time-relative_body.time).to_ratio();
Ratio::new(r.num,r.den) 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={
let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.widen_4(),r.den.widen_4())
};
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;
for &directed_edge_id in self.face_edges(contact_face_id).as_ref(){ for &directed_edge_id in self.face_edges(contact_face_id).as_ref(){
@@ -800,19 +780,18 @@ impl MinkowskiMesh<'_>{
//WARNING: truncated precision //WARNING: truncated precision
//wrap for speed //wrap for speed
for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).wrap_4(),n.dot(relative_body.velocity).wrap_4()*2,n.dot(relative_body.acceleration).wrap_4()){ for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).wrap_4(),n.dot(relative_body.velocity).wrap_4()*2,n.dot(relative_body.acceleration).wrap_4()){
if low(&start_time,&dt)&&upp(&dt,&best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ if start_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=Bound::Included(dt); best_time=dt;
best_edge=Some((directed_edge_id,dt)); best_edge=Some(directed_edge_id);
break; break;
} }
} }
} }
best_edge best_edge.map(|e|(e.as_undirected(),best_time))
} }
fn infinity_in(&self,infinity_body:Body)->Option<(MinkowskiFace,GigaTime)>{ fn infinity_in(&self,infinity_body:Body)->Option<(MinkowskiFace,GigaTime)>{
let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position); let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position);
// Bound::Included means that the surface of the mesh is included in the mesh infinity_fev.crawl(self,&infinity_body,Time::MIN/4,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);
@@ -950,10 +929,10 @@ impl MeshQuery for MinkowskiMesh<'_>{
} }
fn edge_verts(&self,edge_id:MinkowskiEdge)->impl AsRef<[MinkowskiVert;2]>{ fn edge_verts(&self,edge_id:MinkowskiEdge)->impl AsRef<[MinkowskiVert;2]>{
AsRefHelper(match edge_id{ AsRefHelper(match edge_id{
MinkowskiEdge::VertEdge(v0,e1)=>self.mesh1.edge_verts(e1).as_ref().map(|vert_id1| MinkowskiEdge::VertEdge(v0,e1)=>(*self.mesh1.edge_verts(e1).as_ref()).map(|vert_id1|
MinkowskiVert::VertVert(v0,vert_id1) MinkowskiVert::VertVert(v0,vert_id1)
), ),
MinkowskiEdge::EdgeVert(e0,v1)=>self.mesh0.edge_verts(e0).as_ref().map(|vert_id0| MinkowskiEdge::EdgeVert(e0,v1)=>(*self.mesh0.edge_verts(e0).as_ref()).map(|vert_id0|
MinkowskiVert::VertVert(vert_id0,v1) MinkowskiVert::VertVert(vert_id0,v1)
), ),
}) })
@@ -963,43 +942,38 @@ impl MeshQuery for MinkowskiMesh<'_>{
MinkowskiVert::VertVert(v0,v1)=>{ MinkowskiVert::VertVert(v0,v1)=>{
let mut edges=Vec::new(); let mut edges=Vec::new();
//detect shared volume when the other mesh is mirrored along a test edge dir //detect shared volume when the other mesh is mirrored along a test edge dir
let v0f_thing=self.mesh0.vert_faces(v0); let v0f=self.mesh0.vert_faces(v0);
let v1f_thing=self.mesh1.vert_faces(v1); let v1f=self.mesh1.vert_faces(v1);
let v0f=v0f_thing.as_ref(); let v0f_n:Vec<_>=v0f.as_ref().iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
let v1f=v1f_thing.as_ref(); let v1f_n:Vec<_>=v1f.as_ref().iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
let v0f_n:Vec<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect(); let the_len=v0f.as_ref().len()+v1f.as_ref().len();
let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
// scratch vector
let mut face_normals=Vec::with_capacity(v0f.len()+v1f.len());
face_normals.clone_from(&v0f_n);
for &directed_edge_id in self.mesh0.vert_edges(v0).as_ref(){ for &directed_edge_id in self.mesh0.vert_edges(v0).as_ref(){
let n=self.mesh0.directed_edge_n(directed_edge_id); let n=self.mesh0.directed_edge_n(directed_edge_id);
let nn=n.dot(n); let nn=n.dot(n);
// TODO: there's gotta be a better way to do this // TODO: there's gotta be a better way to do this
// drop faces beyond v0f_n //make a set of faces
face_normals.truncate(v0f.len()); let mut face_normals=Vec::with_capacity(the_len);
// make a set of faces from mesh0's perspective //add mesh0 faces as-is
face_normals.clone_from(&v0f_n);
for face_n in &v1f_n{ for face_n in &v1f_n{
//add reflected mesh1 faces //add reflected mesh1 faces
//wrap for speed //wrap for speed
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().wrap_3()); face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().wrap_3());
} }
if is_empty_volume(&face_normals){ if is_empty_volume(face_normals){
edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1)); edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1));
} }
} }
face_normals.clone_from(&v1f_n);
for &directed_edge_id in self.mesh1.vert_edges(v1).as_ref(){ for &directed_edge_id in self.mesh1.vert_edges(v1).as_ref(){
let n=self.mesh1.directed_edge_n(directed_edge_id); let n=self.mesh1.directed_edge_n(directed_edge_id);
let nn=n.dot(n); let nn=n.dot(n);
// drop faces beyond v1f_n let mut face_normals=Vec::with_capacity(the_len);
face_normals.truncate(v1f.len()); face_normals.clone_from(&v1f_n);
// make a set of faces from mesh1's perspective
for face_n in &v0f_n{ for face_n in &v0f_n{
//wrap for speed //wrap for speed
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().wrap_3()); face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().wrap_3());
} }
if is_empty_volume(&face_normals){ if is_empty_volume(face_normals){
edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id)); edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id));
} }
} }
@@ -1013,7 +987,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
} }
} }
fn is_empty_volume(normals:&[Vector3<Fixed<3,96>>])->bool{ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
let len=normals.len(); let len=normals.len();
for i in 0..len-1{ for i in 0..len-1{
for j in i+1..len{ for j in i+1..len{
@@ -1039,6 +1013,6 @@ fn is_empty_volume(normals:&[Vector3<Fixed<3,96>>])->bool{
#[test] #[test]
fn test_is_empty_volume(){ fn test_is_empty_volume(){
assert!(!is_empty_volume(&[vec3::X.widen_3(),vec3::Y.widen_3(),vec3::Z.widen_3()])); assert!(!is_empty_volume([vec3::X.widen_3(),vec3::Y.widen_3(),vec3::Z.widen_3()].to_vec()));
assert!(is_empty_volume(&[vec3::X.widen_3(),vec3::Y.widen_3(),vec3::Z.widen_3(),vec3::NEG_X.widen_3()])); assert!(is_empty_volume([vec3::X.widen_3(),vec3::Y.widen_3(),vec3::Z.widen_3(),vec3::NEG_X.widen_3()].to_vec()));
} }

View File

@@ -417,7 +417,7 @@ impl HitboxMesh{
} }
} }
#[inline] #[inline]
fn transformed_mesh(&self)->TransformedMesh{ const fn transformed_mesh(&self)->TransformedMesh{
TransformedMesh::new(self.mesh.complete_mesh_view(),&self.transform) TransformedMesh::new(self.mesh.complete_mesh_view(),&self.transform)
} }
} }
@@ -1193,7 +1193,7 @@ fn recalculate_touching(
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(contact.model_id),contact)
} }
while let Some(&intersect)=touching.intersects.iter().next(){ while let Some(&intersect)=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(touching,mode,run,models.intersect_attr(intersect.model_id),intersect,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();
@@ -1605,7 +1605,6 @@ fn collision_start_intersect(
None=>(), None=>(),
} }
} }
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.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);
} }
@@ -1638,14 +1637,7 @@ fn collision_end_contact(
} }
} }
fn collision_end_intersect( fn collision_end_intersect(
move_state:&mut MoveState,
body:&mut Body,
touching:&mut TouchingState, touching:&mut TouchingState,
models:&PhysicsModels,
hitbox_mesh:&HitboxMesh,
style:&StyleModifiers,
camera:&PhysicsCamera,
input_state:&InputState,
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,
@@ -1653,7 +1645,6 @@ fn collision_end_intersect(
time:Time, time:Time,
){ ){
touching.remove(&Collision::Intersect(intersect)); touching.remove(&Collision::Intersect(intersect));
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(intersect.model_id.into());
match zone{ match zone{
@@ -1710,7 +1701,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
contact contact
), ),
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.touching,
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.model_id),
@@ -1933,15 +1924,6 @@ mod test{
),Some(Time::from_secs(2))); ),Some(Time::from_secs(2)));
} }
#[test] #[test]
fn test_collision_small_mv(){
test_collision(Body::new(
int3(0,5,0),
int3(0,-1,0)+(vec3::X>>32),
vec3::ZERO,
Time::ZERO
),Some(Time::from_secs(2)));
}
#[test]
fn test_collision_degenerate_east(){ fn test_collision_degenerate_east(){
test_collision(Body::new( test_collision(Body::new(
int3(3,5,0), int3(3,5,0),

View File

@@ -349,7 +349,7 @@ pub struct PartialMap1{
impl PartialMap1{ impl PartialMap1{
pub fn add_prop_meshes<'a>( pub fn add_prop_meshes<'a>(
self, self,
prop_meshes:Meshes<model::Mesh>, prop_meshes:Meshes,
)->PartialMap2{ )->PartialMap2{
PartialMap2{ PartialMap2{
attributes:self.attributes, attributes:self.attributes,

View File

@@ -1,5 +1,5 @@
pub use fixed_wide::fixed::*; pub use fixed_wide::fixed::*;
pub use ratio_ops::ratio::{Ratio,Divide,Parity}; pub use ratio_ops::ratio::{Ratio,Divide};
//integer units //integer units
@@ -132,21 +132,20 @@ impl<T> std::ops::Mul for Time<T>{
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)))
} }
} }
macro_rules! impl_time_i64_rhs_operator { impl<T> std::ops::Div<i64> for Time<T>{
($op:ident,$method:ident)=>{ type Output=Self;
impl<T> core::ops::$op<i64> for Time<T>{ #[inline]
type Output=Self; fn div(self,rhs:i64)->Self::Output{
#[inline] Self::raw(self.0/rhs)
fn $method(self,rhs:i64)->Self::Output{ }
Self::raw(self.0.$method(rhs)) }
} impl<T> std::ops::Mul<i64> for Time<T>{
} type Output=Self;
#[inline]
fn mul(self,rhs:i64)->Self::Output{
Self::raw(self.0*rhs)
} }
} }
impl_time_i64_rhs_operator!(Div,div);
impl_time_i64_rhs_operator!(Mul,mul);
impl_time_i64_rhs_operator!(Shr,shr);
impl_time_i64_rhs_operator!(Shl,shl);
impl<T> core::ops::Mul<Time<T>> for Planar64{ impl<T> core::ops::Mul<Time<T>> for Planar64{
type Output=Ratio<Fixed<2,64>,Planar64>; type Output=Ratio<Fixed<2,64>,Planar64>;
fn mul(self,rhs:Time<T>)->Self::Output{ fn mul(self,rhs:Time<T>)->Self::Output{
@@ -657,7 +656,7 @@ pub mod mat3{
} }
} }
#[derive(Clone,Copy,Debug,Default,Hash,Eq,PartialEq)] #[derive(Clone,Copy,Default,Hash,Eq,PartialEq)]
pub struct Planar64Affine3{ pub struct Planar64Affine3{
pub matrix3:Planar64Mat3,//includes scale above 1 pub matrix3:Planar64Mat3,//includes scale above 1
pub translation:Planar64Vec3, pub translation:Planar64Vec3,

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::loader::Loader; use crate::loader::Loader;
use crate::mesh::Meshes; use crate::mesh::Meshes;
use crate::texture::{RenderConfigs,Texture}; use crate::texture::{RenderConfigs,Texture};
use strafesnet_common::model::{MeshId,RenderConfig,RenderConfigId,TextureId}; use strafesnet_common::model::{Mesh,MeshId,RenderConfig,RenderConfigId,TextureId};
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy,Debug)]
pub enum LoadFailureMode{ pub enum LoadFailureMode{
@@ -93,7 +93,7 @@ impl<H:core::hash::Hash+Eq> MeshDeferredLoader<H>{
pub fn into_indices(self)->impl Iterator<Item=H>{ pub fn into_indices(self)->impl Iterator<Item=H>{
self.mesh_id_from_asset_id.into_keys() self.mesh_id_from_asset_id.into_keys()
} }
pub fn into_meshes<'a,M:Clone,L:Loader<Resource=M,Index<'a>=H>+'a>(self,loader:&mut L,failure_mode:LoadFailureMode)->Result<Meshes<M>,L::Error>{ pub fn into_meshes<'a,L:Loader<Resource=Mesh,Index<'a>=H>+'a>(self,loader:&mut L,failure_mode:LoadFailureMode)->Result<Meshes,L::Error>{
let mut mesh_list=vec![None;self.mesh_id_from_asset_id.len()]; let mut mesh_list=vec![None;self.mesh_id_from_asset_id.len()];
for (index,mesh_id) in self.mesh_id_from_asset_id{ for (index,mesh_id) in self.mesh_id_from_asset_id{
let resource_result=loader.load(index); let resource_result=loader.load(index);

View File

@@ -1,15 +1,15 @@
use strafesnet_common::model::MeshId; use strafesnet_common::model::{Mesh,MeshId};
pub struct Meshes<M>{ pub struct Meshes{
meshes:Vec<Option<M>>, meshes:Vec<Option<Mesh>>,
} }
impl<M> Meshes<M>{ impl Meshes{
pub(crate) const fn new(meshes:Vec<Option<M>>)->Self{ pub(crate) const fn new(meshes:Vec<Option<Mesh>>)->Self{
Self{ Self{
meshes, meshes,
} }
} }
pub fn consume(self)->impl Iterator<Item=(MeshId,M)>{ pub fn consume(self)->impl Iterator<Item=(MeshId,Mesh)>{
self.meshes.into_iter().enumerate().filter_map(|(mesh_id,maybe_mesh)| self.meshes.into_iter().enumerate().filter_map(|(mesh_id,maybe_mesh)|
maybe_mesh.map(|mesh|(MeshId::new(mesh_id as u32),mesh)) maybe_mesh.map(|mesh|(MeshId::new(mesh_id as u32),mesh))
) )

View File

@@ -13,11 +13,11 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
bytemuck = "1.14.3" bytemuck = "1.14.3"
glam = "0.30.0" glam = "0.30.0"
lazy-regex = "3.1.0" lazy-regex = "3.1.0"
rbx_binary = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_dom_weak = { version = "3.1.0-sn4", registry = "strafesnet", features = ["instance-userdata"] }
rbx_mesh = "0.3.1" rbx_mesh = "0.3.1"
rbx_reflection_database = "1.0.0" rbx_binary = { path = "../../../../git/rbx-dom/rbx_binary", registry = "strafesnet" }
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" } rbx_dom_weak = { path = "../../../../git/rbx-dom/rbx_dom_weak", registry = "strafesnet" }
rbx_reflection_database = { path = "../../../../git/rbx-dom/rbx_reflection_database"}
rbx_xml = { path = "../../../../git/rbx-dom/rbx_xml", 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.0", path = "../roblox_emulator", default-features = false, registry = "strafesnet" } roblox_emulator = { version = "0.5.0", path = "../roblox_emulator", default-features = false, registry = "strafesnet" }
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }

View File

@@ -21,19 +21,19 @@ pub mod data{
} }
} }
pub struct Model{ pub struct Model<'a>{
dom:WeakDom, dom:WeakDom<'a>,
} }
impl Model{ impl<'a> Model<'a>{
fn new(dom:WeakDom)->Self{ fn new(dom:WeakDom<'a>)->Self{
Self{dom} Self{dom}
} }
pub fn to_snf(&self,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{ pub fn to_snf(&self,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{
to_snf(self,failure_mode) to_snf(self,failure_mode)
} }
} }
impl AsRef<WeakDom> for Model{ impl<'a> AsRef<WeakDom<'a>> for Model<'a>{
fn as_ref(&self)->&WeakDom{ fn as_ref(&self)->&WeakDom<'a>{
&self.dom &self.dom
} }
} }
@@ -95,7 +95,7 @@ pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
let mut buf=std::io::BufReader::new(input); let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?; let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match peek.get(0..8){ match peek.get(0..8){
Some(b"<roblox!")=>rbx_binary::from_reader(buf).map(Model::new).map_err(ReadError::RbxBinary), Some(b"<roblox!")=>rbx_binary::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxBinary),
Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml), Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml),
_=>Err(ReadError::UnknownFileFormat), _=>Err(ReadError::UnknownFileFormat),
} }

View File

@@ -6,10 +6,7 @@ use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
use crate::data::RobloxMeshBytes; use crate::data::RobloxMeshBytes;
use crate::rbx::RobloxPartDescription; use crate::rbx::RobloxPartDescription;
// disallow non-static lifetimes use rbx_dom_weak::hstr;
fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}
fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{ fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{
let mut file=std::fs::File::open(path)?; let mut file=std::fs::File::open(path)?;
@@ -143,12 +140,6 @@ impl MeshIndex<'_>{
} }
} }
#[derive(Clone)]
pub struct MeshWithSize{
pub(crate) mesh:Mesh,
pub(crate) size:strafesnet_common::integer::Planar64Vec3,
}
pub struct MeshLoader; pub struct MeshLoader;
impl MeshLoader{ impl MeshLoader{
pub fn new()->Self{ pub fn new()->Self{
@@ -158,7 +149,7 @@ impl MeshLoader{
impl Loader for MeshLoader{ 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=Mesh;
fn load<'a>(&mut self,index:Self::Index<'a>)->Result<Self::Resource,Self::Error>{ fn load<'a>(&mut self,index:Self::Index<'a>)->Result<Self::Resource,Self::Error>{
let mesh=match index.mesh_type{ let mesh=match index.mesh_type{
MeshType::FileMesh=>{ MeshType::FileMesh=>{
@@ -174,7 +165,7 @@ impl Loader for MeshLoader{
let RobloxAssetId(asset_id)=index.content.parse()?; let RobloxAssetId(asset_id)=index.content.parse()?;
let file_name=format!("unions/{}",asset_id); let file_name=format!("unions/{}",asset_id);
let data=read_entire_file(file_name)?; let data=read_entire_file(file_name)?;
let dom=rbx_binary::from_reader(std::io::Cursor::new(data))?; let dom=rbx_binary::from_reader_default(data.as_slice())?;
let &[referent]=dom.root().children()else{ let &[referent]=dom.root().children()else{
return Err(MeshError::OneChildPolicy); return Err(MeshError::OneChildPolicy);
}; };
@@ -182,12 +173,12 @@ impl Loader for MeshLoader{
return Err(MeshError::MissingInstance); return Err(MeshError::MissingInstance);
}; };
if physics_data.is_empty(){ if physics_data.is_empty(){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&static_ustr("PhysicsData")){ if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(hstr!("PhysicsData")){
physics_data=data.as_ref(); physics_data=data.as_ref();
} }
} }
if mesh_data.is_empty(){ if mesh_data.is_empty(){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&static_ustr("MeshData")){ if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(hstr!("MeshData")){
mesh_data=data.as_ref(); mesh_data=data.as_ref();
} }
} }

View File

@@ -1,11 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use rbx_mesh::mesh::{Vertex2,Vertex2Truncated}; use rbx_mesh::mesh::{Vertex2,Vertex2Truncated};
use strafesnet_common::aabb::Aabb; use strafesnet_common::{integer::vec3,model::{self,ColorId,IndexedVertex,NormalId,PolygonGroup,PolygonList,PositionId,RenderConfigId,TextureCoordinateId,VertexId}};
use strafesnet_common::integer::vec3;
use strafesnet_common::model::{self,ColorId,IndexedVertex,NormalId,PolygonGroup,PolygonList,PositionId,RenderConfigId,TextureCoordinateId,VertexId};
use crate::loader::MeshWithSize;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
@@ -99,7 +95,7 @@ fn ingest_faces2_lods3(
)) ))
} }
pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithSize,Error>{ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<model::Mesh,Error>{
//generate that mesh boi //generate that mesh boi
let mut unique_pos=Vec::new(); let mut unique_pos=Vec::new();
let mut pos_id_from=HashMap::new(); let mut pos_id_from=HashMap::new();
@@ -112,10 +108,8 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
let mut unique_vertices=Vec::new(); let mut unique_vertices=Vec::new();
let mut vertex_id_from=HashMap::new(); let mut vertex_id_from=HashMap::new();
let mut polygon_groups=Vec::new(); let mut polygon_groups=Vec::new();
let mut aabb=Aabb::default();
let mut acquire_pos_id=|pos|{ let mut acquire_pos_id=|pos|{
let p=vec3::try_from_f32_array(pos).map_err(Error::Planar64Vec3)?; let p=vec3::try_from_f32_array(pos).map_err(Error::Planar64Vec3)?;
aabb.grow(p);
Ok(PositionId::new(*pos_id_from.entry(p).or_insert_with(||{ Ok(PositionId::new(*pos_id_from.entry(p).or_insert_with(||{
let pos_id=unique_pos.len(); let pos_id=unique_pos.len();
unique_pos.push(p); unique_pos.push(p);
@@ -203,7 +197,7 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods); ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
}, },
} }
let mesh=model::Mesh{ Ok(model::Mesh{
unique_pos, unique_pos,
unique_normal, unique_normal,
unique_tex, unique_tex,
@@ -219,6 +213,5 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
}], }],
//disable physics //disable physics
physics_groups:Vec::new(), physics_groups:Vec::new(),
}; })
Ok(MeshWithSize{mesh,size:aabb.size()})
} }

View File

@@ -56,7 +56,7 @@ const CUBE_DEFAULT_TEXTURE_COORDS:[TextureCoordinate;4]=[
TextureCoordinate::new(1.0,1.0), TextureCoordinate::new(1.0,1.0),
TextureCoordinate::new(0.0,1.0), TextureCoordinate::new(0.0,1.0),
]; ];
pub const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[ const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[
vec3::int(-1,-1, 1),//0 left bottom back vec3::int(-1,-1, 1),//0 left bottom back
vec3::int( 1,-1, 1),//1 right bottom back vec3::int( 1,-1, 1),//1 right bottom back
vec3::int( 1, 1, 1),//2 right top back vec3::int( 1, 1, 1),//2 right top back
@@ -66,7 +66,7 @@ pub const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[
vec3::int( 1,-1,-1),//6 right bottom front vec3::int( 1,-1,-1),//6 right bottom front
vec3::int(-1,-1,-1),//7 left bottom front vec3::int(-1,-1,-1),//7 left bottom front
]; ];
pub const CUBE_DEFAULT_NORMALS:[Planar64Vec3;6]=[ const CUBE_DEFAULT_NORMALS:[Planar64Vec3;6]=[
vec3::int( 1, 0, 0),//CubeFace::Right vec3::int( 1, 0, 0),//CubeFace::Right
vec3::int( 0, 1, 0),//CubeFace::Top vec3::int( 0, 1, 0),//CubeFace::Top
vec3::int( 0, 0, 1),//CubeFace::Back vec3::int( 0, 0, 1),//CubeFace::Back
@@ -121,7 +121,8 @@ impl FaceDescription{
} }
} }
} }
pub const CUBE_DEFAULT_POLYS:[[[u32;2];4];6]=[ pub fn unit_cube(CubeFaceDescription(face_descriptions):CubeFaceDescription)->Mesh{
const CUBE_DEFAULT_POLYS:[[[u32;2];4];6]=[
// right (1, 0, 0) // right (1, 0, 0)
[ [
[6,2],//[vertex,tex] [6,2],//[vertex,tex]
@@ -165,7 +166,6 @@ pub const CUBE_DEFAULT_POLYS:[[[u32;2];4];6]=[
[7,2], [7,2],
], ],
]; ];
pub fn unit_cube(CubeFaceDescription(face_descriptions):CubeFaceDescription)->Mesh{
let mut generated_pos=Vec::new(); let mut generated_pos=Vec::new();
let mut generated_tex=Vec::new(); let mut generated_tex=Vec::new();
let mut generated_normal=Vec::new(); let mut generated_normal=Vec::new();

View File

@@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::loader::{MeshWithSize,MeshIndex}; use crate::loader::MeshIndex;
use crate::primitives::{self,CubeFace,CubeFaceDescription,WedgeFaceDescription,CornerWedgeFaceDescription,FaceDescription,Primitives}; use crate::primitives::{self,CubeFace,CubeFaceDescription,WedgeFaceDescription,CornerWedgeFaceDescription,FaceDescription,Primitives};
use strafesnet_common::aabb::Aabb;
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_common::model; use strafesnet_common::model;
use strafesnet_common::gameplay_modes::{NormalizedModes,Mode,ModeId,ModeUpdate,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone}; use strafesnet_common::gameplay_modes::{NormalizedModes,Mode,ModeId,ModeUpdate,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone};
@@ -12,16 +13,13 @@ use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,Mes
use strafesnet_deferred_loader::mesh::Meshes; use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture}; use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
// disallow non-static lifetimes use rbx_dom_weak::{hstr,HashStr};
fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}
fn recursive_collect_superclass( fn recursive_collect_superclass(
objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>, objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,
dom:&rbx_dom_weak::WeakDom, dom:&rbx_dom_weak::WeakDom,
instance:&rbx_dom_weak::Instance, instance:&rbx_dom_weak::Instance,
superclass:&str superclass:&HashStr,
){ ){
let instance=instance; let instance=instance;
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get();
@@ -30,7 +28,7 @@ fn recursive_collect_superclass(
}; };
objects.extend( objects.extend(
dom.descendants_of(instance.referent()).filter_map(|instance|{ dom.descendants_of(instance.referent()).filter_map(|instance|{
let class=db.classes.get(instance.class.as_str())?; let class=db.classes.get(instance.class)?;
db.has_superclass(class,superclass).then(||instance.referent()) db.has_superclass(class,superclass).then(||instance.referent())
}) })
); );
@@ -56,19 +54,15 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
let mut contacting=attr::ContactingAttributes::default(); let mut contacting=attr::ContactingAttributes::default();
let mut force_can_collide=can_collide; let mut force_can_collide=can_collide;
let mut force_intersecting=false; let mut force_intersecting=false;
let mut allow_booster=true;
match name{ match name{
"Water"=>{ "Water"=>{
force_can_collide=false; force_can_collide=false;
allow_booster=false;
//TODO: read stupid CustomPhysicalProperties //TODO: read stupid CustomPhysicalProperties
intersecting.water=Some(attr::IntersectingWater{density:Planar64::ONE,viscosity:Planar64::ONE/10,velocity}); intersecting.water=Some(attr::IntersectingWater{density:Planar64::ONE,viscosity:Planar64::ONE/10,velocity});
}, },
"Accelerator"=>{ "Accelerator"=>{
//although the new game supports collidable accelerators, this is a roblox compatability map loader //although the new game supports collidable accelerators, this is a roblox compatability map loader
force_can_collide=false; force_can_collide=false;
// Accelerator is not allowed to be booster in roblox
allow_booster=false;
general.accelerator=Some(attr::Accelerator{acceleration:velocity}); general.accelerator=Some(attr::Accelerator{acceleration:velocity});
}, },
// "UnorderedCheckpoint"=>general.teleport_behaviour=Some(model::TeleportBehaviour::StageElement(attr::StageElement{ // "UnorderedCheckpoint"=>general.teleport_behaviour=Some(model::TeleportBehaviour::StageElement(attr::StageElement{
@@ -77,10 +71,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
// force:false, // force:false,
// behaviour:model::StageElementBehaviour::Unordered // behaviour:model::StageElementBehaviour::Unordered
// })), // })),
"SetVelocity"=>{ "SetVelocity"=>general.trajectory=Some(attr::SetTrajectory::Velocity(velocity)),
allow_booster=false;
general.trajectory=Some(attr::SetTrajectory::Velocity(velocity));
},
"MapStart"=>{ "MapStart"=>{
force_can_collide=false; force_can_collide=false;
force_intersecting=true; force_intersecting=true;
@@ -240,7 +231,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 velocity!=vec3::ZERO{
general.booster=Some(attr::Booster::Velocity(velocity)); general.booster=Some(attr::Booster::Velocity(velocity));
} }
match force_can_collide{ match force_can_collide{
@@ -416,7 +407,7 @@ fn get_texture_description<'a>(
//use the biggest one and cut it down later... //use the biggest one and cut it down later...
let mut part_texture_description=RobloxPartDescription::default(); let mut part_texture_description=RobloxPartDescription::default();
temp_objects.clear(); temp_objects.clear();
recursive_collect_superclass(temp_objects,&dom,object,"Decal"); recursive_collect_superclass(temp_objects,&dom,object,hstr!("Decal"));
for &mut decal_ref in temp_objects{ for &mut decal_ref in temp_objects{
let Some(decal)=dom.get_by_ref(decal_ref) else{ let Some(decal)=dom.get_by_ref(decal_ref) else{
println!("Decal get_by_ref failed"); println!("Decal get_by_ref failed");
@@ -428,10 +419,10 @@ fn get_texture_description<'a>(
Some(rbx_dom_weak::types::Variant::Color3(decal_color3)), Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)), Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
)=( )=(
decal.properties.get(&static_ustr("TextureContent")), decal.properties.get(hstr!("TextureContent")),
decal.properties.get(&static_ustr("Face")), decal.properties.get(hstr!("Face")),
decal.properties.get(&static_ustr("Color3")), decal.properties.get(hstr!("Color3")),
decal.properties.get(&static_ustr("Transparency")), decal.properties.get(hstr!("Transparency")),
)else{ )else{
println!("Decal is missing a required property"); println!("Decal is missing a required property");
continue; continue;
@@ -453,10 +444,10 @@ fn get_texture_description<'a>(
Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_u)), Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_u)),
Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_v)), Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_v)),
) = ( ) = (
decal.properties.get(&static_ustr("OffsetStudsU")), decal.properties.get(hstr!("OffsetStudsU")),
decal.properties.get(&static_ustr("OffsetStudsV")), decal.properties.get(hstr!("OffsetStudsV")),
decal.properties.get(&static_ustr("StudsPerTileU")), decal.properties.get(hstr!("StudsPerTileU")),
decal.properties.get(&static_ustr("StudsPerTileV")), decal.properties.get(hstr!("StudsPerTileV")),
) )
{ {
let (size_u,size_v)=match cube_face{ let (size_u,size_v)=match cube_face{
@@ -543,7 +534,7 @@ pub fn convert<'a>(
let mut object_refs=Vec::new(); let mut object_refs=Vec::new();
let mut temp_objects=Vec::new(); let mut temp_objects=Vec::new();
recursive_collect_superclass(&mut object_refs, &dom, dom.root(),"BasePart"); recursive_collect_superclass(&mut object_refs, &dom, dom.root(),hstr!("BasePart"));
for object_ref in object_refs { for object_ref in object_refs {
if let Some(object)=dom.get_by_ref(object_ref){ if let Some(object)=dom.get_by_ref(object_ref){
if let ( if let (
@@ -554,12 +545,12 @@ pub fn convert<'a>(
Some(rbx_dom_weak::types::Variant::Color3uint8(color3)), Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
Some(rbx_dom_weak::types::Variant::Bool(can_collide)), Some(rbx_dom_weak::types::Variant::Bool(can_collide)),
) = ( ) = (
object.properties.get(&static_ustr("CFrame")), object.properties.get(hstr!("CFrame")),
object.properties.get(&static_ustr("Size")), object.properties.get(hstr!("Size")),
object.properties.get(&static_ustr("Velocity")), object.properties.get(hstr!("Velocity")),
object.properties.get(&static_ustr("Transparency")), object.properties.get(hstr!("Transparency")),
object.properties.get(&static_ustr("Color")), object.properties.get(hstr!("Color")),
object.properties.get(&static_ustr("CanCollide")), object.properties.get(hstr!("CanCollide")),
) )
{ {
let model_transform=planar64_affine3_from_roblox(cf,size); let model_transform=planar64_affine3_from_roblox(cf,size);
@@ -578,7 +569,7 @@ pub fn convert<'a>(
//TODO: also detect "CylinderMesh" etc here //TODO: also detect "CylinderMesh" etc here
let shape=match object.class.as_str(){ let shape=match object.class.as_str(){
"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get(&static_ustr("Shape")){ "Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get(hstr!("Shape")){
Shape::Primitive(shape.to_u32().try_into().expect("Funky roblox PartType")) Shape::Primitive(shape.to_u32().try_into().expect("Funky roblox PartType"))
}else{ }else{
panic!("Part has no Shape!"); panic!("Part has no Shape!");
@@ -652,11 +643,11 @@ pub fn convert<'a>(
Some(rbx_dom_weak::types::Variant::Content(texture_content)), Some(rbx_dom_weak::types::Variant::Content(texture_content)),
)=( )=(
// mesh must exist // mesh must exist
object.properties.get(&static_ustr("MeshContent")), object.properties.get(hstr!("MeshContent")),
// texture is allowed to be none // texture is allowed to be none
object.properties.get(&static_ustr("TextureContent")), object.properties.get(hstr!("TextureContent")),
){ ){
let mesh_asset_id=get_content_url(mesh_content).unwrap_or_default(); let mesh_asset_id=get_content_url(mesh_content).expect("MeshPart Mesh is not a Uri");
let texture_asset_id=get_content_url(texture_content); let texture_asset_id=get_content_url(texture_content);
( (
MeshAvailability::DeferredMesh(render_config_deferred_loader.acquire_render_config_id(texture_asset_id)), MeshAvailability::DeferredMesh(render_config_deferred_loader.acquire_render_config_id(texture_asset_id)),
@@ -669,13 +660,13 @@ pub fn convert<'a>(
let mut content=""; let mut content="";
let mut mesh_data:&[u8]=&[]; let mut mesh_data:&[u8]=&[];
let mut physics_data:&[u8]=&[]; let mut physics_data:&[u8]=&[];
if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get(&static_ustr("AssetId")){ if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get(hstr!("AssetId")){
content=asset_id.as_ref(); content=asset_id.as_ref();
} }
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&static_ustr("MeshData")){ if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(hstr!("MeshData")){
mesh_data=data.as_ref(); mesh_data=data.as_ref();
} }
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&static_ustr("PhysicsData")){ if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(hstr!("PhysicsData")){
physics_data=data.as_ref(); physics_data=data.as_ref();
} }
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size); let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size);
@@ -715,23 +706,23 @@ pub fn convert<'a>(
deferred_unions_deferred_attributes, deferred_unions_deferred_attributes,
} }
} }
struct MeshIdWithSize{ struct MeshWithAabb{
mesh:model::MeshId, mesh:model::Mesh,
size:Planar64Vec3, aabb:Aabb,
} }
fn acquire_mesh_id_from_render_config_id<'a>( fn acquire_mesh_id_from_render_config_id<'a>(
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:&'a HashMap<model::MeshId,MeshWithAabb>,
old_mesh_id:model::MeshId, old_mesh_id:model::MeshId,
render:RenderConfigId, render:RenderConfigId,
)->Option<MeshIdWithSize>{ )->Option<(model::MeshId,&'a Aabb)>{
//ignore meshes that fail to load completely for now //ignore meshes that fail to load completely for now
loaded_meshes.get(&old_mesh_id).map(|&MeshWithSize{ref mesh,size}|MeshIdWithSize{ loaded_meshes.get(&old_mesh_id).map(|mesh_with_aabb|(
mesh:*mesh_id_from_render_config_id.entry(old_mesh_id).or_insert_with(||HashMap::new()) *mesh_id_from_render_config_id.entry(old_mesh_id).or_insert_with(||HashMap::new())
.entry(render).or_insert_with(||{ .entry(render).or_insert_with(||{
let mesh_id=model::MeshId::new(primitive_meshes.len() as u32); let mesh_id=model::MeshId::new(primitive_meshes.len() as u32);
let mut mesh_clone=mesh.clone(); let mut mesh_clone=mesh_with_aabb.mesh.clone();
//set the render group lool //set the render group lool
if let Some(graphics_group)=mesh_clone.graphics_groups.first_mut(){ if let Some(graphics_group)=mesh_clone.graphics_groups.first_mut(){
graphics_group.render=render; graphics_group.render=render;
@@ -739,22 +730,22 @@ fn acquire_mesh_id_from_render_config_id<'a>(
primitive_meshes.push(mesh_clone); primitive_meshes.push(mesh_clone);
mesh_id mesh_id
}), }),
size, &mesh_with_aabb.aabb,
}) ))
} }
fn acquire_union_id_from_render_config_id<'a>( fn acquire_union_id_from_render_config_id<'a>(
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:&'a HashMap<model::MeshId,MeshWithAabb>,
old_union_id:model::MeshId, old_union_id:model::MeshId,
part_texture_description:RobloxPartDescription, part_texture_description:RobloxPartDescription,
)->Option<MeshIdWithSize>{ )->Option<(model::MeshId,&'a Aabb)>{
//ignore uniones that fail to load completely for now //ignore uniones that fail to load completely for now
loaded_meshes.get(&old_union_id).map(|&MeshWithSize{ref mesh,size}|MeshIdWithSize{ loaded_meshes.get(&old_union_id).map(|union_with_aabb|(
mesh:*union_id_from_render_config_id.entry(old_union_id).or_insert_with(||HashMap::new()) *union_id_from_render_config_id.entry(old_union_id).or_insert_with(||HashMap::new())
.entry(part_texture_description.clone()).or_insert_with(||{ .entry(part_texture_description.clone()).or_insert_with(||{
let union_id=model::MeshId::new(primitive_meshes.len() as u32); let union_id=model::MeshId::new(primitive_meshes.len() as u32);
let mut union_clone=mesh.clone(); let mut union_clone=union_with_aabb.mesh.clone();
//set the render groups //set the render groups
for (graphics_group,maybe_face_texture_description) in union_clone.graphics_groups.iter_mut().zip(part_texture_description.0){ for (graphics_group,maybe_face_texture_description) in union_clone.graphics_groups.iter_mut().zip(part_texture_description.0){
if let Some(face_texture_description)=maybe_face_texture_description{ if let Some(face_texture_description)=maybe_face_texture_description{
@@ -764,8 +755,8 @@ fn acquire_union_id_from_render_config_id<'a>(
primitive_meshes.push(union_clone); primitive_meshes.push(union_clone);
union_id union_id
}), }),
size, &union_with_aabb.aabb,
}) ))
} }
pub struct PartialMap1<'a>{ pub struct PartialMap1<'a>{
primitive_meshes:Vec<model::Mesh>, primitive_meshes:Vec<model::Mesh>,
@@ -776,7 +767,7 @@ pub struct PartialMap1<'a>{
impl PartialMap1<'_>{ impl PartialMap1<'_>{
pub fn add_meshpart_meshes_and_calculate_attributes( pub fn add_meshpart_meshes_and_calculate_attributes(
mut self, mut self,
meshpart_meshes:Meshes<MeshWithSize>, meshpart_meshes:Meshes,
)->PartialMap2{ )->PartialMap2{
//calculate attributes //calculate attributes
let mut modes_builder=ModesBuilder::default(); let mut modes_builder=ModesBuilder::default();
@@ -788,8 +779,17 @@ impl PartialMap1<'_>{
//decode roblox meshes //decode roblox meshes
//generate mesh_id_map based on meshes that failed to load //generate mesh_id_map based on meshes that failed to load
let loaded_meshes:HashMap<model::MeshId,MeshWithSize>= let loaded_meshes:HashMap<model::MeshId,MeshWithAabb>=
meshpart_meshes.consume().collect(); meshpart_meshes.consume().map(|(old_mesh_id,mesh)|{
let mut aabb=strafesnet_common::aabb::Aabb::default();
for &pos in &mesh.unique_pos{
aabb.grow(pos);
}
(old_mesh_id,MeshWithAabb{
mesh,
aabb,
})
}).collect();
// SAFETY: I have no idea what I'm doing and this is definitely unsound in some subtle way // SAFETY: I have no idea what I'm doing and this is definitely unsound in some subtle way
// I just want to chain iterators together man // I just want to chain iterators together man
@@ -802,22 +802,23 @@ impl PartialMap1<'_>{
self.deferred_models_deferred_attributes.into_iter().flat_map(|deferred_model_deferred_attributes|{ self.deferred_models_deferred_attributes.into_iter().flat_map(|deferred_model_deferred_attributes|{
//meshes need to be cloned from loaded_meshes with a new id when they are used with a new render_id //meshes need to be cloned from loaded_meshes with a new id when they are used with a new render_id
//insert into primitive_meshes //insert into primitive_meshes
let MeshIdWithSize{mesh,size:mesh_size}=acquire_mesh_id_from_render_config_id( let (mesh,aabb)=acquire_mesh_id_from_render_config_id(
unsafe{*aint_no_way.get()}, unsafe{*aint_no_way.get()},
&mut mesh_id_from_render_config_id, &mut mesh_id_from_render_config_id,
&loaded_meshes, &loaded_meshes,
deferred_model_deferred_attributes.model.mesh, deferred_model_deferred_attributes.model.mesh,
deferred_model_deferred_attributes.render deferred_model_deferred_attributes.render
)?; )?;
let size=aabb.size();
Some(ModelDeferredAttributes{ Some(ModelDeferredAttributes{
mesh, mesh,
deferred_attributes:deferred_model_deferred_attributes.model.deferred_attributes, deferred_attributes:deferred_model_deferred_attributes.model.deferred_attributes,
color:deferred_model_deferred_attributes.model.color, color:deferred_model_deferred_attributes.model.color,
transform:Planar64Affine3::new( transform:Planar64Affine3::new(
Planar64Mat3::from_cols([ Planar64Mat3::from_cols([
(deferred_model_deferred_attributes.model.transform.matrix3.x_axis*2/mesh_size.x).divide().narrow_1().unwrap(), (deferred_model_deferred_attributes.model.transform.matrix3.x_axis*2/size.x).divide().narrow_1().unwrap(),
(deferred_model_deferred_attributes.model.transform.matrix3.y_axis*2/mesh_size.y).divide().narrow_1().unwrap(), (deferred_model_deferred_attributes.model.transform.matrix3.y_axis*2/size.y).divide().narrow_1().unwrap(),
(deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/mesh_size.z).divide().narrow_1().unwrap(), (deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().narrow_1().unwrap(),
]), ]),
deferred_model_deferred_attributes.model.transform.translation deferred_model_deferred_attributes.model.transform.translation
), ),
@@ -825,13 +826,14 @@ impl PartialMap1<'_>{
}).chain(self.deferred_unions_deferred_attributes.into_iter().flat_map(|deferred_union_deferred_attributes|{ }).chain(self.deferred_unions_deferred_attributes.into_iter().flat_map(|deferred_union_deferred_attributes|{
//meshes need to be cloned from loaded_meshes with a new id when they are used with a new render_id //meshes need to be cloned from loaded_meshes with a new id when they are used with a new render_id
//insert into primitive_meshes //insert into primitive_meshes
let MeshIdWithSize{mesh,size}=acquire_union_id_from_render_config_id( let (mesh,aabb)=acquire_union_id_from_render_config_id(
unsafe{*aint_no_way.get()}, unsafe{*aint_no_way.get()},
&mut union_id_from_render_config_id, &mut union_id_from_render_config_id,
&loaded_meshes, &loaded_meshes,
deferred_union_deferred_attributes.model.mesh, deferred_union_deferred_attributes.model.mesh,
deferred_union_deferred_attributes.render deferred_union_deferred_attributes.render
)?; )?;
let size=aabb.size();
Some(ModelDeferredAttributes{ Some(ModelDeferredAttributes{
mesh, mesh,
deferred_attributes:deferred_union_deferred_attributes.model.deferred_attributes, deferred_attributes:deferred_union_deferred_attributes.model.deferred_attributes,

View File

@@ -1,8 +1,4 @@
use crate::loader::MeshWithSize; use rbx_mesh::mesh_data::{NormalId2 as MeshDataNormalId2,VertexId as MeshDataVertexId};
use crate::rbx::RobloxPartDescription;
use crate::primitives::{CUBE_DEFAULT_VERTICES,CUBE_DEFAULT_POLYS};
use rbx_mesh::mesh_data::{VertexId as MeshDataVertexId,NormalId2 as MeshDataNormalId2};
use rbx_mesh::physics_data::VertexId as PhysicsDataVertexId; use rbx_mesh::physics_data::VertexId as PhysicsDataVertexId;
use strafesnet_common::model::{self,IndexedVertex,PolygonGroup,PolygonGroupId,PolygonList,RenderConfigId}; use strafesnet_common::model::{self,IndexedVertex,PolygonGroup,PolygonGroupId,PolygonList,RenderConfigId};
use strafesnet_common::integer::vec3; use strafesnet_common::integer::vec3;
@@ -61,10 +57,10 @@ pub fn convert(
roblox_physics_data:&[u8], roblox_physics_data:&[u8],
roblox_mesh_data:&[u8], roblox_mesh_data:&[u8],
size:glam::Vec3, size:glam::Vec3,
RobloxPartDescription(part_texture_description):RobloxPartDescription, crate::rbx::RobloxPartDescription(part_texture_description):crate::rbx::RobloxPartDescription,
)->Result<MeshWithSize,Error>{ )->Result<model::Mesh,Error>{
const NORMAL_FACES:usize=6; const NORMAL_FACES:usize=6;
let mut polygon_groups_normal_id:[_;NORMAL_FACES]=[vec![],vec![],vec![],vec![],vec![],vec![]]; let mut polygon_groups_normal_id=vec![Vec::new();NORMAL_FACES];
// build graphics and physics meshes // build graphics and physics meshes
let mut mb=strafesnet_common::model::MeshBuilder::new(); let mut mb=strafesnet_common::model::MeshBuilder::new();
@@ -84,15 +80,6 @@ pub fn convert(
rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::CSGMDL2(mesh_data2))=>mesh_data2.mesh, rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::CSGMDL2(mesh_data2))=>mesh_data2.mesh,
rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::CSGMDL4(mesh_data4))=>mesh_data4.mesh, rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::CSGMDL4(mesh_data4))=>mesh_data4.mesh,
}; };
//autoscale to size, idk what roblox is doing with the graphics mesh size
let mut pos_min=glam::Vec3::MAX;
let mut pos_max=glam::Vec3::MIN;
for vertex in &graphics_mesh.vertices{
let p=vertex.pos.into();
pos_min=pos_min.min(p);
pos_max=pos_max.max(p);
}
let graphics_size=pos_max-pos_min;
for [MeshDataVertexId(vertex_id0),MeshDataVertexId(vertex_id1),MeshDataVertexId(vertex_id2)] in graphics_mesh.faces{ for [MeshDataVertexId(vertex_id0),MeshDataVertexId(vertex_id1),MeshDataVertexId(vertex_id2)] in graphics_mesh.faces{
let face=[ let face=[
graphics_mesh.vertices.get(vertex_id0 as usize).ok_or(Error::MissingVertexId(vertex_id0))?, graphics_mesh.vertices.get(vertex_id0 as usize).ok_or(Error::MissingVertexId(vertex_id0))?,
@@ -102,8 +89,7 @@ pub fn convert(
let mut normal_agreement_checker=MeshDataNormalChecker::new(); let mut normal_agreement_checker=MeshDataNormalChecker::new();
let face=face.into_iter().map(|vertex|{ let face=face.into_iter().map(|vertex|{
normal_agreement_checker.check(vertex.normal_id); normal_agreement_checker.check(vertex.normal_id);
let pos=glam::Vec3::from_array(vertex.pos)/graphics_size; let pos=mb.acquire_pos_id(vec3::try_from_f32_array(vertex.pos)?);
let pos=mb.acquire_pos_id(vec3::try_from_f32_array(pos.to_array())?);
let normal=mb.acquire_normal_id(vec3::try_from_f32_array(vertex.norm)?); let normal=mb.acquire_normal_id(vec3::try_from_f32_array(vertex.norm)?);
let tex_coord=glam::Vec2::from_array(vertex.tex); let tex_coord=glam::Vec2::from_array(vertex.tex);
let maybe_face_description=&cube_face_description[vertex.normal_id as usize-1]; let maybe_face_description=&cube_face_description[vertex.normal_id as usize-1];
@@ -140,11 +126,7 @@ pub fn convert(
}; };
//physics //physics
let polygon_groups_normal_it=polygon_groups_normal_id.into_iter().map(|faces| let physics_convex_meshes=if !roblox_physics_data.is_empty(){
// graphics polygon groups (to be rendered)
Ok(PolygonGroup::PolygonList(PolygonList::new(faces)))
);
let polygon_groups:Vec<PolygonGroup>=if !roblox_physics_data.is_empty(){
let physics_data=rbx_mesh::read_physics_data_versioned( let physics_data=rbx_mesh::read_physics_data_versioned(
std::io::Cursor::new(roblox_physics_data) std::io::Cursor::new(roblox_physics_data)
).map_err(Error::RobloxPhysicsData)?; ).map_err(Error::RobloxPhysicsData)?;
@@ -158,48 +140,39 @@ pub fn convert(
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::PhysicsInfoMesh(pim)) rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::PhysicsInfoMesh(pim))
=>vec![pim.mesh], =>vec![pim.mesh],
}; };
let physics_convex_meshes_it=physics_convex_meshes.into_iter().map(|mesh|{ physics_convex_meshes
// this can be factored out of the loop but I am lazy
let color=mb.acquire_color_id(glam::Vec4::ONE);
let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
// physics polygon groups (to do physics)
Ok(PolygonGroup::PolygonList(PolygonList::new(mesh.faces.into_iter().map(|[PhysicsDataVertexId(vertex_id0),PhysicsDataVertexId(vertex_id1),PhysicsDataVertexId(vertex_id2)]|{
let face=[
mesh.vertices.get(vertex_id0 as usize).ok_or(Error::MissingVertexId(vertex_id0))?,
mesh.vertices.get(vertex_id1 as usize).ok_or(Error::MissingVertexId(vertex_id1))?,
mesh.vertices.get(vertex_id2 as usize).ok_or(Error::MissingVertexId(vertex_id2))?,
].map(|v|glam::Vec3::from_slice(v)/size);
let vertex_norm=(face[1]-face[0])
.cross(face[2]-face[0]);
let normal=mb.acquire_normal_id(vec3::try_from_f32_array(vertex_norm.to_array()).map_err(Error::Planar64Vec3)?);
face.into_iter().map(|vertex_pos|{
let pos=mb.acquire_pos_id(vec3::try_from_f32_array(vertex_pos.to_array()).map_err(Error::Planar64Vec3)?);
Ok(mb.acquire_vertex_id(IndexedVertex{pos,tex,normal,color}))
}).collect()
}).collect::<Result<_,_>>()?)))
});
polygon_groups_normal_it.chain(physics_convex_meshes_it).collect::<Result<_,_>>()?
}else{ }else{
// generate a unit cube as default physics Vec::new()
let pos_list=CUBE_DEFAULT_VERTICES.map(|pos|mb.acquire_pos_id(pos>>1));
let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
let normal=mb.acquire_normal_id(vec3::ZERO);
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,_]|
mb.acquire_vertex_id(IndexedVertex{pos:pos_list[pos_id as usize],tex,normal,color})
).to_vec()).to_vec()));
polygon_groups_normal_it.chain([Ok(polygon_group)]).collect::<Result<_,_>>()?
}; };
let polygon_groups:Vec<PolygonGroup>=polygon_groups_normal_id.into_iter().map(|faces|
// graphics polygon groups (to be rendered)
Ok(PolygonGroup::PolygonList(PolygonList::new(faces)))
).chain(physics_convex_meshes.into_iter().map(|mesh|{
// this can be factored out of the loop but I am lazy
let color=mb.acquire_color_id(glam::Vec4::ONE);
let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
// physics polygon groups (to do physics)
Ok(PolygonGroup::PolygonList(PolygonList::new(mesh.faces.into_iter().map(|[PhysicsDataVertexId(vertex_id0),PhysicsDataVertexId(vertex_id1),PhysicsDataVertexId(vertex_id2)]|{
let face=[
mesh.vertices.get(vertex_id0 as usize).ok_or(Error::MissingVertexId(vertex_id0))?,
mesh.vertices.get(vertex_id1 as usize).ok_or(Error::MissingVertexId(vertex_id1))?,
mesh.vertices.get(vertex_id2 as usize).ok_or(Error::MissingVertexId(vertex_id2))?,
].map(|v|glam::Vec3::from_slice(v)/size);
let vertex_norm=(face[1]-face[0])
.cross(face[2]-face[0]);
let normal=mb.acquire_normal_id(vec3::try_from_f32_array(vertex_norm.to_array()).map_err(Error::Planar64Vec3)?);
face.into_iter().map(|vertex_pos|{
let pos=mb.acquire_pos_id(vec3::try_from_f32_array(vertex_pos.to_array()).map_err(Error::Planar64Vec3)?);
Ok(mb.acquire_vertex_id(IndexedVertex{pos,tex,normal,color}))
}).collect()
}).collect::<Result<_,_>>()?)))
})).collect::<Result<_,_>>()?;
let physics_groups=(NORMAL_FACES..polygon_groups.len()).map(|id|model::IndexedPhysicsGroup{ let physics_groups=(NORMAL_FACES..polygon_groups.len()).map(|id|model::IndexedPhysicsGroup{
groups:vec![PolygonGroupId::new(id as u32)] groups:vec![PolygonGroupId::new(id as u32)]
}).collect(); }).collect();
let mesh=mb.build( Ok(mb.build(
polygon_groups, polygon_groups,
graphics_groups, graphics_groups,
physics_groups, physics_groups,
); ))
Ok(MeshWithSize{
mesh,
size:vec3::ONE,
})
} }

View File

@@ -8,14 +8,14 @@ description = "Run embedded Luau scripts which manipulate the DOM."
authors = ["Rhys Lloyd <krakow20@gmail.com>"] authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[features] [features]
default=["run-service"] default=[]
run-service=[] run-service=[]
[dependencies] [dependencies]
glam = "0.30.0" glam = "0.30.0"
mlua = { version = "0.10.1", features = ["luau"] } mlua = { version = "0.10.1", features = ["luau"] }
phf = { version = "0.11.2", features = ["macros"] } phf = { version = "0.11.2", features = ["macros"] }
rbx_dom_weak = { version = "3.1.0-sn4", registry = "strafesnet", features = ["instance-userdata"] } rbx_dom_weak = { path = "../../../../git/rbx-dom/rbx_dom_weak", registry = "strafesnet" }
rbx_reflection = "5.0.0" rbx_reflection = { path = "../../../../git/rbx-dom/rbx_reflection" }
rbx_reflection_database = "1.0.0" rbx_reflection_database = { path = "../../../../git/rbx-dom/rbx_reflection_database" }
rbx_types = "2.0.0" rbx_types = { path = "../../../../git/rbx-dom/rbx_types" }

View File

@@ -1,5 +1,4 @@
use crate::util::static_ustr; use rbx_dom_weak::{hstr,types::Ref,InstanceBuilder,WeakDom,UnhashedStr};
use rbx_dom_weak::{types::Ref,InstanceBuilder,WeakDom};
#[derive(Debug)] #[derive(Debug)]
pub enum ServicesError{ pub enum ServicesError{
@@ -28,36 +27,36 @@ impl Services{
} }
} }
pub type LuaAppData=&'static mut WeakDom; pub type LuaAppData=&'static mut WeakDom<'static>;
pub struct Context{ pub struct Context<'a>{
pub(crate)dom:WeakDom, pub(crate)dom:WeakDom<'a>,
pub(crate)services:Services, pub(crate)services:Services,
} }
impl Context{ impl<'a> Context<'a>{
pub fn from_place(dom:WeakDom)->Result<Context,ServicesError>{ pub fn from_place(dom:WeakDom<'a>)->Result<Context<'a>,ServicesError>{
let services=Services::find_services(&dom)?; let services=Services::find_services(&dom)?;
Ok(Self{dom,services}) Ok(Self{dom,services})
} }
pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance){ pub fn script_singleton(source:String)->(Context<'a>,crate::runner::instance::Instance){
let script=InstanceBuilder::new("Script") let script=InstanceBuilder::new(hstr!("Script"))
.with_property("Source",rbx_types::Variant::String(source)); .with_property(hstr!("Source"),rbx_types::Variant::String(source));
let script_ref=script.referent(); let script_ref=script.referent();
let dom=WeakDom::new( let dom=WeakDom::new(
InstanceBuilder::new("DataModel") InstanceBuilder::new(hstr!("DataModel"))
.with_child(script) .with_child(script)
); );
let context=Self::from_model(dom); let context=Self::from_model(dom);
(context,crate::runner::instance::Instance::new_unchecked(script_ref)) (context,crate::runner::instance::Instance::new_unchecked(script_ref))
} }
/// 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<'b>(&'b self,superclass:&'b str)->impl Iterator<Item=Ref>+'b{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get();
let Some(superclass)=db.classes.get(superclass)else{ let Some(superclass)=db.classes.get(UnhashedStr::from_ref(superclass))else{
panic!("Invalid class"); panic!("Invalid class");
}; };
self.dom.descendants().filter_map(|instance|{ self.dom.descendants().filter_map(|instance|{
let class=db.classes.get(instance.class.as_str())?; let class=db.classes.get(instance.class)?;
db.has_superclass(class,superclass).then(||instance.referent()) db.has_superclass(class,superclass).then(||instance.referent())
}) })
} }
@@ -65,7 +64,7 @@ impl Context{
self.superclass_iter("Script") self.superclass_iter("Script")
.filter_map(|script_ref|{ .filter_map(|script_ref|{
let script=self.dom.get_by_ref(script_ref)?; let script=self.dom.get_by_ref(script_ref)?;
if let None|Some(rbx_dom_weak::types::Variant::Bool(false))=script.properties.get(&static_ustr("Disabled")){ if let None|Some(rbx_dom_weak::types::Variant::Bool(false))=script.properties.get(hstr!("Disabled")){
return Some(crate::runner::instance::Instance::new_unchecked(script_ref)); return Some(crate::runner::instance::Instance::new_unchecked(script_ref));
} }
None None
@@ -73,17 +72,17 @@ impl Context{
.collect() .collect()
} }
pub fn from_model(mut dom:WeakDom)->Context{ pub fn from_model(mut dom:WeakDom<'a>)->Context<'a>{
//snapshot root instances //snapshot root instances
let children=dom.root().children().to_owned(); let children=dom.root().children().to_owned();
//insert services //insert services
let game=dom.root_ref(); let game=dom.root_ref();
let terrain_bldr=InstanceBuilder::new("Terrain"); let terrain_bldr=InstanceBuilder::new(hstr!("Terrain"));
let workspace=dom.insert(game, let workspace=dom.insert(game,
InstanceBuilder::new("Workspace") InstanceBuilder::new(hstr!("Workspace"))
//Set Workspace.Terrain property equal to Terrain //Set Workspace.Terrain property equal to Terrain
.with_property("Terrain",terrain_bldr.referent()) .with_property(hstr!("Terrain"),terrain_bldr.referent())
.with_child(terrain_bldr) .with_child(terrain_bldr)
); );
@@ -96,18 +95,18 @@ impl Context{
//Lowercase and upper case workspace property! //Lowercase and upper case workspace property!
let game=dom.root_mut(); let game=dom.root_mut();
// TODO: DELETE THIS! // TODO: DELETE THIS!
game.properties.insert(static_ustr("workspace"),rbx_types::Variant::Ref(workspace)); game.properties.insert(hstr!("workspace"),rbx_types::Variant::Ref(workspace));
game.properties.insert(static_ustr("Workspace"),rbx_types::Variant::Ref(workspace)); game.properties.insert(hstr!("Workspace"),rbx_types::Variant::Ref(workspace));
} }
dom.insert(game,InstanceBuilder::new("Lighting")); dom.insert(game,InstanceBuilder::new(hstr!("Lighting")));
let services=Services{game,workspace}; let services=Services{game,workspace};
Self{dom,services} Self{dom,services}
} }
} }
impl AsRef<WeakDom> for Context{ impl<'a> AsRef<WeakDom<'a>> for Context<'a>{
fn as_ref(&self)->&WeakDom{ fn as_ref(&self)->&WeakDom<'a>{
&self.dom &self.dom
} }
} }

View File

@@ -1,4 +1,3 @@
mod util;
pub mod runner; pub mod runner;
pub mod context; pub mod context;
#[cfg(feature="run-service")] #[cfg(feature="run-service")]

View File

@@ -1,11 +1,13 @@
use rbx_dom_weak::{HashStr, UnhashedStr};
#[derive(Clone,Copy)] #[derive(Clone,Copy)]
pub struct EnumItem<'a>{ pub struct EnumItem<'a>{
name:Option<&'a str>, name:Option<&'a HashStr>,
value:u32, value:u32,
} }
impl<'a> EnumItem<'a>{ impl<'a> EnumItem<'a>{
fn known_name((name,&value):(&'a std::borrow::Cow<'a,str>,&u32))->Self{ fn known_name((name,&value):(&&'a HashStr,&u32))->Self{
Self{name:Some(name.as_ref()),value} Self{name:Some(name),value}
} }
} }
impl<'a> From<rbx_types::Enum> for EnumItem<'a>{ impl<'a> From<rbx_types::Enum> for EnumItem<'a>{
@@ -38,7 +40,7 @@ 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();
db.enums.get(index).map(|ed|EnumItems{ed}) db.enums.get(UnhashedStr::from_ref(index)).map(|ed|EnumItems{ed})
} }
} }
#[derive(Clone,Copy)] #[derive(Clone,Copy)]
@@ -51,7 +53,7 @@ impl<'a> EnumItems<'a>{
self.ed.items.iter().find(|&(_,&v)|v==value).map(EnumItem::known_name) self.ed.items.iter().find(|&(_,&v)|v==value).map(EnumItem::known_name)
} }
pub fn from_name(&self,name:&str)->Option<EnumItem<'a>>{ pub fn from_name(&self,name:&str)->Option<EnumItem<'a>>{
self.ed.items.get_key_value(name).map(EnumItem::known_name) self.ed.items.get_key_value(UnhashedStr::from_ref(name)).map(EnumItem::known_name)
} }
pub fn from_enum(&self,enum_item:EnumItem)->Option<EnumItem<'a>>{ pub fn from_enum(&self,enum_item:EnumItem)->Option<EnumItem<'a>>{
match enum_item.name{ match enum_item.name{
@@ -105,7 +107,7 @@ impl mlua::UserData for EnumItems<'static>{
}); });
methods.add_meta_function(mlua::MetaMethod::Index,|_,(this,val):(EnumItems,mlua::String)|{ methods.add_meta_function(mlua::MetaMethod::Index,|_,(this,val):(EnumItems,mlua::String)|{
let index=&*val.to_str()?; let index=&*val.to_str()?;
Ok(this.ed.items.get_key_value(index).map(EnumItem::known_name)) Ok(this.ed.items.get_key_value(UnhashedStr::from_ref(index)).map(EnumItem::known_name))
}); });
} }
} }
@@ -124,7 +126,7 @@ type_from_lua_userdata!(Enums);
impl mlua::UserData for EnumItem<'_>{ impl mlua::UserData for EnumItem<'_>{
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){ fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
fields.add_field_method_get("Name",|_,this|Ok(this.name)); fields.add_field_method_get("Name",|_,this|Ok(this.name.map(|s|s.as_str())));
fields.add_field_method_get("Value",|_,this|Ok(this.value)); fields.add_field_method_get("Value",|_,this|Ok(this.value));
} }
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){ fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){

View File

@@ -2,9 +2,8 @@ use std::collections::{hash_map::Entry,HashMap};
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti}; use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
use rbx_types::Ref; use rbx_types::Ref;
use rbx_dom_weak::{Ustr,InstanceBuilder,WeakDom}; use rbx_dom_weak::{hstr,HashStr,UnhashedStr,InstanceBuilder,WeakDom};
use crate::util::static_ustr;
use crate::runner::vector3::Vector3; use crate::runner::vector3::Vector3;
use crate::runner::number::Number; use crate::runner::number::Number;
@@ -38,7 +37,7 @@ 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();
let (Some(class),Some(superclass))=(db.classes.get(class),db.classes.get(superclass))else{ let (Some(class),Some(superclass))=(db.classes.get(UnhashedStr::from_ref(class)),db.classes.get(UnhashedStr::from_ref(superclass)))else{
return false; return false;
}; };
db.has_superclass(class,superclass) db.has_superclass(class,superclass)
@@ -57,7 +56,7 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),mlua::Error>{ pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),mlua::Error>{
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=script.get(dom)?; let instance=script.get(dom)?;
let source=match instance.properties.get(&static_ustr("Source")){ let source=match instance.properties.get(hstr!("Source")){
Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(), Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
_=>Err(mlua::Error::external("Missing script.Source"))?, _=>Err(mlua::Error::external("Missing script.Source"))?,
}; };
@@ -65,32 +64,32 @@ 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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,name:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,name:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,class:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,class:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,superclass:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get();
let superclass_descriptor=db.classes.get(superclass)?; let superclass_descriptor=db.classes.get(UnhashedStr::from_ref(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).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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,superclass:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get();
let superclass_descriptor=db.classes.get(superclass)?; let superclass_descriptor=db.classes.get(UnhashedStr::from_ref(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).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
}) })
} }
@@ -105,10 +104,10 @@ impl Instance{
pub fn new(referent:Ref)->Option<Self>{ pub fn new(referent:Ref)->Option<Self>{
referent.is_some().then_some(Self{referent}) referent.is_some().then_some(Self{referent})
} }
pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{ pub fn get<'a>(&self,dom:&'a WeakDom<'a>)->mlua::Result<&'a rbx_dom_weak::Instance<'a>>{
dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing")) dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
} }
pub fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{ pub fn get_mut<'a,'b>(&self,dom:&'a mut WeakDom<'b>)->mlua::Result<&'a mut rbx_dom_weak::Instance<'b>>{
dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing")) dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
} }
} }
@@ -150,7 +149,7 @@ impl mlua::UserData for Instance{
fields.add_field_method_get("ClassName",|lua,this|{ fields.add_field_method_get("ClassName",|lua,this|{
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?;
Ok(instance.class.to_owned()) Ok(instance.class.as_str().to_owned())
}) })
}); });
} }
@@ -283,7 +282,7 @@ impl mlua::UserData for Instance{
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();
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?; let class=db.classes.get(instance.class).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
// a property does not exist in any runtime instance // a property does not exist in any runtime instance
@@ -342,7 +341,7 @@ impl mlua::UserData for Instance{
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();
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?; let class=db.classes.get(instance.class).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)
).ok_or_else(|| ).ok_or_else(||
@@ -560,7 +559,7 @@ fn class_methods_store_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut ClassMethodsS
/// A virtual property pointer definition shorthand. /// A virtual property pointer definition shorthand.
type VirtualPropertyFunctionPointer=fn(&rbx_types::Variant)->Option<rbx_types::Variant>; type VirtualPropertyFunctionPointer=fn(&rbx_types::Variant)->Option<rbx_types::Variant>;
const fn vpp( const fn vpp(
property:&'static str, property:&'static HashStr,
pointer:VirtualPropertyFunctionPointer, pointer:VirtualPropertyFunctionPointer,
)->VirtualProperty{ )->VirtualProperty{
VirtualProperty{ VirtualProperty{
@@ -569,7 +568,7 @@ const fn vpp(
} }
} }
struct VirtualProperty{ struct VirtualProperty{
property:&'static str,// Source property name property:&'static HashStr,// Source property name
pointer:VirtualPropertyFunctionPointer, pointer:VirtualPropertyFunctionPointer,
} }
type VPD=phf::Map<&'static str,// Class name type VPD=phf::Map<&'static str,// Class name
@@ -577,9 +576,10 @@ type VPD=phf::Map<&'static str,// Class name
VirtualProperty VirtualProperty
> >
>; >;
static CFRAME:&HashStr=hstr!("CFrame");
static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
"BasePart"=>phf::phf_map!{ "BasePart"=>phf::phf_map!{
"Position"=>vpp("CFrame",|c:&rbx_types::Variant|{ "Position"=>vpp(CFRAME,|c:&rbx_types::Variant|{
let c=match c{ let c=match c{
rbx_types::Variant::CFrame(c)=>c, rbx_types::Variant::CFrame(c)=>c,
_=>return None,//fail silently and ungracefully _=>return None,//fail silently and ungracefully
@@ -590,7 +590,7 @@ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
}; };
fn find_virtual_property( fn find_virtual_property(
properties:&rbx_dom_weak::UstrMap<rbx_types::Variant>, properties:&rbx_dom_weak::HashStrMap<rbx_types::Variant>,
class:&rbx_reflection::ClassDescriptor, class:&rbx_reflection::ClassDescriptor,
index:&str, index:&str,
)->Option<rbx_types::Variant>{ )->Option<rbx_types::Variant>{
@@ -599,7 +599,7 @@ fn find_virtual_property(
let virtual_property=class_virtual_properties.get(index)?; let virtual_property=class_virtual_properties.get(index)?;
//Get source property //Get source property
let variant=properties.get(&static_ustr(virtual_property.property))?; let variant=properties.get(virtual_property.property)?;
//Transform Source property with provided function //Transform Source property with provided function
(virtual_property.pointer)(variant) (virtual_property.pointer)(variant)
@@ -641,16 +641,15 @@ static LAZY_USER_DATA:LUD=phf::phf_map!{
fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{ fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
let db=rbx_reflection_database::get(); let db=rbx_reflection_database::get();
let Some(class)=db.classes.get(instance.class.as_str())else{ let Some(class)=db.classes.get(instance.class)else{
return Ok(None) return Ok(None)
}; };
if let Some((&static_str,create_userdata))=db.superclasses_iter(class).find_map(|superclass| if let Some((hstr,create_userdata))=db.superclasses_iter(class).find_map(|superclass|
// find pair (class,index) // find pair (class,index)
LAZY_USER_DATA.get(&superclass.name) LAZY_USER_DATA.get(&superclass.name)
.and_then(|map|map.get_entry(index)) .and_then(|map|map.get(index)).map(|f|(superclass.name,f))
){ ){
let index_ustr=static_ustr(static_str); return Ok(Some(match instance.userdata.entry(hstr){
return Ok(Some(match instance.userdata.entry(index_ustr){
Entry::Occupied(entry)=>entry.get().clone(), Entry::Occupied(entry)=>entry.get().clone(),
Entry::Vacant(entry)=>entry.insert(create_userdata(lua)?).clone(), Entry::Vacant(entry)=>entry.insert(create_userdata(lua)?).clone(),
})); }));

View File

@@ -1,5 +1,4 @@
use crate::context::Context; use crate::context::Context;
use crate::util::static_ustr;
#[cfg(feature="run-service")] #[cfg(feature="run-service")]
use crate::scheduler::scheduler_mut; use crate::scheduler::scheduler_mut;
@@ -59,7 +58,7 @@ impl Runner{
init(&runner.lua).map_err(Error::RustLua)?; init(&runner.lua).map_err(Error::RustLua)?;
Ok(runner) Ok(runner)
} }
pub fn runnable_context<'a>(self,context:&'a mut Context)->Result<Runnable<'a>,Error>{ pub fn runnable_context<'a>(self,context:&'a mut Context<'a>)->Result<Runnable<'a>,Error>{
{ {
let globals=self.lua.globals(); let globals=self.lua.globals();
globals.set("game",super::instance::Instance::new_unchecked(context.services.game)).map_err(Error::RustLua)?; globals.set("game",super::instance::Instance::new_unchecked(context.services.game)).map_err(Error::RustLua)?;
@@ -68,8 +67,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{core::mem::transmute(&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

@@ -1,3 +0,0 @@
pub fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}

View File

@@ -14,10 +14,10 @@ image = "0.25.2"
image_dds = "0.7.1" image_dds = "0.7.1"
lazy-regex = "3.1.0" lazy-regex = "3.1.0"
rbx_asset = { version = "0.4.4", registry = "strafesnet" } rbx_asset = { version = "0.4.4", registry = "strafesnet" }
rbx_binary = { version = "1.1.0-sn4", registry = "strafesnet" } rbx_binary = { path = "../../../git/rbx-dom/rbx_binary", registry = "strafesnet" }
rbx_dom_weak = { version = "3.1.0-sn4", registry = "strafesnet" } rbx_dom_weak = { path = "../../../git/rbx-dom/rbx_dom_weak", registry = "strafesnet" }
rbx_reflection_database = "1.0.0" rbx_reflection_database = { path = "../../../git/rbx-dom/rbx_reflection_database"}
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" } rbx_xml = { path = "../../../git/rbx-dom/rbx_xml", registry = "strafesnet" }
rbxassetid = { version = "0.1.0", registry = "strafesnet" } rbxassetid = { version = "0.1.0", registry = "strafesnet" }
strafesnet_bsp_loader = { version = "0.3.0", path = "../lib/bsp_loader", registry = "strafesnet" } strafesnet_bsp_loader = { version = "0.3.0", path = "../lib/bsp_loader", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.0", path = "../lib/deferred_loader", registry = "strafesnet" } strafesnet_deferred_loader = { version = "0.5.0", path = "../lib/deferred_loader", registry = "strafesnet" }