/Rendering/Basics/Drawing/BillboardManager.cs
C# | 498 lines | 304 code | 45 blank | 149 comment | 26 complexity | 3aa5df99fe294f51d297c2a4eb3132b8 MD5 | raw file
Possible License(s): Apache-2.0
- using Delta.Engine;
- using Delta.Engine.Dynamic;
- using Delta.Rendering.Enums;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
-
- namespace Delta.Rendering.Basics.Drawing
- {
- /// <summary>
- /// Class that allows drawing billboards and managing them in a more
- /// optimized and useful matter than just drawing out materials (which is
- /// also possible). Usually used from Effects system to draw 3D billboards.
- /// </summary>
- public class BillboardManager : DynamicModule
- {
- #region Instance (Static)
- /// <summary>
- /// Instance for this Billboard manager (handled only here privately)
- /// </summary>
- public static BillboardManager Instance
- {
- get
- {
- if (instance == null)
- {
- // Needs to be created via factory to make sure we only do this once
- instance = Factory.Create<BillboardManager>();
- }
- return instance;
- }
- }
- #endregion
-
- #region Private
-
- #region DefaultNormal (Private)
- /// <summary>
- /// The default normal vector of billboards e.g. Vector.Zero so the
- /// billboard will be calculated in special ways.
- /// </summary>
- private static Vector DefaultNormal = Vector.Zero;
- #endregion
-
- #region DefaultGroundNormal (Private)
- /// <summary>
- /// The default ground normal used to calculate the billboard directly.
- /// </summary>
- private static Vector DefaultGroundNormal = Vector.UnitZ;
- #endregion
-
- #region instance (Private)
- /// <summary>
- /// Private instance
- /// </summary>
- private static BillboardManager instance;
- #endregion
-
- #region transformMatrix (Private)
- /// <summary>
- /// These matrices cache the last calculated transformation for billboards,
- /// used for billboards that only get calculated the first time and then
- /// reusing these matrices.
- /// </summary>
- private Matrix transformMatrix;
- #endregion
-
- #region transformMatrixAll (Private)
- private Matrix transformMatrixAll;
- #endregion
-
- #region transformMatrixGround (Private)
- private Matrix transformMatrixGround;
- #endregion
-
- #region transformMatrixFront (Private)
- private Matrix transformMatrixFront;
- #endregion
-
- #region transformMatrixUp (Private)
- private Matrix transformMatrixUp;
- #endregion
-
- #region calculatedTransformAll (Private)
- /// <summary>
- /// Flags for the calculation caches above to notice if something needs
- /// to be calculated or not.
- /// </summary>
- private bool calculatedTransformAll;
- #endregion
-
- #region calculatedTransformGround (Private)
- private bool calculatedTransformGround;
- #endregion
-
- #region calculatedTransformFront (Private)
- private bool calculatedTransformFront;
- #endregion
-
- #region calculatedTransformUp (Private)
- private bool calculatedTransformUp;
- #endregion
-
- #region currentPosition3D (Private)
- /// <summary>
- /// The current 3d position of the billboard, set in the ProcessBillboard
- /// method. This "cache" is only used so we don't have to copy over
- /// the position every time (even with ref it costs) and to make the
- /// method more readable by removing all the "ref position3D" stuff.
- /// </summary>
- private Vector currentPosition3D;
- #endregion
-
- #region billboardMode (Private)
- /// <summary>
- /// The current mode of the billboard. Same usage as position3D above.
- /// </summary>
- private BillboardMode billboardMode;
- #endregion
-
- #region points3D (Private)
- /// <summary>
- /// Helper for calculating billboard positions
- /// </summary>
- private readonly Vector[] points3D = new Vector[4];
- #endregion
-
- #region rotatedPoints (Private)
- /// <summary>
- /// Rotation points helper for the Add method with rotation. The
- /// Rectangle.Rotate method will fill these 4 points and they will then
- /// be filled into the vertex data stream.
- /// </summary>
- private readonly Point[] rotatedPoints = new Point[4];
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Creates a new instance of BillboardManager.
- /// </summary>
- public BillboardManager()
- : base("BillboardManager", typeof(MaterialManager))
- {
- }
- #endregion
-
- #region Draw (Public)
- /// <summary>
- /// Performs draw billboard stuff.
- /// </summary>
- /// <param name="material">The material to draw with.</param>
- /// <param name="position3D">The position in 3D space.</param>
- /// <param name="size">The size of the quad.</param>
- /// <param name="rotation">The rotation.</param>
- public void Draw(MaterialColored material, Vector position3D, Size size,
- float rotation)
- {
- ProcessBillboard(material, ref position3D, ref size, rotation,
- ref DefaultNormal);
- }
-
- /// <summary>
- /// Performs draw billboard stuff.
- /// </summary>
- /// <param name="material">The material to draw with.</param>
- /// <param name="position3D">The position in 3D space.</param>
- /// <param name="size">The size of the quad.</param>
- /// <param name="rotation">The rotation.</param>
- /// <param name="normal">Normal to align the billboard to</param>
- public void Draw(MaterialColored material, Vector position3D, Size size,
- float rotation, Vector normal)
- {
- ProcessBillboard(material, ref position3D, ref size, rotation,
- ref normal);
- }
-
- /// <summary>
- /// Performs draw billboard stuff.
- /// </summary>
- /// <param name="material">The material to draw with.</param>
- /// <param name="position3D">The position in 3D space.</param>
- /// <param name="size">The size of the quad.</param>
- /// <param name="rotation">The rotation.</param>
- /// <param name="blendColorOverride">Overwritten blend color</param>
- public void Draw(MaterialColored material, Vector position3D, Size size,
- float rotation, Color blendColorOverride)
- {
- material.BlendColor = blendColorOverride;
- ProcessBillboard(material, ref position3D, ref size, rotation,
- ref DefaultNormal);
- }
- #endregion
-
- #region DrawPlane (Public)
- /// <summary>
- /// Draw the material as a billboard ground plane.
- /// </summary>
- /// <param name="material">The material to draw with.</param>
- public void DrawPlane(MaterialColored material)
- {
- DrawPlane(material, Vector.Zero, new Size(20), 0f);
- }
-
- /// <summary>
- /// Draw the material as a billboard ground plane.
- /// </summary>
- /// <param name="material">The material to draw with.</param>
- /// <param name="position3D">The position in 3D space.</param>
- /// <param name="size">The size of the quad.</param>
- /// <param name="rotation">The rotation.</param>
- public void DrawPlane(MaterialColored material, Vector position3D,
- Size size, float rotation)
- {
- material.billboardMode = BillboardMode.Ground;
- ProcessBillboard(material, ref position3D, ref size, 0f,
- ref DefaultGroundNormal);
- }
- #endregion
-
- #region Run (Public)
- /// <summary>
- /// Run method from DynamicModule.
- /// </summary>
- public override void Run()
- {
- // Reset Billboard pre-calculated transformMatrices
- calculatedTransformAll = false;
- calculatedTransformGround = false;
- calculatedTransformFront = false;
- calculatedTransformUp = false;
- }
- #endregion
-
- #region Methods (Private)
-
- #region SetupBillboardTransformOnce
- /// <summary>
- /// Only does the actual setup if alreadyDone is set to false.
- /// Sets alreadyDone to true afterwards
- /// </summary>
- /// <param name="alreadyDone">
- /// Boolean value used and assigned after setting the transform once.
- /// </param>
- /// <param name="transform">
- /// The transform calculated if alreadyDone is false.
- /// </param>
- private void SetupBillboardTransformOnce(ref Matrix transform,
- ref bool alreadyDone)
- {
- if (alreadyDone == false)
- {
- SetupBillboardTransform(ref transform);
- alreadyDone = true;
- }
- }
- #endregion
-
- #region SetupBillboardTransform
- /// <summary>
- /// Setup billboard transform for all upcoming billboard render actions.
- /// Note: It does not set translation, as this must be done individually.
- /// </summary>
- /// <param name="transform">Transform matrix for the billboards</param>
- private void SetupBillboardTransform(ref Matrix transform)
- {
- //Vector look = billboardMode.IsFlagSet(BillboardMode.Collective) ?
- // Vector.Cross(ScreenSpace.ViewInverse.Right, ScreenSpace.ViewInverse.Up) :
- // ScreenSpace.ViewInverse.Translation - position3D;
- Vector look =
- ScreenSpace.InternalViewInverse.Translation - currentPosition3D;
- Vector cameraUp = ScreenSpace.InternalViewInverse.Up;
- if (billboardMode.IsFlagSet(BillboardMode.FrontAxis))
- {
- cameraUp = Vector.UnitY;
- look.Y = 0;
- }
- else if (billboardMode.IsFlagSet(BillboardMode.UpAxis))
- {
- cameraUp = Vector.UnitZ;
- look.Z = 0;
- }
- else if (billboardMode.IsFlagSet(BillboardMode.Ground))
- {
- cameraUp = -Vector.UnitZ;
- look = -Vector.UnitX;
- }
- look.Normalize();
- Vector right = Vector.Cross(cameraUp, look);
- Vector up = Vector.Cross(look, right);
-
- // Setup transform Matrix
- transform.Right = right;
- transform.Up = look;
- transform.Front = up;
- }
- #endregion
-
- #region ApplyBillboardTransform
- /// <summary>
- /// Apply billboard transform to given Vector Array.
- /// given position overrides transform.Translation
- /// </summary>
- /// <param name="transform">Transform matrix for the billboards</param>
- private void ApplyBillboardTransform(ref Matrix transform)
- {
- transform.Translation = currentPosition3D;
- int len = points3D.Length;
- for (int index = 0; index < len; index++)
- {
- Vector tmp;
- Vector.Transform(ref points3D[index], ref transform, out tmp);
- points3D[index] = tmp;
- }
- }
- #endregion
-
- #region ProcessBillboard
- /// <summary>
- /// Process a billboard and add it to the material manager.
- /// </summary>
- /// <param name="material">Material to render on the billboard.</param>
- /// <param name="position3D">The #define position of the billboard.</param>
- /// <param name="size">The size of the billboard.</param>
- /// <param name="rotation">The rotation of the billboard.</param>
- /// <param name="normal">The normal vector of the billboard
- /// (if available)</param>
- private void ProcessBillboard(MaterialColored material,
- ref Vector position3D, ref Size size, float rotation, ref Vector normal)
- {
- currentPosition3D = position3D;
- // Check creation plane
- billboardMode = material.billboardMode;
- bool createXY = billboardMode.IsFlagSet(BillboardMode.Ground);
-
- //// If the content is reduced in the atlas, remap the rendering!
- //if (material.diffuseMap.useInnerDrawArea)
- //{
- // drawArea =
- // drawArea.GetInnerRectangle(material.diffuseMap.innerDrawArea);
- //}
-
- #region Basic vertex positioning
- // Now setup vertices based on given size at origin in XZ plane
- if (rotation != 0.0f)
- {
- // Apply the rotation
- new Rectangle(-size.WidthHalf, -size.HeightHalf, size.Width,
- size.Height).Rotate(rotation, rotatedPoints);
-
- // Left Upper
- points3D[0].X = rotatedPoints[3].X;
- points3D[0].Y = createXY
- ? rotatedPoints[3].Y
- : 0;
- points3D[0].Z = createXY
- ? 0
- : rotatedPoints[3].Y;
-
- // Right Upper
- points3D[1].X = rotatedPoints[2].X;
- points3D[1].Y = createXY
- ? rotatedPoints[2].Y
- : 0;
- points3D[1].Z = createXY
- ? 0
- : rotatedPoints[2].Y;
-
- // Left Lower
- points3D[2].X = rotatedPoints[1].X;
- points3D[2].Y = createXY
- ? rotatedPoints[1].Y
- : 0;
- points3D[2].Z = createXY
- ? 0
- : rotatedPoints[1].Y;
-
- // Right Lower
- points3D[3].X = rotatedPoints[0].X;
- points3D[3].Y = createXY
- ? rotatedPoints[0].Y
- : 0;
- points3D[3].Z = createXY
- ? 0
- : rotatedPoints[0].Y;
- }
- else
- {
- // Left Upper
- points3D[0].X = -size.WidthHalf;
- points3D[0].Y = createXY
- ? size.HeightHalf
- : 0;
- points3D[0].Z = createXY
- ? 0
- : size.HeightHalf;
-
- // Right Upper
- points3D[1].X = size.WidthHalf;
- points3D[1].Y = createXY
- ? size.HeightHalf
- : 0;
- points3D[1].Z = createXY
- ? 0
- : size.HeightHalf;
-
- // Left Lower
- points3D[3].X = -size.WidthHalf;
- points3D[3].Y = createXY
- ? -size.HeightHalf
- : 0;
- points3D[3].Z = createXY
- ? 0
- : -size.HeightHalf;
-
- // Right Lower
- points3D[2].X = size.WidthHalf;
- points3D[2].Y = createXY
- ? -size.HeightHalf
- : 0;
- points3D[2].Z = createXY
- ? 0
- : -size.HeightHalf;
- } // else
- #endregion
-
- if (normal != Vector.Zero)
- {
- // rotate according to given normal
- // Extract rotation axis
- // Just exchanging frontAxis with UnitY gives strange result
- Vector frontAxis = Vector.UnitZ;
- Vector rotationAxis = Vector.Cross(frontAxis, normal);
- rotationAxis.Normalize();
- // Extract rotation angle
- float angle = Vector.AngleBetweenVectors(frontAxis, normal);
- // Build axis rotation matrix
- Matrix rotationMatrix =
- Matrix.CreateFromAxisAngle(rotationAxis, angle);
-
- // And finally apply the rotation (and translation)
- ApplyBillboardTransform(ref rotationMatrix);
- }
- else // do billboard logic
- {
- // Billboard logic taken from:
- // http://nehe.gamedev.net/data/articles/article.asp?article=19
- // Calculate look direction (into which billboard should look)
- // Here we use pre-calculated transformMatrices and only set position
- if (createXY)
- {
- SetupBillboardTransformOnce(ref transformMatrixGround,
- ref calculatedTransformGround);
- ApplyBillboardTransform(ref transformMatrixGround);
- }
- else if (billboardMode.IsFlagSet(BillboardMode.FrontAxis))
- {
- SetupBillboardTransformOnce(ref transformMatrixFront,
- ref calculatedTransformFront);
- ApplyBillboardTransform(ref transformMatrixFront);
- }
- else if (billboardMode.IsFlagSet(BillboardMode.UpAxis))
- {
- SetupBillboardTransformOnce(ref transformMatrixUp,
- ref calculatedTransformUp);
- ApplyBillboardTransform(ref transformMatrixUp);
- }
- else if (billboardMode.IsFlagSet(BillboardMode.CameraFacing))
- {
- SetupBillboardTransformOnce(ref transformMatrixAll,
- ref calculatedTransformAll);
- ApplyBillboardTransform(ref transformMatrixAll);
- }
- else if (billboardMode.IsFlagSet(BillboardMode.Only2D))
- {
- // Only translate points
- int len = points3D.Length;
- for (int index = 0; index < len; index++)
- {
- points3D[index] += position3D;
- }
- }
- else // CameraFacingPrecise or unknown
- {
- // Calculate billboards individual
- SetupBillboardTransform(ref transformMatrix);
- // Apply transformation matrix
- ApplyBillboardTransform(ref transformMatrix);
- }
- }
-
- material.cachedLayer.AddBillboard(material, points3D);
- }
- #endregion
-
- #endregion
- }
- }