Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
tri.h
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:: /Commando/Code/wwmath/tri.h $*
26 * *
27 * Author:: Greg_h *
28 * *
29 * $Modtime:: 5/04/01 8:35p $*
30 * *
31 * $Revision:: 14 $*
32 * *
33 *---------------------------------------------------------------------------------------------*
34 * Functions: *
35 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
36
37
38#if defined(_MSC_VER)
39#pragma once
40#endif
41
42#ifndef TRI_H
43#define TRI_H
44
45#include "always.h"
46#include "vector4.h"
47#include "vector3.h"
48#include "vector2.h"
49#include <assert.h>
50
51
61{
62public:
63
64 const Vector3 * N;
65 const Vector3 * V[3];
66
68 {
69 assert(N);
70 assert(V[0]);
71 assert(V[1]);
72 assert(V[2]);
73
74 Vector3::Cross_Product(*(V[1])-*(V[0]),*(V[2])-*(V[0]),(Vector3 *)N);
75 ((Vector3 *)N)->Normalize();
76 }
77
78
79 bool Contains_Point(const Vector3 & ipoint) const;
80 void Find_Dominant_Plane(int * axis1,int * axis2) const;
81};
82
83
84/*
85** Utility functions:
86** Functions which have to do with triangles but not neccessarily with TriClass - usually used
87** by TriClass but also available for use elsewhere.
88*/
89
90// Enums for raycast flags (currently used only for semi-infinite axis-aligned rays)
91enum
92{
96};
97
98// This function does a pure 2D point-in-triangle test. We pass in 3D points both for the
99// triangle and test points, but we also pass in the two axes to use (the third axis is ignored,
100// so we are actually testing the projection of the four points onto one of the axis planes). The
101// triangle points are not assumed to be in any particular winding order (that is checked for
102// internally). It is used internally by TriClass::Contains_Point(), and may be used elsewhere.
103// If the ray intersects the camera at an edge we also count it as an intersection. We set a bit
104// in 'flags' to true in this case (some users of this function need this extra information and
105// it is very cheap to compute). We do not modify 'flags' if no edges are hit, so it must be
106// initialized outside this function if its value is to be used.
107inline bool Point_In_Triangle_2D(const Vector3 &tri_point0, const Vector3 &tri_point1,
108 const Vector3 &tri_point2, const Vector3 &test_point, int axis_1, int axis_2,
109 unsigned char &flags)
110{
111 // The function is based on checking signs of determinants, or in a more visually intuitive
112 // sense, checking on which side of a line a point lies. For example, if the points run in
113 // counter-clockwise order, the interior of the triangle is the intersection of the three
114 // half-planes to the left of the directed infinite lines along P0 to P1, P1 to P2, P2 to P0.
115 // Therefore the test point is in the triangle iff it is to the left of all three lines (if
116 // the points are in clockwise order, we check if it is to the right of the lines).
117 Vector2 p0p1(tri_point1[axis_1] - tri_point0[axis_1], tri_point1[axis_2] - tri_point0[axis_2]);
118 Vector2 p1p2(tri_point2[axis_1] - tri_point1[axis_1], tri_point2[axis_2] - tri_point1[axis_2]);
119 Vector2 p2p0(tri_point0[axis_1] - tri_point2[axis_1], tri_point0[axis_2] - tri_point2[axis_2]);
120
121 // Check which side P2 is relative to P0P1. The sign of this test must equal the sign of all
122 // three tests between the lines and the test point for the test point to be inside the
123 // triangle. (this test will also tell us if the three points are colinear - if the triangle
124 // is degenerate).
125 Vector2 p0p2(tri_point2[axis_1] - tri_point0[axis_1], tri_point2[axis_2] - tri_point0[axis_2]);
126 float p0p1p2 = Vector2::Perp_Dot_Product(p0p1, p0p2);
127 if (p0p1p2 != 0.0f) {
128
129 // The triangle is not degenerate - test three sides
130 float side_factor = p0p1p2 > 0.0f ? 1.0f : -1.0f;
131 float factors[3];
132
133 // Now perform tests
134 Vector2 p0pT(test_point[axis_1] - tri_point0[axis_1], test_point[axis_2] - tri_point0[axis_2]);
135 factors[0] = Vector2::Perp_Dot_Product(p0p1, p0pT);
136 if (factors[0] * side_factor < 0.0f) {
137 return false;
138 }
139 Vector2 p1pT(test_point[axis_1] - tri_point1[axis_1], test_point[axis_2] - tri_point1[axis_2]);
140 factors[1] = Vector2::Perp_Dot_Product(p1p2, p1pT);
141 if (factors[1] * side_factor < 0.0f) {
142 return false;
143 }
144 Vector2 p2pT(test_point[axis_1] - tri_point2[axis_1], test_point[axis_2] - tri_point2[axis_2]);
145 factors[2] = Vector2::Perp_Dot_Product(p2p0, p2pT);
146 if (factors[2] * side_factor < 0.0f) {
147 return false;
148 }
149 if ((factors[0] == 0.0f) || (factors[1] == 0.0f) || (factors[2] == 0.0f)) flags |= TRI_RAYCAST_FLAG_HIT_EDGE;
150 return true;
151
152 } else {
153
154 // The triangle is degenerate. This should be a rare case, so it does not matter much if it
155 // is a little slower than the non-colinear case.
156
157 // Find the two outer points along the triangle's line ('start' and 'end' points)
158 float p0p1dist2 = p0p1.Length2();
159 float p1p2dist2 = p1p2.Length2();
160 float p2p0dist2 = p1p2.Length2();
161 float max_dist2;
162 Vector2 pSpE, pSpT; // 'end' point, test point - both in 'start' points' frame
163 if (p0p1dist2 > p1p2dist2) {
164 if (p0p1dist2 > p2p0dist2) {
165 // points 0 and 1 are the 'outer' points. 0 is 'start' and 1 is 'end'.
166 pSpE = p0p1;
167 pSpT.Set(test_point[axis_1] - tri_point0[axis_1], test_point[axis_2] - tri_point0[axis_2]);
168 max_dist2 = p0p1dist2;
169 } else {
170 // points 0 and 2 are the 'outer' points. 2 is 'start' and 0 is 'end'.
171 pSpE = p2p0;
172 pSpT.Set(test_point[axis_1] - tri_point2[axis_1], test_point[axis_2] - tri_point2[axis_2]);
173 max_dist2 = p2p0dist2;
174 }
175 } else {
176 if (p1p2dist2 > p2p0dist2) {
177 // points 1 and 2 are the 'outer' points. 1 is 'start' and 2 is 'end'.
178 pSpE = p1p2;
179 pSpT.Set(test_point[axis_1] - tri_point1[axis_1], test_point[axis_2] - tri_point1[axis_2]);
180 max_dist2 = p1p2dist2;
181 } else {
182 // points 0 and 2 are the 'outer' points. 2 is 'start' and 0 is 'end'.
183 pSpE = p2p0;
184 pSpT.Set(test_point[axis_1] - tri_point2[axis_1], test_point[axis_2] - tri_point2[axis_2]);
185 max_dist2 = p2p0dist2;
186 }
187 }
188
189 if (max_dist2 != 0.0f) {
190 // Triangle is line segment, check if test point is colinear with it
191 if (Vector2::Perp_Dot_Product(pSpE, pSpT)) {
192 // Not colinear
193 return false;
194 } else {
195 // Colinear - is test point's distance from start and end <= segment length?
196 Vector2 pEpT = pSpT - pSpE;
197 if (pSpT.Length2() <= max_dist2 && pEpT.Length2() <= max_dist2) {
199 return true;
200 } else {
201 return false;
202 }
203 }
204 } else {
205 // All triangle points coincide, check if test point coincides with them
206 if (pSpT.Length2() == 0.0f) {
208 return true;
209 } else {
210 return false;
211 }
212 }
213 }
214}
215
216
217// This function tests a semi-infinite axis-aligned ray vs. a triangle.
218// The inputs are blah blah blah
220 const Vector3 &tri_point1, const Vector3 &tri_point2, const Vector4 &tri_plane,
221 const Vector3 &ray_start, int axis_r, int axis_1, int axis_2, int direction,
222 unsigned char & flags)
223{
224 bool retval = false;
225
226 // First check infinite ray vs. triangle (2D check)
227 unsigned char flags_2d = TRI_RAYCAST_FLAG_NONE;
228 if (Point_In_Triangle_2D(tri_point0, tri_point1, tri_point2, ray_start, axis_1, axis_2, flags_2d)) {
229
230 // NOTE: SR plane equations, unlike WWMath's PlaneClass, use the Ax+By+Cz+D = 0
231 // representation. It can also be viewed as C0x+C1y+C2z+C3 = dist where dist is the
232 // signed distance from the plane (and therefore the plane is defined as those points
233 // where dist = 0). Now that we know that the projection along the ray is inside the
234 // triangle, determining whether the ray hits the triangle is a matter of determining
235 // whether the start point is on the triangle plane's "anti-rayward side" (the side
236 // which the ray points away from).
237 // To determine this, we will use C[axis_r] (the plane coefficient of the ray axis).
238 // This coefficient is positive if the positive direction of the ray axis points into
239 // the planes' positive halfspace and negative if it points into the planes' negative
240 // halfspace (it is zero if the ray axis is parallel to the triangle plane). If we
241 // multiply this by 'sign' which is defined to be -1.0 if 'direction' equals 0 and
242 // 1.0 if 'direction' equals 1, we will get a number which is positive or negative
243 // depending on which halfspace the ray itself (as opposed to the rays axis) points
244 // towards. If we further multiply this by dist(start point) - the result of plugging
245 // the start point into the plane equation - we will get a number which is positive
246 // if the start point is on the 'rayward side' (ray does not intersect the triangle)
247 // and is negative if the start point is on the 'anti-rayward side' (ray does
248 // intersect triangle). In either of these two cases we are done.
249 // (see below for what happens when the result is zero - more checks need to be made).
250 static const float sign[2] = {-1.0f, 1.0f };
251 float result = tri_plane[axis_r] * sign[direction] * (tri_plane.X * ray_start.X +
252 tri_plane.Y * ray_start.Y + tri_plane.Z * ray_start.Z + tri_plane.W);
253 if (result < 0.0f) {
254 // Intersection!
255 flags |= (flags_2d & TRI_RAYCAST_FLAG_HIT_EDGE);
256 retval = true;
257 } else {
258 if (result == 0.0f) {
259 // If the result is 0, this means either the ray is parallel to the triangle
260 // plane or the start point is embedded in the triangle plane. Note that since
261 // the start point passed the 2D check, then if the ray is parallel the start
262 // point must also be embedded in the triangle plane. This leaves us with two
263 // cases:
264 // A) The ray is not parallel to the plane - in this case the start point is
265 // embedded in the triangle. We report an intersection, bitwise OR the edge
266 // result from the 2D check into the 'edge hit' flag and set the 'start in tri'
267 // flag.
268 // B) The ray is parallel to the plane. In this case the result of the 2D test
269 // tells us that the infinite line intersects the triangle (actually is
270 // embedded in the triangle along part of its length), but we do not know
271 // whether the semi-infinite ray also does so. We simplify things by not
272 // counting such an 'intersection'. There are four reasons behind this:
273 // 1. It differs from a 'normal' intersection (which has one intersecting
274 // point) - there are infinitely many intersecting points.
275 // 2. Moving the plane by an infinitesimally small amount to either side will
276 // cause the ray to no longer touch the plane.
277 // 3. This will not affect results for the known uses of this function.
278 // 4. By doing so we avoid having to code up a bunch of complex tests.
279 // Therefore in case B) we just report no intersection. We still need to find
280 // out whether the point is embedded in the triangle (for setting the flag) so
281 // we do another simple 2D test on the dominant plane.
282 if (tri_plane[axis_r]) {
283 // Case A)
284 flags |= (flags_2d & TRI_RAYCAST_FLAG_HIT_EDGE);
286 retval = true;
287 } else {
288 // Case B) - test if point in tri (we know it is in plane, so we can use
289 // TriClass function)
290 TriClass tri;
291 tri.V[0] = &tri_point0;
292 tri.V[1] = &tri_point1;
293 tri.V[2] = &tri_point2;
294 tri.N = (Vector3*)&tri_plane;
295 if (tri.Contains_Point(ray_start)) {
297 }
298 }
299 } // if (result == 0.0f)
300 } // else (result < 0.0f)
301 } // if Point_In_Triangle_2D()
302
303 return retval;
304}
305
306#endif
int sign(NUM x)
Definition BaseType.h:158
Definition tri.h:61
bool Contains_Point(const Vector3 &ipoint) const
Definition tri.cpp:201
const Vector3 * N
Definition tri.h:64
void Compute_Normal()
Definition tri.h:67
const Vector3 * V[3]
Definition tri.h:65
void Find_Dominant_Plane(int *axis1, int *axis2) const
Definition tri.cpp:143
static float Perp_Dot_Product(const Vector2 &a, const Vector2 &b)
Definition vector2.h:263
WWINLINE void Set(float x, float y)
Definition vector2.h:92
WWINLINE float Length2(void) const
Definition vector2.h:378
float X
Definition vector3.h:90
float Z
Definition vector3.h:92
float Y
Definition vector3.h:91
static WWINLINE void Cross_Product(const Vector3 &a, const Vector3 &b, Vector3 *result)
Definition vector3.h:374
float Y
Definition vector4.h:67
float Z
Definition vector4.h:68
float X
Definition vector4.h:66
float W
Definition vector4.h:69
bool Cast_Semi_Infinite_Axis_Aligned_Ray_To_Triangle(const Vector3 &tri_point0, const Vector3 &tri_point1, const Vector3 &tri_point2, const Vector4 &tri_plane, const Vector3 &ray_start, int axis_r, int axis_1, int axis_2, int direction, unsigned char &flags)
Definition tri.h:219
@ TRI_RAYCAST_FLAG_NONE
Definition tri.h:93
@ TRI_RAYCAST_FLAG_START_IN_TRI
Definition tri.h:95
@ TRI_RAYCAST_FLAG_HIT_EDGE
Definition tri.h:94
bool Point_In_Triangle_2D(const Vector3 &tri_point0, const Vector3 &tri_point1, const Vector3 &tri_point2, const Vector3 &test_point, int axis_1, int axis_2, unsigned char &flags)
Definition tri.h:107