Jump to content

  • Log In with Google      Sign In   
  • Create Account


Making my own sprite class


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
18 replies to this topic

#1 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 September 2007 - 07:58 AM

After running into several walls with the directX sprite class, such as rendering a bunch of sprites that have different scales, rotations and positions, I decied to make my own sprite class. While making it, I started thinking about the problem of animated sprites. If I just want one quad for every sprite, then the only way I know of to animate the texture would be to edit each vertex each frame and update the UV coordinates. From other sources, I have heard that editing vertex buffers is a bad thing. I am now considering creating a quad for each sprite. Not each instance of each sprite, just for each frame. For example, lets say I have two sprites. One is a space ship that isn't animated. Another is a spinning asteroid which has 5 frames of animation. I would then create 6 quads. If I wanted to render 100 asteroids, I would just use transfroms to position each asteroid. The amount of quads I would need would be equal to all the 'frames' of animation in total, with unanimated sprites being just one frame. Is this a good way to do it, or should I be editing UV coordinates or is there a better way that is none of the above?

Sponsor:

#2 Evil Steve   Members   -  Reputation: 1955

Like
0Likes
Like

Posted 30 September 2007 - 08:13 AM

Modifying the vertex buffer is fine. You'll probably be doing that every frame anyway, since the sprites can change position. Just make sure you create the VB as a dynamic VB, otherwise it'll be particularly expensive to lock.

Also, Draw[Indexed]Primitive() calls are particularly expensive, you don't want to be making more than 500 or so calls per frame. Using transforms for each sprite means you have to call Draw[Indexed]Primitive() once per sprite, which will give you horrible performance.

My sprite manager uses a std::vector of structs, one for each sprite and it fills an internal VB and renders from it once per frame.

#3 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 September 2007 - 08:53 AM

Ill be using matrix transforms to move the sprites. Ill never need to modify the vertex locations. I am using PositionTextured types.

#4 SiS-Shadowman   Members   -  Reputation: 359

Like
0Likes
Like

Posted 30 September 2007 - 11:12 AM

But that way, you need to call Draw(xxx) for every sprite. You will get performance issues if you don't change this. You should really use a dynamic VB and then update the buffer every frame, it doesn't hurt at all.

#5 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 September 2007 - 01:08 PM

But you would need to call Draw(xxx) every time anyway. If I edit my vert UVs, I still need to call Draw to render it. If I just have a different set of verts, I call Draw on them instead.

I could be completly misunderstanding you though, if so, I have no idea what you mean by Draw once vs multiple times.

#6 Scet   Members   -  Reputation: 960

Like
0Likes
Like

Posted 30 September 2007 - 01:54 PM

For my latest version of Doom.Net the technique I used went something like this:

1. Each image is loaded into one giant volume texture, this eliminates the need to to switch textures.

2. Each image stores an array of 4 vertices and 6 indices. The vertices looked something like this:

Vertex V1 = new Vertex( 0, Height, -Width / 2, U1, V1, W );
Vertex V2 = new Vertex( 0, Height, Width / 2, U2, V1, W );
Vertex V3 = new Vertex( 0, 0, -Width / 2, U1, V2, W );
Vertex V4 = new Vertex( 0, 0, Width / 2, U2, V2, W );

This was for 3D billboarding, but something similar will also for 2D.

Edit: Width and Height are the dimensions of the image. That's because in Doom, 1 unit in object space is the size of one pixel. You'll of course need to adjust these to match your game.

3. At the start of each frame a list(vector for C++) of vertices and a list of indices are cleared.

4. When an image is "drawn" its vertices and indices are added to the list. The copies of the vertices in the vertex list then have the necessary transformations done to them through software calculations.

5. When everything is added, every sprite is then rendered with a single DrawIndexedUserPrimitives call.

I'm no GPU guru, but this was the best method I could think of at the time. I never tested it with a dynamic VB/IB, so I don't know if it would be any faster.

#7 MasterWorks   Members   -  Reputation: 496

Like
0Likes
Like

Posted 30 September 2007 - 02:56 PM

