WireFrame in XNA 2D

Started by
6 comments, last by Starnick 12 years, 5 months ago
I have been trying to draw a wireframe instead of my sprites but it looks like I'm missing something.

From what I understand I can just set a new RasterizeState like this:


[source lang="c#"]
RasterizerState state = new RasterizerState();
state.FillMode = FillMode.WireFrame;
spriteBatch.GraphicsDevice.RasterizerState = state;

[/source]

Unfortunately this does not seem to work for me. From searches I read that possibly the spriteBatch calls could be resetting the values. So I have tried playing around with the place where I set the RasterizerState: before the spritebatch.begin and after it. Also before spritebatch.end and after it.

Does anyone have any idea what might be the problem? I guess the only assumption I'm making is that wireframe is handled by the drivers so drawing a wireframe for a 2D sprite would basicly mean i would see 2 triangles show up without the texture.

[source lang="C#"]

Texture2D m_mario;
protected override void LoadContent()
{
m_mario = Texture2D.FromStream(GraphicsDevice, File.Open("Mario.png", FileMode.Open));
}


protected override void Draw(GameTime gameTime)
{

GraphicsDevice.Clear(Color.CornflowerBlue);

RasterizerState state = new RasterizerState();
state.FillMode = FillMode.WireFrame;
spriteBatch.GraphicsDevice.RasterizerState = state;

spriteBatch.Begin();

spriteBatch.Draw(m_mario, new Vector2(300, 300), Color.White);

spriteBatch.End();

base.Draw(gameTime);
}

[/source]
Advertisement

I have been trying to draw a wireframe instead of my sprites but it looks like I'm missing something.

From what I understand I can just set a new RasterizeState like this:


[source lang="c#"]
RasterizerState state = new RasterizerState();
state.FillMode = FillMode.WireFrame;
spriteBatch.GraphicsDevice.RasterizerState = state;

[/source]

Unfortunately this does not seem to work for me. From searches I read that possibly the spriteBatch calls could be resetting the values. So I have tried playing around with the place where I set the RasterizerState: before the spritebatch.begin and after it. Also before spritebatch.end and after it.

Does anyone have any idea what might be the problem? I guess the only assumption I'm making is that wireframe is handled by the drivers so drawing a wireframe for a 2D sprite would basicly mean i would see 2 triangles show up without the texture.

[source lang="C#"]

Texture2D m_mario;
protected override void LoadContent()
{
m_mario = Texture2D.FromStream(GraphicsDevice, File.Open("Mario.png", FileMode.Open));
}


protected override void Draw(GameTime gameTime)
{

GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin();

RasterizerState state = new RasterizerState();
state.FillMode = FillMode.WireFrame;
spriteBatch.GraphicsDevice.RasterizerState = state;

spriteBatch.Draw(m_mario, new Vector2(300, 300), Color.White);

spriteBatch.End();

base.Draw(gameTime);
}

[/source]





Edit: Why is it allways when I ask a question on a forum that 5 minutes later I end up getting it to work.

I changed my spritebatch to immediate:
spriteBatch.Begin(SpriteSortMode.Immediate,BlendState.Opaque);



Unfortunately my solution makes me ask 2 more questions:

1. SpriteSortMode.Immediate is not too great for performance. Is there another way I can initialize the spritebatch or change something to the sprite draw calls giving me the same effect?

2. The wireframe retains the original sprites colors since I'm using Color.White when drawing. Is there any way I can change the wireframe color to for instance "full" red (so no color from the original sprite is retained)?
Take a look at the SpriteBatch.Begin() methods. When using a sprite batch, you specify which render states that should be used in Begin(). So using the parameterless method, or passing null for the other methods will cause the sprite batch to use the default render state value, which in your case explains why the sprites are not drawn as a wireframe.

E.g.

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, state);

This would do the trick - it's telling spritebatch to use deferred sort mode (which is the default), and the three nulls imply use the default blendstate, samplerstate, and depthstencil state respectively.

A good rule of thumb for setting render states is to set the states you only need, so sprite batch doesn't allow you to restore render states it changes after it's done drawing (like previous incarnations of XNA). So setting a state prior to Begin() would just mean that gets overwritten by whatever state the sprite batch uses (either its default, or your specified one). This also goes for any other geometry you're drawing, where you only set the states you need.

