Archived

This topic is now archived and is closed to further replies.

InTheSackMan

Direct3D9 - Problem with 2D Sprites

Recommended Posts

Hi, I just started porting my 2D Sprite Engine to D3D today and i''m having a really annoying problem with my sprites. in DirectDraw I would have an offscreen surface for each of my sprites which could be any size I specified. I would then blit that surface to the secondary surface and then flip the surfaces if I wanted to draw a sprite. In Direct3D I apparently have to use Textures? I''ve been using the D3DXSprite class and it does good job at what its supposed to do. My problem is that I have my sprites ALL in just a few files, and what I used to do is grab a rectangle ( which had the sprite in the frame/animation I wanted ) from the file and throw it onto its own surface. With DX9 I have to use CreateTextureFromFile apparently and it wants me to load the ENTIRE file, or just a portion starting at 0, 0 from what it seems. Is there a way or some sort of function for grabbing a specific rectangle from a file and turning it into a texture? Currently the only fix seems to be to put EACH sprites Frame/animation in its own file, which in my game could yeild over 300 separate files. I REALLY dont feel like doing that. I even tried putting it in its own surface and doing a copyrect to put it on a texture, but alas, copyrect doesnt seem to work in DX9. Thanks, please respond!

Share this post


Link to post
Share on other sites
Hi, there must be more then one way to load a texture in D3D. One way is to load a file, but I suppose that we can load it from a GDI Bitmap object? If I am right, you could load the complete file with all the sprites, then create a GDI bitmap from the part of the bitmap that you need, and load your texture using that bitmap as the source.

I hope it helped

Valrandir


Considering that my name is a base 62 number, how would you write it in base 32 according that we use
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
as our symbols?

Share this post


Link to post
Share on other sites
Well, I was planning on loading PNGs or Targas with Alpha values in them and Regular Windows Bitmaps wont do. It''d be really nice to have the power of CreateTextureFromFileEx while being able to specify a source rectangle to take the texture from the file at. Plus later on I might wanna do more 3Dish stuff and it''d be nice to have it automatically do mipmaps for me and what not. Hope that helps a bit.

Share this post


Link to post
Share on other sites
I''m not sure if this applies to the D3DXSprite class, but when using Vertices, you can specify which piece of the texture you actually want to use.

You might want to check the recent article on this topic.



Open your eyes and free your mind
That you may see the subtle wonder
Of the worlds we live in...

Share this post


Link to post
Share on other sites
hi - actually loading a lot of textures won't be that much of a perfomance hit provided that you load them in advance and not during your render cycle; my textured quad engine does it and I still get relatively decent performance. The key is sorting your textured quads by texture so you don't take a performance hit by switching textures more than you need to during your render cycle.

BUT if you want to minimize the amount of textures you have to load, then you have a number of options:

1: if you are using d3dxsprite, use the srcrect parameter of the .draw method to specify what portion of the texture that you want to draw (this should be similiar to what you did with directdraw). You just pass in a rect structure, and you can keep all of your sprites on big textures.