There's a million ways to do this but if you want proper speed you MUST use a large dynamic vertex buffer, as has been suggested. It's just not feasible to use a matrix transform for each sprite; you can't afford to be rendering just one sprite per DrawPrimitive. Make a massive dynamic VB and each frame use the next available vertices (locking with NOOVERWRITE) until the VB is full; then start over at the beginning (locking with DISCARD). Since your data is fully dynamic this is really the only choice you have; this allows you to scale, rotate, animation, and reposition sprites however you see fit without regard to frame coherence.

If you can group your sprites by texture and render state you will get even greater speed as you will be able to tell the GPU to render hundreds of sprites at once.

#8 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 September 2007 - 02:57 PM

That is similar to what I did, except my list of textures is attached do a corrisponding quad opposed to making a 'list' of quads to render with one draw.

Anyway this whole sprite thing is really frustrating me, it has been 2 weeks and every time I get close to doing one way, something comes up and hoses it all up. Let me go over the history here.

I start off with making my own quads and then notice the Sprite class.
I spend a few days messing with Sprite.Draw2d.
Then I get told I shouldn't use that, and just use my own transforms in lew of Draw2d.

Ok fine...

I then go on and start using Sprite.Draw() and spend a full week on this. I end up doing a bunch of matrix math so I can get a virtual cordinate system, handle aspect ratio and viewport changes. Everything seems great and then...

I try and draw two sprites. They overlap. I can use the Draw() to move them in screen space, which breaks my virtual coordinate system, but I absolutly can not rotate or scale them. The last matrix used affects all sprites. I ask around and am told that I just can't do that without a bunch of Begin / Ends, which is a bad thing.

Now I am hosed, again... I go back to doing my own quads and sprite class just so I can be done with this whole Sprite class mess.

So I had some other issues, like textures dissapearing when my viewport changed size and I couldn't make my textures transparent. I figured that all out, but in the course of all that I get several people telling me (not all from this thread):

I really should use the sprite class. Try Draw2d... like I was doing before? Don't do a bunch of DrawPrimitives either, its slow. Don't edit vertex buffers, its slow. WTF?!

I just want to cry now. 2 weeks and not even a basic sprite system and I am running in circles. I keep getting conflicting information.

Is there anyone, who can difinitavly give me a 'best practices' on how I should be rendering 2D sprites? Id like to move on to other programming issues and stop with the sprite business.

#9 ValMan   Members   -  Reputation: 466

Like
0Likes
Like

Posted 30 September 2007 - 04:10 PM

Quote:

Don't do a bunch of DrawPrimitives either, its slow. Don't edit vertex buffers, its slow. WTF?!

I just want to cry now. 2 weeks and not even a basic sprite system and I am running in circles.


I would consider frustration and lack of information to be the "normal" state of an average programmer. Which is why you keep searching, keep learning and keep trying until you are good enough. And then your next problem is a lot more difficult than the last so the cycle repeats.

Now, about your sprite problem. I might be mistaken, but ID3DXSprite::SetTransform should affect individual sprites. Meaning, you can call SetTransform once, render 3 sprites all with that transform, then call SetTransform again, render 5 more sprites with that transform, etc.

Because you are allowed to specify screen coordinates in the call, you should probably use SetTransform only for rotation (which may include translation if center is off-center), or scaling. You can also animate through changing texture coordinates of the RECT that you pass to ID3DXSprite::Draw.

Perhaps you can give more info on how you set up your rendering (why you are having problems with SetTransform?) It looks like you should be able to make ID3DXSprite work for you, but instead you are trying to roll your own, which is by far not a simple task. For "general" case scenarios, you probably can't create something that is better than ID3DXSprite.

#10 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 September 2007 - 04:17 PM

Quote:
Original post by MasterWorks
There's a million ways to do this but if you want proper speed you MUST use a large dynamic vertex buffer, as has been suggested. It's just not feasible to use a matrix transform for each sprite; you can't afford to be rendering just one sprite per DrawPrimitive. Make a massive dynamic VB and each frame use the next available vertices (locking with NOOVERWRITE) until the VB is full; then start over at the beginning (locking with DISCARD). Since your data is fully dynamic this is really the only choice you have; this allows you to scale, rotate, animation, and reposition sprites however you see fit without regard to frame coherence.

