forked from StrafesNET/strafe-project
Compare commits
34 Commits
physics-th
...
deduplicat
Author | SHA1 | Date | |
---|---|---|---|
e9bf4db43e | |||
7e1cf7041a | |||
50543ffcea | |||
54498f20f9 | |||
2240b80656 | |||
d18f2168e4 | |||
381b7b3c2f | |||
0d6741a81c | |||
2e8cdf968c | |||
dd0ac7cc7e | |||
e2af6fc4ed | |||
bdc0dd1b3b | |||
95fb316a23 | |||
9dec53d764 | |||
3552491a9a | |||
dd13a066d0 | |||
f3dd43b171 | |||
82d71df94e | |||
684dbda73a | |||
e398da3aa6 | |||
944393dabe | |||
4adce7acd3 | |||
5b935c32fe | |||
436706bc4d | |||
bde24d35a2 | |||
fc91d644e6 | |||
2b47827383 | |||
a942e10554 | |||
5d1e38c36c | |||
e78cabf0f5 | |||
4e90da2228 | |||
9fa4ea6716 | |||
aedef03e7c | |||
6a9af0441f |
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -331,6 +331,12 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "configparser"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -1682,10 +1688,11 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strafe-client"
|
name = "strafe-client"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-executor",
|
"async-executor",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
|
"configparser",
|
||||||
"ddsfile",
|
"ddsfile",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"glam",
|
"glam",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "strafe-client"
|
name = "strafe-client"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@ -8,6 +8,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-executor = "1.5.1"
|
async-executor = "1.5.1"
|
||||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||||
|
configparser = "3.0.2"
|
||||||
ddsfile = "0.5.1"
|
ddsfile = "0.5.1"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
glam = "0.24.1"
|
glam = "0.24.1"
|
||||||
|
89
src/aabb.rs
Normal file
89
src/aabb.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use crate::integer::Planar64Vec3;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||||
|
pub enum AabbFace{
|
||||||
|
Right,//+X
|
||||||
|
Top,
|
||||||
|
Back,
|
||||||
|
Left,
|
||||||
|
Bottom,
|
||||||
|
Front,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Aabb{
|
||||||
|
pub min:Planar64Vec3,
|
||||||
|
pub max:Planar64Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Aabb {
|
||||||
|
fn default()->Self {
|
||||||
|
Self{min:Planar64Vec3::MAX,max:Planar64Vec3::MIN}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 grow(&mut self,point:Planar64Vec3){
|
||||||
|
self.min=self.min.min(point);
|
||||||
|
self.max=self.max.max(point);
|
||||||
|
}
|
||||||
|
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: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)->Planar64Vec3{
|
||||||
|
match face {
|
||||||
|
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()->[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)->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
|
||||||
|
// }
|
||||||
|
}
|
107
src/bvh.rs
Normal file
107
src/bvh.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use crate::aabb::Aabb;
|
||||||
|
|
||||||
|
//da algaritum
|
||||||
|
//lista boxens
|
||||||
|
//sort by {minx,maxx,miny,maxy,minz,maxz} (6 lists)
|
||||||
|
//find the sets that minimizes the sum of surface areas
|
||||||
|
//splitting is done when the minimum split sum of surface areas is larger than the node's own surface area
|
||||||
|
|
||||||
|
//start with bisection into octrees because a bad bvh is still 1000x better than no bvh
|
||||||
|
//sort the centerpoints on each axis (3 lists)
|
||||||
|
//bv is put into octant based on whether it is upper or lower in each list
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BvhNode{
|
||||||
|
children:Vec<Self>,
|
||||||
|
models:Vec<u32>,
|
||||||
|
aabb:Aabb,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BvhNode{
|
||||||
|
pub fn the_tester<F:FnMut(u32)>(&self,aabb:&Aabb,f:&mut F){
|
||||||
|
for &model in &self.models{
|
||||||
|
f(model);
|
||||||
|
}
|
||||||
|
for child in &self.children{
|
||||||
|
if aabb.intersects(&child.aabb){
|
||||||
|
child.the_tester(aabb,f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_bvh(boxen:Vec<Aabb>)->BvhNode{
|
||||||
|
generate_bvh_node(boxen.into_iter().enumerate().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
|
||||||
|
let n=boxen.len();
|
||||||
|
if n<20{
|
||||||
|
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(),
|
||||||
|
models,
|
||||||
|
aabb,
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let mut octant=std::collections::HashMap::with_capacity(n);//this ids which octant the boxen is put in
|
||||||
|
let mut sort_x=Vec::with_capacity(n);
|
||||||
|
let mut sort_y=Vec::with_capacity(n);
|
||||||
|
let mut sort_z=Vec::with_capacity(n);
|
||||||
|
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.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
|
||||||
|
sort_y.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
|
||||||
|
sort_z.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
|
||||||
|
let h=n/2;
|
||||||
|
let median_x=sort_x[h].1;
|
||||||
|
let median_y=sort_y[h].1;
|
||||||
|
let median_z=sort_z[h].1;
|
||||||
|
for (i,c) in sort_x{
|
||||||
|
if median_x<c{
|
||||||
|
octant.insert(i,octant[&i]+1<<0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i,c) in sort_y{
|
||||||
|
if median_y<c{
|
||||||
|
octant.insert(i,octant[&i]+1<<1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i,c) in sort_z{
|
||||||
|
if median_z<c{
|
||||||
|
octant.insert(i,octant[&i]+1<<2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//generate lists for unique octant values
|
||||||
|
let mut list_list=Vec::with_capacity(8);
|
||||||
|
let mut octant_list=Vec::with_capacity(8);
|
||||||
|
for (i,aabb) in boxen.into_iter(){
|
||||||
|
let octant_id=octant[&i];
|
||||||
|
let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){
|
||||||
|
list_id
|
||||||
|
}else{
|
||||||
|
let list_id=list_list.len();
|
||||||
|
octant_list.push(octant_id);
|
||||||
|
list_list.push(Vec::new());
|
||||||
|
list_id
|
||||||
|
};
|
||||||
|
list_list[list_id].push((i,aabb));
|
||||||
|
}
|
||||||
|
let mut aabb=Aabb::default();
|
||||||
|
let children=list_list.into_iter().map(|b|{
|
||||||
|
let node=generate_bvh_node(b);
|
||||||
|
aabb.join(&node.aabb);
|
||||||
|
node
|
||||||
|
}).collect();
|
||||||
|
BvhNode{
|
||||||
|
children,
|
||||||
|
models:Vec::new(),
|
||||||
|
aabb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,25 @@
|
|||||||
|
use crate::integer::Time;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TimedInstruction<I> {
|
pub struct TimedInstruction<I>{
|
||||||
pub time: crate::physics::TIME,
|
pub time:Time,
|
||||||
pub instruction: I,
|
pub instruction:I,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InstructionEmitter<I> {
|
pub trait InstructionEmitter<I>{
|
||||||
fn next_instruction(&self, time_limit:crate::physics::TIME) -> Option<TimedInstruction<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>);
|
fn process_instruction(&mut self, instruction:TimedInstruction<I>);
|
||||||
}
|
}
|
||||||
|
|
||||||
//PROPER PRIVATE FIELDS!!!
|
//PROPER PRIVATE FIELDS!!!
|
||||||
pub struct InstructionCollector<I> {
|
pub struct InstructionCollector<I>{
|
||||||
time: crate::physics::TIME,
|
time:Time,
|
||||||
instruction: Option<I>,
|
instruction:Option<I>,
|
||||||
}
|
}
|
||||||
impl<I> InstructionCollector<I> {
|
impl<I> InstructionCollector<I>{
|
||||||
pub fn new(time:crate::physics::TIME) -> Self {
|
pub fn new(time:Time)->Self{
|
||||||
Self{
|
Self{
|
||||||
time,
|
time,
|
||||||
instruction:None
|
instruction:None
|
||||||
@ -25,24 +27,24 @@ impl<I> InstructionCollector<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect(&mut self,instruction:Option<TimedInstruction<I>>){
|
pub fn collect(&mut self,instruction:Option<TimedInstruction<I>>){
|
||||||
match instruction {
|
match instruction{
|
||||||
Some(unwrap_instruction) => {
|
Some(unwrap_instruction)=>{
|
||||||
if unwrap_instruction.time<self.time {
|
if unwrap_instruction.time<self.time {
|
||||||
self.time=unwrap_instruction.time;
|
self.time=unwrap_instruction.time;
|
||||||
self.instruction=Some(unwrap_instruction.instruction);
|
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
|
//STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR
|
||||||
match self.instruction {
|
match self.instruction{
|
||||||
Some(instruction)=>Some(TimedInstruction{
|
Some(instruction)=>Some(TimedInstruction{
|
||||||
time:self.time,
|
time:self.time,
|
||||||
instruction
|
instruction
|
||||||
}),
|
}),
|
||||||
None => None,
|
None=>None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
925
src/integer.rs
Normal file
925
src/integer.rs
Normal file
@ -0,0 +1,925 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
|
pub fn sqrt(&self)->Self{
|
||||||
|
Planar64(unsafe{(((self.0 as i128)<<32) as f64).sqrt().to_int_unchecked()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const PLANAR64_ONE_FLOAT32:f32=(1u64<<32) as f32;
|
||||||
|
const PLANAR64_CONVERT_TO_FLOAT32:f32=1.0/PLANAR64_ONE_FLOAT32;
|
||||||
|
const PLANAR64_ONE_FLOAT64:f64=(1u64<<32) as f64;
|
||||||
|
impl Into<f32> for Planar64{
|
||||||
|
#[inline]
|
||||||
|
fn into(self)->f32{
|
||||||
|
self.0 as f32*PLANAR64_CONVERT_TO_FLOAT32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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_CONVERT_TO_FLOAT32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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_ONE_FLOAT64,s*PLANAR64_ONE_FLOAT64);
|
||||||
|
//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_ONE_FLOAT32
|
||||||
|
])*PLANAR64_CONVERT_TO_FLOAT32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sqrt(){
|
||||||
|
let r=Planar64::int(400);
|
||||||
|
println!("r{}",r.get());
|
||||||
|
let s=r.sqrt();
|
||||||
|
println!("s{}",s.get());
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
use crate::primitives;
|
use crate::primitives;
|
||||||
|
use crate::integer::{Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
|
||||||
|
|
||||||
fn class_is_a(class: &str, superclass: &str) -> bool {
|
fn class_is_a(class: &str, superclass: &str) -> bool {
|
||||||
if class==superclass {
|
if class==superclass {
|
||||||
@ -30,25 +31,39 @@ fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>
|
|||||||
//next class
|
//next class
|
||||||
objects
|
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 general=crate::model::GameMechanicAttributes::default();
|
||||||
let mut intersecting=crate::model::IntersectingAttributes::default();
|
let mut intersecting=crate::model::IntersectingAttributes::default();
|
||||||
let mut contacting=crate::model::ContactingAttributes::default();
|
let mut contacting=crate::model::ContactingAttributes::default();
|
||||||
|
let mut force_can_collide=can_collide;
|
||||||
match name{
|
match name{
|
||||||
//"Water"=>intersecting.water=Some(crate::model::IntersectingWater{density:1.0,drag:1.0}),
|
"Water"=>intersecting.water=Some(crate::model::IntersectingWater{density:Planar64::ONE,viscosity:Planar64::ONE/10,current:velocity}),
|
||||||
"Accelerator"=>intersecting.accelerator=Some(crate::model::IntersectingAccelerator{acceleration:velocity}),
|
"Accelerator"=>{force_can_collide=false;intersecting.accelerator=Some(crate::model::IntersectingAccelerator{acceleration:velocity})},
|
||||||
"MapFinish"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Finish}),
|
"MapFinish"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Finish})},
|
||||||
"MapAnticheat"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Anitcheat}),
|
"MapAnticheat"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Anitcheat})},
|
||||||
"Platform"=>general.stage_element=Some(crate::model::GameMechanicStageElement{
|
"Platform"=>general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
|
||||||
mode_id:0,
|
mode_id:0,
|
||||||
stage_id:0,
|
stage_id:0,
|
||||||
force:false,
|
force:false,
|
||||||
behaviour:crate::model::StageElementBehaviour::Platform,
|
behaviour:crate::model::StageElementBehaviour::Platform,
|
||||||
}),
|
})),
|
||||||
other=>{
|
other=>{
|
||||||
if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
|
if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
|
||||||
.captures(other){
|
.captures(other){
|
||||||
general.stage_element=Some(crate::model::GameMechanicStageElement{
|
general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
|
||||||
mode_id:0,
|
mode_id:0,
|
||||||
stage_id:captures[3].parse::<u32>().unwrap(),
|
stage_id:captures[3].parse::<u32>().unwrap(),
|
||||||
force:match captures.get(1){
|
force:match captures.get(1){
|
||||||
@ -57,14 +72,15 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersect
|
|||||||
},
|
},
|
||||||
behaviour:match &captures[2]{
|
behaviour:match &captures[2]{
|
||||||
"Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
|
"Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
|
||||||
"Trigger"=>crate::model::StageElementBehaviour::Trigger,
|
"Trigger"=>{force_can_collide=false;crate::model::StageElementBehaviour::Trigger},
|
||||||
"Teleport"=>crate::model::StageElementBehaviour::Teleport,
|
"Teleport"=>{force_can_collide=false;crate::model::StageElementBehaviour::Teleport},
|
||||||
"Platform"=>crate::model::StageElementBehaviour::Platform,
|
"Platform"=>crate::model::StageElementBehaviour::Platform,
|
||||||
_=>panic!("regex1[2] messed up bad"),
|
_=>panic!("regex1[2] messed up bad"),
|
||||||
}
|
}
|
||||||
})
|
}));
|
||||||
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
|
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
|
||||||
.captures(other){
|
.captures(other){
|
||||||
|
force_can_collide=false;
|
||||||
match &captures[1]{
|
match &captures[1]{
|
||||||
"Finish"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Finish}),
|
"Finish"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Finish}),
|
||||||
"Anticheat"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Anitcheat}),
|
"Anticheat"=>general.zone=Some(crate::model::GameMechanicZone{mode_id:captures[2].parse::<u32>().unwrap(),behaviour:crate::model::ZoneBehaviour::Anitcheat}),
|
||||||
@ -74,19 +90,24 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersect
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//need some way to skip this
|
//need some way to skip this
|
||||||
if velocity!=glam::Vec3::ZERO{
|
if velocity!=Planar64Vec3::ZERO{
|
||||||
general.booster=Some(crate::model::GameMechanicBooster{velocity});
|
general.booster=Some(crate::model::GameMechanicBooster{velocity});
|
||||||
}
|
}
|
||||||
match can_collide{
|
match force_can_collide{
|
||||||
true=>{
|
true=>{
|
||||||
match name{
|
match name{
|
||||||
//"Bounce"=>(),
|
"Bounce"=>contacting.elasticity=Some(u32::MAX),
|
||||||
"Surf"=>contacting.surf=Some(crate::model::ContactingSurf{}),
|
"Surf"=>contacting.surf=Some(crate::model::ContactingSurf{}),
|
||||||
"Ladder"=>contacting.ladder=Some(crate::model::ContactingLadder{sticky:true}),
|
"Ladder"=>contacting.ladder=Some(crate::model::ContactingLadder{sticky:true}),
|
||||||
other=>{
|
other=>{
|
||||||
//REGEX!!!!
|
if let Some(captures)=lazy_regex::regex!(r"^(Jump|WormholeIn)(\d+)$")
|
||||||
//Jump#
|
.captures(other){
|
||||||
//WormholeIn#
|
match &captures[1]{
|
||||||
|
"Jump"=>general.jump_limit=Some(crate::model::GameMechanicJumpLimit{count:captures[2].parse::<u32>().unwrap()}),
|
||||||
|
"WormholeIn"=>general.teleport_behaviour=Some(crate::model::TeleportBehaviour::Wormhole(crate::model::GameMechanicWormhole{destination_model_id:captures[2].parse::<u32>().unwrap()})),
|
||||||
|
_=>panic!("regex3[1] messed up bad"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::model::CollisionAttributes::Contact{contacting,general}
|
crate::model::CollisionAttributes::Contact{contacting,general}
|
||||||
@ -95,8 +116,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:glam::Vec3,force_intersect
|
|||||||
||general.jump_limit.is_some()
|
||general.jump_limit.is_some()
|
||||||
||general.booster.is_some()
|
||general.booster.is_some()
|
||||||
||general.zone.is_some()
|
||general.zone.is_some()
|
||||||
||general.stage_element.is_some()
|
||general.teleport_behaviour.is_some()
|
||||||
||general.wormhole.is_some()
|
|
||||||
||intersecting.water.is_some()
|
||intersecting.water.is_some()
|
||||||
||intersecting.accelerator.is_some()
|
||intersecting.accelerator.is_some()
|
||||||
{
|
{
|
||||||
@ -176,7 +196,7 @@ impl RobloxFaceTextureDescription{
|
|||||||
}
|
}
|
||||||
type RobloxPartDescription=[Option<RobloxFaceTextureDescription>;6];
|
type RobloxPartDescription=[Option<RobloxFaceTextureDescription>;6];
|
||||||
type RobloxWedgeDescription=[Option<RobloxFaceTextureDescription>;5];
|
type RobloxWedgeDescription=[Option<RobloxFaceTextureDescription>;5];
|
||||||
type RobloxCornerWedgeDescription=[Option<RobloxFaceTextureDescription>;4];
|
type RobloxCornerWedgeDescription=[Option<RobloxFaceTextureDescription>;5];
|
||||||
#[derive(Clone,Eq,Hash,PartialEq)]
|
#[derive(Clone,Eq,Hash,PartialEq)]
|
||||||
enum RobloxBasePartDescription{
|
enum RobloxBasePartDescription{
|
||||||
Sphere,
|
Sphere,
|
||||||
@ -187,7 +207,7 @@ enum RobloxBasePartDescription{
|
|||||||
}
|
}
|
||||||
pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::IndexedModelInstances{
|
pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::IndexedModelInstances{
|
||||||
//IndexedModelInstances includes textures
|
//IndexedModelInstances includes textures
|
||||||
let mut spawn_point=glam::Vec3::ZERO;
|
let mut spawn_point=Planar64Vec3::ZERO;
|
||||||
|
|
||||||
let mut indexed_models=Vec::new();
|
let mut indexed_models=Vec::new();
|
||||||
let mut model_id_from_description=std::collections::HashMap::<RobloxBasePartDescription,usize>::new();
|
let mut model_id_from_description=std::collections::HashMap::<RobloxBasePartDescription,usize>::new();
|
||||||
@ -216,26 +236,14 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
object.properties.get("CanCollide"),
|
object.properties.get("CanCollide"),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let model_transform=glam::Affine3A::from_translation(
|
let model_transform=planar64_affine3_from_roblox(cf,size);
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
//push TempIndexedAttributes
|
//push TempIndexedAttributes
|
||||||
let mut force_intersecting=false;
|
let mut force_intersecting=false;
|
||||||
let mut temp_indexing_attributes=Vec::new();
|
let mut temp_indexing_attributes=Vec::new();
|
||||||
if let Some(attr)=match &object.name[..]{
|
if let Some(attr)=match &object.name[..]{
|
||||||
"MapStart"=>{
|
"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})
|
Some(crate::model::TempIndexedAttributes::Start{mode_id:0})
|
||||||
},
|
},
|
||||||
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}),
|
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint{mode_id:0}),
|
||||||
@ -309,9 +317,7 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
};
|
};
|
||||||
let normal_id=normalid.to_u32();
|
let normal_id=normalid.to_u32();
|
||||||
if normal_id<6{
|
if normal_id<6{
|
||||||
let mut roblox_texture_transform=RobloxTextureTransform::default();
|
let (roblox_texture_color,roblox_texture_transform)=if decal.class=="Texture"{
|
||||||
let mut roblox_texture_color=glam::Vec4::ONE;
|
|
||||||
if decal.class=="Texture"{
|
|
||||||
//generate tranform
|
//generate tranform
|
||||||
if let (
|
if let (
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(ox)),
|
Some(rbx_dom_weak::types::Variant::Float32(ox)),
|
||||||
@ -334,13 +340,19 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
5=>(size.x,size.y),//front
|
5=>(size.x,size.y),//front
|
||||||
_=>panic!("unreachable"),
|
_=>panic!("unreachable"),
|
||||||
};
|
};
|
||||||
roblox_texture_transform=RobloxTextureTransform{
|
(
|
||||||
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency),
|
||||||
scale_u:size_u/(*sx),scale_v:size_v/(*sy),
|
RobloxTextureTransform{
|
||||||
};
|
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
||||||
roblox_texture_color=glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency);
|
scale_u:size_u/(*sx),scale_v:size_v/(*sy),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
||||||
}
|
}
|
||||||
}
|
}else{
|
||||||
|
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
||||||
|
};
|
||||||
part_texture_description[normal_id as usize]=Some(RobloxFaceTextureDescription{
|
part_texture_description[normal_id as usize]=Some(RobloxFaceTextureDescription{
|
||||||
texture:texture_id,
|
texture:texture_id,
|
||||||
color:roblox_texture_color,
|
color:roblox_texture_color,
|
||||||
@ -374,9 +386,11 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
f3,//Cube::Left->Wedge::Left
|
f3,//Cube::Left->Wedge::Left
|
||||||
f4,//Cube::Bottom->Wedge::Bottom
|
f4,//Cube::Bottom->Wedge::Bottom
|
||||||
]),
|
]),
|
||||||
|
//TODO: fix Left+Back texture coordinates to match roblox when not overwridden by Top
|
||||||
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([
|
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([
|
||||||
f0,//Cube::Right->CornerWedge::Right
|
f0,//Cube::Right->CornerWedge::Right
|
||||||
f1,//Cube::Top->CornerWedge::Top
|
if f2.is_some(){f2}else{f1.clone()},//Cube::Back|Cube::Top->CornerWedge::TopBack
|
||||||
|
if f3.is_some(){f3}else{f1},//Cube::Left|Cube::Top->CornerWedge::TopLeft
|
||||||
f4,//Cube::Bottom->CornerWedge::Bottom
|
f4,//Cube::Bottom->CornerWedge::Bottom
|
||||||
f5,//Cube::Front->CornerWedge::Front
|
f5,//Cube::Front->CornerWedge::Front
|
||||||
]),
|
]),
|
||||||
@ -435,10 +449,11 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
for (face_id,roblox_face_description) in cornerwedge_texture_description.iter().enumerate(){
|
for (face_id,roblox_face_description) in cornerwedge_texture_description.iter().enumerate(){
|
||||||
cornerwedge_face_description.insert(
|
cornerwedge_face_description.insert(
|
||||||
match face_id{
|
match face_id{
|
||||||
0=>primitives::CornerWedgeFace::Top,
|
0=>primitives::CornerWedgeFace::Right,
|
||||||
1=>primitives::CornerWedgeFace::Right,
|
1=>primitives::CornerWedgeFace::TopBack,
|
||||||
2=>primitives::CornerWedgeFace::Bottom,
|
2=>primitives::CornerWedgeFace::TopLeft,
|
||||||
3=>primitives::CornerWedgeFace::Front,
|
3=>primitives::CornerWedgeFace::Bottom,
|
||||||
|
4=>primitives::CornerWedgeFace::Front,
|
||||||
_=>panic!("unreachable"),
|
_=>panic!("unreachable"),
|
||||||
},
|
},
|
||||||
match roblox_face_description{
|
match roblox_face_description{
|
||||||
@ -454,7 +469,7 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
indexed_models[model_id].instances.push(crate::model::ModelInstance {
|
indexed_models[model_id].instances.push(crate::model::ModelInstance {
|
||||||
transform:model_transform,
|
transform:model_transform,
|
||||||
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
|
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,
|
temp_indexing:temp_indexing_attributes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
405
src/main.rs
405
src/main.rs
@ -1,17 +1,22 @@
|
|||||||
use std::{borrow::Cow, time::Instant};
|
use std::{borrow::Cow, time::Instant};
|
||||||
use wgpu::{util::DeviceExt, AstcBlock, AstcChannel};
|
use wgpu::{util::DeviceExt, AstcBlock, AstcChannel};
|
||||||
use model::{Vertex,ModelInstance,ModelGraphicsInstance};
|
use model_graphics::{GraphicsVertex,ModelGraphicsInstance};
|
||||||
use physics::{InputInstruction, PhysicsInstruction};
|
use physics::{InputInstruction, PhysicsInstruction};
|
||||||
use instruction::{TimedInstruction, InstructionConsumer};
|
use instruction::{TimedInstruction, InstructionConsumer};
|
||||||
|
|
||||||
|
mod bvh;
|
||||||
|
mod aabb;
|
||||||
mod model;
|
mod model;
|
||||||
|
mod model_graphics;
|
||||||
mod zeroes;
|
mod zeroes;
|
||||||
mod worker;
|
mod worker;
|
||||||
mod physics;
|
mod physics;
|
||||||
|
mod settings;
|
||||||
mod framework;
|
mod framework;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
mod instruction;
|
mod instruction;
|
||||||
mod load_roblox;
|
mod load_roblox;
|
||||||
|
mod integer;
|
||||||
|
|
||||||
struct Entity {
|
struct Entity {
|
||||||
index_count: u32,
|
index_count: u32,
|
||||||
@ -62,35 +67,31 @@ fn perspective_rh(fov_x_slope: f32, fov_y_slope: f32, z_near: f32, z_far: f32) -
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
impl GraphicsCamera{
|
impl GraphicsCamera{
|
||||||
pub fn new(screen_size:glam::UVec2,fov_y:f32)->Self{
|
pub fn new(screen_size:glam::UVec2,fov:glam::Vec2)->Self{
|
||||||
Self{
|
Self{
|
||||||
screen_size,
|
screen_size,
|
||||||
fov: glam::vec2(fov_y*(screen_size.x as f32)/(screen_size.y as f32),fov_y),
|
fov,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn proj(&self)->glam::Mat4{
|
pub fn proj(&self)->glam::Mat4{
|
||||||
perspective_rh(self.fov.x, self.fov.y, 0.5, 2000.0)
|
perspective_rh(self.fov.x, self.fov.y, 0.5, 2000.0)
|
||||||
}
|
}
|
||||||
pub fn view(&self,pos:glam::Vec3,angles:glam::Vec2)->glam::Mat4{
|
pub fn world(&self,pos:glam::Vec3,angles:glam::Vec2)->glam::Mat4{
|
||||||
//f32 good enough for view matrix
|
//f32 good enough for view matrix
|
||||||
glam::Mat4::from_translation(pos) * glam::Mat4::from_euler(glam::EulerRot::YXZ, angles.x, angles.y, 0f32)
|
glam::Mat4::from_translation(pos) * glam::Mat4::from_euler(glam::EulerRot::YXZ, angles.x, angles.y, 0f32)
|
||||||
}
|
}
|
||||||
pub fn set_screen_size(&mut self,screen_size:glam::UVec2){
|
|
||||||
self.screen_size=screen_size;
|
|
||||||
self.fov.x=self.fov.y*(screen_size.x as f32)/(screen_size.y as f32);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_uniform_data(&self,(pos,angles): (glam::Vec3,glam::Vec2)) -> [f32; 16 * 3 + 4] {
|
pub fn to_uniform_data(&self,(pos,angles): (glam::Vec3,glam::Vec2)) -> [f32; 16 * 4] {
|
||||||
let proj=self.proj();
|
let proj=self.proj();
|
||||||
let proj_inv = proj.inverse();
|
let proj_inv = proj.inverse();
|
||||||
let view=self.view(pos,angles);
|
let view_inv=self.world(pos,angles);
|
||||||
let view_inv = view.inverse();
|
let view=view_inv.inverse();
|
||||||
|
|
||||||
let mut raw = [0f32; 16 * 3 + 4];
|
let mut raw = [0f32; 16 * 4];
|
||||||
raw[..16].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj)[..]);
|
raw[..16].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj)[..]);
|
||||||
raw[16..32].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj_inv)[..]);
|
raw[16..32].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj_inv)[..]);
|
||||||
raw[32..48].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view_inv)[..]);
|
raw[32..48].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view)[..]);
|
||||||
raw[48..52].copy_from_slice(AsRef::<[f32; 4]>::as_ref(&view.col(3)));
|
raw[48..64].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view_inv)[..]);
|
||||||
raw
|
raw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,12 +113,16 @@ impl GraphicsState{
|
|||||||
pub fn clear(&mut self){
|
pub fn clear(&mut self){
|
||||||
self.models.clear();
|
self.models.clear();
|
||||||
}
|
}
|
||||||
|
pub fn load_user_settings(&mut self,user_settings:&settings::UserSettings){
|
||||||
|
self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GlobalState{
|
pub struct GlobalState{
|
||||||
start_time: std::time::Instant,
|
start_time: std::time::Instant,
|
||||||
manual_mouse_lock:bool,
|
manual_mouse_lock:bool,
|
||||||
mouse:physics::MouseState,
|
mouse:physics::MouseState,
|
||||||
|
user_settings:settings::UserSettings,
|
||||||
graphics:GraphicsState,
|
graphics:GraphicsState,
|
||||||
physics_thread:worker::CompatWorker<TimedInstruction<InputInstruction>,physics::PhysicsOutputState,Box<dyn FnMut(TimedInstruction<InputInstruction>)->physics::PhysicsOutputState>>,
|
physics_thread:worker::CompatWorker<TimedInstruction<InputInstruction>,physics::PhysicsOutputState,Box<dyn FnMut(TimedInstruction<InputInstruction>)->physics::PhysicsOutputState>>,
|
||||||
}
|
}
|
||||||
@ -223,9 +228,9 @@ impl GlobalState{
|
|||||||
None
|
None
|
||||||
}else{
|
}else{
|
||||||
Some(ModelGraphicsInstance{
|
Some(ModelGraphicsInstance{
|
||||||
transform: glam::Mat4::from(instance.transform),
|
transform: instance.transform.into(),
|
||||||
normal_transform: glam::Mat4::from(instance.transform.inverse()).transpose(),
|
normal_transform: Into::<glam::Mat3>::into(instance.transform.matrix3).inverse().transpose(),
|
||||||
color: instance.color,
|
color:model_graphics::ModelGraphicsColor4::from(instance.color),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
@ -233,18 +238,18 @@ impl GlobalState{
|
|||||||
let id=unique_texture_models.len();
|
let id=unique_texture_models.len();
|
||||||
let mut unique_textures=Vec::new();
|
let mut unique_textures=Vec::new();
|
||||||
for group in model.groups.into_iter(){
|
for group in model.groups.into_iter(){
|
||||||
//ignore zero coppy optimization for now
|
//ignore zero copy optimization for now
|
||||||
let texture_index=if let Some(texture_index)=unique_textures.iter().position(|&texture|texture==group.texture){
|
let texture_index=if let Some(texture_index)=unique_textures.iter().position(|&texture|texture==group.texture){
|
||||||
texture_index
|
texture_index
|
||||||
}else{
|
}else{
|
||||||
//create new texture_index
|
//create new texture_index
|
||||||
let texture_index=unique_textures.len();
|
let texture_index=unique_textures.len();
|
||||||
unique_textures.push(group.texture);
|
unique_textures.push(group.texture);
|
||||||
unique_texture_models.push(model::IndexedModelSingleTexture{
|
unique_texture_models.push(model_graphics::IndexedModelGraphicsSingleTexture{
|
||||||
unique_pos:model.unique_pos.clone(),
|
unique_pos:model.unique_pos.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(),
|
||||||
unique_tex:model.unique_tex.clone(),
|
unique_tex:model.unique_tex.iter().map(|v|*v.as_ref()).collect(),
|
||||||
unique_normal:model.unique_normal.clone(),
|
unique_normal:model.unique_normal.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(),
|
||||||
unique_color:model.unique_color.clone(),
|
unique_color:model.unique_color.iter().map(|v|*v.as_ref()).collect(),
|
||||||
unique_vertices:model.unique_vertices.clone(),
|
unique_vertices:model.unique_vertices.clone(),
|
||||||
texture:group.texture,
|
texture:group.texture,
|
||||||
groups:Vec::new(),
|
groups:Vec::new(),
|
||||||
@ -252,20 +257,193 @@ impl GlobalState{
|
|||||||
});
|
});
|
||||||
texture_index
|
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,
|
polys:group.polys,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//check every model to see if it's using the same (texture,color) but has few instances, if it is combine it into one model
|
||||||
|
//1. collect unique instances of texture and color, note model id
|
||||||
|
//2. for each model id, check if removing it from the pool decreases both the model count and instance count by more than one
|
||||||
|
//3. transpose all models that stay in the set
|
||||||
|
|
||||||
|
//best plan: benchmark set_bind_group, set_vertex_buffer, set_index_buffer and draw_indexed
|
||||||
|
//check if the estimated render performance is better by transposing multiple model instances into one model instance
|
||||||
|
|
||||||
|
//for now: just deduplicate single models...
|
||||||
|
let mut deduplicated_models=Vec::with_capacity(indexed_models_len);//use indexed_models_len because the list will likely get smaller instead of bigger
|
||||||
|
let mut unique_texture_color=std::collections::HashMap::new();//texture->color->vec![(model_id,instance_id)]
|
||||||
|
for (model_id,model) in unique_texture_models.iter().enumerate(){
|
||||||
|
//for now: filter out models with more than one instance
|
||||||
|
if 1<model.instances.len(){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//populate hashmap
|
||||||
|
let unique_color=if let Some(unique_color)=unique_texture_color.get_mut(&model.texture){
|
||||||
|
unique_color
|
||||||
|
}else{
|
||||||
|
//make new hashmap
|
||||||
|
let unique_color=std::collections::HashMap::new();
|
||||||
|
unique_texture_color.insert(model.texture,unique_color);
|
||||||
|
unique_texture_color.get_mut(&model.texture).unwrap()
|
||||||
|
};
|
||||||
|
//separate instances by color
|
||||||
|
for (instance_id,instance) in model.instances.iter().enumerate(){
|
||||||
|
let model_instance_list=if let Some(model_instance_list)=unique_color.get_mut(&instance.color){
|
||||||
|
model_instance_list
|
||||||
|
}else{
|
||||||
|
//make new hashmap
|
||||||
|
let model_instance_list=Vec::new();
|
||||||
|
unique_color.insert(instance.color.clone(),model_instance_list);
|
||||||
|
unique_color.get_mut(&instance.color).unwrap()
|
||||||
|
};
|
||||||
|
//add model instance to list
|
||||||
|
model_instance_list.push((model_id,instance_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//populate a hashmap of models selected for transposition
|
||||||
|
let mut selected_model_instances=std::collections::HashSet::new();
|
||||||
|
for (texture,unique_color) in unique_texture_color.into_iter(){
|
||||||
|
for (color,model_instance_list) in unique_color.into_iter(){
|
||||||
|
if 1<model_instance_list.len(){
|
||||||
|
//create model
|
||||||
|
let mut unique_pos=Vec::new();
|
||||||
|
let mut pos_id_from=std::collections::HashMap::new();
|
||||||
|
let mut map_pos_id=std::collections::HashMap::new();
|
||||||
|
let mut unique_tex=Vec::new();
|
||||||
|
let mut tex_id_from=std::collections::HashMap::new();
|
||||||
|
let mut map_tex_id=std::collections::HashMap::new();
|
||||||
|
let mut unique_normal=Vec::new();
|
||||||
|
let mut normal_id_from=std::collections::HashMap::new();
|
||||||
|
let mut map_normal_id=std::collections::HashMap::new();
|
||||||
|
let mut unique_color=Vec::new();
|
||||||
|
let mut color_id_from=std::collections::HashMap::new();
|
||||||
|
let mut map_color_id=std::collections::HashMap::new();
|
||||||
|
let mut unique_vertices=Vec::new();
|
||||||
|
let mut vertex_id_from=std::collections::HashMap::new();
|
||||||
|
let mut map_vertex_id=std::collections::HashMap::new();
|
||||||
|
|
||||||
|
let mut polys=Vec::new();
|
||||||
|
//transform instance vertices
|
||||||
|
for (model_id,instance_id) in model_instance_list.into_iter(){
|
||||||
|
//populate hashset to prevent these models from being copied
|
||||||
|
selected_model_instances.insert(model_id);
|
||||||
|
//there is only one instance per model
|
||||||
|
let model=&unique_texture_models[model_id];
|
||||||
|
let instance=&model.instances[instance_id];
|
||||||
|
//just hash word slices LOL
|
||||||
|
for (old_pos_id,untransformed_pos) in model.unique_pos.iter().enumerate(){
|
||||||
|
let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array();
|
||||||
|
let h=pos.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
|
||||||
|
let pos_id=if let Some(&pos_id)=pos_id_from.get(&h){
|
||||||
|
pos_id
|
||||||
|
}else{
|
||||||
|
let pos_id=unique_pos.len();
|
||||||
|
unique_pos.push(pos.clone());
|
||||||
|
pos_id_from.insert(h,pos_id);
|
||||||
|
pos_id
|
||||||
|
};
|
||||||
|
map_pos_id.insert(old_pos_id,pos_id);
|
||||||
|
}
|
||||||
|
for (old_tex_id,tex) in model.unique_tex.iter().enumerate(){
|
||||||
|
let h=tex.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
|
||||||
|
let tex_id=if let Some(&tex_id)=tex_id_from.get(&h){
|
||||||
|
tex_id
|
||||||
|
}else{
|
||||||
|
let tex_id=unique_tex.len();
|
||||||
|
unique_tex.push(tex.clone());
|
||||||
|
tex_id_from.insert(h,tex_id);
|
||||||
|
tex_id
|
||||||
|
};
|
||||||
|
map_tex_id.insert(old_tex_id,tex_id);
|
||||||
|
}
|
||||||
|
for (old_normal_id,untransformed_normal) in model.unique_normal.iter().enumerate(){
|
||||||
|
let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array();
|
||||||
|
let h=normal.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
|
||||||
|
let normal_id=if let Some(&normal_id)=normal_id_from.get(&h){
|
||||||
|
normal_id
|
||||||
|
}else{
|
||||||
|
let normal_id=unique_normal.len();
|
||||||
|
unique_normal.push(normal.clone());
|
||||||
|
normal_id_from.insert(h,normal_id);
|
||||||
|
normal_id
|
||||||
|
};
|
||||||
|
map_normal_id.insert(old_normal_id,normal_id);
|
||||||
|
}
|
||||||
|
for (old_color_id,color) in model.unique_color.iter().enumerate(){
|
||||||
|
let h=color.map(|v|u32::from_ne_bytes(v.to_ne_bytes()));
|
||||||
|
let color_id=if let Some(&color_id)=color_id_from.get(&h){
|
||||||
|
color_id
|
||||||
|
}else{
|
||||||
|
let color_id=unique_color.len();
|
||||||
|
unique_color.push(color.clone());
|
||||||
|
color_id_from.insert(h,color_id);
|
||||||
|
color_id
|
||||||
|
};
|
||||||
|
map_color_id.insert(old_color_id,color_id);
|
||||||
|
}
|
||||||
|
//map the indexed vertices onto new indices
|
||||||
|
//creating the vertex map is slightly different because the vertices are directly hashable
|
||||||
|
for (old_vertex_id,unmapped_vertex) in model.unique_vertices.iter().enumerate(){
|
||||||
|
let vertex=model::IndexedVertex{
|
||||||
|
pos:map_pos_id[&(unmapped_vertex.pos as usize)] as u32,
|
||||||
|
tex:map_tex_id[&(unmapped_vertex.tex as usize)] as u32,
|
||||||
|
normal:map_normal_id[&(unmapped_vertex.normal as usize)] as u32,
|
||||||
|
color:map_color_id[&(unmapped_vertex.color as usize)] as u32,
|
||||||
|
};
|
||||||
|
let vertex_id=if let Some(&vertex_id)=vertex_id_from.get(&vertex){
|
||||||
|
vertex_id
|
||||||
|
}else{
|
||||||
|
let vertex_id=unique_vertices.len();
|
||||||
|
unique_vertices.push(vertex.clone());
|
||||||
|
vertex_id_from.insert(vertex,vertex_id);
|
||||||
|
vertex_id
|
||||||
|
};
|
||||||
|
map_vertex_id.insert(old_vertex_id as u32,vertex_id as u32);
|
||||||
|
}
|
||||||
|
for group in &model.groups{
|
||||||
|
for poly in &group.polys{
|
||||||
|
polys.push(model::IndexedPolygon{vertices:poly.vertices.iter().map(|vertex_id|map_vertex_id[vertex_id]).collect()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//push model into dedup
|
||||||
|
deduplicated_models.push(model_graphics::IndexedModelGraphicsSingleTexture{
|
||||||
|
unique_pos,
|
||||||
|
unique_tex,
|
||||||
|
unique_normal,
|
||||||
|
unique_color,
|
||||||
|
unique_vertices,
|
||||||
|
texture,
|
||||||
|
groups:vec![model_graphics::IndexedGroupFixedTexture{
|
||||||
|
polys
|
||||||
|
}],
|
||||||
|
instances:vec![model_graphics::ModelGraphicsInstance{
|
||||||
|
transform:glam::Mat4::IDENTITY,
|
||||||
|
normal_transform:glam::Mat3::IDENTITY,
|
||||||
|
color
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//construct transposed models
|
||||||
|
//fill untouched models
|
||||||
|
for (model_id,model) in unique_texture_models.into_iter().enumerate(){
|
||||||
|
if !selected_model_instances.contains(&model_id){
|
||||||
|
deduplicated_models.push(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//de-index models
|
//de-index models
|
||||||
let mut models=Vec::with_capacity(unique_texture_models.len());
|
let mut models=Vec::with_capacity(indexed_models_len);
|
||||||
for model in unique_texture_models.into_iter(){
|
for model in deduplicated_models.into_iter(){
|
||||||
let mut vertices = Vec::new();
|
let mut vertices = Vec::new();
|
||||||
let mut index_from_vertex = std::collections::HashMap::new();//::<IndexedVertex,usize>
|
let mut index_from_vertex = std::collections::HashMap::new();//::<IndexedVertex,usize>
|
||||||
let mut entities = Vec::new();
|
let mut entities = Vec::new();
|
||||||
//TODO: combine groups using the same render pattern
|
//this mut be combined in a more complex way if the models use different render patterns per group
|
||||||
for group in model.groups {
|
|
||||||
let mut indices = Vec::new();
|
let mut indices = Vec::new();
|
||||||
|
for group in model.groups {
|
||||||
for poly in group.polys {
|
for poly in group.polys {
|
||||||
for end_index in 2..poly.vertices.len() {
|
for end_index in 2..poly.vertices.len() {
|
||||||
for &index in &[0, end_index - 1, end_index] {
|
for &index in &[0, end_index - 1, end_index] {
|
||||||
@ -275,7 +453,7 @@ impl GlobalState{
|
|||||||
}else{
|
}else{
|
||||||
let i=vertices.len() as u16;
|
let i=vertices.len() as u16;
|
||||||
let vertex=&model.unique_vertices[vertex_index as usize];
|
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],
|
pos: model.unique_pos[vertex.pos as usize],
|
||||||
tex: model.unique_tex[vertex.tex as usize],
|
tex: model.unique_tex[vertex.tex as usize],
|
||||||
normal: model.unique_normal[vertex.normal as usize],
|
normal: model.unique_normal[vertex.normal as usize],
|
||||||
@ -287,9 +465,9 @@ impl GlobalState{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entities.push(indices);
|
|
||||||
}
|
}
|
||||||
models.push(model::ModelSingleTexture{
|
entities.push(indices);
|
||||||
|
models.push(model_graphics::ModelGraphicsSingleTexture{
|
||||||
instances:model.instances,
|
instances:model.instances,
|
||||||
vertices,
|
vertices,
|
||||||
entities,
|
entities,
|
||||||
@ -372,7 +550,7 @@ impl GlobalState{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MODEL_BUFFER_SIZE:usize=4*4 + 4*4 + 4;//let size=std::mem::size_of::<ModelInstance>();
|
const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::<ModelInstance>();
|
||||||
const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4;
|
const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4;
|
||||||
fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec<f32> {
|
fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec<f32> {
|
||||||
let mut raw = Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len());
|
let mut raw = Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len());
|
||||||
@ -381,9 +559,14 @@ fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec<f32> {
|
|||||||
//model transform
|
//model transform
|
||||||
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]);
|
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]);
|
||||||
//normal transform
|
//normal transform
|
||||||
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.normal_transform)[..]);
|
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis));
|
||||||
|
raw.extend_from_slice(&[0.0]);
|
||||||
|
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis));
|
||||||
|
raw.extend_from_slice(&[0.0]);
|
||||||
|
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis));
|
||||||
|
raw.extend_from_slice(&[0.0]);
|
||||||
//color
|
//color
|
||||||
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color));
|
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get()));
|
||||||
raw.append(&mut v);
|
raw.append(&mut v);
|
||||||
}
|
}
|
||||||
raw
|
raw
|
||||||
@ -406,51 +589,53 @@ impl framework::Example for GlobalState {
|
|||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
//wee
|
||||||
|
let user_settings=settings::read_user_settings();
|
||||||
let mut indexed_models = Vec::new();
|
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_sphere());
|
||||||
indexed_models.push(primitives::unit_cylinder());
|
indexed_models.push(primitives::unit_cylinder());
|
||||||
indexed_models.push(primitives::unit_cube());
|
indexed_models.push(primitives::unit_cube());
|
||||||
println!("models.len = {:?}", indexed_models.len());
|
println!("models.len = {:?}", indexed_models.len());
|
||||||
indexed_models[0].instances.push(ModelInstance{
|
indexed_models[0].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.)),
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.))).unwrap(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
//quad monkeys
|
//quad monkeys
|
||||||
indexed_models[1].instances.push(ModelInstance{
|
indexed_models[1].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(10.,5.,10.)),
|
transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,10.))).unwrap(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
indexed_models[1].instances.push(ModelInstance{
|
indexed_models[1].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(20.,5.,10.)),
|
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),
|
color:glam::vec4(1.0,0.0,0.0,1.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
indexed_models[1].instances.push(ModelInstance{
|
indexed_models[1].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(10.,5.,20.)),
|
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),
|
color:glam::vec4(0.0,1.0,0.0,1.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
indexed_models[1].instances.push(ModelInstance{
|
indexed_models[1].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(20.,5.,20.)),
|
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),
|
color:glam::vec4(0.0,0.0,1.0,1.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
//decorative monkey
|
//decorative monkey
|
||||||
indexed_models[1].instances.push(ModelInstance{
|
indexed_models[1].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(15.,10.,15.)),
|
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),
|
color:glam::vec4(0.5,0.5,0.5,0.5),
|
||||||
attributes:model::CollisionAttributes::Decoration,
|
attributes:model::CollisionAttributes::Decoration,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
//teapot
|
//teapot
|
||||||
indexed_models[2].instances.push(ModelInstance{
|
indexed_models[2].instances.push(model::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.)),
|
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()
|
..Default::default()
|
||||||
});
|
});
|
||||||
//ground
|
//ground
|
||||||
indexed_models[3].instances.push(ModelInstance{
|
indexed_models[3].instances.push(model::ModelInstance{
|
||||||
transform:glam::Affine3A::from_translation(glam::vec3(0.,0.,0.))*glam::Affine3A::from_scale(glam::vec3(160.0, 1.0, 160.0)),
|
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()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -718,7 +903,7 @@ impl framework::Example for GlobalState {
|
|||||||
module: &shader,
|
module: &shader,
|
||||||
entry_point: "vs_entity_texture",
|
entry_point: "vs_entity_texture",
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
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,
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32x3, 3 => Float32x4],
|
attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32x3, 3 => Float32x4],
|
||||||
}],
|
}],
|
||||||
@ -745,7 +930,11 @@ impl framework::Example for GlobalState {
|
|||||||
|
|
||||||
let mut physics = physics::PhysicsState::default();
|
let mut physics = physics::PhysicsState::default();
|
||||||
|
|
||||||
let camera=GraphicsCamera::new(glam::uvec2(config.width,config.height), 1.0);
|
physics.load_user_settings(&user_settings);
|
||||||
|
|
||||||
|
let screen_size=glam::uvec2(config.width,config.height);
|
||||||
|
|
||||||
|
let camera=GraphicsCamera::new(screen_size,user_settings.calculate_fov(1.0,&screen_size).as_vec2());
|
||||||
let camera_uniforms = camera.to_uniform_data(physics.output().adjust_mouse(&physics.next_mouse));
|
let camera_uniforms = camera.to_uniform_data(physics.output().adjust_mouse(&physics.next_mouse));
|
||||||
let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Camera"),
|
label: Some("Camera"),
|
||||||
@ -780,7 +969,7 @@ impl framework::Example for GlobalState {
|
|||||||
|
|
||||||
let depth_view = Self::create_depth_texture(config, device);
|
let depth_view = Self::create_depth_texture(config, device);
|
||||||
|
|
||||||
let graphics=GraphicsState {
|
let mut graphics=GraphicsState {
|
||||||
pipelines:GraphicsPipelines{
|
pipelines:GraphicsPipelines{
|
||||||
skybox:sky_pipeline,
|
skybox:sky_pipeline,
|
||||||
model:model_pipeline
|
model:model_pipeline
|
||||||
@ -799,10 +988,12 @@ impl framework::Example for GlobalState {
|
|||||||
temp_squid_texture_view: squid_texture_view,
|
temp_squid_texture_view: squid_texture_view,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
graphics.load_user_settings(&user_settings);
|
||||||
|
|
||||||
let indexed_model_instances=model::IndexedModelInstances{
|
let indexed_model_instances=model::IndexedModelInstances{
|
||||||
textures:Vec::new(),
|
textures:Vec::new(),
|
||||||
models:indexed_models,
|
models:indexed_models,
|
||||||
spawn_point:glam::Vec3::Y*50.0,
|
spawn_point:integer::Planar64Vec3::Y*50,
|
||||||
modes:Vec::new(),
|
modes:Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -820,6 +1011,7 @@ impl framework::Example for GlobalState {
|
|||||||
start_time:Instant::now(),
|
start_time:Instant::now(),
|
||||||
manual_mouse_lock:false,
|
manual_mouse_lock:false,
|
||||||
mouse:physics::MouseState::default(),
|
mouse:physics::MouseState::default(),
|
||||||
|
user_settings,
|
||||||
graphics,
|
graphics,
|
||||||
physics_thread,
|
physics_thread,
|
||||||
};
|
};
|
||||||
@ -881,9 +1073,11 @@ impl framework::Example for GlobalState {
|
|||||||
time:physics.time,
|
time:physics.time,
|
||||||
instruction: PhysicsInstruction::Input(physics::PhysicsInputInstruction::Reset),
|
instruction: PhysicsInstruction::Input(physics::PhysicsInputInstruction::Reset),
|
||||||
});
|
});
|
||||||
|
physics.load_user_settings(&self.user_settings);
|
||||||
physics.generate_models(&indexed_model_instances);
|
physics.generate_models(&indexed_model_instances);
|
||||||
self.physics_thread=physics.into_worker();
|
self.physics_thread=physics.into_worker();
|
||||||
|
|
||||||
|
//graphics.load_user_settings(&self.user_settings);
|
||||||
self.generate_model_graphics(device,queue,indexed_model_instances);
|
self.generate_model_graphics(device,queue,indexed_model_instances);
|
||||||
//manual reset
|
//manual reset
|
||||||
}else{
|
}else{
|
||||||
@ -899,51 +1093,23 @@ impl framework::Example for GlobalState {
|
|||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: winit::event::WindowEvent) {
|
fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: winit::event::WindowEvent) {
|
||||||
|
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
|
||||||
match event {
|
match event {
|
||||||
winit::event::WindowEvent::DroppedFile(path) => self.load_file(path,device,queue),
|
winit::event::WindowEvent::DroppedFile(path) => self.load_file(path,device,queue),
|
||||||
winit::event::WindowEvent::Focused(state)=>{
|
winit::event::WindowEvent::Focused(state)=>{
|
||||||
//pause unpause
|
//pause unpause
|
||||||
//recalculate pressed keys on focus
|
//recalculate pressed keys on focus
|
||||||
}
|
},
|
||||||
_=>(),
|
winit::event::WindowEvent::KeyboardInput {
|
||||||
}
|
input:winit::event::KeyboardInput{state, virtual_keycode,..},
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
match event {
|
|
||||||
winit::event::DeviceEvent::Key(winit::event::KeyboardInput {
|
|
||||||
state,
|
|
||||||
scancode: keycode,
|
|
||||||
..
|
..
|
||||||
}) => {
|
}=>{
|
||||||
let s=match state {
|
let s=match state {
|
||||||
winit::event::ElementState::Pressed => true,
|
winit::event::ElementState::Pressed => true,
|
||||||
winit::event::ElementState::Released => false,
|
winit::event::ElementState::Released => false,
|
||||||
};
|
};
|
||||||
if let Some(input_instruction)=match keycode {
|
match virtual_keycode{
|
||||||
17=>Some(InputInstruction::MoveForward(s)),//W
|
Some(winit::event::VirtualKeyCode::Tab)=>{
|
||||||
30=>Some(InputInstruction::MoveLeft(s)),//A
|
|
||||||
31=>Some(InputInstruction::MoveBack(s)),//S
|
|
||||||
32=>Some(InputInstruction::MoveRight(s)),//D
|
|
||||||
18=>Some(InputInstruction::MoveUp(s)),//E
|
|
||||||
16=>Some(InputInstruction::MoveDown(s)),//Q
|
|
||||||
57=>Some(InputInstruction::Jump(s)),//Space
|
|
||||||
44=>Some(InputInstruction::Zoom(s)),//Z
|
|
||||||
19=>if s{Some(InputInstruction::Reset)}else{None},//R
|
|
||||||
01=>{//Esc
|
|
||||||
if s{
|
|
||||||
self.manual_mouse_lock=false;
|
|
||||||
match window.set_cursor_grab(winit::window::CursorGrabMode::None){
|
|
||||||
Ok(())=>(),
|
|
||||||
Err(e)=>println!("Could not release cursor: {:?}",e),
|
|
||||||
}
|
|
||||||
window.set_cursor_visible(true);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
},
|
|
||||||
15=>{//Tab
|
|
||||||
if s{
|
if s{
|
||||||
self.manual_mouse_lock=false;
|
self.manual_mouse_lock=false;
|
||||||
match window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.graphics.camera.screen_size.x as f32/2.0, self.graphics.camera.screen_size.y as f32/2.0)){
|
match window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.graphics.camera.screen_size.x as f32/2.0, self.graphics.camera.screen_size.y as f32/2.0)){
|
||||||
@ -971,16 +1137,56 @@ impl framework::Example for GlobalState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.set_cursor_visible(s);
|
window.set_cursor_visible(s);
|
||||||
None
|
|
||||||
},
|
},
|
||||||
_ => {println!("scancode {}",keycode);None},
|
Some(winit::event::VirtualKeyCode::F11)=>{
|
||||||
}{
|
if s{
|
||||||
self.physics_thread.send(TimedInstruction{
|
if window.fullscreen().is_some(){
|
||||||
time,
|
window.set_fullscreen(None);
|
||||||
instruction:input_instruction,
|
}else{
|
||||||
}).unwrap();
|
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(winit::event::VirtualKeyCode::Escape)=>{
|
||||||
|
if s{
|
||||||
|
self.manual_mouse_lock=false;
|
||||||
|
match window.set_cursor_grab(winit::window::CursorGrabMode::None){
|
||||||
|
Ok(())=>(),
|
||||||
|
Err(e)=>println!("Could not release cursor: {:?}",e),
|
||||||
|
}
|
||||||
|
window.set_cursor_visible(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(keycode)=>{
|
||||||
|
if let Some(input_instruction)=match keycode {
|
||||||
|
winit::event::VirtualKeyCode::W => Some(InputInstruction::MoveForward(s)),
|
||||||
|
winit::event::VirtualKeyCode::A => Some(InputInstruction::MoveLeft(s)),
|
||||||
|
winit::event::VirtualKeyCode::S => Some(InputInstruction::MoveBack(s)),
|
||||||
|
winit::event::VirtualKeyCode::D => Some(InputInstruction::MoveRight(s)),
|
||||||
|
winit::event::VirtualKeyCode::E => Some(InputInstruction::MoveUp(s)),
|
||||||
|
winit::event::VirtualKeyCode::Q => Some(InputInstruction::MoveDown(s)),
|
||||||
|
winit::event::VirtualKeyCode::Space => Some(InputInstruction::Jump(s)),
|
||||||
|
winit::event::VirtualKeyCode::Z => Some(InputInstruction::Zoom(s)),
|
||||||
|
winit::event::VirtualKeyCode::R => if s{Some(InputInstruction::Reset)}else{None},
|
||||||
|
_ => None,
|
||||||
|
}{
|
||||||
|
self.physics_thread.send(TimedInstruction{
|
||||||
|
time,
|
||||||
|
instruction:input_instruction,
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_=>(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
_=>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_event(&mut self, window: &winit::window::Window, event: winit::event::DeviceEvent) {
|
||||||
|
//there's no way this is the best way get a timestamp.
|
||||||
|
let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64);
|
||||||
|
match event {
|
||||||
winit::event::DeviceEvent::MouseMotion {
|
winit::event::DeviceEvent::MouseMotion {
|
||||||
delta,//these (f64,f64) are integers on my machine
|
delta,//these (f64,f64) are integers on my machine
|
||||||
} => {
|
} => {
|
||||||
@ -1022,7 +1228,8 @@ impl framework::Example for GlobalState {
|
|||||||
_queue: &wgpu::Queue,
|
_queue: &wgpu::Queue,
|
||||||
) {
|
) {
|
||||||
self.graphics.depth_view = Self::create_depth_texture(config, device);
|
self.graphics.depth_view = Self::create_depth_texture(config, device);
|
||||||
self.graphics.camera.set_screen_size(glam::uvec2(config.width, config.height));
|
self.graphics.camera.screen_size=glam::uvec2(config.width, config.height);
|
||||||
|
self.graphics.load_user_settings(&self.user_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
@ -1033,7 +1240,7 @@ impl framework::Example for GlobalState {
|
|||||||
_spawner: &framework::Spawner,
|
_spawner: &framework::Spawner,
|
||||||
) {
|
) {
|
||||||
//ideally this would be scheduled to execute and finish right before the render.
|
//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{
|
self.physics_thread.send(TimedInstruction{
|
||||||
time,
|
time,
|
||||||
instruction:InputInstruction::Idle,
|
instruction:InputInstruction::Idle,
|
||||||
|
87
src/model.rs
87
src/model.rs
@ -1,12 +1,6 @@
|
|||||||
use bytemuck::{Pod, Zeroable};
|
use crate::integer::{Planar64,Planar64Vec3,Planar64Affine3};
|
||||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
pub type TextureCoordinate=glam::Vec2;
|
||||||
#[repr(C)]
|
pub type Color4=glam::Vec4;
|
||||||
pub struct Vertex {
|
|
||||||
pub pos: [f32; 3],
|
|
||||||
pub tex: [f32; 2],
|
|
||||||
pub normal: [f32; 3],
|
|
||||||
pub color: [f32; 4],
|
|
||||||
}
|
|
||||||
#[derive(Clone,Hash,PartialEq,Eq)]
|
#[derive(Clone,Hash,PartialEq,Eq)]
|
||||||
pub struct IndexedVertex{
|
pub struct IndexedVertex{
|
||||||
pub pos:u32,
|
pub pos:u32,
|
||||||
@ -22,50 +16,25 @@ pub struct IndexedGroup{
|
|||||||
pub polys:Vec<IndexedPolygon>,
|
pub polys:Vec<IndexedPolygon>,
|
||||||
}
|
}
|
||||||
pub struct IndexedModel{
|
pub struct IndexedModel{
|
||||||
pub unique_pos:Vec<[f32; 3]>,
|
pub unique_pos:Vec<Planar64Vec3>,
|
||||||
pub unique_tex:Vec<[f32; 2]>,
|
pub unique_normal:Vec<Planar64Vec3>,
|
||||||
pub unique_normal:Vec<[f32; 3]>,
|
pub unique_tex:Vec<TextureCoordinate>,
|
||||||
pub unique_color:Vec<[f32; 4]>,
|
pub unique_color:Vec<Color4>,
|
||||||
pub unique_vertices:Vec<IndexedVertex>,
|
pub unique_vertices:Vec<IndexedVertex>,
|
||||||
pub groups: Vec<IndexedGroup>,
|
pub groups: Vec<IndexedGroup>,
|
||||||
pub instances:Vec<ModelInstance>,
|
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::Mat4,
|
|
||||||
pub color:glam::Vec4,
|
|
||||||
}
|
|
||||||
pub struct ModelInstance{
|
pub struct ModelInstance{
|
||||||
//pub id:u64,//this does not actually help with map fixes resimulating bots, they must always be resimulated
|
//pub id:u64,//this does not actually help with map fixes resimulating bots, they must always be resimulated
|
||||||
pub transform:glam::Affine3A,
|
pub transform:Planar64Affine3,
|
||||||
pub color:glam::Vec4,//transparency is in here
|
pub color:Color4,//transparency is in here
|
||||||
pub attributes:CollisionAttributes,
|
pub attributes:CollisionAttributes,
|
||||||
pub temp_indexing:Vec<TempIndexedAttributes>,
|
pub temp_indexing:Vec<TempIndexedAttributes>,
|
||||||
}
|
}
|
||||||
impl std::default::Default for ModelInstance{
|
impl std::default::Default for ModelInstance{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self{
|
Self{
|
||||||
color:glam::Vec4::ONE,
|
color:Color4::ONE,
|
||||||
transform:Default::default(),
|
transform:Default::default(),
|
||||||
attributes:Default::default(),
|
attributes:Default::default(),
|
||||||
temp_indexing:Default::default(),
|
temp_indexing:Default::default(),
|
||||||
@ -77,7 +46,7 @@ pub struct IndexedModelInstances{
|
|||||||
pub models:Vec<IndexedModel>,
|
pub models:Vec<IndexedModel>,
|
||||||
//may make this into an object later.
|
//may make this into an object later.
|
||||||
pub modes:Vec<ModeDescription>,
|
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.
|
//stage description referencing flattened ids is spooky, but the map loading is meant to be deterministic.
|
||||||
pub struct ModeDescription{
|
pub struct ModeDescription{
|
||||||
@ -131,13 +100,13 @@ pub struct ContactingLadder{
|
|||||||
//you have this effect while intersecting
|
//you have this effect while intersecting
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IntersectingWater{
|
pub struct IntersectingWater{
|
||||||
pub viscosity:i64,
|
pub viscosity:Planar64,
|
||||||
pub density:i64,
|
pub density:Planar64,
|
||||||
pub current:glam::Vec3,
|
pub current:Planar64Vec3,
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IntersectingAccelerator{
|
pub struct IntersectingAccelerator{
|
||||||
pub acceleration:glam::Vec3
|
pub acceleration:Planar64Vec3
|
||||||
}
|
}
|
||||||
//All models can be given these attributes
|
//All models can be given these attributes
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -146,7 +115,7 @@ pub struct GameMechanicJumpLimit{
|
|||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GameMechanicBooster{
|
pub struct GameMechanicBooster{
|
||||||
pub velocity:glam::Vec3,
|
pub velocity:Planar64Vec3,
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ZoneBehaviour{
|
pub enum ZoneBehaviour{
|
||||||
@ -183,16 +152,24 @@ pub struct GameMechanicStageElement{
|
|||||||
pub behaviour:StageElementBehaviour
|
pub behaviour:StageElementBehaviour
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GameMechanicWormhole{//(position,angles)*=origin.transform.inverse()*destination.transform
|
pub struct GameMechanicWormhole{
|
||||||
pub model_id:u32,
|
//destination does not need to be another wormhole
|
||||||
|
//this defines a one way portal to a destination model transform
|
||||||
|
//two of these can create a two way wormhole
|
||||||
|
pub destination_model_id:u32,
|
||||||
|
//(position,angles)*=origin.transform.inverse()*destination.transform
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum TeleportBehaviour{
|
||||||
|
StageElement(GameMechanicStageElement),
|
||||||
|
Wormhole(GameMechanicWormhole),
|
||||||
}
|
}
|
||||||
#[derive(Default,Clone)]
|
#[derive(Default,Clone)]
|
||||||
pub struct GameMechanicAttributes{
|
pub struct GameMechanicAttributes{
|
||||||
pub jump_limit:Option<GameMechanicJumpLimit>,
|
pub jump_limit:Option<GameMechanicJumpLimit>,
|
||||||
pub booster:Option<GameMechanicBooster>,
|
pub booster:Option<GameMechanicBooster>,
|
||||||
pub zone:Option<GameMechanicZone>,
|
pub zone:Option<GameMechanicZone>,
|
||||||
pub stage_element:Option<GameMechanicStageElement>,
|
pub teleport_behaviour:Option<TeleportBehaviour>,
|
||||||
pub wormhole:Option<GameMechanicWormhole>,//stage_element and wormhole are in conflict
|
|
||||||
}
|
}
|
||||||
#[derive(Default,Clone)]
|
#[derive(Default,Clone)]
|
||||||
pub struct ContactingAttributes{
|
pub struct ContactingAttributes{
|
||||||
@ -227,7 +204,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();
|
let mut unique_vertex_index = std::collections::HashMap::<obj::IndexTuple,u32>::new();
|
||||||
return data.objects.iter().map(|object|{
|
return data.objects.iter().map(|object|{
|
||||||
unique_vertex_index.clear();
|
unique_vertex_index.clear();
|
||||||
@ -257,9 +234,9 @@ pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:[f32;4]) ->
|
|||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
IndexedModel{
|
IndexedModel{
|
||||||
unique_pos: data.position.clone(),
|
unique_pos: data.position.iter().map(|&v|Planar64Vec3::try_from(v).unwrap()).collect(),
|
||||||
unique_tex: data.texture.clone(),
|
unique_tex: data.texture.iter().map(|&v|TextureCoordinate::from_array(v)).collect(),
|
||||||
unique_normal: data.normal.clone(),
|
unique_normal: data.normal.iter().map(|&v|Planar64Vec3::try_from(v).unwrap()).collect(),
|
||||||
unique_color: vec![color],
|
unique_color: vec![color],
|
||||||
unique_vertices,
|
unique_vertices,
|
||||||
groups,
|
groups,
|
||||||
|
55
src/model_graphics.rs
Normal file
55
src/model_graphics.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
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,PartialEq)]
|
||||||
|
pub struct ModelGraphicsColor4(glam::Vec4);
|
||||||
|
impl ModelGraphicsColor4{
|
||||||
|
pub const fn get(&self)->glam::Vec4{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<glam::Vec4> for ModelGraphicsColor4{
|
||||||
|
fn from(value:glam::Vec4)->Self{
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::hash::Hash for ModelGraphicsColor4{
|
||||||
|
fn hash<H: std::hash::Hasher>(&self,state:&mut H) {
|
||||||
|
for &f in self.0.as_ref(){
|
||||||
|
u32::from_ne_bytes(f.to_ne_bytes()).hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for ModelGraphicsColor4{}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModelGraphicsInstance{
|
||||||
|
pub transform:glam::Mat4,
|
||||||
|
pub normal_transform:glam::Mat3,
|
||||||
|
pub color:ModelGraphicsColor4,
|
||||||
|
}
|
1
src/model_physics.rs
Normal file
1
src/model_physics.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
//
|
687
src/physics.rs
687
src/physics.rs
File diff suppressed because it is too large
Load Diff
@ -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)]
|
#[derive(Debug)]
|
||||||
pub enum Primitives{
|
pub enum Primitives{
|
||||||
@ -17,24 +18,29 @@ pub enum CubeFace{
|
|||||||
Bottom,
|
Bottom,
|
||||||
Front,
|
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_TEXTURE_COORDS:[TextureCoordinate;4]=[
|
||||||
const CUBE_DEFAULT_VERTICES:[[f32;3];8]=[
|
TextureCoordinate::new(0.0,0.0),
|
||||||
[-1.,-1., 1.],//0 left bottom back
|
TextureCoordinate::new(1.0,0.0),
|
||||||
[ 1.,-1., 1.],//1 right bottom back
|
TextureCoordinate::new(1.0,1.0),
|
||||||
[ 1., 1., 1.],//2 right top back
|
TextureCoordinate::new(0.0,1.0),
|
||||||
[-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_NORMALS:[[f32;3];6]=[
|
const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[
|
||||||
[ 1., 0., 0.],//CubeFace::Right
|
Planar64Vec3::int(-1,-1, 1),//0 left bottom back
|
||||||
[ 0., 1., 0.],//CubeFace::Top
|
Planar64Vec3::int( 1,-1, 1),//1 right bottom back
|
||||||
[ 0., 0., 1.],//CubeFace::Back
|
Planar64Vec3::int( 1, 1, 1),//2 right top back
|
||||||
[-1., 0., 0.],//CubeFace::Left
|
Planar64Vec3::int(-1, 1, 1),//3 left top back
|
||||||
[ 0.,-1., 0.],//CubeFace::Bottom
|
Planar64Vec3::int(-1, 1,-1),//4 left top front
|
||||||
[ 0., 0.,-1.],//CubeFace::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]=[
|
const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[
|
||||||
// right (1, 0, 0)
|
// right (1, 0, 0)
|
||||||
@ -89,12 +95,12 @@ pub enum WedgeFace{
|
|||||||
Left,
|
Left,
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
const WEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
|
const WEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
|
||||||
[ 1., 0., 0.],//Wedge::Right
|
Planar64Vec3::int( 1, 0, 0),//Wedge::Right
|
||||||
[ 0., 1.,-1.],//Wedge::TopFront
|
Planar64Vec3::int( 0, 1,-1),//Wedge::TopFront
|
||||||
[ 0., 0., 1.],//Wedge::Back
|
Planar64Vec3::int( 0, 0, 1),//Wedge::Back
|
||||||
[-1., 0., 0.],//Wedge::Left
|
Planar64Vec3::int(-1, 0, 0),//Wedge::Left
|
||||||
[ 0.,-1., 0.],//Wedge::Bottom
|
Planar64Vec3::int( 0,-1, 0),//Wedge::Bottom
|
||||||
];
|
];
|
||||||
/*
|
/*
|
||||||
local cornerWedgeVerticies = {
|
local cornerWedgeVerticies = {
|
||||||
@ -107,25 +113,24 @@ local cornerWedgeVerticies = {
|
|||||||
*/
|
*/
|
||||||
#[derive(Hash,PartialEq,Eq)]
|
#[derive(Hash,PartialEq,Eq)]
|
||||||
pub enum CornerWedgeFace{
|
pub enum CornerWedgeFace{
|
||||||
Top,
|
|
||||||
Right,
|
Right,
|
||||||
|
TopBack,
|
||||||
|
TopLeft,
|
||||||
Bottom,
|
Bottom,
|
||||||
Front,
|
Front,
|
||||||
}
|
}
|
||||||
const CORNERWEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
|
const CORNERWEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
|
||||||
[ 1., 0., 0.],//CornerWedge::Right
|
Planar64Vec3::int( 1, 0, 0),//CornerWedge::Right
|
||||||
[ 0., 1., 1.],//CornerWedge::BackTop
|
Planar64Vec3::int( 0, 1, 1),//CornerWedge::BackTop
|
||||||
[-1., 1., 0.],//CornerWedge::LeftTop
|
Planar64Vec3::int(-1, 1, 0),//CornerWedge::LeftTop
|
||||||
[ 0.,-1., 0.],//CornerWedge::Bottom
|
Planar64Vec3::int( 0,-1, 0),//CornerWedge::Bottom
|
||||||
[ 0., 0.,-1.],//CornerWedge::Front
|
Planar64Vec3::int( 0, 0,-1),//CornerWedge::Front
|
||||||
];
|
];
|
||||||
//HashMap fits this use case perfectly but feels like using a sledgehammer to drive a nail
|
//HashMap fits this use case perfectly but feels like using a sledgehammer to drive a nail
|
||||||
pub fn unit_sphere()->crate::model::IndexedModel{
|
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(){
|
for pos in indexed_model.unique_pos.iter_mut(){
|
||||||
pos[0]=pos[0]*0.5;
|
*pos=*pos/2;
|
||||||
pos[1]=pos[1]*0.5;
|
|
||||||
pos[2]=pos[2]*0.5;
|
|
||||||
}
|
}
|
||||||
indexed_model
|
indexed_model
|
||||||
}
|
}
|
||||||
@ -140,11 +145,11 @@ pub fn unit_cube()->crate::model::IndexedModel{
|
|||||||
t.insert(CubeFace::Front,FaceDescription::default());
|
t.insert(CubeFace::Front,FaceDescription::default());
|
||||||
generate_partial_unit_cube(t)
|
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{
|
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(){
|
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
|
indexed_model
|
||||||
}
|
}
|
||||||
@ -162,7 +167,8 @@ pub type CornerWedgeFaceDescription=std::collections::HashMap::<CornerWedgeFace,
|
|||||||
pub fn unit_cornerwedge()->crate::model::IndexedModel{
|
pub fn unit_cornerwedge()->crate::model::IndexedModel{
|
||||||
let mut t=CornerWedgeFaceDescription::new();
|
let mut t=CornerWedgeFaceDescription::new();
|
||||||
t.insert(CornerWedgeFace::Right,FaceDescription::default());
|
t.insert(CornerWedgeFace::Right,FaceDescription::default());
|
||||||
t.insert(CornerWedgeFace::Top,FaceDescription::default());
|
t.insert(CornerWedgeFace::TopBack,FaceDescription::default());
|
||||||
|
t.insert(CornerWedgeFace::TopLeft,FaceDescription::default());
|
||||||
t.insert(CornerWedgeFace::Bottom,FaceDescription::default());
|
t.insert(CornerWedgeFace::Bottom,FaceDescription::default());
|
||||||
t.insert(CornerWedgeFace::Front,FaceDescription::default());
|
t.insert(CornerWedgeFace::Front,FaceDescription::default());
|
||||||
generate_partial_unit_cornerwedge(t)
|
generate_partial_unit_cornerwedge(t)
|
||||||
@ -172,33 +178,33 @@ pub fn unit_cornerwedge()->crate::model::IndexedModel{
|
|||||||
pub struct FaceDescription{
|
pub struct FaceDescription{
|
||||||
pub texture:Option<u32>,
|
pub texture:Option<u32>,
|
||||||
pub transform:glam::Affine2,
|
pub transform:glam::Affine2,
|
||||||
pub color:glam::Vec4,
|
pub color:Color4,
|
||||||
}
|
}
|
||||||
impl std::default::Default for FaceDescription{
|
impl std::default::Default for FaceDescription{
|
||||||
fn default()->Self {
|
fn default()->Self {
|
||||||
Self{
|
Self{
|
||||||
texture:None,
|
texture:None,
|
||||||
transform:glam::Affine2::IDENTITY,
|
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{
|
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}
|
Self{texture:Some(texture),transform,color}
|
||||||
}
|
}
|
||||||
pub fn from_texture(texture:u32)->Self{
|
pub fn from_texture(texture:u32)->Self{
|
||||||
Self{
|
Self{
|
||||||
texture:Some(texture),
|
texture:Some(texture),
|
||||||
transform:glam::Affine2::IDENTITY,
|
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.
|
//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
|
//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{
|
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_tex=Vec::new();
|
||||||
let mut generated_normal=Vec::new();
|
let mut generated_normal=Vec::new();
|
||||||
let mut generated_color=Vec::new();
|
let mut generated_color=Vec::new();
|
||||||
@ -215,16 +221,16 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate:
|
|||||||
let transform_index=transforms.len();
|
let transform_index=transforms.len();
|
||||||
transforms.push(face_description.transform);
|
transforms.push(face_description.transform);
|
||||||
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
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
|
transform_index
|
||||||
} as u32;
|
} 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
|
color_index
|
||||||
}else{
|
}else{
|
||||||
//create new color_index
|
//create new color_index
|
||||||
let color_index=generated_color.len();
|
let color_index=generated_color.len();
|
||||||
generated_color.push(*face_description.color.as_ref());
|
generated_color.push(face_description.color);
|
||||||
color_index
|
color_index
|
||||||
} as u32;
|
} as u32;
|
||||||
let face_id=match face{
|
let face_id=match face{
|
||||||
@ -313,7 +319,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crat
|
|||||||
[6,2,4],
|
[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_tex=Vec::new();
|
||||||
let mut generated_normal=Vec::new();
|
let mut generated_normal=Vec::new();
|
||||||
let mut generated_color=Vec::new();
|
let mut generated_color=Vec::new();
|
||||||
@ -330,16 +336,16 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crat
|
|||||||
let transform_index=transforms.len();
|
let transform_index=transforms.len();
|
||||||
transforms.push(face_description.transform);
|
transforms.push(face_description.transform);
|
||||||
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
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
|
transform_index
|
||||||
} as u32;
|
} 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
|
color_index
|
||||||
}else{
|
}else{
|
||||||
//create new color_index
|
//create new color_index
|
||||||
let color_index=generated_color.len();
|
let color_index=generated_color.len();
|
||||||
generated_color.push(*face_description.color.as_ref());
|
generated_color.push(face_description.color);
|
||||||
color_index
|
color_index
|
||||||
} as u32;
|
} as u32;
|
||||||
let face_id=match face{
|
let face_id=match face{
|
||||||
@ -425,7 +431,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
|
|||||||
[7,2,4],
|
[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_tex=Vec::new();
|
||||||
let mut generated_normal=Vec::new();
|
let mut generated_normal=Vec::new();
|
||||||
let mut generated_color=Vec::new();
|
let mut generated_color=Vec::new();
|
||||||
@ -442,23 +448,24 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
|
|||||||
let transform_index=transforms.len();
|
let transform_index=transforms.len();
|
||||||
transforms.push(face_description.transform);
|
transforms.push(face_description.transform);
|
||||||
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
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
|
transform_index
|
||||||
} as u32;
|
} 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
|
color_index
|
||||||
}else{
|
}else{
|
||||||
//create new color_index
|
//create new color_index
|
||||||
let color_index=generated_color.len();
|
let color_index=generated_color.len();
|
||||||
generated_color.push(*face_description.color.as_ref());
|
generated_color.push(face_description.color);
|
||||||
color_index
|
color_index
|
||||||
} as u32;
|
} as u32;
|
||||||
let face_id=match face{
|
let face_id=match face{
|
||||||
CornerWedgeFace::Right => 0,
|
CornerWedgeFace::Right => 0,
|
||||||
CornerWedgeFace::Top => 1,
|
CornerWedgeFace::TopBack => 1,
|
||||||
CornerWedgeFace::Bottom => 2,
|
CornerWedgeFace::TopLeft => 2,
|
||||||
CornerWedgeFace::Front => 3,
|
CornerWedgeFace::Bottom => 3,
|
||||||
|
CornerWedgeFace::Front => 4,
|
||||||
};
|
};
|
||||||
//always push normal
|
//always push normal
|
||||||
let normal_index=generated_normal.len() as u32;
|
let normal_index=generated_normal.len() as u32;
|
||||||
|
134
src/settings.rs
Normal file
134
src/settings.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use crate::integer::{Ratio64,Ratio64Vec2};
|
||||||
|
struct Ratio{
|
||||||
|
ratio:f64,
|
||||||
|
}
|
||||||
|
enum DerivedFov{
|
||||||
|
FromScreenAspect,
|
||||||
|
FromAspect(Ratio),
|
||||||
|
}
|
||||||
|
enum Fov{
|
||||||
|
Exactly{x:f64,y:f64},
|
||||||
|
SpecifyXDeriveY{x:f64,y:DerivedFov},
|
||||||
|
SpecifyYDeriveX{x:DerivedFov,y:f64},
|
||||||
|
}
|
||||||
|
impl Default for Fov{
|
||||||
|
fn default()->Self{
|
||||||
|
Fov::SpecifyYDeriveX{x:DerivedFov::FromScreenAspect,y:1.0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enum DerivedSensitivity{
|
||||||
|
FromRatio(Ratio64),
|
||||||
|
}
|
||||||
|
enum Sensitivity{
|
||||||
|
Exactly{x:Ratio64,y:Ratio64},
|
||||||
|
SpecifyXDeriveY{x:Ratio64,y:DerivedSensitivity},
|
||||||
|
SpecifyYDeriveX{x:DerivedSensitivity,y:Ratio64},
|
||||||
|
}
|
||||||
|
impl Default for Sensitivity{
|
||||||
|
fn default()->Self{
|
||||||
|
Sensitivity::SpecifyXDeriveY{x:Ratio64::ONE*524288,y:DerivedSensitivity::FromRatio(Ratio64::ONE)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UserSettings{
|
||||||
|
fov:Fov,
|
||||||
|
sensitivity:Sensitivity,
|
||||||
|
}
|
||||||
|
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::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)->Ratio64Vec2{
|
||||||
|
match &self.sensitivity{
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//sensitivity is raw input dots (i.e. dpi = dots per inch) to radians conversion factor
|
||||||
|
sensitivity_x=0.001
|
||||||
|
sensitivity_y_from_x_ratio=1
|
||||||
|
Sensitivity::DeriveY{x:0.0.001,y:DerivedSensitivity{ratio:1.0}}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn read_user_settings()->UserSettings{
|
||||||
|
let mut cfg=configparser::ini::Ini::new();
|
||||||
|
if let Ok(_)=cfg.load("settings.conf"){
|
||||||
|
let (cfg_fov_x,cfg_fov_y)=(cfg.getfloat("camera","fov_x"),cfg.getfloat("camera","fov_y"));
|
||||||
|
let fov=match(cfg_fov_x,cfg_fov_y){
|
||||||
|
(Ok(Some(fov_x)),Ok(Some(fov_y)))=>Fov::Exactly {
|
||||||
|
x:fov_x,
|
||||||
|
y:fov_y
|
||||||
|
},
|
||||||
|
(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})
|
||||||
|
}else{
|
||||||
|
DerivedFov::FromScreenAspect
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(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{
|
||||||
|
DerivedFov::FromScreenAspect
|
||||||
|
},
|
||||||
|
y:fov_y,
|
||||||
|
},
|
||||||
|
_=>{
|
||||||
|
Fov::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
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:Ratio64::try_from(sensitivity_x).unwrap(),
|
||||||
|
y:Ratio64::try_from(sensitivity_y).unwrap(),
|
||||||
|
},
|
||||||
|
(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)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(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()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
UserSettings{
|
||||||
|
fov,
|
||||||
|
sensitivity,
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
UserSettings::default()
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,8 @@ struct Camera {
|
|||||||
proj_inv: mat4x4<f32>,
|
proj_inv: mat4x4<f32>,
|
||||||
// from world to camera
|
// from world to camera
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
// camera position
|
// from camera to world
|
||||||
cam_pos: vec4<f32>,
|
view_inv: mat4x4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
//group 0 is the camera
|
//group 0 is the camera
|
||||||
@ -31,8 +31,7 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
|
|||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
|
|
||||||
// transposition = inversion for this orthonormal matrix
|
let inv_model_view = mat3x3<f32>(camera.view_inv[0].xyz, camera.view_inv[1].xyz, camera.view_inv[2].xyz);
|
||||||
let inv_model_view = transpose(mat3x3<f32>(camera.view[0].xyz, camera.view[1].xyz, camera.view[2].xyz));
|
|
||||||
let unprojected = camera.proj_inv * pos;
|
let unprojected = camera.proj_inv * pos;
|
||||||
|
|
||||||
var result: SkyOutput;
|
var result: SkyOutput;
|
||||||
@ -43,7 +42,7 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
|
|||||||
|
|
||||||
struct ModelInstance{
|
struct ModelInstance{
|
||||||
transform:mat4x4<f32>,
|
transform:mat4x4<f32>,
|
||||||
normal_transform:mat4x4<f32>,
|
normal_transform:mat3x3<f32>,
|
||||||
color:vec4<f32>,
|
color:vec4<f32>,
|
||||||
}
|
}
|
||||||
//my fancy idea is to create a megatexture for each model that includes all the textures each intance will need
|
//my fancy idea is to create a megatexture for each model that includes all the textures each intance will need
|
||||||
@ -78,11 +77,11 @@ fn vs_entity_texture(
|
|||||||
) -> EntityOutputTexture {
|
) -> EntityOutputTexture {
|
||||||
var position: vec4<f32> = model_instances[instance].transform * vec4<f32>(pos, 1.0);
|
var position: vec4<f32> = model_instances[instance].transform * vec4<f32>(pos, 1.0);
|
||||||
var result: EntityOutputTexture;
|
var result: EntityOutputTexture;
|
||||||
result.normal = (model_instances[instance].normal_transform * vec4<f32>(normal, 1.0)).xyz;
|
result.normal = model_instances[instance].normal_transform * normal;
|
||||||
result.texture = texture;
|
result.texture = texture;
|
||||||
result.color = color;
|
result.color = color;
|
||||||
result.model_color = model_instances[instance].color;
|
result.model_color = model_instances[instance].color;
|
||||||
result.view = position.xyz - camera.cam_pos.xyz;
|
result.view = position.xyz - camera.view_inv[3].xyz;//col(3)
|
||||||
result.position = camera.proj * camera.view * position;
|
result.position = camera.proj * camera.view * position;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -109,5 +108,5 @@ fn fs_entity_texture(vertex: EntityOutputTexture) -> @location(0) vec4<f32> {
|
|||||||
|
|
||||||
let fragment_color = textureSample(model_texture, model_sampler, vertex.texture)*vertex.color;
|
let fragment_color = textureSample(model_texture, model_sampler, vertex.texture)*vertex.color;
|
||||||
let reflected_color = textureSample(cube_texture, cube_sampler, reflected).rgb;
|
let reflected_color = textureSample(cube_texture, cube_sampler, reflected).rgb;
|
||||||
return mix(vec4<f32>(vec3<f32>(0.05) + 0.2 * reflected_color,1.0),mix(vertex.model_color,vec4<f32>(fragment_color.rgb,1.0),fragment_color.a),1.0-pow(1.0-abs(d),2.0));
|
return mix(vec4<f32>(vec3<f32>(0.05) + 0.2 * reflected_color,1.0),mix(vertex.model_color,vec4<f32>(fragment_color.rgb,1.0),fragment_color.a),0.5+0.5*abs(d));
|
||||||
}
|
}
|
||||||
|
@ -74,14 +74,14 @@ impl<Task,Value:Clone,F:FnMut(Task)->Value> CompatWorker<Task,Value,F> {
|
|||||||
fn test_worker() {
|
fn test_worker() {
|
||||||
println!("hiiiii");
|
println!("hiiiii");
|
||||||
// Create the worker thread
|
// Create the worker thread
|
||||||
let worker = Worker::new(crate::physics::Body::with_pva(glam::Vec3::ZERO,glam::Vec3::ZERO,glam::Vec3::ZERO),
|
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(glam::Vec3::ONE,glam::Vec3::ONE,glam::Vec3::ONE)
|
|_|crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send tasks to the worker
|
// Send tasks to the worker
|
||||||
for i in 0..5 {
|
for _ in 0..5 {
|
||||||
let task = crate::instruction::TimedInstruction{
|
let task = crate::instruction::TimedInstruction{
|
||||||
time:0,
|
time:crate::integer::Time::ZERO,
|
||||||
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
||||||
};
|
};
|
||||||
worker.send(task).unwrap();
|
worker.send(task).unwrap();
|
||||||
@ -95,12 +95,12 @@ fn test_worker() {
|
|||||||
|
|
||||||
// Send a new task
|
// Send a new task
|
||||||
let task = crate::instruction::TimedInstruction{
|
let task = crate::instruction::TimedInstruction{
|
||||||
time:0,
|
time:crate::integer::Time::ZERO,
|
||||||
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
||||||
};
|
};
|
||||||
worker.send(task).unwrap();
|
worker.send(task).unwrap();
|
||||||
|
|
||||||
println!("value={:?}",worker.grab_clone());
|
println!("value={}",worker.grab_clone());
|
||||||
|
|
||||||
// wait long enough to see print from final task
|
// wait long enough to see print from final task
|
||||||
thread::sleep(std::time::Duration::from_secs(1));
|
thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
@ -1,26 +1,30 @@
|
|||||||
//find roots of polynomials
|
//find roots of polynomials
|
||||||
|
use crate::integer::Planar64;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zeroes2(a0:f32,a1:f32,a2:f32) -> Vec<f32>{
|
pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
|
||||||
if a2==0f32{
|
if a2==Planar64::ZERO{
|
||||||
return zeroes1(a0, a1);
|
return zeroes1(a0, a1);
|
||||||
}
|
}
|
||||||
let mut radicand=a1*a1-4f32*a2*a0;
|
let mut radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
|
||||||
if 0f32<radicand {
|
if 0<radicand {
|
||||||
radicand=radicand.sqrt();
|
//start with f64 sqrt
|
||||||
if 0f32<a2 {
|
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});
|
||||||
return vec![(-a1-radicand)/(2f32*a2),(-a1+radicand)/(2f32*a2)];
|
//TODO: one or two newtons
|
||||||
|
if Planar64::ZERO<a2 {
|
||||||
|
return vec![(-a1-planar_radicand)/(a2*2),(-a1+planar_radicand)/(a2*2)];
|
||||||
} else {
|
} 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 {
|
} else if radicand==0 {
|
||||||
return vec![-a1/(2f32*a2)];
|
return vec![a1/(a2*-2)];
|
||||||
} else {
|
} else {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zeroes1(a0:f32,a1:f32) -> Vec<f32> {
|
pub fn zeroes1(a0:Planar64,a1:Planar64) -> Vec<Planar64> {
|
||||||
if a1==0f32{
|
if a1==Planar64::ZERO{
|
||||||
return vec![];
|
return vec![];
|
||||||
} else {
|
} else {
|
||||||
return vec![-a0/a1];
|
return vec![-a0/a1];
|
||||||
|
Reference in New Issue
Block a user