Thanks for the quick answer.
I want to use the DrawUserIndexPrimitives , but i don’t know how it works…
Do you have any examples ?
i have some quad batchers i wrote, a few of them that i was tinkering with, they aren’t any were near as performant as spritebatch.
I have a small one.
It should be enough to get you started The game code actually is for other classes built on this, that are far to big to post.
You should be able to see how to work it into a game1 though its a lot like spritebatch. If you cant get it let me know and i will write one out.
look to the Overloaded full Method… SetSpriteToBatch(…) within.
It allows you to send in a quad by vertice edges.
This has limitations i never picked it back up and worked on it, but i suppose its a good starting point.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace TempThrowAwayEx
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D dotTexture;
DirectDrawSpriteBatcher Dd;
BasicEffect basicEffect;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
dotTexture = TextureDotCreate(GraphicsDevice);
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.Texture = dotTexture;
basicEffect.TextureEnabled = true;
Dd = new DirectDrawSpriteBatcher(GraphicsDevice, basicEffect);
Dd.OnResizeUpdateWindowWidthHeight(GraphicsDevice);
}
public static Texture2D TextureDotCreate(GraphicsDevice device)
{
Color[] data = new Color[1];
data[0] = new Color(255, 255, 255, 255);
Texture2D tex = new Texture2D(device, 1, 1);
tex.SetData<Color>(data);
return tex;
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
// TODO: Add your drawing code here
Dd.Begin(GraphicsDevice);
Rectangle dr = new Rectangle(100, 100, 200, 200);
Vector2 center = new Vector2(dr.Center.X, dr.Center.Y);
Vector2 wh = new Vector2(center.X, center.Y);
Vector2 half = wh * .5f;
float skewAmount = .8f;
Vector2 skhalf = half * skewAmount;
Vector2 lt = new Vector2(center.X - skhalf.X, center.Y - half.Y);
Vector2 rt = new Vector2(center.X + skhalf.X, center.Y - half.Y);
Vector2 rb = new Vector2(center.X + half.X, center.Y + half.Y);
Vector2 lb = new Vector2(center.X - half.X, center.Y + half.Y);
Vector2 texture_lt = new Vector2(0f, 0f);
Vector2 texture_rt = new Vector2(1f, 0f);
Vector2 texture_rb = new Vector2(1f, 1f);
Vector2 texture_lb = new Vector2(0f, 1f);
Dd.SetScreenSpriteToBatch(dotTexture, lt, lb, rt, rb, texture_lt, texture_lb, texture_rt, texture_rb, Color.Red, 0f, Vector2.Zero, 0f);
Dd.End();
base.Draw(gameTime);
}
}
public class DirectDrawSpriteBatcher
{
// local copy of the device
GraphicsDevice device;
//
// buffer vars
public int quadCapacity;
public int currentQuads;
public int ti_pointer;
public int vi_pointer;
public int[] triangleIndexList; // = new int[6];
public VertexPositionColorTexture[] spriteVertices; // = new VertexPositionColorTexture[4];
//
// basic vars
public Effect currentEffect;
private Texture2D currentTexture;
private Texture2D dottexture;
private int windowWidth = 0;
private int windowHeight = 0;
private float cw = 800;
private float ch = 600;
private int InitialQuadCapacity = 256;
public const int MaxQuadCapacity = ushort.MaxValue / 6;
public DirectDrawSpriteBatcher(GraphicsDevice device, Effect effectToUse)
{
this.device = device;
windowWidth = device.Viewport.Width;
windowHeight = device.Viewport.Height;
cw = 2f / windowWidth;
ch = 2f / windowHeight;
InitialQuadCapacity = Math.Min(InitialQuadCapacity, MaxQuadCapacity);
spriteVertices = new VertexPositionColorTexture[InitialQuadCapacity * 4];
triangleIndexList = new int[InitialQuadCapacity * 6];
currentEffect = effectToUse;
CreateDotTexture(device);
}
public void SetSpriteToBatch
(
Texture2D texture,
Rectangle destinationPositionRectangle,
Rectangle sourceTextureRectangle,
Color color,
float rotation,
Vector2 scale,
float depth
)
{
var _LT = new Vector2(destinationPositionRectangle.Left, destinationPositionRectangle.Top);
var _LB = new Vector2(destinationPositionRectangle.Left, destinationPositionRectangle.Bottom);
var _RT = new Vector2(destinationPositionRectangle.Right, destinationPositionRectangle.Top);
var _RB = new Vector2(destinationPositionRectangle.Right, destinationPositionRectangle.Bottom);
var u = 1f / texture.Width;
var v = 1f / texture.Height;
var uvL = (float)sourceTextureRectangle.Left * u;
var uvR = (float)sourceTextureRectangle.Right * u;
var uvT = (float)sourceTextureRectangle.Top * v;
var uvB = (float)sourceTextureRectangle.Bottom * v;
var uv0 = new Vector2(uvL, uvT);
var uv1 = new Vector2(uvL, uvB);
var uv2 = new Vector2(uvR, uvT);
var uv3 = new Vector2(uvR, uvB);
//var origin = (_LT + _LB + _RT + _RB) * .25f;
SetSpriteToBatch(texture, _LT, _LB, _RT, _RB, uv0, uv1, uv2, uv3, color, rotation, _LT, scale, depth);
}
public void SetSpriteToBatch
(
Texture2D texture,
Rectangle destinationPositionRectangle,
Rectangle sourceTextureRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
float depth
)
{
var _LT = new Vector2(destinationPositionRectangle.Left, destinationPositionRectangle.Top);
var _LB = new Vector2(destinationPositionRectangle.Left, destinationPositionRectangle.Bottom);
var _RT = new Vector2(destinationPositionRectangle.Right, destinationPositionRectangle.Top);
var _RB = new Vector2(destinationPositionRectangle.Right, destinationPositionRectangle.Bottom);
var u = 1f / texture.Width;
var v = 1f / texture.Height;
var uvL = (float)sourceTextureRectangle.Left * u;
var uvR = (float)sourceTextureRectangle.Right * u;
var uvT = (float)sourceTextureRectangle.Top * v;
var uvB = (float)sourceTextureRectangle.Bottom * v;
var uv0 = new Vector2(uvL, uvT);
var uv1 = new Vector2(uvL, uvB);
var uv2 = new Vector2(uvR, uvT);
var uv3 = new Vector2(uvR, uvB);
SetSpriteToBatch(texture, _LT, _LB, _RT, _RB, uv0, uv1, uv2, uv3, color, rotation, origin, scale, depth);
}
public void SetScreenSpriteToBatch(
Texture2D texture,
Vector2 _LT, Vector2 _LB, Vector2 _RT, Vector2 _RB,
Vector2 uvLT, Vector2 uvLB, Vector2 uvRT, Vector2 uvRB,
Color color, float rotation, Vector2 origin, float depth
)
{
SetSpriteToBatch(texture, _LT, _LB, _RT, _RB, uvLT, uvLB, uvRT, uvRB, color, rotation, origin, Vector2.One, depth);
}
public void SetSpriteToBatch(
Texture2D texture,
Vector2 _LT, Vector2 _LB, Vector2 _RT, Vector2 _RB,
Vector2 uv0, Vector2 uv1, Vector2 uv2, Vector2 uv3,
Color color, float rotation, Vector2 origin, Vector2 scale, float depth
)
{
currentTexture = texture;
// If we really want to just transform from top left to bottom right like xna yuck.
// Then we can instead just set the local origin to _LT
//Vector2 origin = (_LT + _LB + _RT + _RB) * .25f;
// translate to the local origin and scale
var lt = (_LT - origin) * scale;
var lb = (_LB - origin) * scale;
var rt = (_RT - origin) * scale;
var rb = (_RB - origin) * scale;
// rotate
if (rotation != 0)
{
Vector2 q = new Vector2((float)Math.Sin(rotation), (float)Math.Cos(rotation));
lt = new Vector2(lt.X * q.Y - lt.Y * q.X, lt.X * q.X + lt.Y * q.Y);
lb = new Vector2(lb.X * q.Y - lb.Y * q.X, lb.X * q.X + lb.Y * q.Y);
rt = new Vector2(rt.X * q.Y - rt.Y * q.X, rt.X * q.X + rt.Y * q.Y);
rb = new Vector2(rb.X * q.Y - rb.Y * q.X, rb.X * q.X + rb.Y * q.Y);
}
// de-originate and project
var LT = new Vector3((lt.X + origin.X) * cw - 1f, (lt.Y + origin.Y) * -ch + 1f, depth);
var LB = new Vector3((lb.X + origin.X) * cw - 1f, (lb.Y + origin.Y) * -ch + 1f, depth);
var RT = new Vector3((rt.X + origin.X) * cw - 1f, (rt.Y + origin.Y) * -ch + 1f, depth);
var RB = new Vector3((rb.X + origin.X) * cw - 1f, (rb.Y + origin.Y) * -ch + 1f, depth);
// create the vertice quad
spriteVertices[vi_pointer + 0].Position = LT;
spriteVertices[vi_pointer + 0].Color = color;
spriteVertices[vi_pointer + 0].TextureCoordinate = uv0;
spriteVertices[vi_pointer + 1].Position = LB;
spriteVertices[vi_pointer + 1].Color = color;
spriteVertices[vi_pointer + 1].TextureCoordinate = uv1;
spriteVertices[vi_pointer + 2].Position = RT;
spriteVertices[vi_pointer + 2].Color = color;
spriteVertices[vi_pointer + 2].TextureCoordinate = uv2;
spriteVertices[vi_pointer + 3].Position = RB;
spriteVertices[vi_pointer + 3].Color = color;
spriteVertices[vi_pointer + 3].TextureCoordinate = uv3;
// create the indexs
//
// LT 0 2 RT
// | /| Triangle 1 is 0 1 2 ccw
// | / | Triangle 2 is 2 1 3 ccw
// LB 1 3 RB
triangleIndexList[ti_pointer + 0] = 0 + vi_pointer;
triangleIndexList[ti_pointer + 1] = 1 + vi_pointer;
triangleIndexList[ti_pointer + 2] = 2 + vi_pointer;
triangleIndexList[ti_pointer + 3] = 2 + vi_pointer;
triangleIndexList[ti_pointer + 4] = 1 + vi_pointer;
triangleIndexList[ti_pointer + 5] = 3 + vi_pointer;
currentQuads += 1;
vi_pointer += 4;
ti_pointer += 6;
if (currentQuads >= quadCapacity - 1)
{
IncreaseCapacity();
}
}
public void Begin()
{
NumberOfTrianglesDrawn = 0;
NumberOfVerticesDrawn = 0;
NumberOfQuadsDrawn = 0;
}
public void Begin(GraphicsDevice gd)
{
this.device = gd;
windowWidth = gd.Viewport.Width;
windowHeight = gd.Viewport.Height;
cw = 2f / windowWidth;
ch = 2f / windowHeight;
NumberOfTrianglesDrawn = 0;
NumberOfVerticesDrawn = 0;
NumberOfQuadsDrawn = 0;
}
public void DrawAll()
{
if (TriangleDrawCount() > 0)
{
foreach (EffectPass pass in currentEffect.CurrentTechnique.Passes)
{
pass.Apply();
device.DrawUserIndexedPrimitives
(
PrimitiveType.TriangleList,
spriteVertices,
0,
VerticesPerQuad(),
triangleIndexList,
0,
TriangleDrawCount()
);
}
NumberOfVerticesDrawn += (int)(TriangleDrawCount() * .5f * 4f);
NumberOfTrianglesDrawn += TriangleDrawCount();
NumberOfQuadsDrawn += (int)(TriangleDrawCount() * .5f);
}
}
public void End()
{
DrawAll();
ClearAll();
}
public void ClearAll()
{
currentQuads = 0;
vi_pointer = 0;
ti_pointer = 0;
}
private void SetCurrentTexture(Texture2D texture)
{
currentTexture = texture;
currentEffect.Parameters["TextureA"].SetValue(currentTexture);
}
public void SetCurrentEffect(Effect effectToUse)
{
currentEffect = effectToUse;
}
public void OnResizeUpdateWindowWidthHeight(GraphicsDevice gd)
{
this.device = gd;
windowWidth = gd.Viewport.Width;
windowHeight = gd.Viewport.Height;
cw = 2f / windowWidth;
ch = 2f / windowHeight;
}
private void CreateDotTexture(GraphicsDevice device)
{
dottexture = new Texture2D(device, 1, 1, false, device.Adapter.CurrentDisplayMode.Format);
Color[] color_ary = new Color[1];
color_ary[0] = new Color(255, 255, 255, 255);
dottexture.SetData<Color>(color_ary);
}
//
public int TotalQuads()
{
return currentQuads;
}
public int VerticesPerQuad()
{
return 4;
}
public int TriangleDrawCount()
{
return currentQuads * 2;
}
public int TotalVertices() { return spriteVertices.Length; }
public int TotalIndices() { return triangleIndexList.Length; }
private void IncreaseCapacity()
{
int newVerticeCapacity = spriteVertices.Length + InitialQuadCapacity * 4;
int newIndexCapacity = triangleIndexList.Length + InitialQuadCapacity * 6;
VertexPositionColorTexture[] v = new VertexPositionColorTexture[newVerticeCapacity];
int[] ind = new int[newIndexCapacity];
Array.Copy(spriteVertices, v, spriteVertices.Length);
Array.Copy(triangleIndexList, ind, triangleIndexList.Length);
spriteVertices = v;
triangleIndexList = ind;
quadCapacity += InitialQuadCapacity;
}
// metrics
public int NumberOfVerticesDrawn { get; set; }
public int NumberOfTrianglesDrawn { get; set; }
public int NumberOfQuadsDrawn { get; set; }
}
}
I forgot to tell that i was “new” in monogame and codes in general.
This code looks really advanced to me and i might need some helps to make it work with the Game1 class, and get the result i want.
Thanks in advance !
Ah sorry here i updated it.
You should probably just look for some simple triangle and square drawing examples using DrawUserIndexPrimitives im sure they are all over.
I tried to implement the skew with a custom sprite and here is what i got
It skew well but we have a big quality loss…
I found this this link where a sample can be downloaded
I multiplied by .5 the UpperLeft and UpperRight position and here is what i got
The quality is fine but there is a big sprite distortion.
Any clue on why it’s doing that and how to fix this ?
Thanks again.
You have to correct the texture coordinates here
Aint all this possible with a vertexshader? I know it allows deformations of a model/primitive so… With the right transform matrix it should not be too complicated
Totally agree with @Alkher, I needed to skew sprites for shadows in an isometric game and just wrote a shader. Ended up with a very short and clean code.
One trick to remember though - if you relay on global variables per sprite (like how much to skew it) it won’t work with batching. So you might need to do a little hack like encoding that data into the color property. Or maybe there’s a normal way to pass global vars with batching that I’m just not aware of…
Just grab this book
That links to the kindle version so you can save some money and get it immediately.
Note that you will need to work around setting up your projects and utilise the Pipeline instead of the Windows Phone 8.1/XNA dev kit…
Um im seeing the same thing using basic effect with a properly inputed quad.
Edit:
Just what in the world is going on here…
ReEdit this is 100% a bug somewere.
I see the same thing in my own effect.
The triangle is divided from bottom left to top right.
Look at were the center of each triangle is, it’s too far down,
The uv coordinates are not properly interpolated from vertice 1 and 3 in the first triangle or from 3 to 1 in the second? The interpolation is unevenly being distributed towards vertice 3 from 1 in both cases.
No wonder i was seeing artifacts in the vertex shader and pixel shader with my lighting normals, what in the world.
http://i936.photobucket.com/albums/ad207/xlightwavex/miscellaneous/wierdbug_zps2vgv41ox.png
Just look at the center line up and down to see by how much.
here is the vertice winding order which im about change to test it.
//// create the indexs
////
//// LT 0 2 RT
//// | /| Triangle 1 is 0 1 2 ccw
//// | / | Triangle 2 is 2 1 3 ccw
//// LB 1 3 RB
//triangleIndexList[ti_pointer + 0] = 0 + vi_pointer;
//triangleIndexList[ti_pointer + 1] = 1 + vi_pointer;
//triangleIndexList[ti_pointer + 2] = 2 + vi_pointer;
//triangleIndexList[ti_pointer + 3] = 2 + vi_pointer;
//triangleIndexList[ti_pointer + 4] = 1 + vi_pointer;
//triangleIndexList[ti_pointer + 5] = 3 + vi_pointer;
Re:
flipped it and even wound the triangles differently same thing happens just on the opposide side.
// create the indexs
//
// LT 0 2 RT
// | \ | Triangle 1 is 0 1 2 ccw
// | \ | Triangle 2 is 2 1 3 ccw
// LB 1 3 RB
triangleIndexList[ti_pointer + 0] = 0 + vi_pointer;
triangleIndexList[ti_pointer + 1] = 1 + vi_pointer;
triangleIndexList[ti_pointer + 2] = 3 + vi_pointer;
triangleIndexList[ti_pointer + 3] = 0 + vi_pointer;
triangleIndexList[ti_pointer + 4] = 3 + vi_pointer;
triangleIndexList[ti_pointer + 5] = 2 + vi_pointer;
Even drawing each triangle individually they don’t come out right. Could this have to do with barycentric coordinate usage is this the card itself doing this ?
If this is due to the barycenter then its height is too far down in both triangles.
this is what i was seeing before this might just explain it.
http://i936.photobucket.com/albums/ad207/xlightwavex/miscellaneous/Artifact02bWeird_zpskyomr6yo.png
I think this may help you in order to understand the source of the problem and correct it
http://www.reedbeta.com/blog/quadrilateral-interpolation-part-1/
Edit …
This is right i guess argg this is pretty terrible.
http://i936.photobucket.com/albums/ad207/xlightwavex/miscellaneous/wierdbug_zps2vgv41ox.png
.
http://i936.photobucket.com/albums/ad207/xlightwavex/miscellaneous/wierdbug02_zps1w5lg8bd.png
Ok so basically every triangle that is not at a right angle will get distorted be be it in a model a mesh or whatever.
Ok i found this other link who talk about the basis of 3d and texturing.
Just changed the Z position of vertices 0, 4 and 5 and here is the result :
Not sure why it’s working but it is, and that’s all that matter i guess : )
Thank you guys !
ok i have no clue how you can do this without projecting based on the z atm i guess its not correcting it when all the z coordinates fall on the same z depth,
So i guess this has to be corrected in a shader if you want to distort a single quad at all.
@nino so your 2 triangles are no more in the same plane ? So thats no more a quad. How will you draw at the correct positions on your triangles?
@willmotil i think a shader will do it correctly with the method described in my link
Here, the important parts are: (a) the UVs are float3 instead of the usual float2, with qq in the third component; and (b) the pixel shader divides by qq before sampling the texture. The uvq values are precomputed and stored in the vertex data, so the vertex shader just passes them through.
Yh the method i found was bad, i can’t draw at correct position
I might try doing this using shader…
Yes you can do it in shader, use own vertex declaration to send additional info to correct UV cords. Basically you are doing perspective correction thus minimal required amount for proper UV cords is float3 not float2 (this will mean you will have to do some calculation based on ration between top and bottom edge widths on CPU and send it as part of UVcords or you can send info about width of both edges and do math in vertex shader - I would have to dig in my old shaders to find what was minimum required amount of information to be able to solve this in vertex shader).
I don’t know much about shader, can you please send an example file ?
I should have said earlier i know how to do this on a shader but not properly.
As in cheaply or in a simple manner.
I was looking this over again briefly just now and wanted to say i don’t like this solution either due to the disjointing on the lines that is not connecting the triangles of the quads. this warps the entire interpolation. So that the quad edges that are not adjoined are then improperly distorted changing one problem in for another.
Anyways to illustrate the problem i made this picture so you can see that the center is off but that is correct as far as the mapping is concerned. Each triangle needs to know about a fourth point of the entire quad at the least.
So extra information is required to offset these plots.
Its worse though if you have to do distance calculations per plot.
Im pretty sure we could solve this with fairly cheaply with a 3rd degree polynominal with some extra data but sending in a extra point per triangle or calculated offset per vertice doesn’t sound very attractive.
The basic gist is that for Triangle 1 the plots elements that are the furthest away from the unknown point top left of the other triangle and closest to the true quad center the correction must be the greatest. (i should of diagramed this better)
Basically at the true center since this image only deals with simplified x y coordinate changes.
You can see that the distance that must be offset to the right and up is proportional to the missing theta line distance at that y height.
Worse yet there is no way for a single triangle that has no right angles that is not part of a quad or other polygon to be corrected as there simply is nothing to base correctional information off of, i had completely forgotten about all of this stuff.
You can even see this kind of thing on seems in UE4 too (it is part of the 4th or another tutorial series videos):
In the editor, you then play with the textures coordinates to align them (automatically or do it manually)
I think i catched where your texture interpolation problem is. Do you compute the clipspace coordinates (divide by w) in your vertexshader or in your pixelshader?
It may be related to the texture interpolation happening in the vertexshader instead of the pixelshader, then when going to screenspace, the interpolation is wrong.
What is clip space, NDC…
http://omega.di.unipi.it/web/IUM/Waterloo/node15.html