If you can group your sprites by texture and render state you will get even greater speed as you will be able to tell the GPU to render hundreds of sprites at once.


I just re-read what you wrote and read some manuals on this. I have a few questions on implementation.

So I have a giant VB, maybe one that has 250 'quads', worth of sprites, 1000 verts. I have a list of 600 sprites to draw. I then follow these steps:

1. The very first run (per frame) I use Lock.None.
2. I loop through my sprite list, and edit the verts in the VB until I have done all 1000, or 250 quads worth.
3. Unlock the buffer, issue the draw command.
4. Lock with Lock.discard and go to step 2 until I am done with my 600 sprites.

Is that essentially it?

There is another thing you said, I am confused about. You said I can't afford a matrix transform per sprite. The only way I know how to rotate a point (or 4 of them) is to multiply them against a matrix. In fact, I would think that for each quad, you would have to do for each vert, Vector3() * RotationMatrix * ScaleMatrix * TranslationMatrix.

If not, how am I suppose to set up each sprite? Also since I will be doing this stuff manually, should I switch from PositionTextured to TransformedTexturered verts?



#11 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 30 September 2007 - 04:31 PM

Quote:
Perhaps you can give more info on how you set up your rendering (why you are having problems with SetTransform?) It looks like you should be able to make ID3DXSprite work for you, but instead you are trying to roll your own, which is by far not a simple task. For "general" case scenarios, you probably can't create something that is better than ID3DXSprite.


I am trying to make an actio 2D rts game that takes place in Space. So I am going to have a lot of sprites I need for this. Imagine two armies of fighters, cruisers, and capitol ships clashing. There might be 100 of each, and they are shooting bullets and missiels at each other, maybe even lasers if I decided to try and figure out how to make my own procedural textures to use.

After I am done with the basic game logic, which is where each unit goes, what direction it is facing, and what other effects are going, like explosions, Ill have a big list of game objects that Ill need to render.

Ill end up with a bunch of sprites of a few size classes 16x as bullets, 32x as fighters, 64x as bigger ships, and maybe even a few 128x capitol ships. Some of these might not need rotation because a bullet might be round. Most of them will.

All of them will need scaling because I do not know of another way to keep the aspect ratio and position on the screen independent of the resolution. I can't just say Unit 112 has an X location of 600, because it would appear in different relative spots on a system running 800x600 vs 1920x1200. I also have the issue of knowing the screen is in 1920x1200 but the viewport is only 400x400 because the window is shrunk.

Then of course, there is the final location of the sprite. I am not sure if I should try and figure out of each sprite is on screen or just pump them all out and let the render system figure out if it wants to cull it or not.

With all that in mind, I now need to render all the sprites. I was using the sprite class to draw them all using the Draw function. Only when I tried to rotate and scale them, it applied to ALL sprites, not the last one I did the Sprite.draw() with. I was stuck. So this is why I went with my own sprite class.



#12 SiS-Shadowman   Members   -  Reputation: 359

Like
0Likes
Like

Posted 30 September 2007 - 07:44 PM

Quote:
Original post by TheSilverHammer
So I have a giant VB, maybe one that has 250 'quads', worth of sprites, 1000 verts. I have a list of 600 sprites to draw. I then follow these steps:

1. The very first run (per frame) I use Lock.None.
2. I loop through my sprite list, and edit the verts in the VB until I have done all 1000, or 250 quads worth.
3. Unlock the buffer, issue the draw command.
4. Lock with Lock.discard and go to step 2 until I am done with my 600 sprites.

Is that essentially it?


Yep, that's it. There's not much to say about it. One tipp: Make your system dynamic: If the buffer is too small to hold all sprites, that need to be rendered, just adjust it's size: there's no need to call Draw(Indexed)Primitve twice for all the sprites.

That means: You have a function that will eventually call Draw(Indexed)Primitive. But before it can do that, it will check wether the std::vector contains more sprites than the VB can hold. If it does, release the VB and create a VB with the needed size.
After that, just call Draw(Indexed)Primitive

Quote:
I am trying to make an actio 2D rts game that takes place in Space.


I assume that your camera never rotates if it's a 2D game?
This will make things easier concerning the matrix multiplications.

