2 /*! \file gim_tri_collision.h
3 \author Francisco Leon Najera
6 -----------------------------------------------------------------------------
7 This source file is part of GIMPACT Library.
9 For the latest info, see http://gimpact.sourceforge.net/
11 Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
12 email: projectileman@yahoo.com
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of EITHER:
16 (1) The GNU Lesser General Public License as published by the Free
17 Software Foundation; either version 2.1 of the License, or (at
18 your option) any later version. The text of the GNU Lesser
19 General Public License is included with this library in the
20 file GIMPACT-LICENSE-LGPL.TXT.
21 (2) The BSD-style license that is included with this library in
22 the file GIMPACT-LICENSE-BSD.TXT.
23 (3) The zlib/libpng license that is included with this library in
24 the file GIMPACT-LICENSE-ZLIB.TXT.
26 This library is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
29 GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details.
31 -----------------------------------------------------------------------------
34 #include "gim_tri_collision.h"
36 #define TRI_LOCAL_EPSILON 0.000001f
37 #define MIN_EDGE_EDGE_DIS 0.00001f
39 class GIM_TRIANGLE_CALCULATION_CACHE
43 btVector3 tu_vertices[3];
44 btVector3 tv_vertices[3];
47 btVector3 closest_point_u;
48 btVector3 closest_point_v;
49 btVector3 edge_edge_dir;
57 btVector3 temp_points[MAX_TRI_CLIPPING];
58 btVector3 temp_points1[MAX_TRI_CLIPPING];
59 btVector3 contact_points[MAX_TRI_CLIPPING];
61 //! if returns false, the faces are paralele
62 SIMD_FORCE_INLINE bool compute_intervals(
75 /* here we know that D0D2<=0.0 */
76 /* that is D0, D1 are on the same side, D2 on the other or on the plane */
77 scale_edge0 = -D2 / (D0 - D2);
78 scale_edge1 = -D1 / (D2 - D1);
84 /* here we know that d0d1<=0.0 */
85 scale_edge0 = -D0 / (D1 - D0);
86 scale_edge1 = -D1 / (D2 - D1);
90 else if (D1 * D2 > 0.0f || D0 != 0.0f)
92 /* here we know that d0d1<=0.0 or that D0!=0.0 */
93 scale_edge0 = -D0 / (D1 - D0);
94 scale_edge1 = -D2 / (D0 - D2);
108 SIMD_FORCE_INLINE GUINT clip_triangle(
109 const btVector4 &tri_plane,
110 const btVector3 *tripoints,
111 const btVector3 *srcpoints,
112 btVector3 *clip_points)
118 EDGE_PLANE(tripoints[0], tripoints[1], tri_plane, edgeplane);
120 GUINT clipped_count = PLANE_CLIP_TRIANGLE3D(
121 edgeplane, srcpoints[0], srcpoints[1], srcpoints[2], temp_points);
123 if (clipped_count == 0) return 0;
127 EDGE_PLANE(tripoints[1], tripoints[2], tri_plane, edgeplane);
129 clipped_count = PLANE_CLIP_POLYGON3D(
130 edgeplane, temp_points, clipped_count, temp_points1);
132 if (clipped_count == 0) return 0;
136 EDGE_PLANE(tripoints[2], tripoints[0], tri_plane, edgeplane);
138 clipped_count = PLANE_CLIP_POLYGON3D(
139 edgeplane, temp_points1, clipped_count, clip_points);
141 return clipped_count;
143 /*GUINT i0 = (tri_plane.closestAxis()+1)%3;
146 btVector3 temp_points[MAX_TRI_CLIPPING];
147 btVector3 temp_points1[MAX_TRI_CLIPPING];
149 GUINT clipped_count= PLANE_CLIP_TRIANGLE_GENERIC(
150 0,srcpoints[0],srcpoints[1],srcpoints[2],temp_points,
151 DISTANCE_EDGE(tripoints[0],tripoints[1],i0,i1));
154 if(clipped_count == 0) return 0;
157 clipped_count = PLANE_CLIP_POLYGON_GENERIC(
158 0,temp_points,clipped_count,temp_points1,
159 DISTANCE_EDGE(tripoints[1],tripoints[2],i0,i1));
161 if(clipped_count == 0) return 0;
164 clipped_count = PLANE_CLIP_POLYGON_GENERIC(
165 0,temp_points1,clipped_count,clipped_points,
166 DISTANCE_EDGE(tripoints[2],tripoints[0],i0,i1));
168 return clipped_count;*/
171 SIMD_FORCE_INLINE void sort_isect(
172 GREAL &isect0, GREAL &isect1, GUINT &e0, GUINT &e1, btVector3 &vec0, btVector3 &vec1)
177 GIM_SWAP_NUMBERS(isect0, isect1);
178 GIM_SWAP_NUMBERS(e0, e1);
179 btVector3 tmp = vec0;
185 //! Test verifying interval intersection with the direction between planes
187 \pre tv_plane and tu_plane must be set
189 distances[2] is set with the distance
190 closest_point_u, closest_point_v, edge_edge_dir are set too
192 - 0: faces are paralele
193 - 1: face U casts face V
194 - 2: face V casts face U
197 SIMD_FORCE_INLINE GUINT cross_line_intersection_test()
199 // Compute direction of intersection line
200 edge_edge_dir = tu_plane.cross(tv_plane);
202 VEC_LENGTH(edge_edge_dir, Dlen);
206 return 0; //faces near paralele
209 edge_edge_dir *= 1 / Dlen; //normalize
211 // Compute interval for triangle 1
212 GUINT tu_e0, tu_e1; //edge indices
213 GREAL tu_scale_e0, tu_scale_e1; //edge scale
214 if (!compute_intervals(du[0], du[1], du[2],
215 du0du1, du0du2, tu_scale_e0, tu_scale_e1, tu_e0, tu_e1)) return 0;
217 // Compute interval for triangle 2
218 GUINT tv_e0, tv_e1; //edge indices
219 GREAL tv_scale_e0, tv_scale_e1; //edge scale
221 if (!compute_intervals(dv[0], dv[1], dv[2],
222 dv0dv1, dv0dv2, tv_scale_e0, tv_scale_e1, tv_e0, tv_e1)) return 0;
225 btVector3 up_e0 = tu_vertices[tu_e0].lerp(tu_vertices[(tu_e0 + 1) % 3], tu_scale_e0);
226 btVector3 up_e1 = tu_vertices[tu_e1].lerp(tu_vertices[(tu_e1 + 1) % 3], tu_scale_e1);
228 btVector3 vp_e0 = tv_vertices[tv_e0].lerp(tv_vertices[(tv_e0 + 1) % 3], tv_scale_e0);
229 btVector3 vp_e1 = tv_vertices[tv_e1].lerp(tv_vertices[(tv_e1 + 1) % 3], tv_scale_e1);
231 //proyected intervals
232 GREAL isect_u[] = {up_e0.dot(edge_edge_dir), up_e1.dot(edge_edge_dir)};
233 GREAL isect_v[] = {vp_e0.dot(edge_edge_dir), vp_e1.dot(edge_edge_dir)};
235 sort_isect(isect_u[0], isect_u[1], tu_e0, tu_e1, up_e0, up_e1);
236 sort_isect(isect_v[0], isect_v[1], tv_e0, tv_e1, vp_e0, vp_e1);
238 const GREAL midpoint_u = 0.5f * (isect_u[0] + isect_u[1]); // midpoint
239 const GREAL midpoint_v = 0.5f * (isect_v[0] + isect_v[1]); // midpoint
241 if (midpoint_u < midpoint_v)
243 if (isect_u[1] >= isect_v[1]) // face U casts face V
247 else if (isect_v[0] <= isect_u[0]) // face V casts face U
252 closest_point_u = up_e1;
253 closest_point_v = vp_e0;
254 // calc edges and separation
256 if (isect_u[1] + MIN_EDGE_EDGE_DIS < isect_v[0]) //calc distance between two lines instead
259 tu_vertices[tu_e1], tu_vertices[(tu_e1 + 1) % 3],
260 tv_vertices[tv_e0], tv_vertices[(tv_e0 + 1) % 3],
264 edge_edge_dir = closest_point_u - closest_point_v;
265 VEC_LENGTH(edge_edge_dir, distances[2]);
266 edge_edge_dir *= 1.0f / distances[2]; // normalize
270 distances[2] = isect_v[0] - isect_u[1]; //distance negative
271 //edge_edge_dir *= -1.0f; //normal pointing from V to U
276 if (isect_v[1] >= isect_u[1]) // face V casts face U
280 else if (isect_u[0] <= isect_v[0]) // face U casts face V
285 closest_point_u = up_e0;
286 closest_point_v = vp_e1;
287 // calc edges and separation
289 if (isect_v[1] + MIN_EDGE_EDGE_DIS < isect_u[0]) //calc distance between two lines instead
292 tu_vertices[tu_e0], tu_vertices[(tu_e0 + 1) % 3],
293 tv_vertices[tv_e1], tv_vertices[(tv_e1 + 1) % 3],
297 edge_edge_dir = closest_point_u - closest_point_v;
298 VEC_LENGTH(edge_edge_dir, distances[2]);
299 edge_edge_dir *= 1.0f / distances[2]; // normalize
303 distances[2] = isect_u[0] - isect_v[1]; //distance negative
304 //edge_edge_dir *= -1.0f; //normal pointing from V to U
310 //! collides by two sides
311 SIMD_FORCE_INLINE bool triangle_collision(
320 GIM_TRIANGLE_CONTACT_DATA &contacts)
322 margin = margin_u + margin_v;
333 // plane v vs U points
335 TRIANGLE_PLANE(tv_vertices[0], tv_vertices[1], tv_vertices[2], tv_plane);
337 du[0] = DISTANCE_PLANE_POINT(tv_plane, tu_vertices[0]);
338 du[1] = DISTANCE_PLANE_POINT(tv_plane, tu_vertices[1]);
339 du[2] = DISTANCE_PLANE_POINT(tv_plane, tu_vertices[2]);
341 du0du1 = du[0] * du[1];
342 du0du2 = du[0] * du[2];
344 if (du0du1 > 0.0f && du0du2 > 0.0f) // same sign on all of them + not equal 0 ?
346 if (du[0] < 0) //we need test behind the triangle plane
348 distances[0] = GIM_MAX3(du[0], du[1], du[2]);
349 distances[0] = -distances[0];
350 if (distances[0] > margin) return false; //never intersect
353 VEC_SWAP(tv_vertices[0], tv_vertices[1]);
354 VEC_SCALE_4(tv_plane, -1.0f, tv_plane);
358 distances[0] = GIM_MIN3(du[0], du[1], du[2]);
359 if (distances[0] > margin) return false; //never intersect
364 //Look if we need to invert the triangle
365 distances[0] = (du[0] + du[1] + du[2]) / 3.0f; //centroid
367 if (distances[0] < 0.0f)
370 VEC_SWAP(tv_vertices[0], tv_vertices[1]);
371 VEC_SCALE_4(tv_plane, -1.0f, tv_plane);
373 distances[0] = GIM_MAX3(du[0], du[1], du[2]);
374 distances[0] = -distances[0];
378 distances[0] = GIM_MIN3(du[0], du[1], du[2]);
382 // plane U vs V points
384 TRIANGLE_PLANE(tu_vertices[0], tu_vertices[1], tu_vertices[2], tu_plane);
386 dv[0] = DISTANCE_PLANE_POINT(tu_plane, tv_vertices[0]);
387 dv[1] = DISTANCE_PLANE_POINT(tu_plane, tv_vertices[1]);
388 dv[2] = DISTANCE_PLANE_POINT(tu_plane, tv_vertices[2]);
390 dv0dv1 = dv[0] * dv[1];
391 dv0dv2 = dv[0] * dv[2];
393 if (dv0dv1 > 0.0f && dv0dv2 > 0.0f) // same sign on all of them + not equal 0 ?
395 if (dv[0] < 0) //we need test behind the triangle plane
397 distances[1] = GIM_MAX3(dv[0], dv[1], dv[2]);
398 distances[1] = -distances[1];
399 if (distances[1] > margin) return false; //never intersect
402 VEC_SWAP(tu_vertices[0], tu_vertices[1]);
403 VEC_SCALE_4(tu_plane, -1.0f, tu_plane);
407 distances[1] = GIM_MIN3(dv[0], dv[1], dv[2]);
408 if (distances[1] > margin) return false; //never intersect
413 //Look if we need to invert the triangle
414 distances[1] = (dv[0] + dv[1] + dv[2]) / 3.0f; //centroid
416 if (distances[1] < 0.0f)
419 VEC_SWAP(tu_vertices[0], tu_vertices[1]);
420 VEC_SCALE_4(tu_plane, -1.0f, tu_plane);
422 distances[1] = GIM_MAX3(dv[0], dv[1], dv[2]);
423 distances[1] = -distances[1];
427 distances[1] = GIM_MIN3(dv[0], dv[1], dv[2]);
432 /* bl = cross_line_intersection_test();
435 //take edge direction too
436 bl = distances.maxAxis();
441 if (distances[0] < distances[1]) bl = 1;
444 if (bl == 2) //edge edge separation
446 if (distances[2] > margin) return false;
448 contacts.m_penetration_depth = -distances[2] + margin;
449 contacts.m_points[0] = closest_point_v;
450 contacts.m_point_count = 1;
451 VEC_COPY(contacts.m_separating_normal, edge_edge_dir);
456 //clip face against other
460 if (bl == 0) //clip U points against V
462 point_count = clip_triangle(tv_plane, tv_vertices, tu_vertices, contact_points);
463 if (point_count == 0) return false;
464 contacts.merge_points(tv_plane, margin, contact_points, point_count);
466 else //clip V points against U
468 point_count = clip_triangle(tu_plane, tu_vertices, tv_vertices, contact_points);
469 if (point_count == 0) return false;
470 contacts.merge_points(tu_plane, margin, contact_points, point_count);
471 contacts.m_separating_normal *= -1.f;
473 if (contacts.m_point_count == 0) return false;
478 /*class GIM_TRIANGLE_CALCULATION_CACHE
483 btVector3 tu_vertices[3];
484 btVector3 tv_vertices[3];
485 btVector3 temp_points[MAX_TRI_CLIPPING];
486 btVector3 temp_points1[MAX_TRI_CLIPPING];
487 btVector3 clipped_points[MAX_TRI_CLIPPING];
488 GIM_TRIANGLE_CONTACT_DATA contacts1;
489 GIM_TRIANGLE_CONTACT_DATA contacts2;
494 const btVector4 & tri_plane,
495 const btVector3 * tripoints,
496 const btVector3 * srcpoints,
497 btVector3 * clipped_points)
503 EDGE_PLANE(tripoints[0],tripoints[1],tri_plane,edgeplane);
505 GUINT clipped_count = PLANE_CLIP_TRIANGLE3D(
506 edgeplane,srcpoints[0],srcpoints[1],srcpoints[2],temp_points);
508 if(clipped_count == 0) return 0;
512 EDGE_PLANE(tripoints[1],tripoints[2],tri_plane,edgeplane);
514 clipped_count = PLANE_CLIP_POLYGON3D(
515 edgeplane,temp_points,clipped_count,temp_points1);
517 if(clipped_count == 0) return 0;
521 EDGE_PLANE(tripoints[2],tripoints[0],tri_plane,edgeplane);
523 clipped_count = PLANE_CLIP_POLYGON3D(
524 edgeplane,temp_points1,clipped_count,clipped_points);
526 return clipped_count;
532 //! collides only on one side
533 bool triangle_collision(
534 const btVector3 & u0,
535 const btVector3 & u1,
536 const btVector3 & u2,
538 const btVector3 & v0,
539 const btVector3 & v1,
540 const btVector3 & v2,
542 GIM_TRIANGLE_CONTACT_DATA & contacts)
545 margin = margin_u + margin_v;
557 // plane v vs U points
560 TRIANGLE_PLANE(tv_vertices[0],tv_vertices[1],tv_vertices[2],contacts1.m_separating_normal);
562 clipped_count = clip_triangle(
563 contacts1.m_separating_normal,tv_vertices,tu_vertices,clipped_points);
565 if(clipped_count == 0 )
567 return false;//Reject
570 //find most deep interval face1
571 contacts1.merge_points(contacts1.m_separating_normal,margin,clipped_points,clipped_count);
572 if(contacts1.m_point_count == 0) return false; // too far
574 //Normal pointing to triangle1
575 //contacts1.m_separating_normal *= -1.f;
577 //Clip tri1 by tri2 edges
579 TRIANGLE_PLANE(tu_vertices[0],tu_vertices[1],tu_vertices[2],contacts2.m_separating_normal);
581 clipped_count = clip_triangle(
582 contacts2.m_separating_normal,tu_vertices,tv_vertices,clipped_points);
584 if(clipped_count == 0 )
586 return false;//Reject
589 //find most deep interval face1
590 contacts2.merge_points(contacts2.m_separating_normal,margin,clipped_points,clipped_count);
591 if(contacts2.m_point_count == 0) return false; // too far
593 contacts2.m_separating_normal *= -1.f;
595 ////check most dir for contacts
596 if(contacts2.m_penetration_depth<contacts1.m_penetration_depth)
598 contacts.copy_from(contacts2);
602 contacts.copy_from(contacts1);
610 bool GIM_TRIANGLE::collide_triangle_hard_test(
611 const GIM_TRIANGLE &other,
612 GIM_TRIANGLE_CONTACT_DATA &contact_data) const
614 GIM_TRIANGLE_CALCULATION_CACHE calc_cache;
615 return calc_cache.triangle_collision(
616 m_vertices[0], m_vertices[1], m_vertices[2], m_margin,
617 other.m_vertices[0], other.m_vertices[1], other.m_vertices[2], other.m_margin,