/Rendering/Models/GeometryHelper.cs
C# | 382 lines | 219 code | 28 blank | 135 comment | 9 complexity | 84ce793b3fde845f32245c0f7e070ddf MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using Delta.ContentSystem.Rendering;
- using Delta.Utilities.Graphics;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
-
- namespace Delta.Rendering.Models
- {
- /// <summary>
- /// Helper class to generate geometry based on the GeometryData class.
- /// This class is derived from GeometryData and can be used as well, it just
- /// adds a few more static Create methods.
- /// <para />
- /// Note: That class has only static method but can't be a static class
- /// because it's derived from the GeometryData to extend its create
- /// functionality.
- /// </summary>
- public class GeometryHelper : GeometryData
- {
- #region CreatePlane
- /// <summary>
- /// Create plane
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setHeight">Height of the set.</param>
- /// <returns></returns>
- public static GeometryData CreatePlane(VertexFormat vertexFormat,
- float setWidth, float setHeight)
- {
- return CreatePlane(vertexFormat, setWidth, setHeight, Color.White);
- } // CreatePlane(vertexFormat, setWidth, setHeight)
-
- /// <summary>
- /// Creates an XY plane in the specified vertex format (you can use
- /// one of the predefined vertex formats from VertexData).
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="setWidth">Set width.</param>
- /// <param name="setHeight">Set Height.</param>
- /// <param name="setColor">Set Color.</param>
- /// <returns></returns>
- public static GeometryData CreatePlane(VertexFormat vertexFormat,
- float setWidth, float setHeight, Color setColor)
- {
- float halfWidth = setWidth * 0.5f;
- float halfDepth = setHeight * 0.5f;
- float height = 0;
-
- // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3):
- // 0 - 1
- // | / |
- // 2 - 3
- GeometryData geometry = new GeometryData(
- "<GeneratedPlane(" + setWidth + "x" + setHeight + ")>",
- 4, vertexFormat, 6, true, false);
- Vector planeNormal = new Vector(0, 0, 1);
- Vector planeTangent = new Vector(1, 0, 0);
- // Front left
- geometry.SetVertexData(0,
- new Vector(-halfWidth, halfDepth, height),
- new Point(0, 0), setColor, planeNormal, planeTangent);
- // Front right
- geometry.SetVertexData(1,
- new Vector(halfWidth, halfDepth, height),
- new Point(1, 0), setColor, planeNormal, planeTangent);
- // Rear left
- geometry.SetVertexData(2,
- new Vector(-halfWidth, -halfDepth, height),
- new Point(0, 1), setColor, planeNormal, planeTangent);
- // Rear right
- geometry.SetVertexData(3,
- new Vector(halfWidth, -halfDepth, height),
- new Point(1, 1), setColor, planeNormal, planeTangent);
-
- // Top left polygon (Note: Counter-Clockwise is front)
- geometry.Indices[0] = 0;
- geometry.Indices[1] = 2;
- geometry.Indices[2] = 1;
- // Bottom right polygon
- geometry.Indices[3] = 2;
- geometry.Indices[4] = 3;
- geometry.Indices[5] = 1;
-
- return geometry;
- } // CreatePlane(vertexFormat, setWidth, setHeight)
- #endregion
-
- #region CreateSegmentedPlane
- /// <summary>
- /// Create plane
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setHeight">Height of the set.</param>
- /// <param name="segments">The segments.</param>
- /// <returns></returns>
- public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat,
- float setWidth, float setHeight, int segments)
- {
- return CreateSegmentedPlane(vertexFormat, setWidth, setHeight, segments,
- Color.White);
- } // CreatePlane(vertexFormat, setWidth, setHeight)
-
- /// <summary>
- /// Creates an XY plane in the specified vertex format (you can use
- /// one of the predefined vertex formats from VertexData).
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="setWidth">Sets Width.</param>
- /// <param name="setHeight">Sets Height.</param>
- /// <param name="segments">The segments.</param>
- /// <param name="setColor">Sets Color.</param>
- /// <returns>
- /// Geometry Data
- /// </returns>
- public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat,
- float setWidth, float setHeight, int segments, Color setColor)
- {
- if (segments < 1)
- {
- throw new InvalidOperationException("You need at least one segment " +
- "for CreateSegmentedPlane!");
- }
-
- float halfWidth = setWidth * 0.5f;
- float halfDepth = setHeight * 0.5f;
- float height = 0;
-
- // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3):
- // 0 - 1
- // | / |
- // 2 - 3
- // But duplicate this many times for as many segments in each direction.
- int totalSegments = segments * segments;
- int segmentStride = segments + 1;
- int totalPoints = segmentStride * segmentStride;
- GeometryData geometry = new GeometryData(
- "<GeneratedSegmentedPlane(" + setWidth + "x" + setHeight + ")>",
- totalPoints, vertexFormat, 6 * totalSegments, true, false);
- Vector planeNormal = new Vector(0, 0, 1);
- Vector planeTangent = new Vector(1, 0, 0);
- // For each segment + 1, build the points
- int pointIndex = 0;
- for (int ySegment = 0; ySegment < segments + 1; ySegment++)
- {
- for (int xSegment = 0; xSegment < segments + 1; xSegment++)
- {
- float xFactor = (float)xSegment / (float)segments;
- float yFactor = (float)ySegment / (float)segments;
- geometry.SetVertexData(pointIndex++,
- new Vector(-halfWidth + xFactor * setWidth,
- halfDepth - yFactor * setHeight, height),
- new Point(xFactor, yFactor), setColor, planeNormal, planeTangent);
- } // for
- } // for
-
- // For each segment, build up the 2 triangles
- int index = 0;
- for (int ySegment = 0; ySegment < segments; ySegment++)
- {
- for (int xSegment = 0; xSegment < segments; xSegment++)
- {
- // Top left polygon (Note: Counter-Clockwise is front)
- geometry.Indices[index++] = //0,0
- (ushort)(xSegment + 0 + (ySegment + 0) * segmentStride);
- geometry.Indices[index++] = //0,1
- (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride);
- geometry.Indices[index++] = //1,0
- (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride);
- // Bottom right polygon
- geometry.Indices[index++] = //0,1
- (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride);
- geometry.Indices[index++] = //1,1
- (ushort)(xSegment + 1 + (ySegment + 1) * segmentStride);
- geometry.Indices[index++] = //1,0
- (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride);
- } // for
- } // for
-
- return geometry;
- } // CreateSegmentedPlane(vertexFormat, setWidth, setHeight)
- #endregion
-
- #region CreateCube
- /// <summary>
- /// Generates a cube mesh with the given sizes and a material. Cube meshes
- /// have their pivot point at Vector.Zero (to make them easier to place).
- /// An inverted box with fewer vertices than the normal box.
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="width">The width in the X.</param>
- /// <param name="depth">The depth in the Z.</param>
- /// <param name="height">The height in the Y.</param>
- /// <returns>
- /// Geometry Data
- /// </returns>
- public static GeometryData CreateCube(VertexFormat vertexFormat,
- float width, float depth, float height)
- {
- return CreateCube(vertexFormat, width, depth, height,
- "<GeneratedBox(" + width + ", " + depth + ", " + height + ")>",
- height / 2, Color.White);
- } // CreateBox(vertexFormat, width, depth)
-
- /// <summary>
- /// Generates a Cube mesh with a given pivot point height (usually used
- /// to generate the box at Vector.Zero, see other overloads).
- /// An inverted box with fewer vertices than the normal box.
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="width">The width.</param>
- /// <param name="depth">The depth.</param>
- /// <param name="height">The height.</param>
- /// <param name="meshName">Name of the mesh.</param>
- /// <param name="heightOffset">The height offset.</param>
- /// <param name="setColor">Color of the set.</param>
- /// <returns>
- /// Geometry Data
- /// </returns>
- public static GeometryData CreateCube(VertexFormat vertexFormat,
- float width, float depth, float height, string meshName,
- float heightOffset, Color setColor)
- {
- float right = width / 2.0f;
- float back = depth / 2.0f;
- float top = height / 2.0f;
- float bottom = -height / 2.0f;
-
- GeometryData geometry = new GeometryData(meshName, 8, vertexFormat,
- 6 * 6, true, false);
-
- // First go to the start position for this vertex
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(-right, -back, top));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(right, -back, top));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(-right, -back, bottom));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(right, -back, bottom));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(right, back, top));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(-right, back, top));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(right, back, bottom));
- geometry.Format.Elements[0].SaveData(geometry.writer,
- new Vector(-right, back, bottom));
-
- // Now go through all the faces and generate the indices (see comments)
- // Indices are reversed so that the Cube is visible when you are inside it.
- geometry.Indices = new ushort[]
- {
- // Front face quad (0, 1, 2, 2, 1, 3)
- 0, 1, 2, 2, 1, 3,
- // Back face quad (4, 5, 6, 6, 5, 7)
- 4, 5, 6, 6, 5, 7,
- // Left face quad (5, 0, 7, 7, 0, 2)
- 5, 0, 7, 7, 0, 2,
- // Right face quad (1, 4, 3, 3, 4, 6)
- 1, 4, 3, 3, 4, 6,
- // Upper face quad (5, 4, 0, 0, 4, 1,)
- 5, 4, 0, 0, 4, 1,
- // Lower face quad (6, 7, 3, 3, 7, 2,)
- 6, 7, 3, 3, 7, 2,
- };
-
- return geometry;
- } // CreateCube(vertexFormat, width, depth)
- #endregion
-
- #region CreateSphere
- /// <summary>
- /// Creates a sphere.
- /// </summary>
- /// <param name="vertexFormat">The vertex format.</param>
- /// <param name="radius">The radius.</param>
- /// <param name="setColor">Color of the set.</param>
- /// <returns></returns>
- public static GeometryData CreateSphere(VertexFormat vertexFormat,
- float radius, Color setColor)
- {
- // Set up the subdivision in which we build the geometry of the sphere.
- // The tessellation indicates in how many segments the sphere is
- // interpolated, smaller spheres have less tessellation, bigger spheres
- // use more tessellation, but we keep it between 4 and 32.
- int tessellation = (int)(MathHelper.Sqrt(radius) * 6.0f);
- tessellation = MathHelper.Clamp(tessellation, 6, 32);
- // Make multiple of 3 for better fitting texturing
- tessellation = (tessellation / 3) * 3;
- int verticalSegments = tessellation;
- // Add one horizontal segment to fit around a circle, good for UVs
- int horizontalSegments = tessellation * 2 + 1;
-
- // Create rings of vertices at progressively higher latitudes.
- GeometryData geometry = new GeometryData(
- "<GeneratedSphere(" + radius + ")>",
- verticalSegments * horizontalSegments,
- vertexFormat, 6 * (verticalSegments - 1) * horizontalSegments,
- true, false);
- for (int index = 0; index < verticalSegments; index++)
- {
- // Lets begin with the latitude and divide each segment. As we are
- // using circular surface, we will split each position along the
- // vertical axis with the cosine. That is every position in the
- // vertical segment creates a ring with a maximum width stated by the
- // cosine (at the top width=0, in the medium reaches the maximum
- // width = 1, and at the bottom width=0).
- float latitude = index * 180.0f / (float)(verticalSegments - 1) - 90.0f;
- float dy = MathHelper.Sin(latitude);
- float dxz = MathHelper.Cos(latitude);
-
- // Create a single ring of vertices at this latitude.
- for (int j = 0; j < horizontalSegments; j++)
- {
- // Next step is tessellation along horizontal axis in which we just
- // simple indicates the position of each vertex in the ring with the
- // previously established width along the surface of the sphere
- float longitude = j * 360.0f / (float)(horizontalSegments - 1);
-
- float dx = MathHelper.Cos(longitude) * dxz;
- float dz = MathHelper.Sin(longitude) * dxz;
-
- // finally we got the correct position
- Vector normal = new Vector(dx, dy, dz);
-
- // and we assign the corresponding U,V coordinate of the texture
- // in a way that for each circle of the sphere it contains a line
- // of the texture image
- geometry.SetVertexData(
- index * horizontalSegments + j,
- normal * radius,
- new Point((float)j / (float)(horizontalSegments - 1),
- (float)index / (float)(verticalSegments - 1)), setColor, normal,
- Vector.Cross(normal, Vector.UnitZ));
- } // for
- } // for
-
- // Create a fan connecting the bottom vertex to the bottom latitude ring
- // and finally set up the indices connecting each vertex.
- // Fill the sphere body with triangles joining each pair of rings.
- int num = 0;
- for (int index = 0; index < verticalSegments - 1; index++)
- {
- for (int j = 0; j < horizontalSegments; j++)
- {
- int nextI = (index + 1);
- int nextJ = (j + 1) % horizontalSegments;
-
- // Note: Counter-Clockwise is front
- geometry.Indices[num++] =
- (ushort)(index * horizontalSegments + j);
- geometry.Indices[num++] =
- (ushort)(nextI * horizontalSegments + j);
- geometry.Indices[num++] =
- (ushort)(index * horizontalSegments + nextJ);
-
- geometry.Indices[num++] =
- (ushort)(index * horizontalSegments + nextJ);
- geometry.Indices[num++] =
- (ushort)(nextI * horizontalSegments + j);
- geometry.Indices[num++] =
- (ushort)(nextI * horizontalSegments + nextJ);
- } // for
- } // for
-
- return geometry;
- } // CreateSphere(vertexFormat, radius, setColor)
- #endregion
-
- #region Constructor (forbidden)
- /// <summary>
- /// Geometry data
- /// </summary>
- private GeometryHelper()
- : base(null, null)
- {
- }
- #endregion
- }
- }