It's something I read from jollyjeffers:
If you set the ViewProjectionmatrix to be an identity matrix, you can scale and reposition your sprites really easy. Just try it out. If you render a quad with a matrix that sets the scale to 1 for all axes, the quad will fill out the entire screen, no matter under wich resolution your app is running. If you render it with 0.5 scale, your quad will just fill 1/4th of the screen.
Positioning becomes as easy as scaling: (-1|1) is the top left point of the screen (I hope I'm not mistaken *g), (1|-1) the bottom right.

#13 Evil Steve   Members   -  Reputation: 1955

Like
1Likes
Like

Posted 30 September 2007 - 09:25 PM

Here's some code from my sprite manager (C++, but the concept is the same):

static const size_t s_nBatchSize = 8192;

static bool SpriteSorter(const ESprite* a, const ESprite* b)
{
// First, sort by depth
if(a->GetDepth() == b->GetDepth())
{
// Then by texture
if(a->GetTexture().Get() == b->GetTexture().Get())
{
// Sort by pointer
return a < b;
}
return a->GetTexture().Get() < b->GetTexture().Get();
}

return (a->GetDepth() >= b->GetDepth());
}

struct SpriteVertex
{
D3DXVECTOR3 vPos;
u32 nColour;
float u1;
float v1;
};

bool PSpriteMgr::Init(PGraphics& gfx)
{
// Create vertex buffer
m_pVB = new PDynamicVertexBuffer(gfx);
HRESULT hResult = m_pVB->Create(s_nBatchSize*4*2, sizeof(SpriteVertex));
if(FAILED(hResult))
{
m_pVB = 0;
std::wstringstream str;
str << L"Failed to create vertex buffer for sprite manager. Error: ";
str << DXGetErrorString(hResult);
PApp::Get().SetError(str.str());
ELog::Get().ErrorFormat(L"SPRT MGR: %s\n", PApp::Get().GetError().c_str());
return false;
}

// Create index buffer
m_pIB = new PIndexBuffer(gfx);
hResult = m_pIB->Create(s_nBatchSize*6);
if(FAILED(hResult))
{
m_pIB = 0;
m_pVB = 0;
std::wstringstream str;
str << L"Failed to create index buffer for sprite manager. Error: ";
str << DXGetErrorString(hResult);
PApp::Get().SetError(str.str());
ELog::Get().ErrorFormat(L"SPRT MGR: %s\n", PApp::Get().GetError().c_str());
return false;
}

// Lock the IB
WORD* pIndex = m_pIB->Lock(0, s_nBatchSize*6);
if(!pIndex)
{
m_pIB = 0;
m_pVB = 0;
std::wstringstream str;
str << L"Failed to lock index buffer for sprite manager. Error: ";
str << DXGetErrorString(hResult);
PApp::Get().SetError(str.str());
ELog::Get().ErrorFormat(L"SPRT MGR: %s\n", PApp::Get().GetError().c_str());
return false;
}

// Fill it with data
for(size_t i=0; i<s_nBatchSize; ++i)
{
*pIndex++ = (WORD)(i*4 + 0);
*pIndex++ = (WORD)(i*4 + 1);
*pIndex++ = (WORD)(i*4 + 2);

*pIndex++ = (WORD)(i*4 + 1);
*pIndex++ = (WORD)(i*4 + 3);
*pIndex++ = (WORD)(i*4 + 2);
}

// Unlock it
m_pIB->Unlock();

// Create vertex declaration
m_pVertexDecl = new PVertexDecl;
m_pVertexDecl->AppendPosition().AppendDiffuse().AppendTextureCoords();
if(!m_pVertexDecl->GetDecl()) // Ensure decl is created
{
m_pVertexDecl = 0;
m_pIB = 0;
m_pVB = 0;
return false;
}

// Init shader
m_pShader = gfx.GetShader(L"Sprite.vsh", PVertexShader::VS_2_0);
if(!m_pShader)
{
m_pVertexDecl = 0;
m_pIB = 0;
m_pVB = 0;
return false;
}

// Done
return true;
}

//============================================================================

bool PSpriteMgr::Render(PGraphics& gfx)
{
// Merge pending sprites into main list
for(SpriteVecIter it=m_pendingSprites.begin(); it!=m_pendingSprites.end(); ++it)
m_sprites.push_back(*it);
m_pendingSprites.clear();

// Sort sprite list
std::sort(m_sprites.begin(), m_sprites.end(), SpriteSorter);

// No sprites means early out
if(m_sprites.empty())
return true;

// Apply device state
m_pShader->SetMatrix("mWorldViewProj", EMatrix().SetToOrthogonal(0.0f, 0.0f,
(float)gfx.GetWidth(), (float)gfx.GetHeight(), 0.0f, 1000.0f));
gfx.SetIndices(m_pIB);
gfx.SetVertexDecl(m_pVertexDecl);
gfx.SetVertexShader(m_pShader);
gfx.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
gfx.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
gfx.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
gfx.SetRenderState(D3DRS_LIGHTING, FALSE);

// Render sprites
u32 nRemain = (u32)m_sprites.size();
SpriteListIter itSprite = m_sprites.begin();
ESprite* pLast = *itSprite;
PTexture* pLastTexture = (PTexture*)pLast->GetTexture().Get();
LPDIRECT3DTEXTURE9 pLastD3DTexture = NULL;

while(nRemain > 0)
{
// Lock as much of the VB as possible
Assert(m_pVB, "VB not created");
u32 nSpritesToLock = min(nRemain, (u32)s_nBatchSize);
u32 nRenderOffset=0;
SpriteVertex* pVertex = (SpriteVertex*)m_pVB->Lock(nSpritesToLock*4, nRenderOffset);
if(!pVertex)
return false;

// Fill the VB and unlock it
SpriteListIter itCurr = itSprite;
for(u32 i=0; i<nSpritesToLock; ++i)
{
PTexture* pTexture = (PTexture*)(*itCurr)->GetTexture().Get();
EVector2 vPos = (*itCurr)->GetPos() + (*itCurr)->GetOrigin();
float fDepth = (float)(*itCurr)->GetDepth() / 65535.0f;
EVector2 vSize = EVector2((float)pTexture->GetWidth(), (float)pTexture->GetHeight()) * (*itCurr)->GetScale();
u32 nColourTL = (*itCurr)->GetColourTL().ToARGB();
u32 nColourTR = (*itCurr)->GetColourTR().ToARGB();
u32 nColourBL = (*itCurr)->GetColourBL().ToARGB();
u32 nColourBR = (*itCurr)->GetColourBR().ToARGB();
++itCurr;

pVertex->vPos = D3DXVECTOR3(vPos.x - 0.5f, vPos.y - 0.5f, fDepth);
pVertex->nColour = nColourTL;
pVertex->u1 = pTexture->GetTUMin();
pVertex->v1 = pTexture->GetTVMin();
pVertex++;
pVertex->vPos = D3DXVECTOR3(vPos.x+vSize.x - 0.5f, vPos.y - 0.5f, fDepth);
pVertex->nColour = nColourTR;
pVertex->u1 = pTexture->GetTUMax();
pVertex->v1 = pTexture->GetTVMin();
pVertex++;
pVertex->vPos = D3DXVECTOR3(vPos.x - 0.5f, vPos.y+vSize.y - 0.5f, fDepth);
pVertex->nColour = nColourBL;
pVertex->u1 = pTexture->GetTUMin();
pVertex->v1 = pTexture->GetTVMax();
pVertex++;
pVertex->vPos = D3DXVECTOR3(vPos.x+vSize.x - 0.5f, vPos.y+vSize.y - 0.5f, fDepth);
pVertex->nColour = nColourBR;
pVertex->u1 = pTexture->GetTUMax();
pVertex->v1 = pTexture->GetTVMax();
pVertex++;
}
m_pVB->Unlock();

// Submit batches
size_t nStart = 0;
size_t nCount = 0;
gfx.SetStreamSource(m_pVB, nRenderOffset);

while(nSpritesToLock > 0)
{
ESprite* pCurr = *itSprite++;
--nSpritesToLock;

// Need to flush?
if(pLastD3DTexture != ((PTexture*)pCurr->GetTexture().Get())->GetTexture())
{
if(nCount > 0)
{
gfx.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, (UINT)(nStart*4),
(UINT)(nCount*4), (UINT)(nStart*6), (UINT)(nCount*2));
nStart += nCount;
}
nCount = 1;
pLast = pCurr;
pLastTexture = (PTexture*)pLast->GetTexture().Get();
pLastD3DTexture = pLastTexture->GetTexture();
gfx.SetTexture(0, pLastTexture);
}
else
++nCount;
}

// Render last batch
gfx.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, (UINT)(nStart*4),
(UINT)(nCount*4), (UINT)(nStart*6), (UINT)(nCount*2));

nRemain -= min(nRemain, (u32)s_nBatchSize);
}

