Exemple #1
0
TraceResult CheckNode(
    int nodeIndex,
    float startFraction,
    float endFraction,

    const Vec3& start,
    const Vec3& end,
    const Vec3& extents,
    const TraceBounds& boundsAabb,

    TraceResult result,
    const Bsp::CollisionBsp& bsp)
{
    if (result.pathFraction <= startFraction)
    {
        // already hit something nearer
        return result;
    }

    if (nodeIndex < 0)
    {
        // this is a leaf
        const auto& leaf = bsp.leaves[-(nodeIndex + 1)];

        for (int i = 0; i < leaf.leafBrushCount; i++)
        {
            const auto& brush =
                    bsp.brushes[bsp.leafBrushes[leaf.firstLeafBrushIndex + i].brushIndex];

            // Don't even bother if there are no brush sides.
            if (brush.brush.sideCount <= 0)
            {
                continue;
            }

            // Only test solid brushes
            // 1 == CONTENTS_SOLID
            if (!(bsp.textures[brush.brush.textureIndex].contentFlags & 1))
            {
                continue;
            }

            // Early exit if the AABB doesn't collide.
            if (AabbDontIntersect(
                        boundsAabb.aabbMin,
                        boundsAabb.aabbMax,
                        brush.aabbMin,
                        brush.aabbMax))
            {
                continue;
            }

            result = CheckBrush(bsp, brush.brush, boundsAabb.bounds, result);
        }

        // don't have to do anything else for leaves
        return result;
    }

    // this is a node
    const auto& node = bsp.nodes[nodeIndex];
    const auto& plane = bsp.planes[node.planeIndex];

    float startDistance = DotF(start, plane.normal) - plane.distance;
    float endDistance   = DotF(end, plane.normal) - plane.distance;

    // Offset used for non-ray tests.
    const auto& bounds = boundsAabb.bounds;
    float offset = bounds.sphereRadius;

    // extents are zero for ray or sphere tests.
    offset +=
        std::abs(extents.data[0] * plane.normal.data[0]) +
        std::abs(extents.data[1] * plane.normal.data[1]) +
        std::abs(extents.data[2] * plane.normal.data[2]);

    if (startDistance >= offset && endDistance >= offset)
    {
        // both points are in front of the plane
        // so check the front child
        return CheckNode(
            node.childIndex[0],
            startFraction,
            endFraction,
            start,
            end,
            extents,
            boundsAabb,
            result,
            bsp);
    }

    if (startDistance < -offset && endDistance < -offset)
    {
        // both points are behind the plane
        // so check the back child
        return CheckNode(
            node.childIndex[1],
            startFraction,
            endFraction,
            start,
            end,
            extents,
            boundsAabb,
            result,
            bsp);
    }

    // the line spans the splitting plane
    // Default values assume startDistance == endDistance.
    int side = 0;
    float fraction1 = 1.0f;
    float fraction2 = 0.0f;

    // split the segment into two
    if (startDistance < endDistance)
    {
        // back
        side = 1;
        float inverseDistance = 1.0f / (startDistance - endDistance);
        fraction1 = (startDistance - offset + EPSILON) * inverseDistance;
        fraction2 = (startDistance + offset + EPSILON) * inverseDistance;
    }

    if (endDistance < startDistance)
    {
        // front
        float inverseDistance = 1.0f / (startDistance - endDistance);
        fraction1 = (startDistance + offset + EPSILON) * inverseDistance;
        fraction2 = (startDistance - offset - EPSILON) * inverseDistance;
    }

    // make sure the numbers are valid
    fraction1 = Clamp0To1(fraction1);
    fraction2 = Clamp0To1(fraction2);

    // calculate the middle point for the first side
    {
        auto middleFraction =
                startFraction + (endFraction - startFraction) * fraction1;

        auto middle = Lerp(start, end, fraction1);

        // check the first side
        result = CheckNode(
            node.childIndex[side],
            startFraction,
            middleFraction,
            start,
            middle,
            extents,
            boundsAabb,
            result,
            bsp);
    }

    // calculate the middle point for the second side
    {
        auto middleFraction =
                startFraction + (endFraction - startFraction) * fraction2;

        auto middle = Lerp(start, end, fraction2);

        // check the second side
        result = CheckNode(
            node.childIndex[!side],
            middleFraction,
            endFraction,
            middle,
            end,
            extents,
            boundsAabb,
            result,
            bsp);
    }

    return result;
}
Exemple #2
0
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 );
  }
}
void CQuake3BSP::CheckNode(int nodeIndex, float startRatio, float endRatio, CVector3 vStart, CVector3 vEnd)
{
    // Check if the next node is a leaf
    if(nodeIndex < 0)
    {
        // If this node in the BSP is a leaf, we need to negate and add 1 to offset
        // the real node index into the m_pLeafs[] array.  You could also do [~nodeIndex].
        tBSPLeaf *pLeaf = &m_pLeafs[-(nodeIndex + 1)];

        // We have a leaf, so let's go through all of the brushes for that leaf
        for(int i = 0; i < pLeaf->numOfLeafBrushes; i++)
        {
            // Get the current brush that we going to check
            tBSPBrush *pBrush = &m_pBrushes[m_pLeafBrushes[pLeaf->leafBrush + i]];

            // Check if we have brush sides and the current brush is solid and collidable
            if((pBrush->numOfBrushSides > 0) && (m_pTextures[pBrush->textureID].textureType & 1))
            {
                // Now we delve into the dark depths of the real calculations for collision.
                // We can now check the movement vector against our brush planes.
                CheckBrush(pBrush, vStart, vEnd);
            }
        }

        // Since we found the brushes, we can go back up and stop recursing at this level
        return;
    }

    // Grad the next node to work with and grab this node's plane data
    tBSPNode *pNode = &m_pNodes[nodeIndex];
    tBSPPlane *pPlane = &m_pPlanes[pNode->plane];

    // Here we use the plane equation to find out where our initial start position is
    // according the the node that we are checking.  We then grab the same info for the end pos.
    float startDistance = Dot(vStart, pPlane->vNormal) - pPlane->d;
    float endDistance = Dot(vEnd, pPlane->vNormal) - pPlane->d;
    float offset = 0.0f;

    // If we are doing sphere collision, include an offset for our collision tests below
    if(m_traceType == TYPE_SPHERE)
        offset = m_traceRadius;

    // Here we check to see if we are working with a BOX or not
    else if(m_traceType == TYPE_BOX)
    {
        // Get the distance our AABB is from the current splitter plane
        offset = (float)(fabs( m_vExtents.x * pPlane->vNormal.x ) +
                         fabs( m_vExtents.y * pPlane->vNormal.y ) +
                         fabs( m_vExtents.z * pPlane->vNormal.z ) );
    }

    // Here we check to see if the start and end point are both in front of the current node.
    // If so, we want to check all of the nodes in front of this current splitter plane.
    if(startDistance >= offset && endDistance >= offset)
    {
        // Traverse the BSP tree on all the nodes in front of this current splitter plane
        CheckNode(pNode->front, startDistance, endDistance, vStart, vEnd);
    }
    // If both points are behind the current splitter plane, traverse down the back nodes
    else if(startDistance < -offset && endDistance < -offset)
    {
        // Traverse the BSP tree on all the nodes in back of this current splitter plane
        CheckNode(pNode->back, startDistance, endDistance, vStart, vEnd);
    }
    else
    {
        // If we get here, then our ray needs to be split in half to check the nodes
        // on both sides of the current splitter plane.  Thus we create 2 ratios.
        float Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f;
        CVector3 vMiddle;	// This stores the middle point for our split ray

        // Start of the side as the front side to check
        int side = pNode->front;

        // Here we check to see if the start point is in back of the plane (negative)
        if(startDistance < endDistance)
        {
            // Since the start position is in back, let's check the back nodes
            side = pNode->back;

            // Here we create 2 ratios that hold a distance from the start to the
            // extent closest to the start (take into account a sphere and epsilon).
            float inverseDistance = 1.0f / (startDistance - endDistance);
            Ratio1 = (startDistance - offset - kEpsilon) * inverseDistance;
            Ratio2 = (startDistance + offset + kEpsilon) * inverseDistance;
        }
        // Check if the starting point is greater than the end point (positive)
        else if(startDistance > endDistance)
        {
            // This means that we are going to recurse down the front nodes first.
            // We do the same thing as above and get 2 ratios for split ray.
            float inverseDistance = 1.0f / (startDistance - endDistance);
            Ratio1 = (startDistance + offset + kEpsilon) * inverseDistance;
            Ratio2 = (startDistance - offset - kEpsilon) * inverseDistance;
        }

        // Make sure that we have valid numbers and not some weird float problems.
        // This ensures that we have a value from 0 to 1 as a good ratio should be :)
        if (Ratio1 < 0.0f) Ratio1 = 0.0f;
        else if (Ratio1 > 1.0f) Ratio1 = 1.0f;

        if (Ratio2 < 0.0f) Ratio2 = 0.0f;
        else if (Ratio2 > 1.0f) Ratio2 = 1.0f;

        // Just like we do in the Trace() function, we find the desired middle
        // point on the ray, but instead of a point we get a middleRatio percentage.
        middleRatio = startRatio + ((endRatio - startRatio) * Ratio1);
        vMiddle = vStart + ((vEnd - vStart) * Ratio1);

        // Now we recurse on the current side with only the first half of the ray
        CheckNode(side, startRatio, middleRatio, vStart, vMiddle);

        // Now we need to make a middle point and ratio for the other side of the node
        middleRatio = startRatio + ((endRatio - startRatio) * Ratio2);
        vMiddle = vStart + ((vEnd - vStart) * Ratio2);

        // Depending on which side should go last, traverse the bsp with the
        // other side of the split ray (movement vector).
        if(side == pNode->back)
            CheckNode(pNode->front, middleRatio, endRatio, vMiddle, vEnd);
        else
            CheckNode(pNode->back, middleRatio, endRatio, vMiddle, vEnd);
    }
}