/// <summary> /// Extends sphere to enclose another sphere</summary> /// <param name="sphere">Sphere to enclose</param> /// <returns>Extended sphere</returns> public Sphere3F Extend(Sphere3F sphere) { if (!m_initialized) { Center = sphere.Center; Radius = sphere.Radius; m_initialized = true; } else if (!Contains(sphere)) { if (Center == sphere.Center) { Radius = sphere.Radius; } else { Vec3F normal = Vec3F.Normalize(sphere.Center - Center); Vec3F p1 = sphere.Center + (normal * sphere.Radius); Vec3F p2 = Center - (normal * Radius); Radius = (p2 - p1).Length / 2.0f; Center = (p1 + p2) / 2.0f; } } return(this); }
/// <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> /// Constructor</summary> /// <param name="angle">Angle of rotation</param> /// <param name="axis">Axis of rotation. The resulting axis has length of 1.</param> public AngleAxisF(float angle, Vec3F axis) { Angle = angle; Axis.X = axis.X; Axis.Y = axis.Y; Axis.Z = axis.Z; Axis.Normalize(); }
/// <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> /// Transforms the given plane by this matrix</summary> /// <param name="p">Input plane</param> /// <param name="transposeOfInverse">Transpose of the inverse of this matrix, for performance reasons</param> /// <param name="result">Output plane</param> public void Transform(Plane3F p, Matrix4F transposeOfInverse, out Plane3F result) { Vec3F normal = p.Normal; Vec3F point = p.PointOnPlane(); TransformNormal(normal, transposeOfInverse, out normal); Transform(point, out point); normal.Normalize(); result = new Plane3F(normal, point); }
/// <summary> /// Transforms the given plane by this matrix</summary> /// <param name="p">Input plane</param> /// <param name="result">Output plane</param> public void Transform(Plane3F p, out Plane3F result) { Vec3F normal = p.Normal; Vec3F point = p.PointOnPlane(); TransformNormal(normal, out normal); Transform(point, out point); normal.Normalize(); result = new Plane3F(normal, point); }
/// <summary> /// Extends sphere to enclose point</summary> /// <param name="pt">Point to enclose</param> /// <returns>Extended sphere</returns> public Sphere3F Extend(Vec3F pt) { if (!m_initialized) { Center = pt; Radius = 0; m_initialized = true; } else if (!Contains(pt)) { Vec3F normal = Vec3F.Normalize(pt - Center); Vec3F p = Center - (normal * Radius); Radius = (pt - p).Length / 2.0f; Center = pt + p / 2.0f; } return(this); }
/// <summary> /// Creates Billboard matrix from the given parameters.</summary> public static void CreateBillboard(Matrix4F matrix, Vec3F objectPos, Vec3F camPos, Vec3F camUp, Vector3 camLook) { Vector3 look = objectPos - camPos; float len = look.LengthSquared; if (len < 0.0001f) { look = -camLook; } else { look.Normalize(); } Vector3 right = Vec3F.Cross(camUp, look); right.Normalize(); Vector3 up = Vec3F.Cross(look, right); matrix.M11 = right.X; matrix.M12 = right.Y; matrix.M13 = right.Z; matrix.M14 = 0f; matrix.M21 = up.X; matrix.M22 = up.Y; matrix.M23 = up.Z; matrix.M24 = 0f; matrix.M31 = look.X; matrix.M32 = look.Y; matrix.M33 = look.Z; matrix.M34 = 0f; matrix.M41 = objectPos.X; matrix.M42 = objectPos.Y; matrix.M43 = objectPos.Z; matrix.M44 = 1f; }
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); } } }
/// <summary> /// Create bi-polar sphere /// </summary> /// <param name="radius">radias of the sphere</param> /// <param name="slices">number of slices</param> /// <param name="stacks">number of stacks</param> /// <param name="pos">output positions</param> /// <param name="normal">output normals</param> /// <param name="tex">output texture coordinates</param> /// <param name="indices">output indices</param> public static void CreateSphere(float radius, uint slices, uint stacks, List <Vector3> pos, List <Vector3> normal, List <Vector2> tex, List <uint> indices) { if (radius <= 0) { throw new ArgumentOutOfRangeException("radius"); } if (slices < 2 || stacks < 2) { throw new ArgumentException("invalid number slices or stacks"); } // caches sin cos. float[] cosPhi = new float[stacks]; float[] sinPhi = new float[stacks]; float phiStep = MathHelper.Pi / stacks; float[] cosTheta = new float[slices]; float[] sinTheta = new float[slices]; float thetaStep = MathHelper.TwoPi / slices; float phi = 0; for (int s = 0; s < stacks; s++, phi += phiStep) { sinPhi[s] = (float)Math.Sin(phi); cosPhi[s] = (float)Math.Cos(phi); } float theta = 0; for (int s = 0; s < slices; s++, theta += thetaStep) { sinTheta[s] = (float)Math.Sin(theta); cosTheta[s] = (float)Math.Cos(theta); } uint numVerts = 2 + (stacks - 1) * slices; Vector3 northPole = new Vector3(0, radius, 0); pos.Add(northPole); // north pole. normal.Add(Vector3.Normalize(northPole)); // north pole. for (int s = 1; s < stacks; s++) { float y = radius * cosPhi[s]; float r = radius * sinPhi[s]; for (int l = 0; l < slices; l++) { Vector3 p; p.Y = y; p.Z = r * cosTheta[l]; p.X = r * sinTheta[l]; pos.Add(p); if (normal != null) { normal.Add(Vector3.Normalize(p)); } } } Vector3 southPole = new Vector3(0, -radius, 0); pos.Add(southPole); normal.Add(Vector3.Normalize(southPole)); // 2l + (s-2) * 2l // 2l * ( 1 + s-2) // 2l * ( s- 1) // uint numTris = 2 * slices * (stacks - 1); // create index of north pole cap. for (uint l = 1; l < slices; l++) { indices.Add(l + 1); indices.Add(0); indices.Add(l); } indices.Add(1); indices.Add(0); indices.Add(slices); for (uint s = 0; s < (stacks - 2); s++) { uint l = 1; for (; l < slices; l++) { indices.Add((s + 1) * slices + l + 1); // bottom right. indices.Add(s * slices + l + 1); // top right. indices.Add(s * slices + l); // top left. indices.Add((s + 1) * slices + l); // bottom left. indices.Add((s + 1) * slices + l + 1); // bottom right. indices.Add(s * slices + l); // top left. } indices.Add((s + 1) * slices + 1); // bottom right. indices.Add(s * slices + 1); // top right. indices.Add(s * slices + slices); // top left. indices.Add((s + 1) * slices + slices); // bottom left. indices.Add((s + 1) * slices + 1); // bottom right. indices.Add(s * slices + slices); // top left. } // create index for south pole cap. uint baseIndex = slices * (stacks - 2); uint lastIndex = (uint)pos.Count - 1; for (uint l = 1; l < slices; l++) { indices.Add(baseIndex + l); indices.Add(lastIndex); indices.Add(baseIndex + l + 1); } indices.Add(baseIndex + slices); indices.Add(lastIndex); indices.Add(baseIndex + 1); }
/// <summary> /// Transforms the ray by the given matrix. The Direction member will be normalized. There are /// no particular restrictions on M. Any transformation done to a point in world space or /// object space can be done on this ray, including any combination of rotations, translations, /// uniform scales, non-uniform scales, etc.</summary> /// <param name="M">Transformation matrix</param> public void Transform(Matrix4F M) { M.Transform(Origin, out Origin); M.TransformVector(Direction, out Direction); Direction.Normalize(); }