Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
colmathline.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/***********************************************************************************************
20 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
21 ***********************************************************************************************
22 * *
23 * Project Name : WWMath *
24 * *
25 * $Archive:: /VSS_Sync/wwmath/colmathline.cpp $*
26 * *
27 * Author:: Greg Hjelstrom *
28 * *
29 * $Modtime:: 8/29/01 10:25p $*
30 * *
31 * $Revision:: 10 $*
32 * *
33 *---------------------------------------------------------------------------------------------*
34 * Functions: *
35 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
36
37
38#include "colmath.h"
39#include "aaplane.h"
40#include "plane.h"
41#include "lineseg.h"
42#include "tri.h"
43#include "sphere.h"
44#include "aabox.h"
45#include "obbox.h"
46#include "wwdebug.h"
47
48
49/*
50** Structure used in the line->box test. There was a lot of common code between the axis-
51** aligned and oriented box tests so I package all of the truely relevant information into
52** this struct and pass it into a function local to this module. In the case of oriented
53** boxes, the ray must be transformed into the box's coordinate system prior to the call
54** and the normal is calculated slightly differently.
55*/
67
68/*
69** Enumeration which can be used to categorize a point with respect to the projection
70** of a box onto an axis. The point will either be to the negative side of the span, in the
71** span, or on the positive side of the span.
72*/
78
79
80/*
81** Table of normals for an axis aligned box.
82** access like this:
83**
84** _box_normal[AXIS][SIDE]
85**
86** <AXIS> is 0,1,2 meaning x,y,z
87** <SIDE> is BOX_SIDE_NEGATIVE or BOX_SIDE_POSITIVE
88*/
89static Vector3 _box_normal[3][2] =
90{
91 // plane = 0 (x axis)
92 {
93 Vector3(-1,0,0), // Left
94 Vector3(1,0,0) // Right
95 },
96 // plane = 1 (y axis)
97 {
98 Vector3(0,-1,0),
99 Vector3(0,1,0)
100 },
101 // plane = 2 (z axis)
102 {
103 Vector3(0,0,-1),
104 Vector3(0,0,1)
105 }
106};
107
108/*
109** Local function prototypes
110*/
111inline bool Test_Aligned_Box(BoxTestStruct * test);
112
113
114bool CollisionMath::Collide(const LineSegClass & line,const AAPlaneClass & plane,CastResultStruct * result)
115{
116 float num,den,t;
117
118 den = line.Get_DP()[plane.Normal];
119
120 /*
121 ** Check if line is parallel to this plane
122 */
123 if (den == 0.0f) {
124 return false;
125 }
126
127 num = -(line.Get_P0()[plane.Normal] - plane.Dist);
128 t = num/den;
129
130 /*
131 ** If t is not between 0 and 1, the line containing the segment intersects
132 ** the plane but the segment does not
133 */
134 if ((t < 0.0f) || (t > 1.0f)) {
135 return false;
136 }
137
138 /*
139 ** Ok, we hit the plane!
140 */
141 if (t < result->Fraction) {
142 result->Fraction = t;
143 result->Normal.Set(0,0,0);
144 result->Normal[plane.Normal] = 1.0f;
145 return true;
146 }
147 return false;
148}
149
150
151bool CollisionMath::Collide(const LineSegClass & line,const PlaneClass & plane,CastResultStruct * result)
152{
153 float num,den,t;
154 den = Vector3::Dot_Product(plane.N,line.Get_DP());
155
156 /*
157 ** If the denominator is zero, the ray is parallel to the plane
158 */
159 if (den == 0.0f) {
160 return false;
161 }
162
163 num = -(Vector3::Dot_Product(plane.N,line.Get_P0()) - plane.D);
164 t = num/den;
165
166 /*
167 ** If t is not between 0 and 1, the line containing the segment intersects
168 ** the plane but the segment does not
169 */
170 if ((t < 0.0f) || (t > 1.0f)) {
171 return false;
172 }
173
174 /*
175 ** Ok, we hit the plane!
176 */
177 if (t < result->Fraction) {
178 result->Fraction = t;
179 result->Normal = plane.N;
180
181 /*
182 ** if user is asking for the point, compute it.
183 */
184 if (result->ComputeContactPoint) {
185 result->ContactPoint = line.Get_P0() + result->Fraction * line.Get_DP();
186 }
187 return true;
188 }
189 return false;
190}
191
193{
195
196 /*
197 ** Compute intersection of the line with the plane of the polygon
198 */
199 PlaneClass plane(*tri.N,*tri.V[0]);
200 Vector3 ipoint;
201 float num,den,t;
202
203 den = Vector3::Dot_Product(plane.N,line.Get_DP());
204
205 /*
206 ** If the denominator is zero, the ray is parallel to the plane
207 */
208 if (den == 0.0f) {
209 return false;
210 }
211 num = -(Vector3::Dot_Product(plane.N,line.Get_P0()) - plane.D);
212 t = num/den;
213
214 /*
215 ** If t is not between 0 and 1, the line containing the segment intersects
216 ** the plane but the segment does not
217 */
218 if ((t < 0.0f) || (t > 1.0f)) {
219 return false;
220 }
221
222 ipoint = line.Get_P0() + t*line.Get_DP();
223
224 /*
225 ** Check if this point is inside the triangle
226 */
227 if (!tri.Contains_Point(ipoint)) {
228 return false;
229 }
230
231 /*
232 ** Ok, we hit the triangle, set the collision results
233 */
234 if (t < res->Fraction) {
235 res->Fraction = t;
236 res->Normal = plane.N;
237 if (res->ComputeContactPoint) {
238 res->ContactPoint = line.Get_P0() + res->Fraction * line.Get_DP();
239 }
241 return true;
242 }
243 return false;
244}
245
247{
248 // this game from graphics gems 1, page 388
249 // intersection of a ray with a sphere
250 Vector3 dc = sphere.Center - line.Get_P0();
251 float clen = Vector3::Dot_Product((dc) , line.Get_Dir());
252 float disc = (sphere.Radius * sphere.Radius) - (dc.Length2() - clen*clen);
253 if (disc < 0.0f) {
254 return false;
255 } else {
256 float d = WWMath::Sqrt(disc);
257 float frac = (clen - d) / line.Get_Length();
258 if (frac<0.0f)
259 frac = (clen + d) / line.Get_Length();
260 if (frac<0.0f) return false;
261 if (frac < res->Fraction) {
262
263 res->Fraction = frac;
264
265 Vector3 p = line.Get_P0() + (clen - d)*line.Get_Dir();
266 Vector3 norm = p - sphere.Center;
267 norm /= sphere.Radius;
268
269 res->Normal = norm;
270 if (res->ComputeContactPoint) {
271 res->ContactPoint = line.Get_P0() + res->Fraction * line.Get_DP();
272 }
273 return true;
274 }
275 }
276 return false;
277}
278
280{
281 // set up the test struct
283 test.Min = box.Center - box.Extent;
284 test.Max = box.Center + box.Extent;
285 test.P0 = line.Get_P0();
286 test.DP = line.Get_DP();
287
288 // check ray against the box, exit if the ray totally missed the box,
289 if (!Test_Aligned_Box(&test)) {
290 return false;
291 }
292
293 // if ray starts inside the box, note that fact and bail.
294 if (test.Inside) {
295 res->StartBad = true;
296 return true;
297 }
298
299 // Now, if this intersection is before any current intersection
300 // that we've found, install our intersection
301 if (test.Fraction < res->Fraction) {
302 res->Fraction = test.Fraction;
303 assert(test.Side != BOX_SIDE_MIDDLE);
304 res->Normal = _box_normal[test.Axis][test.Side];
305 if (res->ComputeContactPoint) {
306 res->ContactPoint = line.Get_P0() + res->Fraction * line.Get_DP();
307 }
308 return true;
309 }
310 return false;
311}
312
313bool CollisionMath::Collide(const LineSegClass & line,const OBBoxClass & box,CastResultStruct * result)
314{
315 // set up the test struct
317 test.Min = box.Center - box.Extent;
318 test.Max = box.Center + box.Extent;
319
320 test.P0 = (box.Basis.Transpose() * (line.Get_P0() - box.Center)) + box.Center;
321 test.DP = box.Basis.Transpose() * line.Get_DP();
322
323 // check ray against the box, exit if the ray totally missed the box,
324 if (!Test_Aligned_Box(&test)) {
325 return false;
326 }
327
328 // if ray starts inside the box, don't collide
329 if (test.Inside) {
330 result->StartBad = true;
331 return true;
332 }
333
334 // Now, if this intersection is before any current intersection
335 // that we've found, install our intersection
336 if (test.Fraction < result->Fraction) {
337 result->Fraction = test.Fraction;
338 assert(test.Side != BOX_SIDE_MIDDLE);
339
340 switch (test.Axis) {
341 case 0:
342 result->Normal.Set(box.Basis[0][0],box.Basis[1][0],box.Basis[2][0]);
343 break;
344
345 case 1:
346 result->Normal.Set(box.Basis[0][1],box.Basis[1][1],box.Basis[2][1]);
347 break;
348
349 case 2:
350 result->Normal.Set(box.Basis[0][2],box.Basis[1][2],box.Basis[2][2]);
351 break;
352 }
353
354 if (test.Side == BOX_SIDE_NEGATIVE) {
355 result->Normal = -result->Normal;
356 }
357 if (result->ComputeContactPoint) {
358 result->ContactPoint = line.Get_P0() + result->Fraction * line.Get_DP();
359 }
360 return true;
361 }
362 return false;
363}
364
365
366
367/***********************************************************************************************
368 * Test_Aligned_Box -- used as the guts of the Box intersection tests *
369 * *
370 * INPUT: *
371 * *
372 * OUTPUT: *
373 * *
374 * WARNINGS: *
375 * *
376 * HISTORY: *
377 * 4/20/98 GTH : Created. *
378 *=============================================================================================*/
380{
381 int i;
382
383 // candidate intersection plane distance for each axis
384 float candidateplane[3];
385
386 // t value along the ray for each axis
387 float maxt[3];
388
389 // intersection point
390 Vector3 coord;
391
392 // which "side" of the box for each axis
393 int quadrant[3];
394 bool inside = true;
395
396 for (i=0; i<3; i++) {
397 if (test->P0[i] < test->Min[i]) {
398 quadrant[i] = BOX_SIDE_NEGATIVE;
399 candidateplane[i] = test->Min[i];
400 inside = false;
401 } else if (test->P0[i] > test->Max[i]) {
402 quadrant[i] = BOX_SIDE_POSITIVE;
403 candidateplane[i] = test->Max[i];
404 inside = false;
405 } else {
406 quadrant[i] = BOX_SIDE_MIDDLE;
407 }
408 }
409
410 /*
411 ** Ray started out inside the box...
412 */
413 if (inside) {
414 test->Fraction = 0.0f;
415 test->Inside = true;
416 return true;
417 }
418
419 /*
420 ** Calculate the 3 distances to the candidate planes
421 */
422 for (i=0; i<3; i++) {
423 if (quadrant[i] != BOX_SIDE_MIDDLE && test->DP[i] != 0.0f) {
424 maxt[i] = (candidateplane[i] - test->P0[i]) / test->DP[i];
425 } else {
426 maxt[i] = -1.0f;
427 }
428 }
429
430 /*
431 ** Get the largest of the maxt's for the final choice of intersection
432 */
433 int intersection_plane = 0;
434
435 for (i=1; i<3; i++) {
436 if (maxt[i] > maxt[intersection_plane]) {
437 intersection_plane = i;
438 }
439 }
440
441 /*
442 ** If the ray is "in front" of all of the planes, return
443 */
444 if (maxt[intersection_plane] < 0.0f) {
445 return false;
446 }
447
448 /*
449 ** Check if the ray is inside the box, i.e. on the two planes which
450 ** are not the intersection planes, the intersection point should
451 ** be between the min and max of the box.
452 */
453 for (i=0; i<3; i++) {
454
455 if (intersection_plane != i) {
456
457 coord[i] = test->P0[i] + maxt[intersection_plane] * test->DP[i];
458
459 if ((coord[i] < test->Min[i]) || (coord[i] > test->Max[i])) {
460
461 return false; // ray is outside the box
462
463 }
464
465 } else {
466
467 coord[i] = candidateplane[i];
468
469 }
470 }
471
472 /*
473 ** Fill in the intersection values
474 */
475 test->Fraction = maxt[intersection_plane];
476 test->Inside = false;
477 test->Axis = intersection_plane;
478 test->Side = quadrant[intersection_plane];
479 return true;
480}
481
482
483
484
Vector3 Center
Definition aabox.h:123
Vector3 Extent
Definition aabox.h:124
float Dist
Definition aaplane.h:67
AxisEnum Normal
Definition aaplane.h:66
static bool Collide(const LineSegClass &line, const AAPlaneClass &plane, CastResultStruct *result)
float Get_Length() const
Definition lineseg.h:74
const Vector3 & Get_Dir() const
Definition lineseg.h:73
const Vector3 & Get_DP() const
Definition lineseg.h:72
const Vector3 & Get_P0() const
Definition lineseg.h:70
WWINLINE Matrix3x3 Transpose(void) const
Definition matrix3.h:413
Vector3 Extent
Definition obbox.h:114
Matrix3x3 Basis
Definition obbox.h:112
Vector3 Center
Definition obbox.h:113
Vector3 N
Definition plane.h:67
float D
Definition plane.h:68
float Radius
Definition sphere.h:91
Vector3 Center
Definition sphere.h:90
Definition tri.h:61
bool Contains_Point(const Vector3 &ipoint) const
Definition tri.cpp:201
const Vector3 * N
Definition tri.h:64
const Vector3 * V[3]
Definition tri.h:65
static WWINLINE float Dot_Product(const Vector3 &a, const Vector3 &b)
Definition vector3.h:293
WWINLINE float Length2(void) const
Definition vector3.h:469
WWINLINE void Set(float x, float y, float z)
Definition vector3.h:103
static float Sqrt(float val)
Definition wwmath.h:568
#define TRACK_COLLISION_RAY_TRI_HIT
Definition colmath.h:299
#define TRACK_COLLISION_RAY_TRI
Definition colmath.h:298
BoxSideType
@ BOX_SIDE_POSITIVE
@ BOX_SIDE_MIDDLE
@ BOX_SIDE_NEGATIVE
bool Test_Aligned_Box(BoxTestStruct *test)
Vector3 ContactPoint
Definition castres.h:70
bool ComputeContactPoint
Definition castres.h:69
Vector3 Normal
Definition castres.h:66
int test
Definition test6.cpp:32