Point sprites

http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Point_sprites.php

As of version 4.0, XNA Game Studio no longer supports point sprites.

“The big reason is that DirectX 10 and 11 do not support point sprites, so if we kept them in our API, we would be unable to someday move that API to future DirectX versions.

A smaller reason is that point sprites behave differently on Windows and Xbox, […] not […] consistent from one Windows GPU to another […] The final reason is that point sprites are slower than triangles on some common graphics chipsets.” – Shawn Hargreaves

This is a pretty big challenge then considering that this tutorial is all about point sprites!

First we need to remove the structure defining the point sprite vertex format which is all the code in:

private struct VertexPointSprite

Also we wont be needing the VertexDeclaration so we can remove these two lines:

VertexDeclaration pointSpriteVertexDeclaration;

pointSpriteVertexDeclaration = new VertexDeclaration(device, VertexPointSprite.VertexElements);

Now what we will do is to rewrite this using quads.

A quad is made up of two triangles that form a rectangle or square. Drawing two triangles only requires four vertices, but six index entries.

To determine the location of each of the four vertices in the quad the constructor calculates the four corners from the origin, facing, width, and height.

The Quad constructor calls SetUpVertices(), which assigns UV coordinates ranging from (0,0) to (1,1), and makes the entire texture appear on the quad.

UV coordinates are derived from texture coordinates that are mapped into values along a u-axis and a v-axis.

The way we do this is by using uv-coordinates. A uv-coordinate indicates a location in the texture image that we want to use. U is the location horizontally, and V is the location vertically.

Instead of specifying a location in pixels (i.e. “10 pixels across and 40 pixels down”), we specify our coordinate as a fraction of how far to go across the entire image. The top left corner is (0, 0) the bottom right corner is (1, 1)  top right is (1, 0) and bottom left is (0, 1) simple enough.

A vertex array is declared and filled with VertexPositionTexture data, then populate the vertices with position and texture coordinates.

An array of shorts is declared, data type larger than this is not necessary for a single quad giving the luxury of not having to worry which graphics profile that is being used. The array is then filled with the indices which will tell the graphics card which order to draw the vertices. The indices are specified in clockwise order since triangles drawn in counter-clockwise order would be facing away from the camera causing them to be culled.

The VertexBuffer and IndexBuffer are initialized and the vertex and index data that has just been generated is added to these buffers. The two properties in this class allow public access to this data which is necessary when finally drawing the quad on-screen.

The code for the quad class is as follows, add this just below the lines:

namespace XNASeries2

{

public class Quad

#region Fields

public VertexPositionTexture[] vertices;
public short[] indices;

Vector3 Origin;
Vector3 Up;

Vector3 Left;
Vector3 UpperLeft;
Vector3 UpperRight;
Vector3 LowerLeft;
Vector3 LowerRight;

VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;

GraphicsDevice Device;
#endregion

#region Properties
public VertexBuffer VertexBuffer
{

get

{

return vertexBuffer;

}

}

public IndexBuffer IndexBuffer
{

get

{

return indexBuffer;

}

}

#endregion
public Quad(GraphicsDevice device, Vector3 origin, Vector3 up, float width, float height)
{

vertices = new VertexPositionTexture[4];
indices = new short[6];
Origin = origin;
Up = up;
Device = device;

// Calculate the quad corners
Left = Vector3.Cross(Vector3.Backward, Up);
Vector3 uppercenter = (Up * height / 2) + origin;
UpperLeft = uppercenter + (Left * width / 2);
UpperRight = uppercenter – (Left * width / 2);
LowerLeft = UpperLeft – (Up * height);
LowerRight = UpperRight – (Up * height);

SetUpVertices();

}
private void SetUpVertices()
{

// Fill in texture coordinates to display full texture
// on quad
Vector2 textureUpperLeft = new Vector2(0.0f, 0.0f);
Vector2 textureUpperRight = new Vector2(1.0f, 0.0f);
Vector2 textureLowerLeft = new Vector2(0.0f, 1.0f);
Vector2 textureLowerRight = new Vector2(1.0f, 1.0f);

// Create 4 vertices
vertices[0] = new VertexPositionTexture(LowerLeft, textureLowerLeft);
vertices[1] = new VertexPositionTexture(UpperLeft, textureUpperLeft);
vertices[2] = new VertexPositionTexture(LowerRight, textureLowerRight);
vertices[3] = new VertexPositionTexture(UpperRight, textureUpperRight);

// Set the index buffer for each vertex, using
// clockwise winding
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 2;
indices[4] = 1;
indices[5] = 3;

// Initialize the VertexBuffer, and insert the data
this.vertexBuffer = new VertexBuffer(Device, typeof(VertexPositionTexture), vertices.Length, BufferUsage.WriteOnly);
this.vertexBuffer.SetData(vertices);

// Initialize the IndexBuffer, and insert the data
indexBuffer = new IndexBuffer(Device, typeof(short), indices.Length, BufferUsage.WriteOnly);
indexBuffer.SetData(indices);
}

}

}

We also want to use a separate effect for drawing the quad and we need a quad to draw so add these declarations at the top of your Game1 class

Effect quadEffect;

Quad quad;

In the LoadContent() method we need to load the effect and initialize the quad with a width and height of 1 so add these lines:

quadEffect = Content.Load(“effects”);

quad = new Quad(device, Vector3.Zero, Vector3.Up, 1, 1);

The DrawBullets() method is very similar, we still check the bullet list isn’t empty and iterate over that list. We make sure to set the current technique, the view and projection matrices and the texture outside of the loop.

The CullMode is set to none for now as explained earlier if the quad isn’t facing the camera it will be culled away, we will solve this later.

The quad is scaled down and we apply the correct position using a translation matrix, this should be quite familiar to you by now.

Use the VertexBuffer and IndexBuffer properties on the Quad to supply the primitive data, and specify four vertices for two triangles to draw.

The CullMode is then reset to CullCounterClockwise so that the rest of our rendering code isn’t affected.

private void DrawBullets()
{

if (bulletList.Count > 0)
{

quadEffect.CurrentTechnique = quadEffect.Techniques[“Textured”];
quadEffect.Parameters[“xView”].SetValue(viewMatrix);
quadEffect.Parameters[“xProjection”].SetValue(projectionMatrix);
quadEffect.Parameters[“xTexture”].SetValue(bulletTexture);
device.RasterizerState = RasterizerState.CullNone;

for (int i = 0; i < bulletList.Count; i++)
{

//scale down the quad
Matrix worldMatrix = Matrix.CreateScale(0.05f, 0.05f, 0.05f) * Matrix.CreateTranslation(bulletList[i].position);

foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
{

quadEffect.Parameters[“xWorld”].SetValue(worldMatrix);
pass.Apply();

device.SetVertexBuffer(quad.VertexBuffer);
device.Indices = quad.IndexBuffer;
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);

}

}

device.RasterizerState = RasterizerState.CullCounterClockwise;

}

}

And last but not least the code to add to the end of the draw method remove:

DrawSprites();

and replace it with:

DrawBullets();

And that’s it for Quads! Next up Alpha Blending, with some billboards thrown in for good measure.

Advertisements

3 thoughts on “Point sprites

  1. Hi, this post is like 2 years old so im not sure if i will get a reply, but hey, dont ask dont get eh?

    Im trying to run the bullet code however i get this break error:

    InvalidOperationException was unhandled – The current vertex declaration does not include all the elements required by the current vertex shader. Normal0 is missing.

    on this line :
    graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);

    Thanks in advance 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s