1498 lines
39 KiB
C++
1498 lines
39 KiB
C++
/*************************************************************************
|
|
* *
|
|
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
|
|
* All rights reserved. Email: russ@q12.org Web: www.q12.org *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of EITHER: *
|
|
* (1) The GNU Lesser General Public License as published by the Free *
|
|
* Software Foundation; either version 2.1 of the License, or (at *
|
|
* your option) any later version. The text of the GNU Lesser *
|
|
* General Public License is included with this library in the *
|
|
* file LICENSE.TXT. *
|
|
* (2) The BSD-style license that is included with this library in *
|
|
* the file LICENSE-BSD.TXT. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
|
|
* LICENSE.TXT and LICENSE-BSD.TXT for more details. *
|
|
* *
|
|
*************************************************************************/
|
|
|
|
|
|
/*************************************************************************
|
|
* *
|
|
* Triangle-box collider by Alen Ladavac and Vedran Klanac. *
|
|
* Ported to ODE by Oskari Nyman. *
|
|
* *
|
|
*************************************************************************/
|
|
|
|
|
|
#include <ode/collision.h>
|
|
#include <ode/matrix.h>
|
|
#include <ode/rotation.h>
|
|
#include <ode/odemath.h>
|
|
#include "collision_util.h"
|
|
|
|
#define TRIMESH_INTERNAL
|
|
#include "collision_trimesh_internal.h"
|
|
|
|
#if dTRIMESH_ENABLED
|
|
|
|
|
|
static void
|
|
GenerateContact(int in_Flags, dContactGeom* in_Contacts, int in_Stride,
|
|
dxGeom* in_g1, dxGeom* in_g2,
|
|
const dVector3 in_ContactPos, const dVector3 in_Normal, dReal in_Depth,
|
|
int& OutTriCount);
|
|
|
|
|
|
// largest number, double or float
|
|
#if defined(dSINGLE)
|
|
#define MAXVALUE FLT_MAX
|
|
#else
|
|
#define MAXVALUE DBL_MAX
|
|
#endif
|
|
|
|
|
|
// dVector3
|
|
// r=a-b
|
|
#define SUBTRACT(a,b,r) do{ \
|
|
(r)[0]=(a)[0] - (b)[0]; \
|
|
(r)[1]=(a)[1] - (b)[1]; \
|
|
(r)[2]=(a)[2] - (b)[2]; }while(0)
|
|
|
|
|
|
// dVector3
|
|
// a=b
|
|
#define SET(a,b) do{ \
|
|
(a)[0]=(b)[0]; \
|
|
(a)[1]=(b)[1]; \
|
|
(a)[2]=(b)[2]; }while(0)
|
|
|
|
|
|
// dMatrix3
|
|
// a=b
|
|
#define SETM(a,b) do{ \
|
|
(a)[0]=(b)[0]; \
|
|
(a)[1]=(b)[1]; \
|
|
(a)[2]=(b)[2]; \
|
|
(a)[3]=(b)[3]; \
|
|
(a)[4]=(b)[4]; \
|
|
(a)[5]=(b)[5]; \
|
|
(a)[6]=(b)[6]; \
|
|
(a)[7]=(b)[7]; \
|
|
(a)[8]=(b)[8]; \
|
|
(a)[9]=(b)[9]; \
|
|
(a)[10]=(b)[10]; \
|
|
(a)[11]=(b)[11]; }while(0)
|
|
|
|
|
|
// dVector3
|
|
// r=a+b
|
|
#define ADD(a,b,r) do{ \
|
|
(r)[0]=(a)[0] + (b)[0]; \
|
|
(r)[1]=(a)[1] + (b)[1]; \
|
|
(r)[2]=(a)[2] + (b)[2]; }while(0)
|
|
|
|
|
|
// dMatrix3, int, dVector3
|
|
// v=column a from m
|
|
#define GETCOL(m,a,v) do{ \
|
|
(v)[0]=(m)[(a)+0]; \
|
|
(v)[1]=(m)[(a)+4]; \
|
|
(v)[2]=(m)[(a)+8]; }while(0)
|
|
|
|
|
|
// dVector4, dVector3
|
|
// distance between plane p and point v
|
|
#define POINTDISTANCE(p,v) \
|
|
( p[0]*v[0] + p[1]*v[1] + p[2]*v[2] + p[3] )
|
|
|
|
|
|
// dVector4, dVector3, dReal
|
|
// construct plane from normal and d
|
|
#define CONSTRUCTPLANE(plane,normal,d) do{ \
|
|
plane[0]=normal[0];\
|
|
plane[1]=normal[1];\
|
|
plane[2]=normal[2];\
|
|
plane[3]=d; }while(0)
|
|
|
|
|
|
// dVector3
|
|
// length of vector a
|
|
#define LENGTHOF(a) \
|
|
dSqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2])
|
|
|
|
|
|
// box data
|
|
static dMatrix3 mHullBoxRot;
|
|
static dVector3 vHullBoxPos;
|
|
static dVector3 vBoxHalfSize;
|
|
|
|
// mesh data
|
|
static dVector3 vHullDstPos;
|
|
|
|
// global collider data
|
|
static dVector3 vBestNormal;
|
|
static dReal fBestDepth;
|
|
static int iBestAxis = 0;
|
|
static int iExitAxis = 0;
|
|
static dVector3 vE0, vE1, vE2, vN;
|
|
|
|
// global info for contact creation
|
|
static int iFlags;
|
|
static dContactGeom *ContactGeoms;
|
|
static int iStride;
|
|
static dxGeom *Geom1;
|
|
static dxGeom *Geom2;
|
|
static int ctContacts = 0;
|
|
|
|
|
|
|
|
// Test normal of mesh face as separating axis for intersection
|
|
static bool _cldTestNormal( dReal fp0, dReal fR, dVector3 vNormal, int iAxis )
|
|
{
|
|
// calculate overlapping interval of box and triangle
|
|
dReal fDepth = fR+fp0;
|
|
|
|
// if we do not overlap
|
|
if ( fDepth<0 ) {
|
|
// do nothing
|
|
return false;
|
|
}
|
|
|
|
// calculate normal's length
|
|
dReal fLength = LENGTHOF(vNormal);
|
|
// if long enough
|
|
if ( fLength > 0.0f ) {
|
|
|
|
dReal fOneOverLength = 1.0f/fLength;
|
|
// normalize depth
|
|
fDepth = fDepth*fOneOverLength;
|
|
|
|
// get minimum depth
|
|
if (fDepth<fBestDepth) {
|
|
vBestNormal[0] = -vNormal[0]*fOneOverLength;
|
|
vBestNormal[1] = -vNormal[1]*fOneOverLength;
|
|
vBestNormal[2] = -vNormal[2]*fOneOverLength;
|
|
iBestAxis = iAxis;
|
|
//dAASSERT(fDepth>=0);
|
|
fBestDepth = fDepth;
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test box axis as separating axis
|
|
static bool _cldTestFace( dReal fp0, dReal fp1, dReal fp2, dReal fR, dReal fD,
|
|
dVector3 vNormal, int iAxis )
|
|
{
|
|
dReal fMin, fMax;
|
|
|
|
// find min of triangle interval
|
|
if ( fp0 < fp1 ) {
|
|
if ( fp0 < fp2 ) {
|
|
fMin = fp0;
|
|
} else {
|
|
fMin = fp2;
|
|
}
|
|
} else {
|
|
if( fp1 < fp2 ) {
|
|
fMin = fp1;
|
|
} else {
|
|
fMin = fp2;
|
|
}
|
|
}
|
|
|
|
// find max of triangle interval
|
|
if ( fp0 > fp1 ) {
|
|
if ( fp0 > fp2 ) {
|
|
fMax = fp0;
|
|
} else {
|
|
fMax = fp2;
|
|
}
|
|
} else {
|
|
if( fp1 > fp2 ) {
|
|
fMax = fp1;
|
|
} else {
|
|
fMax = fp2;
|
|
}
|
|
}
|
|
|
|
// calculate minimum and maximum depth
|
|
dReal fDepthMin = fR - fMin;
|
|
dReal fDepthMax = fMax + fR;
|
|
|
|
// if we dont't have overlapping interval
|
|
if ( fDepthMin < 0 || fDepthMax < 0 ) {
|
|
// do nothing
|
|
return false;
|
|
}
|
|
|
|
dReal fDepth = 0;
|
|
|
|
// if greater depth is on negative side
|
|
if ( fDepthMin > fDepthMax ) {
|
|
// use smaller depth (one from positive side)
|
|
fDepth = fDepthMax;
|
|
// flip normal direction
|
|
vNormal[0] = -vNormal[0];
|
|
vNormal[1] = -vNormal[1];
|
|
vNormal[2] = -vNormal[2];
|
|
fD = -fD;
|
|
// if greater depth is on positive side
|
|
} else {
|
|
// use smaller depth (one from negative side)
|
|
fDepth = fDepthMin;
|
|
}
|
|
|
|
|
|
// if lower depth than best found so far
|
|
if (fDepth<fBestDepth) {
|
|
// remember current axis as best axis
|
|
vBestNormal[0] = vNormal[0];
|
|
vBestNormal[1] = vNormal[1];
|
|
vBestNormal[2] = vNormal[2];
|
|
iBestAxis = iAxis;
|
|
//dAASSERT(fDepth>=0);
|
|
fBestDepth = fDepth;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test cross products of box axis and triangle edges as separating axis
|
|
static bool _cldTestEdge( dReal fp0, dReal fp1, dReal fR, dReal fD,
|
|
dVector3 vNormal, int iAxis )
|
|
{
|
|
dReal fMin, fMax;
|
|
|
|
|
|
// ===== Begin Patch by Francisco Leon, 2006/10/28 =====
|
|
|
|
// Fixed Null Normal. This prevents boxes passing
|
|
// through trimeshes at certain contact angles
|
|
|
|
fMin = vNormal[0] * vNormal[0] +
|
|
vNormal[1] * vNormal[1] +
|
|
vNormal[2] * vNormal[2];
|
|
|
|
if ( fMin <= dEpsilon ) /// THIS NORMAL WOULD BE DANGEROUS
|
|
return true;
|
|
|
|
// ===== Ending Patch by Francisco Leon =====
|
|
|
|
|
|
// calculate min and max interval values
|
|
if ( fp0 < fp1 ) {
|
|
fMin = fp0;
|
|
fMax = fp1;
|
|
} else {
|
|
fMin = fp1;
|
|
fMax = fp0;
|
|
}
|
|
|
|
// check if we overlapp
|
|
dReal fDepthMin = fR - fMin;
|
|
dReal fDepthMax = fMax + fR;
|
|
|
|
// if we don't overlapp
|
|
if ( fDepthMin < 0 || fDepthMax < 0 ) {
|
|
// do nothing
|
|
return false;
|
|
}
|
|
|
|
dReal fDepth;
|
|
|
|
|
|
// if greater depth is on negative side
|
|
if ( fDepthMin > fDepthMax ) {
|
|
// use smaller depth (one from positive side)
|
|
fDepth = fDepthMax;
|
|
// flip normal direction
|
|
vNormal[0] = -vNormal[0];
|
|
vNormal[1] = -vNormal[1];
|
|
vNormal[2] = -vNormal[2];
|
|
fD = -fD;
|
|
// if greater depth is on positive side
|
|
} else {
|
|
// use smaller depth (one from negative side)
|
|
fDepth = fDepthMin;
|
|
}
|
|
|
|
// calculate normal's length
|
|
dReal fLength = LENGTHOF(vNormal);
|
|
|
|
// if long enough
|
|
if ( fLength > 0.0f ) {
|
|
|
|
// normalize depth
|
|
dReal fOneOverLength = 1.0f/fLength;
|
|
fDepth = fDepth*fOneOverLength;
|
|
fD*=fOneOverLength;
|
|
|
|
|
|
// if lower depth than best found so far (favor face over edges)
|
|
if (fDepth*1.5f<fBestDepth) {
|
|
// remember current axis as best axis
|
|
vBestNormal[0] = vNormal[0]*fOneOverLength;
|
|
vBestNormal[1] = vNormal[1]*fOneOverLength;
|
|
vBestNormal[2] = vNormal[2]*fOneOverLength;
|
|
iBestAxis = iAxis;
|
|
//dAASSERT(fDepth>=0);
|
|
fBestDepth = fDepth;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// clip polygon with plane and generate new polygon points
|
|
static void _cldClipPolyToPlane( dVector3 avArrayIn[], int ctIn,
|
|
dVector3 avArrayOut[], int &ctOut,
|
|
const dVector4 &plPlane )
|
|
{
|
|
// start with no output points
|
|
ctOut = 0;
|
|
|
|
int i0 = ctIn-1;
|
|
|
|
// for each edge in input polygon
|
|
for (int i1=0; i1<ctIn; i0=i1, i1++) {
|
|
|
|
|
|
// calculate distance of edge points to plane
|
|
dReal fDistance0 = POINTDISTANCE( plPlane ,avArrayIn[i0] );
|
|
dReal fDistance1 = POINTDISTANCE( plPlane ,avArrayIn[i1] );
|
|
|
|
|
|
// if first point is in front of plane
|
|
if( fDistance0 >= 0 ) {
|
|
// emit point
|
|
avArrayOut[ctOut][0] = avArrayIn[i0][0];
|
|
avArrayOut[ctOut][1] = avArrayIn[i0][1];
|
|
avArrayOut[ctOut][2] = avArrayIn[i0][2];
|
|
ctOut++;
|
|
}
|
|
|
|
// if points are on different sides
|
|
if( (fDistance0 > 0 && fDistance1 < 0) || ( fDistance0 < 0 && fDistance1 > 0) ) {
|
|
|
|
// find intersection point of edge and plane
|
|
dVector3 vIntersectionPoint;
|
|
vIntersectionPoint[0]= avArrayIn[i0][0] - (avArrayIn[i0][0]-avArrayIn[i1][0])*fDistance0/(fDistance0-fDistance1);
|
|
vIntersectionPoint[1]= avArrayIn[i0][1] - (avArrayIn[i0][1]-avArrayIn[i1][1])*fDistance0/(fDistance0-fDistance1);
|
|
vIntersectionPoint[2]= avArrayIn[i0][2] - (avArrayIn[i0][2]-avArrayIn[i1][2])*fDistance0/(fDistance0-fDistance1);
|
|
|
|
// emit intersection point
|
|
avArrayOut[ctOut][0] = vIntersectionPoint[0];
|
|
avArrayOut[ctOut][1] = vIntersectionPoint[1];
|
|
avArrayOut[ctOut][2] = vIntersectionPoint[2];
|
|
ctOut++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool _cldTestSeparatingAxes(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2) {
|
|
// reset best axis
|
|
iBestAxis = 0;
|
|
iExitAxis = -1;
|
|
fBestDepth = MAXVALUE;
|
|
|
|
// calculate edges
|
|
SUBTRACT(v1,v0,vE0);
|
|
SUBTRACT(v2,v0,vE1);
|
|
SUBTRACT(vE1,vE0,vE2);
|
|
|
|
// calculate poly normal
|
|
dCROSS(vN,=,vE0,vE1);
|
|
|
|
// extract box axes as vectors
|
|
dVector3 vA0,vA1,vA2;
|
|
GETCOL(mHullBoxRot,0,vA0);
|
|
GETCOL(mHullBoxRot,1,vA1);
|
|
GETCOL(mHullBoxRot,2,vA2);
|
|
|
|
// box halfsizes
|
|
dReal fa0 = vBoxHalfSize[0];
|
|
dReal fa1 = vBoxHalfSize[1];
|
|
dReal fa2 = vBoxHalfSize[2];
|
|
|
|
// calculate relative position between box and triangle
|
|
dVector3 vD;
|
|
SUBTRACT(v0,vHullBoxPos,vD);
|
|
|
|
// calculate length of face normal
|
|
dReal fNLen = LENGTHOF( vN );
|
|
|
|
dVector3 vL;
|
|
dReal fp0, fp1, fp2, fR, fD;
|
|
|
|
// Test separating axes for intersection
|
|
// ************************************************
|
|
// Axis 1 - Triangle Normal
|
|
SET(vL,vN);
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0;
|
|
fp2 = fp0;
|
|
fR=fa0*dFabs( dDOT(vN,vA0) ) + fa1 * dFabs( dDOT(vN,vA1) ) + fa2 * dFabs( dDOT(vN,vA2) );
|
|
|
|
|
|
if( !_cldTestNormal( fp0, fR, vL, 1) ) {
|
|
iExitAxis=1;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// Test Faces
|
|
// ************************************************
|
|
// Axis 2 - Box X-Axis
|
|
SET(vL,vA0);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 + dDOT(vA0,vE0);
|
|
fp2 = fp0 + dDOT(vA0,vE1);
|
|
fR = fa0;
|
|
|
|
|
|
if( !_cldTestFace( fp0, fp1, fp2, fR, fD, vL, 2) ) {
|
|
iExitAxis=2;
|
|
return false;
|
|
}
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 3 - Box Y-Axis
|
|
SET(vL,vA1);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 + dDOT(vA1,vE0);
|
|
fp2 = fp0 + dDOT(vA1,vE1);
|
|
fR = fa1;
|
|
|
|
|
|
if( !_cldTestFace( fp0, fp1, fp2, fR, fD, vL, 3) ) {
|
|
iExitAxis=3;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 4 - Box Z-Axis
|
|
SET(vL,vA2);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 + dDOT(vA2,vE0);
|
|
fp2 = fp0 + dDOT(vA2,vE1);
|
|
fR = fa2;
|
|
|
|
|
|
if( !_cldTestFace( fp0, fp1, fp2, fR, fD, vL, 4) ) {
|
|
iExitAxis=4;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// Test Edges
|
|
// ************************************************
|
|
// Axis 5 - Box X-Axis cross Edge0
|
|
dCROSS(vL,=,vA0,vE0);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0;
|
|
fp2 = fp0 + dDOT(vA0,vN);
|
|
fR = fa1 * dFabs(dDOT(vA2,vE0)) + fa2 * dFabs(dDOT(vA1,vE0));
|
|
|
|
|
|
if( !_cldTestEdge( fp1, fp2, fR, fD, vL, 5) ) {
|
|
iExitAxis=5;
|
|
return false;
|
|
}
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 6 - Box X-Axis cross Edge1
|
|
dCROSS(vL,=,vA0,vE1);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 - dDOT(vA0,vN);
|
|
fp2 = fp0;
|
|
fR = fa1 * dFabs(dDOT(vA2,vE1)) + fa2 * dFabs(dDOT(vA1,vE1));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp1, fR, fD, vL, 6) ) {
|
|
iExitAxis=6;
|
|
return false;
|
|
}
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 7 - Box X-Axis cross Edge2
|
|
dCROSS(vL,=,vA0,vE2);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 - dDOT(vA0,vN);
|
|
fp2 = fp0 - dDOT(vA0,vN);
|
|
fR = fa1 * dFabs(dDOT(vA2,vE2)) + fa2 * dFabs(dDOT(vA1,vE2));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp1, fR, fD, vL, 7) ) {
|
|
iExitAxis=7;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 8 - Box Y-Axis cross Edge0
|
|
dCROSS(vL,=,vA1,vE0);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0;
|
|
fp2 = fp0 + dDOT(vA1,vN);
|
|
fR = fa0 * dFabs(dDOT(vA2,vE0)) + fa2 * dFabs(dDOT(vA0,vE0));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp2, fR, fD, vL, 8) ) {
|
|
iExitAxis=8;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 9 - Box Y-Axis cross Edge1
|
|
dCROSS(vL,=,vA1,vE1);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 - dDOT(vA1,vN);
|
|
fp2 = fp0;
|
|
fR = fa0 * dFabs(dDOT(vA2,vE1)) + fa2 * dFabs(dDOT(vA0,vE1));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp1, fR, fD, vL, 9) ) {
|
|
iExitAxis=9;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 10 - Box Y-Axis cross Edge2
|
|
dCROSS(vL,=,vA1,vE2);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 - dDOT(vA1,vN);
|
|
fp2 = fp0 - dDOT(vA1,vN);
|
|
fR = fa0 * dFabs(dDOT(vA2,vE2)) + fa2 * dFabs(dDOT(vA0,vE2));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp1, fR, fD, vL, 10) ) {
|
|
iExitAxis=10;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 11 - Box Z-Axis cross Edge0
|
|
dCROSS(vL,=,vA2,vE0);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0;
|
|
fp2 = fp0 + dDOT(vA2,vN);
|
|
fR = fa0 * dFabs(dDOT(vA1,vE0)) + fa1 * dFabs(dDOT(vA0,vE0));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp2, fR, fD, vL, 11) ) {
|
|
iExitAxis=11;
|
|
return false;
|
|
}
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 12 - Box Z-Axis cross Edge1
|
|
dCROSS(vL,=,vA2,vE1);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 - dDOT(vA2,vN);
|
|
fp2 = fp0;
|
|
fR = fa0 * dFabs(dDOT(vA1,vE1)) + fa1 * dFabs(dDOT(vA0,vE1));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp1, fR, fD, vL, 12) ) {
|
|
iExitAxis=12;
|
|
return false;
|
|
}
|
|
// ************************************************
|
|
|
|
// ************************************************
|
|
// Axis 13 - Box Z-Axis cross Edge2
|
|
dCROSS(vL,=,vA2,vE2);
|
|
fD = dDOT(vL,vN)/fNLen;
|
|
fp0 = dDOT(vL,vD);
|
|
fp1 = fp0 - dDOT(vA2,vN);
|
|
fp2 = fp0 - dDOT(vA2,vN);
|
|
fR = fa0 * dFabs(dDOT(vA1,vE2)) + fa1 * dFabs(dDOT(vA0,vE2));
|
|
|
|
|
|
if( !_cldTestEdge( fp0, fp1, fR, fD, vL, 13) ) {
|
|
iExitAxis=13;
|
|
return false;
|
|
}
|
|
|
|
// ************************************************
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// find two closest points on two lines
|
|
static bool _cldClosestPointOnTwoLines( dVector3 vPoint1, dVector3 vLenVec1,
|
|
dVector3 vPoint2, dVector3 vLenVec2,
|
|
dReal &fvalue1, dReal &fvalue2)
|
|
{
|
|
// calulate denominator
|
|
dVector3 vp;
|
|
SUBTRACT(vPoint2,vPoint1,vp);
|
|
dReal fuaub = dDOT(vLenVec1,vLenVec2);
|
|
dReal fq1 = dDOT(vLenVec1,vp);
|
|
dReal fq2 = -dDOT(vLenVec2,vp);
|
|
dReal fd = 1.0f - fuaub * fuaub;
|
|
|
|
// if denominator is positive
|
|
if (fd > 0.0f) {
|
|
// calculate points of closest approach
|
|
fd = 1.0f/fd;
|
|
fvalue1 = (fq1 + fuaub*fq2)*fd;
|
|
fvalue2 = (fuaub*fq1 + fq2)*fd;
|
|
return true;
|
|
// otherwise
|
|
} else {
|
|
// lines are parallel
|
|
fvalue1 = 0.0f;
|
|
fvalue2 = 0.0f;
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// clip and generate contacts
|
|
static void _cldClipping(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2) {
|
|
dIASSERT( !(iFlags & CONTACTS_UNIMPORTANT) || ctContacts < (iFlags & NUMC_MASK) ); // Do not call the function if there is no room to store results
|
|
|
|
// if we have edge/edge intersection
|
|
if ( iBestAxis > 4 ) {
|
|
|
|
dVector3 vub,vPb,vPa;
|
|
|
|
SET(vPa,vHullBoxPos);
|
|
|
|
// calculate point on box edge
|
|
for( int i=0; i<3; i++) {
|
|
dVector3 vRotCol;
|
|
GETCOL(mHullBoxRot,i,vRotCol);
|
|
dReal fSign = dDOT(vBestNormal,vRotCol) > 0 ? 1.0f : -1.0f;
|
|
|
|
vPa[0] += fSign * vBoxHalfSize[i] * vRotCol[0];
|
|
vPa[1] += fSign * vBoxHalfSize[i] * vRotCol[1];
|
|
vPa[2] += fSign * vBoxHalfSize[i] * vRotCol[2];
|
|
}
|
|
|
|
int iEdge = (iBestAxis-5)%3;
|
|
|
|
// decide which edge is on triangle
|
|
if ( iEdge == 0 ) {
|
|
SET(vPb,v0);
|
|
SET(vub,vE0);
|
|
} else if ( iEdge == 1) {
|
|
SET(vPb,v2);
|
|
SET(vub,vE1);
|
|
} else {
|
|
SET(vPb,v1);
|
|
SET(vub,vE2);
|
|
}
|
|
|
|
|
|
// setup direction parameter for face edge
|
|
dNormalize3(vub);
|
|
|
|
dReal fParam1, fParam2;
|
|
|
|
// setup direction parameter for box edge
|
|
dVector3 vua;
|
|
int col=(iBestAxis-5)/3;
|
|
GETCOL(mHullBoxRot,col,vua);
|
|
|
|
// find two closest points on both edges
|
|
_cldClosestPointOnTwoLines( vPa, vua, vPb, vub, fParam1, fParam2 );
|
|
vPa[0] += vua[0]*fParam1;
|
|
vPa[1] += vua[1]*fParam1;
|
|
vPa[2] += vua[2]*fParam1;
|
|
|
|
vPb[0] += vub[0]*fParam2;
|
|
vPb[1] += vub[1]*fParam2;
|
|
vPb[2] += vub[2]*fParam2;
|
|
|
|
// calculate collision point
|
|
dVector3 vPntTmp;
|
|
ADD(vPa,vPb,vPntTmp);
|
|
|
|
vPntTmp[0]*=0.5f;
|
|
vPntTmp[1]*=0.5f;
|
|
vPntTmp[2]*=0.5f;
|
|
|
|
// generate contact point between two closest points
|
|
#if 0 //#ifdef ORIG -- if to use conditional define, GenerateContact must be moved into #else
|
|
dContactGeom* Contact = SAFECONTACT(iFlags, ContactGeoms, ctContacts, iStride);
|
|
Contact->depth = fBestDepth;
|
|
SET(Contact->normal,vBestNormal);
|
|
SET(Contact->pos,vPntTmp);
|
|
Contact->g1 = Geom1;
|
|
Contact->g2 = Geom2;
|
|
ctContacts++;
|
|
#endif
|
|
GenerateContact(iFlags, ContactGeoms, iStride, Geom1, Geom2,
|
|
vPntTmp, vBestNormal, fBestDepth, ctContacts);
|
|
|
|
|
|
// if triangle is the referent face then clip box to triangle face
|
|
} else if ( iBestAxis == 1 ) {
|
|
|
|
|
|
dVector3 vNormal2;
|
|
vNormal2[0]=-vBestNormal[0];
|
|
vNormal2[1]=-vBestNormal[1];
|
|
vNormal2[2]=-vBestNormal[2];
|
|
|
|
|
|
// vNr is normal in box frame, pointing from triangle to box
|
|
dMatrix3 mTransposed;
|
|
mTransposed[0*4+0]=mHullBoxRot[0*4+0];
|
|
mTransposed[0*4+1]=mHullBoxRot[1*4+0];
|
|
mTransposed[0*4+2]=mHullBoxRot[2*4+0];
|
|
|
|
mTransposed[1*4+0]=mHullBoxRot[0*4+1];
|
|
mTransposed[1*4+1]=mHullBoxRot[1*4+1];
|
|
mTransposed[1*4+2]=mHullBoxRot[2*4+1];
|
|
|
|
mTransposed[2*4+0]=mHullBoxRot[0*4+2];
|
|
mTransposed[2*4+1]=mHullBoxRot[1*4+2];
|
|
mTransposed[2*4+2]=mHullBoxRot[2*4+2];
|
|
|
|
dVector3 vNr;
|
|
vNr[0]=mTransposed[0*4+0]*vNormal2[0]+ mTransposed[0*4+1]*vNormal2[1]+ mTransposed[0*4+2]*vNormal2[2];
|
|
vNr[1]=mTransposed[1*4+0]*vNormal2[0]+ mTransposed[1*4+1]*vNormal2[1]+ mTransposed[1*4+2]*vNormal2[2];
|
|
vNr[2]=mTransposed[2*4+0]*vNormal2[0]+ mTransposed[2*4+1]*vNormal2[1]+ mTransposed[2*4+2]*vNormal2[2];
|
|
|
|
|
|
dVector3 vAbsNormal;
|
|
vAbsNormal[0] = dFabs( vNr[0] );
|
|
vAbsNormal[1] = dFabs( vNr[1] );
|
|
vAbsNormal[2] = dFabs( vNr[2] );
|
|
|
|
// get closest face from box
|
|
int iB0, iB1, iB2;
|
|
if (vAbsNormal[1] > vAbsNormal[0]) {
|
|
if (vAbsNormal[1] > vAbsNormal[2]) {
|
|
iB1 = 0; iB0 = 1; iB2 = 2;
|
|
} else {
|
|
iB1 = 0; iB2 = 1; iB0 = 2;
|
|
}
|
|
} else {
|
|
|
|
if (vAbsNormal[0] > vAbsNormal[2]) {
|
|
iB0 = 0; iB1 = 1; iB2 = 2;
|
|
} else {
|
|
iB1 = 0; iB2 = 1; iB0 = 2;
|
|
}
|
|
}
|
|
|
|
// Here find center of box face we are going to project
|
|
dVector3 vCenter;
|
|
dVector3 vRotCol;
|
|
GETCOL(mHullBoxRot,iB0,vRotCol);
|
|
|
|
if (vNr[iB0] > 0) {
|
|
vCenter[0] = vHullBoxPos[0] - v0[0] - vBoxHalfSize[iB0] * vRotCol[0];
|
|
vCenter[1] = vHullBoxPos[1] - v0[1] - vBoxHalfSize[iB0] * vRotCol[1];
|
|
vCenter[2] = vHullBoxPos[2] - v0[2] - vBoxHalfSize[iB0] * vRotCol[2];
|
|
} else {
|
|
vCenter[0] = vHullBoxPos[0] - v0[0] + vBoxHalfSize[iB0] * vRotCol[0];
|
|
vCenter[1] = vHullBoxPos[1] - v0[1] + vBoxHalfSize[iB0] * vRotCol[1];
|
|
vCenter[2] = vHullBoxPos[2] - v0[2] + vBoxHalfSize[iB0] * vRotCol[2];
|
|
}
|
|
|
|
// Here find 4 corner points of box
|
|
dVector3 avPoints[4];
|
|
|
|
dVector3 vRotCol2;
|
|
GETCOL(mHullBoxRot,iB1,vRotCol);
|
|
GETCOL(mHullBoxRot,iB2,vRotCol2);
|
|
|
|
for(int x=0;x<3;x++) {
|
|
avPoints[0][x] = vCenter[x] + (vBoxHalfSize[iB1] * vRotCol[x]) - (vBoxHalfSize[iB2] * vRotCol2[x]);
|
|
avPoints[1][x] = vCenter[x] - (vBoxHalfSize[iB1] * vRotCol[x]) - (vBoxHalfSize[iB2] * vRotCol2[x]);
|
|
avPoints[2][x] = vCenter[x] - (vBoxHalfSize[iB1] * vRotCol[x]) + (vBoxHalfSize[iB2] * vRotCol2[x]);
|
|
avPoints[3][x] = vCenter[x] + (vBoxHalfSize[iB1] * vRotCol[x]) + (vBoxHalfSize[iB2] * vRotCol2[x]);
|
|
}
|
|
|
|
|
|
// clip Box face with 4 planes of triangle (1 face plane, 3 egde planes)
|
|
dVector3 avTempArray1[9];
|
|
dVector3 avTempArray2[9];
|
|
dVector4 plPlane;
|
|
|
|
int iTempCnt1=0;
|
|
int iTempCnt2=0;
|
|
|
|
// zeroify vectors - necessary?
|
|
for(int i=0; i<9; i++) {
|
|
avTempArray1[i][0]=0;
|
|
avTempArray1[i][1]=0;
|
|
avTempArray1[i][2]=0;
|
|
|
|
avTempArray2[i][0]=0;
|
|
avTempArray2[i][1]=0;
|
|
avTempArray2[i][2]=0;
|
|
}
|
|
|
|
|
|
// Normal plane
|
|
dVector3 vTemp;
|
|
vTemp[0]=-vN[0];
|
|
vTemp[1]=-vN[1];
|
|
vTemp[2]=-vN[2];
|
|
dNormalize3(vTemp);
|
|
CONSTRUCTPLANE(plPlane,vTemp,0);
|
|
|
|
_cldClipPolyToPlane( avPoints, 4, avTempArray1, iTempCnt1, plPlane );
|
|
|
|
|
|
// Plane p0
|
|
dVector3 vTemp2;
|
|
SUBTRACT(v1,v0,vTemp2);
|
|
dCROSS(vTemp,=,vN,vTemp2);
|
|
dNormalize3(vTemp);
|
|
CONSTRUCTPLANE(plPlane,vTemp,0);
|
|
|
|
_cldClipPolyToPlane( avTempArray1, iTempCnt1, avTempArray2, iTempCnt2, plPlane );
|
|
|
|
|
|
// Plane p1
|
|
SUBTRACT(v2,v1,vTemp2);
|
|
dCROSS(vTemp,=,vN,vTemp2);
|
|
dNormalize3(vTemp);
|
|
SUBTRACT(v0,v2,vTemp2);
|
|
CONSTRUCTPLANE(plPlane,vTemp,dDOT(vTemp2,vTemp));
|
|
|
|
_cldClipPolyToPlane( avTempArray2, iTempCnt2, avTempArray1, iTempCnt1, plPlane );
|
|
|
|
|
|
// Plane p2
|
|
SUBTRACT(v0,v2,vTemp2);
|
|
dCROSS(vTemp,=,vN,vTemp2);
|
|
dNormalize3(vTemp);
|
|
CONSTRUCTPLANE(plPlane,vTemp,0);
|
|
|
|
_cldClipPolyToPlane( avTempArray1, iTempCnt1, avTempArray2, iTempCnt2, plPlane );
|
|
|
|
|
|
// END of clipping polygons
|
|
|
|
|
|
|
|
// for each generated contact point
|
|
for ( int i=0; i<iTempCnt2; i++ ) {
|
|
// calculate depth
|
|
dReal fTempDepth = dDOT(vNormal2,avTempArray2[i]);
|
|
|
|
// clamp depth to zero
|
|
if (fTempDepth > 0) {
|
|
fTempDepth = 0;
|
|
}
|
|
|
|
dVector3 vPntTmp;
|
|
ADD(avTempArray2[i],v0,vPntTmp);
|
|
|
|
#if 0 //#ifdef ORIG -- if to use conditional define, GenerateContact must be moved into #else
|
|
dContactGeom* Contact = SAFECONTACT(iFlags, ContactGeoms, ctContacts, iStride);
|
|
|
|
Contact->depth = -fTempDepth;
|
|
SET(Contact->normal,vBestNormal);
|
|
SET(Contact->pos,vPntTmp);
|
|
Contact->g1 = Geom1;
|
|
Contact->g2 = Geom2;
|
|
ctContacts++;
|
|
#endif
|
|
GenerateContact(iFlags, ContactGeoms, iStride, Geom1, Geom2,
|
|
vPntTmp, vBestNormal, -fTempDepth, ctContacts);
|
|
|
|
if ((ctContacts | CONTACTS_UNIMPORTANT) == (iFlags & (NUMC_MASK | CONTACTS_UNIMPORTANT))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//dAASSERT(ctContacts>0);
|
|
|
|
// if box face is the referent face, then clip triangle on box face
|
|
} else { // 2 <= if iBestAxis <= 4
|
|
|
|
// get normal of box face
|
|
dVector3 vNormal2;
|
|
SET(vNormal2,vBestNormal);
|
|
|
|
// get indices of box axes in correct order
|
|
int iA0,iA1,iA2;
|
|
iA0 = iBestAxis-2;
|
|
if ( iA0 == 0 ) {
|
|
iA1 = 1; iA2 = 2;
|
|
} else if ( iA0 == 1 ) {
|
|
iA1 = 0; iA2 = 2;
|
|
} else {
|
|
iA1 = 0; iA2 = 1;
|
|
}
|
|
|
|
dVector3 avPoints[3];
|
|
// calculate triangle vertices in box frame
|
|
SUBTRACT(v0,vHullBoxPos,avPoints[0]);
|
|
SUBTRACT(v1,vHullBoxPos,avPoints[1]);
|
|
SUBTRACT(v2,vHullBoxPos,avPoints[2]);
|
|
|
|
// CLIP Polygons
|
|
// define temp data for clipping
|
|
dVector3 avTempArray1[9];
|
|
dVector3 avTempArray2[9];
|
|
|
|
int iTempCnt1, iTempCnt2;
|
|
|
|
// zeroify vectors - necessary?
|
|
for(int i=0; i<9; i++) {
|
|
avTempArray1[i][0]=0;
|
|
avTempArray1[i][1]=0;
|
|
avTempArray1[i][2]=0;
|
|
|
|
avTempArray2[i][0]=0;
|
|
avTempArray2[i][1]=0;
|
|
avTempArray2[i][2]=0;
|
|
}
|
|
|
|
// clip triangle with 5 box planes (1 face plane, 4 edge planes)
|
|
|
|
dVector4 plPlane;
|
|
|
|
// Normal plane
|
|
dVector3 vTemp;
|
|
vTemp[0]=-vNormal2[0];
|
|
vTemp[1]=-vNormal2[1];
|
|
vTemp[2]=-vNormal2[2];
|
|
CONSTRUCTPLANE(plPlane,vTemp,vBoxHalfSize[iA0]);
|
|
|
|
_cldClipPolyToPlane( avPoints, 3, avTempArray1, iTempCnt1, plPlane );
|
|
|
|
|
|
// Plane p0
|
|
GETCOL(mHullBoxRot,iA1,vTemp);
|
|
CONSTRUCTPLANE(plPlane,vTemp,vBoxHalfSize[iA1]);
|
|
|
|
_cldClipPolyToPlane( avTempArray1, iTempCnt1, avTempArray2, iTempCnt2, plPlane );
|
|
|
|
|
|
// Plane p1
|
|
GETCOL(mHullBoxRot,iA1,vTemp);
|
|
vTemp[0]=-vTemp[0];
|
|
vTemp[1]=-vTemp[1];
|
|
vTemp[2]=-vTemp[2];
|
|
CONSTRUCTPLANE(plPlane,vTemp,vBoxHalfSize[iA1]);
|
|
|
|
_cldClipPolyToPlane( avTempArray2, iTempCnt2, avTempArray1, iTempCnt1, plPlane );
|
|
|
|
|
|
// Plane p2
|
|
GETCOL(mHullBoxRot,iA2,vTemp);
|
|
CONSTRUCTPLANE(plPlane,vTemp,vBoxHalfSize[iA2]);
|
|
|
|
_cldClipPolyToPlane( avTempArray1, iTempCnt1, avTempArray2, iTempCnt2, plPlane );
|
|
|
|
|
|
// Plane p3
|
|
GETCOL(mHullBoxRot,iA2,vTemp);
|
|
vTemp[0]=-vTemp[0];
|
|
vTemp[1]=-vTemp[1];
|
|
vTemp[2]=-vTemp[2];
|
|
CONSTRUCTPLANE(plPlane,vTemp,vBoxHalfSize[iA2]);
|
|
|
|
_cldClipPolyToPlane( avTempArray2, iTempCnt2, avTempArray1, iTempCnt1, plPlane );
|
|
|
|
|
|
// for each generated contact point
|
|
for ( int i=0; i<iTempCnt1; i++ ) {
|
|
// calculate depth
|
|
dReal fTempDepth = dDOT(vNormal2,avTempArray1[i])-vBoxHalfSize[iA0];
|
|
|
|
// clamp depth to zero
|
|
if (fTempDepth > 0) {
|
|
fTempDepth = 0;
|
|
}
|
|
|
|
// generate contact data
|
|
dVector3 vPntTmp;
|
|
ADD(avTempArray1[i],vHullBoxPos,vPntTmp);
|
|
|
|
#if 0 //#ifdef ORIG -- if to use conditional define, GenerateContact must be moved into #else
|
|
dContactGeom* Contact = SAFECONTACT(iFlags, ContactGeoms, ctContacts, iStride);
|
|
|
|
Contact->depth = -fTempDepth;
|
|
SET(Contact->normal,vBestNormal);
|
|
SET(Contact->pos,vPntTmp);
|
|
Contact->g1 = Geom1;
|
|
Contact->g2 = Geom2;
|
|
ctContacts++;
|
|
#endif
|
|
GenerateContact(iFlags, ContactGeoms, iStride, Geom1, Geom2,
|
|
vPntTmp, vBestNormal, -fTempDepth, ctContacts);
|
|
|
|
if ((ctContacts | CONTACTS_UNIMPORTANT) == (iFlags & (NUMC_MASK | CONTACTS_UNIMPORTANT))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//dAASSERT(ctContacts>0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// test one mesh triangle on intersection with given box
|
|
static void _cldTestOneTriangle(const dVector3 &v0, const dVector3 &v1, const dVector3 &v2)//, void *pvUser)
|
|
{
|
|
// do intersection test and find best separating axis
|
|
if(!_cldTestSeparatingAxes(v0, v1, v2) ) {
|
|
// if not found do nothing
|
|
return;
|
|
}
|
|
|
|
// if best separation axis is not found
|
|
if ( iBestAxis == 0 ) {
|
|
// this should not happen (we should already exit in that case)
|
|
//dMessage (0, "best separation axis not found");
|
|
// do nothing
|
|
return;
|
|
}
|
|
|
|
_cldClipping(v0, v1, v2);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// OPCODE version of box to mesh collider
|
|
#if dTRIMESH_OPCODE
|
|
int dCollideBTL(dxGeom* g1, dxGeom* BoxGeom, int Flags, dContactGeom* Contacts, int Stride){
|
|
dIASSERT (Stride >= (int)sizeof(dContactGeom));
|
|
dIASSERT (g1->type == dTriMeshClass);
|
|
dIASSERT (BoxGeom->type == dBoxClass);
|
|
dIASSERT ((Flags & NUMC_MASK) >= 1);
|
|
|
|
|
|
dxTriMesh* TriMesh = (dxTriMesh*)g1;
|
|
|
|
|
|
// get source hull position, orientation and half size
|
|
const dMatrix3& mRotBox=*(const dMatrix3*)dGeomGetRotation(BoxGeom);
|
|
const dVector3& vPosBox=*(const dVector3*)dGeomGetPosition(BoxGeom);
|
|
|
|
// to global
|
|
SETM(mHullBoxRot,mRotBox);
|
|
SET(vHullBoxPos,vPosBox);
|
|
|
|
dGeomBoxGetLengths(BoxGeom, vBoxHalfSize);
|
|
vBoxHalfSize[0] *= 0.5f;
|
|
vBoxHalfSize[1] *= 0.5f;
|
|
vBoxHalfSize[2] *= 0.5f;
|
|
|
|
|
|
|
|
// get destination hull position and orientation
|
|
const dMatrix3& mRotMesh=*(const dMatrix3*)dGeomGetRotation(TriMesh);
|
|
const dVector3& vPosMesh=*(const dVector3*)dGeomGetPosition(TriMesh);
|
|
|
|
// to global
|
|
SET(vHullDstPos,vPosMesh);
|
|
|
|
|
|
|
|
// global info for contact creation
|
|
ctContacts = 0;
|
|
iStride=Stride;
|
|
iFlags=Flags;
|
|
ContactGeoms=Contacts;
|
|
Geom1=TriMesh;
|
|
Geom2=BoxGeom;
|
|
|
|
|
|
|
|
// reset stuff
|
|
fBestDepth = MAXVALUE;
|
|
vBestNormal[0]=0;
|
|
vBestNormal[1]=0;
|
|
vBestNormal[2]=0;
|
|
|
|
OBBCollider& Collider = TriMesh->_OBBCollider;
|
|
|
|
|
|
|
|
|
|
// Make OBB
|
|
OBB Box;
|
|
Box.mCenter.x = vPosBox[0];
|
|
Box.mCenter.y = vPosBox[1];
|
|
Box.mCenter.z = vPosBox[2];
|
|
|
|
// It is a potential issue to explicitly cast to float
|
|
// if custom width floating point type is introduced in OPCODE.
|
|
// It is necessary to make a typedef and cast to it
|
|
// (e.g. typedef float opc_float;)
|
|
// However I'm not sure in what header it should be added.
|
|
|
|
Box.mExtents.x = /*(float)*/vBoxHalfSize[0];
|
|
Box.mExtents.y = /*(float)*/vBoxHalfSize[1];
|
|
Box.mExtents.z = /*(float)*/vBoxHalfSize[2];
|
|
|
|
Box.mRot.m[0][0] = /*(float)*/mRotBox[0];
|
|
Box.mRot.m[1][0] = /*(float)*/mRotBox[1];
|
|
Box.mRot.m[2][0] = /*(float)*/mRotBox[2];
|
|
|
|
Box.mRot.m[0][1] = /*(float)*/mRotBox[4];
|
|
Box.mRot.m[1][1] = /*(float)*/mRotBox[5];
|
|
Box.mRot.m[2][1] = /*(float)*/mRotBox[6];
|
|
|
|
Box.mRot.m[0][2] = /*(float)*/mRotBox[8];
|
|
Box.mRot.m[1][2] = /*(float)*/mRotBox[9];
|
|
Box.mRot.m[2][2] = /*(float)*/mRotBox[10];
|
|
|
|
Matrix4x4 amatrix;
|
|
Matrix4x4 BoxMatrix = MakeMatrix(vPosBox, mRotBox, amatrix);
|
|
|
|
Matrix4x4 InvBoxMatrix;
|
|
InvertPRMatrix(InvBoxMatrix, BoxMatrix);
|
|
|
|
// TC results
|
|
if (TriMesh->doBoxTC) {
|
|
dxTriMesh::BoxTC* BoxTC = 0;
|
|
for (int i = 0; i < TriMesh->BoxTCCache.size(); i++){
|
|
if (TriMesh->BoxTCCache[i].Geom == BoxGeom){
|
|
BoxTC = &TriMesh->BoxTCCache[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!BoxTC){
|
|
TriMesh->BoxTCCache.push(dxTriMesh::BoxTC());
|
|
|
|
BoxTC = &TriMesh->BoxTCCache[TriMesh->BoxTCCache.size() - 1];
|
|
BoxTC->Geom = BoxGeom;
|
|
BoxTC->FatCoeff = 1.1f; // Pierre recommends this, instead of 1.0
|
|
}
|
|
|
|
// Intersect
|
|
Collider.SetTemporalCoherence(true);
|
|
Collider.Collide(*BoxTC, Box, TriMesh->Data->BVTree, null, &MakeMatrix(vPosMesh, mRotMesh, amatrix));
|
|
}
|
|
else {
|
|
Collider.SetTemporalCoherence(false);
|
|
Collider.Collide(dxTriMesh::defaultBoxCache, Box, TriMesh->Data->BVTree, null,
|
|
&MakeMatrix(vPosMesh, mRotMesh, amatrix));
|
|
}
|
|
|
|
if (! Collider.GetContactStatus()) {
|
|
// no collision occurred
|
|
return 0;
|
|
}
|
|
|
|
// Retrieve data
|
|
int TriCount = Collider.GetNbTouchedPrimitives();
|
|
const int* Triangles = (const int*)Collider.GetTouchedPrimitives();
|
|
|
|
if (TriCount != 0){
|
|
if (TriMesh->ArrayCallback != null){
|
|
TriMesh->ArrayCallback(TriMesh, BoxGeom, Triangles, TriCount);
|
|
}
|
|
|
|
int ctContacts0 = 0;
|
|
|
|
// loop through all intersecting triangles
|
|
for (int i = 0; i < TriCount; i++){
|
|
|
|
|
|
const int Triint = Triangles[i];
|
|
if (!Callback(TriMesh, BoxGeom, Triint)) continue;
|
|
|
|
|
|
dVector3 dv[3];
|
|
FetchTriangle(TriMesh, Triint, vPosMesh, mRotMesh, dv);
|
|
|
|
|
|
// test this triangle
|
|
_cldTestOneTriangle(dv[0],dv[1],dv[2]);
|
|
|
|
// fill-in tri index for generated contacts
|
|
for (; ctContacts0<ctContacts; ctContacts0++)
|
|
SAFECONTACT(iFlags, ContactGeoms, ctContacts0, iStride)->side1 = Triint;
|
|
|
|
/*
|
|
NOTE by Oleh_Derevenko:
|
|
The function continues checking triangles after maximal number
|
|
of contacts is reached because it selects maximal penetration depths.
|
|
See also comments in GenerateContact()
|
|
*/
|
|
// Putting "break" at the end of loop prevents unnecessary checks on first pass and "continue"
|
|
if ((ctContacts | CONTACTS_UNIMPORTANT) == (iFlags & (NUMC_MASK | CONTACTS_UNIMPORTANT)))
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
return ctContacts;
|
|
}
|
|
#endif
|
|
|
|
// GIMPACT version of box to mesh collider
|
|
#if dTRIMESH_GIMPACT
|
|
int dCollideBTL(dxGeom* g1, dxGeom* BoxGeom, int Flags, dContactGeom* Contacts, int Stride)
|
|
{
|
|
dIASSERT (Stride >= (int)sizeof(dContactGeom));
|
|
dIASSERT (g1->type == dTriMeshClass);
|
|
dIASSERT (BoxGeom->type == dBoxClass);
|
|
dIASSERT ((Flags & NUMC_MASK) >= 1);
|
|
|
|
|
|
dxTriMesh* TriMesh = (dxTriMesh*)g1;
|
|
|
|
|
|
// get source hull position, orientation and half size
|
|
const dMatrix3& mRotBox=*(const dMatrix3*)dGeomGetRotation(BoxGeom);
|
|
const dVector3& vPosBox=*(const dVector3*)dGeomGetPosition(BoxGeom);
|
|
|
|
// to global
|
|
SETM(mHullBoxRot,mRotBox);
|
|
SET(vHullBoxPos,vPosBox);
|
|
|
|
dGeomBoxGetLengths(BoxGeom, vBoxHalfSize);
|
|
vBoxHalfSize[0] *= 0.5f;
|
|
vBoxHalfSize[1] *= 0.5f;
|
|
vBoxHalfSize[2] *= 0.5f;
|
|
|
|
// get destination hull position and orientation
|
|
/*const dMatrix3& mRotMesh=*(const dMatrix3*)dGeomGetRotation(TriMesh);
|
|
const dVector3& vPosMesh=*(const dVector3*)dGeomGetPosition(TriMesh);
|
|
|
|
// to global
|
|
SET(vHullDstPos,vPosMesh);*/
|
|
|
|
// global info for contact creation
|
|
ctContacts = 0;
|
|
iStride=Stride;
|
|
iFlags=Flags;
|
|
ContactGeoms=Contacts;
|
|
Geom1=TriMesh;
|
|
Geom2=BoxGeom;
|
|
|
|
|
|
// reset stuff
|
|
fBestDepth = MAXVALUE;
|
|
vBestNormal[0]=0;
|
|
vBestNormal[1]=0;
|
|
vBestNormal[2]=0;
|
|
|
|
|
|
//*****at first , collide box aabb******//
|
|
|
|
GIM_TRIMESH * ptrimesh = &TriMesh->m_collision_trimesh;
|
|
aabb3f test_aabb;
|
|
|
|
test_aabb.minX = BoxGeom->aabb[0];
|
|
test_aabb.maxX = BoxGeom->aabb[1];
|
|
test_aabb.minY = BoxGeom->aabb[2];
|
|
test_aabb.maxY = BoxGeom->aabb[3];
|
|
test_aabb.minZ = BoxGeom->aabb[4];
|
|
test_aabb.maxZ = BoxGeom->aabb[5];
|
|
|
|
GDYNAMIC_ARRAY collision_result;
|
|
GIM_CREATE_BOXQUERY_LIST(collision_result);
|
|
|
|
gim_aabbset_box_collision(&test_aabb, &ptrimesh->m_aabbset , &collision_result);
|
|
|
|
if(collision_result.m_size==0)
|
|
{
|
|
GIM_DYNARRAY_DESTROY(collision_result);
|
|
return 0;
|
|
}
|
|
//*****Set globals for box collision******//
|
|
|
|
//collide triangles
|
|
|
|
GUINT * boxesresult = GIM_DYNARRAY_POINTER(GUINT,collision_result);
|
|
gim_trimesh_locks_work_data(ptrimesh);
|
|
|
|
int ctContacts0 = 0;
|
|
|
|
for(unsigned int i=0;i<collision_result.m_size;i++)
|
|
{
|
|
dVector3 dv[3];
|
|
|
|
int Triint = boxesresult[i];
|
|
gim_trimesh_get_triangle_vertices(ptrimesh, Triint,dv[0],dv[1],dv[2]);
|
|
// test this triangle
|
|
_cldTestOneTriangle(dv[0],dv[1],dv[2]);
|
|
|
|
// fill-in tri index for generated contacts
|
|
for (; ctContacts0<ctContacts; ctContacts0++)
|
|
SAFECONTACT(iFlags, ContactGeoms, ctContacts0, iStride)->side1 = Triint;
|
|
|
|
/*
|
|
NOTE by Oleh_Derevenko:
|
|
The function continues checking triangles after maximal number
|
|
of contacts is reached because it selects maximal penetration depths.
|
|
See also comments in GenerateContact()
|
|
*/
|
|
// Putting "break" at the end of loop prevents unnecessary checks on first pass and "continue"
|
|
if ((ctContacts | CONTACTS_UNIMPORTANT) == (iFlags & (NUMC_MASK | CONTACTS_UNIMPORTANT)))
|
|
break;
|
|
}
|
|
|
|
gim_trimesh_unlocks_work_data(ptrimesh);
|
|
GIM_DYNARRAY_DESTROY(collision_result);
|
|
|
|
return ctContacts;
|
|
}
|
|
#endif
|
|
|
|
|
|
// GenerateContact - Written by Jeff Smith (jeff@burri.to)
|
|
// Generate a "unique" contact. A unique contact has a unique
|
|
// position or normal. If the potential contact has the same
|
|
// position and normal as an existing contact, but a larger
|
|
// penetration depth, this new depth is used instead
|
|
//
|
|
static void
|
|
GenerateContact(int in_Flags, dContactGeom* in_Contacts, int in_Stride,
|
|
dxGeom* in_g1, dxGeom* in_g2,
|
|
const dVector3 in_ContactPos, const dVector3 in_Normal, dReal in_Depth,
|
|
int& OutTriCount)
|
|
{
|
|
/*
|
|
NOTE by Oleh_Derevenko:
|
|
This function is called after maximal number of contacts has already been
|
|
collected because it has a side effect of replacing penetration depth of
|
|
existing contact with larger penetration depth of another matching normal contact.
|
|
If this logic is not necessary any more, you can bail out on reach of contact
|
|
number maximum immediately in dCollideBTL(). You will also need to correct
|
|
conditional statements after invocations of GenerateContact() in _cldClipping().
|
|
*/
|
|
do
|
|
{
|
|
dContactGeom* Contact;
|
|
dVector3 diff;
|
|
|
|
if (!(in_Flags & CONTACTS_UNIMPORTANT))
|
|
{
|
|
bool duplicate = false;
|
|
for (int i=0; i<OutTriCount; i++)
|
|
{
|
|
Contact = SAFECONTACT(in_Flags, in_Contacts, i, in_Stride);
|
|
|
|
// same position?
|
|
for (int j=0; j<3; j++)
|
|
diff[j] = in_ContactPos[j] - Contact->pos[j];
|
|
if (dDOT(diff, diff) < dEpsilon)
|
|
{
|
|
// same normal?
|
|
if (dFabs(dDOT(in_Normal, Contact->normal)) > (REAL(1.0)-dEpsilon))
|
|
{
|
|
if (in_Depth > Contact->depth)
|
|
Contact->depth = in_Depth;
|
|
duplicate = true;
|
|
/*
|
|
NOTE by Oleh_Derevenko:
|
|
There may be a case when two normals are close to each other but not duplicate
|
|
while third normal is detected to be duplicate for both of them.
|
|
This is the only reason I can think of, there is no "break" statement.
|
|
Perhaps author considered it to be logical that the third normal would
|
|
replace the depth in both of initial contacts.
|
|
However, I consider it a questionable practice which should not
|
|
be applied without deep understanding of underlaying physics.
|
|
Even more, is this situation with close normal triplet acceptable at all?
|
|
Should not be two initial contacts reduced to one (replaced with the latter)?
|
|
If you know the answers for these questions, you may want to change this code.
|
|
See the same statement in GenerateContact() of collision_trimesh_trimesh.cpp
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
if (duplicate || OutTriCount == (in_Flags & NUMC_MASK))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dIASSERT(OutTriCount < (in_Flags & NUMC_MASK));
|
|
}
|
|
|
|
// Add a new contact
|
|
Contact = SAFECONTACT(in_Flags, in_Contacts, OutTriCount, in_Stride);
|
|
|
|
Contact->pos[0] = in_ContactPos[0];
|
|
Contact->pos[1] = in_ContactPos[1];
|
|
Contact->pos[2] = in_ContactPos[2];
|
|
Contact->pos[3] = 0.0;
|
|
|
|
Contact->normal[0] = in_Normal[0];
|
|
Contact->normal[1] = in_Normal[1];
|
|
Contact->normal[2] = in_Normal[2];
|
|
Contact->normal[3] = 0.0;
|
|
|
|
Contact->depth = in_Depth;
|
|
|
|
Contact->g1 = in_g1;
|
|
Contact->g2 = in_g2;
|
|
|
|
OutTriCount++;
|
|
}
|
|
while (false);
|
|
}
|
|
|
|
#endif // dTRIMESH_ENABLED
|