return true;
}



Even if that doesn't make much sense, the comments should give you a rough idea of what's going on. Most of the types there are my own custom classes, but all you need to care about is that it uses a static index buffer (Filled once at init time) and a dynamic vertex buffer, which is filled every frame.

Hope this helps.

#14 MasterWorks   Members   -  Reputation: 496

Like
0Likes
Like

Posted 01 October 2007 - 12:31 AM

Quote:
Original post by TheSilverHammer
So I have a giant VB, maybe one that has 250 'quads', worth of sprites, 1000 verts. I have a list of 600 sprites to draw.

Others have already given you some good feedback on this, but my advice would be make the 'giant VB' more giant. You want to be using it over multiple frames; by reusing the same vertices within a frame you could easily cause a pipeline stall. You can use the NOOVERWRITE flag to clue the driver in that you are doing this, allowing it to continue working on the old data while you supply the new (upcoming sprites) data. I would recommend having something like 32000 vertices and you only have to 'start from the beginning' of it every 10th frame or whatever.

Quote:
There is another thing you said, I am confused about. You said I can't afford a matrix transform per sprite. The only way I know how to rotate a point (or 4 of them) is to multiply them against a matrix.

This is of course fine; I am referring to using the device's world matrix which can be used to transform a group of points (such as a 3D mesh). By all means determine your final sprite positions using matrices on the CPU, but doing the transformation math on the GPU is difficult for a sprite system as you describe.

