Skip to content

Commit d4b049f

Browse files
committed
Derive AABB mins/maxs from brush vertex data
1 parent dcc7e50 commit d4b049f

3 files changed

Lines changed: 184 additions & 74 deletions

File tree

spawn_zone_tool.py

Lines changed: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
from typing import Dict, List, Tuple, Optional
66

77
import sys
8+
import math
9+
import itertools
810

911
SCRIPT_DIR = Path(__file__).resolve().parent
1012
sys.path.insert(0, str(SCRIPT_DIR))
1113

14+
from panda3d.core import Vec3
1215
from p3d_libmap.map_parser import MapParser
1316

1417

@@ -43,23 +46,126 @@ def load_map_without_geo(path: Path):
4346
return parser.map_data
4447

4548

46-
def get_bounds_for_brush(brush, origin=(0.0, 0.0, 0.0)):
47-
"""Compute min/max bounds for a brush."""
48-
ox, oy, oz = origin
49+
def _vec_sub(a, b):
50+
return (a[0] - b[0], a[1] - b[1], a[2] - b[2])
4951

50-
xs, ys, zs = [], [], []
52+
def _vec_add(a, b):
53+
return (a[0] + b[0], a[1] + b[1], a[2] + b[2])
5154

52-
for face in brush.faces:
53-
pts = (face.plane_points.v0, face.plane_points.v1, face.plane_points.v2)
54-
for p in pts:
55-
xs.append(p.x + ox)
56-
ys.append(p.y + oy)
57-
zs.append(p.z + oz)
55+
def _vec_dot(a, b):
56+
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
57+
58+
def _vec_cross(a, b):
59+
return (
60+
a[1]*b[2] - a[2]*b[1],
61+
a[2]*b[0] - a[0]*b[2],
62+
a[0]*b[1] - a[1]*b[0],
63+
)
5864

59-
if not xs:
65+
def _vec_scale(v, s):
66+
return (v[0]*s, v[1]*s, v[2]*s)
67+
68+
def _vec_len(v):
69+
return math.sqrt(_vec_dot(v, v))
70+
71+
def _vec_norm(v):
72+
l = _vec_len(v)
73+
if l == 0.0:
74+
return (0.0, 0.0, 0.0)
75+
return (v[0]/l, v[1]/l, v[2]/l)
76+
77+
def _plane_from_face(face):
78+
# p1, p2, p3 are any 3 points on the plane
79+
p1 = (face.plane_points.v0.x, face.plane_points.v0.y, face.plane_points.v0.z)
80+
p2 = (face.plane_points.v1.x, face.plane_points.v1.y, face.plane_points.v1.z)
81+
p3 = (face.plane_points.v2.x, face.plane_points.v2.y, face.plane_points.v2.z)
82+
83+
# normal = normalize((p3 - p1) x (p2 - p1)) (matches your format description)
84+
n = _vec_cross(_vec_sub(p3, p1), _vec_sub(p2, p1))
85+
n = _vec_norm(n)
86+
87+
# Plane equation: n·x = d
88+
d = _vec_dot(n, p1)
89+
90+
# Half-space rule from your docs:
91+
# points p where (p - p1)·n <= 0 are inside
92+
# => n·p <= n·p1 == d is inside
93+
return n, d
94+
95+
def _intersect_3_planes(p1, p2, p3, eps=1e-9):
96+
# Planes: n·x = d
97+
(n1, d1) = p1
98+
(n2, d2) = p2
99+
(n3, d3) = p3
100+
101+
n2xn3 = _vec_cross(n2, n3)
102+
denom = _vec_dot(n1, n2xn3)
103+
104+
if abs(denom) < eps:
105+
return None # parallel / no single point intersection
106+
107+
term1 = _vec_scale(n2xn3, d1)
108+
term2 = _vec_scale(_vec_cross(n3, n1), d2)
109+
term3 = _vec_scale(_vec_cross(n1, n2), d3)
110+
111+
x = _vec_scale(_vec_add(_vec_add(term1, term2), term3), 1.0 / denom)
112+
return x
113+
114+
def get_vertices_for_brush(brush, epsilon=0.05):
115+
"""
116+
Return a list of world-space vertices for a convex Quake brush by intersecting planes.
117+
epsilon is in Quake units.
118+
"""
119+
planes = []
120+
for face in brush.faces:
121+
n, d = _plane_from_face(face)
122+
# Skip degenerate faces
123+
if _vec_len(n) == 0.0:
124+
continue
125+
planes.append((n, d))
126+
127+
verts = []
128+
for i in range(len(planes)):
129+
for j in range(i + 1, len(planes)):
130+
for k in range(j + 1, len(planes)):
131+
p = _intersect_3_planes(planes[i], planes[j], planes[k])
132+
if p is None:
133+
continue
134+
135+
# Inside test: n·p <= d + epsilon for all planes
136+
inside = True
137+
for (n, d) in planes:
138+
if _vec_dot(n, p) > d + epsilon:
139+
inside = False
140+
break
141+
if not inside:
142+
continue
143+
144+
# Dedupe near-identical points
145+
dup = False
146+
for q in verts:
147+
if (abs(p[0] - q[0]) <= epsilon and
148+
abs(p[1] - q[1]) <= epsilon and
149+
abs(p[2] - q[2]) <= epsilon):
150+
dup = True
151+
break
152+
if not dup:
153+
verts.append(p)
154+
155+
return verts
156+
157+
def get_aabb_for_brush(brush, epsilon=0.05):
158+
verts = get_vertices_for_brush(brush, epsilon=epsilon)
159+
if not verts:
60160
return None, None
61161

