Compare commits

...

58 Commits

Author SHA1 Message Date
22fa3ee81b ???? 2023-10-14 12:02:15 -07:00
3a8655c343 Display for Planar64+Planar64Mat3 2023-10-14 11:59:44 -07:00
691c3e0482 WRONG 2023-10-14 11:59:31 -07:00
1dfc566453 clone Ratio64 explicitly 2023-10-14 11:39:21 -07:00
762f10fb01 nonzero doesn't actually hint the compiler and is therefore useless 2023-10-14 11:38:54 -07:00
8fba543684 tabs 2023-10-14 11:22:36 -07:00
8c90eb1d94 sensible default sens 2023-10-14 01:18:07 -07:00
b9e200e070 ModelVertex is unused and will probably never be used 2023-10-14 00:52:15 -07:00
e0e55c6883 whoops (the bug) 2023-10-14 00:47:50 -07:00
0195511e49 impl Display for Planar64Vec3 2023-10-14 00:22:03 -07:00
7b09b3333b fix Time nanoseconds display 2023-10-14 00:21:53 -07:00
cf202f52f0 fixish test 2023-10-14 00:15:46 -07:00
35e8856e0f impl Display for Planar64Affine3 2023-10-13 23:54:16 -07:00
1271797a66 ITS ALL WRONG 2023-10-13 23:54:16 -07:00
6bb9db739c mouse does not need to be initialized with -1 time 2023-10-13 23:54:16 -07:00
9152237f2c whoops 2023-10-13 23:54:16 -07:00
282329fb33 angles are negative from mouse coordninates 2023-10-13 23:54:16 -07:00
c88451c0f0 allow partial underflow 2023-10-13 23:54:16 -07:00
5f1d732b59 constants 2023-10-13 23:54:16 -07:00
d33b830338 Ratio64: implement nearest fraction algorithm 2023-10-13 23:54:16 -07:00
80b1d25a13 pub pub 2023-10-13 16:29:33 -07:00
bf6f37fa00 fixings for physics 2023-10-13 16:29:33 -07:00
fa8ea26ddc fixup physics 2023-10-13 16:27:55 -07:00
f5d6280e0a WRONG 2023-10-13 16:27:45 -07:00
4e98e9a577 const const const 2023-10-13 16:27:36 -07:00
8fea9e0025 implement primitives 2023-10-13 16:06:01 -07:00
ac2f1d3eac Planar64Mat3 div + Planar64Mat3::int_from_cols_array 2023-10-13 16:06:01 -07:00
bcab0d92fd implement integers in main 2023-10-13 16:06:01 -07:00
36a5298b6d inline everything everywhere all at once 2023-10-13 16:06:01 -07:00
cea85a099d Planar64Affine3 stuff 2023-10-13 16:06:01 -07:00
9cc1674624 improve Planar64 into f32 2023-10-13 16:06:01 -07:00
7d33f69a47 implement load_roblox 2023-10-13 16:06:01 -07:00
9cb42009cb implement Planar64Vec3*i64 + Planar64Affine3::new 2023-10-13 16:06:01 -07:00
bcd421c4dd integer types for Model 2023-10-13 16:06:01 -07:00
4d62042549 Drop Unit64, Angle64. Disable Unit32 for now 2023-10-13 15:39:19 -07:00
fcf4d05baa Planar64::try_from(f32|f64) 2023-10-13 15:39:19 -07:00
2f33a28c95 implement Ratio::try_from(f32) 2023-10-13 15:39:19 -07:00
d939fbff94 implement Planar64Affine3::default() 2023-10-13 15:39:19 -07:00
9ca2f0a194 split out model 2023-10-13 15:39:19 -07:00
4bbd11dbb6 gcd 2023-10-12 18:03:47 -07:00
10a293e789 wip 2023-10-12 18:03:47 -07:00
01b5769dc0 fix TODOs 2023-10-12 04:39:54 -07:00
7f7b0d92e6 implement settings 2023-10-12 04:36:17 -07:00
3b7a1d5dff implement ModelPhysics 2023-10-12 04:36:17 -07:00
7309949dd0 implement instruction 2023-10-12 04:36:17 -07:00
43a0eef5d1 implement aabb & bvh 2023-10-12 04:36:17 -07:00
76cd82967a ALL MY SHLS ARE WRONG
(thanks julien_c)
2023-10-12 04:36:17 -07:00
69712847e3 implement zeroes 2023-10-12 04:32:13 -07:00
a8f82a14a9 mul type is defined by lhs 2023-10-12 04:32:13 -07:00
101c92cba4 clarify new as int 2023-10-12 04:32:13 -07:00
54ec21c490 wip 2023-10-12 04:32:13 -07:00
f16bc043c4 replace TIME with Time 2023-10-12 04:32:13 -07:00
4616fd7b3b implement PhysicsState 2023-10-12 04:32:13 -07:00
b6b63b4c85 implement StyleModifiers 2023-10-12 04:32:13 -07:00
c21c587edc implement body hash 2023-10-12 04:32:13 -07:00
9a12265881 implement PhysicsCamera 2023-10-12 04:32:12 -07:00
3ff73ed0bc wip: integer physics 2023-10-11 22:16:40 -07:00
57386334af wacky integer types for various future plans 2023-10-11 22:16:40 -07:00
14 changed files with 1419 additions and 480 deletions

View File

