PageRenderTime 146ms CodeModel.GetById 61ms app.highlight 19ms RepoModel.GetById 61ms app.codeStats 0ms

/Rendering/Models/Mesh.cs

#
C# | 572 lines | 263 code | 46 blank | 263 comment | 9 complexity | ddac8929b43d11dd995fb09df3567913 MD5 | raw file
  1using System;
  2using Delta.ContentSystem.Rendering;
  3using Delta.Graphics.Basics;
  4using Delta.Rendering.Basics.Materials;
  5using Delta.Utilities;
  6using Delta.Utilities.Datatypes;
  7
  8namespace Delta.Rendering.Models
  9{
 10	/// <summary>
 11	/// Mesh model class for 3d meshes (but 2d meshes also work). Basically
 12	/// a mesh is just a combination of Geometry and Material. Models can
 13	/// consist of multiple meshes.
 14	/// </summary>
 15	public class Mesh
 16	{
 17		#region Constants
 18		/// <summary>
 19		/// Line colors for displaying the bones as lines.
 20		/// </summary>
 21		private static readonly Color[] BoneColors = new[]
 22		{
 23			Color.Blue, Color.Red, Color.Yellow, Color.White, Color.Teal,
 24			Color.Brown, Color.Orange, Color.LightBlue, Color.Green,
 25			Color.Purple,
 26		};
 27		#endregion
 28
 29		#region CreatePlane (Static)
 30		/// <summary>
 31		/// Creates an XY plane.
 32		/// </summary>
 33		/// <param name="setName">Set name</param>
 34		/// <param name="setWidth">Set width</param>
 35		/// <param name="setDepth">Set depth</param>
 36		/// <param name="setColor">Vertex color during creation.</param>
 37		/// <param name="setMaterialData">Set material data</param>
 38		/// <returns>Mesh with the created plane and given material</returns>
 39		public static Mesh CreatePlane(string setName, float setWidth,
 40			float setDepth, Color setColor, MaterialData setMaterialData)
 41		{
 42			// First we still need to get the shader for the required vertex format
 43			Shader shader = Shader.Create(setMaterialData.ShaderName);
 44			// for the plane we create
 45			GeometryData planeData = GeometryData.CreatePlane(shader.VertexFormat,
 46				setWidth, setDepth, setColor);
 47
 48			return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
 49		}
 50
 51		/// <summary>
 52		/// Creates an XY plane
 53		/// </summary>
 54		/// <param name="setName">Set name</param>
 55		/// <param name="setWidth">Set width</param>
 56		/// <param name="setDepth">Set depth</param>
 57		/// <param name="setMaterialData">Set material data</param>
 58		/// <returns>Mesh with the created plane and given material</returns>
 59		public static Mesh CreatePlane(string setName, float setWidth,
 60			float setDepth, MaterialData setMaterialData)
 61		{
 62			// First we still need to get the shader for the required vertex format
 63			Shader shader = Shader.Create(setMaterialData.ShaderName);
 64			// for the plane we create
 65			GeometryData planeData = GeometryData.CreatePlane(shader.VertexFormat,
 66				setWidth, setDepth, setMaterialData.Diffuse);
 67
 68			return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
 69		}
 70		#endregion
 71
 72		#region CreateSegmentedPlane (Static)
 73		/// <summary>
 74		/// Creates an XY plane with segments, which is useful for testing
 75		/// specular and other shader effects with pre-calculated data in the
 76		/// vertex shader (which will not look close up for just a 1x1 plane).
 77		/// </summary>
 78		/// <param name="setName">Name of the set.</param>
 79		/// <param name="setWidth">Width of the set.</param>
 80		/// <param name="setDepth">The set depth.</param>
 81		/// <param name="setSegments">The set segments.</param>
 82		/// <param name="uvForEachSegment">Create new uv from 0-1 for each segment.
 83		/// Useful for tiling (works even without tileable textures)</param>
 84		/// <param name="setColor">Vertex color during creation.</param>
 85		/// <param name="setMaterialData">The set material data.</param>
 86		/// <returns>Mesh containing segmented plane data.</returns>
 87		public static Mesh CreateSegmentedPlane(string setName, float setWidth,
 88			float setDepth, int setSegments, bool uvForEachSegment, Color setColor,
 89			MaterialData setMaterialData)
 90		{
 91			// First we still need to get the shader for the required vertex format
 92			Shader shader = Shader.Create(setMaterialData.ShaderName);
 93			// for the plane we create
 94			GeometryData planeData = GeometryData.CreateSegmentedPlane(
 95				shader.VertexFormat, setWidth, setDepth, setSegments, uvForEachSegment,
 96				setColor);
 97
 98			return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
 99		}
100
101		/// <summary>
102		/// Creates an XY plane with segments, which is useful for testing
103		/// specular and other shader effects with pre-calculated data in the
104		/// vertex shader (which will not look close up for just a 1x1 plane).
105		/// </summary>
106		/// <param name="setName">Name of the set.</param>
107		/// <param name="setWidth">Width of the set.</param>
108		/// <param name="setDepth">The set depth.</param>
109		/// <param name="setSegments">The set segments.</param>
110		/// <param name="setMaterialData">The set material data.</param>
111		/// <returns>
112		/// The new create Mesh.
113		/// </returns>
114		public static Mesh CreateSegmentedPlane(string setName, float setWidth,
115			float setDepth, int setSegments, MaterialData setMaterialData)
116		{
117			return CreateSegmentedPlane(setName, setWidth, setDepth, setSegments,
118				false, setMaterialData.Diffuse, setMaterialData);
119		}
120		#endregion
121
122		#region CreateBox (Static)
123		/// <summary>
124		/// Create box mesh.
125		/// </summary>
126		/// <param name="setName">Name of the set.</param>
127		/// <param name="setWidth">Width of the set.</param>
128		/// <param name="setDepth">The set depth.</param>
129		/// <param name="setHeight">Height of the set.</param>
130		/// <param name="setColor">Vertex color during creation.</param>
131		/// <param name="setMaterialData">The set material data.</param>
132		/// <returns>Mesh containing box data.</returns>
133		public static Mesh CreateBox(string setName, float setWidth,
134			float setDepth, float setHeight, Color setColor,
135			MaterialData setMaterialData)
136		{
137			// First we still need to get the shader for the required vertex format
138			Shader shader = Shader.Create(setMaterialData.ShaderName);
139			// for the box we create
140			GeometryData boxData = GeometryData.CreateBox(shader.VertexFormat,
141				setWidth, setDepth, setHeight, false, setColor);
142
143			return new Mesh(setName, boxData, setMaterialData, Vector.Zero);
144		}
145
146		/// <summary>
147		/// Create box mesh.
148		/// </summary>
149		/// <param name="setName">Name of the set.</param>
150		/// <param name="setWidth">Width of the set.</param>
151		/// <param name="setDepth">The set depth.</param>
152		/// <param name="setHeight">Height of the set.</param>
153		/// <param name="setMaterialData">The set material data.</param>
154		/// <returns>Mesh containing box data.</returns>
155		public static Mesh CreateBox(string setName, float setWidth,
156			float setDepth, float setHeight,
157			MaterialData setMaterialData)
158		{
159			return CreateBox(setName, setWidth, setDepth, setHeight,
160				setMaterialData.Diffuse, setMaterialData);
161		}
162		#endregion
163
164		#region CreateCube (Static)
165		/// <summary>
166		/// Create cube shape with all 6 faces with equal size.
167		/// </summary>
168		/// <param name="setName">Name of the set.</param>
169		/// <param name="setSize">The size of the faces.</param>
170		/// <param name="setColor">Vertex color during creation.</param>
171		/// <param name="setMaterialData">The set material data.</param>
172		/// <returns>Mesh containing cube data.</returns>
173		public static Mesh CreateCube(string setName, float setSize,
174			Color setColor, MaterialData setMaterialData)
175		{
176			// First we still need to get the shader for the required vertex format
177			Shader shader = Shader.Create(setMaterialData.ShaderName);
178
179			// for the box we create
180			GeometryData planeData = GeometryData.CreateCube(shader.VertexFormat,
181				setSize, setColor);
182
183			return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
184		}
185
186		/// <summary>
187		/// Create cube shape with all 6 faces with equal size.
188		/// </summary>
189		/// <param name="setName">Name of the set.</param>
190		/// <param name="setSize">The size of the faces.</param>
191		/// <param name="setMaterialData">The set material data.</param>
192		/// <returns>Mesh containing cube data.</returns>
193		public static Mesh CreateCube(string setName, float setSize,
194			MaterialData setMaterialData)
195		{
196			return CreateCube(setName, setSize, setMaterialData.Diffuse,
197				setMaterialData);
198		}
199		#endregion
200
201		#region CreateInnerBox (Static)
202		/// <summary>
203		/// Creates an inner box (-&gt; like the 'CreateBox()' method but in the
204		/// "inverted" way).
205		/// Note: This kind of a box is helpful if you want to render something
206		/// inside a box like a simple (demo) room.
207		/// </summary>
208		/// <param name="setName">Name of the set.</param>
209		/// <param name="setWidth">Width of the set.</param>
210		/// <param name="setDepth">The set depth.</param>
211		/// <param name="setHeight">Height of the set.</param>
212		/// <param name="setColor">Vertex color during creation.</param>
213		/// <param name="setMaterialData">The set material data.</param>
214		/// <returns>Mesh containing inner box data.</returns>
215		public static Mesh CreateInnerBox(string setName, float setWidth,
216			float setDepth, float setHeight, Color setColor, MaterialData setMaterialData)
217		{
218			// First we still need to get the shader for the required vertex format
219			Shader shader = Shader.Create(setMaterialData.ShaderName);
220			// for the cube we create
221			GeometryData planeData = GeometryData.CreateBox(shader.VertexFormat,
222				setWidth, setDepth, setHeight, true, setColor);
223
224			return new Mesh(setName, planeData, setMaterialData, Vector.Zero);
225		}
226
227		/// <summary>
228		/// Creates an inner box (-&gt; like the 'CreateBox()' method but in the
229		/// "inverted" way).
230		/// Note: This kind of a box is helpful if you want to render something
231		/// inside a box like a simple (demo) room.
232		/// </summary>
233		/// <param name="setName">Name of the set.</param>
234		/// <param name="setWidth">Width of the set.</param>
235		/// <param name="setDepth">The set depth.</param>
236		/// <param name="setHeight">Height of the set.</param>
237		/// <param name="setMaterialData">The set material data.</param>
238		/// <returns>Mesh containing inner box data.</returns>
239		public static Mesh CreateInnerBox(string setName, float setWidth,
240			float setDepth, float setHeight, MaterialData setMaterialData)
241		{
242			return CreateInnerBox(setName, setWidth, setDepth, setHeight,
243				setMaterialData.Diffuse, setMaterialData);
244		}
245		#endregion
246
247		#region CreateSphere (Static)
248		/// <summary>
249		/// Creates a sphere as a Mesh with the given parameters.
250		/// </summary>
251		/// <param name="setName">Name of the sphere mesh</param>
252		/// <param name="setRadius">Radius for the sphere from the center</param>
253		/// <param name="setColor">
254		/// Color for the new sphere in case the vertex format supports colored
255		/// vertices (otherwise ignored).
256		/// </param>
257		/// <param name="setMaterialData">The material data for the mesh.</param>
258		/// <returns>Mesh containing the sphere.</returns>
259		public static Mesh CreateSphere(string setName, float setRadius,
260			Color setColor, MaterialData setMaterialData)
261		{
262			// First we still need to get the shader for the required vertex format
263			Shader shader = Shader.Create(setMaterialData.ShaderName);
264			// for the sphere we create
265			GeometryData sphereData = GeometryData.CreateSphere(shader.VertexFormat,
266				setRadius, setColor);
267
268			return new Mesh(setName, sphereData, setMaterialData, Vector.Zero);
269		}
270
271		/// <summary>
272		/// Creates a sphere as a Mesh with the given parameters.
273		/// </summary>
274		/// <param name="setName">Name of the sphere mesh</param>
275		/// <param name="setRadius">Radius for the sphere from the center</param>
276		/// <param name="setMaterialData">The material data for the mesh.</param>
277		/// <returns>Mesh containing the sphere.</returns>
278		public static Mesh CreateSphere(string setName, float setRadius,
279			MaterialData setMaterialData)
280		{
281			return CreateSphere(setName, setRadius, setMaterialData.Diffuse,
282				setMaterialData);
283		}
284		#endregion
285
286		#region CreateInnerSphere (Static)
287		/// <summary>
288		/// Create sphere with indices inverted (if you want to be inside the
289		/// sphere, e.g. for a skydome).
290		/// </summary>
291		/// <param name="setName">Name of the sphere mesh</param>
292		/// <param name="setRadius">Radius for the sphere from the center</param>
293		/// <param name="setColor">
294		/// Color for the new sphere in case the vertex format supports colored
295		/// vertices (otherwise ignored).
296		/// </param>
297		/// <param name="setMaterialData">The material data for the mesh.</param>
298		/// <returns>Mesh containing the inner sphere.</returns>
299		public static Mesh CreateInnerSphere(string setName, float setRadius,
300			Color setColor, MaterialData setMaterialData)
301		{
302			// First we still need to get the shader for the required vertex format
303			Shader shader = Shader.Create(setMaterialData.ShaderName);
304			// for the sphere we create
305			GeometryData sphereData = GeometryData.CreateSphere(shader.VertexFormat,
306				setRadius, setColor, true);
307
308			return new Mesh(setName, sphereData, setMaterialData, Vector.Zero);
309		}
310
311		/// <summary>
312		/// Create sphere with indices inverted.
313		/// </summary>
314		/// <param name="setName">Name of the set.</param>
315		/// <param name="setRadius">The set radius.</param>
316		/// <param name="setMaterialData">The set material data.</param>
317		/// <returns>Mesh containing inner sphere data.</returns>
318		public static Mesh CreateInnerSphere(string setName, float setRadius,
319			MaterialData setMaterialData)
320		{
321			return CreateInnerSphere(setName, setRadius, setMaterialData.Diffuse,
322				setMaterialData);
323		}
324		#endregion
325
326		#region CreateCapsule (Static)
327		/// <summary>
328		/// Create capsule mesh.
329		/// </summary>
330		/// <param name="setName">Name of the set.</param>
331		/// <param name="setDiameter">The diameter of cylinder.</param>
332		/// <param name="setLength">Length of the set.</param>
333		/// <param name="setColor">Vertex color during creation.</param>
334		/// <param name="setMaterialData">The set material data.</param>
335		/// <returns>Mesh containing capsule data.</returns>
336		public static Mesh CreateCapsule(string setName, float setDiameter,
337			float setLength, Color setColor, MaterialData setMaterialData)
338		{
339			// First we still need to get the shader for the required vertex format
340			Shader shader = Shader.Create(setMaterialData.ShaderName);
341			// for the sphere we create
342			GeometryData capsuleData = GeometryData.CreateCapsule(
343				shader.VertexFormat, setDiameter, setLength, setColor);
344
345			return new Mesh(setName, capsuleData, setMaterialData, Vector.Zero);
346		}
347
348		/// <summary>
349		/// Create capsule mesh.
350		/// </summary>
351		/// <param name="setName">Name of the set.</param>
352		/// <param name="setDiameter">The diameter of cylinder.</param>
353		/// <param name="setLength">Length of the set.</param>
354		/// <param name="setMaterialData">The set material data.</param>
355		/// <returns>Mesh containing capsule data.</returns>
356		public static Mesh CreateCapsule(string setName, float setDiameter,
357			float setLength, MaterialData setMaterialData)
358		{
359			return CreateCapsule(setName, setDiameter, setLength,
360				setMaterialData.Diffuse, setMaterialData);
361		}
362		#endregion
363
364		#region Create Cylinder
365
366		///// <summary>
367		///// Create cylinder
368		///// </summary>
369		//public static Mesh CreateCylinder(string setName, float setHeight,
370		//  float setRadius, MaterialData setMaterialData)
371		//{
372		//  // First we still need to get the shader for the required vertex format
373		//  Shader shader = Shader.Create(setMaterialData.ShaderName);
374		//  // for the sphere we create
375		//  GeometryData capsuleData = GeometryData.CreateCylinder(
376		//    shader.VertexFormat, setHeight, setRadius, Color.White);
377
378		//  return new Mesh(setName, capsuleData, setMaterialData, Vector.Zero,
379		//    null);
380		//}
381		#endregion
382
383		#region Name (Public)
384		/// <summary>
385		/// The name of the model. Usually set from content, but can also be a
386		/// dynamically created model with a custom name.
387		/// </summary>
388		public string Name
389		{
390			get;
391			private set;
392		}
393		#endregion
394
395		#region Geometry (Public)
396		/// <summary>
397		/// Gets the geometry which defines the mesh.
398		/// </summary>
399		public Geometry Geometry
400		{
401			get;
402			private set;
403		}
404		#endregion
405
406		#region PositionOffset (Public)
407		/// <summary>
408		/// The offset translation of the mesh to represent its local space.
409		/// Automatically added to rendering when using the Draw method with the
410		/// matrix overload.
411		/// </summary>
412		/// <returns>Vector</returns>
413		public Vector PositionOffset
414		{
415			get;
416			private set;
417		}
418		#endregion
419
420		#region Material (Public)
421		/// <summary>
422		/// The material which is used to visualize the geometry of the mesh.
423		/// </summary>
424		public BaseMaterial Material;
425		#endregion
426
427		#region IsHidden (Public)
428		/// <summary>
429		/// Level Editor Property, is true when the mesh should not be rendered.
430		/// </summary>
431		public bool IsHidden
432		{
433			get;
434			set;
435		}
436		#endregion
437
438		#region Private
439
440		#region hasPositionOffset (Private)
441		/// <summary>
442		/// Remember if we have a position offset, if it is Zero, this is true
443		/// and the Draw code is a little more optimized (often the case).
444		/// </summary>
445		private readonly bool hasPositionOffset;
446		#endregion
447
448		#region cachedRenderMatrix (Private)
449		/// <summary>
450		/// Default render matrix when rendering this model without any extra
451		/// position or render matrix parameters. Usually used for levels and
452		/// static geometry. Initially Matrix.Identity, but the PositionOffset
453		/// is also already applied on this. Can be ignored if hasPositionOffset
454		/// is false.
455		/// </summary>
456		private Matrix cachedRenderMatrix = Matrix.Identity;
457		#endregion
458
459		#endregion
460
461		#region Constructors
462		/// <summary>
463		/// Create model from content name. Will warn about missing materials.
464		/// </summary>
465		/// <param name="contentName">Name of the content.</param>
466		public Mesh(string contentName)
467			: this(MeshData.Get(contentName))
468		{
469		}
470
471		/// <summary>
472		/// Create model from ModelData (usually called from content name
473		/// constructor above).
474		/// </summary>
475		/// <param name="setData">Set mesh data</param>
476		public Mesh(MeshData setData)
477			: this(setData.Name, setData.Geometry, setData.Material,
478				setData.PositionOffset)
479		{
480		}
481
482		/// <summary>
483		/// Prevents a default instance of the <see cref="Mesh"/> class from being created.
484		/// </summary>
485		/// <param name="setName">Name of the set.</param>
486		/// <param name="setGeometryData">The set geometry data.</param>
487		/// <param name="setMaterialData">The set material data.</param>
488		/// <param name="setPositionOffset">The set position offset.</param>
489		private Mesh(string setName, GeometryData setGeometryData,
490			MaterialData setMaterialData, Vector setPositionOffset)
491		{
492			// If anything is invalid or empty, create box
493			if (setGeometryData == null ||
494			    setMaterialData == null)
495			{
496				// Use default material (checker map) for the box we create
497				MaterialData emptyMaterialData = new MaterialData();
498				BaseMaterial emptyMaterial =
499					new Material(emptyMaterialData);
500				GeometryData planeData = GeometryData.CreateCube(
501					emptyMaterial.shader.VertexFormat, 1);
502				setName += "_Failed";
503				setGeometryData = planeData;
504				setMaterialData = emptyMaterialData;
505			} // if
506
507			Name = setName;
508			Geometry = Geometry.Create(setGeometryData);
509			PositionOffset = setPositionOffset;
510			hasPositionOffset =
511				PositionOffset.X != 0 ||
512				PositionOffset.Y != 0 ||
513				PositionOffset.Z != 0;
514			cachedRenderMatrix.Translation += PositionOffset;
515			if (setMaterialData.Diffuse != Color.White)
516			{
517				Material = new MaterialColored(setMaterialData);
518			}
519			else
520			{
521				Material = new Material(setMaterialData);
522			}
523		}
524		#endregion
525
526		#region Draw (Public)
527		/// <summary>
528		/// Draws the mesh exactly in that way as it was originally exported by the
529		/// artist (including the computed position offset).
530		/// </summary>
531		public void Draw()
532		{
533			Material.Draw(Geometry, ref cachedRenderMatrix);
534		}
535
536		/// <summary>
537		/// Draws the mesh with a custom draw transformation additionally to the
538		/// original mesh transformation (including the computed position offset).
539		/// </summary>
540		/// <param name="customDrawTransformation">The custom draw transformation.</param>
541		public void Draw(ref Matrix customDrawTransformation)
542		{
543			// If we have a position offset, we need to modify the render matrix!
544			if (hasPositionOffset)
545			{
546				// Helper to add render matrices offset quickly, we do not want to
547				// change the input matrix (it is passed as ref).
548				Matrix renderMatrix = customDrawTransformation;
549				// Adding is quicker than multiplying two matrices.
550				renderMatrix.Translation += PositionOffset;
551				Material.Draw(Geometry, ref renderMatrix);
552			}
553			else
554			{
555				Material.Draw(Geometry, ref customDrawTransformation);
556			}
557		}
558		#endregion
559
560		#region DrawCentered (Public)
561		/// <summary>
562		/// Draws the mesh centered in the world origin or in other words without
563		/// the position offset computed by the content importer.
564		/// </summary>
565		public void DrawCentered()
566		{
567			// Note: PositionOffset and cachedRenderMatrix is ignored here on purpose
568			Material.Draw(Geometry, ref Matrix.Identity);
569		}
570		#endregion
571	}
572}