Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e70e31e2e | |||
4fa0197120 | |||
fbe4183a3c | |||
8f7e44228b | |||
279efb5781 | |||
f0dc898b24 | |||
6ecafb3e78 | |||
27ac930e14 | |||
cb12fd9338 | |||
ff4ddfbe7f | |||
7de45620c7 | |||
c4dba062eb | |||
f0fcf47ca9 | |||
03d1bb3bc2 | |||
7032234698 | |||
e9f5b3b7ae | |||
6a085e42ba | |||
64fb36ec23 | |||
4f3254e67e | |||
2c312df51a | |||
11a864682a | |||
45c6de5d3c | |||
2ebaf72864 | |||
890974ed11 | |||
c7db340c82 | |||
911fdb9038 | |||
337cf031d3 |
294
lib/bsp_loader/src/brush.rs
Normal file
294
lib/bsp_loader/src/brush.rs
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
use strafesnet_common::integer::Planar64;
|
||||||
|
use strafesnet_common::{model,integer};
|
||||||
|
use strafesnet_common::integer::{vec3::Vector3,Fixed,Ratio};
|
||||||
|
|
||||||
|
use crate::{valve_transform_normal,valve_transform_dist};
|
||||||
|
|
||||||
|
#[derive(Hash,Eq,PartialEq)]
|
||||||
|
struct Face{
|
||||||
|
normal:integer::Planar64Vec3,
|
||||||
|
dot:integer::Planar64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Faces{
|
||||||
|
faces:Vec<Vec<integer::Planar64Vec3>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve3(c0:&Face,c1:&Face,c2:&Face)->Option<Ratio<Vector3<Fixed<3,96>>,Fixed<3,96>>>{
|
||||||
|
let n0_n1=c0.normal.cross(c1.normal);
|
||||||
|
let det=c2.normal.dot(n0_n1);
|
||||||
|
if det.abs().is_zero(){
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
c1.normal.cross(c2.normal)*c0.dot
|
||||||
|
+c2.normal.cross(c0.normal)*c1.dot
|
||||||
|
+c0.normal.cross(c1.normal)*c2.dot
|
||||||
|
)/det)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PlanesToFacesError{
|
||||||
|
InitFace1,
|
||||||
|
InitFace2,
|
||||||
|
InitIntersection,
|
||||||
|
FindNewIntersection,
|
||||||
|
EmptyFaces,
|
||||||
|
InfiniteLoop1,
|
||||||
|
InfiniteLoop2,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for PlanesToFacesError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl core::error::Error for PlanesToFacesError{}
|
||||||
|
|
||||||
|
fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,PlanesToFacesError>{
|
||||||
|
let mut faces=Vec::new();
|
||||||
|
// for each face, determine one edge at a time until you complete the face
|
||||||
|
'face: for face0 in &face_list{
|
||||||
|
// 1. find first edge
|
||||||
|
// 2. follow edges around face
|
||||||
|
|
||||||
|
// === finding first edge ===
|
||||||
|
// 1. pick the most perpendicular set of 3 faces
|
||||||
|
// 2. check if any faces occlude the intersection
|
||||||
|
// 3. use this test to replace left and right alternating until they are not occluded
|
||||||
|
|
||||||
|
// find the most perpendicular face to face0
|
||||||
|
let mut face1=face_list.iter().min_by_key(|&p|{
|
||||||
|
face0.normal.dot(p.normal).abs()
|
||||||
|
}).ok_or(PlanesToFacesError::InitFace1)?;
|
||||||
|
|
||||||
|
// direction of edge formed by face0 x face1
|
||||||
|
let edge_dir=face0.normal.cross(face1.normal);
|
||||||
|
|
||||||
|
// find the most perpendicular face to both face0 and face1
|
||||||
|
let mut face2=face_list.iter().max_by_key(|&p|{
|
||||||
|
// find the best *oriented* face (no .abs())
|
||||||
|
edge_dir.dot(p.normal)
|
||||||
|
}).ok_or(PlanesToFacesError::InitFace2)?;
|
||||||
|
|
||||||
|
let mut detect_loop=200u8;
|
||||||
|
|
||||||
|
let mut intersection=solve3(face0,face1,face2).ok_or(PlanesToFacesError::InitIntersection)?;
|
||||||
|
|
||||||
|
// repeatedly update face0, face1 until all faces form part of the convex solid
|
||||||
|
'find: loop{
|
||||||
|
if let Some(a)=detect_loop.checked_sub(1){
|
||||||
|
detect_loop=a;
|
||||||
|
}else{
|
||||||
|
return Err(PlanesToFacesError::InfiniteLoop1);
|
||||||
|
}
|
||||||
|
// test if any *other* faces occlude the intersection
|
||||||
|
for new_face in &face_list{
|
||||||
|
// new face occludes intersection point
|
||||||
|
if (new_face.dot.fix_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){
|
||||||
|
// replace one of the faces with the new face
|
||||||
|
// dont' try to replace face0 because we are exploring that face in particular
|
||||||
|
if let Some(new_intersection)=solve3(face0,new_face,face2){
|
||||||
|
// face1 does not occlude (or intersect) the new intersection
|
||||||
|
if (face1.dot.fix_2()/Planar64::ONE).gt_ratio(face1.normal.dot(new_intersection.num)/new_intersection.den){
|
||||||
|
face1=new_face;
|
||||||
|
intersection=new_intersection;
|
||||||
|
continue 'find;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(new_intersection)=solve3(face0,face1,new_face){
|
||||||
|
// face2 does not occlude (or intersect) the new intersection
|
||||||
|
if (face2.dot.fix_2()/Planar64::ONE).gt_ratio(face2.normal.dot(new_intersection.num)/new_intersection.den){
|
||||||
|
face2=new_face;
|
||||||
|
intersection=new_intersection;
|
||||||
|
continue 'find;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have found a set of faces for which the intersection is on the convex solid
|
||||||
|
break 'find;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if face0 must go, meaning it is a degenerate face and does not contribute anything to the convex solid
|
||||||
|
for new_face in &face_list{
|
||||||
|
if core::ptr::eq(face0,new_face){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if core::ptr::eq(face1,new_face){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if core::ptr::eq(face2,new_face){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(new_intersection)=solve3(new_face,face1,face2){
|
||||||
|
// face0 does not occlude (or intersect) the new intersection
|
||||||
|
if (face0.dot.fix_2()/Planar64::ONE).lt_ratio(face0.normal.dot(new_intersection.num)/new_intersection.den){
|
||||||
|
// abort! reject face0 entirely
|
||||||
|
continue 'face;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === follow edges around face ===
|
||||||
|
// Note that we chose face2 such that the 3 faces create a particular winding order.
|
||||||
|
// If we choose a consistent face to follow (face1, face2) it will always wind with a consistent chirality
|
||||||
|
|
||||||
|
let mut detect_loop=200u8;
|
||||||
|
|
||||||
|
// keep looping until we meet this face again
|
||||||
|
let face1=face1;
|
||||||
|
let mut face=Vec::new();
|
||||||
|
loop{
|
||||||
|
// push point onto vertices
|
||||||
|
// problem: this may push a vertex that does not fit in the fixed point range and is thus meaningless
|
||||||
|
face.push(intersection.divide().fix_1());
|
||||||
|
|
||||||
|
// we looped back around to face1, we're done!
|
||||||
|
if core::ptr::eq(face1,face2){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the measure
|
||||||
|
let edge_dir=face0.normal.cross(face2.normal);
|
||||||
|
|
||||||
|
// the dot product to beat
|
||||||
|
let d_intersection=edge_dir.dot(intersection.num)/intersection.den;
|
||||||
|
|
||||||
|
// find the next face moving clockwise around face0
|
||||||
|
let (new_face,new_intersection,_)=face_list.iter().filter_map(|new_face|{
|
||||||
|
// ignore faces that are part of the current edge
|
||||||
|
if core::ptr::eq(face0,new_face)
|
||||||
|
|core::ptr::eq(face2,new_face){
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let new_intersection=solve3(face0,face2,new_face)?;
|
||||||
|
|
||||||
|
// the d value must be larger
|
||||||
|
let d_new_intersection=edge_dir.dot(new_intersection.num)/new_intersection.den;
|
||||||
|
if d_new_intersection.le_ratio(d_intersection){
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((new_face,new_intersection,d_new_intersection))
|
||||||
|
}).min_by_key(|&(_,_,d)|d).ok_or(PlanesToFacesError::FindNewIntersection)?;
|
||||||
|
|
||||||
|
face2=new_face;
|
||||||
|
intersection=new_intersection;
|
||||||
|
|
||||||
|
if let Some(a)=detect_loop.checked_sub(1){
|
||||||
|
detect_loop=a;
|
||||||
|
}else{
|
||||||
|
return Err(PlanesToFacesError::InfiniteLoop2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
faces.push(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
if faces.is_empty(){
|
||||||
|
Err(PlanesToFacesError::EmptyFaces)
|
||||||
|
}else{
|
||||||
|
Ok(Faces{
|
||||||
|
faces,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BrushToMeshError{
|
||||||
|
SliceBrushSides,
|
||||||
|
MissingPlane,
|
||||||
|
InvalidFaceCount{
|
||||||
|
count:usize,
|
||||||
|
},
|
||||||
|
InvalidPlanes(PlanesToFacesError),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for BrushToMeshError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl core::error::Error for BrushToMeshError{}
|
||||||
|
|
||||||
|
pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,BrushToMeshError>{
|
||||||
|
let brush_start_idx=brush.brush_side as usize;
|
||||||
|
let sides_range=brush_start_idx..brush_start_idx+brush.num_brush_sides as usize;
|
||||||
|
let sides=bsp.brush_sides.get(sides_range).ok_or(BrushToMeshError::SliceBrushSides)?;
|
||||||
|
let face_list=sides.iter().map(|side|{
|
||||||
|
let plane=bsp.plane(side.plane as usize)?;
|
||||||
|
Some(Face{
|
||||||
|
normal:valve_transform_normal(plane.normal.into()),
|
||||||
|
dot:valve_transform_dist(plane.dist.into()),
|
||||||
|
})
|
||||||
|
}).collect::<Option<std::collections::HashSet<_>>>().ok_or(BrushToMeshError::MissingPlane)?;
|
||||||
|
|
||||||
|
if face_list.len()<4{
|
||||||
|
return Err(BrushToMeshError::InvalidFaceCount{count:face_list.len()});
|
||||||
|
}
|
||||||
|
|
||||||
|
let faces=planes_to_faces(face_list).map_err(BrushToMeshError::InvalidPlanes)?;
|
||||||
|
|
||||||
|
// generate the mesh
|
||||||
|
let mut mb=model::MeshBuilder::new();
|
||||||
|
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
||||||
|
let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
|
||||||
|
// normals are ignored by physics
|
||||||
|
let normal=mb.acquire_normal_id(integer::vec3::ZERO);
|
||||||
|
|
||||||
|
let polygon_list=faces.faces.into_iter().map(|face|{
|
||||||
|
face.into_iter().map(|pos|{
|
||||||
|
let pos=mb.acquire_pos_id(pos);
|
||||||
|
mb.acquire_vertex_id(model::IndexedVertex{
|
||||||
|
pos,
|
||||||
|
tex,
|
||||||
|
normal,
|
||||||
|
color,
|
||||||
|
})
|
||||||
|
}).collect()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let polygon_groups=vec![model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))];
|
||||||
|
let physics_groups=vec![model::IndexedPhysicsGroup{
|
||||||
|
groups:vec![model::PolygonGroupId::new(0)],
|
||||||
|
}];
|
||||||
|
let graphics_groups=vec![model::IndexedGraphicsGroup{
|
||||||
|
render:model::RenderConfigId::new(0),
|
||||||
|
groups:vec![model::PolygonGroupId::new(0)],
|
||||||
|
}];
|
||||||
|
|
||||||
|
Ok(mb.build(polygon_groups,graphics_groups,physics_groups))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test{
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_cube(){
|
||||||
|
let face_list=[
|
||||||
|
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||||
|
].into_iter().collect();
|
||||||
|
let faces=planes_to_faces(face_list).unwrap();
|
||||||
|
dbg!(faces);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_cube_with_degernate_face(){
|
||||||
|
let face_list=[
|
||||||
|
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||||
|
Face{normal:integer::vec3::NEG_Z,dot:Planar64::EPSILON},
|
||||||
|
].into_iter().collect();
|
||||||
|
let faces=planes_to_faces(face_list).unwrap();
|
||||||
|
dbg!(faces);
|
||||||
|
}
|
||||||
|
}
|
@ -41,38 +41,41 @@ pub fn convert<'a>(
|
|||||||
//figure out real attributes later
|
//figure out real attributes later
|
||||||
let mut unique_attributes=Vec::new();
|
let mut unique_attributes=Vec::new();
|
||||||
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
|
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
|
||||||
const TEMP_TOUCH_ME_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0);
|
unique_attributes.push(gameplay_attributes::CollisionAttributes::contact_default());
|
||||||
|
const ATTRIBUTE_DECORATION:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0);
|
||||||
|
const ATTRIBUTE_CONTACT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(1);
|
||||||
|
|
||||||
//declare all prop models to Loader
|
//declare all prop models to Loader
|
||||||
let prop_models=bsp.static_props().map(|prop|{
|
let prop_models=Vec::new();
|
||||||
//get or create mesh_id
|
// let prop_models=bsp.static_props().map(|prop|{
|
||||||
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
// //get or create mesh_id
|
||||||
let placement=prop.as_prop_placement();
|
// let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
||||||
model::Model{
|
// let placement=prop.as_prop_placement();
|
||||||
mesh:mesh_id,
|
// model::Model{
|
||||||
attributes:TEMP_TOUCH_ME_ATTRIBUTE,
|
// mesh:mesh_id,
|
||||||
transform:integer::Planar64Affine3::new(
|
// attributes:ATTRIBUTE_DECORATION,
|
||||||
integer::mat3::try_from_f32_array_2d((
|
// transform:integer::Planar64Affine3::new(
|
||||||
glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
|
// integer::mat3::try_from_f32_array_2d((
|
||||||
//TODO: figure this out
|
// glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
|
||||||
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
|
// //TODO: figure this out
|
||||||
).to_cols_array_2d()).unwrap(),
|
// *glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
|
||||||
valve_transform(placement.origin.into()),
|
// ).to_cols_array_2d()).unwrap(),
|
||||||
),
|
// valve_transform(placement.origin.into()),
|
||||||
color:glam::Vec4::ONE,
|
// ),
|
||||||
}
|
// color:glam::Vec4::ONE,
|
||||||
}).collect();
|
// }
|
||||||
|
// }).collect();
|
||||||
|
|
||||||
//TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups
|
//TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups
|
||||||
|
|
||||||
//the generated MeshIds in here will collide with the Loader Mesh Ids
|
//the generated MeshIds in here will collide with the Loader Mesh Ids
|
||||||
//but I can't think of a good workaround other than just remapping one later.
|
//but I can't think of a good workaround other than just remapping one later.
|
||||||
let world_meshes:Vec<model::Mesh>=bsp.models().map(|world_model|{
|
let mut world_meshes:Vec<model::Mesh>=bsp.models().map(|world_model|{
|
||||||
let mut mb=model::MeshBuilder::new();
|
let mut mb=model::MeshBuilder::new();
|
||||||
|
|
||||||
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
||||||
let mut graphics_groups=Vec::new();
|
let mut graphics_groups=Vec::new();
|
||||||
let mut render_id_to_graphics_group_id=std::collections::HashMap::new();
|
//let mut render_id_to_graphics_group_id=std::collections::HashMap::new();
|
||||||
let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{
|
let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{
|
||||||
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
|
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
|
||||||
let face_texture=face.texture();
|
let face_texture=face.texture();
|
||||||
@ -102,15 +105,15 @@ pub fn convert<'a>(
|
|||||||
//a render config for it, and then returns the id to that render config
|
//a render config for it, and then returns the id to that render config
|
||||||
let render_id=render_config_deferred_loader.acquire_render_config_id(Some(Cow::Borrowed(face_texture_data.name())));
|
let render_id=render_config_deferred_loader.acquire_render_config_id(Some(Cow::Borrowed(face_texture_data.name())));
|
||||||
//deduplicate graphics groups by render id
|
//deduplicate graphics groups by render id
|
||||||
let graphics_group_id=*render_id_to_graphics_group_id.entry(render_id).or_insert_with(||{
|
// let graphics_group_id=*render_id_to_graphics_group_id.entry(render_id).or_insert_with(||{
|
||||||
let graphics_group_id=graphics_groups.len();
|
// let graphics_group_id=graphics_groups.len();
|
||||||
graphics_groups.push(model::IndexedGraphicsGroup{
|
// graphics_groups.push(model::IndexedGraphicsGroup{
|
||||||
render:render_id,
|
// render:render_id,
|
||||||
groups:vec![],
|
// groups:vec![],
|
||||||
});
|
// });
|
||||||
graphics_group_id
|
// graphics_group_id
|
||||||
});
|
// });
|
||||||
graphics_groups[graphics_group_id].groups.push(polygon_group_id);
|
// graphics_groups[graphics_group_id].groups.push(polygon_group_id);
|
||||||
}
|
}
|
||||||
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
|
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
|
||||||
}).collect();
|
}).collect();
|
||||||
@ -118,6 +121,17 @@ pub fn convert<'a>(
|
|||||||
mb.build(polygon_groups,graphics_groups,vec![])
|
mb.build(polygon_groups,graphics_groups,vec![])
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
let brush_mesh_start_idx=world_meshes.len();
|
||||||
|
for brush in &bsp.brushes{
|
||||||
|
let mesh_result=crate::brush::brush_to_mesh(bsp,brush);
|
||||||
|
match mesh_result{
|
||||||
|
Ok(mesh)=>world_meshes.push(mesh),
|
||||||
|
Err(e)=>println!("Brush mesh error: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("How many brush: {}",bsp.brushes.len());
|
||||||
|
println!("Generated brush models: {}",world_meshes.len()-brush_mesh_start_idx);
|
||||||
|
|
||||||
let world_models:Vec<model::Model>=
|
let world_models:Vec<model::Model>=
|
||||||
//one instance of the main world mesh
|
//one instance of the main world mesh
|
||||||
std::iter::once((
|
std::iter::once((
|
||||||
@ -143,17 +157,26 @@ pub fn convert<'a>(
|
|||||||
(model::MeshId::new(mesh_id),brush.origin,brush.color)
|
(model::MeshId::new(mesh_id),brush.origin,brush.color)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).map(|(mesh_id,model_origin,vbsp::Color{r,g,b})|{
|
).map(|(mesh_id,model_origin,vbsp::Color{r,g,b})|model::Model{
|
||||||
model::Model{
|
mesh:mesh_id,
|
||||||
mesh:mesh_id,
|
attributes:ATTRIBUTE_DECORATION,
|
||||||
attributes:TEMP_TOUCH_ME_ATTRIBUTE,
|
transform:integer::Planar64Affine3::new(
|
||||||
|
integer::mat3::identity(),
|
||||||
|
valve_transform(model_origin.into())
|
||||||
|
),
|
||||||
|
color:(glam::Vec3::from_array([r as f32,g as f32,b as f32])/255.0).extend(1.0),
|
||||||
|
}).chain(
|
||||||
|
// physics models
|
||||||
|
(brush_mesh_start_idx..world_meshes.len()).map(|mesh_id|model::Model{
|
||||||
|
mesh:model::MeshId::new(mesh_id as u32),
|
||||||
|
attributes:ATTRIBUTE_CONTACT_DEFAULT,
|
||||||
transform:integer::Planar64Affine3::new(
|
transform:integer::Planar64Affine3::new(
|
||||||
integer::mat3::identity(),
|
integer::mat3::identity(),
|
||||||
valve_transform(model_origin.into())
|
integer::vec3::ZERO,
|
||||||
),
|
),
|
||||||
color:(glam::Vec3::from_array([r as f32,g as f32,b as f32])/255.0).extend(1.0),
|
color:glam::Vec4::ONE,
|
||||||
}
|
})
|
||||||
}).collect();
|
).collect();
|
||||||
|
|
||||||
PartialMap1{
|
PartialMap1{
|
||||||
attributes:unique_attributes,
|
attributes:unique_attributes,
|
||||||
|
@ -2,9 +2,16 @@ use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLo
|
|||||||
|
|
||||||
mod bsp;
|
mod bsp;
|
||||||
mod mesh;
|
mod mesh;
|
||||||
|
mod brush;
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
|
|
||||||
const VALVE_SCALE:f32=1.0/16.0;
|
const VALVE_SCALE:f32=1.0/16.0;
|
||||||
|
pub(crate) fn valve_transform_dist(d:f32)->strafesnet_common::integer::Planar64{
|
||||||
|
(d*VALVE_SCALE).try_into().unwrap()
|
||||||
|
}
|
||||||
|
pub(crate) fn valve_transform_normal([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
|
||||||
|
strafesnet_common::integer::vec3::try_from_f32_array([x,z,-y]).unwrap()
|
||||||
|
}
|
||||||
pub(crate) fn valve_transform([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
|
pub(crate) fn valve_transform([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
|
||||||
strafesnet_common::integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
|
strafesnet_common::integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::vector::Vector;
|
use crate::vector::Vector;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
pub struct Matrix<const X:usize,const Y:usize,T>{
|
pub struct Matrix<const X:usize,const Y:usize,T>{
|
||||||
pub(crate) array:[[T;Y];X],
|
pub(crate) array:[[T;Y];X],
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
/// v.x += v.z;
|
/// v.x += v.z;
|
||||||
/// println!("v.x={}",v.x);
|
/// println!("v.x={}",v.x);
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
pub struct Vector<const N:usize,T>{
|
pub struct Vector<const N:usize,T>{
|
||||||
pub(crate) array:[T;N],
|
pub(crate) array:[T;N],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user