/Rendering/Models/Mesh.cs
C# | 572 lines | 263 code | 46 blank | 263 comment | 9 complexity | ddac8929b43d11dd995fb09df3567913 MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using Delta.ContentSystem.Rendering;
- using Delta.Graphics.Basics;
- using Delta.Rendering.Basics.Materials;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
-
- namespace Delta.Rendering.Models
- {
- /// <summary>
- /// Mesh model class for 3d meshes (but 2d meshes also work). Basically
- /// a mesh is just a combination of Geometry and Material. Models can
- /// consist of multiple meshes.
- /// </summary>
- public class Mesh
- {
- #region Constants
- /// <summary>
- /// Line colors for displaying the bones as lines.
- /// </summary>
- private static readonly Color[] BoneColors = new[]
- {
- Color.Blue, Color.Red, Color.Yellow, Color.White, Color.Teal,
- Color.Brown, Color.Orange, Color.LightBlue, Color.Green,
- Color.Purple,
- };
- #endregion
-
- #region CreatePlane (Static)
- /// <summary>
- /// Creates an XY plane.
- /// </summary>
- /// <param name="setName">Set name</param>
- /// <param name="setWidth">Set width</param>
- /// <param name="setDepth">Set depth</param>
- /// <param name="setColor">Vertex color during creation.</param>
- /// <param name="setMaterialData">Set material data</param>
- /// <returns>Mesh with the created plane and given material</returns>
- public static Mesh CreatePlane(string setName, float setWidth,
- float setDepth, Color setColor, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the plane we create
- GeometryData planeData = GeometryData.CreatePlane(shader.VertexFormat,
- setWidth, setDepth, setColor);
-
- return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Creates an XY plane
- /// </summary>
- /// <param name="setName">Set name</param>
- /// <param name="setWidth">Set width</param>
- /// <param name="setDepth">Set depth</param>
- /// <param name="setMaterialData">Set material data</param>
- /// <returns>Mesh with the created plane and given material</returns>
- public static Mesh CreatePlane(string setName, float setWidth,
- float setDepth, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the plane we create
- GeometryData planeData = GeometryData.CreatePlane(shader.VertexFormat,
- setWidth, setDepth, setMaterialData.Diffuse);
-
- return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
- }
- #endregion
-
- #region CreateSegmentedPlane (Static)
- /// <summary>
- /// Creates an XY plane with segments, which is useful for testing
- /// specular and other shader effects with pre-calculated data in the
- /// vertex shader (which will not look close up for just a 1x1 plane).
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setDepth">The set depth.</param>
- /// <param name="setSegments">The set segments.</param>
- /// <param name="uvForEachSegment">Create new uv from 0-1 for each segment.
- /// Useful for tiling (works even without tileable textures)</param>
- /// <param name="setColor">Vertex color during creation.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing segmented plane data.</returns>
- public static Mesh CreateSegmentedPlane(string setName, float setWidth,
- float setDepth, int setSegments, bool uvForEachSegment, Color setColor,
- MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the plane we create
- GeometryData planeData = GeometryData.CreateSegmentedPlane(
- shader.VertexFormat, setWidth, setDepth, setSegments, uvForEachSegment,
- setColor);
-
- return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Creates an XY plane with segments, which is useful for testing
- /// specular and other shader effects with pre-calculated data in the
- /// vertex shader (which will not look close up for just a 1x1 plane).
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setDepth">The set depth.</param>
- /// <param name="setSegments">The set segments.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>
- /// The new create Mesh.
- /// </returns>
- public static Mesh CreateSegmentedPlane(string setName, float setWidth,
- float setDepth, int setSegments, MaterialData setMaterialData)
- {
- return CreateSegmentedPlane(setName, setWidth, setDepth, setSegments,
- false, setMaterialData.Diffuse, setMaterialData);
- }
- #endregion
-
- #region CreateBox (Static)
- /// <summary>
- /// Create box mesh.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setDepth">The set depth.</param>
- /// <param name="setHeight">Height of the set.</param>
- /// <param name="setColor">Vertex color during creation.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing box data.</returns>
- public static Mesh CreateBox(string setName, float setWidth,
- float setDepth, float setHeight, Color setColor,
- MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the box we create
- GeometryData boxData = GeometryData.CreateBox(shader.VertexFormat,
- setWidth, setDepth, setHeight, false, setColor);
-
- return new Mesh(setName, boxData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Create box mesh.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setDepth">The set depth.</param>
- /// <param name="setHeight">Height of the set.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing box data.</returns>
- public static Mesh CreateBox(string setName, float setWidth,
- float setDepth, float setHeight,
- MaterialData setMaterialData)
- {
- return CreateBox(setName, setWidth, setDepth, setHeight,
- setMaterialData.Diffuse, setMaterialData);
- }
- #endregion
-
- #region CreateCube (Static)
- /// <summary>
- /// Create cube shape with all 6 faces with equal size.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setSize">The size of the faces.</param>
- /// <param name="setColor">Vertex color during creation.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing cube data.</returns>
- public static Mesh CreateCube(string setName, float setSize,
- Color setColor, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
-
- // for the box we create
- GeometryData planeData = GeometryData.CreateCube(shader.VertexFormat,
- setSize, setColor);
-
- return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Create cube shape with all 6 faces with equal size.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setSize">The size of the faces.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing cube data.</returns>
- public static Mesh CreateCube(string setName, float setSize,
- MaterialData setMaterialData)
- {
- return CreateCube(setName, setSize, setMaterialData.Diffuse,
- setMaterialData);
- }
- #endregion
-
- #region CreateInnerBox (Static)
- /// <summary>
- /// Creates an inner box (-> like the 'CreateBox()' method but in the
- /// "inverted" way).
- /// Note: This kind of a box is helpful if you want to render something
- /// inside a box like a simple (demo) room.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setDepth">The set depth.</param>
- /// <param name="setHeight">Height of the set.</param>
- /// <param name="setColor">Vertex color during creation.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing inner box data.</returns>
- public static Mesh CreateInnerBox(string setName, float setWidth,
- float setDepth, float setHeight, Color setColor, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the cube we create
- GeometryData planeData = GeometryData.CreateBox(shader.VertexFormat,
- setWidth, setDepth, setHeight, true, setColor);
-
- return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Creates an inner box (-> like the 'CreateBox()' method but in the
- /// "inverted" way).
- /// Note: This kind of a box is helpful if you want to render something
- /// inside a box like a simple (demo) room.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setWidth">Width of the set.</param>
- /// <param name="setDepth">The set depth.</param>
- /// <param name="setHeight">Height of the set.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing inner box data.</returns>
- public static Mesh CreateInnerBox(string setName, float setWidth,
- float setDepth, float setHeight, MaterialData setMaterialData)
- {
- return CreateInnerBox(setName, setWidth, setDepth, setHeight,
- setMaterialData.Diffuse, setMaterialData);
- }
- #endregion
-
- #region CreateSphere (Static)
- /// <summary>
- /// Creates a sphere as a Mesh with the given parameters.
- /// </summary>
- /// <param name="setName">Name of the sphere mesh</param>
- /// <param name="setRadius">Radius for the sphere from the center</param>
- /// <param name="setColor">
- /// Color for the new sphere in case the vertex format supports colored
- /// vertices (otherwise ignored).
- /// </param>
- /// <param name="setMaterialData">The material data for the mesh.</param>
- /// <returns>Mesh containing the sphere.</returns>
- public static Mesh CreateSphere(string setName, float setRadius,
- Color setColor, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the sphere we create
- GeometryData sphereData = GeometryData.CreateSphere(shader.VertexFormat,
- setRadius, setColor);
-
- return new Mesh(setName, sphereData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Creates a sphere as a Mesh with the given parameters.
- /// </summary>
- /// <param name="setName">Name of the sphere mesh</param>
- /// <param name="setRadius">Radius for the sphere from the center</param>
- /// <param name="setMaterialData">The material data for the mesh.</param>
- /// <returns>Mesh containing the sphere.</returns>
- public static Mesh CreateSphere(string setName, float setRadius,
- MaterialData setMaterialData)
- {
- return CreateSphere(setName, setRadius, setMaterialData.Diffuse,
- setMaterialData);
- }
- #endregion
-
- #region CreateInnerSphere (Static)
- /// <summary>
- /// Create sphere with indices inverted (if you want to be inside the
- /// sphere, e.g. for a skydome).
- /// </summary>
- /// <param name="setName">Name of the sphere mesh</param>
- /// <param name="setRadius">Radius for the sphere from the center</param>
- /// <param name="setColor">
- /// Color for the new sphere in case the vertex format supports colored
- /// vertices (otherwise ignored).
- /// </param>
- /// <param name="setMaterialData">The material data for the mesh.</param>
- /// <returns>Mesh containing the inner sphere.</returns>
- public static Mesh CreateInnerSphere(string setName, float setRadius,
- Color setColor, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the sphere we create
- GeometryData sphereData = GeometryData.CreateSphere(shader.VertexFormat,
- setRadius, setColor, true);
-
- return new Mesh(setName, sphereData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Create sphere with indices inverted.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setRadius">The set radius.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing inner sphere data.</returns>
- public static Mesh CreateInnerSphere(string setName, float setRadius,
- MaterialData setMaterialData)
- {
- return CreateInnerSphere(setName, setRadius, setMaterialData.Diffuse,
- setMaterialData);
- }
- #endregion
-
- #region CreateCapsule (Static)
- /// <summary>
- /// Create capsule mesh.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setDiameter">The diameter of cylinder.</param>
- /// <param name="setLength">Length of the set.</param>
- /// <param name="setColor">Vertex color during creation.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing capsule data.</returns>
- public static Mesh CreateCapsule(string setName, float setDiameter,
- float setLength, Color setColor, MaterialData setMaterialData)
- {
- // First we still need to get the shader for the required vertex format
- Shader shader = Shader.Create(setMaterialData.ShaderName);
- // for the sphere we create
- GeometryData capsuleData = GeometryData.CreateCapsule(
- shader.VertexFormat, setDiameter, setLength, setColor);
-
- return new Mesh(setName, capsuleData, setMaterialData, Vector.Zero);
- }
-
- /// <summary>
- /// Create capsule mesh.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setDiameter">The diameter of cylinder.</param>
- /// <param name="setLength">Length of the set.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <returns>Mesh containing capsule data.</returns>
- public static Mesh CreateCapsule(string setName, float setDiameter,
- float setLength, MaterialData setMaterialData)
- {
- return CreateCapsule(setName, setDiameter, setLength,
- setMaterialData.Diffuse, setMaterialData);
- }
- #endregion
-
- #region Create Cylinder
-
- ///// <summary>
- ///// Create cylinder
- ///// </summary>
- //public static Mesh CreateCylinder(string setName, float setHeight,
- // float setRadius, MaterialData setMaterialData)
- //{
- // // First we still need to get the shader for the required vertex format
- // Shader shader = Shader.Create(setMaterialData.ShaderName);
- // // for the sphere we create
- // GeometryData capsuleData = GeometryData.CreateCylinder(
- // shader.VertexFormat, setHeight, setRadius, Color.White);
-
- // return new Mesh(setName, capsuleData, setMaterialData, Vector.Zero,
- // null);
- //}
- #endregion
-
- #region Name (Public)
- /// <summary>
- /// The name of the model. Usually set from content, but can also be a
- /// dynamically created model with a custom name.
- /// </summary>
- public string Name
- {
- get;
- private set;
- }
- #endregion
-
- #region Geometry (Public)
- /// <summary>
- /// Gets the geometry which defines the mesh.
- /// </summary>
- public Geometry Geometry
- {
- get;
- private set;
- }
- #endregion
-
- #region PositionOffset (Public)
- /// <summary>
- /// The offset translation of the mesh to represent its local space.
- /// Automatically added to rendering when using the Draw method with the
- /// matrix overload.
- /// </summary>
- /// <returns>Vector</returns>
- public Vector PositionOffset
- {
- get;
- private set;
- }
- #endregion
-
- #region Material (Public)
- /// <summary>
- /// The material which is used to visualize the geometry of the mesh.
- /// </summary>
- public BaseMaterial Material;
- #endregion
-
- #region IsHidden (Public)
- /// <summary>
- /// Level Editor Property, is true when the mesh should not be rendered.
- /// </summary>
- public bool IsHidden
- {
- get;
- set;
- }
- #endregion
-
- #region Private
-
- #region hasPositionOffset (Private)
- /// <summary>
- /// Remember if we have a position offset, if it is Zero, this is true
- /// and the Draw code is a little more optimized (often the case).
- /// </summary>
- private readonly bool hasPositionOffset;
- #endregion
-
- #region cachedRenderMatrix (Private)
- /// <summary>
- /// Default render matrix when rendering this model without any extra
- /// position or render matrix parameters. Usually used for levels and
- /// static geometry. Initially Matrix.Identity, but the PositionOffset
- /// is also already applied on this. Can be ignored if hasPositionOffset
- /// is false.
- /// </summary>
- private Matrix cachedRenderMatrix = Matrix.Identity;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create model from content name. Will warn about missing materials.
- /// </summary>
- /// <param name="contentName">Name of the content.</param>
- public Mesh(string contentName)
- : this(MeshData.Get(contentName))
- {
- }
-
- /// <summary>
- /// Create model from ModelData (usually called from content name
- /// constructor above).
- /// </summary>
- /// <param name="setData">Set mesh data</param>
- public Mesh(MeshData setData)
- : this(setData.Name, setData.Geometry, setData.Material,
- setData.PositionOffset)
- {
- }
-
- /// <summary>
- /// Prevents a default instance of the <see cref="Mesh"/> class from being created.
- /// </summary>
- /// <param name="setName">Name of the set.</param>
- /// <param name="setGeometryData">The set geometry data.</param>
- /// <param name="setMaterialData">The set material data.</param>
- /// <param name="setPositionOffset">The set position offset.</param>
- private Mesh(string setName, GeometryData setGeometryData,
- MaterialData setMaterialData, Vector setPositionOffset)
- {
- // If anything is invalid or empty, create box
- if (setGeometryData == null ||
- setMaterialData == null)
- {
- // Use default material (checker map) for the box we create
- MaterialData emptyMaterialData = new MaterialData();
- BaseMaterial emptyMaterial =
- new Material(emptyMaterialData);
- GeometryData planeData = GeometryData.CreateCube(
- emptyMaterial.shader.VertexFormat, 1);
- setName += "_Failed";
- setGeometryData = planeData;
- setMaterialData = emptyMaterialData;
- } // if
-
- Name = setName;
- Geometry = Geometry.Create(setGeometryData);
- PositionOffset = setPositionOffset;
- hasPositionOffset =
- PositionOffset.X != 0 ||
- PositionOffset.Y != 0 ||
- PositionOffset.Z != 0;
- cachedRenderMatrix.Translation += PositionOffset;
- if (setMaterialData.Diffuse != Color.White)
- {
- Material = new MaterialColored(setMaterialData);
- }
- else
- {
- Material = new Material(setMaterialData);
- }
- }
- #endregion
-
- #region Draw (Public)
- /// <summary>
- /// Draws the mesh exactly in that way as it was originally exported by the
- /// artist (including the computed position offset).
- /// </summary>
- public void Draw()
- {
- Material.Draw(Geometry, ref cachedRenderMatrix);
- }
-
- /// <summary>
- /// Draws the mesh with a custom draw transformation additionally to the
- /// original mesh transformation (including the computed position offset).
- /// </summary>
- /// <param name="customDrawTransformation">The custom draw transformation.</param>
- public void Draw(ref Matrix customDrawTransformation)
- {
- // If we have a position offset, we need to modify the render matrix!
- if (hasPositionOffset)
- {
- // Helper to add render matrices offset quickly, we do not want to
- // change the input matrix (it is passed as ref).
- Matrix renderMatrix = customDrawTransformation;
- // Adding is quicker than multiplying two matrices.
- renderMatrix.Translation += PositionOffset;
- Material.Draw(Geometry, ref renderMatrix);
- }
- else
- {
- Material.Draw(Geometry, ref customDrawTransformation);
- }
- }
- #endregion
-
- #region DrawCentered (Public)
- /// <summary>
- /// Draws the mesh centered in the world origin or in other words without
- /// the position offset computed by the content importer.
- /// </summary>
- public void DrawCentered()
- {
- // Note: PositionOffset and cachedRenderMatrix is ignored here on purpose
- Material.Draw(Geometry, ref Matrix.Identity);
- }
- #endregion
- }
- }