Skip to main content
gamedev.net
🔒 Locked

Quick Multiple Sprite Question

Started by Sean_Seanston 8 replies 900+ views
Original Post
Sean_Seanston
Sean_Seanston
I'm just starting to program in DirectX and looking at this tutorial here: http://www.toymaker.info/Games/html/sprites.html So I have one question: To draw a sprite you need to do

sprite->Begin(...);
sprite->Draw(...);
sprite->End(...);
And he mentions Note: if you are drawing many sprites it is best to still use just one sprite interface and do all your drawing between the begin and end calls. This allows Direct3D to speed up the rendering of multiple sprites Which means you might do something like:

sprite->Begin(...);
sprite->Draw(...);
sprite2->Draw(...);
sprite3->Draw(...);
sprite->End(...);
Right? In which case, would it be worthwhile maybe having a function somewhere in a class related to the game engine that has some kind of dummy sprite and does Begin() and End() for you? That would work wouldn't it? You could then just use that all the time to begin and end your sprite rendering. Something like:

Engine::BeginRender();
...Do all sprite Draw() calls...
Engine::EndRender();
Seems neater but maybe I'm completely misunderstanding this?
MJP
MJP
What you're suggesting sounds perfectly fine to me. [smile]
Aardvajk
Aardvajk
sprite->Begin(...);sprite->Draw(...);sprite2->Draw(...);sprite3->Draw(...);sprite->End(...);


I'm not sure about this - you're not supposed to draw through different sprite interfaces.

What the article meant was more like:


sprite->Begin(...);sprite->Draw(...);sprite->Draw(...);sprite->Draw(...);sprite->End(...);


You need to understand that Draw() just adds a sprite to an internal buffer of the ID3DXSprite instance. Calling Flush() or (more usually) End() is what actually draws the sprites to the back buffer.

This is a common confusion due to the weird name ID3DXSprite. You would think it referred to a single sprite, wouldn't you? It actually represents an interface for batching the drawing of maybe multiple sprites.

This doesn't invalidate your design idea though. Maybe something like:

// psuedoclass Actor{public:    void Draw(ID3DXSprite *Sprite){ Sprite->Draw(/* me */); }};class Engine{private:    ID3DXSprite *Spr;    std::list<Actor*> Actors;    void RenderActors();};void Engine::RenderActors(){    Spr->Begin(...);    for(/* each actor A in Actors */) A->Draw(Spr);    Spr->End(...);}
Sean_Seanston
Sean_Seanston
Quote:
You need to understand that Draw() just adds a sprite to an internal buffer of the ID3DXSprite instance. Calling Flush() or (more usually) End() is what actually draws the sprites to the back buffer.


Oh right... that clears some things up then.

So it's basically just like an object used to deal with sprites? Meaning I should more or less never ever need to use more than a single LPD3DXSPRITE for all my sprites in a particular game/program? (Well, provided they're all kind of in the same area and whatnot and not somehow being done where all other LPD3DXSPRITE objects are inaccessible.)

Quote:
This is a common confusion due to the weird name ID3DXSprite. You would think it referred to a single sprite, wouldn't you? It actually represents an interface for batching the drawing of maybe multiple sprites.


Ya... very confusing name.

Yeah, from reading that code snippet it seems I understand it now.
It's much like just some sort of module you can add to your Engine or whatever and use to draw sprites?

That site really should have made that clearer :p. Tyvm...
Evil Steve
Evil Steve
I made a Journal Entry a while ago about what ID3DXSprite does internally. It's probably a bit out of date, but gives you a rough idea on what's going on under the hood.
Sean_Seanston
Sean_Seanston
I see this article here:
http://www.gamedev.net/reference/articles/article1608.asp

which argues against using the D3D sprite because of various reasons where it distorts the image and whatnot.

What do you all think about this? Is ID3DXSprite good enough not to bother using anything else or is it a flawed method that should be replaced by a better custom one as soon as possible?

Sounds fairly problematic, but then it was written in 2002 and since then we have DX9a, b and c so maybe it was fixed or something. Bah, starting to program in unknown territory is always so confusing :p
Aardvajk
Aardvajk
Quote:
Original post by Sean_Seanston
I see this article here:
http://www.gamedev.net/reference/articles/article1608.asp

which argues against using the D3D sprite because of various reasons where it distorts the image and whatnot.

What do you all think about this? Is ID3DXSprite good enough not to bother using anything else or is it a flawed method that should be replaced by a better custom one as soon as possible?

Sounds fairly problematic, but then it was written in 2002 and since then we have DX9a, b and c so maybe it was fixed or something. Bah, starting to program in unknown territory is always so confusing :p


Personally I don't use it. If you read Steve's post he linked to above, it seems to do a lot of mental stuff under the hood.

This is an excellent article that got me started using D3D for 2D games that takes you through manually setting up textured quads.
Sean_Seanston
Sean_Seanston
K, I'm going through that now... should work out in the end and get me started... but I was trying to run the example code and I get a strange problem, a load of:

