/// <summary> /// Finds the intersection point of the ray with the given plane. Checks /// for the ray being parallel with the plane or the ray pointing away /// from the plane</summary> /// <param name="plane">Must be constructed "by the rules" of Plane3F</param> /// <param name="intersectionPoint">The resulting intersection point or /// the zero-point if there was no intersection</param> /// <returns>True if the ray points towards the plane and intersects it</returns> public bool IntersectPlane(Plane3F plane, out Vec3F intersectionPoint) { // both the normal and direction must be unit vectors. float cos = Vec3F.Dot(plane.Normal, Direction); if (Math.Abs(cos) > 0.0001f) { // dist > 0 means "on the same side as the normal", aka, "the front". float dist = plane.SignedDistance(Origin); // There are two cases for the ray shooting away from the plane: // 1. If the ray is in front of the plane and pointing away, // then 'cos' and 'dist' are both > 0. // 2. If the ray is in back of the plane and pointing away, // then 'cos' and 'dist' are both < 0. // So, if the signs are the same, then there's no intersection. // So, if 'cos' * 'dist' is positive, then there's no intersection. if (cos * dist < 0.0) { // There are two cases for the ray hitting the plane: // 1. Origin is behind the plane, so the ray and normal are aligned // and 'dist' is < 0. We need to negate 'dist'. // 2. Origin is in front of the plane, so the ray needs to be negated // so that cos(angle-180) is calculated. 'dist' is > 0. // Either way, we've got a -1 thrown into the mix. Tricky! float t = -dist / cos; intersectionPoint = Origin + (Direction * t); return(true); } } intersectionPoint = new Vec3F(0, 0, 0); return(false); }
/// <summary> /// Finds the intersection point of the ray with the given plane. The ray is /// treated as an infinite line, so it will intersect going "forwards" or /// "backwards". The plane equation is the opposite of that for Plane3F. For /// all points 'p' on the plane: planeNormal * p + D = 0</summary> /// <param name="planeNormal">Plane normal</param> /// <param name="planeDistance">Plane constant</param> /// <returns>Intersection point of ray with plane</returns> /// <remarks>Does not check for ray parallel to plane</remarks> public Vec3F IntersectPlane(Vec3F planeNormal, float planeDistance) { float t = -(Vec3F.Dot(planeNormal, Origin) + planeDistance) / Vec3F.Dot(planeNormal, Direction); return(Origin + (Direction * t)); }
/// <summary> /// Returns the point on this ray resulting from projecting 'point' onto this ray. /// This is also the same as finding the closest point on this ray to 'point'.</summary> /// <param name="point">Point to project onto this ray</param> /// <returns>Closest point on this ray to 'point'</returns> public Vec3F ProjectPoint(Vec3F point) { Vec3F normalizedDir = Vec3F.Normalize(Direction); float distToX = Vec3F.Dot(point - Origin, normalizedDir); Vec3F x = Origin + distToX * normalizedDir; return(x); }
/// <summary> /// Constructs a plane from 3 non-linear points on the plane, such that /// Normal * p = Distance</summary> /// <param name="p1">First point</param> /// <param name="p2">Second point</param> /// <param name="p3">Third point</param> public Plane3F(Vec3F p1, Vec3F p2, Vec3F p3) { Vec3F d12 = p2 - p1; Vec3F d13 = p3 - p1; Vec3F normal = Vec3F.Cross(d12, d13); Normal = Vec3F.Normalize(normal); Distance = Vec3F.Dot(Normal, p1); }
/// <summary> /// Tests if a specified ray intersects this sphere</summary> /// <param name="ray">The ray, with an origin and unit-length direction. Only intersections in /// front of the ray count.</param> /// <param name="x">The intersection point, if there was an intersection</param> /// <returns>True iff ray intersects this sphere</returns> /// <remarks>Algorithm is from _Real-Time Rendering_, p. 299</remarks> public bool Intersects(Ray3F ray, out Vec3F x) { // vector from ray's origin to sphere's center Vec3F oToC = Center - ray.Origin; // 'd' is the distance from the ray's origin to the nearest point on the ray to Center. // Assumes that Direction has unit length. float d = Vec3F.Dot(oToC, ray.Direction); // distToCenterSquared is the distance from ray's origin to Center, squared. float distToCenterSquared = oToC.LengthSquared; float radiusSquared = Radius * Radius; // if the sphere is behind the ray and the ray's origin is outside the sphere... if (d < 0 && distToCenterSquared > radiusSquared) { x = new Vec3F(); return(false); } // 'm' is the distance from the ray to the center. Pythagorean's theorem. float mSquared = distToCenterSquared - d * d; if (mSquared > radiusSquared) { x = new Vec3F(); return(false); } // 'q' is the distance from the first intersection point to the nearest point // on the ray to Center. Pythagorean's theorem. float q = (float)Math.Sqrt(radiusSquared - mSquared); // 't' is the distance along 'ray' to the point of first intersection. float t; if (distToCenterSquared > radiusSquared) { // ray's origin is outside the sphere. t = d - q; } else { // ray's origin is inside the sphere. t = d + q; } // the point of intersection is ray's origin plus distance along ray's direction x = ray.Origin + t * ray.Direction; return(true); }
/// <summary> /// Tests if any part of the polygon is inside this frustum. Errs on the side of inclusion. /// The polygon and the frustum and the eye must be in the same space -- probably object space /// to avoid the expense of transforming all the polygons into view space unnecessarily.</summary> /// <param name="vertices">A polygon. Can be non-convex and non-planar for the frustum test. /// Must be convex and planar for backface culling.</param> /// <param name="eye">The camera's eye associated with this frustum. Is only used if 'backfaceCull' /// is 'true'.</param> /// <param name="backfaceCull">Should back-facing polygons always be excluded?</param> /// <returns>True iff any part of the polygon is inside this frustum</returns> public bool ContainsPolygon(Vec3F[] vertices, Vec3F eye, bool backfaceCull) { // If all of the points are outside any one plane, then the polygon is not contained. for (int p = 0; p < 6; p++) { int v = 0; for (; v < vertices.Length; v++) { float dist = m_planes[p].SignedDistance(vertices[v]); if (dist > 0.0f) { break; } } if (v == vertices.Length) { return(false); } } if (backfaceCull) { // First calculate polygon normal, pointing "out from" the visible side. Vec3F normal = new Vec3F(); for (int i = 2; i < vertices.Length; i++) { normal = Vec3F.Cross(vertices[0] - vertices[1], vertices[i] - vertices[1]); // Make sure that these 3 verts are not collinear if (normal.LengthSquared != 0) { break; } } // We can't use the near plane's normal, since it may no longer be pointing in the correct // direction (due to non-uniform scalings, shear transforms, etc.). if (Vec3F.Dot(normal, vertices[0] - eye) <= 0) { return(false); } } // This is an approximation. To be absolutely sure, we'd have to test the 8 points // of this frustum against the plane of the polygon and against the plane of each edge, // perpendicular to the polygon plane. return(true); }
private Vec3F CalcEndTangents(Vec3F p1, Vec3F p2, Vec3F p2Tangent) { Vec3F v1 = p1 - p2; float v1Len = v1.Length; Vec3F u1 = v1 / v1Len; // Make sure that v1 and p2Tangent are facing the same way if (Vec3F.Dot(v1, p2Tangent) < 0) { p2Tangent = -p2Tangent; } // calc t for which the tangent vector t*p2Tangent is projected on 1/2 of v1 float t = (0.5f * v1Len) / Vec3F.Dot(p2Tangent, u1); Vec3F p3 = p2 + (p2Tangent * t); Vec3F tangent = (p3 - p1) / (p3 - p1).Length; return(tangent); }
private void BuildCurves() { m_curves.Clear(); if (Count > 1) { if (Count == 2) { BuildInitialCurveFrom2Points(); } else { Vec3F zeroVec = new Vec3F(0, 0, 0); Vec3F[] tangents = CalcPointTangents(); for (int i = 1; i < Count; i++) { Vec3F chord = this[i].Position - this[i - 1].Position; float segLen = chord.Length * 0.333f; Vec3F[] points = new Vec3F[4]; points[0] = this[i - 1].Position; points[3] = this[i].Position; // calc points[1] if (this[i - 1].Tangent2 != zeroVec) { points[1] = this[i - 1].Position + this[i - 1].Tangent2; } else { Vec3F tangent = tangents[i - 1]; if (Vec3F.Dot(chord, tangent) < 0) { tangent = -tangent; } points[1] = this[i - 1].Position + (tangent * segLen); } // calc points[2] if (this[i].Tangent1 != zeroVec) { points[2] = this[i].Position + this[i].Tangent1; } else { Vec3F tangent = tangents[i]; if (Vec3F.Dot(-chord, tangent) < 0) { tangent = -tangent; } points[2] = this[i].Position + (tangent * segLen); } BezierCurve curve = new BezierCurve(points); m_curves.Add(curve); } // Calculate last curve if is closed if (m_isClosed) { Vec3F[] points = new Vec3F[4]; points[0] = this[Count - 1].Position; points[3] = this[0].Position; float tanLen = (points[3] - points[0]).Length / 3.0f; Vec3F v = m_curves[m_curves.Count - 1].ControlPoints[2] - points[0]; v = v / v.Length; points[1] = points[0] - (v * tanLen); v = m_curves[0].ControlPoints[1] - points[3]; v = v / v.Length; points[2] = points[3] - (v * tanLen); BezierCurve curve = new BezierCurve(points); m_curves.Add(curve); } } } }
private bool IsLeftOf(TriVx v0, TriVx v1, TriVx v2, Vec3F normal) { Vec3F cross = Vec3F.Cross(v1.V - v0.V, v2.V - v0.V); return(Vec3F.Dot(cross, normal) > 0.0f); }
/// <summary> /// Returns the signed distance along the ray from the ray's origin to the projection of 'p' /// onto this ray</summary> /// <param name="p">Point to be projected onto this ray</param> /// <returns>Signed distance along the ray, from the ray's origin to the projected point. /// A negative number means that 'p' falls "behind" the ray.</returns> public float GetDistanceToProjection(Vec3F p) { return(Vec3F.Dot(p - Origin, Direction)); }
/// <summary> /// Finds the intersection point of the ray with the given convex polygon, if any. Returns the nearest /// vertex of the polygon to the intersection point. Can optionally backface cull the polygon. /// For backface culling, the front of the polygon is considered to be the side that has /// the vertices ordered counter-clockwise.</summary> /// <param name="vertices">Polygon vertices</param> /// <param name="intersectionPoint">Intersection point</param> /// <param name="nearestVert">Nearest polygon vertex to the point of intersection</param> /// <param name="normal">Normal unit-length vector, facing out from the side whose /// vertices are ordered counter-clockwise</param> /// <param name="backFaceCull">True if backface culling should be done</param> /// <returns>True iff the ray intersects the polygon</returns> public bool IntersectPolygon(Vec3F[] vertices, out Vec3F intersectionPoint, out Vec3F nearestVert, out Vec3F normal, bool backFaceCull) { bool intersects = true; int sign = 0; normal = new Vec3F(); // First calc polygon normal for (int i = 2; i < vertices.Length; i++) { normal = Vec3F.Cross(vertices[i] - vertices[1], vertices[0] - vertices[1]); // Make sure that these 3 verts are not collinear float lengthSquared = normal.LengthSquared; if (lengthSquared != 0) { normal *= (1.0f / (float)Math.Sqrt(lengthSquared)); break; } } if (backFaceCull) { if (Vec3F.Dot(normal, Direction) >= 0.0) { intersectionPoint = new Vec3F(); nearestVert = new Vec3F(); return(false); } } // Build plane and check if the ray intersects the plane. intersects = IntersectPlane( new Plane3F(normal, vertices[1]), out intersectionPoint); // Now check all vertices against intersection point for (int i = 0; i < vertices.Length && intersects; i++) { int i1 = i; int i0 = (i == 0 ? vertices.Length - 1 : i - 1); Vec3F cross = Vec3F.Cross(vertices[i1] - vertices[i0], intersectionPoint - vertices[i0]); // Check if edge and intersection vectors are collinear if (cross.LengthSquared == 0.0f) { // check if point is on edge intersects = ((vertices[i1] - vertices[i0]).LengthSquared >= (intersectionPoint - vertices[i0]).LengthSquared); break; } else { float dot = Vec3F.Dot(cross, normal); if (i == 0) { sign = Math.Sign(dot); } else if (Math.Sign(dot) != sign) { intersects = false; } } } // if we have a definite intersection, find the closest snap-to vertex. if (intersects) { nearestVert = vertices[0]; float nearestDistSqr = (intersectionPoint - nearestVert).LengthSquared; for (int i = 1; i < vertices.Length; i++) { float distSqr = (vertices[i] - intersectionPoint).LengthSquared; if (distSqr < nearestDistSqr) { nearestDistSqr = distSqr; nearestVert = vertices[i]; } } } else { nearestVert = s_blankPoint; } return(intersects); }
/// <summary> /// Returns the signed distance from this plane. This only works if the plane equation is /// Normal * p = Distance for any point 'p' on the plane. A positive result means that /// the point is on the same side of the plane that the normal is "on".</summary> /// <param name="point">Mathematical point</param> /// <returns>Signed distance from 'point' to this plane. Positive means "in front".</returns> public float SignedDistance(Vec3F point) { return(Vec3F.Dot(Normal, point) - Distance); }
/// <summary> /// Constructs a plane from normal and point on plane, such that Normal * p = Distance</summary> /// <param name="normal">Normal. Should be a unit vector.</param> /// <param name="pointOnPlane">Point on plane</param> public Plane3F(Vec3F normal, Vec3F pointOnPlane) { Normal = normal; Distance = Vec3F.Dot(normal, pointOnPlane); }