#region File Description
//-----------------------------------------------------------------------------
// GameBillboard.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using RobotGameData.Render;
using RobotGameData.Helper;
using RobotGameData.Resource;
using RobotGameData.Collision;
using RobotGameData.GameInterface;
#endregion
namespace RobotGameData.GameObject
{
#region BillboardObject
public class BillboardObject : INamed
{
[Flags]
public enum UpdateTypes
{
None = 0x00000000,
Position = 0x00000001,
Texturecoord = 0x00000002,
Color = 0x00000004,
Enable = 0x0000008,
Axis = 0x00000010,
}
String name = String.Empty;
uint updateFlag = 0;
bool enable = true;
Vector3 start = Vector3.Zero;
Vector3 end = Vector3.Zero;
float height = 1.0f;
Color color = new Color(255, 255, 255, 255);
Vector2[] uv = new Vector2[2]
{
Vector2.Zero,
Vector2.One,
};
public string Name
{
get { return name; }
set { name = value; }
}
public bool Enable
{
get { return enable; }
set { enable = value; }
}
public Vector3 Start
{
get { return start; }
set { start = value; }
}
public Vector3 End
{
get { return end; }
set { end = value; }
}
public float Size
{
get { return height; }
set { height = value; }
}
public Color Color
{
get { return color; }
set { color = value; }
}
public void AddUpdateType(UpdateTypes billboardUpdate)
{
updateFlag |= (uint)billboardUpdate;
}
public void RemoveUpdateType(UpdateTypes billboardUpdate)
{
updateFlag &= ~(uint)billboardUpdate;
}
public Vector2 MinUV
{
get { return this.uv[0]; }
set { this.uv[0] = value; }
}
public Vector2 MaxUV
{
get { return this.uv[1]; }
set { this.uv[1] = value; }
}
}
#endregion
///
/// this sprite always looks at the view matrix in the 3D world.
/// It has a begin point and an end point in the 3D world.
///
public class GameBillboard : GameMesh
{
#region Fields
const int vertexStride = 4;
const int indexStride = 6;
List billboardList = new List();
RenderingSpace renderingSpace = RenderingSpace.World;
Matrix lastViewMatrix;
int objectCount = 0;
bool needToUpdate = false;
bool alwaysUpdate = false;
#endregion
#region Properties
public int ObjectCount
{
get { return objectCount; }
}
public RenderingSpace RenderingSpace
{
get { return renderingSpace; }
}
public static int VertexStride
{
get { return vertexStride; }
}
public static int IndexStride
{
get { return indexStride; }
}
#endregion
///
/// updates the vertex data and draws
/// It always looks at the view matrix at the center between
/// the begin point and the end point of the billboard.
///
/// render information
protected override void OnDraw(RenderTracer renderTracer)
{
int count = 0;
for (int i = 0; i < objectCount; i++)
{
if (billboardList[i].Enable == false) continue;
count++;
}
if (count == 0) return;
PrimitiveCount = count * 2;
UpdateVertexCount = objectCount * vertexStride;
// needs to update?
if (renderTracer.View != lastViewMatrix)
{
needToUpdate = true;
this.lastViewMatrix = renderTracer.View;
}
if (alwaysUpdate || needToUpdate)
{
int vertexOffset = 0;
int indexOffset = 0;
// calculates inverse view matrix.
Matrix billboardMatrix = this.TransformedMatrix * renderTracer.View;
billboardMatrix = Helper3D.Transpose(billboardMatrix);
// gets inverse view direction.
Vector3 invertViewAt = billboardMatrix.Forward;
invertViewAt.Normalize();
for (int i = 0; i < objectCount; i++)
{
BillboardObject obj = billboardList[i];
if (obj.Enable == false) continue;
Vector3 vec = Vector3.Zero;
Vector3 dir = Vector3.Zero;
dir = obj.End - obj.Start;
dir.Normalize();
Vector3.Cross(ref dir, ref invertViewAt, out vec);
vec.Normalize();
// updates vertex positions.
SetBufferPosition(ref vertexData, obj, vertexOffset, vec);
// updates texture coordinates.
SetBufferTextureCoord(ref vertexData, obj, vertexOffset,
renderingSpace);
// updates vertex colors.
SetBufferColor(ref vertexData, obj, vertexOffset);
indexData[indexOffset + 0] = (short)(vertexOffset + 0);
indexData[indexOffset + 1] = (short)(vertexOffset + 2);
indexData[indexOffset + 2] = (short)(vertexOffset + 1);
indexData[indexOffset + 3] = (short)(vertexOffset + 1);
indexData[indexOffset + 4] = (short)(vertexOffset + 2);
indexData[indexOffset + 5] = (short)(vertexOffset + 3);
vertexOffset += vertexStride;
indexOffset += indexStride;
}
if (userPrimitive == false)
{
// binds the vertex buffer.
BindVertexBuffer();
// binds the index buffer.
BindIndexBuffer();
}
if (needToUpdate)
needToUpdate = false;
}
// draws mesh
base.OnDraw(renderTracer);
}
///
/// create billboard objects using the texture.
///
/// billboard object count
/// texture file name
/// 3D render space
///
public void Create(int count, string fileName, RenderingSpace renderingSpace,
bool alwaysUpdate)
{
// load a texture.
GameResourceTexture2D resource =
FrameworkCore.ResourceManager.LoadTexture(fileName);
Create(count, resource.Texture2D, renderingSpace, alwaysUpdate);
}
///
/// create billboard objects using the texture.
///
/// billboard object count
/// texture resource
/// 3D render space
///
public void Create(int count, Texture2D texture, RenderingSpace renderingSpace,
bool alwaysUpdate)
{
this.objectCount = count;
this.renderingSpace = renderingSpace;
this.alwaysUpdate = alwaysUpdate;
// create billboard objects.
for (int i = 0; i < count; i++)
{
BillboardObject obj = new BillboardObject();
obj.AddUpdateType(BillboardObject.UpdateTypes.Enable);
billboardList.Add(obj);
}
base.Create(count * vertexStride, count * indexStride, texture);
}
protected override void UnloadContent()
{
billboardList.Clear();
base.UnloadContent();
}
///
/// enables/disables all billboard objects.
///
public void SetUpdateType(bool billboardUpdate)
{
for (int i = 0; i < billboardList.Count; i++)
SetUpdateType(i, billboardUpdate);
}
///
/// enables/disables an individual billboard object.
///
public void SetUpdateType(int index, bool billboardUpdate)
{
if (billboardList.Count <= index || 0 > index)
throw new ArgumentException("Invalid index.");
if (billboardList[index].Enable != billboardUpdate)
{
billboardList[index].Enable = billboardUpdate;
needToUpdate = true;
}
}
///
/// configures the begin position of each billboard object.
///
/// an index of billboard object
/// begin position of the billboard object
public void SetStart(int index, Vector3 position)
{
if (billboardList.Count <= index || 0 > index)
throw new ArgumentException("Invalid index.");
billboardList[index].Start = position;
billboardList[index].AddUpdateType(
BillboardObject.UpdateTypes.Position);
needToUpdate = true;
}
///
/// configures the begin position of each billboard object.
///
/// an index of billboard object
/// x-component of begin position of the billboard object
/// y-component of begin position of the billboard object
/// z-component of begin position of the billboard object
public void SetStart(int index, float x, float y, float z)
{
SetStart(index, x, y, z);
}
///
/// configures the end position of each billboard object.
///
/// an index of billboard object
/// end position of the billboard object
public void SetEnd(int index, Vector3 position)
{
if (billboardList.Count <= index || 0 > index)
throw new ArgumentException("Invalid index.");
billboardList[index].End = position;
billboardList[index].AddUpdateType(
BillboardObject.UpdateTypes.Position);
needToUpdate = true;
}
///
/// configures the end position of each billboard object.
///
/// an index of billboard object
/// x-component of end position of the billboard object
/// y-component of end position of the billboard object
/// z-component of end position of the billboard object
public void SetEnd(int index, float x, float y, float z)
{
SetEnd(index, x, y, z);
}
///
/// configures the size of each billboard object.
///
/// an index of billboard object
/// the height of billboard object
public void SetSize(int index, float height)
{
if (billboardList.Count <= index || 0 > index)
throw new ArgumentException("Invalid index.");
billboardList[index].Size = height;
billboardList[index].AddUpdateType(
BillboardObject.UpdateTypes.Position);
needToUpdate = true;
}
///
/// configures the texture coordinates of each billboard object.
///
/// an index of billboard object
/// texture coordinates of minimum
/// texture coordinates of maximum
public void SetTextureCoord(int index, Vector2 min, Vector2 max)
{
if (billboardList.Count <= index || 0 > index)
throw new ArgumentException("Invalid index.");
billboardList[index].MinUV = min;
billboardList[index].MaxUV = max;
billboardList[index].AddUpdateType(
BillboardObject.UpdateTypes.Texturecoord);
needToUpdate = true;
}
///
/// configures the texture coordinates of each billboard object.
///
/// an index of billboard object
/// texture "u1" coordinate
/// texture "v1" coordinate
/// texture "u2" coordinate
/// texture "v2" coordinate
public void SetTextureCoord(int index, float u1, float v1, float u2, float v2)
{
SetTextureCoord(index, new Vector2(u1, v1), new Vector2(u2, v2));
}
///
/// configures the vertex color of each billboard object.
///
/// an index of billboard object
/// color
public void SetColor(int index, Color color)
{
if (billboardList.Count <= index || 0 > index)
throw new ArgumentException("Invalid index.");
billboardList[index].Color = color;
billboardList[index].AddUpdateType(
BillboardObject.UpdateTypes.Color);
needToUpdate = true;
}
///
/// configures the vertex color of each billboard object.
///
/// an index of billboard object
/// a red component of color
/// a green component of color
/// a blue component of color
/// an alpha component of color
public void SetColor(int index, byte r, byte g, byte b, byte a)
{
SetColor(index, new Color(r, g, b, a));
}
///
/// configures a position vector to the vertex component data
/// using the billboard object.
///
/// target vertex component data
/// source billboard object
/// start index of the vertex component data
/// position vector
private static void SetBufferPosition(
ref VertexPositionColorTexture[] vertexData, BillboardObject obj,
int startIndex, Vector3 ay)
{
Vector3 by = Vector3.Zero;
float cy = obj.Size * 0.5f;
by = ay * cy;
// [0] [1]
// +-------------+
// | |
// +c |
// | |
// +-------------+
// [2] [3]
// 0 2 1 - 1 2 3
vertexData[startIndex + 0].Position = obj.Start - by;
vertexData[startIndex + 1].Position = obj.End - by;
vertexData[startIndex + 2].Position = obj.Start + by;
vertexData[startIndex + 3].Position = obj.End + by;
}
///
/// configures texture coordinates to the vertex component data
/// using the billboard object.
///
/// target vertex component data
/// source billboard object
/// start index of the vertex component data
/// 3D render space
private static void SetBufferTextureCoord(
ref VertexPositionColorTexture[] vertexData, BillboardObject obj,
int startIndex, RenderingSpace renderingSpace)
{
float u1 = 0.0f, v1 = 0.0f, u2 = 0.0f, v2 = 0.0f;
u1 = obj.MinUV.X;
v1 = obj.MinUV.Y;
u2 = obj.MaxUV.X;
v2 = obj.MaxUV.Y;
if (renderingSpace == RenderingSpace.Screen)
{
float swap = v1;
v1 = v2;
v2 = swap;
}
vertexData[startIndex + 0].TextureCoordinate = new Vector2(u1, v2);
vertexData[startIndex + 1].TextureCoordinate = new Vector2(u1, v1);
vertexData[startIndex + 2].TextureCoordinate = new Vector2(u2, v2);
vertexData[startIndex + 3].TextureCoordinate = new Vector2(u2, v1);
}
///
/// configures vertex color to the vertex component data
/// using the billboard object.
///
/// target vertex component data
/// source billboard object
/// start index of the vertex component data
private static void SetBufferColor(ref VertexPositionColorTexture[] vertexData,
BillboardObject obj, int startIndex)
{
for (int i = 0; i < 4; i++)
vertexData[startIndex + i].Color = obj.Color;
}
}
}