Edit: Why is it allways when I ask a question on a forum that 5 minutes later I end up getting it to work.

I changed my spritebatch to immediate:
spriteBatch.Begin(SpriteSortMode.Immediate,BlendState.Opaque);



Unfortunately my solution makes me ask 2 more questions:

1. SpriteSortMode.Immediate is not too great for performance. Is there another way I can initialize the spritebatch or change something to the sprite draw calls giving me the same effect?

2. The wireframe retains the original sprites colors since I'm using Color.White when drawing. Is there any way I can change the wireframe color to for instance "full" red (so no color from the original sprite is retained)?


Hmm I'm not sure why you'd be getting the wireframe without specifying the state in Begin() like I mentioned when using immediate sort mode. But how immediate works is the sprite is not added to the batch's internal queue, instead it's just immediately drawn (hence its name). The default sort mode, deferred, will simply add the sprite to the queue and the End() call will flush these sprites, so each is drawn at the very end - and in the order that they were submitted. So they aren't sorted, but may be batched if possible. Other sort modes like by texture would mean the sprites are grouped together according to their texture, to reduce the number of draw calls (e.g. say you use two textures, and draw 100 sprites 50 each, no matter the order those 100 sprites get reduced to two draw calls, where as deferred mode could be many more depending the order you submit the sprites to the batch).

As for your questions, #1 should be answered by the previous post - use one of the overloaded Begin() methods to pass in your rasterizer state, as well as a sort mode that would allow for the batching I described.

For #2 if you're doing this as a means of debugging, you could always just pass in a red texture that is the same size of your sprite. That way you don't have to change any other parameters, just make a check if you want to draw the debug rectangle and use the debug (red) texture at draw time.
Thank you for your quick reply.

I guess the reason why it is showing the wireframe in immediate mode is because then you can change "anything" in the graphics device and those settings will be used instantly on the next draw. I guess its why immediate was introduced to give more control within a spriteBatch.

Deferred mode is exactly what I need. Had no idea that I could null all the other params (apart form rasteriser) and then the default settings would be used.


