1 /************************************************************************************
2 
3 	AstroMenace
4 	Hardcore 3D space scroll-shooter with spaceship upgrade possibilities.
5 	Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard
6 
7 
8 	AstroMenace is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	AstroMenace is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 	GNU General Public License for more details.
17 
18 	You should have received a copy of the GNU General Public License
19 	along with AstroMenace. If not, see <https://www.gnu.org/licenses/>.
20 
21 
22 	Website: https://viewizard.com/
23 	Project: https://github.com/viewizard/astromenace
24 	E-mail: viewizard@viewizard.com
25 
26 *************************************************************************************/
27 
28 #include "../math/math.h"
29 #include "../model3d/model3d.h"
30 
31 namespace viewizard {
32 
33 /*
34  * Check, is point belong triangle.
35  */
PointInTriangle(const sVECTOR3D & point,const sVECTOR3D & pa,const sVECTOR3D & pb,const sVECTOR3D & pc)36 static bool PointInTriangle(const sVECTOR3D &point, const sVECTOR3D &pa,
37 			    const sVECTOR3D &pb, const sVECTOR3D &pc)
38 {
39 	sVECTOR3D V1{point.x - pa.x, point.y - pa.y, point.z - pa.z};
40 	sVECTOR3D V2{point.x - pb.x, point.y - pb.y, point.z - pb.z};
41 	sVECTOR3D V3{point.x - pc.x, point.y - pc.y, point.z - pc.z};
42 
43 	V1.NormalizeHi();
44 	V2.NormalizeHi();
45 	V3.NormalizeHi();
46 
47 	float TotalAngle{acosf(V1 * V2) + acosf(V2 * V3) + acosf(V3 * V1)};
48 	if (fabsf(TotalAngle - 2 * 3.14159265f /* PI */) <= 0.005f /* allowable deviation */)
49 		return true;
50 
51 	return false;
52 }
53 
54 /*
55  * AABB-AABB collision detection.
56  */
vw_AABBAABBCollision(const bounding_box & Object1AABB,const sVECTOR3D & Object1Location,const bounding_box & Object2AABB,const sVECTOR3D & Object2Location)57 bool vw_AABBAABBCollision(const bounding_box &Object1AABB, const sVECTOR3D &Object1Location,
58 			  const bounding_box &Object2AABB, const sVECTOR3D &Object2Location)
59 {
60 	// check projection's collisions
61 	if (fabsf(Object1Location.x - Object2Location.x) > fabsf(Object1AABB[0].x + Object2AABB[0].x))
62 		return false;
63 	if (fabsf(Object1Location.y - Object2Location.y) > fabsf(Object1AABB[0].y + Object2AABB[0].y))
64 		return false;
65 	if (fabsf(Object1Location.z - Object2Location.z) > fabsf(Object1AABB[0].z + Object2AABB[0].z))
66 		return false;
67 
68 	return true;
69 }
70 
71 /*
72  * OBB-OBB collision detection.
73  */
vw_OBBOBBCollision(const bounding_box & Object1OBB,const sVECTOR3D & Object1OBBLocation,const sVECTOR3D & Object1Location,const float (& Object1RotationMatrix)[9],const bounding_box & Object2OBB,const sVECTOR3D & Object2OBBLocation,const sVECTOR3D & Object2Location,const float (& Object2RotationMatrix)[9])74 bool vw_OBBOBBCollision(const bounding_box &Object1OBB, const sVECTOR3D &Object1OBBLocation,
75 			const sVECTOR3D &Object1Location, const float (&Object1RotationMatrix)[9],
76 			const bounding_box &Object2OBB, const sVECTOR3D &Object2OBBLocation,
77 			const sVECTOR3D &Object2Location, const float (&Object2RotationMatrix)[9])
78 {
79 	// calcuate rotation matrix
80 	float TMPInvObject1RotationMatrix[9]{Object1RotationMatrix[0], Object1RotationMatrix[1], Object1RotationMatrix[2],
81 					     Object1RotationMatrix[3], Object1RotationMatrix[4], Object1RotationMatrix[5],
82 					     Object1RotationMatrix[6], Object1RotationMatrix[7], Object1RotationMatrix[8]};
83 	vw_Matrix33InverseRotate(TMPInvObject1RotationMatrix);
84 	// calcuate first box size
85 	sVECTOR3D a{(Object1OBB[0] - Object1OBB[6]) ^ 0.5f};
86 	vw_Matrix33CalcPoint(a, TMPInvObject1RotationMatrix);
87 	// calcuate inverse rotation matrix
88 	float TMPInvObject2RotationMatrix[9]{Object2RotationMatrix[0], Object2RotationMatrix[1], Object2RotationMatrix[2],
89 					     Object2RotationMatrix[3], Object2RotationMatrix[4], Object2RotationMatrix[5],
90 					     Object2RotationMatrix[6], Object2RotationMatrix[7], Object2RotationMatrix[8]};
91 	vw_Matrix33InverseRotate(TMPInvObject2RotationMatrix);
92 	// calcuate second box size
93 	sVECTOR3D b{(Object2OBB[0] - Object2OBB[6]) ^ 0.5f};
94 	vw_Matrix33CalcPoint(b, TMPInvObject2RotationMatrix);
95 	// calcuate offset in global coordinate systems
96 	sVECTOR3D T{(Object2Location + Object2OBBLocation) -
97 		    (Object1Location + Object1OBBLocation)};
98 	vw_Matrix33CalcPoint(T, TMPInvObject1RotationMatrix);
99 	// calcuate transformation matrix
100 	vw_Matrix33Mult(TMPInvObject1RotationMatrix, Object2RotationMatrix);
101 	float R[3][3]{{TMPInvObject1RotationMatrix[0], TMPInvObject1RotationMatrix[3], TMPInvObject1RotationMatrix[6]},
102 		      {TMPInvObject1RotationMatrix[1], TMPInvObject1RotationMatrix[4], TMPInvObject1RotationMatrix[7]},
103 		      {TMPInvObject1RotationMatrix[2], TMPInvObject1RotationMatrix[5], TMPInvObject1RotationMatrix[8]}};
104 
105 	// 1 (Ra)x
106 	if (fabsf(T.x) > a.x + b.x * fabsf(R[0][0]) + b.y * fabsf(R[0][1]) + b.z * fabsf(R[0][2]))
107 		return false;
108 	// 2 (Ra)y
109 	if (fabsf(T.y) > a.y + b.x * fabsf(R[1][0]) + b.y * fabsf(R[1][1]) + b.z * fabsf(R[1][2]))
110 		return false;
111 	// 3 (Ra)z
112 	if (fabsf(T.z) > a.z + b.x * fabsf(R[2][0]) + b.y * fabsf(R[2][1]) + b.z * fabsf(R[2][2]))
113 		return false;
114 
115 	// 4 (Rb)x
116 	if (fabsf(T.x * R[0][0] + T.y * R[1][0] + T.z * R[2][0]) >
117 	    (b.x + a.x * fabsf(R[0][0]) + a.y * fabsf(R[1][0]) + a. z * fabsf(R[2][0])))
118 		return false;
119 	// 5 (Rb)y
120 	if (fabsf(T.x * R[0][1] + T.y * R[1][1] + T.z * R[2][1]) >
121 	    (b.y + a.x * fabsf(R[0][1]) + a.y * fabsf(R[1][1]) + a.z * fabsf(R[2][1])))
122 		return false;
123 	// 6 (Rb)z
124 	if (fabsf(T.x * R[0][2] + T.y * R[1][2] + T.z * R[2][2]) >
125 	    (b.z + a.x * fabsf(R[0][2]) + a.y * fabsf(R[1][2]) + a.z * fabsf(R[2][2])))
126 		return false;
127 
128 	// 7 (Ra)x X (Rb)x
129 	if (fabsf(T.z * R[1][0] - T.y * R[2][0]) >
130 	    a.y * fabsf(R[2][0]) + a.z * fabsf(R[1][0]) + b.y * fabsf(R[0][2]) + b.z * fabsf(R[0][1]))
131 		return false;
132 	// 8 (Ra)x X (Rb)y
133 	if (fabsf(T.z * R[1][1] - T.y * R[2][1]) >
134 	    a.y * fabsf(R[2][1]) + a.z * fabsf(R[1][1]) + b.x * fabsf(R[0][2]) + b.z * fabsf(R[0][0]))
135 		return false;
136 	// 9 (Ra)x X (Rb)z
137 	if (fabsf(T.z * R[1][2]-T.y * R[2][2]) >
138 	    a.y * fabsf(R[2][2]) + a.z * fabsf(R[1][2]) + b.x * fabsf(R[0][1]) + b.y * fabsf(R[0][0]))
139 		return false;
140 	// 10 (Ra)y X (Rb)x
141 	if (fabsf(T.x * R[2][0]-T.z * R[0][0]) >
142 	    a.x * fabsf(R[2][0]) + a.z * fabsf(R[0][0]) + b.y * fabsf(R[1][2]) + b.z * fabsf(R[1][1]))
143 		return false;
144 	// 11 (Ra)y X (Rb)y
145 	if (fabsf(T.x * R[2][1]-T.z * R[0][1]) >
146 	    a.x * fabsf(R[2][1]) + a.z * fabsf(R[0][1]) + b.x * fabsf(R[1][2]) + b.z * fabsf(R[1][0]))
147 		return false;
148 	// 12 (Ra)y X (Rb)z
149 	if (fabsf(T.x*R[2][2]-T.z*R[0][2]) >
150 	    a.x * fabsf(R[2][2]) + a.z * fabsf(R[0][2]) + b.x * fabsf(R[1][1]) + b.y * fabsf(R[1][0]))
151 		return false;
152 	// 13 (Ra)z X (Rb)x
153 	if (fabsf(T.y * R[0][0]-T.x * R[1][0]) >
154 	    a.x * fabsf(R[1][0]) + a.y * fabsf(R[0][0]) + b.y * fabsf(R[2][2]) + b.z * fabsf(R[2][1]))
155 		return false;
156 	// 14 (Ra)z X (Rb)y
157 	if (fabsf(T.y * R[0][1]-T.x * R[1][1]) >
158 	    a.x * fabsf(R[1][1]) + a.y * fabsf(R[0][1]) + b.x * fabsf(R[2][2]) + b.z * fabsf(R[2][0]))
159 		return false;
160 	// 15 (Ra)z X (Rb)z
161 	if (fabsf(T.y * R[0][2]-T.x * R[1][2]) >
162 	    a.x * fabsf(R[1][2]) + a.y * fabsf(R[0][2]) + b.x * fabsf(R[2][1]) + b.y * fabsf(R[2][0]))
163 		return false;
164 
165 	return true;
166 }
167 
168 /*
169  * Sphere-Sphere collision detection.
170  */
vw_SphereSphereCollision(float Object1Radius,const sVECTOR3D & Object1Location,float Object2Radius,const sVECTOR3D & Object2Location,const sVECTOR3D & Object2PrevLocation)171 bool vw_SphereSphereCollision(float Object1Radius, const sVECTOR3D &Object1Location,
172 			      float Object2Radius, const sVECTOR3D &Object2Location,
173 			      const sVECTOR3D &Object2PrevLocation)
174 {
175 	bool Result{true};
176 
177 	sVECTOR3D Object1m2Location{Object1Location.x - Object2Location.x,
178 				   Object1Location.y - Object2Location.y,
179 				   Object1Location.z - Object2Location.z};
180 	float Object1p1Radius{Object1Radius + Object2Radius};
181 
182 	// fast check cube collisions
183 	if ((fabsf(Object1m2Location.x) > Object1p1Radius) ||
184 	    (fabsf(Object1m2Location.y) > Object1p1Radius) ||
185 	    (fabsf(Object1m2Location.z) > Object1p1Radius))
186 		Result = false;
187 
188 	// fast check for sphere collision
189 	if (Result) {
190 		// power of 2 for distance, no reason in sqrt here
191 		float Dist2{Object1m2Location.x * Object1m2Location.x +
192 			    Object1m2Location.y * Object1m2Location.y +
193 			    Object1m2Location.z * Object1m2Location.z};
194 
195 		// power of 2 for minimal distance
196 		float NeedDist2{Object1p1Radius * Object1p1Radius};
197 
198 		// if distance less or equal - collision detected
199 		if (Dist2 <= NeedDist2)
200 			return true;
201 	}
202 
203 	// check for distance from point to line (ray)
204 	if (!Result) {
205 		sVECTOR3D Ray{Object2Location.x - Object2PrevLocation.x,
206 			     Object2Location.y - Object2PrevLocation.y,
207 			     Object2Location.z - Object2PrevLocation.z};
208 		Ray.Normalize();
209 
210 		// calculate closest point on ray
211 		float Point{Ray.x * Object1Location.x +
212 			    Ray.y * Object1Location.y +
213 			    Ray.z * Object1Location.z - Ray.x * Object2PrevLocation.x +
214 							Ray.y * Object2PrevLocation.y +
215 							Ray.z * Object2PrevLocation.z};
216 
217 		// calculate closest point on line segment
218 		sVECTOR3D IntercPoint{Object2PrevLocation.x * Point,
219 				      Object2PrevLocation.y * Point,
220 				      Object2PrevLocation.z * Point};
221 
222 		// out of our line segment
223 		if ((Object2PrevLocation.x - IntercPoint.x) * (Object2Location.x - IntercPoint.x) +
224 		    (Object2PrevLocation.y - IntercPoint.y) * (Object2Location.y - IntercPoint.y) +
225 		    (Object2PrevLocation.z - IntercPoint.z) * (Object2Location.z - IntercPoint.z) >= 0.0f)
226 			return false;
227 
228 		// check distance, same idea with power of 2 as above
229 		float NewDist2{(IntercPoint.x - Object1Location.x) * (IntercPoint.x - Object1Location.x) +
230 			       (IntercPoint.y - Object1Location.y) * (IntercPoint.y - Object1Location.y) +
231 			       (IntercPoint.z - Object1Location.z) * (IntercPoint.z - Object1Location.z)};
232 
233 		if (NewDist2 <= Object1Radius * Object1Radius)
234 			return true;
235 	}
236 
237 	// objects too far from each other
238 	return false;
239 }
240 
241 /*
242  * Sphere-AABB collision detection.
243  */
vw_SphereAABBCollision(const bounding_box & Object1AABB,const sVECTOR3D & Object1Location,float Object2Radius,const sVECTOR3D & Object2Location,const sVECTOR3D & Object2PrevLocation)244 bool vw_SphereAABBCollision(const bounding_box &Object1AABB, const sVECTOR3D &Object1Location,
245 			    float Object2Radius, const sVECTOR3D &Object2Location, const sVECTOR3D &Object2PrevLocation)
246 {
247 	bool Result{true};
248 
249 	// detect distance AABB<->cube
250 	if ((fabsf(Object1Location.x - Object2Location.x) > Object1AABB[0].x + Object2Radius) ||
251 	    (fabsf(Object1Location.y - Object2Location.y) > Object1AABB[0].y + Object2Radius) ||
252 	    (fabsf(Object1Location.z - Object2Location.z) > Object1AABB[0].z + Object2Radius))
253 		Result = false;
254 
255 	// check for distance to line (ray)
256 	if (!Result) {
257 		// middle point
258 		sVECTOR3D mid{(Object2Location + Object2PrevLocation) / 2.0f};
259 		// line (ray) direction
260 		sVECTOR3D dir{Object2Location - Object2PrevLocation};
261 		// half of line
262 		float hl{dir.Length() / 2.0f};
263 		dir.Normalize();
264 
265 		sVECTOR3D T{Object1Location - mid};
266 
267 		// check axis
268 		if ((fabs(T.x) > Object1AABB[0].x + hl * fabs(dir.x)) ||
269 		    (fabs(T.y) > Object1AABB[0].y + hl * fabs(dir.y)) ||
270 		    (fabs(T.z) > Object1AABB[0].z + hl * fabs(dir.z)))
271 			return false;
272 
273 		// check X ^ dir
274 		double r{Object1AABB[0].y * fabs(dir.z) + Object1AABB[0].z * fabs(dir.y)};
275 		if (fabs(T.y * dir.z - T.z * dir.y) > r)
276 			return false;
277 
278 		// check  Y ^ dir
279 		r = Object1AABB[0].x * fabs(dir.z) + Object1AABB[0].z * fabs(dir.x);
280 		if (fabs(T.z * dir.x - T.x * dir.z) > r)
281 			return false;
282 
283 		// check  Z ^ dir
284 		r = Object1AABB[0].x * fabs(dir.y) + Object1AABB[0].y * fabs(dir.x);
285 		if (fabs(T.x * dir.y - T.y * dir.x) > r)
286 			return false;
287 
288 		// collision detected
289 		return true;
290 	}
291 
292 	return Result;
293 }
294 
295 /*
296  * Sphere-OBB collision detection.
297  */
vw_SphereOBBCollision(const bounding_box & Object1OBB,const sVECTOR3D & Object1OBBLocation,const sVECTOR3D & Object1Location,const float (& Object1RotationMatrix)[9],float Object2Radius,const sVECTOR3D & Object2Location,const sVECTOR3D & Object2PrevLocation)298 bool vw_SphereOBBCollision(const bounding_box &Object1OBB, const sVECTOR3D &Object1OBBLocation,
299 			   const sVECTOR3D &Object1Location, const float (&Object1RotationMatrix)[9],
300 			   float Object2Radius, const sVECTOR3D &Object2Location, const sVECTOR3D &Object2PrevLocation)
301 {
302 	sVECTOR3D TMPMax{Object1OBB[0]};
303 	sVECTOR3D TMPMin{Object1OBB[6]};
304 	sVECTOR3D TMPPoint1{Object2Location - (Object1Location + Object1OBBLocation)};
305 	sVECTOR3D TMPPoint2{Object2PrevLocation - (Object1Location + Object1OBBLocation)};
306 
307 	// calculate rotation matrix
308 	float TMPInvRotationMatrix[9]{Object1RotationMatrix[0], Object1RotationMatrix[1], Object1RotationMatrix[2],
309 				      Object1RotationMatrix[3], Object1RotationMatrix[4], Object1RotationMatrix[5],
310 				      Object1RotationMatrix[6], Object1RotationMatrix[7], Object1RotationMatrix[8]};
311 	vw_Matrix33InverseRotate(TMPInvRotationMatrix);
312 	// move it to coordinates
313 	vw_Matrix33CalcPoint(TMPMax, TMPInvRotationMatrix);
314 	vw_Matrix33CalcPoint(TMPMin, TMPInvRotationMatrix);
315 	vw_Matrix33CalcPoint(TMPPoint1, TMPInvRotationMatrix);
316 	vw_Matrix33CalcPoint(TMPPoint2, TMPInvRotationMatrix);
317 
318 	// same idea as for Sphere-AABB collision detection
319 	bool Result{true};
320 	if ((TMPPoint1.x + Object2Radius < TMPMin.x) ||
321 	    (TMPPoint1.y + Object2Radius < TMPMin.y) ||
322 	    (TMPPoint1.z + Object2Radius < TMPMin.z) ||
323 	    (TMPPoint1.x - Object2Radius > TMPMax.x) ||
324 	    (TMPPoint1.y - Object2Radius > TMPMax.y) ||
325 	    (TMPPoint1.z - Object2Radius > TMPMax.z))
326 		Result = false;
327 
328 	// check for distance to line (ray)
329 	if (!Result) {
330 		// middle point
331 		sVECTOR3D mid{(Object2Location + Object2PrevLocation) / 2.0f};
332 		// line (ray) direction
333 		sVECTOR3D dir{Object2Location - Object2PrevLocation};
334 		// half of line
335 		float hl{dir.Length() / 2.0f};
336 		dir.Normalize();
337 
338 		sVECTOR3D T{Object1Location - mid};
339 
340 		// check axis
341 		if ((fabs(T.x) > TMPMax.x + hl * fabs(dir.x)) ||
342 		    (fabs(T.y) > TMPMax.y + hl * fabs(dir.y)) ||
343 		    (fabs(T.z) > TMPMax.z + hl * fabs(dir.z)))
344 			return false;
345 
346 		// check X ^ dir
347 		double r{TMPMax.y * fabs(dir.z) + TMPMax.z * fabs(dir.y)};
348 		if (fabs(T.y * dir.z - T.z * dir.y) > r)
349 			return false;
350 
351 		// check  Y ^ dir
352 		r = TMPMax.x * fabs(dir.z) + TMPMax.z * fabs(dir.x);
353 		if (fabs(T.z * dir.x - T.x * dir.z) > r)
354 			return false;
355 
356 		// check  Z ^ dir
357 		r = TMPMax.x * fabs(dir.y) + TMPMax.y * fabs(dir.x);
358 		if (fabs(T.x * dir.y - T.y * dir.x) > r)
359 			return false;
360 
361 		// collision detected
362 		return true;
363 	}
364 
365 	return Result;
366 }
367 
368 /*
369  * Sphere-Mesh collision detection.
370  */
vw_SphereMeshCollision(const sVECTOR3D & Object1Location,const sChunk3D & Object1Chunks,const float (& Object1RotationMatrix)[9],float Object2Radius,const sVECTOR3D & Object2Location,const sVECTOR3D & Object2PrevLocation,sVECTOR3D & CollisionLocation)371 bool vw_SphereMeshCollision(const sVECTOR3D &Object1Location, const sChunk3D &Object1Chunks,
372 			    const float (&Object1RotationMatrix)[9], float Object2Radius, const sVECTOR3D &Object2Location,
373 			    const sVECTOR3D &Object2PrevLocation, sVECTOR3D &CollisionLocation)
374 {
375 	// translation matrix
376 	float TransMat[16]{Object1RotationMatrix[0], Object1RotationMatrix[1], Object1RotationMatrix[2], 0.0f,
377 			   Object1RotationMatrix[3], Object1RotationMatrix[4], Object1RotationMatrix[5], 0.0f,
378 			   Object1RotationMatrix[6], Object1RotationMatrix[7], Object1RotationMatrix[8], 0.0f,
379 			   Object1Location.x, Object1Location.y, Object1Location.z, 1.0f};
380 
381 	float TransMatTMP[16];
382 	vw_Matrix44Identity(TransMatTMP);
383 
384 	// care about rotation
385 	if ((Object1Chunks.Rotation.x != 0.0f) ||
386 	    (Object1Chunks.Rotation.y != 0.0f) ||
387 	    (Object1Chunks.Rotation.z != 0.0f))
388 		vw_Matrix44CreateRotate(TransMatTMP, Object1Chunks.Rotation);
389 
390 	// don't care about GeometryAnimation here, for more speed
391 
392 	// generate final translation matrix
393 	vw_Matrix44Translate(TransMatTMP, Object1Chunks.Location);
394 	vw_Matrix44Mult(TransMat, TransMatTMP);
395 
396 	// detect collision with mesh triangles
397 	for (unsigned int i = 0; i < Object1Chunks.VertexQuantity; i += 3) {
398 		// we use index buffer here in order to find triangle's vertices in mesh
399 		unsigned int IndexPos = Object1Chunks.RangeStart + i; // index buffer position
400 		unsigned int VertexPos{0}; // vertex buffer position
401 		if (Object1Chunks.IndexArray)
402 			VertexPos = Object1Chunks.IndexArray.get()[IndexPos] * Object1Chunks.VertexStride;
403 		else
404 			VertexPos = (IndexPos) * Object1Chunks.VertexStride;
405 
406 		// translate triangle's vertices in proper coordinates for collision detection
407 		sVECTOR3D Point1{Object1Chunks.VertexArray.get()[VertexPos],
408 				 Object1Chunks.VertexArray.get()[VertexPos + 1],
409 				 Object1Chunks.VertexArray.get()[VertexPos + 2]};
410 		vw_Matrix44CalcPoint(Point1, TransMat);
411 
412 		if (Object1Chunks.IndexArray)
413 			VertexPos = Object1Chunks.IndexArray.get()[IndexPos + 1] * Object1Chunks.VertexStride;
414 		else
415 			VertexPos = (IndexPos + 1) * Object1Chunks.VertexStride;
416 
417 		sVECTOR3D Point2{Object1Chunks.VertexArray.get()[VertexPos],
418 				 Object1Chunks.VertexArray.get()[VertexPos + 1],
419 				 Object1Chunks.VertexArray.get()[VertexPos + 2]};
420 		vw_Matrix44CalcPoint(Point2, TransMat);
421 
422 		if (Object1Chunks.IndexArray)
423 			VertexPos = Object1Chunks.IndexArray.get()[IndexPos + 2] * Object1Chunks.VertexStride;
424 		else
425 			VertexPos = (IndexPos + 2) * Object1Chunks.VertexStride;
426 
427 		sVECTOR3D Point3{Object1Chunks.VertexArray.get()[VertexPos],
428 				 Object1Chunks.VertexArray.get()[VertexPos + 1],
429 				 Object1Chunks.VertexArray.get()[VertexPos + 2]};
430 		vw_Matrix44CalcPoint(Point3, TransMat);
431 
432 		// calculate 2 vectors for plane
433 		sVECTOR3D PlaneVector1{Point2 - Point1};
434 		sVECTOR3D PlaneVector2{Point3 - Point1};
435 
436 		// calculate normal for plane
437 		sVECTOR3D NormalVector{PlaneVector1};
438 		NormalVector.Multiply(PlaneVector2);
439 		NormalVector.Normalize();
440 
441 		// calculate distance from point to plane
442 		float Distance{(Object2Location - Point1) * NormalVector};
443 
444 		// point close enough to plane for check collision with plane (triangle)
445 		if (fabsf(Distance) <= Object2Radius) {
446 			// calculate collision point on plane for ray
447 			sVECTOR3D IntercPoint{Object2Location - (NormalVector ^ Distance)};
448 
449 			// return the point data if point belongs to triangle (not just plane)
450 			if (PointInTriangle(IntercPoint, Point1, Point2, Point3)) {
451 				CollisionLocation = IntercPoint;
452 				return true;
453 			}
454 		}
455 
456 		// check for distance, do we really close enough
457 		// note, we use ^2 and don't calculate the real distance
458 		float Object2Radius2{Object2Radius * Object2Radius};
459 
460 		// check distance to point1
461 		sVECTOR3D DistancePoint1{Object2Location - Point1};
462 		float Distance2Point1{DistancePoint1.x * DistancePoint1.x +
463 				      DistancePoint1.y * DistancePoint1.y +
464 				      DistancePoint1.z * DistancePoint1.z};
465 		if (Distance2Point1 <= Object2Radius2) {
466 			CollisionLocation = Point1;
467 			return true;
468 		}
469 
470 		// check distance to point2
471 		sVECTOR3D DistancePoint2{Object2Location - Point2};
472 		float Distance2Point2{DistancePoint2.x * DistancePoint2.x +
473 				      DistancePoint2.y * DistancePoint2.y +
474 				      DistancePoint2.z * DistancePoint2.z};
475 		if (Distance2Point2 <= Object2Radius2) {
476 			CollisionLocation = Point2;
477 			return true;
478 		}
479 
480 		// check distance to point3
481 		sVECTOR3D DistancePoint3{Object2Location - Point3};
482 		float Distance2Point3{DistancePoint3.x * DistancePoint3.x +
483 				      DistancePoint3.y * DistancePoint3.y +
484 				      DistancePoint3.z * DistancePoint3.z};
485 		if (Distance2Point3 <= Object2Radius2) {
486 			CollisionLocation = Point3;
487 			return true;
488 		}
489 
490 		// check for ray, old object location - current object location
491 		// make sure we don't slipped through object (low FPS, fast object, etc)
492 
493 		// check that this is "front" for triangle, and skip triangles with "back" sided to ray start point
494 		sVECTOR3D vDir1{Point1 - Object2PrevLocation};
495 		float d1{vDir1 * NormalVector};
496 		if (d1 <= 0.001f /* allowable deviation */) {
497 			// calculate distance from point to plane
498 			float originDistance{NormalVector * Point1};
499 
500 			sVECTOR3D vLineDir{Object2Location - Object2PrevLocation};
501 
502 			// Use the plane equation with the normal and the ray
503 			float Numerator{ -(NormalVector.x * Object2PrevLocation.x +
504 					   NormalVector.y * Object2PrevLocation.y +
505 					   NormalVector.z * Object2PrevLocation.z - originDistance)};
506 
507 			float Denominator{NormalVector * vLineDir};
508 			if (Denominator != 0.0f) {
509 				float dist{Numerator / Denominator};
510 
511 				// calculate collision point on plane for ray
512 				sVECTOR3D IntercPoint{Object2PrevLocation + (vLineDir ^ dist)};
513 
514 				// check, do line (not ray here) cross the plane
515 				if (((Object2PrevLocation - IntercPoint) * (Object2Location - IntercPoint) < 0.0f) &&
516 				    (PointInTriangle(IntercPoint, Point1, Point2, Point3))) {
517 					CollisionLocation = IntercPoint;
518 					return true;
519 				}
520 			}
521 		}
522 	}
523 
524 	return false;
525 }
526 
527 } // viewizard namespace
528