I'm trying to build a framework off of XNA that lets me render 2D texture mapped polygons. SpriteBatch is very fast and optimized but it only lets me draw quads. I have tried using Graphics Device to render the polygons, and then after, render all the quads, but it is giving me anomalies. The first picture above shows a really simple scene with a grass texture mapped onto a polygon. Everything renders perfectly. However, if I throw a SpriteBatch Begin and End call sequence before or after drawing the polygon I get results like this. Note: the lines are being drawn by spritebatch as they should, but the texture coordinates seem to be forced into quads.
Here's the early and unfinished code I've written so far.
public class TexturedPolygon : gxtIDraw, IDisposable
{
private bool visible;
public bool Visible { get { return visible; } set { visible = value; } }
private float depth;
public float Depth { get { return depth; } set { depth = value; } }
private Color colorOverlay;
public Color ColorOverlay { get { return colorOverlay; } set { SetColor(value); } }
private gxtPolygon localPolygon;
private gxtPolygon worldPolygon;
public gxtPolygon Polygon { get { return worldPolygon; } set { worldPolygon = value; } }
private float rotation;
public float Rotation { get { return rotation; } set { SetRotation(value); } }
private Vector2 position;
public Vector2 Position { get { return position; } set { SetPosition(value); } }
private IndexBuffer indexBuffer;
private VertexBuffer vertexBuffer;
BasicEffect effect;
private Texture2D texture;
public Texture2D Texture { get { return texture; } set { texture = value; effect.Texture = value; } }
public bool TextureEnabled { get { return effect.TextureEnabled; } set { effect.TextureEnabled = value; } }
int[] indicesArray;
VertexPositionColorTexture[] verts;
public TexturedPolygon(gxtPolygon polygon, GraphicsDevice graphics)
{
visible = true;
depth = 0.0f;
colorOverlay = Color.White;
rotation = 0.0f;
position = Vector2.Zero;
indexBuffer = new IndexBuffer(graphics, typeof(int), 3 + ((polygon.NumVertices - 3) * 3), BufferUsage.WriteOnly);
vertexBuffer = new VertexBuffer(graphics, typeof(VertexPositionColorTexture), polygon.NumVertices, BufferUsage.WriteOnly);
effect = new BasicEffect(graphics);
effect.LightingEnabled = false;
effect.VertexColorEnabled = false; // important
effect.View = Matrix.CreateLookAt(new Vector3(0, 0, -1.0f), new Vector3(0.0f, 0.0f, 0.0f), -Vector3.Up);
effect.Projection = Matrix.CreateOrthographic(800.0f, 600.0f, 0.1f, 1.5f);
SetupPolygon(polygon);
Triangulate(polygon.v);
SetPosition(position);
SetRotation(rotation);
}
private void SetupPolygon(gxtPolygon polygon)
{
worldPolygon = polygon;
// calc local poly and AABB using centroid translation
Vector2 centroid = polygon.GetCentroid();
Vector2[] localVertices = new Vector2[polygon.NumVertices];
for (int i = 0; i < localVertices.Length; i++)
{
localVertices = polygon.v - centroid;
}
localPolygon = new gxtPolygon(localVertices);
//localAABB = gxtGeometry.ComputeAABB(localVertices);
// applies existing position and rotation characteristics to the
// world polygon
Matrix rotationMatrix;
Matrix.CreateRotationZ(rotation, out rotationMatrix);
for (int i = 0; i < polygon.NumVertices; i++)
{
worldPolygon.v = Vector2.Transform(localPolygon.v, rotationMatrix) + position;
}
effect.World = Matrix.CreateWorld(new Vector3(position.X, position.Y, 0.0f), -Vector3.UnitZ, Vector3.Up);
}
private void Triangulate(Vector2[] vertices)
{
// setup vertex buffer
verts = new VertexPositionColorTexture[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
verts = new VertexPositionColorTexture(new Vector3(vertices.X, vertices.Y, 0.0f), colorOverlay, new Vector2(0.0f, 0.0f)); // coords will be figured out later
}
vertexBuffer.SetData<VertexPositionColorTexture>(verts);
// setup index buffer, uses proper triangulation
List<int> indices = new List<int>(3 + ((vertices.Length - 3) * 3));
for (int i = 1, j = 2; j < vertices.Length; i = j, j++)
{
indices.Add(0);
indices.Add(i);
indices.Add(j);
}
indicesArray = indices.ToArray();
indexBuffer.SetData<int>(indicesArray);
}
public void SetRotation(float rad)
{
if (rotation == rad) return;
rotation = rad;
Matrix rotMat;
Matrix.CreateRotationZ(rotation, out rotMat);
for (int i = 0; i < worldPolygon.NumVertices; i++)
{
worldPolygon.v = Vector2.Transform(localPolygon.v, rotMat) + position;
}
}
public void Translate(Vector2 t)
{
if (t == Vector2.Zero) return;
position += t;
worldPolygon.Translate(t);
// effect.world must be the centroid
effect.World = Matrix.CreateWorld(new Vector3(position.X, position.Y, 0.0f), -Vector3.UnitZ, Vector3.Up);
}
public void SetColor(Color color)
{
if (color == colorOverlay)
return;
colorOverlay = color;
for (int i = 0; i < verts.Length; i++)
{
verts.Color = colorOverlay;
}
vertexBuffer.SetData<VertexPositionColorTexture>(verts);
}
public void SetPosition(Vector2 pos)
{
if (position == pos) return;
Vector2 prevPos = position;
Vector2 trans = pos - prevPos;
Translate(trans);
}
public void CalculateUVCoords()
{
Vector2 topLeft = new Vector2(-texture.Width * 0.5f, -texture.Height * 0.5f);
Vector2 oneOverSizeVector = new Vector2(1.0f / texture.Width, 1.0f / texture.Height);
for (int i = 0; i < verts.Length; i++)
{
verts.TextureCoordinate = Vector2.Multiply(localPolygon.v - topLeft, oneOverSizeVector);
}
vertexBuffer.SetData<VertexPositionColorTexture>(verts);
}
public void Draw(GraphicsDevice graphics)
{
graphics.RasterizerState = RasterizerState.CullNone;
graphics.SetVertexBuffer(vertexBuffer);
graphics.Indices = indexBuffer;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexBuffer.VertexCount, 0, indicesArray.Length / 3);
}
}
public void Dispose()
{
texture.Dispose();
effect.Dispose();
vertexBuffer.Dispose();
indexBuffer.Dispose();
}
So, my major questions are as follows:
1) Can I use graphics device and spritebatch together?
2) How might I fix this problem if I can? Am I missing a clear or reset call or something?
3) I like to use front-to-back depth based rendering, as is enabled in spritebatch. How might I accomplish this so render depths are correct for everything?