internal bool IsInsideClipBoundary(FrustumFace face) { switch (face) { case FrustumFace.Left: return(IsInsideLeftClipBoundary); case FrustumFace.Right: return(IsInsideRightClipBoundary); case FrustumFace.Bottom: return(IsInsideBottomClipBoundary); case FrustumFace.Top: return(IsInsideTopClipBoundary); case FrustumFace.Near: return(IsInsideNearClipBoundary); case FrustumFace.Far: return(IsInsideFarClipBoundary); default: return(false); } }
//Clip the edge with a face private static UnitProperty ClipEdge(UnitProperty start, UnitProperty end, FrustumFace faceIndex) { float t = 0; //vector = end - start var vector = end - start; // result = start + (end - start) * t switch (faceIndex) { case FrustumFace.Left: // (start.x + vector.x * t) / (start.w + vector.w * t) = -1 // t = -(start.x + start.w) / (vector.x + vector.w) // and (vector.x + vector.w) != 0 // because there is most one vertex on the face(x = -w), so (vector.x + vector.w) = (end.x - start.x + end.w - start.w) != 0 t = -(start.PositionTransformed.X + start.PositionTransformed.W) / (vector.PositionTransformed.X + vector.PositionTransformed.W); break; case FrustumFace.Right: // (start.x + vector.x * t) / (start.w + vector.w * t) = 1 // t = (start.w - start.x) / (vector.x - vector.w) // and (vector.x - vector.w) != 0 // because there is most one vertex on the face(x = w), so (vector.x - vector.w) = (end.x - start.x - end.w + start.w) != 0 t = (start.PositionTransformed.W - start.PositionTransformed.X) / (vector.PositionTransformed.X - vector.PositionTransformed.W); break; case FrustumFace.Bottom: // (start.y + vector.y * t) / (start.w + vector.w * t) = -1 // t = (start.y + start.w) / (vector.y + vector.w) // and (vector.y + vector.w) != 0 // because there is most one vertex on the face(y = -w), so (vector.y + vector.w) = (end.y - start.y + end.w - start.w) != 0 t = -(start.PositionTransformed.Y + start.PositionTransformed.W) / (vector.PositionTransformed.Y + vector.PositionTransformed.W); break; case FrustumFace.Top: // (start.y + vector.y * t) / (start.w + vector.w * t) = 1 // t = (start.w - start.y) / (vector.y - vector.w) // and (vector.y - vector.w) != 0 // because there is most one vertex on the face(y = w), so (vector.y - vector.w) = (end.y - start.y - end.w + start.w) != 0 t = (start.PositionTransformed.W - start.PositionTransformed.Y) / (vector.PositionTransformed.Y - vector.PositionTransformed.W); break; case FrustumFace.Near: // (start.z + vector.z * t) / (start.w + vector.w * t) = 0 // t = - (start.z / vector.z) // and vector.z != 0 // because there is most one vertex on the face(z = 0), so vector.z = (end.z - start.z) != 0 t = -(start.PositionTransformed.Z / vector.PositionTransformed.Z); break; case FrustumFace.Far: // (start.z + vector.z * t) / (start.w + vector.w * t) = 1 // t = (start.w - start.z) / (vector.z - vector.w) // and (vector.z - vector.w) != 0 // because there is most one vertex on the face(z = w), so (vector.z - vector.w) = (end.z - start.z - end.w + start.w) != 0 t = (start.PositionTransformed.W - start.PositionTransformed.Z) / (vector.PositionTransformed.Z - vector.PositionTransformed.W); break; default: break; } var result = start + vector * t; //divide for the new vertex result.PositionAfterDivide = result.PositionTransformed / result.PositionTransformed.W; return(result); }
//Sutherland-Hodgeman algorithm private void ClipPrimitives(ref DrawCall drawCall) { //we enum all primitives in the draw call for (int i = 0; i < drawCall.Primitives.Length; i++) { //create a temp for calculating var result = new Primitive(drawCall.Primitives[i].Vertics); //for all face of frustum for (FrustumFace face = FrustumFace.Left; face <= FrustumFace.Far; face++) { var verticesList = new List <UnitProperty>(); //enum all edge in edge primitive for (int vertexIndex = 0; vertexIndex < result.Vertics.Length; vertexIndex++) { var currentVertex = result.Vertics[vertexIndex]; var nextVertex = result.Vertics[0]; //the last edge if (vertexIndex + 1 != result.Vertics.Length) { nextVertex = result.Vertics[vertexIndex + 1]; } //the edge is not inside the clip boundary, so we do not add the vertex to result if (currentVertex.IsInsideClipBoundary(face) is false && nextVertex.IsInsideClipBoundary(face) is false) { continue; } //the edge is inside the clip boundary, so we add the vertex to result if (currentVertex.IsInsideClipBoundary(face) is true && nextVertex.IsInsideClipBoundary(face) is true) { //we only add the start vertex verticesList.Add(currentVertex); continue; } //the start is not insided but the end is insided. if (currentVertex.IsInsideClipBoundary(face) is false) { verticesList.Add(ClipEdge(currentVertex, nextVertex, face)); continue; } //the start is insided but the end is not insided if (nextVertex.IsInsideClipBoundary(face) is false) { verticesList.Add(currentVertex); verticesList.Add(ClipEdge(currentVertex, nextVertex, face)); continue; } } //update the primitive result = new Primitive(verticesList.ToArray()); } //get result drawCall.Primitives[i] = result; } }