private void CheckBrush(ref Q3Brush brush, ref Q3CollisionData cd) { float startFraction = -1.0f; float endFraction = 1.0f; bool startsOut = false; bool endsOut = false; for (int i = 0; i < brush.NumSides; i++) { Q3BrushSide brushSide = m_brushSides[brush.FirstSide + i]; Q3Plane plane = m_planes[brushSide.PlaneNum]; float startDistance = 0, endDistance = 0; if (cd.type == Q3CollisionType.Ray) { startDistance = cd.startPosition.Dot(new Vector3( plane.Normal) ) - plane.Distance; endDistance = cd.endPosition.Dot(new Vector3(plane.Normal) ) - plane.Distance; } else if (cd.type == Q3CollisionType.Sphere) { startDistance = cd.startPosition.Dot(new Vector3( plane.Normal)) - (plane.Distance + cd.sphereRadius); endDistance = cd.endPosition.Dot(new Vector3( plane.Normal)) - (plane.Distance + cd.sphereRadius); } else if (cd.type == Q3CollisionType.Box) { Vector3 offset = new Vector3(); if ((new Vector3(plane.Normal)).X < 0) offset.X = cd.boxMaximums.X; else offset.X = cd.boxMinimums.X; if ((new Vector3(plane.Normal)).Y < 0) offset.Y = cd.boxMaximums.Y; else offset.Y = cd.boxMinimums.Y; if ((new Vector3(plane.Normal)).Z < 0) offset.Z = cd.boxMaximums.Z; else offset.Z = cd.boxMinimums.Z; startDistance = (cd.startPosition.X + offset.X) * (new Vector3(plane.Normal)).X + (cd.startPosition.Y + offset.Y) * (new Vector3(plane.Normal)).Y + (cd.startPosition.Z + offset.Z) * (new Vector3(plane.Normal)).Z - plane.Distance; endDistance = (cd.endPosition.X + offset.X) * (new Vector3(plane.Normal)).X + (cd.endPosition.Y + offset.Y) * (new Vector3(plane.Normal)).Y + (cd.endPosition.Z + offset.Z) * (new Vector3(plane.Normal)).Z - plane.Distance; } if (startDistance > 0) startsOut = true; if (endDistance > 0) endsOut = true; if (startDistance > 0 && endDistance > 0) { return; } if (startDistance <= 0 && endDistance <= 0) { continue; } if (startDistance > endDistance) { float fraction = (startDistance - Q3Constants.Epsilon) / (startDistance - endDistance); if (fraction > startFraction) startFraction = fraction; } else { float fraction = (startDistance + Q3Constants.Epsilon) / (startDistance - endDistance); if (fraction < endFraction) endFraction = fraction; } } if (false == startsOut) { cd.startOutside = false; if (false == endsOut) cd.inSolid = true; return; } if (startFraction < endFraction) { if (startFraction > -1.0f && startFraction < cd.ratio) { if (startFraction < 0) startFraction = 0; cd.ratio = startFraction; } } }
public Q3CollisionData TraceSphere(Vector3 startPosition, Vector3 endPosition, float sphereRadius) { Q3CollisionData collision = new Q3CollisionData(); collision.type = Q3CollisionType.Sphere; collision.sphereRadius = sphereRadius; return Trace(startPosition, endPosition, ref collision); }
public Q3CollisionData TraceRay(Vector3 startPosition, Vector3 endPosition) { Q3CollisionData collision = new Q3CollisionData(); collision.type = Q3CollisionType.Ray; return Trace(startPosition, endPosition, ref collision); }
public Q3CollisionData TraceBox(Vector3 startPosition, Vector3 endPosition, AABB bbox) { Q3CollisionData collision = new Q3CollisionData(); Vector3 boxMinimums = bbox.Min; Vector3 boxMaximums = bbox.Max; if (boxMinimums.X == 0 && boxMinimums.Y == 0 && boxMinimums.Z == 0 && boxMaximums.X == 0 && boxMaximums.Y == 0 && boxMaximums.Z == 0) { collision.type = Q3CollisionType.Ray; return Trace(startPosition, endPosition, ref collision); } if (boxMaximums.X < boxMinimums.X) { float x = boxMaximums.X; boxMaximums.X = boxMinimums.X; boxMinimums.X = x; } if (boxMaximums.Y < boxMinimums.Y) { float y = boxMaximums.Y; boxMaximums.Y = boxMinimums.Y; boxMinimums.Y = y; } if (boxMaximums.Z < boxMinimums.Z) { float z = boxMaximums.Z; boxMaximums.Z = boxMinimums.Z; boxMinimums.Z = z; } Vector3 boxExtents = new Vector3(); boxExtents.X = Math.Max(Math.Abs(boxMaximums.X), Math.Abs(boxMinimums.X)); boxExtents.Y = Math.Max(Math.Abs(boxMaximums.Y), Math.Abs(boxMinimums.Y)); boxExtents.Z = Math.Max(Math.Abs(boxMaximums.Z), Math.Abs(boxMinimums.Z)); collision.type = Q3CollisionType.Box; collision.boxMinimums = boxMinimums; collision.boxMaximums = boxMaximums; collision.boxExtents = boxExtents; return Trace(startPosition, endPosition, ref collision); }
private void WalkNode(int nodeIndex, float startRatio, float endRatio, Vector3 startPosition, Vector3 endPosition, ref Q3CollisionData cd) { // Is this a leaf? if (0 > nodeIndex) { Q3Leaf leaf = m_leaves[~nodeIndex]; for (int i = 0; i < leaf.BrushCount; i++) { Q3Brush brush = m_brushes[m_leafBrushes[leaf.BrushStart + i]]; if (0 < brush.NumSides && 1 == ((int)m_shaders[brush.ShaderIndex].ContentFlags & 1)) { CheckBrush(ref brush, ref cd); } } return; } // This is a node Q3Node thisNode = m_nodes[nodeIndex]; Q3Plane thisPlane = m_planes[thisNode.Plane]; float startDistance = startPosition.Dot( new Vector3(thisPlane.Normal) ) - thisPlane.Distance; float endDistance = endPosition.Dot( new Vector3(thisPlane.Normal) ) - thisPlane.Distance; float offset = 0; // Set offset for sphere-based collision if (cd.type == Q3CollisionType.Sphere) { offset = cd.sphereRadius; } // Set offest for box-based collision if (cd.type == Q3CollisionType.Box) { offset = Math.Abs(cd.boxExtents.X * (new Vector3(thisPlane.Normal)).X) + Math.Abs(cd.boxExtents.Y * (new Vector3(thisPlane.Normal)).Y) + Math.Abs(cd.boxExtents.Z * (new Vector3(thisPlane.Normal)).Z); } if (startDistance >= offset && endDistance >= offset) { // Both points are in front WalkNode(thisNode.Front, startRatio, endRatio, startPosition, endPosition, ref cd); } else if (startDistance < -offset && endDistance < -offset) { WalkNode(thisNode.Back, startRatio, endRatio, startPosition, endPosition, ref cd); } else { // The line spans the splitting plane int side = 0; float fraction1 = 0.0f; float fraction2 = 0.0f; float middleFraction = 0.0f; Vector3 middlePosition = new Vector3(); if (startDistance < endDistance) { side = 1; float inverseDistance = 1.0f / (startDistance - endDistance); fraction1 = (startDistance - offset + Q3Constants.Epsilon) * inverseDistance; fraction2 = (startDistance + offset + Q3Constants.Epsilon) * inverseDistance; } else if (endDistance < startDistance) { side = 0; float inverseDistance = 1.0f / (startDistance - endDistance); fraction1 = (startDistance + offset + Q3Constants.Epsilon) * inverseDistance; fraction2 = (startDistance - offset - Q3Constants.Epsilon) * inverseDistance; } else { side = 0; fraction1 = 1.0f; fraction2 = 0.0f; } 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; middleFraction = startRatio + (endRatio - startRatio) * fraction1; middlePosition = startPosition + fraction1 * (endPosition - startPosition); int side1; int side2; if (0 == side) { side1 = thisNode.Front; side2 = thisNode.Back; } else { side1 = thisNode.Back; side2 = thisNode.Front; } WalkNode(side1, startRatio, middleFraction, startPosition, middlePosition, ref cd); middleFraction = startRatio + (endRatio - startRatio) * fraction2; middlePosition = startPosition + fraction2 * (endPosition - startPosition); WalkNode(side2, middleFraction, endRatio, middlePosition, endPosition, ref cd); } }
private Q3CollisionData Trace(Vector3 startPosition, Vector3 endPosition, ref Q3CollisionData collision) { collision.startOutside = true; collision.inSolid = false; collision.ratio = 1.0f; collision.startPosition = startPosition; collision.endPosition = endPosition; collision.collisionPoint = startPosition; WalkNode(0, 0.0f, 1.0f, startPosition, endPosition, ref collision); if (1.0f == collision.ratio) { collision.collisionPoint = endPosition; } else { collision.collisionPoint = startPosition + (collision.ratio - 0.002f) * (endPosition - startPosition); } return collision; }