/// <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 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 CalcTangent(Vec3F p1, Vec3F p2, Vec3F p3) { Vec3F v1 = (p1 - p2) / (p1 - p2).Length; Vec3F v2 = (p3 - p2) / (p3 - p2).Length; Vec3F bisector = v1 + v2; Vec3F normal = Vec3F.Cross(v1, v2); if (normal.Length < 0.1e-5f) // 3 points colinear { return(v2); } Vec3F tangent = Vec3F.Cross(normal, bisector); tangent = tangent / tangent.Length; return(tangent); }
/// <summary> /// Orthonormalizes the matrix</summary> /// <param name="m">The matrix to orthonormalize</param> public void OrthoNormalize(Matrix4F m) { Vec3F xAxis = new Vec3F(m.M11, m.M12, m.M13); Vec3F yAxis = new Vec3F(m.M21, m.M22, m.M23); Vec3F zAxis; zAxis = Vec3F.Cross(xAxis, yAxis); yAxis = Vec3F.Cross(zAxis, xAxis); xAxis = xAxis / xAxis.Length; yAxis = yAxis / yAxis.Length; zAxis = zAxis / zAxis.Length; M11 = xAxis.X; M12 = xAxis.Y; M13 = xAxis.Z; M21 = yAxis.X; M22 = yAxis.Y; M23 = yAxis.Z; M31 = zAxis.X; M32 = zAxis.Y; M33 = zAxis.Z; M41 = m.M41; M42 = m.M42; M43 = m.M43; }
public static void WritePolygon( Vector3[] pts, List <Vector3> pos, List <Vector3> normal, List <uint> indices) { // Calculate a normal for this face! /////////////////////////////////////// // We have good C++ code for fitting planes to faces. // But we don't have access to it from this code! var n = new Vector3(0.0f, 0.0f, 0.0f); for (uint c = 0; c < pts.Length - 2; ++c) { n += /*Vector3.Normalize*/ (Vector3.Cross(pts[c] - pts[c + 1], pts[c + 2] - pts[c + 1])); } // note that if we don't do the above normalize, then the contribution of // each triangle will be weighted by the length of the cross product (which is // proportional to the triangle area -- effectively weighting by triangle area). n = -Vector3.Normalize(n); var firstIndex = pos.Count; for (uint c = 0; c < pts.Length; ++c) { pos.Add(pts[c]); normal.Add(n); } for (uint c = 2; c < pts.Length; ++c) { indices.Add((uint)(firstIndex)); indices.Add((uint)(firstIndex + c - 1)); indices.Add((uint)(firstIndex + c)); } }
public static void CreateCylinder(float rad1, float rad2, float height, uint slices, uint stacks, List <Vector3> pos, List <Vector3> normal, List <uint> indices) { float stackHeight = height / stacks; // Amount to increment radius as we move up each stack level from bottom to top. float radiusStep = (rad2 - rad1) / stacks; uint numRings = stacks + 1; // Compute vertices for each stack ring. for (uint i = 0; i < numRings; ++i) { float y = i * stackHeight; float r = rad1 + i * radiusStep; // Height and radius of next ring up. float y_next = (i + 1) * stackHeight; float r_next = rad1 + (i + 1) * radiusStep; // vertices of ring float dTheta = 2.0f * MathHelper.Pi / slices; for (uint j = 0; j <= slices; ++j) { float c = (float)Math.Cos(j * dTheta); float s = (float)Math.Sin(j * dTheta); // tex coord if needed. //float u = j/(float) slices; //float v = 1.0f - (float) i/stacks; // Partial derivative in theta direction to get tangent vector (this is a unit vector). Vector3 T = new Vector3(-s, 0.0f, c); // Compute tangent vector down the slope of the cone (if the top/bottom // radii differ then we get a cone and not a true cylinder). Vector3 P = new Vector3(r * c, y, r * s); Vector3 P_next = new Vector3(r_next * c, y_next, r_next * s); Vector3 B = P - P_next; B.Normalize(); Vector3 N = Vector3.Cross(T, B); N.Normalize(); P.Z *= -1; N.Z *= -1; pos.Add(P); if (normal != null) { normal.Add(N); } } } uint numRingVertices = slices + 1; // Compute indices for each stack. for (uint i = 0; i < stacks; ++i) { for (uint j = 0; j < slices; ++j) { indices.Add(i * numRingVertices + j); indices.Add((i + 1) * numRingVertices + j + 1); indices.Add((i + 1) * numRingVertices + j); indices.Add(i * numRingVertices + j); indices.Add(i * numRingVertices + j + 1); indices.Add((i + 1) * numRingVertices + j + 1); } } // build bottom cap. if (rad1 > 0) { uint baseIndex = (uint)pos.Count; // Duplicate cap vertices because the texture coordinates and normals differ. float y = 0; // vertices of ring float dTheta = 2.0f * MathHelper.Pi / slices; for (uint i = 0; i <= slices; ++i) { float x = rad1 * (float)Math.Cos(i * dTheta); float z = rad1 * (float)Math.Sin(i * dTheta); // Map [-1,1]-->[0,1] for planar texture coordinates. //float u = +0.5f * x / mBottomRadius + 0.5f; //float v = -0.5f * z / mBottomRadius + 0.5f; Vector3 p = new Vec3F(x, y, -z); pos.Add(p); if (normal != null) { normal.Add(new Vec3F(0.0f, -1.0f, 0.0f)); } } // cap center vertex pos.Add(new Vec3F(0.0f, y, 0.0f)); if (normal != null) { normal.Add(new Vec3F(0.0f, -1.0f, 0.0f)); } // tex coord for center cap 0.5f, 0.5f // index of center vertex uint centerIndex = (uint)pos.Count - 1; for (uint i = 0; i < slices; ++i) { indices.Add(centerIndex); indices.Add(baseIndex + i + 1); indices.Add(baseIndex + i); } } // build top cap. if (rad2 > 0) { uint baseIndex = (uint)pos.Count; // Duplicate cap vertices because the texture coordinates and normals differ. float y = height; // vertices of ring float dTheta = 2.0f * MathHelper.Pi / slices; for (uint i = 0; i <= slices; ++i) { float x = rad2 * (float)Math.Cos(i * dTheta); float z = rad2 * (float)Math.Sin(i * dTheta); // Map [-1,1]-->[0,1] for planar texture coordinates. //float u = +0.5f * x / mTopRadius + 0.5f; //float v = -0.5f * z / mTopRadius + 0.5f; pos.Add(new Vec3F(x, y, -z)); if (normal != null) { normal.Add(new Vec3F(0.0f, 1.0f, 0.0f)); } } // pos, norm, tex1 for cap center vertex pos.Add(new Vec3F(0.0f, y, 0.0f)); if (normal != null) { normal.Add(new Vec3F(0.0f, 1.0f, 0.0f)); } // tex coord 0.5f, 0.5f // index of center vertex uint centerIndex = (uint)pos.Count - 1; for (uint i = 0; i < slices; ++i) { indices.Add(centerIndex); indices.Add(baseIndex + i); indices.Add(baseIndex + i + 1); } } }
private void CalcPlaneNormal(int planeIndex, Vec3F u1, Vec3F u2) { m_planes[planeIndex].Normal = Vec3F.Cross(u1, u2); m_planes[planeIndex].Normal.Normalize(); m_planes[planeIndex].Distance = 0; }
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> /// 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); }