494 lines
14 KiB
Rust
494 lines
14 KiB
Rust
|
use strafesnet_common::model::{Color4,TextureCoordinate,IndexedModel,IndexedPolygon,IndexedGroup,IndexedVertex};
|
||
|
use strafesnet_common::integer::Planar64Vec3;
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub enum Primitives{
|
||
|
Sphere,
|
||
|
Cube,
|
||
|
Cylinder,
|
||
|
Wedge,
|
||
|
CornerWedge,
|
||
|
}
|
||
|
#[derive(Hash,PartialEq,Eq)]
|
||
|
pub enum CubeFace{
|
||
|
Right,
|
||
|
Top,
|
||
|
Back,
|
||
|
Left,
|
||
|
Bottom,
|
||
|
Front,
|
||
|
}
|
||
|
const CUBE_DEFAULT_TEXTURE_COORDS:[TextureCoordinate;4]=[
|
||
|
TextureCoordinate::new(0.0,0.0),
|
||
|
TextureCoordinate::new(1.0,0.0),
|
||
|
TextureCoordinate::new(1.0,1.0),
|
||
|
TextureCoordinate::new(0.0,1.0),
|
||
|
];
|
||
|
const CUBE_DEFAULT_VERTICES:[Planar64Vec3;8]=[
|
||
|
Planar64Vec3::int(-1,-1, 1),//0 left bottom back
|
||
|
Planar64Vec3::int( 1,-1, 1),//1 right bottom back
|
||
|
Planar64Vec3::int( 1, 1, 1),//2 right top back
|
||
|
Planar64Vec3::int(-1, 1, 1),//3 left top back
|
||
|
Planar64Vec3::int(-1, 1,-1),//4 left top front
|
||
|
Planar64Vec3::int( 1, 1,-1),//5 right top front
|
||
|
Planar64Vec3::int( 1,-1,-1),//6 right bottom front
|
||
|
Planar64Vec3::int(-1,-1,-1),//7 left bottom front
|
||
|
];
|
||
|
const CUBE_DEFAULT_NORMALS:[Planar64Vec3;6]=[
|
||
|
Planar64Vec3::int( 1, 0, 0),//CubeFace::Right
|
||
|
Planar64Vec3::int( 0, 1, 0),//CubeFace::Top
|
||
|
Planar64Vec3::int( 0, 0, 1),//CubeFace::Back
|
||
|
Planar64Vec3::int(-1, 0, 0),//CubeFace::Left
|
||
|
Planar64Vec3::int( 0,-1, 0),//CubeFace::Bottom
|
||
|
Planar64Vec3::int( 0, 0,-1),//CubeFace::Front
|
||
|
];
|
||
|
const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[
|
||
|
// right (1, 0, 0)
|
||
|
[
|
||
|
[6,2,0],//[vertex,tex,norm]
|
||
|
[5,1,0],
|
||
|
[2,0,0],
|
||
|
[1,3,0],
|
||
|
],
|
||
|
// top (0, 1, 0)
|
||
|
[
|
||
|
[5,3,1],
|
||
|
[4,2,1],
|
||
|
[3,1,1],
|
||
|
[2,0,1],
|
||
|
],
|
||
|
// back (0, 0, 1)
|
||
|
[
|
||
|
[0,3,2],
|
||
|
[1,2,2],
|
||
|
[2,1,2],
|
||
|
[3,0,2],
|
||
|
],
|
||
|
// left (-1, 0, 0)
|
||
|
[
|
||
|
[0,2,3],
|
||
|
[3,1,3],
|
||
|
[4,0,3],
|
||
|
[7,3,3],
|
||
|
],
|
||
|
// bottom (0,-1, 0)
|
||
|
[
|
||
|
[1,1,4],
|
||
|
[0,0,4],
|
||
|
[7,3,4],
|
||
|
[6,2,4],
|
||
|
],
|
||
|
// front (0, 0,-1)
|
||
|
[
|
||
|
[4,1,5],
|
||
|
[5,0,5],
|
||
|
[6,3,5],
|
||
|
[7,2,5],
|
||
|
],
|
||
|
];
|
||
|
|
||
|
#[derive(Hash,PartialEq,Eq)]
|
||
|
pub enum WedgeFace{
|
||
|
Right,
|
||
|
TopFront,
|
||
|
Back,
|
||
|
Left,
|
||
|
Bottom,
|
||
|
}
|
||
|
const WEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
|
||
|
Planar64Vec3::int( 1, 0, 0),//Wedge::Right
|
||
|
Planar64Vec3::int( 0, 1,-1),//Wedge::TopFront
|
||
|
Planar64Vec3::int( 0, 0, 1),//Wedge::Back
|
||
|
Planar64Vec3::int(-1, 0, 0),//Wedge::Left
|
||
|
Planar64Vec3::int( 0,-1, 0),//Wedge::Bottom
|
||
|
];
|
||
|
/*
|
||
|
local cornerWedgeVerticies = {
|
||
|
Vector3.new(-1/2,-1/2,-1/2),7
|
||
|
Vector3.new(-1/2,-1/2, 1/2),0
|
||
|
Vector3.new( 1/2,-1/2,-1/2),6
|
||
|
Vector3.new( 1/2,-1/2, 1/2),1
|
||
|
Vector3.new( 1/2, 1/2,-1/2),5
|
||
|
}
|
||
|
*/
|
||
|
#[derive(Hash,PartialEq,Eq)]
|
||
|
pub enum CornerWedgeFace{
|
||
|
Right,
|
||
|
TopBack,
|
||
|
TopLeft,
|
||
|
Bottom,
|
||
|
Front,
|
||
|
}
|
||
|
const CORNERWEDGE_DEFAULT_NORMALS:[Planar64Vec3;5]=[
|
||
|
Planar64Vec3::int( 1, 0, 0),//CornerWedge::Right
|
||
|
Planar64Vec3::int( 0, 1, 1),//CornerWedge::BackTop
|
||
|
Planar64Vec3::int(-1, 1, 0),//CornerWedge::LeftTop
|
||
|
Planar64Vec3::int( 0,-1, 0),//CornerWedge::Bottom
|
||
|
Planar64Vec3::int( 0, 0,-1),//CornerWedge::Front
|
||
|
];
|
||
|
pub fn unit_sphere()->crate::model::IndexedModel{
|
||
|
unit_cube()
|
||
|
}
|
||
|
#[derive(Default)]
|
||
|
pub struct CubeFaceDescription([Option<FaceDescription>;6]);
|
||
|
impl CubeFaceDescription{
|
||
|
pub fn insert(&mut self,index:CubeFace,value:FaceDescription){
|
||
|
self.0[index as usize]=Some(value);
|
||
|
}
|
||
|
pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,6>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{
|
||
|
self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u)))
|
||
|
}
|
||
|
}
|
||
|
pub fn unit_cube()->crate::model::IndexedModel{
|
||
|
let mut t=CubeFaceDescription::default();
|
||
|
t.insert(CubeFace::Right,FaceDescription::default());
|
||
|
t.insert(CubeFace::Top,FaceDescription::default());
|
||
|
t.insert(CubeFace::Back,FaceDescription::default());
|
||
|
t.insert(CubeFace::Left,FaceDescription::default());
|
||
|
t.insert(CubeFace::Bottom,FaceDescription::default());
|
||
|
t.insert(CubeFace::Front,FaceDescription::default());
|
||
|
generate_partial_unit_cube(t)
|
||
|
}
|
||
|
pub fn unit_cylinder()->crate::model::IndexedModel{
|
||
|
unit_cube()
|
||
|
}
|
||
|
#[derive(Default)]
|
||
|
pub struct WedgeFaceDescription([Option<FaceDescription>;5]);
|
||
|
impl WedgeFaceDescription{
|
||
|
pub fn insert(&mut self,index:WedgeFace,value:FaceDescription){
|
||
|
self.0[index as usize]=Some(value);
|
||
|
}
|
||
|
pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,5>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{
|
||
|
self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u)))
|
||
|
}
|
||
|
}
|
||
|
pub fn unit_wedge()->crate::model::IndexedModel{
|
||
|
let mut t=WedgeFaceDescription::default();
|
||
|
t.insert(WedgeFace::Right,FaceDescription::default());
|
||
|
t.insert(WedgeFace::TopFront,FaceDescription::default());
|
||
|
t.insert(WedgeFace::Back,FaceDescription::default());
|
||
|
t.insert(WedgeFace::Left,FaceDescription::default());
|
||
|
t.insert(WedgeFace::Bottom,FaceDescription::default());
|
||
|
generate_partial_unit_wedge(t)
|
||
|
}
|
||
|
#[derive(Default)]
|
||
|
pub struct CornerWedgeFaceDescription([Option<FaceDescription>;5]);
|
||
|
impl CornerWedgeFaceDescription{
|
||
|
pub fn insert(&mut self,index:CornerWedgeFace,value:FaceDescription){
|
||
|
self.0[index as usize]=Some(value);
|
||
|
}
|
||
|
pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,5>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{
|
||
|
self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u)))
|
||
|
}
|
||
|
}
|
||
|
pub fn unit_cornerwedge()->crate::model::IndexedModel{
|
||
|
let mut t=CornerWedgeFaceDescription::default();
|
||
|
t.insert(CornerWedgeFace::Right,FaceDescription::default());
|
||
|
t.insert(CornerWedgeFace::TopBack,FaceDescription::default());
|
||
|
t.insert(CornerWedgeFace::TopLeft,FaceDescription::default());
|
||
|
t.insert(CornerWedgeFace::Bottom,FaceDescription::default());
|
||
|
t.insert(CornerWedgeFace::Front,FaceDescription::default());
|
||
|
generate_partial_unit_cornerwedge(t)
|
||
|
}
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub struct FaceDescription{
|
||
|
pub texture:Option<u32>,
|
||
|
pub transform:glam::Affine2,
|
||
|
pub color:Color4,
|
||
|
}
|
||
|
impl std::default::Default for FaceDescription{
|
||
|
fn default()->Self {
|
||
|
Self{
|
||
|
texture:None,
|
||
|
transform:glam::Affine2::IDENTITY,
|
||
|
color:Color4::new(1.0,1.0,1.0,0.0),//zero alpha to hide the default texture
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//TODO: it's probably better to use a shared vertex buffer between all primitives and use indexed rendering instead of generating a unique vertex buffer for each primitive.
|
||
|
//implementation: put all roblox primitives into one model.groups <- this won't work but I forget why
|
||
|
pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate::model::IndexedModel{
|
||
|
let mut generated_pos=Vec::new();
|
||
|
let mut generated_tex=Vec::new();
|
||
|
let mut generated_normal=Vec::new();
|
||
|
let mut generated_color=Vec::new();
|
||
|
let mut generated_vertices=Vec::new();
|
||
|
let mut groups=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.
|
||
|
for (face_id,face_description) in face_descriptions.pairs(){
|
||
|
//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){
|
||
|
transform_index
|
||
|
}else{
|
||
|
//create new transform_index
|
||
|
let transform_index=transforms.len();
|
||
|
transforms.push(face_description.transform);
|
||
|
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
||
|
generated_tex.push(face_description.transform.transform_point2(tex));
|
||
|
}
|
||
|
transform_index
|
||
|
} as u32;
|
||
|
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
|
||
|
color_index
|
||
|
}else{
|
||
|
//create new color_index
|
||
|
let color_index=generated_color.len();
|
||
|
generated_color.push(face_description.color);
|
||
|
color_index
|
||
|
} as u32;
|
||
|
//always push normal
|
||
|
let normal_index=generated_normal.len() as u32;
|
||
|
generated_normal.push(CUBE_DEFAULT_NORMALS[face_id]);
|
||
|
//push vertices as they are needed
|
||
|
groups.push(IndexedGroup{
|
||
|
texture:face_description.texture,
|
||
|
polys:vec![IndexedPolygon{
|
||
|
vertices:CUBE_DEFAULT_POLYS[face_id].map(|tup|{
|
||
|
let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize];
|
||
|
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
|
||
|
pos_index
|
||
|
}else{
|
||
|
//create new pos_index
|
||
|
let pos_index=generated_pos.len();
|
||
|
generated_pos.push(pos);
|
||
|
pos_index
|
||
|
} as u32;
|
||
|
//always push vertex
|
||
|
let vertex=IndexedVertex{
|
||
|
pos:pos_index,
|
||
|
tex:tup[1]+4*transform_index,
|
||
|
normal:normal_index,
|
||
|
color:color_index,
|
||
|
};
|
||
|
let vert_index=generated_vertices.len();
|
||
|
generated_vertices.push(vertex);
|
||
|
vert_index as u32
|
||
|
}).to_vec(),
|
||
|
}],
|
||
|
});
|
||
|
}
|
||
|
IndexedModel{
|
||
|
unique_pos:generated_pos,
|
||
|
unique_tex:generated_tex,
|
||
|
unique_normal:generated_normal,
|
||
|
unique_color:generated_color,
|
||
|
unique_vertices:generated_vertices,
|
||
|
groups,
|
||
|
instances:Vec::new(),
|
||
|
}
|
||
|
}
|
||
|
//don't think too hard about the copy paste because this is all going into the map tool eventually...
|
||
|
pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->crate::model::IndexedModel{
|
||
|
let wedge_default_polys=vec![
|
||
|
// right (1, 0, 0)
|
||
|
vec![
|
||
|
[6,2,0],//[vertex,tex,norm]
|
||
|
[2,0,0],
|
||
|
[1,3,0],
|
||
|
],
|
||
|
// FrontTop (0, 1, -1)
|
||
|
vec![
|
||
|
[3,1,1],
|
||
|
[2,0,1],
|
||
|
[6,3,1],
|
||
|
[7,2,1],
|
||
|
],
|
||
|
// back (0, 0, 1)
|
||
|
vec![
|
||
|
[0,3,2],
|
||
|
[1,2,2],
|
||
|
[2,1,2],
|
||
|
[3,0,2],
|
||
|
],
|
||
|
// left (-1, 0, 0)
|
||
|
vec![
|
||
|
[0,2,3],
|
||
|
[3,1,3],
|
||
|
[7,3,3],
|
||
|
],
|
||
|
// bottom (0,-1, 0)
|
||
|
vec![
|
||
|
[1,1,4],
|
||
|
[0,0,4],
|
||
|
[7,3,4],
|
||
|
[6,2,4],
|
||
|
],
|
||
|
];
|
||
|
let mut generated_pos=Vec::new();
|
||
|
let mut generated_tex=Vec::new();
|
||
|
let mut generated_normal=Vec::new();
|
||
|
let mut generated_color=Vec::new();
|
||
|
let mut generated_vertices=Vec::new();
|
||
|
let mut groups=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.
|
||
|
for (face_id,face_description) in face_descriptions.pairs(){
|
||
|
//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){
|
||
|
transform_index
|
||
|
}else{
|
||
|
//create new transform_index
|
||
|
let transform_index=transforms.len();
|
||
|
transforms.push(face_description.transform);
|
||
|
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
||
|
generated_tex.push(face_description.transform.transform_point2(tex));
|
||
|
}
|
||
|
transform_index
|
||
|
} as u32;
|
||
|
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
|
||
|
color_index
|
||
|
}else{
|
||
|
//create new color_index
|
||
|
let color_index=generated_color.len();
|
||
|
generated_color.push(face_description.color);
|
||
|
color_index
|
||
|
} as u32;
|
||
|
//always push normal
|
||
|
let normal_index=generated_normal.len() as u32;
|
||
|
generated_normal.push(WEDGE_DEFAULT_NORMALS[face_id]);
|
||
|
//push vertices as they are needed
|
||
|
groups.push(IndexedGroup{
|
||
|
texture:face_description.texture,
|
||
|
polys:vec![IndexedPolygon{
|
||
|
vertices:wedge_default_polys[face_id].iter().map(|tup|{
|
||
|
let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize];
|
||
|
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
|
||
|
pos_index
|
||
|
}else{
|
||
|
//create new pos_index
|
||
|
let pos_index=generated_pos.len();
|
||
|
generated_pos.push(pos);
|
||
|
pos_index
|
||
|
} as u32;
|
||
|
//always push vertex
|
||
|
let vertex=IndexedVertex{
|
||
|
pos:pos_index,
|
||
|
tex:tup[1]+4*transform_index,
|
||
|
normal:normal_index,
|
||
|
color:color_index,
|
||
|
};
|
||
|
let vert_index=generated_vertices.len();
|
||
|
generated_vertices.push(vertex);
|
||
|
vert_index as u32
|
||
|
}).collect(),
|
||
|
}],
|
||
|
});
|
||
|
}
|
||
|
IndexedModel{
|
||
|
unique_pos:generated_pos,
|
||
|
unique_tex:generated_tex,
|
||
|
unique_normal:generated_normal,
|
||
|
unique_color:generated_color,
|
||
|
unique_vertices:generated_vertices,
|
||
|
groups,
|
||
|
instances:Vec::new(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescription)->crate::model::IndexedModel{
|
||
|
let cornerwedge_default_polys=vec![
|
||
|
// right (1, 0, 0)
|
||
|
vec![
|
||
|
[6,2,0],//[vertex,tex,norm]
|
||
|
[5,1,0],
|
||
|
[1,3,0],
|
||
|
],
|
||
|
// BackTop (0, 1, 1)
|
||
|
vec![
|
||
|
[5,3,1],
|
||
|
[0,1,1],
|
||
|
[1,0,1],
|
||
|
],
|
||
|
// LeftTop (-1, 1, 0)
|
||
|
vec![
|
||
|
[5,3,2],
|
||
|
[7,2,2],
|
||
|
[0,1,2],
|
||
|
],
|
||
|
// bottom (0,-1, 0)
|
||
|
vec![
|
||
|
[1,1,3],
|
||
|
[0,0,3],
|
||
|
[7,3,3],
|
||
|
[6,2,3],
|
||
|
],
|
||
|
// front (0, 0,-1)
|
||
|
vec![
|
||
|
[5,0,4],
|
||
|
[6,3,4],
|
||
|
[7,2,4],
|
||
|
],
|
||
|
];
|
||
|
let mut generated_pos=Vec::new();
|
||
|
let mut generated_tex=Vec::new();
|
||
|
let mut generated_normal=Vec::new();
|
||
|
let mut generated_color=Vec::new();
|
||
|
let mut generated_vertices=Vec::new();
|
||
|
let mut groups=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.
|
||
|
for (face_id,face_description) in face_descriptions.pairs(){
|
||
|
//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){
|
||
|
transform_index
|
||
|
}else{
|
||
|
//create new transform_index
|
||
|
let transform_index=transforms.len();
|
||
|
transforms.push(face_description.transform);
|
||
|
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
|
||
|
generated_tex.push(face_description.transform.transform_point2(tex));
|
||
|
}
|
||
|
transform_index
|
||
|
} as u32;
|
||
|
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
|
||
|
color_index
|
||
|
}else{
|
||
|
//create new color_index
|
||
|
let color_index=generated_color.len();
|
||
|
generated_color.push(face_description.color);
|
||
|
color_index
|
||
|
} as u32;
|
||
|
//always push normal
|
||
|
let normal_index=generated_normal.len() as u32;
|
||
|
generated_normal.push(CORNERWEDGE_DEFAULT_NORMALS[face_id]);
|
||
|
//push vertices as they are needed
|
||
|
groups.push(IndexedGroup{
|
||
|
texture:face_description.texture,
|
||
|
polys:vec![IndexedPolygon{
|
||
|
vertices:cornerwedge_default_polys[face_id].iter().map(|tup|{
|
||
|
let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize];
|
||
|
let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){
|
||
|
pos_index
|
||
|
}else{
|
||
|
//create new pos_index
|
||
|
let pos_index=generated_pos.len();
|
||
|
generated_pos.push(pos);
|
||
|
pos_index
|
||
|
} as u32;
|
||
|
//always push vertex
|
||
|
let vertex=IndexedVertex{
|
||
|
pos:pos_index,
|
||
|
tex:tup[1]+4*transform_index,
|
||
|
normal:normal_index,
|
||
|
color:color_index,
|
||
|
};
|
||
|
let vert_index=generated_vertices.len();
|
||
|
generated_vertices.push(vertex);
|
||
|
vert_index as u32
|
||
|
}).collect(),
|
||
|
}],
|
||
|
});
|
||
|
}
|
||
|
IndexedModel{
|
||
|
unique_pos:generated_pos,
|
||
|
unique_tex:generated_tex,
|
||
|
unique_normal:generated_normal,
|
||
|
unique_color:generated_color,
|
||
|
unique_vertices:generated_vertices,
|
||
|
groups,
|
||
|
instances:Vec::new(),
|
||
|
}
|
||
|
}
|