Hi.
We’ve been through some difficulties on rendering from a Tilemap, I’ve tried using TriangleStrip instead of TriangleList but the outcome is always a shattered render result.
The previous implementation of this code used OpenGL and managed to draw the vertices using PrimitiveType.Quads
public sealed class TileRenderer : ITileRenderer
{
private const int MaxTextures = 4;
/*private static readonly Dictionary<Renderer, TileRenderer> RendererDictionary =
new Dictionary<Renderer, TileRenderer>();*/
public static Colour ShadowColour;
private readonly IGraphicsContext _graphicsContext;
private TileBlendMode _tempLastBlendMode;
//private readonly List<Vertex> _vertices = new List<Vertex>();
private const int VerticesArrayLength = 256;
private static Vertex[] _vertices = new Vertex[VerticesArrayLength];
public int NumVertices;
private static readonly Vector2[] VertexPositions = new Vector2[4];
private static readonly Vector2[] VertexUVs = new Vector2[4];
private Effect _effect;
public TileRenderer(ContentManager content, IGraphicsContext graphicsContext)
{
_graphicsContext = graphicsContext;
_effect = content.Load<Effect>("shaders/tile");
// Set defaults
ModelMatrix = Matrix4.Identity;
Colour = Colours.White;
ClipRectangle = new Rectangle(0, 0, 1920, 1080);
}
public Colour Colour { get; set; }
// Input options
public Matrix4 ModelMatrix { get; set; }
public ITexture[] Textures { get; set; }
public Rectangle ClipRectangle
{
get;
set;
}
public int Filter { get; set; }
public double FilterAmount { get; set; }
public bool Rendering { get; private set; }
public int NumTiles { get; private set; }
public void Dispose()
{
}
public void Deactivate() { }
public void BeginRender()
{
if(Rendering)
throw new InvalidOperationException("Already renderering.");
Rendering = true;
NumTiles = 0;
NumVertices = 0;
}
public void AddTile(
Rectanglei source,
Rectanglei destination,
int textureIndex,
bool flipX = false,
bool flipY = false,
float opacity = 1.0f,
TileBlendMode blend = TileBlendMode.Alpha
)
{
if(!Rendering)
throw new Exception("Not currently renderering");
var texture = Textures[textureIndex];
// Add the vertices
// Renderer.GetVertices(destination, VertexPositions);
Array.Copy(destination.Vertices, 0, VertexPositions, 0, 4);
Renderer.GetTextureMappings(texture, source, VertexUVs, flipX, flipY);
for(var i = 0; i < 4; i++)
{
_vertices[NumVertices].VertexPosition.X = (float) VertexPositions[i].X;
_vertices[NumVertices].VertexPosition.Y = (float) VertexPositions[i].Y;
_vertices[NumVertices].VertexTextureMapping.X = (float) VertexUVs[i].X;
_vertices[NumVertices].VertexTextureMapping.Y = (float) VertexUVs[i].Y;
_vertices[NumVertices].VertexTextureIndex = textureIndex;
_vertices[NumVertices].VertexOpacity = opacity;
NumVertices++;
if(NumVertices >= _vertices.Length)
{
var tempVertex = new Vertex[_vertices.Length + VerticesArrayLength];
Array.Copy(_vertices, tempVertex, _vertices.Length);
_vertices = tempVertex;
}
}
NumTiles++;
_tempLastBlendMode = blend;
}
public void SetUpEffect()
{
var graphicsContext = _graphicsContext;
var graphics = (graphicsContext as MonoGameGraphicsContext);
var GraphicsDevice = graphics.WindowContext.Graphics.GraphicsDevice;
var viewport = GraphicsDevice.Viewport;
//var World = SimpleRenderer._modelMatrix;
var ortho = ModelMatrix;
var World = new Microsoft.Xna.Framework.Matrix(
new Microsoft.Xna.Framework.Vector4((float)ortho.Row1.X, (float)ortho.Row1.Y, (float)ortho.Row1.Z, (float)ortho.Row1.W),
new Microsoft.Xna.Framework.Vector4((float)ortho.Row2.X, (float)ortho.Row2.Y, (float)ortho.Row2.Z, (float)ortho.Row2.W),
new Microsoft.Xna.Framework.Vector4((float)ortho.Row3.X, (float)ortho.Row3.Y, (float)ortho.Row3.Z, (float)ortho.Row3.W),
new Microsoft.Xna.Framework.Vector4((float)ortho.Row4.X, (float)ortho.Row4.Y, (float)ortho.Row4.Z, (float)ortho.Row4.W));
var View = Microsoft.Xna.Framework.Matrix.CreateLookAt(new Microsoft.Xna.Framework.Vector3(0, 0, -1), new Microsoft.Xna.Framework.Vector3(0, 0, 0), new Microsoft.Xna.Framework.Vector3(0, -1, 0));
//var Projection2 = Microsoft.Xna.Framework.Matrix.CreateScale(1, -1, 1) * Microsoft.Xna.Framework.Matrix.CreateOrthographicOffCenter(0, 1920, 1080, 0, 0, 1); // nans
ortho = _graphicsContext.CurrentFrameBuffer.CreateOrthographic() * Matrix4.CreateScale(1, -1, 1);
var Projection = new Microsoft.Xna.Framework.Matrix(
new Microsoft.Xna.Framework.Vector4((float)ortho.Row1.X, (float)ortho.Row1.Y, (float)ortho.Row1.Z, (float)ortho.Row1.W),
new Microsoft.Xna.Framework.Vector4((float)ortho.Row2.X, (float)ortho.Row2.Y, (float)ortho.Row2.Z, (float)ortho.Row2.W),
new Microsoft.Xna.Framework.Vector4((float)ortho.Row3.X, (float)ortho.Row3.Y, (float)ortho.Row3.Z, (float)ortho.Row3.W),
new Microsoft.Xna.Framework.Vector4((float)ortho.Row4.X, (float)ortho.Row4.Y, (float)ortho.Row4.Z, (float)ortho.Row4.W));
var wvp = World * View * Projection;
_effect.Parameters["World"].SetValue(World);
_effect.Parameters["View"].SetValue(View);
_effect.Parameters["Projection"].SetValue(Projection);
for (var i = 0; i < Textures.Length; i++)
{
var texture = (Textures[i] as MonoGameTexture).Texture;
_effect.Parameters["InputTexture" + i.ToString()].SetValue(texture);
}
_effect.Parameters["AlphaGrayScale"].SetValue(ShadowRenderer.IsShadowing ? 1.0f : 0.0f);
_effect.Parameters["InputColour"].SetValue(new Microsoft.Xna.Framework.Vector4(Colour.Red / 255.0f, Colour.Green / 255.0f, Colour.Blue / 255.0f, Colour.Alpha / 255.0f));
_effect.Parameters["ShadowColour"].SetValue(new Microsoft.Xna.Framework.Vector4(ShadowColour.Red / 255.0f, ShadowColour.Green / 255.0f, ShadowColour.Blue / 255.0f, ShadowColour.Alpha / 255.0f));
_effect.Parameters["InputFilter"].SetValue(Filter);
_effect.Parameters["InputFilterAmount"].SetValue((float)FilterAmount);
_effect.Parameters["InputClipRectangle"].SetValue(new Microsoft.Xna.Framework.Vector4((float)ClipRectangle.X, (float)ClipRectangle.Y, (float)ClipRectangle.Right, (float)ClipRectangle.Bottom));
//_effect.Parameters["FragmentTextureIndex"].SetValue(_vertices[0].VertexTextureIndex);
//_effect.Parameters["FragmentOpacity"].SetValue(_vertices[0].VertexOpacity);
_effect.Parameters["InputFilter"].SetValue(Filter);
}
private void DrawQuad(Vertex[] destination)
{
var graphicsContext = _graphicsContext;
var graphics = (graphicsContext as MonoGameGraphicsContext);
var GraphicsDevice = graphics.WindowContext.Graphics.GraphicsDevice;
Vertex[] quad = destination;
short[] indices = new short[(int)(destination.Length * 1.5)];
for(int i = 0; i<indices.Length; i += 6)
{
if (GraphicsDevice.RasterizerState == RasterizerState.CullClockwise)
{
indices[i+0] = (short)(i + 0);
indices[i+1] = (short)(i + 1);
indices[i+2] = (short)(i + 2);
indices[i+3] = (short)(i + 2);
indices[i+4] = (short)(i + 3);
indices[i+5] = (short)(i + 0);
}
else
{
indices[i + 0] = (short)(i + 0);
indices[i + 1] = (short)(i + 2);
indices[i + 2] = (short)(i + 1);
indices[i + 3] = (short)(i + 2);
indices[i + 4] = (short)(i + 0);
indices[i + 5] = (short)(i + 3);
}
}
foreach (EffectPass pass in _effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(null);
GraphicsDevice.DrawUserIndexedPrimitives(Microsoft.Xna.Framework.Graphics.PrimitiveType.TriangleList, quad, 0, quad.Length, indices, 0, (indices.Length / 3), Vertex.VertexDeclaration);
}
}
public void SetStates()
{
var graphicsContext = _graphicsContext;
var graphics = (graphicsContext as MonoGameGraphicsContext);
var GraphicsDevice = graphics.WindowContext.Graphics.GraphicsDevice;
// Alpha blending
graphics.BlendMode = _tempLastBlendMode == TileBlendMode.Additive ? BlendMode.Additive : BlendMode.Alpha;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.SamplerStates[0] = SamplerState.PointWrap;
GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
}
public void EndRender()
{
if(!Rendering)
throw new Exception("Not currently renderering");
SetStates();
// Prepare textures
foreach(var texture in Textures)
texture.Filtering = TextureFiltering.NearestNeighbour;
SetUpEffect();
DrawQuad(_vertices);
Rendering = false;
}
private struct Vertex : IVertexType
{
public Microsoft.Xna.Framework.Vector3 VertexPosition;
public Microsoft.Xna.Framework.Vector2 VertexTextureMapping;
public float VertexTextureIndex;
public float VertexOpacity;
public static int currentByteSize = 0;
public static int Offset(float n) { var s = sizeof(float); currentByteSize += s; return currentByteSize - s; }
public static int Offset(Microsoft.Xna.Framework.Vector2 n) { var s = sizeof(float) * 2; currentByteSize += s; return currentByteSize - s; }
public static int Offset(Microsoft.Xna.Framework.Color n) { var s = sizeof(int); currentByteSize += s; return currentByteSize - s; }
public static int Offset(Microsoft.Xna.Framework.Vector3 n) { var s = sizeof(float) * 3; currentByteSize += s; return currentByteSize - s; }
public static int Offset(Microsoft.Xna.Framework.Vector4 n) { var s = sizeof(float) * 4; currentByteSize += s; return currentByteSize - s; }
public readonly static VertexDeclaration VertexDeclaration =
new VertexDeclaration(new VertexElement(Offset(Microsoft.Xna.Framework.Vector3.Zero), VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(Offset(Microsoft.Xna.Framework.Vector2.Zero), VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
new VertexElement(Offset(0.0f), VertexElementFormat.Single, VertexElementUsage.TextureCoordinate, 1),
new VertexElement(Offset(0.0f), VertexElementFormat.Single, VertexElementUsage.TextureCoordinate, 2));
VertexDeclaration IVertexType.VertexDeclaration
{
get { return Vertex.VertexDeclaration; }
}
}
}
The relevant code portion is the following:
short[] indices = new short[(int)(destination.Length * 1.5)];
for(int i = 0; i<indices.Length; i += 6)
{
if (GraphicsDevice.RasterizerState == RasterizerState.CullClockwise)
{
indices[i+0] = (short)(i + 0);
indices[i+1] = (short)(i + 1);
indices[i+2] = (short)(i + 2);
indices[i+3] = (short)(i + 2);
indices[i+4] = (short)(i + 3);
indices[i+5] = (short)(i + 0);
}
else
{
indices[i + 0] = (short)(i + 0);
indices[i + 1] = (short)(i + 2);
indices[i + 2] = (short)(i + 1);
indices[i + 3] = (short)(i + 2);
indices[i + 4] = (short)(i + 0);
indices[i + 5] = (short)(i + 3);
}
}
It’s based on the idea that every quad has a size of 4 vertices and a TriangleList should contain 6 indices, however, I’m fairly defeated by the implementation, the entrance of vertices seem to be always 4096, how can we sort the indices correctly?
Thanks!