// Right now there are some inconsistencies in the way this function behaves // compared to the OBB-OBB version. I think this one is correct, but it's tough to tell... std::list<Contact> OBB::GetClosestPoints(const AABB& other) const { std::list<Contact> ret; // Find the vector from our center to theirs vec2 centerVecN = other.C - C; vec2 faceN(0); // Make it a unit vector in the direction of largest magnitude faceN = glm::normalize(maxComp(centerVecN)); // Get supporting vertex / vertices along that direction std::array<int, 2> sV; int nSupportVerts = GetSupportIndices(faceN, sV); // one support vert means a vertex-face collision if (nSupportVerts == 1) { // Two contact points; the support vertex, and it's best neighbor std::array<glm::vec2, 2> pA_arr = GetSupportNeighbor(faceN, sV[0]); for (auto& pA : pA_arr) { vec2 pB = other.clamp(pA); float d = glm::distance(pA, pB); ret.emplace_back((RigidBody_2D *)this, (RigidBody_2D *)&other, pA, pB, faceN, d); } } // face-face collsion, average two support verts into one contact point else { vec2 pA = 0.5f * (GetVert(sV[0]) + GetVert(sV[1])); vec2 pB = other.clamp(pA); float d = glm::distance(pA, pB); ret.emplace_back((RigidBody_2D *)this, (RigidBody_2D *)&other, pA, pB, faceN, d); } return ret; }
// Indices of the vertices returned by the above function int OBB::GetSupportIndices(vec2 n, std::array<int, 2>& sV) const { // Find the furthest vertex float dMin = -FLT_MAX; for (int i = 0; i < 4; i++) { vec2 v = GetVert(i); float d = glm::dot(n, v); if (d > dMin) { dMin = d; sV[0] = i; } } int num(1); // If there's a different vertex for (int i = 0; i < 4; i++) { if (i == sV[0]) continue; vec2 v = GetVert(i); float d = glm::dot(n, v); // That's pretty close... if (feq(d, dMin, 100.f * kEPS)) { // Take it too dMin = d; sV[num++] = i; } } return num; }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRADDispColl::DispUVToSurfDiagonalTLtoBR( int snapU, int snapV, int nextU, int nextV, float fracU, float fracV, Vector &surfPt, float pushEps ) { // get displacement width int width = GetWidth(); if( ( fracU + fracV ) >= ( 1.0f + TRIEDGE_EPSILON ) ) { int triIndices[3]; triIndices[0] = nextV * width + snapU; triIndices[1] = nextV * width + nextU; triIndices[2] = snapV * width + nextU; Vector triVerts[3]; for( int i = 0; i < 3; i++ ) { GetVert( triIndices[i], triVerts[i] ); } Vector edgeU, edgeV; edgeU = triVerts[0] - triVerts[1]; edgeV = triVerts[2] - triVerts[1]; surfPt = triVerts[1] + edgeU * ( 1.0f - fracU ) + edgeV * ( 1.0f - fracV ); // get face normal and push point away from surface the given pushEps Vector vNormal; vNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( vNormal ); surfPt += ( vNormal * pushEps ); } else { int triIndices[3]; triIndices[0] = snapV * width + snapU; triIndices[1] = nextV * width + snapU; triIndices[2] = snapV * width + nextU; Vector triVerts[3]; for( int i = 0; i < 3; i++ ) { GetVert( triIndices[i], triVerts[i] ); } Vector edgeU, edgeV; edgeU = triVerts[2] - triVerts[0]; edgeV = triVerts[1] - triVerts[0]; // get the position surfPt = triVerts[0] + edgeU * fracU + edgeV * fracV; // get face normal and push point away from surface the given pushEps Vector vNormal; vNormal = CrossProduct( edgeU, edgeV ); VectorNormalize( vNormal ); surfPt += ( vNormal * pushEps ); } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRADDispColl::ClosestBaseFaceData( Vector const &worldPt, Vector &closePt, Vector &closeNormal ) { Vector delta; float minDist = 99999.0f; int ndxMin = -1; for( int ndxPt = 0; ndxPt < 4; ndxPt++ ) { delta = worldPt - m_Points[ndxPt]; float dist = delta.Length(); if( dist < minDist ) { minDist = dist; ndxMin = ndxPt; } } int width = GetWidth(); int height = GetHeight(); switch( ndxMin ) { case 0: { ndxMin = 0; break; } case 1: { ndxMin = ( height - 1 ) * width; break; } case 2: { ndxMin = ( height * width ) - 1; break; } case 3: { ndxMin = width - 1; break; } default: { return; } } GetVert( ndxMin, closePt ); GetVertNormal( ndxMin, closeNormal ); }
void CSubTriangle::GetDivision(int& o_nStartOfLongest, CVector3DF& o_vMidPoint, CVector3DF& o_vMidPointBar) const { //get parent bezier patch // const CBezierPatch& ParentBezierPatch = m_pParent_SubTriangle ? m_pParent_SubTriangle->GetBezierPatch() : m_Parent.GetBezierPatch(); float fDistance = 0.0f; CVector3DF vMidPointBar, vMidPoint; for (int i = 0; i < 3; i++) { const int j = (i + 1) % 3; vMidPointBar = (GetVertBar(i) + GetVertBar(j)) / 2.0f; vMidPoint = m_Parent.GetBezierPatch().GetPointFromBarycentric(vMidPointBar); const float fNewDistance = (GetVert(i) - vMidPoint).Length();// this should work with bezier stuff if (fNewDistance > fDistance) { fDistance = fNewDistance; o_nStartOfLongest = i; o_vMidPoint = vMidPoint; o_vMidPointBar = vMidPointBar; } } }
// Pick the best support neighbor along n // It's critical that this function return an array where the vertices // are in the proper clockwise order std::array<vec2, 2> OBB::GetSupportNeighbor(vec2 n, int idx) const { std::array<vec2, 2> ret; vec2 vb = GetVert(idx); vec2 va = GetVert(idx - 1); vec2 vc = GetVert(idx + 1); vec2 nab = glm::normalize(perp(vb - va)); vec2 nbc = glm::normalize(perp(vc - vb)); float d1 = glm::dot(nab, n); float d2 = glm::dot(nbc, n); if (d1 > d2) return{ { va, vb } }; return{ { vb, vc } }; }
// Not tested, and there may be a better way... bool IsOverlapping( Circle * pCirc, OBB * pOBB ) { for ( int i = 0; i < 4; i++ ) if ( IsPointInside( GetVert( pOBB, i ), pCirc ) ) return true; return IsPointInside( pCirc->v2Center, pOBB ); }
//----------------------------------------------------------------------------- void CCollision::Resolve() { CIwManaged::Resolve(); IwResolveManagedHash(&m_pModel, IW_GRAPHICS_RESTYPE_MODEL); //Build face normals (done on resolve to save disk space) for(uint32 i = 0; i < m_Points.size(); i+=3) { CIwVec3 v1 = (CIwVec3)GetVert(i); CIwVec3 v2 = (CIwVec3)GetVert(i+1); CIwVec3 v3 = (CIwVec3)GetVert(i+2); CIwSVec3 cross = (CIwSVec3)(v2 - v1).Cross(v3 - v1); if( cross != CIwSVec3::g_Zero ) cross.Normalise(); m_Norms.push_back(cross); } }
void OpenGLRect::DrawMyBothRect(const float& fLineWidth /*= 1.0f*/) { GetVert(m_fRectVert); GetColor(m_fRectColor); DrawMyRect(1.0f, GL_QUADS); GetBorderVert(m_fRectVert); GetBorderColor(m_fRectColor); DrawMyRect(fLineWidth); }
// Resolve ghost modelspace location void GhostCollision::ResolveLocation() { float y1 = INT32_MAX; float y2 = INT32_MIN; float x1 = INT32_MAX; float x2 = INT32_MIN; for (uint32 i = 0; i < m_Points.size(); i++) { CIwFVec3 v = (CIwFVec3)GetVert(i); if (v.y < y1) y1 = v.y; if (v.y > y2) y2 = v.y; if (v.x < x1) x1 = v.x; if (v.x > x2) x2 = v.x; } ghostY = y2; ghostX = x1; ghostW = x2 - x1; }
bool CSubTriangle::Intersect(const CRay &ray, CIntersactionInfo &intersectionInfo, bool bDebug) const { // intersectionInfo.m_nBezierIntersections += 1; // QElapsedTimer timer; // timer.start(); bool bIntersect = false; if (intersectionInfo.m_bHighQuality) { bIntersect = m_Parent.GetBezierPatch().IntersectHighQuality(ray, intersectionInfo, *this, bDebug); // if (b && GetSettings()->m_bNormalSmoothing) // { // intersectionInfo.m_vNormal = GetSubSurfSmoothedNormal(intersectionInfo.m_vBarCoordsGlobal); // } } else { bIntersect = CUtils::IntersectTriangle(ray, intersectionInfo, GetVert(0), GetVert(1), GetVert(2), m_vABar, m_vBBar, m_vCBar); if (bIntersect) { if (GetSettings()->m_bNormalSmoothing) { //Smoothed normal CVector3DF vNormalA = intersectionInfo.m_vBarCoordsGlobal.X() * m_Parent.A().Normal_Get(); CVector3DF vNormalB = intersectionInfo.m_vBarCoordsGlobal.Y() * m_Parent.B().Normal_Get(); CVector3DF vNormalC = intersectionInfo.m_vBarCoordsGlobal.Z() * m_Parent.C().Normal_Get(); intersectionInfo.m_vNormal = vNormalA + vNormalB + vNormalC; intersectionInfo.m_vNormal.Normalize(); } else { CVector3DF m_vAB = GetVert(1)- GetVert(0); CVector3DF m_vAC = GetVert(2)- GetVert(0); intersectionInfo.m_vNormal = CVector3DF::Normal(m_vAB, m_vAC); } } } // intersectionInfo.m_nObjTime += timer.nsecsElapsed(); return bIntersect; }
void OpenGLRect::DrawMyFillRect(void) { GetVert(m_fRectVert); GetColor(m_fRectColor); DrawMyRect(1.0f, GL_QUADS); }
// Simlar to the AABB case, but we only care about the center of the circle std::list<Contact> GetSpecContacts( Circle * pCirc, AABB * pAABB) { // Determine which feature region we're on vec2 n; int vIdx( -1 ); // top/bottom face region if ( (pAABB->Right() < pCirc->v2Center.x || pAABB->Left() > pCirc->v2Center.x) == false ) { // Circle is below box if ( pCirc->v2Center.y < pAABB->Bottom() ) { vIdx = 1; n = vec2( 0, 1 ); } else { vIdx = 3; n = vec2( 0, -1 ); } } // left/right face region else if ( (pAABB->Top() < pCirc->v2Center.y || pAABB->Bottom() > pCirc->v2Center.y) == false ) { // Circle is to the left of the box if ( pCirc->v2Center.x < pAABB->Left() ) { vIdx = 2; n = vec2( 1, 0 ); } else { vIdx = 0; n = vec2( -1, 0 ); } } // Vertex region else { // Determine which vertex bool bAIsLeft = (pCirc->v2Center.x < pAABB->Left()); bool bAIsBelow = (pCirc->v2Center.y < pAABB->Bottom()); if ( bAIsLeft ) { if ( bAIsBelow ) vIdx = 2; else vIdx = 3; } else { if ( bAIsBelow ) vIdx = 1; else vIdx = 0; } // We don't need to average contact positions for the corner case vec2 posB = GetVert( pAABB, vIdx ); n = glm::normalize( posB - pCirc->v2Center ); vec2 posA = pCirc->v2Center + pCirc->circData.fRadius * n; float fDist = glm::distance( posA, posB ); return{ Contact( pCirc, pAABB, posA, posB, n, fDist ) }; } // For a face region collision, we want to make sure the contact knows it's // working with one of the box's face normals (meaning distance is along that normal) vec2 posB = 0.5f*(GetVert( pAABB, vIdx ) + GetVert( pAABB, vIdx + 1 )); //n = glm::normalize( posB - pCirc->v2Center ); vec2 posA = pCirc->v2Center + pCirc->circData.fRadius * n; float fDist = glm::dot( posB - posA, n ); return{ Contact( pCirc, pAABB, posA, posB, n, fDist ) }; }
//----------------------------------------------------------------------------- int32 GhostCollision::GetFaceUnderCursor(int32 x, int32 y) { //Calculate pos/dir of cursor from camera CIwFVec3 pos = IwGxGetViewMatrix().t; CIwFVec3 dir(x, y, IwGxGetPerspMul()); dir.x -= IwGxGetScreenWidth()/2; dir.y -= IwGxGetScreenHeight()/2; //Extend to the far plane dir *= IW_FIXED_DIV(IwGxGetFarZ(), IwGxGetPerspMul()); //Transform pos/dir into model space dir = IwGxGetViewMatrix().RotateVec(dir); dir = modelMatrix->TransposeRotateVec(dir); //Use more accurate normalise dir.Normalise(); // Scale to touch far plane dir *= IwGxGetFarZ(); pos = modelMatrix->TransposeTransformVec(pos); //find first face intersection int32 minf = INT32_MAX; //nearest intersection distance uint32 nearest = 0; //nearest intersection index for (uint32 i = 0; i < m_Points.size(); i += 3) { CIwFVec3 v1 = (CIwFVec3)GetVert(i); CIwFVec3 v2 = (CIwFVec3)GetVert(i+1); CIwFVec3 v3 = (CIwFVec3)GetVert(i+2); float f = 0; if (IwIntersectLineTriNorm(pos, dir, v1, v2, v3, m_Norms[i/3], f)) { if (f < minf) { minf = f; nearest = i; } } /*{ // Draw the custom resource. Kills fps but helps debugging. CIwMaterial* pMat = IW_GX_ALLOC_MATERIAL(); pMat->SetColAmbient(0xff0000ff); pMat->SetCullMode(CIwMaterial::CULL_NONE); IwGxSetMaterial(pMat); CIwFVec3* verts = IW_GX_ALLOC(CIwFVec3, 3); verts[0] = v1; verts[1] = v2; verts[2] = v3; IwGxSetVertStreamModelSpace(verts, 3); IwGxDrawPrims(IW_GX_TRI_LIST, NULL, 3); }*/ } if (minf != INT32_MAX) { return nearest; } return -1; }