-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path3Dmath.cpp
524 lines (415 loc) · 20.6 KB
/
3Dmath.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
#include "stdafx.h"
#include <math.h>
#include <float.h>
#include "3Dmath.h"
//inline Vec3 FABS(const Vec3& v){return Vec3(fabs(v.x), fabs(v.y), fabs(v.z));}
//
//inline Vec3 EXPF(const Vec3& v){return Vec3(expf(v.x), expf(v.y), expf(v.z));}
//
//inline Vec3 SQRTF(const Vec3& v){return Vec3(sqrtf(v.x), sqrtf(v.y), sqrtf(v.z));} //should assert v.xyz all are nonneg!!
//
//inline Vec3 Cross(const Vec3& v1, const Vec3& v2) {return Vec3((v1.y * v2.z) - (v1.z * v2.y), (v1.z * v2.x) - (v1.x * v2.z), (v1.x * v2.y) - (v1.y * v2.x));}
//
//inline float Norm(const Vec3& v) { return sqrtf(max(0, (v.x * v.x) + (v.y * v.y) + (v.z * v.z)));}
//
//inline Vec3 Normalize(const Vec3& v){float n = Norm(v); return Vec3(v.x / n, v.y / n, v.z / n);}
//
//inline float Dot(const Vec3& v1, const Vec3& v2){return ( (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z) );}
//
//inline Vec3 DP(const Vec3& v1, const Vec3& v2){return Vec3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);}
//
//inline Vec3 DP(const Vec3& v1, const Vec3& v2, const Vec3& v3){return Vec3(v1.x * v2.x * v3.x, v1.y * v2.y * v3.y, v1.z * v2.z * v3.z);}
//
//inline Vec3 DD(const Vec3& v1, const Vec3& v2){return Vec3(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z);}//Direct divide
//
//inline float Distance(const Vec3& v1, const Vec3& v2){return sqrtf( (v1.x-v2.x)*(v1.x-v2.x)+(v1.y-v2.y)*(v1.y-v2.y)+(v1.z-v2.z)*(v1.z-v2.z) );}
//
////untested!! flip side0's memory s to res-1-s.
//inline void Fliplr(byte* mat, int row, int col)
//{
// byte temp = 0;
// for(int j = 0; j < row; ++j)
// for(int i = 0; i < col / 2; ++i)//NOTE res/2!!!
// {
// temp = *(mat + j * col + i);
// *(mat + j * col + i) = *(mat + j * col + col - 1 - i);
// *(mat + j * col + col - 1 - i) = temp;
// }
//}
//
//inline void Fliplr(Vec3* mat, int row, int col)
//{
// Vec3 temp;
// for(int j = 0; j < row; ++j)
// for(int i = 0; i < col / 2; ++i)//NOTE res/2!!!
// {
// temp = *(mat + j * col + i);
// *(mat + j * col + i) = *(mat + j * col + col - 1 - i);
// *(mat + j * col + col - 1 - i) = temp;
// }
//}
//
//inline void Fliplr(float* mat, int row, int col)
//{
// float temp = 0;
// for(int j = 0; j < row; ++j)
// for(int i = 0; i < col / 2; ++i)//NOTE res/2!!!
// {
// temp = *(mat + j * col + i);
// *(mat + j * col + i) = *(mat + j * col + col - 1 - i);
// *(mat + j * col + col - 1 - i) = temp;
// }
//}
//////////////////////////////////////////////////////////////////////////
Vec3 Normal(Vec3 vPolygon[])
{ // Get 2 vectors from the polygon (2 sides), Remember the order!
Vec3 vVector1 = vPolygon[2] - vPolygon[0];
Vec3 vVector2 = vPolygon[1] - vPolygon[0];
Vec3 vNormal = Cross(vVector1, vVector2); // Take the cross product of our 2 vectors to get a perpendicular vector
// Now we have a normal, but it's at a strange length, so let's make it length 1.
vNormal = Normalize(vNormal); // Use our function we created to normalize the normal (Makes it a length of one)
return vNormal; // Return our normal at our desired length
}
////////////////////////////// CLOSEST POINT ON LINE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns the point on the line vA_vB that is closest to the point v
/////
////////////////////////////// CLOSEST POINT ON LINE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
Vec3 ClosestPointOnLine(Vec3 vA, Vec3 vB, Vec3 v)
{
// Create the vector from end point vA to our point v.
Vec3 vVector1 = v - vA;
// Create a normalized direction vector from end point vA to end point vB
Vec3 vVector2 = Normalize(vB - vA);
// Use the distance formula to find the distance of the line segment (or magnitude)
float d = Distance(vA, vB);
// Using the dot product, we project the vVector1 onto the vector vVector2.
// This essentially gives us the distance from our projected vector from vA.
float t = Dot(vVector2, vVector1);
// If our projected distance from vA, "t", is less than or equal to 0, it must
// be closest to the end point vA. We want to return this end point.
if (t <= 0)
return vA;
// If our projected distance from vA, "t", is greater than or equal to the magnitude
// or distance of the line segment, it must be closest to the end point vB. So, return vB.
if (t >= d)
return vB;
// Here we create a vector that is of length t and in the direction of vVector2
Vec3 vVector3 = vVector2 * t;
// To find the closest point on the line segment, we just add vVector3 to the original
// end point vA.
Vec3 vClosestPoint = vA + vVector3;
// Return the closest point on the line segment
return vClosestPoint;
}
/////////////////////////////////// PLANE DISTANCE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns the distance between a plane and the origin
/////
/////////////////////////////////// PLANE DISTANCE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
float PlaneDistance(Vec3 Normal, Vec3 Point)
{
float distance = 0; // This variable holds the distance from the plane tot he origin
// Use the plane equation to find the distance (Ax + By + Cz + D = 0) We want to find D.
// So, we come up with D = -(Ax + By + Cz)
// Basically, the negated dot product of the normal of the plane and the point. (More about the dot product in another tutorial)
distance = - ((Normal.x * Point.x) + (Normal.y * Point.y) + (Normal.z * Point.z));
return distance; // Return the distance
}
/////////////////////////////////// INTERSECTED PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This checks to see if a line intersects a plane
/////
/////////////////////////////////// INTERSECTED PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool IntersectedPlane(Vec3 vPoly[], Vec3 vLine[], Vec3 &vNormal, float &originDistance)
{
float distance1=0, distance2=0; // The distances from the 2 points of the line from the plane
vNormal = Normal(vPoly); // We need to get the normal of our plane to go any further
// Let's find the distance our plane is from the origin. We can find this value
// from the normal to the plane (polygon) and any point that lies on that plane (Any vertex)
originDistance = PlaneDistance(vNormal, vPoly[0]);
// Get the distance from point1 from the plane using: Ax + By + Cz + D = (The distance from the plane)
distance1 = ((vNormal.x * vLine[0].x) + // Ax +
(vNormal.y * vLine[0].y) + // Bx +
(vNormal.z * vLine[0].z)) + originDistance; // Cz + D
// Get the distance from point2 from the plane using Ax + By + Cz + D = (The distance from the plane)
distance2 = ((vNormal.x * vLine[1].x) + // Ax +
(vNormal.y * vLine[1].y) + // Bx +
(vNormal.z * vLine[1].z)) + originDistance; // Cz + D
// Now that we have 2 distances from the plane, if we times them together we either
// get a positive or negative number. If it's a negative number, that means we collided!
// This is because the 2 points must be on either side of the plane (IE. -1 * 1 = -1).
if(distance1 * distance2 >= 0) // Check to see if both point's distances are both negative or both positive
return false; // Return false if each point has the same sign. -1 and 1 would mean each point is on either side of the plane. -1 -2 or 3 4 wouldn't...
return true; // The line intersected the plane, Return TRUE
}
/////////////////////////////////// ANGLE BETWEEN VECTORS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This checks to see if a point is inside the ranges of a polygon
/////
/////////////////////////////////// ANGLE BETWEEN VECTORS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
double AngleBetweenVectors(Vec3 Vector1, Vec3 Vector2)
{
// Get the dot product of the vectors
float dotProduct = Dot(Vector1, Vector2);
// Get the product of both of the vectors magnitudes
float vectorsMagnitude = Norm(Vector1) * Norm(Vector2) ;
// Get the angle in radians between the 2 vectors
double angle = acos( dotProduct / vectorsMagnitude );
// Here we make sure that the angle is not a -1.#IND0000000 number, which means indefinate
if(_isnan(angle))
return 0;
// Return the angle in radians
return( angle );
}
/////////////////////////////////// INTERSECTION POINT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns the intersection point of the line that intersects the plane
/////
/////////////////////////////////// INTERSECTION POINT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
Vec3 IntersectionPoint(Vec3 vNormal, Vec3 vLine[], double distance)
{
Vec3 v, vLineDir; // Variables to hold the point and the line's direction
double Numerator = 0.0, Denominator = 0.0, dist = 0.0;
// 1) First we need to get the vector of our line, Then normalize it so it's a length of 1
vLineDir = vLine[1] - vLine[0]; // Get the v of the line
vLineDir = Normalize(vLineDir); // Normalize the lines vector
// 2) Use the plane equation (distance = Ax + By + Cz + D) to find the
// distance from one of our points to the plane.
Numerator = - (vNormal.x * vLine[0].x + // Use the plane equation with the normal and the line
vNormal.y * vLine[0].y +
vNormal.z * vLine[0].z + distance);
// 3) If we take the dot product between our line vector and the normal of the polygon,
Denominator = Dot(vNormal, vLineDir); // Get the dot product of the line's vector and the normal of the plane
// Since we are using division, we need to make sure we don't get a divide by zero error
// If we do get a 0, that means that there are INFINATE points because the the line is
// on the plane (the normal is perpendicular to the line - (Normal.v = 0)).
// In this case, we should just return any point on the line.
if( Denominator == 0.0) // Check so we don't divide by zero
return vLine[0]; // Return an arbitrary point on the line
dist = Numerator / Denominator; // Divide to get the multiplying (percentage) factor
// Now, like we said above, we times the dist by the vector, then add our arbitrary point.
v.x = (float)(vLine[0].x + (vLineDir.x * dist));
v.y = (float)(vLine[0].y + (vLineDir.y * dist));
v.z = (float)(vLine[0].z + (vLineDir.z * dist));
return v; // Return the intersection point
}
/////////////////////////////////// INSIDE POLYGON \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This checks to see if a point is inside the ranges of a polygon
/////
/////////////////////////////////// INSIDE POLYGON \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool InsidePolygon(Vec3 vIntersection, Vec3 Poly[], long verticeCount)
{
const double MATCH_FACTOR = 0.99; // Used to cover up the error in floating point
double Angle = 0.0; // Initialize the angle
Vec3 vA, vB; // Create temp vectors
for (int i = 0; i < verticeCount; i++) // Go in a circle to each vertex and get the angle between
{
vA = Poly[i] - vIntersection; // Subtract the intersection point from the current vertex
// Subtract the point from the next vertex
vB = Poly[(i + 1) % verticeCount] - vIntersection;
Angle += AngleBetweenVectors(vA, vB); // Find the angle between the 2 vectors and add them all up as we go along
}
if(Angle >= (MATCH_FACTOR * (2.0 * PI)) ) // If the angle is greater than 2 PI, (360 degrees)
return true; // The point is inside of the polygon
return false; // If you get here, it obviously wasn't inside the polygon, so Return FALSE
}
/////////////////////////////////// INTERSECTED POLYGON \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This checks if a line is intersecting a polygon
/////
/////////////////////////////////// INTERSECTED POLYGON \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool IntersectedPolygon(Vec3 vPoly[], Vec3 vLine[], int verticeCount)
{
Vec3 vNormal;
float originDistance = 0;
// First, make sure our line intersects the plane
// Reference // Reference
if(!IntersectedPlane(vPoly, vLine, vNormal, originDistance))
return false;
// Now that we have our normal and distance passed back from IntersectedPlane(),
// we can use it to calculate the intersection point.
Vec3 vIntersection = IntersectionPoint(vNormal, vLine, originDistance);
// Now that we have the intersection point, we need to test if it's inside the polygon.
if(InsidePolygon(vIntersection, vPoly, verticeCount))
return true; // We collided! Return success
return false; // There was no collision, so return false
}
///////////////////////////////// CLASSIFY SPHERE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This tells if a sphere is BEHIND, in FRONT, or INTERSECTS a plane, also it's distance
/////
///////////////////////////////// CLASSIFY SPHERE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
int ClassifySphere(Vec3 &vCenter,
Vec3 &vNormal, Vec3 &v, float radius, float &distance)
{
// First we need to find the distance our polygon plane is from the origin.
float d = (float)PlaneDistance(vNormal, v);
// Here we use the famous distance formula to find the distance the center point
// of the sphere is from the polygon's plane.
distance = (vNormal.x * vCenter.x + vNormal.y * vCenter.y + vNormal.z * vCenter.z + d);
// If the absolute value of the distance we just found is less than the radius,
// the sphere intersected the plane.
if(fabs(distance) < radius)
return INTERSECTS;
// Else, if the distance is greater than or equal to the radius, the sphere is
// completely in FRONT of the plane.
else if(distance >= radius)
return FRONT;
// If the sphere isn't intersecting or in FRONT of the plane, it must be BEHIND
return BEHIND;
}
///////////////////////////////// EDGE SPHERE COLLSIION \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns true if the sphere is intersecting any of the edges of the polygon
/////
///////////////////////////////// EDGE SPHERE COLLSIION \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool EdgeSphereCollision(Vec3 &vCenter,
Vec3 vPolygon[], int vertexCount, float radius)
{
Vec3 v;
// This function takes in the sphere's center, the polygon's vertices, the vertex count
// and the radius of the sphere. We will return true from this function if the sphere
// is intersecting any of the edges of the polygon.
// Go through all of the vertices in the polygon
for(int i = 0; i < vertexCount; i++)
{
// This returns the closest point on the current edge to the center of the sphere.
v = ClosestPointOnLine(vPolygon[i], vPolygon[(i + 1) % vertexCount], vCenter);
// Now, we want to calculate the distance between the closest point and the center
float distance = Distance(v, vCenter);
// If the distance is less than the radius, there must be a collision so return true
if(distance < radius)
return true;
}
// The was no intersection of the sphere and the edges of the polygon
return false;
}
////////////////////////////// SPHERE POLYGON COLLISION \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns true if our sphere collides with the polygon passed in
/////
////////////////////////////// SPHERE POLYGON COLLISION \\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool SpherePolygonCollision(Vec3 vPolygon[],
Vec3 &vCenter, int vertexCount, float radius)
{
// 1) STEP ONE - Finding the sphere's classification
// Let's use our Normal() function to return us the normal to this polygon
Vec3 vNormal = Normal(vPolygon);
// This will store the distance our sphere is from the plane
float distance = 0.0f;
// This is where we determine if the sphere is in FRONT, BEHIND, or INTERSECTS the plane
int classification = ClassifySphere(vCenter, vNormal, vPolygon[0], radius, distance);
// If the sphere intersects the polygon's plane, then we need to check further
if(classification == INTERSECTS)
{
// 2) STEP TWO - Finding the psuedo intersection point on the plane
// Now we want to project the sphere's center onto the polygon's plane
Vec3 vOffset = vNormal * distance;
// Once we have the offset to the plane, we just subtract it from the center
// of the sphere. "vPosition" now a point that lies on the plane of the polygon.
Vec3 vPosition = vCenter - vOffset;
// 3) STEP THREE - Check if the intersection point is inside the polygons perimeter
// If the intersection point is inside the perimeter of the polygon, it returns true.
// We pass in the intersection point, the list of vertices and vertex count of the poly.
if(InsidePolygon(vPosition, vPolygon, 3))
return true; // We collided!
else
{
// 4) STEP FOUR - Check the sphere intersects any of the polygon's edges
// If we get here, we didn't find an intersection point in the perimeter.
// We now need to check collision against the edges of the polygon.
if(EdgeSphereCollision(vCenter, vPolygon, vertexCount, radius))
{
return true; // We collided!
}
}
}
// If we get here, there is obviously no collision
return false;
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
///////////////////////////////// GET COLLISION OFFSET \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This returns the offset to move the center of the sphere off the collided polygon
/////
///////////////////////////////// GET COLLISION OFFSET \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
Vec3 GetCollisionOffset(Vec3 &vNormal, float radius, float distance)
{
Vec3 vOffset = Vec3(0, 0, 0);
// Once we find if a collision has taken place, we need make sure the sphere
// doesn't move into the wall. In our app, the position will actually move into
// the wall, but we check our collision detection before we render the scene, which
// eliminates the bounce back effect it would cause. The question is, how do we
// know which direction to move the sphere back? In our collision detection, we
// account for collisions on both sides of the polygon. Usually, you just need
// to worry about the side with the normal vector and positive distance. If
// you don't want to back face cull and have 2 sided planes, I check for both sides.
//
// Let me explain the math that is going on here. First, we have the normal to
// the plane, the radius of the sphere, as well as the distance the center of the
// sphere is from the plane. In the case of the sphere colliding in the front of
// the polygon, we can just subtract the distance from the radius, then multiply
// that new distance by the normal of the plane. This projects that leftover
// distance along the normal vector. For instance, say we have these values:
//
// vNormal = (1, 0, 0) radius = 5 distance = 3
//
// If we subtract the distance from the radius we get: (5 - 3 = 2)
// The number 2 tells us that our sphere is over the plane by a distance of 2.
// So basically, we need to move the sphere back 2 units. How do we know which
// direction though? This part is easy, we have a normal vector that tells us the
// direction of the plane.
// If we multiply the normal by the left over distance we get: (2, 0, 0)
// This new offset vectors tells us which direction and how much to move back.
// We then subtract this offset from the sphere's position, giving is the new
// position that is lying right on top of the plane. Ba da bing!
// If we are colliding from behind the polygon (not usual), we do the opposite
// signs as seen below:
// If our distance is greater than zero, we are in front of the polygon
if(distance > 0)
{
// Find the distance that our sphere is overlapping the plane, then
// find the direction vector to move our sphere.
float distanceOver = radius - distance;
vOffset = vNormal * distanceOver;
}
else // Else colliding from behind the polygon
{
// Find the distance that our sphere is overlapping the plane, then
// find the direction vector to move our sphere.
float distanceOver = radius + distance;
vOffset = vNormal * -distanceOver;
}
// There is one problem with check for collisions behind the polygon, and that
// is if you are moving really fast and your center goes past the front of the
// polygon, it will then assume you were colliding from behind and not let
// you back in. Most likely you will take out the if / else check, but I
// figured I would show both ways in case someone didn't want to back face cull.
// Return the offset we need to move back to not be intersecting the polygon.
return vOffset;
}
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
/////////////////////////////////////////////////////////////////////////////////
//
// * QUICK NOTES *
//
// Nothing really new added to this file since the last collision tutorial. We did
// however tweak the EdgePlaneCollision() function to handle the camera collision
// better around edges.
//
//
// Ben Humphrey (DigiBen)
// Game Programmer
// Co-Web Host of www.GameTutorials.com
//
//
Vec3 VectorByMatrix(Vec3* v, float* m) //M * vT
{
return Vec3(
v->x * m[0] + v->y * m[4] + v->z * m[8],
v->x * m[1] + v->y * m[5] + v->z * m[9],
v->x * m[2] + v->y * m[6] + v->z * m[10]);
}