I recently completed a Direct3D space-shooter type game which sounds very similar to your project so I hope I am not way off base here, but the idea is that you can't afford to set a transformation, draw 2 triangles, set the next transformation, draw 2 more triangles, etc. Drawing each sprite by itself like this is SLOW. The overhead for draw calls is such that if you're going to draw 2 triangles, you might as well draw 100 triangles for 5% extra cost (hypothetical numbers but you get the idea).

On a related subject, this is the same reason that it's very important to use texture atlases; you can use (per-vertex) UV coordinates to identify what the sprite 'looks like' instead of needing a device.SetTexture (which is slow itself AND breaks the batch). You don't need to go overboard (i.e. combining completely irrelevant sprites) but I would recommend you try to fill up 512x512 or 1024x1024 sized textures instead of using many smaller ones. Also note that if you're careful with your floating point math you can use these texture atlases to your advantage, creating 'internal' sprites that don't necessarily conform to the power-of-2 texture size guideline.

#15 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 01 October 2007 - 03:57 AM

Masterworks, thats exactly how I do it. I have one 512x texture and it has all the little textures inside it. The way I have it now (which Ill have to change) is that after my texture page is loaded, I have a sub-texture set which is my 'sprite texture'. The sub texture is just a rectangle of UVs for each of the sub textures. I then made a list of quads with the right UV coordinates.

