forked from StrafesNET/strafe-project
Compare commits
1 Commits
file-forma
...
model-look
| Author | SHA1 | Date | |
|---|---|---|---|
| 2632228b8d |
35
Cargo.lock
generated
35
Cargo.lock
generated
@@ -331,12 +331,6 @@ 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"
|
||||||
@@ -840,29 +834,6 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy-regex"
|
|
||||||
version = "3.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e723bd417b2df60a0f6a2b6825f297ea04b245d4ba52b5a22cb679bdf58b05fa"
|
|
||||||
dependencies = [
|
|
||||||
"lazy-regex-proc_macros",
|
|
||||||
"once_cell",
|
|
||||||
"regex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy-regex-proc_macros"
|
|
||||||
version = "3.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0f0a1d9139f0ee2e862e08a9c5d0ba0470f2aa21cd1e1aa1b1562f83116c725f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"regex",
|
|
||||||
"syn 2.0.29",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -1688,23 +1659,21 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strafe-client"
|
name = "strafe-client"
|
||||||
version = "0.8.0"
|
version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-executor",
|
"async-executor",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"configparser",
|
|
||||||
"ddsfile",
|
"ddsfile",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"glam",
|
"glam",
|
||||||
"lazy-regex",
|
|
||||||
"log",
|
"log",
|
||||||
"obj",
|
"obj",
|
||||||
"parking_lot",
|
|
||||||
"pollster",
|
"pollster",
|
||||||
"rbx_binary",
|
"rbx_binary",
|
||||||
"rbx_dom_weak",
|
"rbx_dom_weak",
|
||||||
"rbx_reflection_database",
|
"rbx_reflection_database",
|
||||||
"rbx_xml",
|
"rbx_xml",
|
||||||
|
"regex",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|||||||
14
Cargo.toml
14
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "strafe-client"
|
name = "strafe-client"
|
||||||
version = "0.8.0"
|
version = "0.7.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,23 +8,21 @@ 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"
|
||||||
lazy-regex = "3.0.2"
|
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
obj = "0.10.2"
|
obj = "0.10.2"
|
||||||
parking_lot = "0.12.1"
|
|
||||||
pollster = "0.3.0"
|
pollster = "0.3.0"
|
||||||
rbx_binary = "0.7.1"
|
rbx_binary = "0.7.1"
|
||||||
rbx_dom_weak = "2.5.0"
|
rbx_dom_weak = "2.5.0"
|
||||||
rbx_reflection_database = "0.2.7"
|
rbx_reflection_database = "0.2.7"
|
||||||
rbx_xml = "0.13.1"
|
rbx_xml = "0.13.1"
|
||||||
|
regex = "1.9.5"
|
||||||
wgpu = "0.17.0"
|
wgpu = "0.17.0"
|
||||||
winit = "0.28.6"
|
winit = "0.28.6"
|
||||||
|
|
||||||
#[profile.release]
|
[profile.release]
|
||||||
#lto = true
|
lto = true
|
||||||
#strip = true
|
strip = true
|
||||||
#codegen-units = 1
|
codegen-units = 1
|
||||||
|
|||||||
89
src/aabb.rs
89
src/aabb.rs
@@ -1,89 +0,0 @@
|
|||||||
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
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
1042
src/body.rs
Normal file
1042
src/body.rs
Normal file
File diff suppressed because it is too large
Load Diff
107
src/bvh.rs
107
src/bvh.rs
@@ -1,107 +0,0 @@
|
|||||||
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<usize>,
|
|
||||||
aabb:Aabb,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BvhNode{
|
|
||||||
pub fn the_tester<F:FnMut(usize)>(&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}).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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,9 +51,8 @@ pub trait Example: 'static + Sized {
|
|||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
);
|
);
|
||||||
fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: WindowEvent);
|
fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, event: WindowEvent);
|
||||||
fn device_event(&mut self, window: &winit::window::Window, event: DeviceEvent);
|
fn device_event(&mut self, event: DeviceEvent);
|
||||||
fn load_file(&mut self, path:std::path::PathBuf, device: &wgpu::Device, queue: &wgpu::Queue);
|
|
||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &wgpu::TextureView,
|
view: &wgpu::TextureView,
|
||||||
@@ -368,14 +367,14 @@ fn start<E: Example>(
|
|||||||
println!("{:#?}", instance.generate_report());
|
println!("{:#?}", instance.generate_report());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
example.update(&window,&device,&queue,event);
|
example.update(&device,&queue,event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
event::Event::DeviceEvent {
|
event::Event::DeviceEvent {
|
||||||
event,
|
event,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
example.device_event(&window,event);
|
example.device_event(event);
|
||||||
},
|
},
|
||||||
event::Event::RedrawRequested(_) => {
|
event::Event::RedrawRequested(_) => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
use crate::integer::Time;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TimedInstruction<I>{
|
pub struct TimedInstruction<I> {
|
||||||
pub time:Time,
|
pub time: crate::body::TIME,
|
||||||
pub instruction:I,
|
pub instruction: I,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InstructionEmitter<I>{
|
pub trait InstructionEmitter<I> {
|
||||||
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<I>>;
|
fn next_instruction(&self, time_limit:crate::body::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:Time,
|
time: crate::body::TIME,
|
||||||
instruction:Option<I>,
|
instruction: Option<I>,
|
||||||
}
|
}
|
||||||
impl<I> InstructionCollector<I>{
|
impl<I> InstructionCollector<I> {
|
||||||
pub fn new(time:Time)->Self{
|
pub fn new(time:crate::body::TIME) -> Self {
|
||||||
Self{
|
Self{
|
||||||
time,
|
time,
|
||||||
instruction:None
|
instruction:None
|
||||||
@@ -27,24 +25,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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
953
src/integer.rs
953
src/integer.rs
@@ -1,953 +0,0 @@
|
|||||||
//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::Mul<Time> for Planar64{
|
|
||||||
type Output=Planar64;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self,rhs:Time)->Self::Output{
|
|
||||||
Planar64(((self.0 as i128*rhs.0 as i128)/1_000_000_000) 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 const fn raw(x:i64,y:i64,z:i64)->Self{
|
|
||||||
Self(glam::i64vec3(x,y,z))
|
|
||||||
}
|
|
||||||
#[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_yx(yaw:Angle32,pitch:Angle32)->Self{
|
|
||||||
let xtheta=yaw.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
|
|
||||||
let (xs,xc)=xtheta.sin_cos();
|
|
||||||
let (xc,xs)=(xc*PLANAR64_ONE_FLOAT64,xs*PLANAR64_ONE_FLOAT64);
|
|
||||||
let ytheta=pitch.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
|
|
||||||
let (ys,yc)=ytheta.sin_cos();
|
|
||||||
let (yc,ys)=(yc*PLANAR64_ONE_FLOAT64,ys*PLANAR64_ONE_FLOAT64);
|
|
||||||
//TODO: fix this rounding towards 0
|
|
||||||
let (xc,xs):(i64,i64)=(unsafe{xc.to_int_unchecked()},unsafe{xs.to_int_unchecked()});
|
|
||||||
let (yc,ys):(i64,i64)=(unsafe{yc.to_int_unchecked()},unsafe{ys.to_int_unchecked()});
|
|
||||||
Self::from_cols(
|
|
||||||
Planar64Vec3(glam::i64vec3(xc,0,-xs)),
|
|
||||||
Planar64Vec3(glam::i64vec3(((xs as i128*ys as i128)>>32) as i64,yc,((xc as i128*ys as i128)>>32) as i64)),
|
|
||||||
Planar64Vec3(glam::i64vec3(((xs as i128*yc as i128)>>32) as i64,-ys,((xc as i128*yc as i128)>>32) as i64)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
#[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,5 +1,6 @@
|
|||||||
|
use crate::model::{IndexedModelInstances,ModelInstance};
|
||||||
|
|
||||||
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 {
|
||||||
@@ -31,127 +32,13 @@ fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>
|
|||||||
//next class
|
//next class
|
||||||
objects
|
objects
|
||||||
}
|
}
|
||||||
fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Planar64Affine3{
|
|
||||||
Planar64Affine3::new(
|
|
||||||
Planar64Mat3::from_cols(
|
|
||||||
Planar64Vec3::try_from([cf.orientation.x.x,cf.orientation.y.x,cf.orientation.z.x]).unwrap()
|
|
||||||
*Planar64::try_from(size.x/2.0).unwrap(),
|
|
||||||
Planar64Vec3::try_from([cf.orientation.x.y,cf.orientation.y.y,cf.orientation.z.y]).unwrap()
|
|
||||||
*Planar64::try_from(size.y/2.0).unwrap(),
|
|
||||||
Planar64Vec3::try_from([cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z]).unwrap()
|
|
||||||
*Planar64::try_from(size.z/2.0).unwrap(),
|
|
||||||
),
|
|
||||||
Planar64Vec3::try_from([cf.position.x,cf.position.y,cf.position.z]).unwrap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_intersecting:bool)->crate::model::CollisionAttributes{
|
|
||||||
let mut general=crate::model::GameMechanicAttributes::default();
|
|
||||||
let mut intersecting=crate::model::IntersectingAttributes::default();
|
|
||||||
let mut contacting=crate::model::ContactingAttributes::default();
|
|
||||||
let mut force_can_collide=can_collide;
|
|
||||||
match name{
|
|
||||||
"Water"=>{
|
|
||||||
force_can_collide=false;
|
|
||||||
//TODO: read stupid CustomPhysicalProperties
|
|
||||||
intersecting.water=Some(crate::model::IntersectingWater{density:Planar64::ONE,viscosity:Planar64::ONE/10,current:velocity});
|
|
||||||
},
|
|
||||||
"Accelerator"=>{
|
|
||||||
//although the new game supports collidable accelerators, this is a roblox compatability map loader
|
|
||||||
force_can_collide=false;
|
|
||||||
general.accelerator=Some(crate::model::GameMechanicAccelerator{acceleration:velocity});
|
|
||||||
},
|
|
||||||
"SetVelocity"=>general.trajectory=Some(crate::model::GameMechanicSetTrajectory::Velocity(velocity)),
|
|
||||||
"MapFinish"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Finish})},
|
|
||||||
"MapAnticheat"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Anitcheat})},
|
|
||||||
"Platform"=>general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
|
|
||||||
mode_id:0,
|
|
||||||
stage_id:0,
|
|
||||||
force:false,
|
|
||||||
behaviour:crate::model::StageElementBehaviour::Platform,
|
|
||||||
})),
|
|
||||||
other=>{
|
|
||||||
if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
|
|
||||||
.captures(other){
|
|
||||||
general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
|
|
||||||
mode_id:0,
|
|
||||||
stage_id:captures[3].parse::<u32>().unwrap(),
|
|
||||||
force:match captures.get(1){
|
|
||||||
Some(m)=>m.as_str()=="Force",
|
|
||||||
None=>false,
|
|
||||||
},
|
|
||||||
behaviour:match &captures[2]{
|
|
||||||
"Spawn"|"SpawnAt"=>crate::model::StageElementBehaviour::SpawnAt,
|
|
||||||
//cancollide false so you don't hit the side
|
|
||||||
//NOT a decoration
|
|
||||||
"Trigger"=>{force_can_collide=false;crate::model::StageElementBehaviour::Trigger},
|
|
||||||
"Teleport"=>{force_can_collide=false;crate::model::StageElementBehaviour::Teleport},
|
|
||||||
"Platform"=>crate::model::StageElementBehaviour::Platform,
|
|
||||||
_=>panic!("regex1[2] messed up bad"),
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Jump)(\d+)$")
|
|
||||||
.captures(other){
|
|
||||||
general.teleport_behaviour=Some(crate::model::TeleportBehaviour::StageElement(crate::model::GameMechanicStageElement{
|
|
||||||
mode_id:0,
|
|
||||||
stage_id:0,
|
|
||||||
force:match captures.get(1){
|
|
||||||
Some(m)=>m.as_str()=="Force",
|
|
||||||
None=>false,
|
|
||||||
},
|
|
||||||
behaviour:match &captures[2]{
|
|
||||||
"Jump"=>crate::model::StageElementBehaviour::JumpLimit(captures[3].parse::<u32>().unwrap()),
|
|
||||||
_=>panic!("regex4[1] messed up bad"),
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
|
|
||||||
.captures(other){
|
|
||||||
force_can_collide=false;
|
|
||||||
match &captures[1]{
|
|
||||||
"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}),
|
|
||||||
_=>panic!("regex2[1] messed up bad"),
|
|
||||||
}
|
|
||||||
}else if let Some(captures)=lazy_regex::regex!(r"^(WormholeIn)(\d+)$")
|
|
||||||
.captures(other){
|
|
||||||
force_can_collide=false;
|
|
||||||
match &captures[1]{
|
|
||||||
"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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//need some way to skip this
|
|
||||||
if velocity!=Planar64Vec3::ZERO{
|
|
||||||
general.booster=Some(crate::model::GameMechanicBooster::Velocity(velocity));
|
|
||||||
}
|
|
||||||
match force_can_collide{
|
|
||||||
true=>{
|
|
||||||
match name{
|
|
||||||
"Bounce"=>contacting.contact_behaviour=Some(crate::model::ContactingBehaviour::Elastic(u32::MAX)),
|
|
||||||
"Surf"=>contacting.contact_behaviour=Some(crate::model::ContactingBehaviour::Surf),
|
|
||||||
"Ladder"=>contacting.contact_behaviour=Some(crate::model::ContactingBehaviour::Ladder(crate::model::ContactingLadder{sticky:true})),
|
|
||||||
_=>(),
|
|
||||||
}
|
|
||||||
crate::model::CollisionAttributes::Contact{contacting,general}
|
|
||||||
},
|
|
||||||
false=>if force_intersecting
|
|
||||||
||general.any()
|
|
||||||
||intersecting.any()
|
|
||||||
{
|
|
||||||
crate::model::CollisionAttributes::Intersect{intersecting,general}
|
|
||||||
}else{
|
|
||||||
crate::model::CollisionAttributes::Decoration
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RobloxAssetId(u64);
|
struct RobloxAssetId(u64);
|
||||||
struct RobloxAssetIdParseErr;
|
struct RobloxAssetIdParseErr;
|
||||||
impl std::str::FromStr for RobloxAssetId {
|
impl std::str::FromStr for RobloxAssetId {
|
||||||
type Err=RobloxAssetIdParseErr;
|
type Err=RobloxAssetIdParseErr;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err>{
|
fn from_str(s: &str) -> Result<Self, Self::Err>{
|
||||||
let regman=lazy_regex::regex!(r"(\d+)$");
|
let regman=regex::Regex::new(r"(\d+)$").unwrap();
|
||||||
if let Some(captures) = regman.captures(s) {
|
if let Some(captures) = regman.captures(s) {
|
||||||
if captures.len()==2{//captures[0] is all captures concatenated, and then each individual capture
|
if captures.len()==2{//captures[0] is all captures concatenated, and then each individual capture
|
||||||
if let Ok(id) = captures[0].parse::<u64>() {
|
if let Ok(id) = captures[0].parse::<u64>() {
|
||||||
@@ -215,7 +102,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>;5];
|
type RobloxCornerWedgeDescription=[Option<RobloxFaceTextureDescription>;4];
|
||||||
#[derive(Clone,Eq,Hash,PartialEq)]
|
#[derive(Clone,Eq,Hash,PartialEq)]
|
||||||
enum RobloxBasePartDescription{
|
enum RobloxBasePartDescription{
|
||||||
Sphere,
|
Sphere,
|
||||||
@@ -224,9 +111,9 @@ enum RobloxBasePartDescription{
|
|||||||
Wedge(RobloxWedgeDescription),
|
Wedge(RobloxWedgeDescription),
|
||||||
CornerWedge(RobloxCornerWedgeDescription),
|
CornerWedge(RobloxCornerWedgeDescription),
|
||||||
}
|
}
|
||||||
pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::IndexedModelInstances{
|
pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(IndexedModelInstances,glam::Vec3), Box<dyn std::error::Error>>{
|
||||||
//IndexedModelInstances includes textures
|
//IndexedModelInstances includes textures
|
||||||
let mut spawn_point=Planar64Vec3::ZERO;
|
let mut spawn_point=glam::Vec3::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();
|
||||||
@@ -242,50 +129,36 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
if let (
|
if let (
|
||||||
Some(rbx_dom_weak::types::Variant::CFrame(cf)),
|
Some(rbx_dom_weak::types::Variant::CFrame(cf)),
|
||||||
Some(rbx_dom_weak::types::Variant::Vector3(size)),
|
Some(rbx_dom_weak::types::Variant::Vector3(size)),
|
||||||
Some(rbx_dom_weak::types::Variant::Vector3(velocity)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(transparency)),
|
Some(rbx_dom_weak::types::Variant::Float32(transparency)),
|
||||||
Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
|
Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
|
||||||
Some(rbx_dom_weak::types::Variant::Bool(can_collide)),
|
|
||||||
) = (
|
) = (
|
||||||
object.properties.get("CFrame"),
|
object.properties.get("CFrame"),
|
||||||
object.properties.get("Size"),
|
object.properties.get("Size"),
|
||||||
object.properties.get("Velocity"),
|
|
||||||
object.properties.get("Transparency"),
|
object.properties.get("Transparency"),
|
||||||
object.properties.get("Color"),
|
object.properties.get("Color"),
|
||||||
object.properties.get("CanCollide"),
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let model_transform=planar64_affine3_from_roblox(cf,size);
|
let model_transform=glam::Affine3A::from_translation(
|
||||||
|
glam::Vec3::new(cf.position.x,cf.position.y,cf.position.z)
|
||||||
//push TempIndexedAttributes
|
)
|
||||||
let mut force_intersecting=false;
|
* glam::Affine3A::from_mat3(
|
||||||
let mut temp_indexing_attributes=Vec::new();
|
glam::Mat3::from_cols(
|
||||||
if let Some(attr)=match &object.name[..]{
|
glam::Vec3::new(cf.orientation.x.x,cf.orientation.y.x,cf.orientation.z.x),
|
||||||
"MapStart"=>{
|
glam::Vec3::new(cf.orientation.x.y,cf.orientation.y.y,cf.orientation.z.y),
|
||||||
spawn_point=model_transform.transform_point3(Planar64Vec3::ZERO)+Planar64Vec3::Y*5/2;
|
glam::Vec3::new(cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z),
|
||||||
Some(crate::model::TempIndexedAttributes::Start(crate::model::TempAttrStart{mode_id:0}))
|
),
|
||||||
},
|
)
|
||||||
"UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint(crate::model::TempAttrUnorderedCheckpoint{mode_id:0})),
|
* glam::Affine3A::from_scale(
|
||||||
other=>{
|
glam::Vec3::new(size.x,size.y,size.z)/2.0
|
||||||
let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|OrderedCheckpoint|WormholeOut)(\d+)$");
|
);
|
||||||
if let Some(captures) = regman.captures(other) {
|
if object.name=="MapStart"{
|
||||||
match &captures[1]{
|
spawn_point=model_transform.transform_point3(glam::Vec3::Y)+glam::vec3(0.0,2.5,0.0);
|
||||||
"BonusStart"=>Some(crate::model::TempIndexedAttributes::Start(crate::model::TempAttrStart{mode_id:captures[2].parse::<u32>().unwrap()})),
|
println!("Found MapStart{:?}",spawn_point);
|
||||||
"Spawn"|"ForceSpawn"=>Some(crate::model::TempIndexedAttributes::Spawn(crate::model::TempAttrSpawn{mode_id:0,stage_id:captures[2].parse::<u32>().unwrap()})),
|
|
||||||
"OrderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::OrderedCheckpoint(crate::model::TempAttrOrderedCheckpoint{mode_id:0,checkpoint_id:captures[2].parse::<u32>().unwrap()})),
|
|
||||||
"WormholeOut"=>Some(crate::model::TempIndexedAttributes::Wormhole(crate::model::TempAttrWormhole{wormhole_id:captures[2].parse::<u32>().unwrap()})),
|
|
||||||
_=>None,
|
|
||||||
}
|
}
|
||||||
}else{
|
if *transparency==1.0 {
|
||||||
None
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
}{
|
|
||||||
force_intersecting=true;
|
|
||||||
temp_indexing_attributes.push(attr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: also detect "CylinderMesh" etc here
|
|
||||||
let shape=match &object.class[..]{
|
let shape=match &object.class[..]{
|
||||||
"Part"=>{
|
"Part"=>{
|
||||||
if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){
|
if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){
|
||||||
@@ -295,10 +168,14 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
2=>primitives::Primitives::Cylinder,
|
2=>primitives::Primitives::Cylinder,
|
||||||
3=>primitives::Primitives::Wedge,
|
3=>primitives::Primitives::Wedge,
|
||||||
4=>primitives::Primitives::CornerWedge,
|
4=>primitives::Primitives::CornerWedge,
|
||||||
_=>panic!("Funky roblox PartType={};",shape.to_u32()),
|
_=>{
|
||||||
|
println!("Funky roblox PartType={}; defaulting to cube",shape.to_u32());
|
||||||
|
primitives::Primitives::Cube
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
panic!("Part has no Shape!");
|
println!("Part has no Shape! defaulting to cube");
|
||||||
|
primitives::Primitives::Cube
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"WedgePart"=>primitives::Primitives::Wedge,
|
"WedgePart"=>primitives::Primitives::Wedge,
|
||||||
@@ -309,6 +186,38 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//TODO: also detect "CylinderMesh" etc here
|
||||||
|
let mut face_map=std::collections::HashMap::new();
|
||||||
|
match shape{
|
||||||
|
primitives::Primitives::Cube => {
|
||||||
|
face_map.insert(0,0);//Right
|
||||||
|
face_map.insert(1,1);//Top
|
||||||
|
face_map.insert(2,2);//Back
|
||||||
|
face_map.insert(3,3);//Left
|
||||||
|
face_map.insert(4,4);//Bottom
|
||||||
|
face_map.insert(5,5);//Front
|
||||||
|
},
|
||||||
|
primitives::Primitives::Wedge => {
|
||||||
|
face_map.insert(0,0);//Right
|
||||||
|
face_map.insert(1,1);//Top -> TopFront (some surf maps put surf textures on the Top face)
|
||||||
|
face_map.insert(2,1);//Front -> TopFront
|
||||||
|
face_map.insert(3,2);//Back
|
||||||
|
face_map.insert(4,3);//Left
|
||||||
|
face_map.insert(5,4);//Bottom
|
||||||
|
},
|
||||||
|
primitives::Primitives::CornerWedge => {
|
||||||
|
//Right -> None
|
||||||
|
face_map.insert(1,0);//Top
|
||||||
|
//Back -> None
|
||||||
|
face_map.insert(3,1);//Right
|
||||||
|
face_map.insert(4,2);//Bottom
|
||||||
|
face_map.insert(5,3);//Front
|
||||||
|
},
|
||||||
|
//do not support textured spheres/cylinders imported from roblox
|
||||||
|
//this can be added later, there are some maps that use it
|
||||||
|
primitives::Primitives::Sphere
|
||||||
|
|primitives::Primitives::Cylinder => (),
|
||||||
|
}
|
||||||
//use the biggest one and cut it down later...
|
//use the biggest one and cut it down later...
|
||||||
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
||||||
temp_objects.clear();
|
temp_objects.clear();
|
||||||
@@ -336,8 +245,10 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
texture_id
|
texture_id
|
||||||
};
|
};
|
||||||
let normal_id=normalid.to_u32();
|
let normal_id=normalid.to_u32();
|
||||||
if normal_id<6{
|
if let Some(&face)=face_map.get(&normal_id){
|
||||||
let (roblox_texture_color,roblox_texture_transform)=if decal.class=="Texture"{
|
let mut roblox_texture_transform=RobloxTextureTransform::default();
|
||||||
|
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)),
|
||||||
@@ -358,22 +269,16 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
3=>(size.z,size.y),//left
|
3=>(size.z,size.y),//left
|
||||||
4=>(size.x,size.z),//bottom
|
4=>(size.x,size.z),//bottom
|
||||||
5=>(size.x,size.y),//front
|
5=>(size.x,size.y),//front
|
||||||
_=>panic!("unreachable"),
|
_=>(1.,1.),
|
||||||
};
|
};
|
||||||
(
|
roblox_texture_transform=RobloxTextureTransform{
|
||||||
glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency),
|
|
||||||
RobloxTextureTransform{
|
|
||||||
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
||||||
scale_u:size_u/(*sx),scale_v:size_v/(*sy),
|
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{
|
roblox_texture_color=glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part_texture_description[face]=Some(RobloxFaceTextureDescription{
|
||||||
texture:texture_id,
|
texture:texture_id,
|
||||||
color:roblox_texture_color,
|
color:roblox_texture_color,
|
||||||
transform:roblox_texture_transform,
|
transform:roblox_texture_transform,
|
||||||
@@ -386,36 +291,16 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//obscure rust syntax "slice pattern"
|
//obscure rust syntax "slice pattern"
|
||||||
let [
|
let [f0,f1,f2,f3,f4,f5]=part_texture_description;
|
||||||
f0,//Cube::Right
|
|
||||||
f1,//Cube::Top
|
|
||||||
f2,//Cube::Back
|
|
||||||
f3,//Cube::Left
|
|
||||||
f4,//Cube::Bottom
|
|
||||||
f5,//Cube::Front
|
|
||||||
]=part_texture_description;
|
|
||||||
let basepart_texture_description=match shape{
|
let basepart_texture_description=match shape{
|
||||||
primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere,
|
primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere,
|
||||||
primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]),
|
primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]),
|
||||||
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder,
|
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder,
|
||||||
//use front face texture first and use top face texture as a fallback
|
//HAHAHA
|
||||||
primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([
|
primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([f0,f1,f2,f3,f4]),
|
||||||
f0,//Cube::Right->Wedge::Right
|
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([f0,f1,f2,f3]),
|
||||||
if f5.is_some(){f5}else{f1},//Cube::Front|Cube::Top->Wedge::TopFront
|
|
||||||
f2,//Cube::Back->Wedge::Back
|
|
||||||
f3,//Cube::Left->Wedge::Left
|
|
||||||
f4,//Cube::Bottom->Wedge::Bottom
|
|
||||||
]),
|
|
||||||
//TODO: fix Left+Back texture coordinates to match roblox when not overwridden by Top
|
|
||||||
primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([
|
|
||||||
f0,//Cube::Right->CornerWedge::Right
|
|
||||||
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
|
|
||||||
f5,//Cube::Front->CornerWedge::Front
|
|
||||||
]),
|
|
||||||
};
|
};
|
||||||
//make new model if unit cube has not been created before
|
//make new model if unit cube has not been crated before
|
||||||
let model_id=if let Some(&model_id)=model_id_from_description.get(&basepart_texture_description){
|
let model_id=if let Some(&model_id)=model_id_from_description.get(&basepart_texture_description){
|
||||||
//push to existing texture model
|
//push to existing texture model
|
||||||
model_id
|
model_id
|
||||||
@@ -469,11 +354,10 @@ 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::Right,
|
0=>primitives::CornerWedgeFace::Top,
|
||||||
1=>primitives::CornerWedgeFace::TopBack,
|
1=>primitives::CornerWedgeFace::Right,
|
||||||
2=>primitives::CornerWedgeFace::TopLeft,
|
2=>primitives::CornerWedgeFace::Bottom,
|
||||||
3=>primitives::CornerWedgeFace::Bottom,
|
3=>primitives::CornerWedgeFace::Front,
|
||||||
4=>primitives::CornerWedgeFace::Front,
|
|
||||||
_=>panic!("unreachable"),
|
_=>panic!("unreachable"),
|
||||||
},
|
},
|
||||||
match roblox_face_description{
|
match roblox_face_description{
|
||||||
@@ -486,19 +370,15 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
|
|||||||
});
|
});
|
||||||
model_id
|
model_id
|
||||||
};
|
};
|
||||||
indexed_models[model_id].instances.push(crate::model::ModelInstance {
|
indexed_models[model_id].instances.push(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,Planar64Vec3::try_from([velocity.x,velocity.y,velocity.z]).unwrap(),force_intersecting),
|
|
||||||
temp_indexing:temp_indexing_attributes,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::model::IndexedModelInstances{
|
Ok((IndexedModelInstances{
|
||||||
textures:asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),
|
textures:asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),
|
||||||
models:indexed_models,
|
models:indexed_models,
|
||||||
spawn_point,
|
},spawn_point))
|
||||||
modes:Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
765
src/main.rs
765
src/main.rs
File diff suppressed because it is too large
Load Diff
277
src/model.rs
277
src/model.rs
@@ -1,6 +1,12 @@
|
|||||||
use crate::integer::{Time,Planar64,Planar64Vec3,Planar64Affine3};
|
use bytemuck::{Pod, Zeroable};
|
||||||
pub type TextureCoordinate=glam::Vec2;
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
pub type Color4=glam::Vec4;
|
#[repr(C)]
|
||||||
|
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,
|
||||||
@@ -16,243 +22,50 @@ pub struct IndexedGroup{
|
|||||||
pub polys:Vec<IndexedPolygon>,
|
pub polys:Vec<IndexedPolygon>,
|
||||||
}
|
}
|
||||||
pub struct IndexedModel{
|
pub struct IndexedModel{
|
||||||
pub unique_pos:Vec<Planar64Vec3>,
|
pub unique_pos:Vec<[f32; 3]>,
|
||||||
pub unique_normal:Vec<Planar64Vec3>,
|
pub unique_tex:Vec<[f32; 2]>,
|
||||||
pub unique_tex:Vec<TextureCoordinate>,
|
pub unique_normal:Vec<[f32; 3]>,
|
||||||
pub unique_color:Vec<Color4>,
|
pub unique_color:Vec<[f32; 4]>,
|
||||||
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 ModelInstance{
|
pub struct IndexedGroupFixedTexture{
|
||||||
//pub id:u64,//this does not actually help with map fixes resimulating bots, they must always be resimulated
|
pub polys:Vec<IndexedPolygon>,
|
||||||
pub transform:Planar64Affine3,
|
|
||||||
pub color:Color4,//transparency is in here
|
|
||||||
pub attributes:CollisionAttributes,
|
|
||||||
pub temp_indexing:Vec<TempIndexedAttributes>,
|
|
||||||
}
|
}
|
||||||
impl std::default::Default for ModelInstance{
|
pub struct IndexedModelSingleTexture{
|
||||||
fn default() -> Self {
|
pub unique_pos:Vec<[f32; 3]>,
|
||||||
Self{
|
pub unique_tex:Vec<[f32; 2]>,
|
||||||
color:Color4::ONE,
|
pub unique_normal:Vec<[f32; 3]>,
|
||||||
transform:Default::default(),
|
pub unique_color:Vec<[f32; 4]>,
|
||||||
attributes:Default::default(),
|
pub unique_vertices:Vec<IndexedVertex>,
|
||||||
temp_indexing:Default::default(),
|
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 transform:glam::Affine3A,
|
||||||
|
pub color:glam::Vec4,
|
||||||
}
|
}
|
||||||
pub struct IndexedModelInstances{
|
pub struct IndexedModelInstances{
|
||||||
pub textures:Vec<String>,//RenderPattern
|
pub textures:Vec<String>,//RenderPattern
|
||||||
pub models:Vec<IndexedModel>,
|
pub models:Vec<IndexedModel>,
|
||||||
//may make this into an object later.
|
//object_index for spawns, triggers etc?
|
||||||
pub modes:Vec<ModeDescription>,
|
|
||||||
pub spawn_point:Planar64Vec3,
|
|
||||||
}
|
|
||||||
//stage description referencing flattened ids is spooky, but the map loading is meant to be deterministic.
|
|
||||||
pub struct ModeDescription{
|
|
||||||
pub start:usize,//start=model_id
|
|
||||||
pub spawns:Vec<usize>,//spawns[spawn_id]=model_id
|
|
||||||
pub ordered_checkpoints:Vec<usize>,//ordered_checkpoints[checkpoint_id]=model_id
|
|
||||||
pub unordered_checkpoints:Vec<usize>,//unordered_checkpoints[checkpoint_id]=model_id
|
|
||||||
pub spawn_from_stage_id:std::collections::HashMap::<u32,usize>,
|
|
||||||
pub ordered_checkpoint_from_checkpoint_id:std::collections::HashMap::<u32,usize>,
|
|
||||||
}
|
|
||||||
impl ModeDescription{
|
|
||||||
pub fn get_spawn_model_id(&self,stage_id:u32)->Option<&usize>{
|
|
||||||
self.spawns.get(*self.spawn_from_stage_id.get(&stage_id)?)
|
|
||||||
}
|
|
||||||
pub fn get_ordered_checkpoint_model_id(&self,checkpoint_id:u32)->Option<&usize>{
|
|
||||||
self.ordered_checkpoints.get(*self.ordered_checkpoint_from_checkpoint_id.get(&checkpoint_id)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//I don't want this code to exist!
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TempAttrStart{
|
|
||||||
pub mode_id:u32,
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TempAttrSpawn{
|
|
||||||
pub mode_id:u32,
|
|
||||||
pub stage_id:u32,
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TempAttrOrderedCheckpoint{
|
|
||||||
pub mode_id:u32,
|
|
||||||
pub checkpoint_id:u32,
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TempAttrUnorderedCheckpoint{
|
|
||||||
pub mode_id:u32,
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TempAttrWormhole{
|
|
||||||
pub wormhole_id:u32,
|
|
||||||
}
|
|
||||||
pub enum TempIndexedAttributes{
|
|
||||||
Start(TempAttrStart),
|
|
||||||
Spawn(TempAttrSpawn),
|
|
||||||
OrderedCheckpoint(TempAttrOrderedCheckpoint),
|
|
||||||
UnorderedCheckpoint(TempAttrUnorderedCheckpoint),
|
|
||||||
Wormhole(TempAttrWormhole),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//you have this effect while in contact
|
pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:[f32;4]) -> Vec<IndexedModel>{
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ContactingLadder{
|
|
||||||
pub sticky:bool
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ContactingBehaviour{
|
|
||||||
Surf,
|
|
||||||
Ladder(ContactingLadder),
|
|
||||||
Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32
|
|
||||||
}
|
|
||||||
//you have this effect while intersecting
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct IntersectingWater{
|
|
||||||
pub viscosity:Planar64,
|
|
||||||
pub density:Planar64,
|
|
||||||
pub current:Planar64Vec3,
|
|
||||||
}
|
|
||||||
//All models can be given these attributes
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GameMechanicAccelerator{
|
|
||||||
pub acceleration:Planar64Vec3
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum GameMechanicBooster{
|
|
||||||
Affine(Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more
|
|
||||||
Velocity(Planar64Vec3),//straight up boost velocity adds to your current velocity
|
|
||||||
Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum TrajectoryChoice{
|
|
||||||
HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time
|
|
||||||
LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum GameMechanicSetTrajectory{
|
|
||||||
AirTime(Time),//air time (relative to gravity direction) is invariant across mass and gravity changes
|
|
||||||
Height(Planar64),//boost height (relative to gravity direction) is invariant across mass and gravity changes
|
|
||||||
TargetPointTime{//launch on a trajectory that will land at a target point in a set amount of time
|
|
||||||
target_point:Planar64Vec3,
|
|
||||||
time:Time,//short time = fast and direct, long time = launch high in the air, negative time = wrong way
|
|
||||||
},
|
|
||||||
TrajectoryTargetPoint{//launch at a fixed speed and land at a target point
|
|
||||||
target_point:Planar64Vec3,
|
|
||||||
speed:Planar64,//if speed is too low this will fail to reach the target. The closest-passing trajectory will be chosen instead
|
|
||||||
trajectory_choice:TrajectoryChoice,
|
|
||||||
},
|
|
||||||
Velocity(Planar64Vec3),//SetVelocity
|
|
||||||
DotVelocity{direction:Planar64Vec3,dot:Planar64},//set your velocity in a specific direction without touching other directions
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ZoneBehaviour{
|
|
||||||
//Start is indexed
|
|
||||||
//Checkpoints are indexed
|
|
||||||
Finish,
|
|
||||||
Anitcheat,
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GameMechanicZone{
|
|
||||||
pub mode_id:u32,
|
|
||||||
pub behaviour:ZoneBehaviour,
|
|
||||||
}
|
|
||||||
// enum TrapCondition{
|
|
||||||
// FasterThan(Planar64),
|
|
||||||
// SlowerThan(Planar64),
|
|
||||||
// InRange(Planar64,Planar64),
|
|
||||||
// OutsideRange(Planar64,Planar64),
|
|
||||||
// }
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum StageElementBehaviour{
|
|
||||||
//Spawn,//The behaviour of stepping on a spawn setting the spawnid
|
|
||||||
SpawnAt,
|
|
||||||
Trigger,
|
|
||||||
Teleport,
|
|
||||||
Platform,
|
|
||||||
JumpLimit(u32),
|
|
||||||
//Speedtrap(TrapCondition),//Acts as a trigger with a speed condition
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GameMechanicStageElement{
|
|
||||||
pub mode_id:u32,
|
|
||||||
pub stage_id:u32,//which spawn to send to
|
|
||||||
pub force:bool,//allow setting to lower spawn id i.e. 7->3
|
|
||||||
pub behaviour:StageElementBehaviour
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GameMechanicWormhole{
|
|
||||||
//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),
|
|
||||||
}
|
|
||||||
//attributes listed in order of handling
|
|
||||||
#[derive(Default,Clone)]
|
|
||||||
pub struct GameMechanicAttributes{
|
|
||||||
pub zone:Option<GameMechanicZone>,
|
|
||||||
pub booster:Option<GameMechanicBooster>,
|
|
||||||
pub trajectory:Option<GameMechanicSetTrajectory>,
|
|
||||||
pub teleport_behaviour:Option<TeleportBehaviour>,
|
|
||||||
pub accelerator:Option<GameMechanicAccelerator>,
|
|
||||||
}
|
|
||||||
impl GameMechanicAttributes{
|
|
||||||
pub fn any(&self)->bool{
|
|
||||||
self.booster.is_some()
|
|
||||||
||self.trajectory.is_some()
|
|
||||||
||self.zone.is_some()
|
|
||||||
||self.teleport_behaviour.is_some()
|
|
||||||
||self.accelerator.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Default,Clone)]
|
|
||||||
pub struct ContactingAttributes{
|
|
||||||
//friction?
|
|
||||||
pub contact_behaviour:Option<ContactingBehaviour>,
|
|
||||||
}
|
|
||||||
impl ContactingAttributes{
|
|
||||||
pub fn any(&self)->bool{
|
|
||||||
self.contact_behaviour.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Default,Clone)]
|
|
||||||
pub struct IntersectingAttributes{
|
|
||||||
pub water:Option<IntersectingWater>,
|
|
||||||
}
|
|
||||||
impl IntersectingAttributes{
|
|
||||||
pub fn any(&self)->bool{
|
|
||||||
self.water.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Spawn(u32) NO! spawns are indexed in the map header instead of marked with attibutes
|
|
||||||
pub enum CollisionAttributes{
|
|
||||||
Decoration,//visual only
|
|
||||||
Contact{//track whether you are contacting the object
|
|
||||||
contacting:ContactingAttributes,
|
|
||||||
general:GameMechanicAttributes,
|
|
||||||
},
|
|
||||||
Intersect{//track whether you are intersecting the object
|
|
||||||
intersecting:IntersectingAttributes,
|
|
||||||
general:GameMechanicAttributes,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
impl std::default::Default for CollisionAttributes{
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Contact{
|
|
||||||
contacting:ContactingAttributes::default(),
|
|
||||||
general:GameMechanicAttributes::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
@@ -282,9 +95,9 @@ pub fn generate_indexed_model_list_from_obj(data:obj::ObjData,color:Color4)->Vec
|
|||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
IndexedModel{
|
IndexedModel{
|
||||||
unique_pos: data.position.iter().map(|&v|Planar64Vec3::try_from(v).unwrap()).collect(),
|
unique_pos: data.position.clone(),
|
||||||
unique_tex: data.texture.iter().map(|&v|TextureCoordinate::from_array(v)).collect(),
|
unique_tex: data.texture.clone(),
|
||||||
unique_normal: data.normal.iter().map(|&v|Planar64Vec3::try_from(v).unwrap()).collect(),
|
unique_normal: data.normal.clone(),
|
||||||
unique_color: vec![color],
|
unique_color: vec![color],
|
||||||
unique_vertices,
|
unique_vertices,
|
||||||
groups,
|
groups,
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
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(){
|
|
||||||
bytemuck::cast::<f32,u32>(f).hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Eq for ModelGraphicsColor4{}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ModelGraphicsInstance{
|
|
||||||
pub transform:glam::Mat4,
|
|
||||||
pub normal_transform:glam::Mat3,
|
|
||||||
pub color:ModelGraphicsColor4,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//
|
|
||||||
1564
src/physics.rs
1564
src/physics.rs
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
|||||||
use crate::model::{Color4,TextureCoordinate,IndexedModel,IndexedPolygon,IndexedGroup,IndexedVertex};
|
use crate::model::{IndexedModel, IndexedPolygon, IndexedGroup, IndexedVertex};
|
||||||
use crate::integer::Planar64Vec3;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Primitives{
|
pub enum Primitives{
|
||||||
@@ -18,29 +17,24 @@ pub enum CubeFace{
|
|||||||
Bottom,
|
Bottom,
|
||||||
Front,
|
Front,
|
||||||
}
|
}
|
||||||
const CUBE_DEFAULT_TEXTURE_COORDS:[TextureCoordinate;4]=[
|
const CUBE_DEFAULT_TEXTURE_COORDS:[[f32;2];4]=[[0.0,0.0],[1.0,0.0],[1.0,1.0],[0.0,1.0]];
|
||||||
TextureCoordinate::new(0.0,0.0),
|
const CUBE_DEFAULT_VERTICES:[[f32;3];8]=[
|
||||||
TextureCoordinate::new(1.0,0.0),
|
[-1.,-1., 1.],//0 left bottom back
|
||||||
TextureCoordinate::new(1.0,1.0),
|
[ 1.,-1., 1.],//1 right bottom back
|
||||||
TextureCoordinate::new(0.0,1.0),
|
[ 1., 1., 1.],//2 right top back
|
||||||
|
[-1., 1., 1.],//3 left top back
|
||||||
|
[-1., 1.,-1.],//4 left top front
|
||||||
|
[ 1., 1.,-1.],//5 right top front
|
||||||
|
[ 1.,-1.,-1.],//6 right bottom front
|
||||||
|
[-1.,-1.,-1.],//7 left bottom front
|
||||||
];
|
];
|
||||||
const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[
|
const CUBE_DEFAULT_NORMALS:[[f32;3];6]=[
|
||||||
Planar64Vec3::int(-1,-1, 1),//0 left bottom back
|
[ 1., 0., 0.],//CubeFace::Right
|
||||||
Planar64Vec3::int( 1,-1, 1),//1 right bottom back
|
[ 0., 1., 0.],//CubeFace::Top
|
||||||
Planar64Vec3::int( 1, 1, 1),//2 right top back
|
[ 0., 0., 1.],//CubeFace::Back
|
||||||
Planar64Vec3::int(-1, 1, 1),//3 left top back
|
[-1., 0., 0.],//CubeFace::Left
|
||||||
Planar64Vec3::int(-1, 1,-1),//4 left top front
|
[ 0.,-1., 0.],//CubeFace::Bottom
|
||||||
Planar64Vec3::int( 1, 1,-1),//5 right top front
|
[ 0., 0.,-1.],//CubeFace::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)
|
||||||
@@ -95,12 +89,12 @@ pub enum WedgeFace{
|
|||||||
Left,
|
Left,
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
const WEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
|
const WEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
|
||||||
Planar64Vec3::int( 1, 0, 0),//Wedge::Right
|
[ 1., 0., 0.],//Wedge::Right
|
||||||
Planar64Vec3::int( 0, 1,-1),//Wedge::TopFront
|
[ 0., 1.,-1.],//Wedge::TopFront
|
||||||
Planar64Vec3::int( 0, 0, 1),//Wedge::Back
|
[ 0., 0., 1.],//Wedge::Back
|
||||||
Planar64Vec3::int(-1, 0, 0),//Wedge::Left
|
[-1., 0., 0.],//Wedge::Left
|
||||||
Planar64Vec3::int( 0,-1, 0),//Wedge::Bottom
|
[ 0.,-1., 0.],//Wedge::Bottom
|
||||||
];
|
];
|
||||||
/*
|
/*
|
||||||
local cornerWedgeVerticies = {
|
local cornerWedgeVerticies = {
|
||||||
@@ -113,24 +107,25 @@ 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:[Planar64Vec3;5]=[
|
const CORNERWEDGE_DEFAULT_NORMALS:[[f32;3];5]=[
|
||||||
Planar64Vec3::int( 1, 0, 0),//CornerWedge::Right
|
[ 1., 0., 0.],//Wedge::Right
|
||||||
Planar64Vec3::int( 0, 1, 1),//CornerWedge::BackTop
|
[ 0., 1., 1.],//Wedge::BackTop
|
||||||
Planar64Vec3::int(-1, 1, 0),//CornerWedge::LeftTop
|
[-1., 1., 0.],//Wedge::LeftTop
|
||||||
Planar64Vec3::int( 0,-1, 0),//CornerWedge::Bottom
|
[ 0.,-1., 0.],//Wedge::Bottom
|
||||||
Planar64Vec3::int( 0, 0,-1),//CornerWedge::Front
|
[ 0., 0.,-1.],//Wedge::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(),Color4::ONE).remove(0);
|
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);
|
||||||
for pos in indexed_model.unique_pos.iter_mut(){
|
for pos in indexed_model.unique_pos.iter_mut(){
|
||||||
*pos=*pos/2;
|
pos[0]=pos[0]*0.5;
|
||||||
|
pos[1]=pos[1]*0.5;
|
||||||
|
pos[2]=pos[2]*0.5;
|
||||||
}
|
}
|
||||||
indexed_model
|
indexed_model
|
||||||
}
|
}
|
||||||
@@ -145,11 +140,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:crate::integer::Planar64Mat3=crate::integer::Planar64Mat3::int_from_cols_array([0,1,0, -1,0,0, 0,0,1]);
|
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));
|
||||||
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(),Color4::ONE).remove(0);
|
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);
|
||||||
for pos in indexed_model.unique_pos.iter_mut(){
|
for pos in indexed_model.unique_pos.iter_mut(){
|
||||||
*pos=TEAPOT_TRANSFORM*(*pos)/10;
|
[pos[0],pos[1],pos[2]]=*(TEAPOT_TRANSFORM*glam::Vec3::from_array(*pos)).as_ref();
|
||||||
}
|
}
|
||||||
indexed_model
|
indexed_model
|
||||||
}
|
}
|
||||||
@@ -167,44 +162,43 @@ 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::TopBack,FaceDescription::default());
|
t.insert(CornerWedgeFace::Top,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Copy,Clone)]
|
||||||
pub struct FaceDescription{
|
pub struct FaceDescription{
|
||||||
pub texture:Option<u32>,
|
pub texture:Option<u32>,
|
||||||
pub transform:glam::Affine2,
|
pub transform:glam::Affine2,
|
||||||
pub color:Color4,
|
pub color:glam::Vec4,
|
||||||
}
|
}
|
||||||
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:Color4::new(1.0,1.0,1.0,0.0),//zero alpha to hide the default texture
|
color:glam::vec4(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:Color4)->Self{
|
pub fn new(texture:u32,transform:glam::Affine2,color:glam::Vec4)->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:Color4::ONE,
|
color:glam::Vec4::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::new();
|
let mut generated_pos=Vec::<[f32;3]>::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();
|
||||||
@@ -212,7 +206,7 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate:
|
|||||||
let mut groups=Vec::new();
|
let mut groups=Vec::new();
|
||||||
let mut transforms=Vec::new();
|
let mut transforms=Vec::new();
|
||||||
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
||||||
for (face,face_description) in face_descriptions.into_iter(){
|
for (face,face_description) in face_descriptions.iter(){
|
||||||
//assume that scanning short lists is faster than hashing.
|
//assume that scanning short lists is faster than hashing.
|
||||||
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
||||||
transform_index
|
transform_index
|
||||||
@@ -221,16 +215,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(tex));
|
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
|
||||||
}
|
}
|
||||||
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){
|
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
|
||||||
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);
|
generated_color.push(*face_description.color.as_ref());
|
||||||
color_index
|
color_index
|
||||||
} as u32;
|
} as u32;
|
||||||
let face_id=match face{
|
let face_id=match face{
|
||||||
@@ -319,7 +313,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crat
|
|||||||
[6,2,4],
|
[6,2,4],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
let mut generated_pos=Vec::new();
|
let mut generated_pos=Vec::<[f32;3]>::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();
|
||||||
@@ -327,7 +321,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crat
|
|||||||
let mut groups=Vec::new();
|
let mut groups=Vec::new();
|
||||||
let mut transforms=Vec::new();
|
let mut transforms=Vec::new();
|
||||||
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
||||||
for (face,face_description) in face_descriptions.into_iter(){
|
for (face,face_description) in face_descriptions.iter(){
|
||||||
//assume that scanning short lists is faster than hashing.
|
//assume that scanning short lists is faster than hashing.
|
||||||
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
||||||
transform_index
|
transform_index
|
||||||
@@ -336,16 +330,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(tex));
|
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
|
||||||
}
|
}
|
||||||
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){
|
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
|
||||||
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);
|
generated_color.push(*face_description.color.as_ref());
|
||||||
color_index
|
color_index
|
||||||
} as u32;
|
} as u32;
|
||||||
let face_id=match face{
|
let face_id=match face{
|
||||||
@@ -431,7 +425,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
|
|||||||
[7,2,4],
|
[7,2,4],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
let mut generated_pos=Vec::new();
|
let mut generated_pos=Vec::<[f32;3]>::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();
|
||||||
@@ -439,7 +433,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
|
|||||||
let mut groups=Vec::new();
|
let mut groups=Vec::new();
|
||||||
let mut transforms=Vec::new();
|
let mut transforms=Vec::new();
|
||||||
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
|
||||||
for (face,face_description) in face_descriptions.into_iter(){
|
for (face,face_description) in face_descriptions.iter(){
|
||||||
//assume that scanning short lists is faster than hashing.
|
//assume that scanning short lists is faster than hashing.
|
||||||
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){
|
||||||
transform_index
|
transform_index
|
||||||
@@ -448,24 +442,23 @@ 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(tex));
|
generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref());
|
||||||
}
|
}
|
||||||
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){
|
let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){
|
||||||
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);
|
generated_color.push(*face_description.color.as_ref());
|
||||||
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::TopBack => 1,
|
CornerWedgeFace::Top => 1,
|
||||||
CornerWedgeFace::TopLeft => 2,
|
CornerWedgeFace::Bottom => 2,
|
||||||
CornerWedgeFace::Bottom => 3,
|
CornerWedgeFace::Front => 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
134
src/settings.rs
@@ -1,134 +0,0 @@
|
|||||||
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>,
|
||||||
// from camera to world
|
// camera position
|
||||||
view_inv: mat4x4<f32>,
|
cam_pos: vec4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
//group 0 is the camera
|
//group 0 is the camera
|
||||||
@@ -31,7 +31,8 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
|
|||||||
1.0
|
1.0
|
||||||
);
|
);
|
||||||
|
|
||||||
let inv_model_view = mat3x3<f32>(camera.view_inv[0].xyz, camera.view_inv[1].xyz, camera.view_inv[2].xyz);
|
// transposition = inversion for this orthonormal matrix
|
||||||
|
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;
|
||||||
@@ -42,7 +43,7 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
|
|||||||
|
|
||||||
struct ModelInstance{
|
struct ModelInstance{
|
||||||
transform:mat4x4<f32>,
|
transform:mat4x4<f32>,
|
||||||
normal_transform:mat3x3<f32>,
|
normal_transform:mat4x4<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
|
||||||
@@ -77,11 +78,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 * normal;
|
result.normal = (model_instances[instance].normal_transform * vec4<f32>(normal, 1.0)).xyz;
|
||||||
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.view_inv[3].xyz;//col(3)
|
result.view = position.xyz - camera.cam_pos.xyz;
|
||||||
result.position = camera.proj * camera.view * position;
|
result.position = camera.proj * camera.view * position;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -108,5 +109,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),0.5+0.5*abs(d));
|
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));
|
||||||
}
|
}
|
||||||
|
|||||||
134
src/sniffer.rs
134
src/sniffer.rs
@@ -1,134 +0,0 @@
|
|||||||
//file format "sniff"
|
|
||||||
|
|
||||||
/* spec
|
|
||||||
|
|
||||||
//begin global header
|
|
||||||
|
|
||||||
//global metadata (32 bytes)
|
|
||||||
b"SNFB"
|
|
||||||
u32 format_version
|
|
||||||
u64 priming_bytes
|
|
||||||
//how many bytes of the file must be read to guarantee all of the expected
|
|
||||||
//format-specific metadata is available to facilitate streaming the remaining contents
|
|
||||||
//used by the database to guarantee that it serves at least the bare minimum
|
|
||||||
u128 resource_uuid
|
|
||||||
//identifies the file from anywhere for any other file
|
|
||||||
|
|
||||||
//global block layout (variable size)
|
|
||||||
u64 num_blocks
|
|
||||||
for block_id in 0..num_blocks{
|
|
||||||
u64 first_byte
|
|
||||||
}
|
|
||||||
|
|
||||||
//end global header
|
|
||||||
|
|
||||||
//begin blocks
|
|
||||||
|
|
||||||
//each block is compressed with zstd or gz or something
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* block types
|
|
||||||
BLOCK_MAP_HEADER:
|
|
||||||
StyleInfoOverrides style_info_overrides
|
|
||||||
//bvh goes here
|
|
||||||
u64 num_nodes
|
|
||||||
//node 0 parent node is implied to be None
|
|
||||||
for node_id in 1..num_nodes{
|
|
||||||
u64 parent_node
|
|
||||||
}
|
|
||||||
//block 0 is the current block, not part of the map data
|
|
||||||
u64 num_spacial_blocks
|
|
||||||
for block_id in 1..num_spacial_blocks{
|
|
||||||
u64 node_id
|
|
||||||
u64 block_id
|
|
||||||
Aabb block_extents
|
|
||||||
}
|
|
||||||
//ideally spacial blocks are sorted from distance to start zone
|
|
||||||
//texture blocks are inserted before the first spacial block they are used in
|
|
||||||
|
|
||||||
BLOCK_MAP_RESOURCE:
|
|
||||||
//an individual one of the following:
|
|
||||||
- model (IndexedModel)
|
|
||||||
- shader (compiled SPIR-V)
|
|
||||||
- image (JpegXL)
|
|
||||||
- sound (Opus)
|
|
||||||
- video (AV1)
|
|
||||||
- animation (Trey thing)
|
|
||||||
|
|
||||||
BLOCK_MAP_OBJECT:
|
|
||||||
//an individual one of the following:
|
|
||||||
- model instance
|
|
||||||
- located resource
|
|
||||||
//for a list of resources, parse the object.
|
|
||||||
|
|
||||||
BLOCK_BOT_HEADER:
|
|
||||||
u128 map_resource_uuid //which map is this bot running
|
|
||||||
u128 time_resource_uuid //resource database time
|
|
||||||
//don't include style info in bot header because it's in the physics state
|
|
||||||
//blocks are laid out in chronological order, but indices may jump around.
|
|
||||||
u64 num_segments
|
|
||||||
for _ in 0..num_segments{
|
|
||||||
i64 time //physics_state timestamp
|
|
||||||
u64 block_id
|
|
||||||
}
|
|
||||||
|
|
||||||
BLOCK_BOT_SEGMENT:
|
|
||||||
//format version indicates what version of these structures to use
|
|
||||||
PhysicsState physics_state
|
|
||||||
//to read, greedily decode instructions until eof
|
|
||||||
loop{
|
|
||||||
//delta encode as much as possible (time,mousepos)
|
|
||||||
//strafe ticks are implied
|
|
||||||
//physics can be implied in an input-only bot file
|
|
||||||
TimedInstruction<PhysicsInstruction> instruction
|
|
||||||
}
|
|
||||||
|
|
||||||
BLOCK_DEMO_HEADER:
|
|
||||||
//timeline of loading maps, player equipment, bots
|
|
||||||
*/
|
|
||||||
struct InputInstructionCodecState{
|
|
||||||
mouse_pos:glam::IVec2,
|
|
||||||
time:crate::integer::Time,
|
|
||||||
}
|
|
||||||
//8B - 12B
|
|
||||||
impl InputInstructionCodecState{
|
|
||||||
pub fn encode(&mut self,ins:&crate::instruction::TimedInstruction<crate::physics::InputInstruction>)->([u8;12],usize){
|
|
||||||
let dt=ins.time-self.time;
|
|
||||||
self.time=ins.time;
|
|
||||||
let mut data=[0u8;12];
|
|
||||||
[data[0],data[1],data[2],data[3]]=(dt.nanos() as u32).to_le_bytes();//4B
|
|
||||||
//instruction id packed with game control parity bit. This could be 1 byte but it ruins the alignment
|
|
||||||
[data[4],data[5],data[6],data[7]]=ins.instruction.id().to_le_bytes();//4B
|
|
||||||
match &ins.instruction{
|
|
||||||
&crate::physics::InputInstruction::MoveMouse(m)=>{//4B
|
|
||||||
let dm=m-self.mouse_pos;
|
|
||||||
[data[8],data[9]]=(dm.x as i16).to_le_bytes();
|
|
||||||
[data[10],data[11]]=(dm.y as i16).to_le_bytes();
|
|
||||||
self.mouse_pos=m;
|
|
||||||
(data,12)
|
|
||||||
},
|
|
||||||
//0B
|
|
||||||
crate::physics::InputInstruction::MoveRight(_)
|
|
||||||
|crate::physics::InputInstruction::MoveUp(_)
|
|
||||||
|crate::physics::InputInstruction::MoveBack(_)
|
|
||||||
|crate::physics::InputInstruction::MoveLeft(_)
|
|
||||||
|crate::physics::InputInstruction::MoveDown(_)
|
|
||||||
|crate::physics::InputInstruction::MoveForward(_)
|
|
||||||
|crate::physics::InputInstruction::Jump(_)
|
|
||||||
|crate::physics::InputInstruction::Zoom(_)
|
|
||||||
|crate::physics::InputInstruction::Reset
|
|
||||||
|crate::physics::InputInstruction::Idle=>(data,8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//everything must be 4 byte aligned, it's all going to be compressed so don't think too had about saving less than 4 bytes
|
|
||||||
//TODO: Omit (mouse only?) instructions that don't surround an actual physics instruction
|
|
||||||
fn write_input_instruction<W:std::io::Write>(state:&mut InputInstructionCodecState,w:&mut W,ins:&crate::instruction::TimedInstruction<crate::physics::InputInstruction>)->Result<usize,std::io::Error>{
|
|
||||||
//TODO: insert idle instruction if gap is over u32 nanoseconds
|
|
||||||
//TODO: don't write idle instructions
|
|
||||||
//OR: end the data block! the full state at the start of the next block will contain an absolute timestamp
|
|
||||||
let (data,size)=state.encode(ins);
|
|
||||||
w.write(&data[0..size])//8B-12B
|
|
||||||
}
|
|
||||||
107
src/worker.rs
107
src/worker.rs
@@ -1,107 +0,0 @@
|
|||||||
use std::thread;
|
|
||||||
use std::sync::{mpsc,Arc};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
//The goal here is to have a worker thread that parks itself when it runs out of work.
|
|
||||||
//The worker thread publishes the result of its work back to the worker object for every item in the work queue.
|
|
||||||
//The physics (target use case) knows when it has not changed the body, so not updating the value is also an option.
|
|
||||||
|
|
||||||
pub struct Worker<Task:Send,Value:Clone> {
|
|
||||||
sender: mpsc::Sender<Task>,
|
|
||||||
value:Arc<Mutex<Value>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Task:Send+'static,Value:Clone+Send+'static> Worker<Task,Value> {
|
|
||||||
pub fn new<F:FnMut(Task)->Value+Send+'static>(value:Value,mut f:F) -> Self {
|
|
||||||
let (sender, receiver) = mpsc::channel::<Task>();
|
|
||||||
let ret=Self {
|
|
||||||
sender,
|
|
||||||
value:Arc::new(Mutex::new(value)),
|
|
||||||
};
|
|
||||||
let value=ret.value.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
loop {
|
|
||||||
match receiver.recv() {
|
|
||||||
Ok(task) => {
|
|
||||||
let v=f(task);//make sure function is evaluated before lock is acquired
|
|
||||||
*value.lock()=v;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
println!("Worker stopping.",);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send(&self,task:Task)->Result<(), mpsc::SendError<Task>>{
|
|
||||||
self.sender.send(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn grab_clone(&self)->Value{
|
|
||||||
self.value.lock().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CompatWorker<Task,Value:Clone,F>{
|
|
||||||
data:std::marker::PhantomData<Task>,
|
|
||||||
f:F,
|
|
||||||
value:Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Task,Value:Clone,F:FnMut(Task)->Value> CompatWorker<Task,Value,F> {
|
|
||||||
pub fn new(value:Value,f:F) -> Self {
|
|
||||||
Self {
|
|
||||||
f,
|
|
||||||
value,
|
|
||||||
data:std::marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send(&mut self,task:Task)->Result<(),()>{
|
|
||||||
self.value=(self.f)(task);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn grab_clone(&self)->Value{
|
|
||||||
self.value.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]//How to run this test with printing: cargo test --release -- --nocapture
|
|
||||||
fn test_worker() {
|
|
||||||
println!("hiiiii");
|
|
||||||
// Create the worker thread
|
|
||||||
let worker = Worker::new(crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO),
|
|
||||||
|_|crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Send tasks to the worker
|
|
||||||
for _ in 0..5 {
|
|
||||||
let task = crate::instruction::TimedInstruction{
|
|
||||||
time:crate::integer::Time::ZERO,
|
|
||||||
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
|
||||||
};
|
|
||||||
worker.send(task).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Signal the worker to stop (in a real-world scenario)
|
|
||||||
// sender.send("STOP".to_string()).unwrap();
|
|
||||||
|
|
||||||
// Sleep to allow the worker thread to finish processing
|
|
||||||
thread::sleep(std::time::Duration::from_secs(2));
|
|
||||||
|
|
||||||
// Send a new task
|
|
||||||
let task = crate::instruction::TimedInstruction{
|
|
||||||
time:crate::integer::Time::ZERO,
|
|
||||||
instruction:crate::physics::PhysicsInstruction::StrafeTick,
|
|
||||||
};
|
|
||||||
worker.send(task).unwrap();
|
|
||||||
|
|
||||||
println!("value={}",worker.grab_clone());
|
|
||||||
|
|
||||||
// wait long enough to see print from final task
|
|
||||||
thread::sleep(std::time::Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
@@ -1,30 +1,26 @@
|
|||||||
//find roots of polynomials
|
//find roots of polynomials
|
||||||
use crate::integer::Planar64;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
|
pub fn zeroes2(a0:f32,a1:f32,a2:f32) -> Vec<f32>{
|
||||||
if a2==Planar64::ZERO{
|
if a2==0f32{
|
||||||
return zeroes1(a0, a1);
|
return zeroes1(a0, a1);
|
||||||
}
|
}
|
||||||
let radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
|
let mut radicand=a1*a1-4f32*a2*a0;
|
||||||
if 0<radicand {
|
if 0f32<radicand {
|
||||||
//start with f64 sqrt
|
radicand=radicand.sqrt();
|
||||||
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});
|
if 0f32<a2 {
|
||||||
//TODO: one or two newtons
|
return vec![(-a1-radicand)/(2f32*a2),(-a1+radicand)/(2f32*a2)];
|
||||||
if Planar64::ZERO<a2 {
|
|
||||||
return vec![(-a1-planar_radicand)/(a2*2),(-a1+planar_radicand)/(a2*2)];
|
|
||||||
} else {
|
} else {
|
||||||
return vec![(-a1+planar_radicand)/(a2*2),(-a1-planar_radicand)/(a2*2)];
|
return vec![(-a1+radicand)/(2f32*a2),(-a1-radicand)/(2f32*a2)];
|
||||||
}
|
}
|
||||||
} else if radicand==0 {
|
} else if radicand==0f32 {
|
||||||
return vec![a1/(a2*-2)];
|
return vec![-a1/(2f32*a2)];
|
||||||
} else {
|
} else {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zeroes1(a0:Planar64,a1:Planar64) -> Vec<Planar64> {
|
pub fn zeroes1(a0:f32,a1:f32) -> Vec<f32> {
|
||||||
if a1==Planar64::ZERO{
|
if a1==0f32{
|
||||||
return vec![];
|
return vec![];
|
||||||
} else {
|
} else {
|
||||||
return vec![-a0/a1];
|
return vec![-a0/a1];
|
||||||
|
|||||||
Reference in New Issue
Block a user