diff options
author | sanine <sanine.not@pm.me> | 2022-10-01 20:59:36 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-10-01 20:59:36 -0500 |
commit | c5fc66ee58f2c60f2d226868bb1cf5b91badaf53 (patch) | |
tree | 277dd280daf10bf77013236b8edfa5f88708c7e0 /libs/ode-0.16.1/ode/src/heightfield.cpp | |
parent | 1cf9cc3408af7008451f9133fb95af66a9697d15 (diff) |
add ode
Diffstat (limited to 'libs/ode-0.16.1/ode/src/heightfield.cpp')
-rw-r--r-- | libs/ode-0.16.1/ode/src/heightfield.cpp | 1876 |
1 files changed, 1876 insertions, 0 deletions
diff --git a/libs/ode-0.16.1/ode/src/heightfield.cpp b/libs/ode-0.16.1/ode/src/heightfield.cpp new file mode 100644 index 0000000..71699db --- /dev/null +++ b/libs/ode-0.16.1/ode/src/heightfield.cpp @@ -0,0 +1,1876 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 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. * + * * + *************************************************************************/ + +// dHeightfield Collider +// Paul Cheyrou-Lagreze aka Tuan Kuranes 2006 Speed enhancements http://www.pop-3d.com +// Martijn Buijs 2006 http://home.planet.nl/~buijs512/ +// Based on Terrain & Cone contrib by: +// Benoit CHAPEROT 2003-2004 http://www.jstarlab.com +// Some code inspired by Magic Software + + +#include <ode/common.h> +#include <ode/collision.h> +#include <ode/rotation.h> +#include "config.h" +#include "matrix.h" +#include "odemath.h" +#include "collision_kernel.h" +#include "collision_std.h" +#include "collision_util.h" +#include "heightfield.h" + + + +#if dTRIMESH_ENABLED +#include "collision_trimesh_colliders.h" +#endif // dTRIMESH_ENABLED + +#define dMIN(A,B) ((A)>(B) ? (B) : (A)) +#define dMAX(A,B) ((A)>(B) ? (A) : (B)) + + +// Three-way MIN and MAX +#define dMIN3(A,B,C) ( (A)<(B) ? dMIN((A),(C)) : dMIN((B),(C)) ) +#define dMAX3(A,B,C) ( (A)>(B) ? dMAX((A),(C)) : dMAX((B),(C)) ) + +#define dOPESIGN(a, op1, op2,b) \ + (a)[0] op1 op2 ((b)[0]); \ + (a)[1] op1 op2 ((b)[1]); \ + (a)[2] op1 op2 ((b)[2]); + +#define dGeomRaySetNoNormalize(myRay, MyPoint, MyVector) { \ + \ + dVector3Copy (MyPoint, (myRay).final_posr->pos); \ + (myRay).final_posr->R[2] = (MyVector)[0]; \ + (myRay).final_posr->R[6] = (MyVector)[1]; \ + (myRay).final_posr->R[10] = (MyVector)[2]; \ + dGeomMoved (&myRay); \ + } + +#define dGeomPlaneSetNoNormalize(MyPlane, MyPlaneDef) { \ + \ + (MyPlane)->p[0] = (MyPlaneDef)[0]; \ + (MyPlane)->p[1] = (MyPlaneDef)[1]; \ + (MyPlane)->p[2] = (MyPlaneDef)[2]; \ + (MyPlane)->p[3] = (MyPlaneDef)[3]; \ + dGeomMoved (MyPlane); \ + } +//////// Local Build Option //////////////////////////////////////////////////// + +// Uncomment this #define to use the (0,0) corner of the geom as the origin, +// rather than the center. This was the way the original heightfield worked, +// but as it does not match the way all other geometries work, so for constancy it +// was changed to work like this. + +// #define DHEIGHTFIELD_CORNER_ORIGIN + + +// Uncomment this #define to add heightfield triangles edge colliding +// Code is not guaranteed and I didn't find the need to add that as +// colliding planes triangles and edge triangles seems enough. +// #define _HEIGHTFIELDEDGECOLLIDING + + +//////// dxHeightfieldData ///////////////////////////////////////////////////////////// + +// dxHeightfieldData constructor +dxHeightfieldData::dxHeightfieldData(): + m_fWidth( 0 ), + m_fDepth( 0 ), + m_fSampleWidth( 0 ), + m_fSampleDepth( 0 ), + m_fSampleZXAspect( 0 ), + m_fInvSampleWidth( 0 ), + m_fInvSampleDepth( 0 ), + + m_fHalfWidth( 0 ), + m_fHalfDepth( 0 ), + + m_fMinHeight( 0 ), + m_fMaxHeight( 0 ), + m_fThickness( 0 ), + m_fScale( 0 ), + m_fOffset( 0 ), + + m_nWidthSamples( 0 ), + m_nDepthSamples( 0 ), + m_bCopyHeightData( 0 ), + m_bWrapMode( 0 ), + m_nGetHeightMode( 0 ), + + m_pHeightData( NULL ), + m_pUserData( NULL ), + + m_pGetHeightCallback( NULL ) +{ + memset( m_contacts, 0, sizeof( m_contacts ) ); +} + +// build Heightfield data +void dxHeightfieldData::SetData( int nWidthSamples, int nDepthSamples, + dReal fWidth, dReal fDepth, + dReal fScale, dReal fOffset, dReal fThickness, + int bWrapMode ) +{ + dIASSERT( fWidth > REAL( 0.0 ) ); + dIASSERT( fDepth > REAL( 0.0 ) ); + dIASSERT( nWidthSamples > 0 ); + dIASSERT( nDepthSamples > 0 ); + + // x,z bounds + m_fWidth = fWidth; + m_fDepth = fDepth; + + // cache half x,z bounds + m_fHalfWidth = fWidth / REAL( 2.0 ); + m_fHalfDepth = fDepth / REAL( 2.0 ); + + // scale and offset + m_fScale = fScale; + m_fOffset = fOffset; + + // infinite min height bounds + m_fThickness = fThickness; + + // number of vertices per side + m_nWidthSamples = nWidthSamples; + m_nDepthSamples = nDepthSamples; + + m_fSampleWidth = m_fWidth / ( m_nWidthSamples - REAL( 1.0 ) ); + m_fSampleDepth = m_fDepth / ( m_nDepthSamples - REAL( 1.0 ) ); + + m_fSampleZXAspect = m_fSampleDepth / m_fSampleWidth; + + m_fInvSampleWidth = REAL( 1.0 ) / m_fSampleWidth; + m_fInvSampleDepth = REAL( 1.0 ) / m_fSampleDepth; + + // finite or repeated terrain? + m_bWrapMode = bWrapMode; +} + + +// recomputes heights bounds +void dxHeightfieldData::ComputeHeightBounds() +{ + int i; + dReal h; + unsigned char *data_byte; + short *data_short; + float *data_float; + double *data_double; + + switch ( m_nGetHeightMode ) + { + + // callback + case 0: + // change nothing, keep using default or user specified bounds + return; + + // byte + case 1: + data_byte = (unsigned char*)m_pHeightData; + m_fMinHeight = dInfinity; + m_fMaxHeight = -dInfinity; + + for (i=0; i<m_nWidthSamples*m_nDepthSamples; i++) + { + h = data_byte[i]; + if (h < m_fMinHeight) m_fMinHeight = h; + if (h > m_fMaxHeight) m_fMaxHeight = h; + } + + break; + + // short + case 2: + data_short = (short*)m_pHeightData; + m_fMinHeight = dInfinity; + m_fMaxHeight = -dInfinity; + + for (i=0; i<m_nWidthSamples*m_nDepthSamples; i++) + { + h = data_short[i]; + if (h < m_fMinHeight) m_fMinHeight = h; + if (h > m_fMaxHeight) m_fMaxHeight = h; + } + + break; + + // float + case 3: + data_float = (float*)m_pHeightData; + m_fMinHeight = dInfinity; + m_fMaxHeight = -dInfinity; + + for (i=0; i<m_nWidthSamples*m_nDepthSamples; i++) + { + h = data_float[i]; + if (h < m_fMinHeight) m_fMinHeight = h; + if (h > m_fMaxHeight) m_fMaxHeight = h; + } + + break; + + // double + case 4: + data_double = (double*)m_pHeightData; + m_fMinHeight = dInfinity; + m_fMaxHeight = -dInfinity; + + for (i=0; i<m_nWidthSamples*m_nDepthSamples; i++) + { + h = static_cast< dReal >( data_double[i] ); + if (h < m_fMinHeight) m_fMinHeight = h; + if (h > m_fMaxHeight) m_fMaxHeight = h; + } + + break; + + } + + // scale and offset + m_fMinHeight *= m_fScale; + m_fMaxHeight *= m_fScale; + m_fMinHeight += m_fOffset; + m_fMaxHeight += m_fOffset; + + // add thickness + m_fMinHeight -= m_fThickness; +} + + +// returns whether point is over terrain Cell triangle? +bool dxHeightfieldData::IsOnHeightfield2 ( const HeightFieldVertex * const CellCorner, + const dReal * const pos, const bool isABC) const +{ + // WARNING!!! + // This function must be written in the way to make sure that every point on + // XZ plane falls in one and only one triangle. Keep that in mind if you + // intend to change the code. + // Also remember about computational errors and possible mismatches in + // values if they are calculated differently in different places in the code. + // Currently both the implementation has been optimized and effects of + // computational errors have been eliminated. + + dReal MaxX, MinX; + dReal MaxZ, MinZ; + + if (isABC) + { + // point A + MinX = CellCorner->vertex[0]; + if (pos[0] < MinX) + return false; + + MaxX = (CellCorner->coords[0] + 1) * m_fSampleWidth; + if (pos[0] >= MaxX) + return false; + + MinZ = CellCorner->vertex[2]; + if (pos[2] < MinZ) + return false; + + MaxZ = (CellCorner->coords[1] + 1) * m_fSampleDepth; + if (pos[2] >= MaxZ) + return false; + + return (MaxZ - pos[2]) > (pos[0] - MinX) * m_fSampleZXAspect; + } + else + { + // point D + MaxX = CellCorner->vertex[0]; + if (pos[0] >= MaxX) + return false; + + MinX = (CellCorner->coords[0] - 1) * m_fSampleWidth; + if (pos[0] < MinX) + return false; + + MaxZ = CellCorner->vertex[2]; + if (pos[2] >= MaxZ) + return false; + + MinZ = (CellCorner->coords[1] - 1) * m_fSampleDepth; + if (pos[2] < MinZ) + return false; + + return (MaxZ - pos[2]) <= (pos[0] - MinX) * m_fSampleZXAspect; + } +} + + +// returns height at given sample coordinates +dReal dxHeightfieldData::GetHeight( int x, int z ) +{ + dReal h=0; + unsigned char *data_byte; + short *data_short; + float *data_float; + double *data_double; + + if ( m_bWrapMode == 0 ) + { + // Finite + if ( x < 0 ) x = 0; + if ( z < 0 ) z = 0; + if ( x > m_nWidthSamples - 1 ) x = m_nWidthSamples - 1; + if ( z > m_nDepthSamples - 1 ) z = m_nDepthSamples - 1; + } + else + { + // Infinite + x %= m_nWidthSamples - 1; + z %= m_nDepthSamples - 1; + if ( x < 0 ) x += m_nWidthSamples - 1; + if ( z < 0 ) z += m_nDepthSamples - 1; + } + + switch ( m_nGetHeightMode ) + { + + // callback (dReal) + case 0: + h = (*m_pGetHeightCallback)(m_pUserData, x, z); + break; + + // byte + case 1: + data_byte = (unsigned char*)m_pHeightData; + h = data_byte[x+(z * m_nWidthSamples)]; + break; + + // short + case 2: + data_short = (short*)m_pHeightData; + h = data_short[x+(z * m_nWidthSamples)]; + break; + + // float + case 3: + data_float = (float*)m_pHeightData; + h = data_float[x+(z * m_nWidthSamples)]; + break; + + // double + case 4: + data_double = (double*)m_pHeightData; + h = (dReal)( data_double[x+(z * m_nWidthSamples)] ); + break; + } + + return (h * m_fScale) + m_fOffset; +} + + +// returns height at given coordinates +dReal dxHeightfieldData::GetHeight( dReal x, dReal z ) +{ + dReal dnX = dFloor( x * m_fInvSampleWidth ); + dReal dnZ = dFloor( z * m_fInvSampleDepth ); + + dReal dx = ( x - ( dnX * m_fSampleWidth ) ) * m_fInvSampleWidth; + dReal dz = ( z - ( dnZ * m_fSampleDepth ) ) * m_fInvSampleDepth; + + int nX = int( dnX ); + int nZ = int( dnZ ); + + //dIASSERT( ( dx + dEpsilon >= 0.0f ) && ( dx - dEpsilon <= 1.0f ) ); + //dIASSERT( ( dz + dEpsilon >= 0.0f ) && ( dz - dEpsilon <= 1.0f ) ); + + dReal y, y0; + + if ( dx + dz <= REAL( 1.0 ) ) // Use <= comparison to prefer simpler branch + { + y0 = GetHeight( nX, nZ ); + + y = y0 + ( GetHeight( nX + 1, nZ ) - y0 ) * dx + + ( GetHeight( nX, nZ + 1 ) - y0 ) * dz; + } + else + { + y0 = GetHeight( nX + 1, nZ + 1 ); + + y = y0 + ( GetHeight( nX + 1, nZ ) - y0 ) * ( REAL(1.0) - dz ) + + ( GetHeight( nX, nZ + 1 ) - y0 ) * ( REAL(1.0) - dx ); + } + + return y; +} + + +// dxHeightfieldData destructor +dxHeightfieldData::~dxHeightfieldData() +{ + unsigned char *data_byte; + short *data_short; + float *data_float; + double *data_double; + + if ( m_bCopyHeightData ) + { + switch ( m_nGetHeightMode ) + { + + // callback + case 0: + // do nothing + break; + + // byte + case 1: + dIASSERT( m_pHeightData ); + data_byte = (unsigned char*)m_pHeightData; + delete [] data_byte; + break; + + // short + case 2: + dIASSERT( m_pHeightData ); + data_short = (short*)m_pHeightData; + delete [] data_short; + break; + + // float + case 3: + dIASSERT( m_pHeightData ); + data_float = (float*)m_pHeightData; + delete [] data_float; + break; + + // double + case 4: + dIASSERT( m_pHeightData ); + data_double = (double*)m_pHeightData; + delete [] data_double; + break; + + } + } +} + + +//////// dxHeightfield ///////////////////////////////////////////////////////////////// + + +// dxHeightfield constructor +dxHeightfield::dxHeightfield( dSpaceID space, + dHeightfieldDataID data, + int bPlaceable ) : + dxGeom( space, bPlaceable ), + tempPlaneBuffer(0), + tempPlaneInstances(0), + tempPlaneBufferSize(0), + tempTriangleBuffer(0), + tempTriangleBufferSize(0), + tempHeightBuffer(0), + tempHeightInstances(0), + tempHeightBufferSizeX(0), + tempHeightBufferSizeZ(0) +{ + type = dHeightfieldClass; + this->m_p_data = data; +} + + +// compute axis aligned bounding box +void dxHeightfield::computeAABB() +{ + const dxHeightfieldData *d = m_p_data; + + if ( d->m_bWrapMode == 0 ) + { + // Finite + if ( gflags & GEOM_PLACEABLE ) + { + dReal dx[6], dy[6], dz[6]; + + // Y-axis + if (d->m_fMinHeight != -dInfinity) + { + dy[0] = ( final_posr->R[ 1] * d->m_fMinHeight ); + dy[1] = ( final_posr->R[ 5] * d->m_fMinHeight ); + dy[2] = ( final_posr->R[ 9] * d->m_fMinHeight ); + } + else + { + // Multiplication is performed to obtain infinity of correct sign + dy[0] = ( final_posr->R[ 1] ? final_posr->R[ 1] * -dInfinity : REAL(0.0) ); + dy[1] = ( final_posr->R[ 5] ? final_posr->R[ 5] * -dInfinity : REAL(0.0) ); + dy[2] = ( final_posr->R[ 9] ? final_posr->R[ 9] * -dInfinity : REAL(0.0) ); + } + + if (d->m_fMaxHeight != dInfinity) + { + dy[3] = ( final_posr->R[ 1] * d->m_fMaxHeight ); + dy[4] = ( final_posr->R[ 5] * d->m_fMaxHeight ); + dy[5] = ( final_posr->R[ 9] * d->m_fMaxHeight ); + } + else + { + dy[3] = ( final_posr->R[ 1] ? final_posr->R[ 1] * dInfinity : REAL(0.0) ); + dy[4] = ( final_posr->R[ 5] ? final_posr->R[ 5] * dInfinity : REAL(0.0) ); + dy[5] = ( final_posr->R[ 9] ? final_posr->R[ 9] * dInfinity : REAL(0.0) ); + } + +#ifdef DHEIGHTFIELD_CORNER_ORIGIN + + // X-axis + dx[0] = 0; dx[3] = ( final_posr->R[ 0] * d->m_fWidth ); + dx[1] = 0; dx[4] = ( final_posr->R[ 4] * d->m_fWidth ); + dx[2] = 0; dx[5] = ( final_posr->R[ 8] * d->m_fWidth ); + + // Z-axis + dz[0] = 0; dz[3] = ( final_posr->R[ 2] * d->m_fDepth ); + dz[1] = 0; dz[4] = ( final_posr->R[ 6] * d->m_fDepth ); + dz[2] = 0; dz[5] = ( final_posr->R[10] * d->m_fDepth ); + +#else // DHEIGHTFIELD_CORNER_ORIGIN + + // X-axis + dx[0] = ( final_posr->R[ 0] * -d->m_fHalfWidth ); + dx[1] = ( final_posr->R[ 4] * -d->m_fHalfWidth ); + dx[2] = ( final_posr->R[ 8] * -d->m_fHalfWidth ); + dx[3] = ( final_posr->R[ 0] * d->m_fHalfWidth ); + dx[4] = ( final_posr->R[ 4] * d->m_fHalfWidth ); + dx[5] = ( final_posr->R[ 8] * d->m_fHalfWidth ); + + // Z-axis + dz[0] = ( final_posr->R[ 2] * -d->m_fHalfDepth ); + dz[1] = ( final_posr->R[ 6] * -d->m_fHalfDepth ); + dz[2] = ( final_posr->R[10] * -d->m_fHalfDepth ); + dz[3] = ( final_posr->R[ 2] * d->m_fHalfDepth ); + dz[4] = ( final_posr->R[ 6] * d->m_fHalfDepth ); + dz[5] = ( final_posr->R[10] * d->m_fHalfDepth ); + +#endif // DHEIGHTFIELD_CORNER_ORIGIN + + // X extents + aabb[0] = final_posr->pos[0] + + dMIN3( dMIN( dx[0], dx[3] ), dMIN( dy[0], dy[3] ), dMIN( dz[0], dz[3] ) ); + aabb[1] = final_posr->pos[0] + + dMAX3( dMAX( dx[0], dx[3] ), dMAX( dy[0], dy[3] ), dMAX( dz[0], dz[3] ) ); + + // Y extents + aabb[2] = final_posr->pos[1] + + dMIN3( dMIN( dx[1], dx[4] ), dMIN( dy[1], dy[4] ), dMIN( dz[1], dz[4] ) ); + aabb[3] = final_posr->pos[1] + + dMAX3( dMAX( dx[1], dx[4] ), dMAX( dy[1], dy[4] ), dMAX( dz[1], dz[4] ) ); + + // Z extents + aabb[4] = final_posr->pos[2] + + dMIN3( dMIN( dx[2], dx[5] ), dMIN( dy[2], dy[5] ), dMIN( dz[2], dz[5] ) ); + aabb[5] = final_posr->pos[2] + + dMAX3( dMAX( dx[2], dx[5] ), dMAX( dy[2], dy[5] ), dMAX( dz[2], dz[5] ) ); + } + else + { + +#ifdef DHEIGHTFIELD_CORNER_ORIGIN + + aabb[0] = 0; aabb[1] = d->m_fWidth; + aabb[2] = d->m_fMinHeight; aabb[3] = d->m_fMaxHeight; + aabb[4] = 0; aabb[5] = d->m_fDepth; + +#else // DHEIGHTFIELD_CORNER_ORIGIN + + aabb[0] = -d->m_fHalfWidth; aabb[1] = +d->m_fHalfWidth; + aabb[2] = d->m_fMinHeight; aabb[3] = d->m_fMaxHeight; + aabb[4] = -d->m_fHalfDepth; aabb[5] = +d->m_fHalfDepth; + +#endif // DHEIGHTFIELD_CORNER_ORIGIN + + } + } + else + { + // Infinite + if ( gflags & GEOM_PLACEABLE ) + { + aabb[0] = -dInfinity; aabb[1] = +dInfinity; + aabb[2] = -dInfinity; aabb[3] = +dInfinity; + aabb[4] = -dInfinity; aabb[5] = +dInfinity; + } + else + { + aabb[0] = -dInfinity; aabb[1] = +dInfinity; + aabb[2] = d->m_fMinHeight; aabb[3] = d->m_fMaxHeight; + aabb[4] = -dInfinity; aabb[5] = +dInfinity; + } + } + +} + + +// dxHeightfield destructor +dxHeightfield::~dxHeightfield() +{ + resetTriangleBuffer(); + resetPlaneBuffer(); + resetHeightBuffer(); +} + +void dxHeightfield::allocateTriangleBuffer(sizeint numTri) +{ + sizeint alignedNumTri = AlignBufferSize(numTri, TEMP_TRIANGLE_BUFFER_ELEMENT_COUNT_ALIGNMENT); + tempTriangleBufferSize = alignedNumTri; + tempTriangleBuffer = new HeightFieldTriangle[alignedNumTri]; +} + +void dxHeightfield::resetTriangleBuffer() +{ + delete[] tempTriangleBuffer; +} + +void dxHeightfield::allocatePlaneBuffer(sizeint numTri) +{ + sizeint alignedNumTri = AlignBufferSize(numTri, TEMP_PLANE_BUFFER_ELEMENT_COUNT_ALIGNMENT); + tempPlaneBufferSize = alignedNumTri; + tempPlaneBuffer = new HeightFieldPlane *[alignedNumTri]; + tempPlaneInstances = new HeightFieldPlane[alignedNumTri]; + + HeightFieldPlane *ptrPlaneMatrix = tempPlaneInstances; + for (sizeint indexTri = 0; indexTri != alignedNumTri; indexTri++) + { + tempPlaneBuffer[indexTri] = ptrPlaneMatrix; + ptrPlaneMatrix += 1; + } +} + +void dxHeightfield::resetPlaneBuffer() +{ + delete[] tempPlaneInstances; + delete[] tempPlaneBuffer; +} + +void dxHeightfield::allocateHeightBuffer(sizeint numX, sizeint numZ) +{ + sizeint alignedNumX = AlignBufferSize(numX, TEMP_HEIGHT_BUFFER_ELEMENT_COUNT_ALIGNMENT_X); + sizeint alignedNumZ = AlignBufferSize(numZ, TEMP_HEIGHT_BUFFER_ELEMENT_COUNT_ALIGNMENT_Z); + tempHeightBufferSizeX = alignedNumX; + tempHeightBufferSizeZ = alignedNumZ; + tempHeightBuffer = new HeightFieldVertex *[alignedNumX]; + sizeint numCells = alignedNumX * alignedNumZ; + tempHeightInstances = new HeightFieldVertex [numCells]; + + HeightFieldVertex *ptrHeightMatrix = tempHeightInstances; + for (sizeint indexX = 0; indexX != alignedNumX; indexX++) + { + tempHeightBuffer[indexX] = ptrHeightMatrix; + ptrHeightMatrix += alignedNumZ; + } +} + +void dxHeightfield::resetHeightBuffer() +{ + delete[] tempHeightInstances; + delete[] tempHeightBuffer; +} +//////// Heightfield data interface //////////////////////////////////////////////////// + + +dHeightfieldDataID dGeomHeightfieldDataCreate() +{ + return new dxHeightfieldData(); +} + + +void dGeomHeightfieldDataBuildCallback( dHeightfieldDataID d, + void* pUserData, dHeightfieldGetHeight* pCallback, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ) +{ + dUASSERT( d, "argument not Heightfield data" ); + dIASSERT( pCallback ); + dIASSERT( widthSamples >= 2 ); // Ensure we're making something with at least one cell. + dIASSERT( depthSamples >= 2 ); + + // callback + d->m_nGetHeightMode = 0; + d->m_pUserData = pUserData; + d->m_pGetHeightCallback = pCallback; + + // set info + d->SetData( widthSamples, depthSamples, width, depth, scale, offset, thickness, bWrap ); + + // default bounds + d->m_fMinHeight = -dInfinity; + d->m_fMaxHeight = dInfinity; +} + + +void dGeomHeightfieldDataBuildByte( dHeightfieldDataID d, + const unsigned char *pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ) +{ + dUASSERT( d, "Argument not Heightfield data" ); + dIASSERT( pHeightData ); + dIASSERT( widthSamples >= 2 ); // Ensure we're making something with at least one cell. + dIASSERT( depthSamples >= 2 ); + + // set info + d->SetData( widthSamples, depthSamples, width, depth, scale, offset, thickness, bWrap ); + d->m_nGetHeightMode = 1; + d->m_bCopyHeightData = bCopyHeightData; + + if ( d->m_bCopyHeightData == 0 ) + { + // Data is referenced only. + d->m_pHeightData = pHeightData; + } + else + { + // We own the height data, allocate storage + d->m_pHeightData = new unsigned char[ d->m_nWidthSamples * d->m_nDepthSamples ]; + dIASSERT( d->m_pHeightData ); + + // Copy data. + memcpy( (void*)d->m_pHeightData, pHeightData, + sizeof( unsigned char ) * d->m_nWidthSamples * d->m_nDepthSamples ); + } + + // Find height bounds + d->ComputeHeightBounds(); +} + + +void dGeomHeightfieldDataBuildShort( dHeightfieldDataID d, + const short* pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ) +{ + dUASSERT( d, "Argument not Heightfield data" ); + dIASSERT( pHeightData ); + dIASSERT( widthSamples >= 2 ); // Ensure we're making something with at least one cell. + dIASSERT( depthSamples >= 2 ); + + // set info + d->SetData( widthSamples, depthSamples, width, depth, scale, offset, thickness, bWrap ); + d->m_nGetHeightMode = 2; + d->m_bCopyHeightData = bCopyHeightData; + + if ( d->m_bCopyHeightData == 0 ) + { + // Data is referenced only. + d->m_pHeightData = pHeightData; + } + else + { + // We own the height data, allocate storage + d->m_pHeightData = new short[ d->m_nWidthSamples * d->m_nDepthSamples ]; + dIASSERT( d->m_pHeightData ); + + // Copy data. + memcpy( (void*)d->m_pHeightData, pHeightData, + sizeof( short ) * d->m_nWidthSamples * d->m_nDepthSamples ); + } + + // Find height bounds + d->ComputeHeightBounds(); +} + + +void dGeomHeightfieldDataBuildSingle( dHeightfieldDataID d, + const float *pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ) +{ + dUASSERT( d, "Argument not Heightfield data" ); + dIASSERT( pHeightData ); + dIASSERT( widthSamples >= 2 ); // Ensure we're making something with at least one cell. + dIASSERT( depthSamples >= 2 ); + + // set info + d->SetData( widthSamples, depthSamples, width, depth, scale, offset, thickness, bWrap ); + d->m_nGetHeightMode = 3; + d->m_bCopyHeightData = bCopyHeightData; + + if ( d->m_bCopyHeightData == 0 ) + { + // Data is referenced only. + d->m_pHeightData = pHeightData; + } + else + { + // We own the height data, allocate storage + d->m_pHeightData = new float[ d->m_nWidthSamples * d->m_nDepthSamples ]; + dIASSERT( d->m_pHeightData ); + + // Copy data. + memcpy( (void*)d->m_pHeightData, pHeightData, + sizeof( float ) * d->m_nWidthSamples * d->m_nDepthSamples ); + } + + // Find height bounds + d->ComputeHeightBounds(); +} + +void dGeomHeightfieldDataBuildDouble( dHeightfieldDataID d, + const double *pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ) +{ + dUASSERT( d, "Argument not Heightfield data" ); + dIASSERT( pHeightData ); + dIASSERT( widthSamples >= 2 ); // Ensure we're making something with at least one cell. + dIASSERT( depthSamples >= 2 ); + + // set info + d->SetData( widthSamples, depthSamples, width, depth, scale, offset, thickness, bWrap ); + d->m_nGetHeightMode = 4; + d->m_bCopyHeightData = bCopyHeightData; + + if ( d->m_bCopyHeightData == 0 ) + { + // Data is referenced only. + d->m_pHeightData = pHeightData; + } + else + { + // We own the height data, allocate storage + d->m_pHeightData = new double[ d->m_nWidthSamples * d->m_nDepthSamples ]; + dIASSERT( d->m_pHeightData ); + + // Copy data. + memcpy( (void*)d->m_pHeightData, pHeightData, + sizeof( double ) * d->m_nWidthSamples * d->m_nDepthSamples ); + } + + // Find height bounds + d->ComputeHeightBounds(); +} + + + + +void dGeomHeightfieldDataSetBounds( dHeightfieldDataID d, dReal minHeight, dReal maxHeight ) +{ + dUASSERT(d, "Argument not Heightfield data"); + d->m_fMinHeight = ( minHeight * d->m_fScale ) + d->m_fOffset - d->m_fThickness; + d->m_fMaxHeight = ( maxHeight * d->m_fScale ) + d->m_fOffset; +} + + +void dGeomHeightfieldDataDestroy( dHeightfieldDataID d ) +{ + dUASSERT(d, "argument not Heightfield data"); + delete d; +} + + +//////// Heightfield geom interface //////////////////////////////////////////////////// + + +dGeomID dCreateHeightfield( dSpaceID space, dHeightfieldDataID data, int bPlaceable ) +{ + return new dxHeightfield( space, data, bPlaceable ); +} + + +void dGeomHeightfieldSetHeightfieldData( dGeomID g, dHeightfieldDataID d ) +{ + dxHeightfield* geom = (dxHeightfield*) g; + geom->m_p_data = d; +} + + +dHeightfieldDataID dGeomHeightfieldGetHeightfieldData( dGeomID g ) +{ + dxHeightfield* geom = (dxHeightfield*) g; + return geom->m_p_data; +} + +//////// dxHeightfield ///////////////////////////////////////////////////////////////// + + +// Typedef for generic 'get point depth' function +typedef dReal dGetDepthFn( dGeomID g, dReal x, dReal y, dReal z ); + + +#define DMESS(A) \ + dMessage(0,"Contact Plane (%d %d %d) %.5e %.5e (%.5e %.5e %.5e)(%.5e %.5e %.5e)).", \ + x,z,(A), \ + pContact->depth, \ + dGeomSphereGetRadius(o2), \ + pContact->pos[0], \ + pContact->pos[1], \ + pContact->pos[2], \ + pContact->normal[0], \ + pContact->normal[1], \ + pContact->normal[2]); + +static inline bool DescendingTriangleSort(const HeightFieldTriangle * const A, const HeightFieldTriangle * const B) +{ + return ((A->maxAAAB - B->maxAAAB) > dEpsilon); +} +static inline bool DescendingPlaneSort(const HeightFieldPlane * const A, const HeightFieldPlane * const B) +{ + return ((A->maxAAAB - B->maxAAAB) > dEpsilon); +} + +void dxHeightfield::sortPlanes(const sizeint numPlanes) +{ + bool has_swapped = true; + do + { + has_swapped = false;//reset flag + for (sizeint i = 0; i < numPlanes - 1; i++) + { + //if they are in the wrong order + if (DescendingPlaneSort(tempPlaneBuffer[i], tempPlaneBuffer[i + 1])) + { + //exchange them + HeightFieldPlane * tempPlane = tempPlaneBuffer[i]; + tempPlaneBuffer[i] = tempPlaneBuffer[i + 1]; + tempPlaneBuffer[i + 1] = tempPlane; + + //we have swapped at least once, list may not be sorted yet + has_swapped = true; + } + } + } //if no swaps were made during this pass, the list has been sorted + while (has_swapped); +} + +static inline dReal DistancePointToLine(const dVector3 &_point, + const dVector3 &_pt0, + const dVector3 &_Edge, + const dReal _Edgelength) +{ + dVector3 v; + dVector3Subtract(_point, _pt0, v); + dVector3 s; + dVector3Copy (_Edge, s); + const dReal dot = dVector3Dot(v, _Edge) / _Edgelength; + dVector3Scale(s, dot); + dVector3Subtract(v, s, v); + return dVector3Length(v); +} + + + + +int dxHeightfield::dCollideHeightfieldZone( const int minX, const int maxX, const int minZ, const int maxZ, + dxGeom* o2, const int numMaxContactsPossible, + int flags, dContactGeom* contact, + int skip ) +{ + dContactGeom *pContact = 0; + int x, z; + // check if not above or inside terrain first + // while filling a heightmap partial temporary buffer + const unsigned int numX = (maxX - minX) + 1; + const unsigned int numZ = (maxZ - minZ) + 1; + const dReal minO2Height = o2->aabb[2]; + const dReal maxO2Height = o2->aabb[3]; + unsigned int x_local, z_local; + dReal maxY = - dInfinity; + dReal minY = dInfinity; + // localize and const for faster access + const dReal cfSampleWidth = m_p_data->m_fSampleWidth; + const dReal cfSampleDepth = m_p_data->m_fSampleDepth; + { + if (tempHeightBufferSizeX < numX || tempHeightBufferSizeZ < numZ) + { + resetHeightBuffer(); + allocateHeightBuffer(numX, numZ); + } + + dReal Xpos, Ypos; + + for ( x = minX, x_local = 0; x_local < numX; x++, x_local++) + { + Xpos = x * cfSampleWidth; // Always calculate pos via multiplication to avoid computational error accumulation during multiple additions + + const dReal c_Xpos = Xpos; + HeightFieldVertex *HeightFieldRow = tempHeightBuffer[x_local]; + for ( z = minZ, z_local = 0; z_local < numZ; z++, z_local++) + { + Ypos = z * cfSampleDepth; // Always calculate pos via multiplication to avoid computational error accumulation during multiple additions + + const dReal h = m_p_data->GetHeight(x, z); + HeightFieldRow[z_local].vertex[0] = c_Xpos; + HeightFieldRow[z_local].vertex[1] = h; + HeightFieldRow[z_local].vertex[2] = Ypos; + HeightFieldRow[z_local].coords[0] = x; + HeightFieldRow[z_local].coords[1] = z; + + maxY = dMAX(maxY, h); + minY = dMIN(minY, h); + } + } + if (minO2Height - maxY > -dEpsilon ) + { + //totally above heightfield + return 0; + } + if (minY - maxO2Height > -dEpsilon ) + { + // totally under heightfield + pContact = CONTACT(contact, 0); + + pContact->pos[0] = o2->final_posr->pos[0]; + pContact->pos[1] = minY; + pContact->pos[2] = o2->final_posr->pos[2]; + + pContact->normal[0] = 0; + pContact->normal[1] = - 1; + pContact->normal[2] = 0; + + pContact->depth = minY - maxO2Height; + + pContact->side1 = -1; + pContact->side2 = -1; + + return 1; + } + } + // get All Planes that could collide against. + dColliderFn *geomRayNCollider=0; + dColliderFn *geomNPlaneCollider=0; + dGetDepthFn *geomNDepthGetter=0; + + // int max_collisionContact = numMaxContactsPossible; -- not used + switch (o2->type) + { + case dRayClass: + geomRayNCollider = NULL; + geomNPlaneCollider = dCollideRayPlane; + geomNDepthGetter = NULL; + //max_collisionContact = 1; + break; + + case dSphereClass: + geomRayNCollider = dCollideRaySphere; + geomNPlaneCollider = dCollideSpherePlane; + geomNDepthGetter = dGeomSpherePointDepth; + //max_collisionContact = 3; + break; + + case dBoxClass: + geomRayNCollider = dCollideRayBox; + geomNPlaneCollider = dCollideBoxPlane; + geomNDepthGetter = dGeomBoxPointDepth; + //max_collisionContact = 8; + break; + + case dCapsuleClass: + geomRayNCollider = dCollideRayCapsule; + geomNPlaneCollider = dCollideCapsulePlane; + geomNDepthGetter = dGeomCapsulePointDepth; + // max_collisionContact = 3; + break; + + case dCylinderClass: + geomRayNCollider = dCollideRayCylinder; + geomNPlaneCollider = dCollideCylinderPlane; + geomNDepthGetter = NULL;// TODO: dGeomCCylinderPointDepth + //max_collisionContact = 3; + break; + + case dConvexClass: + geomRayNCollider = dCollideRayConvex; + geomNPlaneCollider = dCollideConvexPlane; + geomNDepthGetter = NULL;// TODO: dGeomConvexPointDepth; + //max_collisionContact = 3; + break; + +#if dTRIMESH_ENABLED + + case dTriMeshClass: + geomRayNCollider = dCollideRayTrimesh; + geomNPlaneCollider = dCollideTrimeshPlane; + geomNDepthGetter = NULL;// TODO: dGeomTrimeshPointDepth; + //max_collisionContact = 3; + break; + +#endif // dTRIMESH_ENABLED + + default: + dIASSERT(0); // Shouldn't ever get here. + break; + + } + + dxPlane myplane(0,0,0,0,0); + dxPlane* sliding_plane = &myplane; + dReal triplane[4]; + int i; + + // check some trivial case. + // Vector Up plane + if (maxY - minY < dEpsilon) + { + // it's a single plane. + triplane[0] = 0; + triplane[1] = 1; + triplane[2] = 0; + triplane[3] = minY; + dGeomPlaneSetNoNormalize (sliding_plane, triplane); + // find collision and compute contact points + const int numTerrainContacts = geomNPlaneCollider (o2, sliding_plane, flags, contact, skip); + dIASSERT(numTerrainContacts <= numMaxContactsPossible); + for (i = 0; i < numTerrainContacts; i++) + { + pContact = CONTACT(contact, i*skip); + dOPESIGN(pContact->normal, =, -, triplane); + } + return numTerrainContacts; + } + + /* -- This block is invalid as per Martijn Buijs <buijs512@planet.nl> + + The problem seems to be based on the erroneously assumption that if two of + the four vertices of a 'grid' are at the same height, the entire grid can be + represented as a single plane. It works for an axis aligned slope, but fails + on all 4 grids of a 3x3 spike feature. Since the plane normal is constructed + from only 3 vertices (only one of the two triangles) this often results in + discontinuities at the grid edges (causing small jumps when the contact + point moves from one grid to another). + + // unique plane + { + // check for very simple plane heightfield + dReal minXHeightDelta = dInfinity, maxXHeightDelta = - dInfinity; + dReal minZHeightDelta = dInfinity, maxZHeightDelta = - dInfinity; + + + dReal lastXHeight = tempHeightBuffer[0][0].vertex[1]; + for ( x_local = 1; x_local < numX; x_local++) + { + HeightFieldVertex *HeightFieldRow = tempHeightBuffer[x_local]; + + const dReal deltaX = HeightFieldRow[0].vertex[1] - lastXHeight; + + maxXHeightDelta = dMAX (maxXHeightDelta, deltaX); + minXHeightDelta = dMIN (minXHeightDelta, deltaX); + + dReal lastZHeight = HeightFieldRow[0].vertex[1]; + for ( z_local = 1; z_local < numZ; z_local++) + { + const dReal deltaZ = (HeightFieldRow[z_local].vertex[1] - lastZHeight); + + maxZHeightDelta = dMAX (maxZHeightDelta, deltaZ); + minZHeightDelta = dMIN (minZHeightDelta, deltaZ); + + } + } + + if (maxZHeightDelta - minZHeightDelta < dEpsilon && + maxXHeightDelta - minXHeightDelta < dEpsilon ) + { + // it's a single plane. + const dVector3 &A = tempHeightBuffer[0][0].vertex; + const dVector3 &B = tempHeightBuffer[1][0].vertex; + const dVector3 &C = tempHeightBuffer[0][1].vertex; + + // define 2 edges and a point that will define collision plane + { + dVector3 Edge1, Edge2; + dVector3Subtract(C, A, Edge1); + dVector3Subtract(B, A, Edge2); + dVector3Cross(Edge1, Edge2, triplane); + } + + // Define Plane + // Normalize plane normal + const dReal dinvlength = REAL(1.0) / dVector3Length(triplane); + triplane[0] *= dinvlength; + triplane[1] *= dinvlength; + triplane[2] *= dinvlength; + // get distance to origin from plane + triplane[3] = dVector3Dot(triplane, A); + + dGeomPlaneSetNoNormalize (sliding_plane, triplane); + // find collision and compute contact points + const int numTerrainContacts = geomNPlaneCollider (o2, sliding_plane, flags, contact, skip); + dIASSERT(numTerrainContacts <= numMaxContactsPossible); + for (i = 0; i < numTerrainContacts; i++) + { + pContact = CONTACT(contact, i*skip); + dOPESIGN(pContact->normal, =, -, triplane); + } + return numTerrainContacts; + } + } + */ + + int numTerrainContacts = 0; + dContactGeom *PlaneContact = m_p_data->m_contacts; + + const unsigned int numTriMax = (maxX - minX) * (maxZ - minZ) * 2; + if (tempTriangleBufferSize < numTriMax) + { + resetTriangleBuffer(); + allocateTriangleBuffer(numTriMax); + } + + // Sorting triangle/plane resulting from heightfield zone + // Perhaps that would be necessary in case of too much limited + // maximum contact point... + // or in complex mesh case (trimesh and convex) + // need some test or insights on this before enabling this. + const bool isContactNumPointsLimited = + true; + // (numMaxContacts < 8) + // || o2->type == dConvexClass + // || o2->type == dTriMeshClass + // || (numMaxContacts < (int)numTriMax) + + + + // if small heightfield triangle related to O2 colliding + // or no Triangle colliding at all. + bool needFurtherPasses = (o2->type == dTriMeshClass); + //compute Ratio between Triangle size and O2 aabb size + // no FurtherPasses are needed in ray class + if (o2->type != dRayClass && needFurtherPasses == false) + { + const dReal xratio = (o2->aabb[1] - o2->aabb[0]) * m_p_data->m_fInvSampleWidth; + if (xratio > REAL(1.5)) + needFurtherPasses = true; + else + { + const dReal zratio = (o2->aabb[5] - o2->aabb[4]) * m_p_data->m_fInvSampleDepth; + if (zratio > REAL(1.5)) + needFurtherPasses = true; + } + + } + + unsigned int numTri = 0; + HeightFieldVertex *A, *B, *C, *D; + /* (y is up) + A--------B-...x + | /| + | / | + | / | + | / | + | / | + | / | + | / | + |/ | + C--------D + . + . + . + z + */ + // keep only triangle that does intersect geom + + const unsigned int maxX_local = maxX - minX; + const unsigned int maxZ_local = maxZ - minZ; + + for ( x_local = 0; x_local < maxX_local; x_local++) + { + HeightFieldVertex *HeightFieldRow = tempHeightBuffer[x_local]; + HeightFieldVertex *HeightFieldNextRow = tempHeightBuffer[x_local + 1]; + + // First A + C = &HeightFieldRow [0]; + // First B + D = &HeightFieldNextRow[0]; + + for ( z_local = 0; z_local < maxZ_local; z_local++) + { + A = C; + B = D; + + C = &HeightFieldRow [z_local + 1]; + D = &HeightFieldNextRow[z_local + 1]; + + const dReal AHeight = A->vertex[1]; + const dReal BHeight = B->vertex[1]; + const dReal CHeight = C->vertex[1]; + const dReal DHeight = D->vertex[1]; + + const bool isACollide = AHeight > minO2Height; + const bool isBCollide = BHeight > minO2Height; + const bool isCCollide = CHeight > minO2Height; + const bool isDCollide = DHeight > minO2Height; + + A->state = !(isACollide); + B->state = !(isBCollide); + C->state = !(isCCollide); + D->state = !(isDCollide); + + if (isACollide || isBCollide || isCCollide) + { + HeightFieldTriangle * const CurrTriUp = &tempTriangleBuffer[numTri++]; + + CurrTriUp->state = false; + + // changing point order here implies to change it in isOnHeightField + CurrTriUp->vertices[0] = A; + CurrTriUp->vertices[1] = B; + CurrTriUp->vertices[2] = C; + + if (isContactNumPointsLimited) + CurrTriUp->setMinMax(); + CurrTriUp->isUp = true; + } + + if (isBCollide || isCCollide || isDCollide) + { + HeightFieldTriangle * const CurrTriDown = &tempTriangleBuffer[numTri++]; + + CurrTriDown->state = false; + // changing point order here implies to change it in isOnHeightField + + CurrTriDown->vertices[0] = D; + CurrTriDown->vertices[1] = B; + CurrTriDown->vertices[2] = C; + + + if (isContactNumPointsLimited) + CurrTriDown->setMinMax(); + CurrTriDown->isUp = false; + } + + + if (needFurtherPasses && + (isBCollide || isCCollide) + && + (AHeight > CHeight && + AHeight > BHeight && + DHeight > CHeight && + DHeight > BHeight)) + { + // That means Edge BC is concave, therefore + // BC Edge and B and C vertices cannot collide + + B->state = true; + C->state = true; + } + // should find a way to check other edges (AB, BD, CD) too for concavity + } + } + + // at least on triangle should intersect geom + dIASSERT (numTri != 0); + // pass1: VS triangle as Planes + // Group Triangle by same plane definition + // as Terrain often has many triangles using same plane definition + // then collide against that list of triangles. + { + + dVector3 Edge1, Edge2; + //compute all triangles normals. + for (unsigned int k = 0; k < numTri; k++) + { + HeightFieldTriangle * const itTriangle = &tempTriangleBuffer[k]; + + // define 2 edges and a point that will define collision plane + dVector3Subtract(itTriangle->vertices[2]->vertex, itTriangle->vertices[0]->vertex, Edge1); + dVector3Subtract(itTriangle->vertices[1]->vertex, itTriangle->vertices[0]->vertex, Edge2); + + // find a perpendicular vector to the triangle + if (itTriangle->isUp) + dVector3Cross(Edge1, Edge2, triplane); + else + dVector3Cross(Edge2, Edge1, triplane); + + // Define Plane + // Normalize plane normal + const dReal dinvlength = REAL(1.0) / dVector3Length(triplane); + triplane[0] *= dinvlength; + triplane[1] *= dinvlength; + triplane[2] *= dinvlength; + // get distance to origin from plane + triplane[3] = dVector3Dot(triplane, itTriangle->vertices[0]->vertex); + + // saves normal for collision check (planes, triangles, vertices and edges.) + dVector3Copy(triplane, itTriangle->planeDef); + // saves distance for collision check (planes, triangles, vertices and edges.) + itTriangle->planeDef[3] = triplane[3]; + } + + // group by Triangles by Planes sharing shame plane definition + if (tempPlaneBufferSize < numTri) + { + resetPlaneBuffer(); + allocatePlaneBuffer(numTri); + } + + unsigned int numPlanes = 0; + for (unsigned int k = 0; k < numTri; k++) + { + HeightFieldTriangle * const tri_base = &tempTriangleBuffer[k]; + + if (tri_base->state == true) + continue;// already tested or added to plane list. + + HeightFieldPlane * const currPlane = tempPlaneBuffer[numPlanes]; + currPlane->resetTriangleListSize(numTri - k); + currPlane->addTriangle(tri_base); + // saves normal for collision check (planes, triangles, vertices and edges.) + dVector3Copy(tri_base->planeDef, currPlane->planeDef); + // saves distance for collision check (planes, triangles, vertices and edges.) + currPlane->planeDef[3]= tri_base->planeDef[3]; + + const dReal normx = tri_base->planeDef[0]; + const dReal normy = tri_base->planeDef[1]; + const dReal normz = tri_base->planeDef[2]; + const dReal dist = tri_base->planeDef[3]; + + for (unsigned int m = k + 1; m < numTri; m++) + { + + HeightFieldTriangle * const tri_test = &tempTriangleBuffer[m]; + if (tri_test->state == true) + continue;// already tested or added to plane list. + + // normals and distance are the same. + if ( + dFabs(normy - tri_test->planeDef[1]) < dEpsilon && + dFabs(dist - tri_test->planeDef[3]) < dEpsilon && + dFabs(normx - tri_test->planeDef[0]) < dEpsilon && + dFabs(normz - tri_test->planeDef[2]) < dEpsilon + ) + { + currPlane->addTriangle (tri_test); + tri_test->state = true; + } + } + + tri_base->state = true; + if (isContactNumPointsLimited) + currPlane->setMinMax(); + + numPlanes++; + } + + // sort planes + if (isContactNumPointsLimited) + sortPlanes(numPlanes); + +#if !defined(NO_CONTACT_CULLING_BY_ISONHEIGHTFIELD2) + /* + Note by Oleh_Derevenko: + It seems to be incorrect to limit contact count by some particular value + since some of them (and even all of them) may be culled in following condition. + However I do not see an easy way to fix this. + If not that culling the flags modification should be changed here and + additionally repeated after some contacts have been generated (in "if (didCollide)"). + The maximum of contacts in flags would then be set to minimum of contacts + remaining and HEIGHTFIELDMAXCONTACTPERCELL. + */ + int planeTestFlags = (flags & ~NUMC_MASK) | HEIGHTFIELDMAXCONTACTPERCELL; + dIASSERT((HEIGHTFIELDMAXCONTACTPERCELL & ~NUMC_MASK) == 0); +#else // if defined(NO_CONTACT_CULLING_BY_ISONHEIGHTFIELD2) + int numMaxContactsPerPlane = dMIN(numMaxContactsPossible - numTerrainContacts, HEIGHTFIELDMAXCONTACTPERCELL); + int planeTestFlags = (flags & ~NUMC_MASK) | numMaxContactsPerPlane; + dIASSERT((HEIGHTFIELDMAXCONTACTPERCELL & ~NUMC_MASK) == 0); +#endif + + for (unsigned int k = 0; k < numPlanes; k++) + { + HeightFieldPlane * const itPlane = tempPlaneBuffer[k]; + + //set Geom + dGeomPlaneSetNoNormalize (sliding_plane, itPlane->planeDef); + //dGeomPlaneSetParams (sliding_plane, triangle_Plane[0], triangle_Plane[1], triangle_Plane[2], triangle_Plane[3]); + // find collision and compute contact points + bool didCollide = false; + const int numPlaneContacts = geomNPlaneCollider (o2, sliding_plane, planeTestFlags, PlaneContact, sizeof(dContactGeom)); + const sizeint planeTriListSize = itPlane->trianglelistCurrentSize; + for (i = 0; i < numPlaneContacts; i++) + { + dContactGeom *planeCurrContact = PlaneContact + i; + // Check if contact point found in plane is inside Triangle. + const dVector3 &pCPos = planeCurrContact->pos; + for (sizeint b = 0; planeTriListSize > b; b++) + { + if (m_p_data->IsOnHeightfield2 (itPlane->trianglelist[b]->vertices[0], + pCPos, + itPlane->trianglelist[b]->isUp)) + { + pContact = CONTACT(contact, numTerrainContacts*skip); + dVector3Copy(pCPos, pContact->pos); + dOPESIGN(pContact->normal, =, -, itPlane->planeDef); + pContact->depth = planeCurrContact->depth; + pContact->side1 = planeCurrContact->side1; + pContact->side2 = planeCurrContact->side2; + numTerrainContacts++; + if ( numTerrainContacts == numMaxContactsPossible ) + return numTerrainContacts; + + didCollide = true; + break; + } + } + } + if (didCollide) + { +#if defined(NO_CONTACT_CULLING_BY_ISONHEIGHTFIELD2) + /* Note by Oleh_Derevenko: + This code is not used - see another note above + */ + numMaxContactsPerPlane = dMIN(numMaxContactsPossible - numTerrainContacts, HEIGHTFIELDMAXCONTACTPERCELL); + planeTestFlags = (flags & ~NUMC_MASK) | numMaxContactsPerPlane; + dIASSERT((HEIGHTFIELDMAXCONTACTPERCELL & ~NUMC_MASK) == 0); +#endif + for (sizeint b = 0; planeTriListSize > b; b++) + { + // flag Triangles Vertices as collided + // to prevent any collision test of those + for (i = 0; i < 3; i++) + itPlane->trianglelist[b]->vertices[i]->state = true; + } + } + else + { + // flag triangle as not collided so that Vertices or Edge + // of that triangles will be checked. + for (sizeint b = 0; planeTriListSize > b; b++) + { + itPlane->trianglelist[b]->state = false; + } + } + } + } + + + + // pass2: VS triangle vertices + if (needFurtherPasses) + { + dxRay tempRay(0, 1); + dReal depth; + bool vertexCollided; + + // Only one contact is necessary for ray test + int rayTestFlags = (flags & ~NUMC_MASK) | 1; + dIASSERT((1 & ~NUMC_MASK) == 0); + // + // Find Contact Penetration Depth of each vertices + // + for (unsigned int k = 0; k < numTri; k++) + { + const HeightFieldTriangle * const itTriangle = &tempTriangleBuffer[k]; + if (itTriangle->state == true) + continue;// plane triangle did already collide. + + for (sizeint i = 0; i < 3; i++) + { + HeightFieldVertex *vertex = itTriangle->vertices[i]; + if (vertex->state == true) + continue;// vertice did already collide. + + vertexCollided = false; + const dVector3 &triVertex = vertex->vertex; + if ( geomNDepthGetter ) + { + depth = geomNDepthGetter( o2, + triVertex[0], triVertex[1], triVertex[2] ); + if (depth > dEpsilon) + vertexCollided = true; + } + else + { + // We don't have a GetDepth function, so do a ray cast instead. + // NOTE: This isn't ideal, and a GetDepth function should be + // written for all geom classes. + tempRay.length = (minO2Height - triVertex[1]) * REAL(1000.0); + + //dGeomRaySet( &tempRay, pContact->pos[0], pContact->pos[1], pContact->pos[2], + // - itTriangle->Normal[0], - itTriangle->Normal[1], - itTriangle->Normal[2] ); + dGeomRaySetNoNormalize(tempRay, triVertex, itTriangle->planeDef); + + if ( geomRayNCollider( &tempRay, o2, rayTestFlags, PlaneContact, sizeof( dContactGeom ) ) ) + { + depth = PlaneContact[0].depth; + vertexCollided = true; + } + } + if (vertexCollided) + { + pContact = CONTACT(contact, numTerrainContacts*skip); + //create contact using vertices + dVector3Copy (triVertex, pContact->pos); + //create contact using Plane Normal + dOPESIGN(pContact->normal, =, -, itTriangle->planeDef); + + pContact->depth = depth; + pContact->side1 = -1; + pContact->side2 = -1; + + numTerrainContacts++; + if ( numTerrainContacts == numMaxContactsPossible ) + return numTerrainContacts; + + vertex->state = true; + } + } + } + } + +#ifdef _HEIGHTFIELDEDGECOLLIDING + // pass3: VS triangle Edges + if (needFurtherPasses) + { + dVector3 Edge; + dxRay edgeRay(0, 1); + + int numMaxContactsPerTri = dMIN(numMaxContactsPossible - numTerrainContacts, HEIGHTFIELDMAXCONTACTPERCELL); + int triTestFlags = (flags & ~NUMC_MASK) | numMaxContactsPerTri; + dIASSERT((HEIGHTFIELDMAXCONTACTPERCELL & ~NUMC_MASK) == 0); + + for (unsigned int k = 0; k < numTri; k++) + { + const HeightFieldTriangle * const itTriangle = &tempTriangleBuffer[k]; + + if (itTriangle->state == true) + continue;// plane did already collide. + + for (sizeint m = 0; m < 3; m++) + { + const sizeint next = (m + 1) % 3; + HeightFieldVertex *vertex0 = itTriangle->vertices[m]; + HeightFieldVertex *vertex1 = itTriangle->vertices[next]; + + // not concave or under the AABB + // nor triangle already collided against vertices + if (vertex0->state == true && vertex1->state == true) + continue;// plane did already collide. + + dVector3Subtract(vertex1->vertex, vertex0->vertex, Edge); + edgeRay.length = dVector3Length (Edge); + dGeomRaySetNoNormalize(edgeRay, vertex1->vertex, Edge); + int prevTerrainContacts = numTerrainContacts; + pContact = CONTACT(contact, prevTerrainContacts*skip); + const int numCollision = geomRayNCollider(&edgeRay,o2,triTestFlags,pContact,skip); + dIASSERT(numCollision <= numMaxContactsPerTri); + + if (numCollision) + { + numTerrainContacts += numCollision; + + do + { + pContact = CONTACT(contact, prevTerrainContacts*skip); + + //create contact using Plane Normal + dOPESIGN(pContact->normal, =, -, itTriangle->planeDef); + + pContact->depth = DistancePointToLine(pContact->pos, vertex1->vertex, Edge, edgeRay.length); + } + while (++prevTerrainContacts != numTerrainContacts); + + if ( numTerrainContacts == numMaxContactsPossible ) + return numTerrainContacts; + + numMaxContactsPerTri = dMIN(numMaxContactsPossible - numTerrainContacts, HEIGHTFIELDMAXCONTACTPERCELL); + triTestFlags = (flags & ~NUMC_MASK) | numMaxContactsPerTri; + dIASSERT((HEIGHTFIELDMAXCONTACTPERCELL & ~NUMC_MASK) == 0); + } + } + + itTriangle->vertices[0]->state = true; + itTriangle->vertices[1]->state = true; + itTriangle->vertices[2]->state = true; + } + } +#endif // _HEIGHTFIELDEDGECOLLIDING + return numTerrainContacts; +} + +int dCollideHeightfield( dxGeom *o1, dxGeom *o2, int flags, dContactGeom* contact, int skip ) +{ + dIASSERT( skip >= (int)sizeof(dContactGeom) ); + dIASSERT( o1->type == dHeightfieldClass ); + dIASSERT((flags & NUMC_MASK) >= 1); + + int i; + + // if ((flags & NUMC_MASK) == 0) -- An assertion check is made on entry + // { flags = (flags & ~NUMC_MASK) | 1; dIASSERT((1 & ~NUMC_MASK) == 0); } + + int numMaxTerrainContacts = (flags & NUMC_MASK); + + dxHeightfield *terrain = (dxHeightfield*) o1; + + dVector3 posbak; + dMatrix3 Rbak; + dReal aabbbak[6]; + int gflagsbak; + dVector3 pos0,pos1; + dMatrix3 R1; + + int numTerrainContacts = 0; + int numTerrainOrigContacts = 0; + + //@@ Should find a way to set reComputeAABB to false in default case + // aka DHEIGHTFIELD_CORNER_ORIGIN not defined and terrain not PLACEABLE + // so that we can free some memory and speed up things a bit + // while saving some precision loss +#ifndef DHEIGHTFIELD_CORNER_ORIGIN + const bool reComputeAABB = true; +#else + const bool reComputeAABB = ( terrain->gflags & GEOM_PLACEABLE ) ? true : false; +#endif //DHEIGHTFIELD_CORNER_ORIGIN + + // + // Transform O2 into Heightfield Space + // + if (reComputeAABB) + { + // Backup original o2 position, rotation and AABB. + dVector3Copy( o2->final_posr->pos, posbak ); + dMatrix3Copy( o2->final_posr->R, Rbak ); + memcpy( aabbbak, o2->aabb, sizeof( dReal ) * 6 ); + gflagsbak = o2->gflags; + } + + if ( terrain->gflags & GEOM_PLACEABLE ) + { + // Transform o2 into heightfield space. + dSubtractVectors3( pos0, o2->final_posr->pos, terrain->final_posr->pos ); + dMultiply1_331( pos1, terrain->final_posr->R, pos0 ); + dMultiply1_333( R1, terrain->final_posr->R, o2->final_posr->R ); + + // Update o2 with transformed position and rotation. + dVector3Copy( pos1, o2->final_posr->pos ); + dMatrix3Copy( R1, o2->final_posr->R ); + } + +#ifndef DHEIGHTFIELD_CORNER_ORIGIN + o2->final_posr->pos[ 0 ] += terrain->m_p_data->m_fHalfWidth; + o2->final_posr->pos[ 2 ] += terrain->m_p_data->m_fHalfDepth; +#endif // DHEIGHTFIELD_CORNER_ORIGIN + + // Rebuild AABB for O2 + if (reComputeAABB) + o2->computeAABB(); + + // + // Collide + // + + //check if inside boundaries + // using O2 aabb + // aabb[6] is (minx, maxx, miny, maxy, minz, maxz) + const bool wrapped = terrain->m_p_data->m_bWrapMode != 0; + + if ( !wrapped ) + { + if ( o2->aabb[0] > terrain->m_p_data->m_fWidth //MinX + || o2->aabb[4] > terrain->m_p_data->m_fDepth)//MinZ + goto dCollideHeightfieldExit; + + if ( o2->aabb[1] < 0 //MaxX + || o2->aabb[5] < 0)//MaxZ + goto dCollideHeightfieldExit; + } + + { // To narrow scope of following variables + const dReal fInvSampleWidth = terrain->m_p_data->m_fInvSampleWidth; + int nMinX = (int)dFloor(dNextAfter(o2->aabb[0] * fInvSampleWidth, -dInfinity)); + int nMaxX = (int)dCeil(dNextAfter(o2->aabb[1] * fInvSampleWidth, dInfinity)); + const dReal fInvSampleDepth = terrain->m_p_data->m_fInvSampleDepth; + int nMinZ = (int)dFloor(dNextAfter(o2->aabb[4] * fInvSampleDepth, -dInfinity)); + int nMaxZ = (int)dCeil(dNextAfter(o2->aabb[5] * fInvSampleDepth, dInfinity)); + + if ( !wrapped ) + { + nMinX = dMAX( nMinX, 0 ); + nMaxX = dMIN( nMaxX, terrain->m_p_data->m_nWidthSamples - 1 ); + nMinZ = dMAX( nMinZ, 0 ); + nMaxZ = dMIN( nMaxZ, terrain->m_p_data->m_nDepthSamples - 1 ); + + dIASSERT ((nMinX < nMaxX) && (nMinZ < nMaxZ)); + } + + numTerrainOrigContacts = numTerrainContacts; + numTerrainContacts += terrain->dCollideHeightfieldZone( + nMinX,nMaxX,nMinZ,nMaxZ,o2,numMaxTerrainContacts - numTerrainContacts, + flags,CONTACT(contact,numTerrainContacts*skip),skip ); + dIASSERT( numTerrainContacts <= numMaxTerrainContacts ); + } + + dContactGeom *pContact; + for ( i = numTerrainOrigContacts; i != numTerrainContacts; ++i ) + { + pContact = CONTACT(contact,i*skip); + pContact->g1 = o1; + pContact->g2 = o2; + // pContact->side1 = -1; -- Oleh_Derevenko: sides must not be erased here as they are set by respective colliders during ray/plane tests + // pContact->side2 = -1; + } + + + //------------------------------------------------------------------------------ + +dCollideHeightfieldExit: + + if (reComputeAABB) + { + // Restore o2 position, rotation and AABB + dVector3Copy( posbak, o2->final_posr->pos ); + dMatrix3Copy( Rbak, o2->final_posr->R ); + memcpy( o2->aabb, aabbbak, sizeof(dReal)*6 ); + o2->gflags = gflagsbak; + + // + // Transform Contacts to World Space + // + if ( terrain->gflags & GEOM_PLACEABLE ) + { + for ( i = 0; i < numTerrainContacts; ++i ) + { + pContact = CONTACT(contact,i*skip); + dCopyVector3( pos0, pContact->pos ); + +#ifndef DHEIGHTFIELD_CORNER_ORIGIN + pos0[ 0 ] -= terrain->m_p_data->m_fHalfWidth; + pos0[ 2 ] -= terrain->m_p_data->m_fHalfDepth; +#endif // !DHEIGHTFIELD_CORNER_ORIGIN + + dMultiply0_331( pContact->pos, terrain->final_posr->R, pos0 ); + + dAddVectors3( pContact->pos, pContact->pos, terrain->final_posr->pos ); + dCopyVector3( pos0, pContact->normal ); + + dMultiply0_331( pContact->normal, terrain->final_posr->R, pos0 ); + } + } +#ifndef DHEIGHTFIELD_CORNER_ORIGIN + else + { + for ( i = 0; i < numTerrainContacts; ++i ) + { + pContact = CONTACT(contact,i*skip); + pContact->pos[ 0 ] -= terrain->m_p_data->m_fHalfWidth; + pContact->pos[ 2 ] -= terrain->m_p_data->m_fHalfDepth; + } + } +#endif // !DHEIGHTFIELD_CORNER_ORIGIN + } + // Return contact count. + return numTerrainContacts; +} + + + |