void Collision::CheckBrush( Q3BspBrush *brush ) { float startFraction = -1.0f; float endFraction = 1.0f; bool startsOut = false; bool endsOut = false; vec3f collNormal; float endDistSaved = 0.0f; collNormal = vec3f(0.0f, 0.0f, 0.0f); for (int i = 0; i < brush->n_brushsides; i++) { Q3BspBrushSide *brushSide = &mQ3Map->m_pBrushSides[brush->brushside + i]; Q3BspPlane *plane = &mQ3Map->m_pPlanes[brushSide->plane]; float startDistance, endDistance; vec3f planeNormal = vec3f(plane->normal); if (mTraceType == TT_RAY) { startDistance = vec3dot( &mInputStart, &planeNormal ) - plane->dist; endDistance = vec3dot( &mInputEnd, &planeNormal ) - plane->dist; } else /*if (mTraceType == TT_SPHERE)*/ { startDistance = vec3dot( &mInputStart, &planeNormal ) - (plane->dist + mTraceRadius); endDistance = vec3dot( &mInputEnd, &planeNormal ) - (plane->dist + mTraceRadius); } if (startDistance > 0) startsOut = true; if (endDistance > 0) endsOut = true; // make sure the trace isn't completely on one side of the brush if (startDistance > 0 && endDistance > 0) { // both are in front of the plane, its outside of this brush return; } if (startDistance <= 0 && endDistance <= 0) { // both are behind this plane, it will get clipped by another one continue; } if (startDistance > endDistance) { // line is entering into the brush float fraction = (startDistance - EPSILON) / (startDistance - endDistance); if (fraction < 0) fraction = 0; if (fraction > startFraction) { startFraction = fraction; collNormal = vec3f(plane->normal); endDistSaved = endDistance; } } else { // line is leaving the brush float fraction = (startDistance + EPSILON) / (startDistance - endDistance); if (fraction < endFraction) endFraction = fraction; } } if (startsOut == false) { mOutputStartOut = false; if (endsOut == false) mOutputAllSolid = true; return; } if (startFraction < endFraction) { if (startFraction > -1 && startFraction < mOutputFraction) { if (startFraction < 0) startFraction = 0; mGoodPos = false; mOutputFraction = startFraction; mCollisionNormal = collNormal; mEndDistance = endDistSaved; } } }
vec_t vec3distfastvec(const vec3_t b, const vec3_t c){ vec3_t math; vec3subvec(math, b, c); return vec3dot(math, math); }
void Collision::CheckNode( int nodeIndex, float startFraction, float endFraction, vec3f start, vec3f end) { if (nodeIndex < 0) { // this is a leaf Q3BspLeaf *leaf = &mQ3Map->m_pLeafs[-(nodeIndex + 1)]; for (int i = 0; i < leaf->n_leafbrushes; i++) { Q3BspBrush *brush = &mQ3Map->m_pBrushes[mQ3Map->m_pLeafBrushes[leaf->leafbrush + i]]; if (brush->n_brushsides > 0 && (mQ3Map->m_pTextures[brush->texture].contents & 1) ) { CheckBrush( brush ); } } // don't have to do anything else for leaves return; } // this is a node Q3BspNode *node = &mQ3Map->m_pNodes[nodeIndex]; Q3BspPlane *plane = &mQ3Map->m_pPlanes[node->plane]; vec3f normal = vec3f(plane->normal); vec3f startDX = vec3f(start); vec3f endDX = vec3f(end); float startDistance = vec3dot( &startDX, &normal ) - plane->dist; float endDistance = vec3dot( &endDX, &normal ) - plane->dist; float offset; if (mTraceType == TT_RAY) { offset = 0.0f; } else if (mTraceType == TT_SPHERE) { offset = mTraceRadius; } else { offset = 0.0f; } if (startDistance >= offset && endDistance >= offset) { // both points are in front of the plane // so check the front child CheckNode( node->children[0], startFraction, endFraction, start, end ); } else if (startDistance < -offset && endDistance < -offset) { // both points are behind the plane // so check the back child CheckNode( node->children[1], startFraction, endFraction, start, end ); } else { // the line spans the splitting plane int side; float fraction1, fraction2, middleFraction; vec3f middle; // split the segment into two if (startDistance < endDistance) { side = 1; // back float inverseDistance = 1.0f / (startDistance - endDistance); fraction1 = (startDistance - offset + EPSILON) * inverseDistance; fraction2 = (startDistance + offset + EPSILON) * inverseDistance; } else if (endDistance < startDistance) { side = 0; // front float inverseDistance = 1.0f / (startDistance - endDistance); fraction1 = (startDistance + offset + EPSILON) * inverseDistance; fraction2 = (startDistance - offset - EPSILON) * inverseDistance; } else { side = 0; // front fraction1 = 1.0f; fraction2 = 0.0f; } // make sure the numbers are valid if (fraction1 < 0.0f) fraction1 = 0.0f; else if (fraction1 > 1.0f) fraction1 = 1.0f; if (fraction2 < 0.0f) fraction2 = 0.0f; else if (fraction2 > 1.0f) fraction2 = 1.0f; // calculate the middle point for the first side middleFraction = startFraction + (endFraction - startFraction) * fraction1; /*for (int i = 0; i < 3; i++) middle[i] = start[i] + fraction1 * (end[i] - start[i]);*/ middle = start + fraction1 * (end - start); // check the first side CheckNode( node->children[side], startFraction, middleFraction, start, middle ); // calculate the middle point for the second side middleFraction = startFraction + (endFraction - startFraction) * fraction2; /*for (int i = 0; i < 3; i++) middle[i] = start[i] + fraction2 * (end[i] - start[i]);*/ middle = start + fraction2 * (end - start); // check the second side CheckNode( node->children[!side], middleFraction, endFraction, middle, end ); } }