2) get rid of id3dxsprite and write your own textured quad engine (it's not that hard to do and it will be faster). Then,
put all of your images onto one big texture and use the .tu and .tv coordinates to render only the part of the texture that you need.

[edit: this is what Zxylin is talking about above]

Say I have 4 sprites, and each is 128x128. What I could do is load each sprite as a texture, OR I could combine them all into a single texture that is 256x256. If I chose the latter, then I would access each of the four texture with the following .tu and .tv coordinates:

1: 0,0; .5,.5
2: .5,0; 1,.5
3: 0,.5; .5,1
4: .5,.5; 1,1

Does this make sense? -- the .tu and .tv values can range from 0 to 1, so it's not too difficult to figure out what coords to use. You can make you base textures as large as you want, provided that your users' video cards can handle them.

Now, to render these textures, you can use a number of methods:

1: define static vertex coords for each of the sub textures (so for the above you would end up with four static quads, each with separate texture coordinates. Then you just render the quad that corresponds to the texture that you need.
2: define dynamic vertex coords -- here you would write your vertices to a dynamic buffer before you render (you could incur a performance penalty here)
3: use static coords, but use texture transformations to set the quad .tu and .tv coordinates.

My engine uses a combination of techniques #1 and #3 above (mostly #1 though). I define a number of quads that are 1x1 in width and height and have the various .tu and .tv values that I need. I then use scaling and transformation matrices to position these quad where I need them to be and set them to the proper size. Then I render the quad that is appropriate for the portion of the texture that I want to render. If I want special effects (like 2d flowing water), then I also use a texture transformation matrix...

OK - this is probably more than you wanted to know, and there are a few gotchas (DX may scale textures that are not powers of 2 unless you specify otherwise when you load them; you might have to fine tune you .tu and .tv coords, etc...), but once you get these worked out you'll find that it is alot more flexible than directdraw.

Hope this helps-

BM

p.s. there is a new feature on making 3d textured quads. Take a look at this; it's code is in C, but it shouldn't be too difficult to convert if you're using a different language. It doesn't do all that I have described above, but it should provide a good starting point.

[edited by - bmoberly on August 5, 2003 1:37:07 AM]

Share this post


Link to post
Share on other sites
That is definitely the way i'd go if I dont find another way here, but its really not the way I currently have the engine written to do it. Theres a million reasons why using a gigantic texture to store all the sprites is a bad way for me, one being that I use C++ object oriented code and I simply tell the object to draw the sprite in its current frame and animation and the rest takes care of it. I really dont like the idea of having a million RECT's to to specify where in the gigantic texture to grab them from, especially when some sprites may be in different files, etc. I really just want to use the textures as mini-surfaces, which each sprite will have its own array of, and load them up into their own private texture for the frame/animation ( which I'm pretty sure should be more efficient than a bigger texture ) and type something similar to

m_pSprite[m_nCurrentAnimation]->draw( m_nCurrentFrame, m_nX, m_nY ).

So, to try to clarify this a little more if I can, lets say I have a sprite that has 3 frames and only does one animation: Walking to the right. What I want to do is create a sprite which holds an array of 3 textures. I load the 3 textures, each one in a different stage of walking, i.e. left foot, both, right foot. then I type
m_pSprite->draw( m_nCurrentFrame, Xposition, Yposition )
( then check if I should advance frames, yada yada yada )
and with a little luck it works.

Heres the code from the private data of my Sprite class
private:
int m_nWidth;
int m_nHeight;
int m_nFrameCount; // How Many Frames Of Animation The Sprite Has
RECT m_SourceRect; // Source Rectangle For Drawing
// Texture(s) For The Sprite Images
LPDIRECT3DTEXTURE9 *m_SpriteTextures;// A pointer to a pointer, it gets NEWED with as many FRAMES specified in the constructor, i.e. CBaseSprite mySprite( 3, 50, 50 ) the 3 is the framecount, and the fiftys are the width/height

the constructor would then do a m_SpriteTextures = new LPDIRECT3DTEXTURE9[3]; creating 3 pointers to 3 different 50x50 textures. This is all optimized to use the least amount of ram with a sprite manager, and all 3 textures will point to the loaded textures which are stored inside of the sprite manager.

Whew, I hope someone is getting this. I would like to thank everyone for their help thus far, its always nice to have some sort of plan to fall back on when yours blows up in your face.

[edited by - InTheSackMan on August 5, 2003 2:27:21 AM]

Share this post


Link to post
Share on other sites
Hi - what you''ve outlined above makes sense - let me see if I understand it correctly - you want to save your sprites on disk as several large graphics, but you only want to load the portions of those graphics that you need.

If this is the case, then what I would do is look into the d3dx8.LoadSurfaceFromFile function, as it takes a srcrect parameter which will let you only load part of the graphic file that you specify. You can grab the surface level of the texture that you want to modify and then use this function to load the appropriate graphic to it. You might still have to worry about rects or .tu and .tv coords because DX likes its textures to be a power of two.

at any rate, good luck-

BM

Share this post


Link to post
Share on other sites
quote:
Original post by InTheSackMan
Exactly! But how do I get it from the surface to a texture?


You can use CopyRects() after calling GetSurfaceLevel() on the texture.

A better way to do what you need (in a textured quad engine) is to load the texture with all the images on it, and use texture coordinates (u, v) to specify which area of the texture to draw.

The coordinates go from 0 to 1, so you have to convert pixel coordinates to a fraction of the texture.

If you make your drawing function accept a source RECT, you can do the following:

For the top left corner vertex:
u = srcRect.left / textureWidth
v = srcRect.top / textureHeight

For the bottom right:
u = srcRect.right / textureWidth
v = srcRect.bottom / textureHeight

etc.

Hope this helps.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
But if you want speed, you''re going to have to put many sprites into the same big textures, since texture switching is expensive. 3D hardware works very differently from 2D, has both benefits and some not so good things for 2D..

Share this post


Link to post
Share on other sites
The anonymous poster just reminded me:

I should add that you should try to draw all sprites on screen from each texture at once, so you don''t have to call SetTextureStageState() for every sprite drawn.

It''s not always feasible, but if you can manage it you''ll get a small speed increase.

Share this post


Link to post
Share on other sites
Thanks guys! I think I have it working. I kind of settled in the middle - I''m going to use one texture for every animation which means that the sprites can be in multiple files as long as the animation ( walking, jumping etc. ) is not broken up. Ive got everything drawing allright and the engine is running faster than ever. Thanks for the tips, I think i''ll take you guys up on those performance pointers. Thanks again!

Share this post


Link to post
Share on other sites