However, you say chaning texture pages is expensive? ie: Calling SetTexture more then once? Right now I only have one texture page, and making the changes suggested here for just one page is a minor change from what I have. If I need to use more then one page, Ill need a big architectural change (before I was just using SetTexture for every quad, but if I shouldn't do that ...) Ill suppose Ill need to create sprite lists per texture page and render them in seperate batches.


Is there a matrix transform method to apply against a list of verts opposed to one vert at a time? I figure that will be more efficent then doing 4 individual matrix multiplies for each quad.

As far as camera transformations, those are bad? My main camera was going to move around over the playfield, but should I just transpose my sprites based upon the camera location instead?

On Vertexbuffer size, I heard there was some problems if it was too big or that you should never call DrawPrimitive() with more then a certain number of them at once. Is this true, how much is too much?

You said you just made a 2d space game. Are the sprites you used in the public domain? I haven't been able to find any space game sprites. So far I am using the 1942 sprites from the standard sprite lib. Id even be willing to buy a 'starter pack' of game artwork, but I can't find any for sale.

#16 Evil Steve   Members   -  Reputation: 1955

Like
0Likes
Like

Posted 01 October 2007 - 04:20 AM

Quote:
Original post by TheSilverHammer
However, you say chaning texture pages is expensive? ie: Calling SetTexture more then once? Right now I only have one texture page, and making the changes suggested here for just one page is a minor change from what I have. If I need to use more then one page, Ill need a big architectural change (before I was just using SetTexture for every quad, but if I shouldn't do that ...) Ill suppose Ill need to create sprite lists per texture page and render them in seperate batches.
It's not hugely expensive, but the main problem is that it breaks your batching - You'll need to submit two Draw calls to use two textures.

Quote:
Original post by TheSilverHammer
Is there a matrix transform method to apply against a list of verts opposed to one vert at a time? I figure that will be more efficent then doing 4 individual matrix multiplies for each quad.
Is there a reason you don't use an orthogonal projection matrix, and let D3D do the vertex transform? That way you can take advantage of hardware vertex processing.

m_pRightAs far as camera transformations, those are bad? My main camera was going to move around over the playfield, but should I just transpose my sprites based upon the camera location instead?[/quote]I'd recommend using an orthogonal projection matrix. All transforms are effectively free, since they should be happening on the graphics card anyway.

Quote:
Original post by TheSilverHammer
On Vertexbuffer size, I heard there was some problems if it was too big or that you should never call DrawPrimitive() with more then a certain number of them at once. Is this true, how much is too much?
I wouldn't make a dynamic vertex buffer over 4 or 8MB or so. I use 8192 verts just because that's what ID3DXSprite (The C++ version of the D3DX sprite) uses.


EDIT: Fixed by broken copy & paste :P

[Edited by - Evil Steve on October 1, 2007 12:20:10 PM]

#17 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 01 October 2007 - 06:37 AM

Quote:
Is there a reason you don't use an orthogonal projection matrix, and let D3D do the vertex transform? That way you can take advantage of hardware vertex processing.


Errr what? I am drwaing 500 sprites that each have thier own rotation, scale and location. How do you define a matrix for 'these 4 verts' without a seperate draw command for each quad? You just have confused me.



#18 Evil Steve   Members   -  Reputation: 1955

Like
0Likes
Like

Posted 01 October 2007 - 06:44 AM

Quote:
Original post by TheSilverHammer
Quote:
Is there a reason you don't use an orthogonal projection matrix, and let D3D do the vertex transform? That way you can take advantage of hardware vertex processing.


Errr what? I am drwaing 500 sprites that each have thier own rotation, scale and location. How do you define a matrix for 'these 4 verts' without a seperate draw command for each quad? You just have confused me.
You're right, I'm an idiot [smile] Sorry, I totally forgot about rotation and scale...

You might be best off using a vertex shader for the rotation, although I'm not sure how easy that would be. Failing that, doing transforms on a per-vector basis is likely to be less efficient than transforming a lot at once, but only really because of the function call overhead. I wouldn't worry about it too much - don't go out of your way to get all the vectors to transform in one array, unless your data comes in that way anyway.

#19 TheSilverHammer   Members   -  Reputation: 122

Like
0Likes
Like

Posted 01 October 2007 - 09:30 AM

I have been googling around to see if there is a function that applies a matrix against an array of verts instead of doing one vert at a time. Is there such a function or will I need to do it one at a time?

In the case I need to do it manually, I was thinking of this method, which may be way overboard.

Each sprite would have a reference to a matrix from a list of matrixes(whats the plural?) when each object updates its position before rendering starts (ie: the game object the sprite is attached to) it just updates the reference in this array.

Once I get down to the busniess of drawing, Ill just have a for-loop match the matrix in the index to the set of verts for the quad I am setting up. This way all the matrixes and verts will be in a solid block of memory and the caches should be able to predict the next memory Ill need and run much faster.

I do not know if this level of optimization is wasted on C# or not or if the yeild will be so small it will not be worth the extra code complexity.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS