Skip to content

Commit 8cb6252

Browse files
authored
Add props to bspeater (#29)
* almost correct prop detection * probably fix wallrunning and fix some crashes * better props pos and angles * cleanup the transform stuff * fix floors being gone * fix window no collide stuff, better debug and mdl parse thing
1 parent 64e1079 commit 8cb6252

File tree

7 files changed

+240
-100
lines changed

7 files changed

+240
-100
lines changed

Cargo.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bspeater/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ flume = { version = "0.11.1", default-features = false }
2323
bonsai-bt = "0.10.0"
2424
indexmap = "2.11.4"
2525
rustc-hash = "2.1.1"
26+
hexasphere = "16.0.0"

bspeater/src/bindings.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ pub struct Vertex {
553553
pub bone_id: [u8; MAX_NUM_BONES_PER_VERT],
554554
}
555555

556+
unsafe impl Pod for Studiohdr {}
556557
#[repr(C)]
557558
#[derive(Debug, Clone, Copy, Zeroable)]
558559
pub struct Studiohdr {
@@ -800,7 +801,7 @@ pub struct Compactedge {
800801
pub is_virtual: bool,
801802
}
802803

803-
// static_assert(sizeof(compactedge_t) == 4);
804+
static ASSERT_EDGE: () = assert!(std::mem::size_of::<Compactedge>() == 4);
804805

805806
#[bitfield(bits = 128)]
806807
#[repr(C)]

bspeater/src/debug.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
22
use bevy_fly_camera::FlyCamera;
33
use oktree::prelude::*;
4-
use std::{iter, ops::BitAnd, sync::Arc};
4+
use std::{ops::BitAnd, sync::Arc};
55

66
use crate::{
77
ATTRIBUTE_PRIMATIVE_TYPE, ATTRIBUTE_UNIQUE_CONTENTS, CELL_SIZE, ChunkCells, DebugAmount,
@@ -256,19 +256,13 @@ fn debug_contents(
256256
.cast_ray(
257257
Ray3d::new(
258258
translation,
259-
// Dir3::new(
260-
// Transform::from_rotation(rotation)
261-
// .transform_point(Vec3::X)
262-
// .normalize(),
263-
// )
264-
// .unwrap_or(),
265-
Dir3::X,
259+
Dir3::new(rotation.mul_vec3(-Vec3::Z).normalize()).unwrap_or(Dir3::X),
266260
),
267261
&MeshRayCastSettings::default(),
268262
)
269263
.first()
270264
else {
271-
bevy::log::warn!("couldn't get anything in this ray cast odd");
265+
// bevy::log::warn!("couldn't get anything in this ray cast odd");
272266
return Ok(());
273267
};
274268

bspeater/src/geoset_loader.rs

Lines changed: 134 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,42 @@
11
use crate::*;
22
use bevy::math::DVec3;
3-
use rayon::prelude::*;
3+
4+
const EXTRA_FLAGS: &[(&str, [(i32, i32); 3])] = &[
5+
(
6+
"mp_black_water_canal",
7+
[(0, 0), (0, Contents::WINDOW_NO_COLLIDE as i32), (0, 0)],
8+
),
9+
(
10+
"mp_colony02",
11+
[(0, 0), (0, Contents::WINDOW_NO_COLLIDE as i32), (0, 0)],
12+
),
13+
];
14+
const SHOULD_CHECK_WINDOW_NO_COLLIDE: &[(&str, bool)] = &[
15+
("mp_angel_city", true),
16+
("mp_black_water_canal", true),
17+
("mp_grave", true),
18+
("mp_colony02", true),
19+
("mp_complex3", true),
20+
("mp_crashsite3", true),
21+
("mp_drydock", true),
22+
("mp_eden", true),
23+
("mp_thaw", true),
24+
("mp_forwardbase_kodai", true),
25+
("mp_glitch", true),
26+
("mp_homestead", true),
27+
("mp_relic02", true),
28+
("mp_rise", true),
29+
("mp_wargames", true),
30+
("mp_lobby", true),
31+
("mp_lf_deck", true),
32+
("mp_lf_meadow", true),
33+
("mp_lf_stacks", true),
34+
("mp_lf_township", true),
35+
("mp_lf_traffic", true),
36+
("mp_lf_uma", true),
37+
("mp_coliseum", true),
38+
("mp_coliseum_column", true),
39+
];
440

541
pub fn geoset_to_meshes(
642
BSPData {
@@ -17,7 +53,19 @@ pub fn geoset_to_meshes(
1753
props,
1854
model_data,
1955
}: BSPData,
56+
map_name: &str,
2057
) -> Vec<Mesh> {
58+
let extra_flags = EXTRA_FLAGS
59+
.iter()
60+
.find_map(|(name, rest)| (*name == map_name).then_some(rest))
61+
.copied()
62+
.unwrap_or_default();
63+
let should_check_window_no_collide = SHOULD_CHECK_WINDOW_NO_COLLIDE
64+
.iter()
65+
.find_map(|(name, rest)| (*name == map_name).then_some(rest))
66+
.copied()
67+
.unwrap_or_default();
68+
2169
geo_sets
2270
.into_iter()
2371
.flat_map(|geoset| {
@@ -30,26 +78,46 @@ pub fn geoset_to_meshes(
3078
.chain(geoset.prim_count.eq(&1).then_some(geoset.prim_start))
3179
})
3280
.filter_map(|primative| {
33-
let flag = Contents::SOLID as i32 | Contents::PLAYER_CLIP as i32;
34-
let no_flag = Contents::WINDOW_NO_COLLIDE as i32;
81+
let ty =
82+
PrimitiveType::try_from((primative >> 29) & 0x7).expect("invalid primative type");
83+
let (flag, no_flag) = match ty {
84+
PrimitiveType::Brush => (
85+
Contents::SOLID as i32 | Contents::PLAYER_CLIP as i32,
86+
Contents::WINDOW_NO_COLLIDE as i32,
87+
),
88+
PrimitiveType::Tricoll => (
89+
Contents::SOLID as i32
90+
| Contents::PLAYER_CLIP as i32
91+
| Contents::BULLET_CLIP as i32,
92+
0,
93+
),
94+
PrimitiveType::Prop => (Contents::SOLID as i32 | Contents::PLAYER_CLIP as i32, 0),
95+
};
96+
97+
let extra_flags = extra_flags
98+
.get((ty as usize).saturating_sub(1))
99+
.copied()
100+
.unwrap_or_default();
101+
let (flag, no_flag) = (flag | extra_flags.0, no_flag | extra_flags.1);
102+
35103
// if it doesn't contain any
36104
if unique_contents[primative as usize & 0xFF] & flag == 0
37105
|| unique_contents[primative as usize & 0xFF] & no_flag != 0
38106
{
39107
None
40108
} else {
41109
Some((
42-
PrimitiveType::try_from((primative >> 29) & 0x7)
43-
.expect("invalid primative type"),
110+
ty,
44111
((primative >> 8) & 0x1FFFFF) as usize,
45112
unique_contents[primative as usize & 0xFF],
113+
unique_contents[primative as usize & 0xFF],
46114
))
47115
}
48116
})
49-
.collect::<std::collections::HashSet<(PrimitiveType, usize, i32)>>()
117+
.collect::<std::collections::HashSet<(PrimitiveType, usize, i32, i32)>>()
50118
// maybe this doesn't improve anything but it's cool
51119
.into_par_iter()
52-
.filter_map(|(ty, index, contents)| {
120+
.filter_map(|(ty, index, contents, flags)| {
53121
let mut pushing_vertices: Vec<Vec3> = Vec::new();
54122
let mut indices = Vec::new();
55123

@@ -78,6 +146,25 @@ pub fn geoset_to_meshes(
78146
)?,
79147
}
80148

149+
// just check if the surface is not flat which means we have a huge wall here that is marked WINDOW_NO_COLLIDE therefore it needs to be gone
150+
if should_check_window_no_collide
151+
&& flags & Contents::WINDOW_NO_COLLIDE as i32 != 0
152+
&& (pushing_vertices
153+
.iter()
154+
.map(|v| v.y as i64)
155+
.max()
156+
.unwrap_or_default()
157+
- pushing_vertices
158+
.iter()
159+
.map(|v| v.y as i64)
160+
.min()
161+
.unwrap_or_default())
162+
.abs()
163+
> 200
164+
{
165+
return None;
166+
}
167+
81168
Some(
82169
Mesh::new(
83170
bevy::render::mesh::PrimitiveTopology::TriangleList,
@@ -176,31 +263,34 @@ fn prop_to_mesh(
176263
if props.len() <= index {
177264
return None;
178265
}
266+
179267
let static_prop = props[index];
180-
let transform = Transform::from_translation(static_prop.origin)
268+
let transform = Transform::from_translation(static_prop.origin.xzy())
181269
.with_rotation(Quat::from_euler(
182270
EulerRot::XYZ,
183-
static_prop.angles.x,
184-
static_prop.angles.y,
185-
static_prop.angles.z,
271+
static_prop.angles.x.to_radians(),
272+
static_prop.angles.y.to_radians(),
273+
static_prop.angles.z.to_radians(),
186274
))
187275
.with_scale(Vec3::splat(static_prop.scale))
188-
.compute_affine();
276+
.compute_matrix();
189277

190278
if let Some(model_data) = model_data
191279
.get(static_prop.model_index as usize)
192280
.and_then(|o| o.as_ref())
193-
// .filter(|_| static_prop.solid == 1)
281+
.cloned()
282+
// .or_else(|| Some(ico(Sphere::new(static_prop.scale * 3.), 3)))
283+
.filter(|_| static_prop.solid == 6)
284+
// figure what this actually is ^ rigth vphysics stuff I rember
194285
{
195286
indices.extend(&model_data.1);
196287
pushing_vertices.extend(
197288
model_data
198289
.0
199290
.iter()
200291
.copied()
201-
.map(|vert| transform.transform_point3(vert)),
292+
.map(|vert| transform.mul_vec4(vert.extend(1.)).xyz()),
202293
);
203-
println!("phys model");
204294
} else {
205295
// println!("no phys model");
206296
}
@@ -267,3 +357,32 @@ fn calculate_intersection_point(planes: [&Vec4; 3]) -> Option<DVec3> {
267357

268358
Some(DVec3::new(d.dot(u), m3.dot(v), -m2.dot(v)) / denom)
269359
}
360+
361+
// pub fn ico(sphere: Sphere, subdivisions: u32) -> (Vec<Vec3>, Vec<u32>) {
362+
// use hexasphere::shapes::IcoSphere;
363+
// let generated = IcoSphere::new(subdivisions as usize, |point| {
364+
// let inclination = ops::acos(point.y);
365+
// let azimuth = ops::atan2(point.z, point.x);
366+
367+
// let norm_inclination = inclination / PI;
368+
// let norm_azimuth = 0.5 - (azimuth / core::f32::consts::TAU);
369+
370+
// [norm_azimuth, norm_inclination]
371+
// });
372+
373+
// let raw_points = generated.raw_points();
374+
375+
// let points = raw_points
376+
// .iter()
377+
// .map(|&p| (p * sphere.radius).into())
378+
// .map(Vec3::from_array)
379+
// .collect::<Vec<Vec3>>();
380+
381+
// let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20);
382+
383+
// for i in 0..20 {
384+
// generated.get_indices(i, &mut indices);
385+
// }
386+
387+
// (points, indices)
388+
// }

bspeater/src/main.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use anyhow::Context;
55
use avian3d::prelude::*;
66
use bevy::{
77
asset::RenderAssetUsages,
8-
pbr::wireframe::WireframeConfig,
8+
pbr::wireframe::{WireframeConfig, WireframePlugin},
99
prelude::*,
1010
render::{
1111
RenderPlugin,
@@ -272,20 +272,23 @@ fn main() -> anyhow::Result<()> {
272272
println!("vertices {:#?}", vertices.len());
273273
println!("normals {:#?}", normals.len());
274274

275-
let meshes = geoset_loader::geoset_to_meshes(BSPData {
276-
vertices,
277-
tricoll_headers,
278-
tricoll_triangles,
279-
geo_sets,
280-
col_primatives,
281-
unique_contents,
282-
brushes,
283-
brush_side_plane_offsets,
284-
brush_planes,
285-
grid,
286-
props,
287-
model_data,
288-
});
275+
let meshes = geoset_loader::geoset_to_meshes(
276+
BSPData {
277+
vertices,
278+
tricoll_headers,
279+
tricoll_triangles,
280+
geo_sets,
281+
col_primatives,
282+
unique_contents,
283+
brushes,
284+
brush_side_plane_offsets,
285+
brush_planes,
286+
grid,
287+
props,
288+
model_data,
289+
},
290+
&map_name,
291+
);
289292

290293
let mut app = App::new();
291294

@@ -305,8 +308,8 @@ fn main() -> anyhow::Result<()> {
305308
}),
306309
FlyCameraPlugin,
307310
PhysicsPlugins::default(),
308-
PhysicsDebugPlugin::default(),
309-
// WireframePlugin::default(),
311+
// PhysicsDebugPlugin::default(),
312+
WireframePlugin::default(),
310313
))
311314
.init_resource::<WireframeConfig>()
312315
.init_resource::<ChunkCells>()

0 commit comments

Comments
 (0)