error C2065: 'bufferVertices' : undeclared identifiererror C2065: 'index' : undeclared identifiererror C2228: left of '.x' must have class/struct/union


Which seem to be caused by "bufferVertices" and "index" not being declared anywhere in any of the example files, hmmmm. Anyone else get it to work or am I missing something completely?

EDIT: Well I just commented out that code since it was only for rotation and it all works fine now for rendering an image.

Some questions though:

1. If I'm going to make a 2D game, and therefore I just need a series of quads, do I only need a single vertex buffer that I can use for drawing all of them? Kinda seems that way.

2. What's an index buffer for? Is it just like a vertex buffer for a slightly different purpose?

EDIT2: As an aside, I'm a bit unclear about the implementation of that CTexture class in the article. I understand what it does and how it's useful but why does it need "struct LOADEDTEXTURE" with a texture inside when it also has a texture outside? The one outside with the bool is to see if the CTexture has anything loaded into it already, but why not just check if LOADEDTEXTURE.texture is NULL or not? Would that not have the exact same effect with less complication? In fact, why does he really use a struct inside CTexture at all? I suppose it keeps the data pertaining to the actual texture away from all the static stuff but really does it have a point? Maybe something is going way over my head. Or I'm too tired right now :D

[Edited by - Sean_Seanston on January 1, 2009 6:58:01 PM]
Aardvajk
Aardvajk
Quote:
Original post by Sean_Seanston
1. If I'm going to make a 2D game, and therefore I just need a series of quads, do I only need a single vertex buffer that I can use for drawing all of them? Kinda seems that way.


You can do, although if you have a large number of sprites, this might not be most efficient.

While you are filling your vertex buffer on the CPU, the GPU is idle. Once you DrawPrimitive() the buffer over, the GPU starts working but your CPU now may be waiting for the GPU to finish rendering.

With a large volume of sprites, it may be better to pass over the quads in a few batches so that the CPU and GPU can both be working at the same time.

To be honest though, when you consider the number of polygons being rendered per frame in any 3D game from the last five years, it is pretty unlikely that a 2D game using quads with two triangles each is going to be rendering enough quads per frame to make this worthwhile.

I believe that the point sprite example from the DXSDK uses this batching method though, as particle systems may have a sufficient number of quads to make this worthwhile.

Quote:
Original post by Sean_Seanston
2. What's an index buffer for? Is it just like a vertex buffer for a slightly different purpose?


Not really.

Consider a quad with four corners, made up of two triangles:

0------1|\     || \    ||  \   ||   \  ||    \ ||     \|3------2


Triangle one is made up of verts 0, 1 and 2. Triangle two is made up of 0, 2 and 3.

Assuming we are using D3DPT_TRIANGELIST (one quad could be drawn with a triangle strip, but two distinct quads couldn't), without an index buffer you would have to have a vertex buffer with six entries:

0 1 2 0 2 3


You could then call DrawPrimitive() with a triangle count of 2.

Using an index buffer, you could define a vertex buffer with just four vertices, then your index buffer defines the indices of the vertices that make up the triangles:

vb: 0 1 2 3ib: 0 1 2 0 2 3


When you call DrawIndexedPrimitive(), the hardware draws triangles by running through the index buffer rather than the vertex buffer, using each index to select the vertex from the vertex buffer.

In effect, you can have a vertex buffer that only contains unique vertices, even when these vertices are shared.

It may not seem to be of much value in this example, but it becomes of more use when you have lots of vertices, especially if you are using dynamic buffers as you have less vertices you need to generate each frame.

It is obviously of more use in a 3D application where the number of shared vertices on a mesh can be immense, although once texture coordinates come into play, its use becomes less as often a vertex that is shared in terms of position by two or more triangles will not share the same texture coordinates.

Quote:
Original post by Sean_Seanston
As an aside, I'm a bit unclear about the implementation of that CTexture class in the article. I understand what it does and how it's useful but why does it need "struct LOADEDTEXTURE" with a texture inside when it also has a texture outside? The one outside with the bool is to see if the CTexture has anything loaded into it already, but why not just check if LOADEDTEXTURE.texture is NULL or not? Would that not have the exact same effect with less complication? In fact, why does he really use a struct inside CTexture at all? I suppose it keeps the data pertaining to the actual texture away from all the static stuff but really does it have a point? Maybe something is going way over my head. Or I'm too tired right now :D


I think the idea is that a separate list of textures is stored in a static vector and instances of the CTexture class then point into this vector rather than "own" the actual texture themselves.

This means that two or more instances of CTexture can all point to the same texture without the texture being loaded multiple times, while allowing the user of CTexture to treat it as if they are separate textures.

Sean_Seanston
Sean_Seanston
Alright... that clears some things up. Thanks for the help :D

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.