2023-09-28 22:43:10 +00:00
use crate ::model ::{ IndexedModel , IndexedPolygon , IndexedGroup , IndexedVertex } ;
2023-09-30 23:18:23 +00:00
#[ derive(Debug) ]
pub enum Primitives {
Sphere ,
Cube ,
Cylinder ,
Wedge ,
CornerWedge ,
}
#[ derive(Hash,PartialEq,Eq) ]
pub enum CubeFace {
Right ,
Top ,
Back ,
Left ,
Bottom ,
Front ,
}
2023-09-28 00:24:25 +00:00
const CUBE_DEFAULT_TEXTURE_COORDS :[ [ f32 ; 2 ] ; 4 ] = [ [ 0.0 , 0.0 ] , [ 1.0 , 0.0 ] , [ 1.0 , 1.0 ] , [ 0.0 , 1.0 ] ] ;
const CUBE_DEFAULT_VERTICES :[ [ f32 ; 3 ] ; 8 ] = [
[ - 1. , - 1. , 1. ] , //0 left bottom back
[ 1. , - 1. , 1. ] , //1 right bottom back
[ 1. , 1. , 1. ] , //2 right top back
[ - 1. , 1. , 1. ] , //3 left top back
[ - 1. , 1. , - 1. ] , //4 left top front
[ 1. , 1. , - 1. ] , //5 right top front
[ 1. , - 1. , - 1. ] , //6 right bottom front
[ - 1. , - 1. , - 1. ] , //7 left bottom front
] ;
const CUBE_DEFAULT_NORMALS :[ [ f32 ; 3 ] ; 6 ] = [
2023-09-30 23:18:23 +00:00
[ 1. , 0. , 0. ] , //CubeFace::Right
[ 0. , 1. , 0. ] , //CubeFace::Top
[ 0. , 0. , 1. ] , //CubeFace::Back
[ - 1. , 0. , 0. ] , //CubeFace::Left
[ 0. , - 1. , 0. ] , //CubeFace::Bottom
[ 0. , 0. , - 1. ] , //CubeFace::Front
2023-09-28 00:24:25 +00:00
] ;
const CUBE_DEFAULT_POLYS :[ [ [ u32 ; 3 ] ; 4 ] ; 6 ] = [
// right (1, 0, 0)
[
2023-09-28 22:43:10 +00:00
[ 6 , 2 , 0 ] , //[vertex,tex,norm]
2023-09-28 00:24:25 +00:00
[ 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 ] ,
] ,
] ;
2023-09-30 23:18:23 +00:00
#[ derive(Hash,PartialEq,Eq) ]
pub enum WedgeFace {
Right ,
TopFront ,
Back ,
Left ,
Bottom ,
}
const WEDGE_DEFAULT_NORMALS :[ [ f32 ; 3 ] ; 5 ] = [
[ 1. , 0. , 0. ] , //Wedge::Right
[ 0. , 1. , - 1. ] , //Wedge::TopFront
[ 0. , 0. , 1. ] , //Wedge::Back
[ - 1. , 0. , 0. ] , //Wedge::Left
[ 0. , - 1. , 0. ] , //Wedge::Bottom
] ;
/*
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 {
Top ,
Right ,
Bottom ,
Front ,
}
const CORNERWEDGE_DEFAULT_NORMALS :[ [ f32 ; 3 ] ; 5 ] = [
[ 1. , 0. , 0. ] , //Wedge::Right
[ 0. , 1. , 1. ] , //Wedge::BackTop
[ - 1. , 1. , 0. ] , //Wedge::LeftTop
[ 0. , - 1. , 0. ] , //Wedge::Bottom
[ 0. , 0. , - 1. ] , //Wedge::Front
] ;
//HashMap fits this use case perfectly but feels like using a sledgehammer to drive a nail
pub fn unit_sphere ( ) ->crate ::model ::IndexedModel {
let mut indexed_model = crate ::model ::generate_indexed_model_list_from_obj ( obj ::ObjData ::load_buf ( & include_bytes! ( " ../models/suzanne.obj " ) [ .. ] ) . unwrap ( ) , * glam ::Vec4 ::ONE . as_ref ( ) ) . remove ( 0 ) ;
for pos in indexed_model . unique_pos . iter_mut ( ) {
pos [ 0 ] = pos [ 0 ] * 0.5 ;
pos [ 1 ] = pos [ 1 ] * 0.5 ;
pos [ 2 ] = pos [ 2 ] * 0.5 ;
}
indexed_model
}
pub type CubeFaceDescription = std ::collections ::HashMap ::< CubeFace , FaceDescription > ;
pub fn unit_cube ( ) ->crate ::model ::IndexedModel {
let mut t = CubeFaceDescription ::new ( ) ;
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 )
}
2023-10-01 00:59:40 +00:00
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 ) ) ;
2023-09-30 23:18:23 +00:00
pub fn unit_cylinder ( ) ->crate ::model ::IndexedModel {
let mut indexed_model = crate ::model ::generate_indexed_model_list_from_obj ( obj ::ObjData ::load_buf ( & include_bytes! ( " ../models/teapot.obj " ) [ .. ] ) . unwrap ( ) , * glam ::Vec4 ::ONE . as_ref ( ) ) . remove ( 0 ) ;
for pos in indexed_model . unique_pos . iter_mut ( ) {
2023-10-01 00:59:40 +00:00
[ pos [ 0 ] , pos [ 1 ] , pos [ 2 ] ] = * ( TEAPOT_TRANSFORM * glam ::Vec3 ::from_array ( * pos ) ) . as_ref ( ) ;
2023-09-30 23:18:23 +00:00
}
indexed_model
}
pub type WedgeFaceDescription = std ::collections ::HashMap ::< WedgeFace , FaceDescription > ;
pub fn unit_wedge ( ) ->crate ::model ::IndexedModel {
let mut t = WedgeFaceDescription ::new ( ) ;
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 )
}
pub type CornerWedgeFaceDescription = std ::collections ::HashMap ::< CornerWedgeFace , FaceDescription > ;
pub fn unit_cornerwedge ( ) ->crate ::model ::IndexedModel {
let mut t = CornerWedgeFaceDescription ::new ( ) ;
t . insert ( CornerWedgeFace ::Right , FaceDescription ::default ( ) ) ;
t . insert ( CornerWedgeFace ::Top , FaceDescription ::default ( ) ) ;
t . insert ( CornerWedgeFace ::Bottom , FaceDescription ::default ( ) ) ;
t . insert ( CornerWedgeFace ::Front , FaceDescription ::default ( ) ) ;
generate_partial_unit_cornerwedge ( t )
2023-09-27 03:25:56 +00:00
}
2023-09-28 00:24:25 +00:00
2023-09-28 22:43:10 +00:00
#[ derive(Copy,Clone) ]
2023-09-28 00:24:25 +00:00
pub struct FaceDescription {
2023-09-28 22:43:10 +00:00
pub texture :Option < u32 > ,
pub transform :glam ::Affine2 ,
pub color :glam ::Vec4 ,
2023-09-28 00:24:25 +00:00
}
impl std ::default ::Default for FaceDescription {
2023-09-30 23:18:23 +00:00
fn default ( ) ->Self {
2023-09-28 00:24:25 +00:00
Self {
2023-09-28 22:43:10 +00:00
texture :None ,
2023-09-28 00:24:25 +00:00
transform :glam ::Affine2 ::IDENTITY ,
2023-09-29 08:58:44 +00:00
color :glam ::vec4 ( 1.0 , 1.0 , 1.0 , 0.0 ) , //zero alpha to hide the default texture
2023-09-28 00:24:25 +00:00
}
}
}
impl FaceDescription {
2023-09-28 22:43:10 +00:00
pub fn new ( texture :u32 , transform :glam ::Affine2 , color :glam ::Vec4 ) ->Self {
Self { texture :Some ( texture ) , transform , color }
}
pub fn from_texture ( texture :u32 ) ->Self {
Self {
texture :Some ( texture ) ,
transform :glam ::Affine2 ::IDENTITY ,
color :glam ::Vec4 ::ONE ,
}
2023-09-28 00:24:25 +00:00
}
}
2023-09-28 22:43:10 +00:00
//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.
2023-09-30 23:18:23 +00:00
//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 {
2023-09-28 22:43:10 +00:00
let mut generated_pos = Vec ::< [ f32 ; 3 ] > ::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 ( ) ;
2023-09-27 21:10:45 +00:00
let mut transforms = Vec ::new ( ) ;
2023-09-28 22:43:10 +00:00
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
2023-10-02 08:57:15 +00:00
for ( face , face_description ) in face_descriptions . into_iter ( ) {
2023-09-30 23:18:23 +00:00
//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 ( glam ::Vec2 ::from_array ( tex ) ) . as_ref ( ) ) ;
}
transform_index
} as u32 ;
let color_index = if let Some ( color_index ) = generated_color . iter ( ) . position ( | color | color = = face_description . color . as_ref ( ) ) {
color_index
} else {
//create new color_index
let color_index = generated_color . len ( ) ;
generated_color . push ( * face_description . color . as_ref ( ) ) ;
color_index
} as u32 ;
let face_id = match face {
CubeFace ::Right = > 0 ,
CubeFace ::Top = > 1 ,
CubeFace ::Back = > 2 ,
CubeFace ::Left = > 3 ,
CubeFace ::Bottom = > 4 ,
CubeFace ::Front = > 5 ,
} ;
//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 ::< [ f32 ; 3 ] > ::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.
2023-10-02 08:57:15 +00:00
for ( face , face_description ) in face_descriptions . into_iter ( ) {
2023-09-30 23:18:23 +00:00
//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 ( glam ::Vec2 ::from_array ( tex ) ) . as_ref ( ) ) ;
}
transform_index
} as u32 ;
let color_index = if let Some ( color_index ) = generated_color . iter ( ) . position ( | color | color = = face_description . color . as_ref ( ) ) {
color_index
} else {
//create new color_index
let color_index = generated_color . len ( ) ;
generated_color . push ( * face_description . color . as_ref ( ) ) ;
color_index
} as u32 ;
let face_id = match face {
WedgeFace ::Right = > 0 ,
WedgeFace ::TopFront = > 1 ,
WedgeFace ::Back = > 2 ,
WedgeFace ::Left = > 3 ,
WedgeFace ::Bottom = > 4 ,
} ;
//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 ::< [ f32 ; 3 ] > ::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.
2023-10-02 08:57:15 +00:00
for ( face , face_description ) in face_descriptions . into_iter ( ) {
2023-09-30 23:18:23 +00:00
//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 ( glam ::Vec2 ::from_array ( tex ) ) . as_ref ( ) ) ;
}
transform_index
} as u32 ;
let color_index = if let Some ( color_index ) = generated_color . iter ( ) . position ( | color | color = = face_description . color . as_ref ( ) ) {
color_index
} else {
//create new color_index
let color_index = generated_color . len ( ) ;
generated_color . push ( * face_description . color . as_ref ( ) ) ;
color_index
} as u32 ;
let face_id = match face {
CornerWedgeFace ::Right = > 0 ,
CornerWedgeFace ::Top = > 1 ,
CornerWedgeFace ::Bottom = > 2 ,
CornerWedgeFace ::Front = > 3 ,
} ;
//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 ( ) ,
} ] ,
} ) ;
2023-09-23 02:41:27 +00:00
}
2023-09-28 22:43:10 +00:00
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 ( ) ,
2023-09-28 00:24:25 +00:00
}
}