public static void WritePolygonFlipY( Vector3[] pts, List <Vector3> pos, List <Vector3> normals, List <uint> indices) { var flippedPts = new Vector3[pts.Length]; for (int c = 0; c < pts.Length; ++c) { var p = pts[pts.Length - c - 1]; flippedPts[c] = new Vector3(p.X, -p.Y, p.Z); } WritePolygon(flippedPts, pos, normals, indices); }
private bool IsEar(LinkedListNode <TriVx> node, Vec3F normal) { bool isEar = true; TriVx v0 = PrevNode(node).Value; TriVx v1 = node.Value; TriVx v2 = NextNode(node).Value; foreach (TriVx v in node.List) { if (v.IsReflex && v != v0 && v != v1 && v != v2) { // Chec for containment if (IsLeftOf(v0, v1, v, normal) && IsLeftOf(v1, v2, v, normal) && IsLeftOf(v2, v0, v, normal)) { isEar = false; } } } return(isEar); }
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)); } }
private Vec3F[] CalcPointTangents() { int n = Count; Vec3F[] tangents = new Vec3F[n]; for (int i = 1; i < n - 1; i++) { tangents[i] = CalcTangent(this[i - 1].Position, this[i].Position, this[i + 1].Position); } if (m_isClosed) { tangents[0] = CalcTangent(this[n - 1].Position, this[0].Position, this[1].Position); tangents[n - 1] = CalcTangent(this[n - 2].Position, this[n - 1].Position, this[0].Position); } else { tangents[0] = CalcEndTangents(this[0].Position, this[1].Position, tangents[1]); tangents[n - 1] = CalcEndTangents(this[n - 2].Position, this[n - 1].Position, tangents[n - 2]); } return(tangents); }
/// <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; }
/// <summary> /// Constructor with min and max</summary> /// <param name="min">Minima of extents</param> /// <param name="max">Maxima of extents</param> public Box(Vec3F min, Vec3F max) { Min = min; Max = max; m_initialized = true; }
/// <summary> /// Sets sphere to given center and radius</summary> /// <param name="center">Center point</param> /// <param name="radius">Radius</param> public void Set(Vec3F center, float radius) { Center = center; Radius = radius; m_initialized = true; }
/// <summary> /// Cosntructor using center and radius</summary> /// <param name="center">Center point</param> /// <param name="radius">Radius</param> public Sphere3F(Vec3F center, float radius) { Center = center; Radius = radius; m_initialized = true; }
/// <summary> /// Creates Billboard matrix from the given parameters.</summary> public static Matrix4F CreateBillboard(Vec3F objectPos, Vec3F camPos, Vec3F camUp, Vector3 camLook) { Matrix4F matrix = new Matrix4F(); CreateBillboard(matrix, objectPos, camPos, camUp, camLook); return matrix; }
/// <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> /// 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> /// Constructor</summary> /// <param name="angles">Array of 3 floats to supply the Euler angles on /// the x, y, and z axes</param> /// <param name="order">Order that the rotations should be applied</param> public EulerAngles3F(float[] angles, EulerAngleOrder order) { m_Angles = new Vec3F(angles); m_Order = order; }
private void UpdateEar(LinkedListNode <TriVx> node, List <LinkedListNode <TriVx> > ears, Vec3F normal) { if (IsEar(node, normal)) { // If it's not already an ear add it to the ear list if (!node.Value.IsEar) { ears.Add(node); node.Value.IsEar = true; } } else { // Ok, it's not an ear - check if it is marked as one if (node.Value.IsEar) { ears.Remove(node); node.Value.IsEar = false; } } }
/// <summary> /// Finds the intersection point of the ray with the given convex polygon</summary> /// <param name="vertices">Polygon vertices</param> /// <param name="intersectionPoint">Intersection point</param> /// <returns>True iff the ray intersects the polygon</returns> public bool IntersectPolygon(Vec3F[] vertices, out Vec3F intersectionPoint) { Vec3F nearestVert, normal; return(IntersectPolygon(vertices, out intersectionPoint, out nearestVert, out normal, false)); }
/// <summary> /// Moves this ray in a perpendicular direction from its current location so that 'point' will /// be on the infinite line that includes the new ray. This is almost like setting the origin /// to be 'point', except the current origin is moved in a strictly perpendicular direction.</summary> /// <param name="point">Point on infinite line that includes the new ray</param> public void MoveToIncludePoint(Vec3F point) { Vec3F x = ProjectPoint(point); Origin = Origin + (point - x); }
/// <summary> /// Constructs a 3D ray consisting of a point and a unit-vector direction</summary> /// <param name="origin">Ray origin</param> /// <param name="direction">Ray direction; must be a unit vector in order for /// IntersectPlane method to work</param> public Ray3F(Vec3F origin, Vec3F direction) { Origin = origin; Direction = direction; }
/// <summary> /// Constructor</summary> /// <param name="angles">Euler angles on the x, y, and z axes</param> /// <param name="order">Order that the rotations should be applied</param> public EulerAngles3F(Vec3F angles, EulerAngleOrder order) { m_Angles = angles; m_Order = order; }
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> /// 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); }
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); } } }
public TriVx(Vec3F v) { V = v; }
/// <summary> /// Constructor for a point in a bezier curve</summary> /// <param name="position">position of the point</param> /// <param name="incomingTangent">incoming tangent</param> /// <param name="outgoingTangent">outgoing tangent</param> public BezierPoint(Vec3F position, Vec3F incomingTangent, Vec3F outgoingTangent) { Position = position; Tangent1 = incomingTangent; Tangent2 = outgoingTangent; }
/// <summary> /// Triangulate the polygon</summary> /// <param name="normal">Polygon normal</param> /// <returns>List of triangles from the polygon</returns> public IList <Triangle3F> Triangulate(Vec3F normal) { List <Triangle3F> tris = new List <Triangle3F>(); // Build TriVx array LinkedList <TriVx> polygon = new LinkedList <TriVx>(); foreach (Vec3F v in Vertices) { polygon.AddLast(new TriVx(v)); } if (polygon.Count < 3) { throw new InvalidOperationException("Polygon has less than 3 vertices"); } List <LinkedListNode <TriVx> > ears = new List <LinkedListNode <TriVx> >(); if (polygon.Count == 3) { // This is a triangle so just add any arbitrary vertex as an ear polygon.First.Value.IsEar = true; ears.Add(polygon.First); } else { LinkedListNode <TriVx> current = null; // Update reflex flag for (current = polygon.First; current != null; current = current.Next) { current.Value.IsReflex = IsReflex(current, normal); } // Construct ears from all convex vertices for (current = polygon.First; current != null; current = current.Next) { if (!current.Value.IsReflex && IsEar(current, normal)) { current.Value.IsEar = true; ears.Add(current); } } } while (polygon.Count > 0 && ears.Count > 0) { LinkedListNode <TriVx> node = ears[0]; LinkedListNode <TriVx> prev = PrevNode(node); LinkedListNode <TriVx> next = NextNode(node); tris.Add(new Triangle3F(prev.Value.V, node.Value.V, next.Value.V)); if (polygon.Count == 3) { polygon.Clear(); } else { ears.Remove(node); polygon.Remove(node); // Now test adjacent vertices // If adjacent vertex is convex it remains convex, otherwise it needs testing if (prev.Value.IsReflex) { prev.Value.IsReflex = IsReflex(prev, normal); } if (!prev.Value.IsReflex) { UpdateEar(prev, ears, normal); } // Do the same for next if (next.Value.IsReflex) { next.Value.IsReflex = IsReflex(next, normal); } if (!next.Value.IsReflex) { UpdateEar(next, ears, normal); } } } return(tris); }
/// <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 = camPos - objectPos; 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 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> /// Tests if point is inside this sphere</summary> /// <param name="pt">Point</param> /// <returns>True iff pt is inside this sphere</returns> public bool Contains(Vec3F pt) { return((pt - Center).Length < Radius); }
/// <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> /// Constructs a sphere from another sphere</summary> /// <param name="sphere">Other sphere</param> public Sphere3F(Sphere3F sphere) { Center = sphere.Center; Radius = sphere.Radius; m_initialized = true; }
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)); } }
/// <summary> /// Tests if specified ray intersects the box</summary> /// <param name="ray">The ray</param> /// <returns>True iff ray intersects box</returns> public bool Intersects(Ray3F ray) { // http://www.gametutorials.com/gtstore/pc-429-9-ray-and-aabb-collision.aspx // Compute the ray delta Vec3F rayDelta = ray.Direction * 100000f; // First we check to see if the origin of the ray is // inside the AABB. If it is, the ray intersects the AABB so // we'll return true. We start by assuming the ray origin is // inside the AABB bool inside = true; // This stores the distance from either the min or max (x,y,z) to the ray's // origin (x,y,z) respectively, divided by the length of the ray. The largest // value has the delta time of a possible intersection. float xt, yt, zt; // Test the X component of the ray's origin to see if we are inside or not if (ray.Origin.X < Min.X) { xt = Min.X - ray.Origin.X; if (xt > rayDelta.X) // If the ray is moving away from the AABB, there is no intersection { return(false); } xt /= rayDelta.X; inside = false; } else if (ray.Origin.X > Max.X) { xt = Max.X - ray.Origin.X; if (xt < rayDelta.X) // If the ray is moving away from the AABB, there is no intersection { return(false); } xt /= rayDelta.X; inside = false; } else { // Later on we use the "xt", "yt", and "zt" variables to determine which plane (either // xy, xz, or yz) we may collide with. Since the x component of the ray // origin is in between, the AABB's left and right side (which reside in the yz plane), // we know we don't have to test those sides so we set this to a negative value. xt = -1.0f; } // Test the X component of the ray's origin to see if we are inside or not if (ray.Origin.Y < Min.Y) { yt = Min.Y - ray.Origin.Y; if (yt > rayDelta.Y) // If the ray is moving away from the AABB, there is no intersection { return(false); } yt /= rayDelta.Y; inside = false; } else if (ray.Origin.Y > Max.Y) { yt = Max.Y - ray.Origin.Y; if (yt < rayDelta.Y) // If the ray is moving away from the AABB, there is no intersection { return(false); } yt /= rayDelta.Y; inside = false; } else { // Later on we use the "xt", "yt", and "zt" variables to determine which plane (either // xy, xz, or yz) we may collide with. Since the y component of the ray // origin is in between, the AABB's top and bottom side (which reside in the xz plane), // we know we don't have to test those sides so we set this to a negative value. yt = -1.0f; } if (ray.Origin.Z < Min.Z) { zt = Min.Z - ray.Origin.Z; if (zt > rayDelta.Z) // If the ray is moving away from the AABB, there is no intersection { return(false); } zt /= rayDelta.Z; inside = false; } else if (ray.Origin.Z > Max.Z) { zt = Max.Z - ray.Origin.Z; if (zt < rayDelta.Z) // If the ray is moving away from the AABB, there is no intersection { return(false); } zt /= rayDelta.Z; inside = false; } else { // Later on we use the "xt", "yt", and "zt" variables to determine which plane (either // xy, xz, or yz) we may collide with. Since the z component of the ray // origin is in between, the AABB's front and back side (which reside in the xy plane), // we know we don't have to test those sides so we set this to a negative value. zt = -1.0f; } // If the origin inside the AABB if (inside) { return(true); // The ray intersects the AABB } // Otherwise we have some checking to do... // We want to test the AABB planes with largest value out of xt, yt, and zt. So // first we determine which value is the largest. float t = xt; if (yt > t) { t = yt; } if (zt > t) { t = zt; } // **NOTE** Normally comparing two floating point numbers won't necessarily work, however, // since we set to explicitly to equal either xt, yt, or zt above, the equality test // will pass if (t == xt) // If the ray intersects with the AABB's YZ plane { // Compute intersection values float y = ray.Origin.Y + rayDelta.Y * t; float z = ray.Origin.Z + rayDelta.Z * t; // Test to see if collision takes place within the bounds of the AABB if (y < Min.Y || y > Max.Y) { return(false); } else if (z < Min.Z || z > Max.Z) { return(false); } } else if (t == yt) // Intersects with the XZ plane { // Compute intersection values float x = ray.Origin.X + rayDelta.X * t; float z = ray.Origin.Z + rayDelta.Z * t; // Test to see if collision takes place within the bounds of the AABB if (x < Min.X || x > Max.X) { return(false); } else if (z < Min.Z || z > Max.Z) { return(false); } } else // Intersects with the XY plane { // Compute intersection values float x = ray.Origin.X + rayDelta.X * t; float y = ray.Origin.Y + rayDelta.Y * t; // Test to see if collision takes place within the bounds of the AABB if (x < Min.X || x > Max.X) { return(false); } else if (y < Min.Y || y > Max.Y) { return(false); } } // The ray intersects the AABB return(true); }
public static void WritePolygonFlipY( Vector3[] pts, List<Vector3> pos, List<Vector3> normals, List<uint> indices) { var flippedPts = new Vector3[pts.Length]; for (int c = 0; c < pts.Length; ++c) { var p = pts[pts.Length - c - 1]; flippedPts[c] = new Vector3(p.X, -p.Y, p.Z); } WritePolygon(flippedPts, pos, normals, indices); }
/// <summary> /// Construct triangle from 3 vertices</summary> /// <param name="v0">Vertex 0</param> /// <param name="v1">Vertex 1</param> /// <param name="v2">Vertex 2</param> public Triangle3F(Vec3F v0, Vec3F v1, Vec3F v2) { V0 = v0; V1 = v1; V2 = v2; }
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; }
/// <summary> /// Creates Billboard matrix from the given parameters.</summary> public static Matrix4F CreateBillboard(Vec3F objectPos, Vec3F camPos, Vec3F camUp, Vector3 camLook) { Matrix4F matrix = new Matrix4F(); CreateBillboard(matrix, objectPos, camPos, camUp, camLook); return(matrix); }
/// <summary> /// Transforms by this matrix a vector that is perpendicular to some geometry or that is the component of a 3D plane. /// Points may be transformed using Transform(). Directional vectors like those in rays can be /// transformed by TransformVector(), and normals need to be tranformed by TransformNormal().</summary> /// <param name="n">The normal vector to some geometry</param> /// <param name="transposeOfInverse">The transpose of the inverse of this matrix, for performance reasons</param> /// <param name="result">The normal vector after being transformed by this matrix</param> public void TransformNormal(Vec3F n, Matrix4F transposeOfInverse, out Vec3F result) { transposeOfInverse.Transform(n, out result); }