@ -1,3 +1,5 @@
use crate::integer::Planar64Vec3;
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum AabbFace{
Right,//+X
@ -8,84 +10,80 @@ pub enum AabbFace{
Front,
}
#[derive(Clone)]
pub struct Aabb {
pub min: glam::Vec3,
pub max: glam::Vec3,
pub struct Aabb{
pub min:Planar64Vec3,
pub max:Planar64Vec3,
}
impl Default for Aabb {
fn default() -> Self {
Aabb::new()
fn default()->Self {
Self{min:Planar64Vec3::MAX,max:Planar64Vec3::MIN}
}
}
impl Aabb {
const VERTEX_DATA: [glam::Vec3; 8] = [
glam::vec3(1., -1., -1.),
glam::vec3(1., 1., -1.),
glam::vec3(1., 1., 1.),
glam::vec3(1., -1., 1.),
glam::vec3(-1., -1., 1.),
glam::vec3(-1., 1., 1.),
glam::vec3(-1., 1., -1.),
glam::vec3(-1., -1., -1.),
impl Aabb{
const VERTEX_DATA:[Planar64Vec3;8]=[
Planar64Vec3::int( 1,-1,-1),
Planar64Vec3::int( 1, 1,-1),
Planar64Vec3::int( 1, 1, 1),
Planar64Vec3::int( 1,-1, 1),
Planar64Vec3::int(-1,-1, 1),
Planar64Vec3::int(-1, 1, 1),
Planar64Vec3::int(-1, 1,-1),
Planar64Vec3::int(-1,-1,-1),
];
pub fn new() -> Self {
Self {min: glam::Vec3::INFINITY,max: glam::Vec3::NEG_INFINITY}
}
pub fn grow(&mut self, point:glam::Vec3){
pub fn grow(&mut self,point:Planar64Vec3){
self.min=self.min.min(point);
self.max=self.max.max(point);
}
pub fn join(&mut self, aabb:&Aabb){
pub fn join(&mut self,aabb:&Aabb){
self.min=self.min.min(aabb.min);
self.max=self.max.max(aabb.max);
}
pub fn inflate(&mut self, hs:glam::Vec3){
pub fn inflate(&mut self,hs:Planar64Vec3){
self.min-=hs;
self.max+=hs;
}
pub fn intersects(&self,aabb:&Aabb)->bool{
(self.min.cmplt(aabb.max)&aabb.min.cmplt(self.max)).all()
}
pub fn normal(face:AabbFace) -> glam::Vec3 {
pub fn normal(face:AabbFace)->Planar64Vec3{
match face {
AabbFace::Right => glam::vec3(1.,0.,0.),
AabbFace::Top => glam::vec3(0.,1.,0.),
AabbFace::Back => glam::vec3(0.,0.,1.),
AabbFace::Left => glam::vec3(-1.,0.,0.),
AabbFace::Bottom => glam::vec3(0.,-1.,0.),
AabbFace::Front => glam::vec3(0.,0.,-1.),
AabbFace::Right=>Planar64Vec3::int(1,0,0),
AabbFace::Top=>Planar64Vec3::int(0,1,0),
AabbFace::Back=>Planar64Vec3::int(0,0,1),
AabbFace::Left=>Planar64Vec3::int(-1,0,0),
AabbFace::Bottom=>Planar64Vec3::int(0,-1,0),
AabbFace::Front=>Planar64Vec3::int(0,0,-1),
}
}
pub fn unit_vertices() -> [glam::Vec3;8] {
pub fn unit_vertices()->[Planar64Vec3;8] {
return Self::VERTEX_DATA;
}
pub fn face(&self,face:AabbFace) -> Aabb {
let mut aabb=self.clone();
//in this implementation face = worldspace aabb face
match face {
AabbFace::Right => aabb.min.x=aabb.max.x,
AabbFace::Top => aabb.min.y=aabb.max.y,
AabbFace::Back => aabb.min.z=aabb.max.z,
AabbFace::Left => aabb.max.x=aabb.min.x,
AabbFace::Bottom => aabb.max.y=aabb.min.y,
AabbFace::Front => aabb.max.z=aabb.min.z,
}
return aabb;
}
pub fn center(&self)->glam::Vec3{
return (self.min+self.max)/2.0
// pub fn face(&self,face:AabbFace)->Aabb {
// let mut aabb=self.clone();
// //in this implementation face = worldspace aabb face
// match face {
// AabbFace::Right => aabb.min.x=aabb.max.x,
// AabbFace::Top => aabb.min.y=aabb.max.y,
// AabbFace::Back => aabb.min.z=aabb.max.z,
// AabbFace::Left => aabb.max.x=aabb.min.x,
// AabbFace::Bottom => aabb.max.y=aabb.min.y,
// AabbFace::Front => aabb.max.z=aabb.min.z,
// }
// return aabb;
// }
pub fn center(&self)->Planar64Vec3{
return self.min.midpoint(self.max)
}
//probably use floats for area & volume because we don't care about precision
pub fn area_weight(&self)->f32{
let d=self.max-self.min;
d.x*d.y+d.y*d.z+d.z*d.x
}
pub fn volume(&self)->f32{
let d=self.max-self.min;
d.x*d.y*d.z
}
// pub fn area_weight(&self)->f32{
// let d=self.max-self.min;
// d.x*d.y+d.y*d.z+d.z*d.x
// }
// pub fn volume(&self)->f32{
// let d=self.max-self.min;
// d.x*d.y*d.z
// }
}

View File

@ -36,7 +36,7 @@ pub fn generate_bvh(boxen:Vec<Aabb>)->BvhNode{
fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
let n=boxen.len();
if n<20{
let mut aabb=Aabb::new();
let mut aabb=Aabb::default();
let models=boxen.into_iter().map(|b|{aabb.join(&b.1);b.0 as u32}).collect();
BvhNode{
children:Vec::new(),
@ -51,9 +51,9 @@ fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
for (i,aabb) in boxen.iter(){
let center=aabb.center();
octant.insert(*i,0);
sort_x.push((*i,center.x));
sort_y.push((*i,center.y));
sort_z.push((*i,center.z));
sort_x.push((*i,center.x()));
sort_y.push((*i,center.y()));
sort_z.push((*i,center.z()));
}
sort_x.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
sort_y.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
@ -92,7 +92,7 @@ fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
};
list_list[list_id].push((i,aabb));
}
let mut aabb=Aabb::new();
let mut aabb=Aabb::default();
let children=list_list.into_iter().map(|b|{
let node=generate_bvh_node(b);
aabb.join(&node.aabb);

View File

@ -1,23 +1,25 @@
use crate::integer::Time;
#[derive(Debug)]
pub struct TimedInstruction<I> {
pub time: crate::physics::TIME,
pub instruction: I,
pub struct TimedInstruction<I>{
pub time:Time,
pub instruction:I,
}
pub trait InstructionEmitter<I> {
fn next_instruction(&self, time_limit:crate::physics::TIME) -> Option<TimedInstruction<I>>;
pub trait InstructionEmitter<I>{
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<I>>;
}
pub trait InstructionConsumer<I> {
pub trait InstructionConsumer<I>{
fn process_instruction(&mut self, instruction:TimedInstruction<I>);
}
//PROPER PRIVATE FIELDS!!!
pub struct InstructionCollector<I> {
time: crate::physics::TIME,
instruction: Option<I>,
pub struct InstructionCollector<I>{
time:Time,
instruction:Option<I>,
}
impl<I> InstructionCollector<I> {
pub fn new(time:crate::physics::TIME) -> Self {
impl<I> InstructionCollector<I>{
pub fn new(time:Time)->Self{
Self{
time,
instruction:None
@ -25,24 +27,24 @@ impl<I> InstructionCollector<I> {
}
pub fn collect(&mut self,instruction:Option<TimedInstruction<I>>){
match instruction {
Some(unwrap_instruction) => {
match instruction{
Some(unwrap_instruction)=>{
if unwrap_instruction.time<self.time {
self.time=unwrap_instruction.time;
self.instruction=Some(unwrap_instruction.instruction);
}
},
None => (),
None=>(),
}
}
pub fn instruction(self) -> Option<TimedInstruction<I>> {
pub fn instruction(self)->Option<TimedInstruction<I>>{
//STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR
match self.instruction {
match self.instruction{
Some(instruction)=>Some(TimedInstruction{
time:self.time,
instruction
}),
None => None,
None=>None,
}
}
}

914
src/integer.rs Normal file
View File

@ -0,0 +1,914 @@
//integer units
#[derive(Clone,Copy,Hash,PartialEq,PartialOrd,Debug)]
pub struct Time(i64);
impl Time{
pub const ZERO:Self=Self(0);
pub const ONE_SECOND:Self=Self(1_000_000_000);
pub const ONE_MILLISECOND:Self=Self(1_000_000);
pub const ONE_MICROSECOND:Self=Self(1_000);
pub const ONE_NANOSECOND:Self=Self(1);
#[inline]
pub fn from_secs(num:i64)->Self{
Self(Self::ONE_SECOND.0*num)
}
#[inline]
pub fn from_millis(num:i64)->Self{
Self(Self::ONE_MILLISECOND.0*num)
}
#[inline]
pub fn from_micros(num:i64)->Self{
Self(Self::ONE_MICROSECOND.0*num)
}
#[inline]
pub fn from_nanos(num:i64)->Self{
Self(Self::ONE_NANOSECOND.0*num)
}
//should I have checked subtraction? force all time variables to be positive?
#[inline]
pub fn nanos(&self)->i64{
self.0
}
}
impl From<Planar64> for Time{
#[inline]
fn from(value:Planar64)->Self{
Time((((value.0 as i128)*1_000_000_000)>>32) as i64)
}
}
impl std::fmt::Display for Time{
#[inline]
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0)
}
}
impl std::ops::Neg for Time{
type Output=Time;
#[inline]
fn neg(self)->Self::Output {
Time(-self.0)
}
}
impl std::ops::Add<Time> for Time{
type Output=Time;
#[inline]
fn add(self,rhs:Self)->Self::Output {
Time(self.0+rhs.0)
}
}
impl std::ops::Sub<Time> for Time{
type Output=Time;
#[inline]
fn sub(self,rhs:Self)->Self::Output {
Time(self.0-rhs.0)
}
}
impl std::ops::Mul<Time> for Time{
type Output=Time;
#[inline]
fn mul(self,rhs:Time)->Self::Output{
Self((((self.0 as i128)*(rhs.0 as i128))/1_000_000_000) as i64)
}
}
impl std::ops::Div<i64> for Time{
type Output=Time;
#[inline]
fn div(self,rhs:i64)->Self::Output {
Time(self.0/rhs)
}
}
#[inline]
const fn gcd(mut a:u64,mut b:u64)->u64{
while b!=0{
(a,b)=(b,a.rem_euclid(b));
};
a
}
#[derive(Clone,Hash)]
pub struct Ratio64{
num:i64,
den:u64,
}
impl Ratio64{
pub const ZERO:Self=Ratio64{num:0,den:1};
pub const ONE:Self=Ratio64{num:1,den:1};
#[inline]
pub const fn new(num:i64,den:u64)->Option<Ratio64>{
if den==0{
None
}else{
let d=gcd(num.unsigned_abs(),den);
Some(Self{num:num/d as i64,den:den/d})
}
}
#[inline]
pub fn mul_int(&self,rhs:i64)->i64{
rhs*self.num/self.den as i64
}
#[inline]
pub fn rhs_div_int(&self,rhs:i64)->i64{
rhs*self.den as i64/self.num
}
#[inline]
pub fn mul_ref(&self,rhs:&Ratio64)->Ratio64{
let (num,den)=(self.num*rhs.num,self.den*rhs.den);
let d=gcd(num.unsigned_abs(),den);
Self{
num:num/d as i64,
den:den/d,
}
}
}
//from num_traits crate
#[inline]
fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
let bits: u32 = f.to_bits();
let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 };
let mut exponent: i16 = ((bits >> 23) & 0xff) as i16;
let mantissa = if exponent == 0 {
(bits & 0x7fffff) << 1
} else {
(bits & 0x7fffff) | 0x800000
};
// Exponent bias + mantissa shift
exponent -= 127 + 23;
(mantissa as u64, exponent, sign)
}
#[inline]
fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
let bits: u64 = f.to_bits();
let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
let mantissa = if exponent == 0 {
(bits & 0xfffffffffffff) << 1
} else {
(bits & 0xfffffffffffff) | 0x10000000000000
};
// Exponent bias + mantissa shift
exponent -= 1023 + 52;
(mantissa, exponent, sign)
}
#[derive(Debug)]
pub enum Ratio64TryFromFloatError{
Nan,
Infinite,
Subnormal,
HighlyNegativeExponent(i16),
HighlyPositiveExponent(i16),
}
const MAX_DENOMINATOR:u128=u64::MAX as u128;
#[inline]
fn ratio64_from_mes((m,e,s):(u64,i16,i8))->Result<Ratio64,Ratio64TryFromFloatError>{
if e< -127{
//this can also just be zero
Err(Ratio64TryFromFloatError::HighlyNegativeExponent(e))
}else if e< -63{
//approximate input ratio within denominator limit
let mut target_num=m as u128;
let mut target_den=1u128<<-e;
let mut num=1;
let mut den=0;
let mut prev_num=0;
let mut prev_den=1;
while target_den!=0{
let whole=target_num/target_den;
(target_num,target_den)=(target_den,target_num-whole*target_den);
let new_num=whole*num+prev_num;
let new_den=whole*den+prev_den;
if MAX_DENOMINATOR<new_den{
break;
}else{
(prev_num,prev_den)=(num,den);
(num,den)=(new_num,new_den);
}
}
Ok(Ratio64::new(num as i64,den as u64).unwrap())
}else if e<0{
Ok(Ratio64::new((m as i64)*(s as i64),1<<-e).unwrap())
}else if (64-m.leading_zeros() as i16)+e<64{
Ok(Ratio64::new((m as i64)*(s as i64)*(1<<e),1).unwrap())
}else{
Err(Ratio64TryFromFloatError::HighlyPositiveExponent(e))
}
}
impl TryFrom<f32> for Ratio64{
type Error=Ratio64TryFromFloatError;
#[inline]
fn try_from(value:f32)->Result<Self,Self::Error>{
match value.classify(){
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f32(value)),
}
}
}
impl TryFrom<f64> for Ratio64{
type Error=Ratio64TryFromFloatError;
#[inline]
fn try_from(value:f64)->Result<Self,Self::Error>{
match value.classify(){
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f64(value)),
}
}
}
impl std::ops::Mul<Ratio64> for Ratio64{
type Output=Ratio64;
#[inline]
fn mul(self,rhs:Ratio64)->Self::Output{
let (num,den)=(self.num*rhs.num,self.den*rhs.den);
let d=gcd(num.unsigned_abs(),den);
Self{
num:num/d as i64,
den:den/d,
}
}
}
impl std::ops::Mul<i64> for Ratio64{
type Output=Ratio64;
#[inline]
fn mul(self,rhs:i64)->Self::Output {
Self{
num:self.num*rhs,
den:self.den,
}
}
}
impl std::ops::Div<u64> for Ratio64{
type Output=Ratio64;
#[inline]
fn div(self,rhs:u64)->Self::Output {
Self{
num:self.num,
den:self.den*rhs,
}
}
}
#[derive(Clone,Hash)]
pub struct Ratio64Vec2{
pub x:Ratio64,
pub y:Ratio64,
}
impl Ratio64Vec2{
pub const ONE:Self=Self{x:Ratio64::ONE,y:Ratio64::ONE};
#[inline]
pub fn new(x:Ratio64,y:Ratio64)->Self{
Self{x,y}
}
#[inline]
pub fn mul_int(&self,rhs:glam::I64Vec2)->glam::I64Vec2{
glam::i64vec2(
self.x.mul_int(rhs.x),
self.y.mul_int(rhs.y),
)
}
}
impl std::ops::Mul<i64> for Ratio64Vec2{
type Output=Ratio64Vec2;
#[inline]
fn mul(self,rhs:i64)->Self::Output {
Self{
x:self.x*rhs,
y:self.y*rhs,
}
}
}
///[-pi,pi) = [-2^31,2^31-1]
#[derive(Clone,Copy,Hash)]
pub struct Angle32(i32);
impl Angle32{
pub const FRAC_PI_2:Self=Self(1<<30);
pub const PI:Self=Self(-1<<31);
#[inline]
pub fn wrap_from_i64(theta:i64)->Self{
//take lower bits
//note: this was checked on compiler explorer and compiles to 1 instruction!
Self(i32::from_ne_bytes(((theta&((1<<32)-1)) as u32).to_ne_bytes()))
}
#[inline]
pub fn clamp_from_i64(theta:i64)->Self{
//the assembly is a bit confusing for this, I thought it was checking the same thing twice
//but it's just checking and then overwriting the value for both upper and lower bounds.
Self(theta.clamp(i32::MIN as i64,i32::MAX as i64) as i32)
}
#[inline]
pub fn get(&self)->i32{
self.0
}
/// Clamps the value towards the midpoint of the range.
/// Note that theta_min can be larger than theta_max and it will wrap clamp the other way around
#[inline]
pub fn clamp(&self,theta_min:Self,theta_max:Self)->Self{
//((max-min as u32)/2 as i32)+min
let midpoint=((
u32::from_ne_bytes(theta_max.0.to_ne_bytes())
.wrapping_sub(u32::from_ne_bytes(theta_min.0.to_ne_bytes()))
/2
) as i32)//(u32::MAX/2) as i32 ALWAYS works
.wrapping_add(theta_min.0);
//(theta-mid).clamp(max-mid,min-mid)+mid
Self(
self.0.wrapping_sub(midpoint)
.max(theta_min.0.wrapping_sub(midpoint))
.min(theta_max.0.wrapping_sub(midpoint))
.wrapping_add(midpoint)
)
}
/*
#[inline]
pub fn cos(&self)->Unit32{
//TODO: fix this rounding towards 0
Unit32(unsafe{((self.0 as f64*ANGLE32_TO_FLOAT64_RADIANS).cos()*UNIT32_ONE_FLOAT64).to_int_unchecked()})
}
#[inline]
pub fn sin(&self)->Unit32{
//TODO: fix this rounding towards 0
Unit32(unsafe{((self.0 as f64*ANGLE32_TO_FLOAT64_RADIANS).sin()*UNIT32_ONE_FLOAT64).to_int_unchecked()})
}
*/
}
const ANGLE32_TO_FLOAT64_RADIANS:f64=std::f64::consts::PI/((1i64<<31) as f64);
impl Into<f32> for Angle32{
#[inline]
fn into(self)->f32{
(self.0 as f64*ANGLE32_TO_FLOAT64_RADIANS) as f32
}
}
impl std::ops::Neg for Angle32{
type Output=Angle32;
#[inline]
fn neg(self)->Self::Output{
Angle32(self.0.wrapping_neg())
}
}
impl std::ops::Add<Angle32> for Angle32{
type Output=Angle32;
#[inline]
fn add(self,rhs:Self)->Self::Output {
Angle32(self.0.wrapping_add(rhs.0))
}
}
impl std::ops::Sub<Angle32> for Angle32{
type Output=Angle32;
#[inline]
fn sub(self,rhs:Self)->Self::Output {
Angle32(self.0.wrapping_sub(rhs.0))
}
}
impl std::ops::Mul<i32> for Angle32{
type Output=Angle32;
#[inline]
fn mul(self,rhs:i32)->Self::Output {
Angle32(self.0.wrapping_mul(rhs))
}
}
impl std::ops::Mul<Angle32> for Angle32{
type Output=Angle32;
#[inline]
fn mul(self,rhs:Self)->Self::Output {
Angle32(self.0.wrapping_mul(rhs.0))
}
}
/* Unit type unused for now, may revive it for map files
///[-1.0,1.0] = [-2^30,2^30]
pub struct Unit32(i32);
impl Unit32{
#[inline]
pub fn as_planar64(&self) -> Planar64{
Planar64(4*(self.0 as i64))
}
}
const UNIT32_ONE_FLOAT64=((1<<30) as f64);
///[-1.0,1.0] = [-2^30,2^30]
pub struct Unit32Vec3(glam::IVec3);
impl TryFrom<[f32;3]> for Unit32Vec3{
type Error=Unit32TryFromFloatError;
fn try_from(value:[f32;3])->Result<Self,Self::Error>{
Ok(Self(glam::ivec3(
Unit32::try_from(Planar64::try_from(value[0])?)?.0,
Unit32::try_from(Planar64::try_from(value[1])?)?.0,
Unit32::try_from(Planar64::try_from(value[2])?)?.0,
)))
}
}
*/
///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy,Hash,Eq,Ord,PartialEq,PartialOrd)]
pub struct Planar64(i64);
impl Planar64{
pub const ZERO:Self=Self(0);
pub const ONE:Self=Self(1<<32);
#[inline]
pub const fn int(num:i32)->Self{
Self(Self::ONE.0*num as i64)
}
#[inline]
pub const fn raw(num:i64)->Self{
Self(num)
}
#[inline]
pub const fn get(&self)->i64{
self.0
}
}
const PLANAR64_FLOAT32_ONE:f32=(1u64<<32) as f32;
const PLANAR64_FLOAT32_MUL:f32=1.0/PLANAR64_FLOAT32_ONE;
const PLANAR64_FLOAT64_ONE:f64=(1u64<<32) as f64;
impl Into<f32> for Planar64{
#[inline]
fn into(self)->f32{
self.0 as f32*PLANAR64_FLOAT32_MUL
}
}
impl From<Ratio64> for Planar64{
#[inline]
fn from(ratio:Ratio64)->Self{
Self((((ratio.num as i128)<<32)/ratio.den as i128) as i64)
}
}
#[derive(Debug)]
pub enum Planar64TryFromFloatError{
Nan,
Infinite,
Subnormal,
HighlyNegativeExponent(i16),
HighlyPositiveExponent(i16),
}
#[inline]
fn planar64_from_mes((m,e,s):(u64,i16,i8))->Result<Planar64,Planar64TryFromFloatError>{
let e32=e+32;
if e32<0&&(m>>-e32)==0{//shifting m will underflow to 0
Ok(Planar64::ZERO)
// println!("m{} e{} s{}",m,e,s);
// println!("f={}",(m as f64)*(2.0f64.powf(e as f64))*(s as f64));
// Err(Planar64TryFromFloatError::HighlyNegativeExponent(e))
}else if (64-m.leading_zeros() as i16)+e32<64{//shifting m will not overflow
if e32<0{
Ok(Planar64((m as i64)*(s as i64)>>-e32))
}else{
Ok(Planar64((m as i64)*(s as i64)<<e32))
}
}else{//if shifting m will overflow (prev check failed)
Err(Planar64TryFromFloatError::HighlyPositiveExponent(e))
}
}
impl TryFrom<f32> for Planar64{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:f32)->Result<Self,Self::Error>{
match value.classify(){
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>planar64_from_mes(integer_decode_f32(value)),
}
}
}
impl TryFrom<f64> for Planar64{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:f64)->Result<Self,Self::Error>{
match value.classify(){
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>planar64_from_mes(integer_decode_f64(value)),
}
}
}
impl std::fmt::Display for Planar64{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{:.3}",
Into::<f32>::into(*self),
)
}
}
impl std::ops::Neg for Planar64{
type Output=Planar64;
#[inline]
fn neg(self)->Self::Output{
Planar64(-self.0)
}
}
impl std::ops::Add<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Planar64(self.0+rhs.0)
}
}
impl std::ops::Sub<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Planar64(self.0-rhs.0)
}
}
impl std::ops::Mul<i64> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self, rhs: i64) -> Self::Output {
Planar64(self.0*rhs)
}
}
impl std::ops::Mul<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Planar64((((self.0 as i128)*(rhs.0 as i128))>>32) as i64)
}
}
impl std::ops::Div<i64> for Planar64{
type Output=Planar64;
#[inline]
fn div(self, rhs: i64) -> Self::Output {
Planar64(self.0/rhs)
}
}
impl std::ops::Div<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn div(self, rhs: Planar64) -> Self::Output {
Planar64((((self.0 as i128)<<32)/rhs.0 as i128) as i64)
}
}
// impl PartialOrd<i64> for Planar64{
// fn partial_cmp(&self, other: &i64) -> Option<std::cmp::Ordering> {
// self.0.partial_cmp(other)
// }
// }
///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy,Default,Hash,Eq,PartialEq)]
pub struct Planar64Vec3(glam::I64Vec3);
impl Planar64Vec3{
pub const ZERO:Self=Planar64Vec3(glam::I64Vec3::ZERO);
pub const ONE:Self=Self::int(1,1,1);
pub const X:Self=Self::int(1,0,0);
pub const Y:Self=Self::int(0,1,0);
pub const Z:Self=Self::int(0,0,1);
pub const NEG_X:Self=Self::int(-1,0,0);
pub const NEG_Y:Self=Self::int(0,-1,0);
pub const NEG_Z:Self=Self::int(0,0,-1);
pub const MIN:Self=Planar64Vec3(glam::I64Vec3::MIN);
pub const MAX:Self=Planar64Vec3(glam::I64Vec3::MAX);
#[inline]
pub const fn int(x:i32,y:i32,z:i32)->Self{
Self(glam::i64vec3((x as i64)<<32,(y as i64)<<32,(z as i64)<<32))
}
#[inline]
pub fn x(&self)->Planar64{
Planar64(self.0.x)
}
#[inline]
pub fn y(&self)->Planar64{
Planar64(self.0.y)
}
#[inline]
pub fn z(&self)->Planar64{
Planar64(self.0.z)
}
#[inline]
pub fn min(&self,rhs:Self)->Self{
Self(glam::i64vec3(
self.0.x.min(rhs.0.x),
self.0.y.min(rhs.0.y),
self.0.z.min(rhs.0.z),
))
}
#[inline]
pub fn max(&self,rhs:Self)->Self{
Self(glam::i64vec3(
self.0.x.max(rhs.0.x),
self.0.y.max(rhs.0.y),
self.0.z.max(rhs.0.z),
))
}
#[inline]
pub fn midpoint(&self,rhs:Self)->Self{
Self((self.0+rhs.0)/2)
}
#[inline]
pub fn cmplt(&self,rhs:Self)->glam::BVec3{
self.0.cmplt(rhs.0)
}
#[inline]
pub fn dot(&self,rhs:Self)->Planar64{
Planar64(((
(self.0.x as i128)*(rhs.0.x as i128)+
(self.0.y as i128)*(rhs.0.y as i128)+
(self.0.z as i128)*(rhs.0.z as i128)
)>>32) as i64)
}
#[inline]
pub fn length(&self)->Planar64{
let radicand=(self.0.x as i128)*(self.0.x as i128)+(self.0.y as i128)*(self.0.y as i128)+(self.0.z as i128)*(self.0.z as i128);
Planar64(unsafe{(radicand as f64).sqrt().to_int_unchecked()})
}
#[inline]
pub fn with_length(&self,length:Planar64)->Self{
let radicand=(self.0.x as i128)*(self.0.x as i128)+(self.0.y as i128)*(self.0.y as i128)+(self.0.z as i128)*(self.0.z as i128);
let self_length:i128=unsafe{(radicand as f64).sqrt().to_int_unchecked()};
//self.0*length/self_length
Planar64Vec3(
glam::i64vec3(
((self.0.x as i128)*(length.0 as i128)/self_length) as i64,
((self.0.y as i128)*(length.0 as i128)/self_length) as i64,
((self.0.z as i128)*(length.0 as i128)/self_length) as i64,
)
)
}
}
impl Into<glam::Vec3> for Planar64Vec3{
#[inline]
fn into(self)->glam::Vec3{
glam::vec3(
self.0.x as f32,
self.0.y as f32,
self.0.z as f32,
)*PLANAR64_FLOAT32_MUL
}
}
impl TryFrom<[f32;3]> for Planar64Vec3{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:[f32;3])->Result<Self,Self::Error>{
Ok(Self(glam::i64vec3(
Planar64::try_from(value[0])?.0,
Planar64::try_from(value[1])?.0,
Planar64::try_from(value[2])?.0,
)))
}
}
impl TryFrom<glam::Vec3A> for Planar64Vec3{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:glam::Vec3A)->Result<Self,Self::Error>{
Ok(Self(glam::i64vec3(
Planar64::try_from(value.x)?.0,
Planar64::try_from(value.y)?.0,
Planar64::try_from(value.z)?.0,
)))
}
}
impl std::fmt::Display for Planar64Vec3{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{:.3},{:.3},{:.3}",
Into::<f32>::into(self.x()),Into::<f32>::into(self.y()),Into::<f32>::into(self.z()),
)
}
}
impl std::ops::Neg for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn neg(self)->Self::Output{
Planar64Vec3(-self.0)
}
}
impl std::ops::Add<Planar64Vec3> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn add(self,rhs:Planar64Vec3) -> Self::Output {
Planar64Vec3(self.0+rhs.0)
}
}
impl std::ops::AddAssign<Planar64Vec3> for Planar64Vec3{
#[inline]
fn add_assign(&mut self,rhs:Planar64Vec3){
*self=*self+rhs
}
}
impl std::ops::Sub<Planar64Vec3> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn sub(self,rhs:Planar64Vec3) -> Self::Output {
Planar64Vec3(self.0-rhs.0)
}
}
impl std::ops::SubAssign<Planar64Vec3> for Planar64Vec3{
#[inline]
fn sub_assign(&mut self,rhs:Planar64Vec3){
*self=*self-rhs
}
}
impl std::ops::Mul<Planar64Vec3> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self, rhs: Planar64Vec3) -> Self::Output {
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)*(rhs.0.x as i128))>>32) as i64,
(((self.0.y as i128)*(rhs.0.y as i128))>>32) as i64,
(((self.0.z as i128)*(rhs.0.z as i128))>>32) as i64
))
}
}
impl std::ops::Mul<Planar64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self, rhs: Planar64) -> Self::Output {
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)*(rhs.0 as i128))>>32) as i64,
(((self.0.y as i128)*(rhs.0 as i128))>>32) as i64,
(((self.0.z as i128)*(rhs.0 as i128))>>32) as i64
))
}
}
impl std::ops::Mul<i64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:i64)->Self::Output {
Planar64Vec3(glam::i64vec3(
self.0.x*rhs,
self.0.y*rhs,
self.0.z*rhs
))
}
}
impl std::ops::Mul<Time> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:Time)->Self::Output{
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)*(rhs.0 as i128))/1_000_000_000) as i64,
(((self.0.y as i128)*(rhs.0 as i128))/1_000_000_000) as i64,
(((self.0.z as i128)*(rhs.0 as i128))/1_000_000_000) as i64
))
}
}
impl std::ops::Div<i64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn div(self,rhs:i64)->Self::Output{
Planar64Vec3(glam::i64vec3(
self.0.x/rhs,
self.0.y/rhs,
self.0.z/rhs,
))
}
}
///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy)]
pub struct Planar64Mat3{
x_axis:Planar64Vec3,
y_axis:Planar64Vec3,
z_axis:Planar64Vec3,
}
impl Default for Planar64Mat3{
#[inline]
fn default() -> Self {
Self{
x_axis:Planar64Vec3::X,
y_axis:Planar64Vec3::Y,
z_axis:Planar64Vec3::Z,
}
}
}
impl std::ops::Mul<Planar64Vec3> for Planar64Mat3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:Planar64Vec3) -> Self::Output {
self.x_axis*rhs.x()
+self.y_axis*rhs.y()
+self.z_axis*rhs.z()
}
}
impl Planar64Mat3{
#[inline]
pub fn from_cols(x_axis:Planar64Vec3,y_axis:Planar64Vec3,z_axis:Planar64Vec3)->Self{
Self{
x_axis,
y_axis,
z_axis,
}
}
pub const fn int_from_cols_array(array:[i32;9])->Self{
Self{
x_axis:Planar64Vec3::int(array[0],array[1],array[2]),
y_axis:Planar64Vec3::int(array[3],array[4],array[5]),
z_axis:Planar64Vec3::int(array[6],array[7],array[8]),
}
}
#[inline]
pub fn from_rotation_y(angle:Angle32)->Self{
let theta=angle.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (s,c)=theta.sin_cos();
let (c,s)=(c*PLANAR64_FLOAT64_ONE,s*PLANAR64_FLOAT64_ONE);
//TODO: fix this rounding towards 0
let (c,s):(i64,i64)=(unsafe{c.to_int_unchecked()},unsafe{s.to_int_unchecked()});
Self::from_cols(
Planar64Vec3(glam::i64vec3(c,0,-s)),
Planar64Vec3::Y,
Planar64Vec3(glam::i64vec3(s,0,c)),
)
}
}
impl Into<glam::Mat3> for Planar64Mat3{
#[inline]
fn into(self)->glam::Mat3{
glam::Mat3::from_cols(
self.x_axis.into(),
self.y_axis.into(),
self.z_axis.into(),
)
}
}
impl TryFrom<glam::Mat3A> for Planar64Mat3{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:glam::Mat3A)->Result<Self,Self::Error>{
Ok(Self{
x_axis:Planar64Vec3::try_from(value.x_axis)?,
y_axis:Planar64Vec3::try_from(value.y_axis)?,
z_axis:Planar64Vec3::try_from(value.z_axis)?,
})
}
}
impl std::fmt::Display for Planar64Mat3{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}",
Into::<f32>::into(self.x_axis.x()),Into::<f32>::into(self.x_axis.y()),Into::<f32>::into(self.x_axis.z()),
Into::<f32>::into(self.y_axis.x()),Into::<f32>::into(self.y_axis.y()),Into::<f32>::into(self.y_axis.z()),
Into::<f32>::into(self.z_axis.x()),Into::<f32>::into(self.z_axis.y()),Into::<f32>::into(self.z_axis.z()),
)
}
}
impl std::ops::Div<i64> for Planar64Mat3{
type Output=Planar64Mat3;
#[inline]
fn div(self,rhs:i64)->Self::Output{
Planar64Mat3{
x_axis:self.x_axis/rhs,
y_axis:self.y_axis/rhs,
z_axis:self.z_axis/rhs,
}
}
}
///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy,Default)]
pub struct Planar64Affine3{
pub matrix3:Planar64Mat3,//includes scale above 1
pub translation:Planar64Vec3,
}
impl Planar64Affine3{
#[inline]
pub fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{
Self{matrix3,translation}
}
#[inline]
pub fn transform_point3(&self,point:Planar64Vec3) -> Planar64Vec3{
Planar64Vec3(
self.translation.0
+(self.matrix3.x_axis*point.x()).0
+(self.matrix3.y_axis*point.y()).0
+(self.matrix3.z_axis*point.z()).0
)
}
}
impl Into<glam::Mat4> for Planar64Affine3{
#[inline]
fn into(self)->glam::Mat4{
glam::Mat4::from_cols_array(&[
self.matrix3.x_axis.0.x as f32,self.matrix3.x_axis.0.y as f32,self.matrix3.x_axis.0.z as f32,0.0,
self.matrix3.y_axis.0.x as f32,self.matrix3.y_axis.0.y as f32,self.matrix3.y_axis.0.z as f32,0.0,
self.matrix3.z_axis.0.x as f32,self.matrix3.z_axis.0.y as f32,self.matrix3.z_axis.0.z as f32,0.0,
self.translation.0.x as f32,self.translation.0.y as f32,self.translation.0.z as f32,PLANAR64_FLOAT32_ONE
])*PLANAR64_FLOAT32_MUL
}
}
impl TryFrom<glam::Affine3A> for Planar64Affine3{
type Error=Planar64TryFromFloatError;
fn try_from(value: glam::Affine3A)->Result<Self, Self::Error> {
Ok(Self{
matrix3:Planar64Mat3::try_from(value.matrix3)?,
translation:Planar64Vec3::try_from(value.translation)?
})
}
}
impl std::fmt::Display for Planar64Affine3{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"translation: {:.3},{:.3},{:.3}\nmatrix3:\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}",
Into::<f32>::into(self.translation.x()),Into::<f32>::into(self.translation.y()),Into::<f32>::into(self.translation.z()),
Into::<f32>::into(self.matrix3.x_axis.x()),Into::<f32>::into(self.matrix3.x_axis.y()),Into::<f32>::into(self.matrix3.x_axis.z()),
Into::<f32>::into(self.matrix3.y_axis.x()),Into::<f32>::into(self.matrix3.y_axis.y()),Into::<f32>::into(self.matrix3.y_axis.z()),
Into::<f32>::into(self.matrix3.z_axis.x()),Into::<f32>::into(self.matrix3.z_axis.y()),Into::<f32>::into(self.matrix3.z_axis.z()),
)
}
}

