//-----------------------------------------------------------------------------
// LineManager3D.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RacingGame;
using System.Collections.Generic;
using RacingGame.Helpers;
using RacingGame.Shaders;
namespace RacingGame.Graphics
{
///
/// Helper class for game for rendering lines.
/// This class will collect all line calls, then build a new vertex buffer
/// if any line has changed or the line number changed and finally will
/// render all lines in the vertex buffer at the end of the frame (so this
/// class is obviously only for 2D lines directly on screen, no z buffer
/// and no stuff will be in front of the lines, because everything is
/// rendered at the end of the frame).
///
internal class LineManager3D : IDisposable
{
///
/// Struct for a line, instances of this class will be added to lines.
///
struct Line
{
// Positions
public Vector3 startPoint, endPoint;
// Colors
public Color startColor, endColor;
///
/// Constructor
///
public Line(
Vector3 setStartPoint, Color setStartColor,
Vector3 setEndPoint, Color setEndColor)
{
startPoint = setStartPoint;
startColor = setStartColor;
endPoint = setEndPoint;
endColor = setEndColor;
}
///
/// Are these two Lines equal?
///
public static bool operator ==(Line a, Line b)
{
return
a.startPoint == b.startPoint &&
a.endPoint == b.endPoint &&
a.startColor == b.startColor &&
a.endColor == b.endColor;
}
///
/// Are these two Lines not equal?
///
public static bool operator !=(Line a, Line b)
{
return
a.startPoint != b.startPoint ||
a.endPoint != b.endPoint ||
a.startColor != b.startColor ||
a.endColor != b.endColor;
}
///
/// Support Equals(.) to keep the compiler happy
/// (because we used == and !=)
///
public override bool Equals(object a)
{
if (a is Line)
return (Line)a == this;
return false;
}
///
/// Support GetHashCode() to keep the compiler happy
/// (because we used == and !=)
///
public override int GetHashCode()
{
return 0; // Not supported or nessescary
}
}
///
/// Number of lines used this frame, will be set to 0 when rendering.
///
private int numOfLines = 0;
///
/// The actual list for all the lines, it will NOT be reseted each
/// frame like numOfLines! We will remember the last lines and
/// only change this list when anything changes (new line, old
/// line missing, changing line data).
/// When this happens buildVertexBuffer will be set to true.
///
private List lines = new List();
///
/// Build vertex buffer this frame because the line list was changed?
///
private bool buildVertexBuffer = false;
///
/// Vertex buffer for all lines
///
VertexPositionColor[] lineVertices =
new VertexPositionColor[MaxNumOfLines * 2];
///
/// Real number of primitives currently used.
///
private int numOfPrimitives = 0;
///
/// Max. number of lines allowed to prevent to big buffer, will never
/// be reached, but in case something goes wrong or numOfLines is not
/// reseted each frame, we won't add unlimited lines (all new lines
/// will be ignored if this max. number is reached).
///
protected const int MaxNumOfLines =
4096;
///
/// Init LineManager
///
public LineManager3D()
{
if (BaseGame.Device == null)
throw new ArgumentNullException(
"XNA device is not initialized, can't init line manager.");
}
///
/// Dispose
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Dispose
///
/// Disposing
protected virtual void Dispose(bool disposing)
{
}
///
/// Add line
///
public void AddLine(
Vector3 startPoint, Color startColor,
Vector3 endPoint, Color endColor)
{
// Don't add new lines if limit is reached
if (numOfLines >= MaxNumOfLines)
{
/*ignore
Log.Write("Too many lines requested in LineManager3D. " +
"Max lines = " + MaxNumOfLines);
*/
return;
}
// Build line
Line line = new Line(startPoint, startColor, endPoint, endColor);
// Check if this exact line exists at the current lines position.
if (lines.Count > numOfLines)
{
if ((Line)lines[numOfLines] != line)
{
// overwrite old line, otherwise just increase numOfLines
lines[numOfLines] = line;
// Remember to build vertex buffer in Render()
buildVertexBuffer = true;
}
}
else
{
// Then just add new line
lines.Add(line);
// Remember to build vertex buffer in Render()
buildVertexBuffer = true;
}
// nextUpValue line
numOfLines++;
}
///
/// Add line (only 1 color for start and end version)
///
public void AddLine(Vector3 startPoint, Vector3 endPoint,
Color color)
{
AddLine(startPoint, color, endPoint, color);
}
protected void UpdateVertexBuffer()
{
// Don't do anything if we got no lines.
if (numOfLines == 0 ||
// Or if some data is invalid
lines.Count < numOfLines)
{
numOfPrimitives = 0;
return;
}
// Set all lines
for (int lineNum = 0; lineNum < numOfLines; lineNum++)
{
Line line = (Line)lines[lineNum];
lineVertices[lineNum * 2 + 0] = new VertexPositionColor(
line.startPoint, line.startColor);
lineVertices[lineNum * 2 + 1] = new VertexPositionColor(
line.endPoint, line.endColor);
}
numOfPrimitives = numOfLines;
// Vertex buffer was build
buildVertexBuffer = false;
}
///
/// Render all lines added this frame
///
public void Render()
{
// Need to build vertex buffer?
if (buildVertexBuffer ||
numOfPrimitives != numOfLines)
{
UpdateVertexBuffer();
}
// Render lines if we got any lines to render
if (numOfPrimitives > 0)
{
BaseGame.WorldMatrix = Matrix.Identity;
ShaderEffect.lineRendering.Render(
"LineRendering3D",
delegate
{
BaseGame.SetAlphaBlendingEnabled(true);
BaseGame.Device.DrawUserPrimitives(
PrimitiveType.LineList, lineVertices, 0, numOfPrimitives);
});
}
// Ok, finally reset numOfLines for next frame
numOfLines = 0;
}
}
}