Yes the wireframe is for debugging though unfortunately It is unknown what the size of the textures in the application are going to be. (I'm loading all content manually using xml files and you never know how your users are going to (ab)use that freedom.) I guess I could add a 1px red texture in there somewhere and when I enable wireframe I can stretch it to the size it needs to be. One problem would be then that I'd have to write code around every place where i'm drawing a texture to swap it with the red one. You think it would be possible to color the wireframes with a BasicEffect?

Thank you for your quick reply.

I guess the reason why it is showing the wireframe in immediate mode is because then you can change "anything" in the graphics device and those settings will be used instantly on the next draw. I guess its why immediate was introduced to give more control within a spriteBatch.

Deferred mode is exactly what I need. Had no idea that I could null all the other params (apart form rasteriser) and then the default settings would be used.


Yes the wireframe is for debugging though unfortunately It is unknown what the size of the textures in the application are going to be. (I'm loading all content manually using xml files and you never know how your users are going to (ab)use that freedom.) I guess I could add a 1px red texture in there somewhere and when I enable wireframe I can stretch it to the size it needs to be. One problem would be then that I'd have to write code around every place where i'm drawing a texture to swap it with the red one. You think it would be possible to color the wireframes with a BasicEffect?


For debugging, what you described is what I was thinking in regards to having a small, blank red texture, which could be problematic since you do want to minimize hard coding any value. Another idea is to avoid drawing wireframes in the first place and write a line batch utility where you're just drawing the rectangles using line lists (I've built something like this to debug normals/tangents/binormals in my engine, basically like spritebatch but for lines). Although you'd still be writing debug code in a few places potentially too. This may be mitigated if you have a central point in your code that you're doing all the drawing (e.g. have a sprite object that can draw itself, but also draw a debug rectangle - or both).

Perhaps the easiest alternative would be to write your own sprite effect (the actual spritebatch shader XNA uses, as well as all the XNA stock effects can be downloaded here) and pass that into the proper spritebatch.Begin() overload method. So you write an effect that is more or less the default sprite batch effect, but you don't sample the texture and just output a debug color (like red). So when you're debugging, you just switch out which effect and rasterizer state to be used. Re-using basic effect could also be considered, as I think it has a technique that doesn't sample textures? Pretty sure it does. I only say this because sprite batch, with a custom shader, will be setting the sprite texture to the first pixel texture slot onto the device, whether or not you use it.

Shawn Hargreaves has written on this subject, of using custom shaders with spritebatch here and here. The first link is more what you're trying to accomplish, but the latter link is interesting as it's about using spritebatch for billboarding in a 3D environment (and how to nicely batch them).


Edit:

I didn't pay close attention to the second code snippet - yeah changing the state after Begin() is why it works. I believe state changes get pushed to the device in Begin(), although I honestly don't remember (for the other sort modes, I don't remember if it takes place in End() when drawing would actually occur). But again this changing the state is not really advised in the middle of a Begin()...End() block, just like having two sprite batches and trying to nest them since the two batches would conflict with one another or do any other drawing. This is the reason why the Begin() overloads that take in render state objects exists in the first place.
Thanks again.

I'm not sure what you mean by: " I only say this because sprite batch, with a custom shader, will be setting the sprite texture to the first pixel texture slot onto the device, whether or not you use it."


I have managed to get the following going:
I can toggle wireframe and disableing textures each with their own hotkey.

One downside though Is that for both wireframe and disableing textures to be toggled seperately I needed another spritebatch cycle. (basically have to draw everything twice) Could you have a check on the code below and see whether I'm making things more complicated than needed?



[source lang="c#"']

protected override void Initialize()
{
// TODO: Add your initialization logic here
m_resourceManager.Start();

m_wireframe_rasterizer = new RasterizerState();
m_wireframe_rasterizer.FillMode = FillMode.WireFrame;


//wireframe coloring effect.
m_wireframe_effect = new BasicEffect(GraphicsDevice);

Matrix projection = Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);

m_wireframe_effect.World = Matrix.Identity;
m_wireframe_effect.View = Matrix.Identity;
m_wireframe_effect.Projection = halfPixelOffset * projection;

m_wireframe_effect.TextureEnabled = false;
m_wireframe_effect.VertexColorEnabled = true;


base.Initialize();
}




protected override void Draw(GameTime gameTime)
{

GraphicsDevice.Clear(Color.CornflowerBlue);


if (m_textures_enabled)
{
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null);

spriteBatch.Draw(m_mario, new Vector2(300, 300), Color.White);

spriteBatch.End();
}

if (m_wireframe_enabled)
{
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, m_wireframe_rasterizer, m_wireframe_effect);

spriteBatch.Draw(m_mario, new Vector2(300, 300), Color.Red);

spriteBatch.End();
}


base.Draw(gameTime);
}
[/source]

Thanks again.

I'm not sure what you mean by: " I only say this because sprite batch, with a custom shader, will be setting the sprite texture to the first pixel texture slot onto the device, whether or not you use it."


It's a bit inconsequential now, but I was referring to at some point before sprite batch draws geometry, it will be setting renderstates, buffers, and calling apply() on whatever effect will be used. This includes setting the sprite texture that will be used, which it does by setting it to the graphics device, rather than using the effects framework, e.g.:

[source lang="C#"]
graphicsDevice.Textures[0] = spriteTexture;
[/source]

As for your code, if you want to draw the wireframe over the sprite you're going to have to do two draw calls. Whenever you have to make a material change (shaders, renderstates, etc), in general you have to break them up into separate draw calls. When dealing with a single mario sprite, this may sound like too much - but depending on how many sprites you're drawing and how you setup your assets this is not necessarily a bad thing.

For example, you'd have sprite sheets for animated sprites, maybe texture atlasses for static sprites (like backgrounds, items, etc) so you could easily batch those sprites (sorting by texture, or breaking things up by depth using a few sprite batch cycles - since you don't want characters or items behind the background obviously, so draw the background first, then all your characters and items, etc and at each stage you're batching the sprites best you can). So if you do things right or depending on how complex the scene is, you can get away with only a few draw calls for the scene. But you should only need one sprite batch cycle to draw debugging shapes, since all of them will use the same rasterizer state, same effect, and same dummy texture, so you should collect all your sprites in the scene (sizes, positions, etc), submit them to the sprite batch using deferred sort mode (don't really need to sort them), which will then execute a single draw call in End() since no state changes would be required.

This topic is closed to new replies.

Advertisement