62-
return (min(xs), min(ys), min(zs)), (max(xs), max(ys), max(zs))
162+
xs = [v[0] for v in verts]
163+
ys = [v[1] for v in verts]
164+
zs = [v[2] for v in verts]
165+
166+
mins = (min(xs), min(ys), min(zs))
167+
maxs = (max(xs), max(ys), max(zs))
168+
return mins, maxs
63169

64170

65171
# ---------------------------------------------------------------------------
@@ -132,7 +238,11 @@ def get_id_for_zone(name: str) -> int:
132238
# Zone brushes
133239
brushes = []
134240
for i, brush in enumerate(ent.brushes):
135-
mins, maxs = get_bounds_for_brush(brush)
241+
mins, maxs = get_aabb_for_brush(brush)
242+
if mins is None or maxs is None:
243+
print(f" * Brush {i}: (no points?)")
244+
continue
245+
136246
brushes.append(ZoneBrush(mins=mins, maxs=maxs))
137247
print(f" * Brush {i}: mins={mins}, maxs={maxs}")
138248

@@ -184,4 +294,4 @@ def main():
184294

185295

186296
if __name__ == "__main__":
187-
main()
297+
main()

tests/nszs/nzp_warehouse2.nsz

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ z1
88
1
99
1
1010
-1104.0 -852.0 304.0
11-
-3.0 -348.0 440.0
11+
-4.0 -348.0 440.0
1212
spawn_zone_up
1313
1
1414
z9
@@ -17,7 +17,7 @@ z9
1717
2
1818
1
1919
-1108.0 -852.0 440.0
20-
-3.0 -348.0 576.0
20+
-4.0 -348.0 576.0
2121
trench_zone
2222
4
2323
z5
@@ -26,7 +26,7 @@ z5
2626
3
2727
1
2828
-2220.0 -852.0 304.0
29-
-1119.0 -348.0 440.0
29+
-1120.0 -348.0 440.0
3030
trench_zone_up
3131
3
3232
z11
@@ -35,7 +35,7 @@ z11
3535
4
3636
1
3737
-2220.0 -852.0 440.0
38-
-1115.0 -348.0 576.0
38+
-1116.0 -348.0 576.0
3939
outdoor_zone
4040
6
4141
z6
@@ -45,7 +45,7 @@ z6
4545
5
4646
1
4747
-2224.0 -332.0 304.0
48-
-1123.0 172.0 440.0
48+
-1124.0 172.0 440.0
4949
mule_zone
5050
5
5151
z7
@@ -55,7 +55,7 @@ z7
5555
6
5656
1
5757
-1104.0 -324.0 304.0
58-
-3.0 180.0 576.0
58+
-4.0 180.0 576.0
5959
big_zone_left
6060
9
6161
z8