View File

@ -1,4 +1,5 @@
use crate::primitives;
use crate::integer::{Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
fn class_is_a(class: &str, superclass: &str) -> bool {
if class==superclass {
@ -30,7 +31,20 @@ fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>
//next class
objects
}
fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersecting:bool)->crate::model::CollisionAttributes{
fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Planar64Affine3{
Planar64Affine3::new(
Planar64Mat3::from_cols(
Planar64Vec3::try_from([cf.orientation.x.x,cf.orientation.y.x,cf.orientation.z.x]).unwrap()
*Planar64::try_from(size.x/2.0).unwrap(),
Planar64Vec3::try_from([cf.orientation.x.y,cf.orientation.y.y,cf.orientation.z.y]).unwrap()
*Planar64::try_from(size.y/2.0).unwrap(),
Planar64Vec3::try_from([cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z]).unwrap()
*Planar64::try_from(size.z/2.0).unwrap(),
),
Planar64Vec3::try_from([cf.position.x,cf.position.y,cf.position.z]).unwrap()
)
}
fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_intersecting:bool)->crate::model::CollisionAttributes{
let mut general=crate::model::GameMechanicAttributes::default();
let mut intersecting=crate::model::IntersectingAttributes::default();
let mut contacting=crate::model::ContactingAttributes::default();
@ -76,7 +90,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersect
}
}
//need some way to skip this
if velocity!=glam::Vec3::ZERO{
if velocity!=Planar64Vec3::ZERO{
general.booster=Some(crate::model::GameMechanicBooster{velocity});
}
match force_can_collide{
@ -189,7 +203,7 @@ enum RobloxBasePartDescription{
}
pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::IndexedModelInstances{
//IndexedModelInstances includes textures
let mut spawn_point=glam::Vec3::ZERO;
let mut spawn_point=Planar64Vec3::ZERO;
let mut indexed_models=Vec::new();
let mut model_id_from_description=std::collections::HashMap::<RobloxBasePartDescription,usize>::new();
@ -218,26 +232,14 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
object.properties.get("CanCollide"),
)
{
let model_transform=glam::Affine3A::from_translation(
glam::Vec3::new(cf.position.x,cf.position.y,cf.position.z)
)
* glam::Affine3A::from_mat3(
glam::Mat3::from_cols(
glam::Vec3::new(cf.orientation.x.x,cf.orientation.y.x,cf.orientation.z.x),
glam::Vec3::new(cf.orientation.x.y,cf.orientation.y.y,cf.orientation.z.y),
glam::Vec3::new(cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z),
),
)
* glam::Affine3A::from_scale(
glam::Vec3::new(size.x,size.y,size.z)/2.0
);
let model_transform=planar64_affine3_from_roblox(cf,size);
//push TempIndexedAttributes
let mut force_intersecting=false;
let mut temp_indexing_attributes=Vec::new();
if let Some(attr)=match &object.name[..]{
"MapStart"=>{
spawn_point=model_transform.transform_point3(glam::Vec3::ZERO)+glam::vec3(0.0,2.5,0.0);
spawn_point=model_transform.transform_point3(Planar64Vec3::ZERO)+Planar64Vec3::Y*5/2;
Some(crate::model::TempIndexedAttributes::Start{mode_id:0})
},
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}),
@ -463,7 +465,7 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
indexed_models[model_id].instances.push(crate::model::ModelInstance {
transform:model_transform,
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
attributes:get_attributes(&object.name,*can_collide,glam::vec3(velocity.x,velocity.y,velocity.z),force_intersecting),
attributes:get_attributes(&object.name,*can_collide,Planar64Vec3::try_from([velocity.x,velocity.y,velocity.z]).unwrap(),force_intersecting),
temp_indexing:temp_indexing_attributes,
});
}

View File

@ -1,12 +1,13 @@
use std::{borrow::Cow, time::Instant};
use wgpu::{util::DeviceExt, AstcBlock, AstcChannel};
use model::{Vertex,ModelInstance,ModelGraphicsInstance};
use model_graphics::{GraphicsVertex,ModelGraphicsInstance};
use physics::{InputInstruction, PhysicsInstruction};
use instruction::{TimedInstruction, InstructionConsumer};
mod bvh;
mod aabb;
mod model;
mod model_graphics;
mod zeroes;
mod worker;
mod physics;
@ -15,6 +16,7 @@ mod framework;
mod primitives;
mod instruction;
mod load_roblox;
mod integer;
struct Entity {
index_count: u32,
@ -226,8 +228,8 @@ impl GlobalState{
None
}else{
Some(ModelGraphicsInstance{
transform: glam::Mat4::from(instance.transform),
normal_transform: glam::Mat3::from(instance.transform.matrix3.inverse().transpose()),
transform: instance.transform.into(),
normal_transform: Into::<glam::Mat3>::into(instance.transform.matrix3).inverse().transpose(),
color: instance.color,
})
}
@ -243,11 +245,11 @@ impl GlobalState{
//create new texture_index
let texture_index=unique_textures.len();
unique_textures.push(group.texture);
unique_texture_models.push(model::IndexedModelSingleTexture{
unique_pos:model.unique_pos.clone(),
unique_tex:model.unique_tex.clone(),
unique_normal:model.unique_normal.clone(),
unique_color:model.unique_color.clone(),
unique_texture_models.push(model_graphics::IndexedModelGraphicsSingleTexture{
unique_pos:model.unique_pos.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(),
unique_tex:model.unique_tex.iter().map(|v|*v.as_ref()).collect(),
unique_normal:model.unique_normal.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(),
unique_color:model.unique_color.iter().map(|v|*v.as_ref()).collect(),
unique_vertices:model.unique_vertices.clone(),
texture:group.texture,
groups:Vec::new(),
@ -255,7 +257,7 @@ impl GlobalState{
});
texture_index
};
unique_texture_models[id+texture_index].groups.push(model::IndexedGroupFixedTexture{
unique_texture_models[id+texture_index].groups.push(model_graphics::IndexedGroupFixedTexture{
polys:group.polys,
});
}
@ -278,7 +280,7 @@ impl GlobalState{
}else{
let i=vertices.len() as u16;
let vertex=&model.unique_vertices[vertex_index as usize];
vertices.push(Vertex {
vertices.push(model_graphics::GraphicsVertex{
pos: model.unique_pos[vertex.pos as usize],
tex: model.unique_tex[vertex.tex as usize],
normal: model.unique_normal[vertex.normal as usize],
@ -292,7 +294,7 @@ impl GlobalState{
}
}
entities.push(indices);
models.push(model::ModelSingleTexture{
models.push(model_graphics::ModelGraphicsSingleTexture{
instances:model.instances,
vertices,
entities,
@ -417,50 +419,50 @@ impl framework::Example for GlobalState {
//wee
let user_settings=settings::read_user_settings();
let mut indexed_models = Vec::new();
indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teslacyberv3.0.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()));
indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teslacyberv3.0.obj")[..]).unwrap(),glam::Vec4::ONE));
indexed_models.push(primitives::unit_sphere());
indexed_models.push(primitives::unit_cylinder());
indexed_models.push(primitives::unit_cube());
println!("models.len = {:?}", indexed_models.len());
indexed_models[0].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.)),
indexed_models[0].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.))).unwrap(),
..Default::default()
});
//quad monkeys
indexed_models[1].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(10.,5.,10.)),
indexed_models[1].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,10.))).unwrap(),
..Default::default()
});
indexed_models[1].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(20.,5.,10.)),
indexed_models[1].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,10.))).unwrap(),
color:glam::vec4(1.0,0.0,0.0,1.0),
..Default::default()
});
indexed_models[1].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(10.,5.,20.)),
indexed_models[1].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,20.))).unwrap(),
color:glam::vec4(0.0,1.0,0.0,1.0),
..Default::default()
});
indexed_models[1].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(20.,5.,20.)),
indexed_models[1].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,20.))).unwrap(),
color:glam::vec4(0.0,0.0,1.0,1.0),
..Default::default()
});
//decorative monkey
indexed_models[1].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(15.,10.,15.)),
indexed_models[1].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(15.,10.,15.))).unwrap(),
color:glam::vec4(0.5,0.5,0.5,0.5),
attributes:model::CollisionAttributes::Decoration,
..Default::default()
});
//teapot
indexed_models[2].instances.push(ModelInstance{
transform:glam::Affine3A::from_scale_rotation_translation(glam::vec3(0.5, 1.0, 0.2),glam::quat(-0.22248298016985793,-0.839457167990537,-0.05603504040830783,-0.49261857546227916),glam::vec3(-10.,7.,10.)),
indexed_models[2].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_scale_rotation_translation(glam::vec3(0.5, 1.0, 0.2),glam::quat(-0.22248298016985793,-0.839457167990537,-0.05603504040830783,-0.49261857546227916),glam::vec3(-10.,7.,10.))).unwrap(),
..Default::default()
});
//ground
indexed_models[3].instances.push(ModelInstance{
transform:glam::Affine3A::from_translation(glam::vec3(0.,0.,0.))*glam::Affine3A::from_scale(glam::vec3(160.0, 1.0, 160.0)),
indexed_models[3].instances.push(model::ModelInstance{
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(0.,0.,0.))*glam::Affine3A::from_scale(glam::vec3(160.0, 1.0, 160.0))).unwrap(),
..Default::default()
});
@ -728,7 +730,7 @@ impl framework::Example for GlobalState {
module: &shader,
entry_point: "vs_entity_texture",
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
array_stride: std::mem::size_of::<GraphicsVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32x3, 3 => Float32x4],
}],
@ -818,7 +820,7 @@ impl framework::Example for GlobalState {
let indexed_model_instances=model::IndexedModelInstances{
textures:Vec::new(),
models:indexed_models,
spawn_point:glam::Vec3::Y*50.0,
spawn_point:integer::Planar64Vec3::Y*50,
modes:Vec::new(),
};
@ -918,7 +920,7 @@ impl framework::Example for GlobalState {
#[allow(clippy::single_match)]
fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: winit::event::WindowEvent) {
let time=self.start_time.elapsed().as_nanos() as i64;
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
match event {
winit::event::WindowEvent::DroppedFile(path) => self.load_file(path,device,queue),
winit::event::WindowEvent::Focused(state)=>{
@ -1010,7 +1012,7 @@ impl framework::Example for GlobalState {
fn device_event(&mut self, window: &winit::window::Window, event: winit::event::DeviceEvent) {
//there's no way this is the best way get a timestamp.
let time=self.start_time.elapsed().as_nanos() as i64;
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
match event {
winit::event::DeviceEvent::MouseMotion {
delta,//these (f64,f64) are integers on my machine
@ -1065,7 +1067,7 @@ impl framework::Example for GlobalState {
_spawner: &framework::Spawner,
) {
//ideally this would be scheduled to execute and finish right before the render.
let time=self.start_time.elapsed().as_nanos() as i64;
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
self.physics_thread.send(TimedInstruction{
time,
instruction:InputInstruction::Idle,

View File

@ -1,12 +1,6 @@
use bytemuck::{Pod, Zeroable};
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct Vertex {
pub pos: [f32; 3],
pub tex: [f32; 2],
pub normal: [f32; 3],
pub color: [f32; 4],
}
use crate::integer::{Planar64,Planar64Vec3,Planar64Affine3};
pub type TextureCoordinate=glam::Vec2;
pub type Color4=glam::Vec4;
#[derive(Clone,Hash,PartialEq,Eq)]
pub struct IndexedVertex{
pub pos:u32,
@ -22,50 +16,25 @@ pub struct IndexedGroup{
pub polys:Vec<IndexedPolygon>,
}
pub struct IndexedModel{
pub unique_pos:Vec<[f32; 3]>,
pub unique_tex:Vec<[f32; 2]>,
pub unique_normal:Vec<[f32; 3]>,
pub unique_color:Vec<[f32; 4]>,
pub unique_pos:Vec<Planar64Vec3>,
pub unique_normal:Vec<Planar64Vec3>,
pub unique_tex:Vec<TextureCoordinate>,
pub unique_color:Vec<Color4>,
pub unique_vertices:Vec<IndexedVertex>,
pub groups: Vec<IndexedGroup>,
pub instances:Vec<ModelInstance>,
}
pub struct IndexedGroupFixedTexture{
pub polys:Vec<IndexedPolygon>,
}
pub struct IndexedModelSingleTexture{
pub unique_pos:Vec<[f32; 3]>,
pub unique_tex:Vec<[f32; 2]>,
pub unique_normal:Vec<[f32; 3]>,
pub unique_color:Vec<[f32; 4]>,
pub unique_vertices:Vec<IndexedVertex>,
pub texture:Option<u32>,//RenderPattern? material/texture/shader/flat color
pub groups: Vec<IndexedGroupFixedTexture>,
pub instances:Vec<ModelGraphicsInstance>,
}
pub struct ModelSingleTexture{
pub instances: Vec<ModelGraphicsInstance>,
pub vertices: Vec<Vertex>,
pub entities: Vec<Vec<u16>>,
pub texture: Option<u32>,
}
#[derive(Clone)]
pub struct ModelGraphicsInstance{
pub transform:glam::Mat4,
pub normal_transform:glam::Mat3,
pub color:glam::Vec4,
}
pub struct ModelInstance{
//pub id:u64,//this does not actually help with map fixes resimulating bots, they must always be resimulated
pub transform:glam::Affine3A,
pub color:glam::Vec4,//transparency is in here
pub transform:Planar64Affine3,
pub color:Color4,//transparency is in here
pub attributes:CollisionAttributes,
pub temp_indexing:Vec<TempIndexedAttributes>,
}
impl std::default::Default for ModelInstance{
fn default() -> Self {
Self{
color:glam::Vec4::ONE,
color:Color4::ONE,
transform:Default::default(),
attributes:Default::default(),
temp_indexing:Default::default(),
@ -77,7 +46,7 @@ pub struct IndexedModelInstances{
pub models:Vec<IndexedModel>,
//may make this into an object later.
pub modes:Vec<ModeDescription>,
pub spawn_point:glam::Vec3,
pub spawn_point:Planar64Vec3,
}
//stage description referencing flattened ids is spooky, but the map loading is meant to be deterministic.
pub struct ModeDescription{
@ -131,13 +100,13 @@ pub struct ContactingLadder{
//you have this effect while intersecting
#[derive(Clone)]
pub struct IntersectingWater{
pub viscosity:i64,
pub density:i64,
pub current:glam::Vec3,
pub viscosity:Planar64,
pub density:Planar64,
pub current:Planar64Vec3,
}
#[derive(Clone)]
pub struct IntersectingAccelerator{
pub acceleration:glam::Vec3
pub acceleration:Planar64Vec3
}
//All models can be given these attributes
#[derive(Clone)]
@ -146,7 +115,7 @@ pub struct GameMechanicJumpLimit{
}
#[derive(Clone)]
pub struct GameMechanicBooster{
pub velocity:glam::Vec3,
pub velocity:Planar64Vec3,
}
#[derive(Clone)]
pub enum ZoneBehaviour{
@ -227,7 +196,7 @@ impl std::default::Default for CollisionAttributes{
}
}
pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:[f32;4]) -> Vec<IndexedModel>{
pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:Color4)->Vec<IndexedModel>{
let mut unique_vertex_index = std::collections::HashMap::<obj::IndexTuple,u32>::new();
return data.objects.iter().map(|object|{
unique_vertex_index.clear();
@ -257,9 +226,9 @@ pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:[f32;4]) ->
}
}).collect();
IndexedModel{
unique_pos: data.position.clone(),
unique_tex: data.texture.clone(),
unique_normal: data.normal.clone(),
unique_pos: data.position.iter().map(|&v|Planar64Vec3::try_from(v).unwrap()).collect(),
unique_tex: data.texture.iter().map(|&v|TextureCoordinate::from_array(v)).collect(),
unique_normal: data.normal.iter().map(|&v|Planar64Vec3::try_from(v).unwrap()).collect(),
unique_color: vec![color],
unique_vertices,
groups,

35
src/model_graphics.rs Normal file
View File

@ -0,0 +1,35 @@
use bytemuck::{Pod, Zeroable};
use crate::model::{IndexedVertex,IndexedPolygon};
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct GraphicsVertex {
pub pos: [f32; 3],
pub tex: [f32; 2],
pub normal: [f32; 3],
pub color: [f32; 4],
}
pub struct IndexedGroupFixedTexture{
pub polys:Vec<IndexedPolygon>,
}
pub struct IndexedModelGraphicsSingleTexture{
pub unique_pos:Vec<[f32; 3]>,
pub unique_tex:Vec<[f32; 2]>,
pub unique_normal:Vec<[f32; 3]>,
pub unique_color:Vec<[f32; 4]>,
pub unique_vertices:Vec<IndexedVertex>,
pub texture:Option<u32>,//RenderPattern? material/texture/shader/flat color
pub groups: Vec<IndexedGroupFixedTexture>,
pub instances:Vec<ModelGraphicsInstance>,
}
pub struct ModelGraphicsSingleTexture{
pub instances: Vec<ModelGraphicsInstance>,
pub vertices: Vec<GraphicsVertex>,
pub entities: Vec<Vec<u16>>,
pub texture: Option<u32>,
}
#[derive(Clone)]
pub struct ModelGraphicsInstance{
pub transform:glam::Mat4,
pub normal_transform:glam::Mat3,
pub color:glam::Vec4,
}

1
src/model_physics.rs Normal file
View File

@ -0,0 +1 @@
//

View File

@ -1,5 +1,7 @@
use crate::{instruction::{InstructionEmitter, InstructionConsumer, TimedInstruction}, zeroes::zeroes2};
use crate::integer::{Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64,Ratio64Vec2};
#[derive(Debug)]
pub enum PhysicsInstruction {
CollisionStart(RelativeCollision),
@ -47,31 +49,12 @@ pub enum InputInstruction {
//for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation.
}
#[derive(Clone)]
#[derive(Clone,Hash)]
pub struct Body {
position: glam::Vec3,//I64 where 2^32 = 1 u
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
acceleration: glam::Vec3,//I64 where 2^32 = 1 u/s/s
time: TIME,//nanoseconds x xxxxD!
}
trait MyHash{
fn hash(&self) -> u64;
}
impl MyHash for Body {
fn hash(&self) -> u64 {
let mut hasher=std::collections::hash_map::DefaultHasher::new();
for &el in self.position.as_ref().iter() {
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
}
for &el in self.velocity.as_ref().iter() {
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
}
for &el in self.acceleration.as_ref().iter() {
std::hash::Hasher::write(&mut hasher, el.to_ne_bytes().as_slice());
}
std::hash::Hasher::write(&mut hasher, self.time.to_ne_bytes().as_slice());
return std::hash::Hasher::finish(&hasher);//hash check to see if walk target is valid
}
position: Planar64Vec3,//I64 where 2^32 = 1 u
velocity: Planar64Vec3,//I64 where 2^32 = 1 u/s
acceleration: Planar64Vec3,//I64 where 2^32 = 1 u/s/s
time:Time,//nanoseconds x xxxxD!
}
pub enum MoveRestriction {
@ -92,7 +75,7 @@ impl InputState {
}
}
impl crate::instruction::InstructionEmitter<InputInstruction> for InputState{
fn next_instruction(&self, time_limit:crate::body::TIME) -> Option<TimedInstruction<InputInstruction>> {
fn next_instruction(&self, time_limit:crate::body::Time) -> Option<TimedInstruction<InputInstruction>> {
//this is polled by PhysicsState for actions like Jump
//no, it has to be the other way around. physics is run up until the jump instruction, and then the jump instruction is pushed.
self.queue.get(0)
@ -110,24 +93,24 @@ impl crate::instruction::InstructionConsumer<InputInstruction> for InputState{
#[derive(Clone,Debug)]
pub struct MouseState {
pub pos: glam::IVec2,
pub time: TIME,
pub time:Time,
}
impl Default for MouseState{
fn default() -> Self {
Self {
time:0,
time:Time::ZERO,
pos:glam::IVec2::ZERO,
}
}
}
impl MouseState {
pub fn lerp(&self,target:&MouseState,time:TIME)->glam::IVec2 {
pub fn lerp(&self,target:&MouseState,time:Time)->glam::IVec2 {
let m0=self.pos.as_i64vec2();
let m1=target.pos.as_i64vec2();
//these are deltas
let t1t=(target.time-time) as i64;
let tt0=(time-self.time) as i64;
let dt=(target.time-self.time) as i64;
let t1t=(target.time-time).nanos();
let tt0=(time-self.time).nanos();
let dt=(target.time-self.time).nanos();
((m0*t1t+m1*tt0)/dt).as_ivec2()
}
}
@ -137,55 +120,71 @@ pub enum WalkEnum{
Transient,
}
pub struct WalkState {
pub target_velocity: glam::Vec3,
pub target_time: TIME,
pub target_velocity: Planar64Vec3,
pub target_time: Time,
pub state: WalkEnum,
}
impl WalkState {
pub fn new() -> Self {
Self{
target_velocity:glam::Vec3::ZERO,
target_time:0,
target_velocity:Planar64Vec3::ZERO,
target_time:Time::ZERO,
state:WalkEnum::Reached,
}
}
}
#[derive(Clone)]
pub struct PhysicsCamera {
offset: glam::Vec3,
angles: glam::DVec2,//YAW AND THEN PITCH
//punch: glam::Vec3,
//punch_velocity: glam::Vec3,
sensitivity: glam::DVec2,
mouse:MouseState,
offset: Planar64Vec3,
//punch: Planar64Vec3,
//punch_velocity: Planar64Vec3,
sensitivity:Ratio64Vec2,//dots to Angle32 ratios
mouse:MouseState,//last seen absolute mouse pos
clamped_mouse_pos:glam::IVec2,//angles are calculated from this cumulative value
angle_pitch_lower_limit:Angle32,
angle_pitch_upper_limit:Angle32,
//angle limits could be an enum + struct that defines whether it's limited and selects clamp or wrap depending
// enum AngleLimit{
// Unlimited,
// Limited{lower:Angle32,upper:Angle32},
// }
//pitch_limit:AngleLimit,
//yaw_limit:AngleLimit,
}
#[inline]
fn mat3_from_rotation_y_f64(angle: f64) -> glam::Mat3 {
let (sina, cosa) = angle.sin_cos();
glam::Mat3::from_cols(
glam::Vec3::new(cosa as f32, 0.0, -sina as f32),
glam::Vec3::Y,
glam::Vec3::new(sina as f32, 0.0, cosa as f32),
)
}
impl PhysicsCamera {
pub fn from_offset(offset:glam::Vec3) -> Self {
pub fn from_offset(offset:Planar64Vec3) -> Self {
Self{
offset,
angles: glam::DVec2::ZERO,
sensitivity: glam::dvec2(1.0/1024.0,1.0/1024.0),
mouse:MouseState{pos:glam::IVec2::ZERO,time:-1},//escape initialization hell divide by zero
sensitivity:Ratio64Vec2::ONE*200_000,
mouse:MouseState::default(),//t=0 does not cause divide by zero because it's immediately replaced
clamped_mouse_pos:glam::IVec2::ZERO,
angle_pitch_lower_limit:-Angle32::FRAC_PI_2,
angle_pitch_upper_limit:Angle32::FRAC_PI_2,
}
}
pub fn simulate_move_angles(&self, mouse_pos: glam::IVec2) -> glam::DVec2 {
let mut a=self.angles-self.sensitivity*(mouse_pos-self.mouse.pos).as_dvec2();
a.y=a.y.clamp(-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2);
return a
pub fn move_mouse(&mut self,mouse_pos:glam::IVec2){
let mut unclamped_mouse_pos=self.clamped_mouse_pos+mouse_pos-self.mouse.pos;
unclamped_mouse_pos.y=unclamped_mouse_pos.y.clamp(
self.sensitivity.y.rhs_div_int(self.angle_pitch_lower_limit.get() as i64) as i32,
self.sensitivity.y.rhs_div_int(self.angle_pitch_upper_limit.get() as i64) as i32,
);
self.clamped_mouse_pos=unclamped_mouse_pos;
}
fn simulate_move_rotation_y(&self, mouse_pos_x: i32) -> glam::Mat3 {
mat3_from_rotation_y_f64(self.angles.x-self.sensitivity.x*((mouse_pos_x-self.mouse.pos.x) as f64))
pub fn simulate_move_angles(&self,mouse_pos:glam::IVec2)->glam::Vec2 {
let a=-self.sensitivity.mul_int((mouse_pos-self.mouse.pos+self.clamped_mouse_pos).as_i64vec2());
let ax=Angle32::wrap_from_i64(a.x);
let ay=Angle32::clamp_from_i64(a.y)
//clamp to actual vertical cam limit
.clamp(self.angle_pitch_lower_limit,self.angle_pitch_upper_limit);
return glam::vec2(ax.into(),ay.into());
}
fn simulate_move_rotation_y(&self,mouse_pos_x:i32)->Planar64Mat3{
let ax=-self.sensitivity.x.mul_int((mouse_pos_x-self.mouse.pos.x+self.clamped_mouse_pos.x) as i64);
Planar64Mat3::from_rotation_y(Angle32::wrap_from_i64(ax))
}
}
@ -206,52 +205,50 @@ pub struct WorldState{}
pub struct StyleModifiers{
pub controls_mask:u32,//controls which are unable to be activated
pub controls_held:u32,//controls which must be active to be able to strafe
pub mv:f32,
pub walkspeed:f32,
pub friction:f32,
pub walk_accel:f32,
pub gravity:glam::Vec3,
pub strafe_tick_num:TIME,
pub strafe_tick_den:TIME,
pub hitbox_halfsize:glam::Vec3,
pub mv:Planar64,
pub walkspeed:Planar64,
pub friction:Planar64,
pub walk_accel:Planar64,
pub gravity:Planar64Vec3,
pub strafe_tick_rate:Ratio64,
pub hitbox_halfsize:Planar64Vec3,
}
impl std::default::Default for StyleModifiers{
fn default() -> Self {
Self{
controls_mask: !0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
controls_held: 0,
strafe_tick_num: 100,//100t
strafe_tick_den: 1_000_000_000,
gravity: glam::vec3(0.0,-100.0,0.0),
friction: 1.2,
walk_accel: 90.0,
mv: 2.7,
walkspeed: 18.0,
hitbox_halfsize: glam::vec3(1.0,2.5,1.0),
strafe_tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
gravity: Planar64Vec3::int(0,-100,0),
friction: Planar64::int(12)/10,
walk_accel: Planar64::int(90),
mv: Planar64::int(27)/10,
walkspeed: Planar64::int(18),
hitbox_halfsize: Planar64Vec3::int(2,5,2)/2,
}
}
}
impl StyleModifiers{
const CONTROL_MOVEFORWARD:u32 = 0b00000001;
const CONTROL_MOVEBACK:u32 = 0b00000010;
const CONTROL_MOVERIGHT:u32 = 0b00000100;
const CONTROL_MOVELEFT:u32 = 0b00001000;
const CONTROL_MOVEUP:u32 = 0b00010000;
const CONTROL_MOVEDOWN:u32 = 0b00100000;
const CONTROL_JUMP:u32 = 0b01000000;
const CONTROL_ZOOM:u32 = 0b10000000;
const CONTROL_MOVEFORWARD:u32=0b00000001;
const CONTROL_MOVEBACK:u32=0b00000010;
const CONTROL_MOVERIGHT:u32=0b00000100;
const CONTROL_MOVELEFT:u32=0b00001000;
const CONTROL_MOVEUP:u32=0b00010000;
const CONTROL_MOVEDOWN:u32=0b00100000;
const CONTROL_JUMP:u32=0b01000000;
const CONTROL_ZOOM:u32=0b10000000;
const FORWARD_DIR:glam::Vec3 = glam::Vec3::NEG_Z;
const RIGHT_DIR:glam::Vec3 = glam::Vec3::X;
const UP_DIR:glam::Vec3 = glam::Vec3::Y;
const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X;
const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
fn get_control(&self,control:u32,controls:u32)->bool{
controls&self.controls_mask&control==control
}
fn get_control_dir(&self,controls:u32)->glam::Vec3{
fn get_control_dir(&self,controls:u32)->Planar64Vec3{
//don't get fancy just do it
let mut control_dir:glam::Vec3 = glam::Vec3::ZERO;
let mut control_dir:Planar64Vec3 = Planar64Vec3::ZERO;
//Disallow strafing if held controls are not held
if controls&self.controls_held!=self.controls_held{
return control_dir;
@ -262,10 +259,10 @@ impl StyleModifiers{
control_dir+=Self::FORWARD_DIR;
}
if controls & Self::CONTROL_MOVEBACK == Self::CONTROL_MOVEBACK {
control_dir+=-Self::FORWARD_DIR;
control_dir-=Self::FORWARD_DIR;
}
if controls & Self::CONTROL_MOVELEFT == Self::CONTROL_MOVELEFT {
control_dir+=-Self::RIGHT_DIR;
control_dir-=Self::RIGHT_DIR;
}
if controls & Self::CONTROL_MOVERIGHT == Self::CONTROL_MOVERIGHT {
control_dir+=Self::RIGHT_DIR;
@ -274,14 +271,14 @@ impl StyleModifiers{
control_dir+=Self::UP_DIR;
}
if controls & Self::CONTROL_MOVEDOWN == Self::CONTROL_MOVEDOWN {
control_dir+=-Self::UP_DIR;
control_dir-=Self::UP_DIR;
}
return control_dir
}
}
pub struct PhysicsState{
pub time:TIME,
pub time:Time,
pub body:Body,
pub world:WorldState,//currently there is only one state the world can be in
pub game:GameMechanicsState,
@ -303,7 +300,7 @@ pub struct PhysicsState{
pub mode_from_mode_id:std::collections::HashMap::<u32,usize>,
//the spawn point is where you spawn when you load into the map.
//This is not the same as Reset which teleports you to Spawn0
pub spawn_point:glam::Vec3,
pub spawn_point:Planar64Vec3,
}
#[derive(Clone)]
pub struct PhysicsOutputState{
@ -312,7 +309,7 @@ pub struct PhysicsOutputState{
}
impl PhysicsOutputState{
pub fn adjust_mouse(&self,mouse:&MouseState)->(glam::Vec3,glam::Vec2){
(self.body.extrapolated_position(mouse.time)+self.camera.offset,self.camera.simulate_move_angles(mouse.pos).as_vec2())
((self.body.extrapolated_position(mouse.time)+self.camera.offset).into(),self.camera.simulate_move_angles(mouse.pos))
}
}
@ -335,15 +332,15 @@ pub struct ModelPhysics {
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
//in this iteration, all it needs is extents.
mesh: TreyMesh,
transform:glam::Affine3A,
transform:crate::integer::Planar64Affine3,
attributes:PhysicsCollisionAttributes,
}
impl ModelPhysics {
fn from_model_transform_attributes(model:&crate::model::IndexedModel,transform:&glam::Affine3A,attributes:PhysicsCollisionAttributes)->Self{
let mut aabb=TreyMesh::new();
fn from_model_transform_attributes(model:&crate::model::IndexedModel,transform:&crate::integer::Planar64Affine3,attributes:PhysicsCollisionAttributes)->Self{
let mut aabb=TreyMesh::default();
for indexed_vertex in &model.unique_vertices {
aabb.grow(transform.transform_point3(glam::Vec3::from_array(model.unique_pos[indexed_vertex.pos as usize])));
aabb.grow(transform.transform_point3(model.unique_pos[indexed_vertex.pos as usize]));
}
Self{
mesh:aabb,
@ -358,16 +355,16 @@ impl ModelPhysics {
crate::model::CollisionAttributes::Decoration=>None,
}
}
pub fn unit_vertices(&self) -> [glam::Vec3;8] {
pub fn unit_vertices(&self) -> [Planar64Vec3;8] {
TreyMesh::unit_vertices()
}
pub fn mesh(&self) -> &TreyMesh {
return &self.mesh;
}
pub fn face_mesh(&self,face:TreyMeshFace)->TreyMesh{
self.mesh.face(face)
}
pub fn face_normal(&self,face:TreyMeshFace) -> glam::Vec3 {
// pub fn face_mesh(&self,face:TreyMeshFace)->TreyMesh{
// self.mesh.face(face)
// }
pub fn face_normal(&self,face:TreyMeshFace) -> Planar64Vec3 {
TreyMesh::normal(face)//this is wrong for scale
}
}
@ -384,34 +381,32 @@ impl RelativeCollision {
pub fn model<'a>(&self,models:&'a Vec<ModelPhysics>)->Option<&'a ModelPhysics>{
models.get(self.model as usize)
}
pub fn mesh(&self,models:&Vec<ModelPhysics>) -> TreyMesh {
return self.model(models).unwrap().face_mesh(self.face).clone()
}
pub fn normal(&self,models:&Vec<ModelPhysics>) -> glam::Vec3 {
// pub fn mesh(&self,models:&Vec<ModelPhysics>) -> TreyMesh {
// return self.model(models).unwrap().face_mesh(self.face).clone()
// }
pub fn normal(&self,models:&Vec<ModelPhysics>) -> Planar64Vec3 {
return self.model(models).unwrap().face_normal(self.face)
}
}
pub type TIME = i64;
impl Body {
pub fn with_pva(position:glam::Vec3,velocity:glam::Vec3,acceleration:glam::Vec3) -> Self {
pub fn with_pva(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3) -> Self {
Self{
position,
velocity,
acceleration,
time: 0,
time:Time::ZERO,
}
}
pub fn extrapolated_position(&self,time: TIME)->glam::Vec3{
let dt=(time-self.time) as f64/1_000_000_000f64;
self.position+self.velocity*(dt as f32)+self.acceleration*((0.5*dt*dt) as f32)
pub fn extrapolated_position(&self,time:Time)->Planar64Vec3{
let dt=time-self.time;
self.position+self.velocity*dt+self.acceleration*(dt*dt/2)
}
pub fn extrapolated_velocity(&self,time: TIME)->glam::Vec3{
let dt=(time-self.time) as f64/1_000_000_000f64;
self.velocity+self.acceleration*(dt as f32)
pub fn extrapolated_velocity(&self,time:Time)->Planar64Vec3{
let dt=time-self.time;
self.velocity+self.acceleration*dt
}
pub fn advance_time(&mut self, time: TIME){
pub fn advance_time(&mut self,time:Time){
self.position=self.extrapolated_position(time);
self.velocity=self.extrapolated_velocity(time);
self.time=time;
@ -421,9 +416,9 @@ impl Body {
impl Default for PhysicsState{
fn default() -> Self {
Self{
spawn_point:glam::vec3(0.0,50.0,0.0),
body: Body::with_pva(glam::vec3(0.0,50.0,0.0),glam::vec3(0.0,0.0,0.0),glam::vec3(0.0,-100.0,0.0)),
time: 0,
spawn_point:Planar64Vec3::int(0,50,0),
body: Body::with_pva(Planar64Vec3::int(0,50,0),Planar64Vec3::int(0,0,0),Planar64Vec3::int(0,-100,0)),
time: Time::ZERO,
style:StyleModifiers::default(),
grounded: false,
contacts: std::collections::HashMap::new(),
@ -431,7 +426,7 @@ impl Default for PhysicsState{
models: Vec::new(),
bvh:crate::bvh::BvhNode::default(),
walk: WalkState::new(),
camera: PhysicsCamera::from_offset(glam::vec3(0.0,4.5-2.5,0.0)),
camera: PhysicsCamera::from_offset(Planar64Vec3::int(0,2,0)),//4.5-2.5=2
next_mouse: MouseState::default(),
controls: 0,
world:WorldState{},
@ -501,7 +496,7 @@ impl PhysicsState {
//shitty mice are 125Hz which is 8ms so this should cover that.
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if 10_000_000<ins.time-self.next_mouse.time{
if Time::from_millis(10)<ins.time-self.next_mouse.time{
//push an event to extrapolate no movement from
timeline.push_front(TimedInstruction{
time:last_mouse_time,
@ -629,7 +624,7 @@ impl PhysicsState {
}
}
//tickless gaming
pub fn run(&mut self, time_limit:TIME){
pub fn run(&mut self, time_limit:Time){
//prepare is ommitted - everything is done via instructions.
while let Some(instruction) = self.next_instruction(time_limit) {//collect
//process
@ -638,7 +633,7 @@ impl PhysicsState {
}
}
pub fn advance_time(&mut self, time: TIME){
pub fn advance_time(&mut self, time: Time){
self.body.advance_time(time);
self.time=time;
}
@ -648,32 +643,32 @@ impl PhysicsState {
}
fn jump(&mut self){
self.grounded=false;//do I need this?
let mut v=self.body.velocity+glam::Vec3::new(0.0,0.715588/2.0*100.0,0.0);
let mut v=self.body.velocity+Planar64Vec3::int(0,715588,0)/(2*1000000/100);//0.715588/2.0*100.0
self.contact_constrain_velocity(&mut v);
self.body.velocity=v;
}
fn contact_constrain_velocity(&self,velocity:&mut glam::Vec3){
fn contact_constrain_velocity(&self,velocity:&mut Planar64Vec3){
for (_,contact) in &self.contacts {
let n=contact.normal(&self.models);
let d=velocity.dot(n);
if d<0f32{
(*velocity)-=d/n.length_squared()*n;
if d<Planar64::ZERO{
(*velocity)-=n*(d/n.dot(n));
}
}
}
fn contact_constrain_acceleration(&self,acceleration:&mut glam::Vec3){
fn contact_constrain_acceleration(&self,acceleration:&mut Planar64Vec3){
for (_,contact) in &self.contacts {
let n=contact.normal(&self.models);
let d=acceleration.dot(n);
if d<0f32{
(*acceleration)-=d/n.length_squared()*n;
if d<Planar64::ZERO{
(*acceleration)-=n*(d/n.dot(n));
}
}
}
fn next_strafe_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
return Some(TimedInstruction{
time:(self.time*self.style.strafe_tick_num/self.style.strafe_tick_den+1)*self.style.strafe_tick_den/self.style.strafe_tick_num,
time:Time::from_nanos(self.style.strafe_tick_rate.rhs_div_int(self.style.strafe_tick_rate.mul_int(self.time.nanos())+1)),
//only poll the physics if there is a before and after mouse event
instruction:PhysicsInstruction::StrafeTick
});
@ -716,19 +711,21 @@ impl PhysicsState {
let mut v=self.walk.target_velocity;
self.contact_constrain_velocity(&mut v);
let mut target_diff=v-self.body.velocity;
target_diff.y=0f32;
if target_diff==glam::Vec3::ZERO{
let mut a=glam::Vec3::ZERO;
//remove normal component
target_diff-=Planar64Vec3::Y*target_diff.y();
if target_diff==Planar64Vec3::ZERO{
let mut a=Planar64Vec3::ZERO;
self.contact_constrain_acceleration(&mut a);
self.body.acceleration=a;
self.walk.state=WalkEnum::Reached;
}else{
let accel=self.style.walk_accel.min(self.style.gravity.length()*self.style.friction);
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
let accel=self.style.walk_accel.min(self.style.gravity.dot(Planar64Vec3::NEG_Y)*self.style.friction);
let time_delta=target_diff.length()/accel;
let mut a=target_diff/time_delta;
let mut a=target_diff.with_length(accel);
self.contact_constrain_acceleration(&mut a);
self.body.acceleration=a;
self.walk.target_time=self.body.time+((time_delta as f64)*1_000_000_000f64) as TIME;
self.walk.target_time=self.body.time+Time::from(time_delta);
self.walk.state=WalkEnum::Transient;
}
}else{
@ -750,13 +747,13 @@ impl PhysicsState {
}
}
fn mesh(&self) -> TreyMesh {
let mut aabb=TreyMesh::new();
let mut aabb=TreyMesh::default();
for vertex in TreyMesh::unit_vertices(){
aabb.grow(self.body.position+self.style.hitbox_halfsize*vertex);
}
aabb
}
fn predict_collision_end(&self,time:TIME,time_limit:TIME,collision_data:&RelativeCollision) -> Option<TimedInstruction<PhysicsInstruction>> {
fn predict_collision_end(&self,time:Time,time_limit:Time,collision_data:&RelativeCollision) -> Option<TimedInstruction<PhysicsInstruction>> {
//must treat cancollide false objects differently: you may not exit through the same face you entered.
//RelativeCollsion must reference the full model instead of a particular face
//this is Ctrl+C Ctrl+V of predict_collision_start but with v=-v before the calc and t=-t after the calc
@ -769,26 +766,26 @@ impl PhysicsState {
//collect x
match collision_data.face {
TreyMeshFace::Top|TreyMeshFace::Back|TreyMeshFace::Bottom|TreyMeshFace::Front=>{
for t in zeroes2(mesh0.max.x-mesh1.min.x,v.x,0.5*a.x) {
for t in zeroes2(mesh0.max.x()-mesh1.min.x(),v.x(),a.x()/2) {
//negative t = back in time
//must be moving towards surface to collide
//must beat the current soonest collision time
//must be moving towards surface
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&0f32<v.x+a.x*-t{
let t_time=self.body.time-Time::from(t);
if time<=t_time&&t_time<best_time&&Planar64::ZERO<v.x()+a.x()*-t{
//collect valid t
best_time=t_time;
exit_face=Some(TreyMeshFace::Left);
break;
}
}
for t in zeroes2(mesh0.min.x-mesh1.max.x,v.x,0.5*a.x) {
for t in zeroes2(mesh0.min.x()-mesh1.max.x(),v.x(),a.x()/2) {
//negative t = back in time
//must be moving towards surface to collide
//must beat the current soonest collision time
//must be moving towards surface
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&v.x+a.x*-t<0f32{
let t_time=self.body.time-Time::from(t);
if time<=t_time&&t_time<best_time&&v.x()+a.x()*-t<Planar64::ZERO{
//collect valid t
best_time=t_time;
exit_face=Some(TreyMeshFace::Right);
@ -798,14 +795,14 @@ impl PhysicsState {
},
TreyMeshFace::Left=>{
//generate event if v.x<0||a.x<0
if -v.x<0f32{
if -v.x()<Planar64::ZERO{
best_time=time;
exit_face=Some(TreyMeshFace::Left);
}
},
TreyMeshFace::Right=>{
//generate event if 0<v.x||0<a.x
if 0f32<(-v.x){
if Planar64::ZERO<(-v.x()){
best_time=time;
exit_face=Some(TreyMeshFace::Right);
}
@ -814,26 +811,26 @@ impl PhysicsState {
//collect y
match collision_data.face {
TreyMeshFace::Left|TreyMeshFace::Back|TreyMeshFace::Right|TreyMeshFace::Front=>{
for t in zeroes2(mesh0.max.y-mesh1.min.y,v.y,0.5*a.y) {
for t in zeroes2(mesh0.max.y()-mesh1.min.y(),v.y(),a.y()/2) {
//negative t = back in time
//must be moving towards surface to collide
//must beat the current soonest collision time
//must be moving towards surface
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&0f32<v.y+a.y*-t{
let t_time=self.body.time-Time::from(t);
if time<=t_time&&t_time<best_time&&Planar64::ZERO<v.y()+a.y()*-t{
//collect valid t
best_time=t_time;
exit_face=Some(TreyMeshFace::Bottom);
break;
}
}
for t in zeroes2(mesh0.min.y-mesh1.max.y,v.y,0.5*a.y) {
for t in zeroes2(mesh0.min.y()-mesh1.max.y(),v.y(),a.y()/2) {
//negative t = back in time
//must be moving towards surface to collide
//must beat the current soonest collision time
//must be moving towards surface
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&v.y+a.y*-t<0f32{
let t_time=self.body.time-Time::from(t);
if time<=t_time&&t_time<best_time&&v.y()+a.y()*-t<Planar64::ZERO{
//collect valid t
best_time=t_time;
exit_face=Some(TreyMeshFace::Top);
@ -843,14 +840,14 @@ impl PhysicsState {
},
TreyMeshFace::Bottom=>{
//generate event if v.y<0||a.y<0
if -v.y<0f32{
if -v.y()<Planar64::ZERO{
best_time=time;
exit_face=Some(TreyMeshFace::Bottom);
}
},
TreyMeshFace::Top=>{
//generate event if 0<v.y||0<a.y
if 0f32<(-v.y){
if Planar64::ZERO<(-v.y()){
best_time=time;
exit_face=Some(TreyMeshFace::Top);
}
@ -859,26 +856,26 @@ impl PhysicsState {
//collect z
match collision_data.face {
TreyMeshFace::Left|TreyMeshFace::Bottom|TreyMeshFace::Right|TreyMeshFace::Top=>{
for t in zeroes2(mesh0.max.z-mesh1.min.z,v.z,0.5*a.z) {
for t in zeroes2(mesh0.max.z()-mesh1.min.z(),v.z(),a.z()/2) {
//negative t = back in time
//must be moving towards surface to collide
//must beat the current soonest collision time
//must be moving towards surface
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&0f32<v.z+a.z*-t{
let t_time=self.body.time-Time::from(t);
if time<=t_time&&t_time<best_time&&Planar64::ZERO<v.z()+a.z()*-t{
//collect valid t
best_time=t_time;
exit_face=Some(TreyMeshFace::Front);
break;
}
}
for t in zeroes2(mesh0.min.z-mesh1.max.z,v.z,0.5*a.z) {
for t in zeroes2(mesh0.min.z()-mesh1.max.z(),v.z(),a.z()/2) {
//negative t = back in time
//must be moving towards surface to collide
//must beat the current soonest collision time
//must be moving towards surface
let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&v.z+a.z*-t<0f32{
let t_time=self.body.time-Time::from(t);
if time<=t_time&&t_time<best_time&&v.z()+a.z()*-t<Planar64::ZERO{
//collect valid t
best_time=t_time;
exit_face=Some(TreyMeshFace::Back);
@ -888,14 +885,14 @@ impl PhysicsState {
},
TreyMeshFace::Front=>{
//generate event if v.z<0||a.z<0
if -v.z<0f32{
if -v.z()<Planar64::ZERO{
best_time=time;
exit_face=Some(TreyMeshFace::Front);
}
},
TreyMeshFace::Back=>{
//generate event if 0<v.z||0<a.z
if 0f32<(-v.z){
if Planar64::ZERO<(-v.z()){
best_time=time;
exit_face=Some(TreyMeshFace::Back);
}
@ -910,7 +907,7 @@ impl PhysicsState {
}
None
}
fn predict_collision_start(&self,time:TIME,time_limit:TIME,model_id:u32) -> Option<TimedInstruction<PhysicsInstruction>> {
fn predict_collision_start(&self,time:Time,time_limit:Time,model_id:u32) -> Option<TimedInstruction<PhysicsInstruction>> {
let mesh0=self.mesh();
let mesh1=self.models.get(model_id as usize).unwrap().mesh();
let (p,v,a,body_time)=(self.body.position,self.body.velocity,self.body.acceleration,self.body.time);
@ -918,15 +915,15 @@ impl PhysicsState {
let mut best_time=time_limit;
let mut best_face:Option<TreyMeshFace>=None;
//collect x
for t in zeroes2(mesh0.max.x-mesh1.min.x,v.x,0.5*a.x) {
for t in zeroes2(mesh0.max.x()-mesh1.min.x(),v.x(),a.x()/2) {
//must collide now or in the future
//must beat the current soonest collision time
//must be moving towards surface
let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&0f32<v.x+a.x*t{
let t_time=body_time+Time::from(t);
if time<=t_time&&t_time<best_time&&Planar64::ZERO<v.x()+a.x()*t{
let dp=self.body.extrapolated_position(t_time)-p;
//faces must be overlapping
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
if mesh1.min.y()<mesh0.max.y()+dp.y()&&mesh0.min.y()+dp.y()<mesh1.max.y()&&mesh1.min.z()<mesh0.max.z()+dp.z()&&mesh0.min.z()+dp.z()<mesh1.max.z() {
//collect valid t
best_time=t_time;
best_face=Some(TreyMeshFace::Left);
@ -934,15 +931,15 @@ impl PhysicsState {
}
}
}
for t in zeroes2(mesh0.min.x-mesh1.max.x,v.x,0.5*a.x) {
for t in zeroes2(mesh0.min.x()-mesh1.max.x(),v.x(),a.x()/2) {
//must collide now or in the future
//must beat the current soonest collision time
//must be moving towards surface
let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&v.x+a.x*t<0f32{
let t_time=body_time+Time::from(t);
if time<=t_time&&t_time<best_time&&v.x()+a.x()*t<Planar64::ZERO{
let dp=self.body.extrapolated_position(t_time)-p;
//faces must be overlapping
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
if mesh1.min.y()<mesh0.max.y()+dp.y()&&mesh0.min.y()+dp.y()<mesh1.max.y()&&mesh1.min.z()<mesh0.max.z()+dp.z()&&mesh0.min.z()+dp.z()<mesh1.max.z() {
//collect valid t
best_time=t_time;
best_face=Some(TreyMeshFace::Right);
@ -951,15 +948,15 @@ impl PhysicsState {
}
}
//collect y
for t in zeroes2(mesh0.max.y-mesh1.min.y,v.y,0.5*a.y) {
for t in zeroes2(mesh0.max.y()-mesh1.min.y(),v.y(),a.y()/2) {
//must collide now or in the future
//must beat the current soonest collision time
//must be moving towards surface
let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&0f32<v.y+a.y*t{
let t_time=body_time+Time::from(t);
if time<=t_time&&t_time<best_time&&Planar64::ZERO<v.y()+a.y()*t{
let dp=self.body.extrapolated_position(t_time)-p;
//faces must be overlapping
if mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
if mesh1.min.x()<mesh0.max.x()+dp.x()&&mesh0.min.x()+dp.x()<mesh1.max.x()&&mesh1.min.z()<mesh0.max.z()+dp.z()&&mesh0.min.z()+dp.z()<mesh1.max.z() {
//collect valid t
best_time=t_time;
best_face=Some(TreyMeshFace::Bottom);
@ -967,15 +964,15 @@ impl PhysicsState {
}
}
}
for t in zeroes2(mesh0.min.y-mesh1.max.y,v.y,0.5*a.y) {
for t in zeroes2(mesh0.min.y()-mesh1.max.y(),v.y(),a.y()/2) {
//must collide now or in the future
//must beat the current soonest collision time
//must be moving towards surface
let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&v.y+a.y*t<0f32{
let t_time=body_time+Time::from(t);
if time<=t_time&&t_time<best_time&&v.y()+a.y()*t<Planar64::ZERO{
let dp=self.body.extrapolated_position(t_time)-p;
//faces must be overlapping
if mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x&&mesh1.min.z<mesh0.max.z+dp.z&&mesh0.min.z+dp.z<mesh1.max.z {
if mesh1.min.x()<mesh0.max.x()+dp.x()&&mesh0.min.x()+dp.x()<mesh1.max.x()&&mesh1.min.z()<mesh0.max.z()+dp.z()&&mesh0.min.z()+dp.z()<mesh1.max.z() {
//collect valid t
best_time=t_time;
best_face=Some(TreyMeshFace::Top);
@ -984,15 +981,15 @@ impl PhysicsState {
}
}
//collect z
for t in zeroes2(mesh0.max.z-mesh1.min.z,v.z,0.5*a.z) {
for t in zeroes2(mesh0.max.z()-mesh1.min.z(),v.z(),a.z()/2) {
//must collide now or in the future
//must beat the current soonest collision time
//must be moving towards surface
let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&0f32<v.z+a.z*t{
let t_time=body_time+Time::from(t);
if time<=t_time&&t_time<best_time&&Planar64::ZERO<v.z()+a.z()*t{
let dp=self.body.extrapolated_position(t_time)-p;
//faces must be overlapping
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x {
if mesh1.min.y()<mesh0.max.y()+dp.y()&&mesh0.min.y()+dp.y()<mesh1.max.y()&&mesh1.min.x()<mesh0.max.x()+dp.x()&&mesh0.min.x()+dp.x()<mesh1.max.x() {
//collect valid t
best_time=t_time;
best_face=Some(TreyMeshFace::Front);
@ -1000,15 +997,15 @@ impl PhysicsState {
}
}
}
for t in zeroes2(mesh0.min.z-mesh1.max.z,v.z,0.5*a.z) {
for t in zeroes2(mesh0.min.z()-mesh1.max.z(),v.z(),a.z()/2) {
//must collide now or in the future
//must beat the current soonest collision time
//must be moving towards surface
let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME;
if time<=t_time&&t_time<best_time&&v.z+a.z*t<0f32{
let t_time=body_time+Time::from(t);
if time<=t_time&&t_time<best_time&&v.z()+a.z()*t<Planar64::ZERO{
let dp=self.body.extrapolated_position(t_time)-p;
//faces must be overlapping
if mesh1.min.y<mesh0.max.y+dp.y&&mesh0.min.y+dp.y<mesh1.max.y&&mesh1.min.x<mesh0.max.x+dp.x&&mesh0.min.x+dp.x<mesh1.max.x {
if mesh1.min.y()<mesh0.max.y()+dp.y()&&mesh0.min.y()+dp.y()<mesh1.max.y()&&mesh1.min.x()<mesh0.max.x()+dp.x()&&mesh0.min.x()+dp.x()<mesh1.max.x() {
//collect valid t
best_time=t_time;
best_face=Some(TreyMeshFace::Back);
@ -1032,7 +1029,7 @@ impl PhysicsState {
impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState {
//this little next instruction function can cache its return value and invalidate the cached value by watching the State.
fn next_instruction(&self,time_limit:TIME) -> Option<TimedInstruction<PhysicsInstruction>> {
fn next_instruction(&self,time_limit:Time) -> Option<TimedInstruction<PhysicsInstruction>> {
//JUST POLLING!!! NO MUTATION
let mut collector = crate::instruction::InstructionCollector::new(time_limit);
//check for collision stop instructions with curent contacts
@ -1043,7 +1040,7 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
// collector.collect(self.predict_collision_end2(self.time,time_limit,collision_data));
// }
//check for collision start instructions (against every part in the game with no optimization!!)
let mut aabb=crate::aabb::Aabb::new();
let mut aabb=crate::aabb::Aabb::default();
aabb.grow(self.body.extrapolated_position(self.time));
aabb.grow(self.body.extrapolated_position(time_limit));
aabb.inflate(self.style.hitbox_halfsize);
@ -1111,7 +1108,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
if let Some(mode)=self.get_mode(stage_element.mode_id){
if let Some(&spawn)=mode.get_spawn_model_id(self.game.stage_id){
if let Some(model)=self.models.get(spawn as usize){
self.body.position=model.transform.transform_point3(glam::Vec3::Y)+glam::Vec3::Y*(self.style.hitbox_halfsize.y+0.1);
self.body.position=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(self.style.hitbox_halfsize.y()+Planar64::ONE/16);
//manual clear //for c in self.contacts{process_instruction(CollisionEnd(c))}
self.contacts.clear();
self.intersects.clear();
@ -1159,7 +1156,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
if let Some(mode)=self.get_mode(stage_element.mode_id){
if let Some(&spawn)=mode.get_spawn_model_id(self.game.stage_id){
if let Some(model)=self.models.get(spawn as usize){
self.body.position=model.transform.transform_point3(glam::Vec3::Y)+glam::Vec3::Y*(self.style.hitbox_halfsize.y+0.1);
self.body.position=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(self.style.hitbox_halfsize.y()+Planar64::ONE/16);
//manual clear //for c in self.contacts{process_instruction(CollisionEnd(c))}
self.contacts.clear();
self.intersects.clear();
@ -1205,14 +1202,14 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
let control_dir=camera_mat*self.style.get_control_dir(self.controls);
let d=self.body.velocity.dot(control_dir);
if d<self.style.mv {
let mut v=self.body.velocity+(self.style.mv-d)*control_dir;
let mut v=self.body.velocity+control_dir*(self.style.mv-d);
self.contact_constrain_velocity(&mut v);
self.body.velocity=v;
}
}
PhysicsInstruction::ReachWalkTargetVelocity => {
//precisely set velocity
let mut a=glam::Vec3::ZERO;
let mut a=Planar64Vec3::ZERO;
self.contact_constrain_acceleration(&mut a);
self.body.acceleration=a;
let mut v=self.walk.target_velocity;
@ -1225,11 +1222,11 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
let mut refresh_walk_target_velocity=true;
match input_instruction{
PhysicsInputInstruction::SetNextMouse(m) => {
self.camera.angles=self.camera.simulate_move_angles(self.next_mouse.pos);
self.camera.move_mouse(self.next_mouse.pos);
(self.camera.mouse,self.next_mouse)=(self.next_mouse.clone(),m);
},
PhysicsInputInstruction::ReplaceMouse(m0,m1) => {
self.camera.angles=self.camera.simulate_move_angles(m0.pos);
self.camera.move_mouse(m0.pos);
(self.camera.mouse,self.next_mouse)=(m0,m1);
},
PhysicsInputInstruction::SetMoveForward(s) => self.set_control(StyleModifiers::CONTROL_MOVEFORWARD,s),
@ -1252,7 +1249,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
PhysicsInputInstruction::Reset => {
//temp
self.body.position=self.spawn_point;
self.body.velocity=glam::Vec3::ZERO;
self.body.velocity=Planar64Vec3::ZERO;
//manual clear //for c in self.contacts{process_instruction(CollisionEnd(c))}
self.contacts.clear();
self.body.acceleration=self.style.gravity;
@ -1267,7 +1264,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
if refresh_walk_target_velocity{
let camera_mat=self.camera.simulate_move_rotation_y(self.camera.mouse.lerp(&self.next_mouse,self.time).x);
let control_dir=camera_mat*self.style.get_control_dir(self.controls);
self.walk.target_velocity=self.style.walkspeed*control_dir;
self.walk.target_velocity=control_dir*self.style.walkspeed;
}
self.refresh_walk_target();
}

View File

@ -1,4 +1,5 @@
use crate::model::{IndexedModel, IndexedPolygon, IndexedGroup, IndexedVertex};
use crate::model::{Color4,TextureCoordinate,IndexedModel,IndexedPolygon,IndexedGroup,IndexedVertex};
use crate::integer::Planar64Vec3;
#[derive(Debug)]
pub enum Primitives{
@ -17,24 +18,29 @@ pub enum CubeFace{
Bottom,
Front,
}
const CUBE_DEFAULT_TEXTURE_COORDS:[[f32;2];4]=[[0.0,0.0],[1.0,0.0],[1.0,1.0],[0.0,1.0]];
const CUBE_DEFAULT_VERTICES:[[f32;3];8]=[
[-1.,-1., 1.],//0 left bottom back
[ 1.,-1., 1.],//1 right bottom back
[ 1., 1., 1.],//2 right top back
[-1., 1., 1.],//3 left top back
[-1., 1.,-1.],//4 left top front
[ 1., 1.,-1.],//5 right top front
[ 1.,-1.,-1.],//6 right bottom front
[-1.,-1.,-1.],//7 left bottom front
const CUBE_DEFAULT_TEXTURE_COORDS:[TextureCoordinate;4]=[
TextureCoordinate::new(0.0,0.0),
TextureCoordinate::new(1.0,0.0),
TextureCoordinate::new(1.0,1.0),
TextureCoordinate::new(0.0,1.0),
];
const CUBE_DEFAULT_NORMALS:[[f32;3];6]=[
[ 1., 0., 0.],//CubeFace::Right
[ 0., 1., 0.],//CubeFace::Top
[ 0., 0., 1.],//CubeFace::Back
[-1., 0., 0.],//CubeFace::Left
[ 0.,-1., 0.],//CubeFace::Bottom
[ 0., 0.,-1.],//CubeFace::Front
const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[
Planar64Vec3::int(-1,-1, 1),//0 left bottom back
Planar64Vec3::int( 1,-1, 1),//1 right bottom back
Planar64Vec3::int( 1, 1, 1),//2 right top back
Planar64Vec3::int(-1, 1, 1),//3 left top back
Planar64Vec3::int(-1, 1,-1),//4 left top front
Planar64Vec3::int( 1, 1,-1),//5 right top front
Planar64Vec3::int( 1,-1,-1),//6 right bottom front
Planar64Vec3::int(-1,-1,-1),//7 left bottom front
];
const CUBE_DEFAULT_NORMALS:[Planar64Vec3;6]=[
Planar64Vec3::int( 1, 0, 0),//CubeFace::Right
Planar64Vec3::int( 0, 1, 0),//CubeFace::Top
Planar64Vec3::int( 0, 0, 1),//CubeFace::Back
Planar64Vec3::int(-1, 0, 0),//CubeFace::Left
Planar64Vec3::int( 0,-1, 0),//CubeFace::Bottom
Planar64Vec3::int( 0, 0,-1),//CubeFace::Front
];
const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[
// right (1, 0, 0)
@ -89,12 +95,12 @@ pub enum WedgeFace{
Left,
Bottom,
}
const WEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
[ 1., 0., 0.],//Wedge::Right
[ 0., 1.,-1.],//Wedge::TopFront
[ 0., 0., 1.],//Wedge::Back
[-1., 0., 0.],//Wedge::Left
[ 0.,-1., 0.],//Wedge::Bottom
const WEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
Planar64Vec3::int( 1, 0, 0),//Wedge::Right
Planar64Vec3::int( 0, 1,-1),//Wedge::TopFront
Planar64Vec3::int( 0, 0, 1),//Wedge::Back
Planar64Vec3::int(-1, 0, 0),//Wedge::Left
Planar64Vec3::int( 0,-1, 0),//Wedge::Bottom
];
/*
local cornerWedgeVerticies = {
@ -113,20 +119,18 @@ pub enum CornerWedgeFace{
Bottom,
Front,
}
const CORNERWEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
[ 1., 0., 0.],//CornerWedge::Right
[ 0., 1., 1.],//CornerWedge::BackTop
[-1., 1., 0.],//CornerWedge::LeftTop
[ 0.,-1., 0.],//CornerWedge::Bottom
[ 0., 0.,-1.],//CornerWedge::Front
const CORNERWEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
Planar64Vec3::int( 1, 0, 0),//CornerWedge::Right
Planar64Vec3::int( 0, 1, 1),//CornerWedge::BackTop
Planar64Vec3::int(-1, 1, 0),//CornerWedge::LeftTop
Planar64Vec3::int( 0,-1, 0),//CornerWedge::Bottom
Planar64Vec3::int( 0, 0,-1),//CornerWedge::Front
];
//HashMap fits this use case perfectly but feels like using a sledgehammer to drive a nail
pub fn unit_sphere()->crate::model::IndexedModel{
let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/suzanne.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()).remove(0);
let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/suzanne.obj")[..]).unwrap(),Color4::ONE).remove(0);
for pos in indexed_model.unique_pos.iter_mut(){
pos[0]=pos[0]*0.5;
pos[1]=pos[1]*0.5;
pos[2]=pos[2]*0.5;
*pos=*pos/2;
}
indexed_model
}
@ -141,11 +145,11 @@ pub fn unit_cube()->crate::model::IndexedModel{
t.insert(CubeFace::Front,FaceDescription::default());
generate_partial_unit_cube(t)
}
const TEAPOT_TRANSFORM:glam::Mat3=glam::mat3(glam::vec3(0.0,0.1,0.0),glam::vec3(-0.1,0.0,0.0),glam::vec3(0.0,0.0,0.1));
const TEAPOT_TRANSFORM:crate::integer::Planar64Mat3=crate::integer::Planar64Mat3::int_from_cols_array([0,1,0, -1,0,0, 0,0,1]);
pub fn unit_cylinder()->crate::model::IndexedModel{
let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teapot.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()).remove(0);
let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teapot.obj")[..]).unwrap(),Color4::ONE).remove(0);
for pos in indexed_model.unique_pos.iter_mut(){
[pos[0],pos[1],pos[2]]=*(TEAPOT_TRANSFORM*glam::Vec3::from_array(*pos)).as_ref();
*pos=TEAPOT_TRANSFORM*(*pos)/10;
}
indexed_model
}
@ -174,33 +178,33 @@ pub fn unit_cornerwedge()->crate::model::IndexedModel{
pub struct FaceDescription{
pub texture:Option<u32>,
pub transform:glam::Affine2,
pub color:glam::Vec4,
pub color:Color4,
}
impl std::default::Default for FaceDescription{
fn default()->Self {
Self{
texture:None,
transform:glam::Affine2::IDENTITY,
color:glam::vec4(1.0,1.0,1.0,0.0),//zero alpha to hide the default texture
color:Color4::new(1.0,1.0,1.0,0.0),//zero alpha to hide the default texture
}
}
}
impl FaceDescription{
pub fn new(texture:u32,transform:glam::Affine2,color:glam::Vec4)->Self{
pub fn new(texture:u32,transform:glam::Affine2,color:Color4)->Self{
Self{texture:Some(texture),transform,color}
}
pub fn from_texture(texture:u32)->Self{
Self{
texture:Some(texture),
transform:glam::Affine2::IDENTITY,
color:glam::Vec4::ONE,
color:Color4::ONE,
}
}
}
//TODO: it's probably better to use a shared vertex buffer between all primitives and use indexed rendering instead of generating a unique vertex buffer for each primitive.
//implementation: put all roblox primitives into one model.groups <- this won't work but I forget why
pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate::model::IndexedModel{
let mut generated_pos=Vec::<[f32;3]>::new();
let mut generated_pos=Vec::new();
let mut generated_tex=Vec::new();
let mut generated_normal=Vec::new();
let mut generated_color=Vec::new();
@ -217,16 +221,16 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate:
let transform_index=transforms.len();
transforms.push(face_description.transform);
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
generated_tex.push(face_description.transform.transform_point2(tex));
}
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
color_index
}else{
//create new color_index
let color_index=generated_color.len();
generated_color.push(*face_description.color.as_ref());
generated_color.push(face_description.color);
color_index
} as u32;
let face_id=match face{
@ -315,7 +319,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crat
[6,2,4],
],
];
let mut generated_pos=Vec::<[f32;3]>::new();
let mut generated_pos=Vec::new();
let mut generated_tex=Vec::new();
let mut generated_normal=Vec::new();
let mut generated_color=Vec::new();
@ -332,16 +336,16 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crat
let transform_index=transforms.len();
transforms.push(face_description.transform);
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
generated_tex.push(face_description.transform.transform_point2(tex));
}
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
color_index
}else{
//create new color_index
let color_index=generated_color.len();
generated_color.push(*face_description.color.as_ref());
generated_color.push(face_description.color);
color_index
} as u32;
let face_id=match face{
@ -427,7 +431,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
[7,2,4],
],
];
let mut generated_pos=Vec::<[f32;3]>::new();
let mut generated_pos=Vec::new();
let mut generated_tex=Vec::new();
let mut generated_normal=Vec::new();
let mut generated_color=Vec::new();
@ -444,16 +448,16 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
let transform_index=transforms.len();
transforms.push(face_description.transform);
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
generated_tex.push(face_description.transform.transform_point2(tex));
}
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
color_index
}else{
//create new color_index
let color_index=generated_color.len();
generated_color.push(*face_description.color.as_ref());
generated_color.push(face_description.color);
color_index
} as u32;
let face_id=match face{

View File

@ -1,3 +1,4 @@
use crate::integer::{Ratio64,Ratio64Vec2};
struct Ratio{
ratio:f64,
}
@ -7,23 +8,25 @@ enum DerivedFov{
}
enum Fov{
Exactly{x:f64,y:f64},
DeriveX{x:DerivedFov,y:f64},
DeriveY{x:f64,y:DerivedFov},
SpecifyXDeriveY{x:f64,y:DerivedFov},
SpecifyYDeriveX{x:DerivedFov,y:f64},
}
impl Default for Fov{
fn default() -> Self {
Fov::DeriveX{x:DerivedFov::FromScreenAspect,y:1.0}
fn default()->Self{
Fov::SpecifyYDeriveX{x:DerivedFov::FromScreenAspect,y:1.0}
}
}
enum DerivedSensitivity{
FromRatio(Ratio64),
}
enum Sensitivity{
Exactly{x:f64,y:f64},
DeriveX{x:Ratio,y:f64},
DeriveY{x:f64,y:Ratio},
Exactly{x:Ratio64,y:Ratio64},
SpecifyXDeriveY{x:Ratio64,y:DerivedSensitivity},
SpecifyYDeriveX{x:DerivedSensitivity,y:Ratio64},
}
impl Default for Sensitivity{
fn default() -> Self {
Sensitivity::DeriveY{x:0.001,y:Ratio{ratio:1.0}}
fn default()->Self{
Sensitivity::SpecifyXDeriveY{x:Ratio64::ONE*524288,y:DerivedSensitivity::FromRatio(Ratio64::ONE)}
}
}
@ -36,21 +39,25 @@ impl UserSettings{
pub fn calculate_fov(&self,zoom:f64,screen_size:&glam::UVec2)->glam::DVec2{
zoom*match &self.fov{
&Fov::Exactly{x,y}=>glam::dvec2(x,y),
Fov::DeriveX{x,y}=>match x{
DerivedFov::FromScreenAspect=>glam::dvec2(y*(screen_size.x as f64/screen_size.y as f64),*y),
DerivedFov::FromAspect(ratio)=>glam::dvec2(y*ratio.ratio,*y),
},
Fov::DeriveY{x,y}=>match y{
Fov::SpecifyXDeriveY{x,y}=>match y{
DerivedFov::FromScreenAspect=>glam::dvec2(*x,x*(screen_size.y as f64/screen_size.x as f64)),
DerivedFov::FromAspect(ratio)=>glam::dvec2(*x,x*ratio.ratio),
},
Fov::SpecifyYDeriveX{x,y}=>match x{
DerivedFov::FromScreenAspect=>glam::dvec2(y*(screen_size.x as f64/screen_size.y as f64),*y),
DerivedFov::FromAspect(ratio)=>glam::dvec2(y*ratio.ratio,*y),
},
}
}
pub fn calculate_sensitivity(&self)->glam::DVec2{
pub fn calculate_sensitivity(&self)->Ratio64Vec2{
match &self.sensitivity{
&Sensitivity::Exactly{x,y}=>glam::dvec2(x,y),
Sensitivity::DeriveX{x,y}=>glam::dvec2(y*x.ratio,*y),
Sensitivity::DeriveY{x,y}=>glam::dvec2(*x,x*y.ratio),
Sensitivity::Exactly{x,y}=>Ratio64Vec2::new(x.clone(),y.clone()),
Sensitivity::SpecifyXDeriveY{x,y}=>match y{
DerivedSensitivity::FromRatio(ratio)=>Ratio64Vec2::new(x.clone(),x.mul_ref(ratio)),
}
Sensitivity::SpecifyYDeriveX{x,y}=>match x{
DerivedSensitivity::FromRatio(ratio)=>Ratio64Vec2::new(y.mul_ref(ratio),y.clone()),
}
}
}
}
@ -71,7 +78,7 @@ pub fn read_user_settings()->UserSettings{
x:fov_x,
y:fov_y
},
(Ok(Some(fov_x)),Ok(None))=>Fov::DeriveY{
(Ok(Some(fov_x)),Ok(None))=>Fov::SpecifyXDeriveY{
x:fov_x,
y:if let Ok(Some(fov_y_from_x_ratio))=cfg.getfloat("camera","fov_y_from_x_ratio"){
DerivedFov::FromAspect(Ratio{ratio:fov_y_from_x_ratio})
@ -79,7 +86,7 @@ pub fn read_user_settings()->UserSettings{
DerivedFov::FromScreenAspect
}
},
(Ok(None),Ok(Some(fov_y)))=>Fov::DeriveX{
(Ok(None),Ok(Some(fov_y)))=>Fov::SpecifyYDeriveX{
x:if let Ok(Some(fov_x_from_y_ratio))=cfg.getfloat("camera","fov_x_from_y_ratio"){
DerivedFov::FromAspect(Ratio{ratio:fov_x_from_y_ratio})
}else{
@ -94,20 +101,24 @@ pub fn read_user_settings()->UserSettings{
let (cfg_sensitivity_x,cfg_sensitivity_y)=(cfg.getfloat("camera","sensitivity_x"),cfg.getfloat("camera","sensitivity_y"));
let sensitivity=match(cfg_sensitivity_x,cfg_sensitivity_y){
(Ok(Some(sensitivity_x)),Ok(Some(sensitivity_y)))=>Sensitivity::Exactly {
x:sensitivity_x,
y:sensitivity_y
x:Ratio64::try_from(sensitivity_x).unwrap(),
y:Ratio64::try_from(sensitivity_y).unwrap(),
},
(Ok(Some(sensitivity_x)),Ok(None))=>Sensitivity::DeriveY{
x:sensitivity_x,
y:Ratio{
ratio:if let Ok(Some(sensitivity_y_from_x_ratio))=cfg.getfloat("camera","sensitivity_y_from_x_ratio"){sensitivity_y_from_x_ratio}else{1.0}
}
},
(Ok(None),Ok(Some(sensitivity_y)))=>Sensitivity::DeriveX{
x:Ratio{
ratio:if let Ok(Some(sensitivity_x_from_y_ratio))=cfg.getfloat("camera","sensitivity_x_from_y_ratio"){sensitivity_x_from_y_ratio}else{1.0}
(Ok(Some(sensitivity_x)),Ok(None))=>Sensitivity::SpecifyXDeriveY{
x:Ratio64::try_from(sensitivity_x).unwrap(),
y:if let Ok(Some(sensitivity_y_from_x_ratio))=cfg.getfloat("camera","sensitivity_y_from_x_ratio"){
DerivedSensitivity::FromRatio(Ratio64::try_from(sensitivity_y_from_x_ratio).unwrap())
}else{
DerivedSensitivity::FromRatio(Ratio64::ONE)
},
y:sensitivity_y,
},
(Ok(None),Ok(Some(sensitivity_y)))=>Sensitivity::SpecifyYDeriveX{
x:if let Ok(Some(sensitivity_x_from_y_ratio))=cfg.getfloat("camera","sensitivity_x_from_y_ratio"){
DerivedSensitivity::FromRatio(Ratio64::try_from(sensitivity_x_from_y_ratio).unwrap())
}else{
DerivedSensitivity::FromRatio(Ratio64::ONE)
},
y:Ratio64::try_from(sensitivity_y).unwrap(),
},
_=>{
Sensitivity::default()

View File

@ -74,14 +74,14 @@ impl<Task,Value:Clone,F:FnMut(Task)->Value> CompatWorker<Task,Value,F> {
fn test_worker() {
println!("hiiiii");
// Create the worker thread
let worker = Worker::new(crate::physics::Body::with_pva(glam::Vec3::ZERO,glam::Vec3::ZERO,glam::Vec3::ZERO),
|_|crate::physics::Body::with_pva(glam::Vec3::ONE,glam::Vec3::ONE,glam::Vec3::ONE)
let worker = Worker::new(crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO),
|_|crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE)
);
// Send tasks to the worker
for _ in 0..5 {
let task = crate::instruction::TimedInstruction{
time:0,
time:crate::integer::Time::ZERO,
instruction:crate::physics::PhysicsInstruction::StrafeTick,
};
worker.send(task).unwrap();
@ -95,7 +95,7 @@ fn test_worker() {
// Send a new task
let task = crate::instruction::TimedInstruction{
time:0,
time:crate::integer::Time::ZERO,
instruction:crate::physics::PhysicsInstruction::StrafeTick,
};
worker.send(task).unwrap();

View File

@ -1,26 +1,30 @@
//find roots of polynomials
use crate::integer::Planar64;
#[inline]
pub fn zeroes2(a0:f32,a1:f32,a2:f32) -> Vec<f32>{
if a2==0f32{
pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
if a2==Planar64::ZERO{
return zeroes1(a0, a1);
}
let mut radicand=a1*a1-4f32*a2*a0;
if 0f32<radicand {
radicand=radicand.sqrt();
if 0f32<a2 {
return vec![(-a1-radicand)/(2f32*a2),(-a1+radicand)/(2f32*a2)];
let mut radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
if 0<radicand {
//start with f64 sqrt
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});
//TODO: one or two newtons
if Planar64::ZERO<a2 {
return vec![(-a1-planar_radicand)/(a2*2),(-a1+planar_radicand)/(a2*2)];
} else {
return vec![(-a1+radicand)/(2f32*a2),(-a1-radicand)/(2f32*a2)];
return vec![(-a1+planar_radicand)/(a2*2),(-a1-planar_radicand)/(a2*2)];
}
} else if radicand==0f32 {
return vec![-a1/(2f32*a2)];
} else if radicand==0 {
return vec![a1/(a2*-2)];
} else {
return vec![];
}
}
#[inline]
pub fn zeroes1(a0:f32,a1:f32) -> Vec<f32> {
if a1==0f32{
pub fn zeroes1(a0:Planar64,a1:Planar64) -> Vec<Planar64> {
if a1==Planar64::ZERO{
return vec![];
} else {
return vec![-a0/a1];