tests/nszs/nzp_zone_test.nsz

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ activate_zombs_1
99
2
1010
3
1111
3
12-
44.0 -96.0 0.0
13-
97.0 65.0 144.0
14-
-64.0 -96.0 0.0
15-
-11.0 65.0 144.0
16-
-12.0 -96.0 0.0
17-
44.0 65.0 144.0
12+
44.0 -96.0 -0.0
13+
96.0 64.0 144.0
14+
-64.0 -96.0 -0.0
15+
-12.0 64.0 144.0
16+
-12.0 -96.0 -0.0
17+
44.0 64.0 144.0
1818
number_2
1919
3
2020
activate_zombs_2
@@ -26,12 +26,12 @@ activate_zombs_2
2626
5
2727
6
2828
3
29-
204.0 -96.0 0.0
30-
257.0 65.0 144.0
31-
96.0 -96.0 0.0
32-
149.0 65.0 144.0
33-
148.0 -96.0 0.0
34-
204.0 65.0 144.0
29+
204.0 -96.0 -0.0
30+
256.0 64.0 144.0
31+
96.0 -96.0 -0.0
32+
148.0 64.0 144.0
33+
148.0 -96.0 -0.0
34+
204.0 64.0 144.0
3535
number_3
3636
6
3737
activate_zombs_3
@@ -41,12 +41,12 @@ activate_zombs_3
4141
2
4242
5
4343
3
44-
364.0 -96.0 0.0
45-
417.0 65.0 144.0
46-
256.0 -96.0 0.0
47-
309.0 65.0 144.0
48-
308.0 -96.0 0.0
49-
364.0 65.0 144.0
44+
364.0 -96.0 -0.0
45+
416.0 64.0 144.0
46+
256.0 -96.0 -0.0
47+
308.0 64.0 144.0
48+
308.0 -96.0 -0.0
49+
364.0 64.0 144.0
5050
number_4
5151
1
5252
activate_zombs_4
@@ -58,12 +58,12 @@ activate_zombs_4
5858
7
5959
8
6060
3
61-
44.0 -256.0 0.0
62-
97.0 -95.0 144.0
63-
-64.0 -256.0 0.0
64-
-11.0 -95.0 144.0
65-
-12.0 -256.0 0.0
66-
44.0 -95.0 144.0
61+
44.0 -256.0 -0.0
62+
96.0 -96.0 144.0
63+
-64.0 -256.0 -0.0
64+
-12.0 -96.0 144.0
65+
-12.0 -256.0 -0.0
66+
44.0 -96.0 144.0
6767
number_5
6868
2
6969
activate_zombs_5
@@ -78,12 +78,12 @@ activate_zombs_5
7878
8
7979
9
8080
3
81-
204.0 -256.0 0.0
82-
257.0 -95.0 144.0
83-
96.0 -256.0 0.0
84-
149.0 -95.0 144.0
85-
148.0 -256.0 0.0
86-
204.0 -95.0 144.0
81+
204.0 -256.0 -0.0
82+
256.0 -96.0 144.0
83+
96.0 -256.0 -0.0
84+
148.0 -96.0 144.0
85+
148.0 -256.0 -0.0
86+
204.0 -96.0 144.0
8787
number_6
8888
5
8989
activate_zombs_6
@@ -95,12 +95,12 @@ activate_zombs_6
9595
8
9696
9
9797
3
98-
364.0 -256.0 0.0
99-
417.0 -95.0 144.0
100-
256.0 -256.0 0.0
101-
309.0 -95.0 144.0
102-
308.0 -256.0 0.0
103-
364.0 -95.0 144.0
98+
364.0 -256.0 -0.0
99+
416.0 -96.0 144.0
100+
256.0 -256.0 -0.0
101+
308.0 -96.0 144.0
102+
308.0 -256.0 -0.0
103+
364.0 -96.0 144.0
104104
number_7
105105
7
106106
activate_zombs_7
@@ -110,12 +110,12 @@ activate_zombs_7
110110
2
111111
8
112112
3
113-
44.0 -416.0 0.0
114-
97.0 -255.0 144.0
115-
-64.0 -416.0 0.0
116-
-11.0 -255.0 144.0
117-
-12.0 -416.0 0.0
118-
44.0 -255.0 144.0
113+
44.0 -416.0 -0.0
114+
96.0 -256.0 144.0
115+
-64.0 -416.0 -0.0
116+
-12.0 -256.0 144.0
117+
-12.0 -416.0 -0.0
118+
44.0 -256.0 144.0
119119
number_8
120120
8
121121
activate_zombs_8
@@ -127,12 +127,12 @@ activate_zombs_8
127127
7
128128
9
129129
3
130-
204.0 -416.0 0.0
131-
257.0 -255.0 144.0
132-
96.0 -416.0 0.0
133-
149.0 -255.0 144.0
134-
148.0 -416.0 0.0
135-
204.0 -255.0 144.0
130+
204.0 -416.0 -0.0
131+
256.0 -256.0 144.0
132+
96.0 -416.0 -0.0
133+
148.0 -256.0 144.0
134+
148.0 -416.0 -0.0
135+
204.0 -256.0 144.0
136136
number_9
137137
9
138138
activate_zombs_9
@@ -142,9 +142,9 @@ activate_zombs_9
142142
5
143143
8
144144
3
145-
364.0 -416.0 0.0
146-
417.0 -255.0 144.0
147-
256.0 -416.0 0.0
148-
309.0 -255.0 144.0
149-
308.0 -416.0 0.0
150-
364.0 -255.0 144.0
145+
364.0 -416.0 -0.0
146+
416.0 -256.0 144.0
147+
256.0 -416.0 -0.0
148+
308.0 -256.0 144.0
149+
308.0 -416.0 -0.0
150+
364.0 -256.0 144.0

0 commit comments

Comments
 (0)