• Advertisement
Sign in to follow this  

[C++, DX9] Combining Vertex and Index buffers

This topic is 2772 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am learning how to use Vertex and Index buffers with shaders. I am still very new the concept and I am working on a big project course atm.

Basically I have a bunch of tile grid that is 20x15 in size. The tiles depending on what level we load is going to support 1 of 3 different textures. The project is a basic tower defense demonstration. So the tile will either have a Path Straight piece, a Path corner piece, or a plain grass/ground piece.

The corner and straight piece will need to be rotated depending obviously which way the level is designed.

Here is the thing. I have all of this working and displaying the way I want it. (minus the rotation at the moment but I know how I am going to do that). Each tile has their own world matrix to handle the position and rotation of the tile.

Right now I am drawing each of the 300 tiles one by one and because of this my FPS has dropped significantly. I know this is not the way I need to do it, but I am completely lost how I should approach revising this the easiest way. I've heard that I can do everything from combining objects together right before drawing, to building the vertex buffers and index buffers together... I am looking for something that's going to be much more efficient than my approach, but at the same time is simple enough for me to understand, realize again that I am very new to this concept and still learning DirectX.

Here is my Tile class (which is also my grid class) This tile class has a bunch of static methods to work 20x15 grid aspect, as well as it also being used to create Tile objects to hold each tile's properties (world matrix, pointer to texture, etc..)

Here is my header file:

//Tile.h - Used for holding information about each tile.

#ifndef TILE_H
#define TILE_H

#include <d3dx9.h>
#include "Vertex.h"
#include "DXCamera.h"
#include "DXProjection.h"

enum TextureType
{
//towers
TT_TileGrass,
TT_TilePathStraight,
TT_TilePathCorner,

};

class Tile
{
public:

//Constructors
Tile(int id);
~Tile();

bool occupied;

D3DXVECTOR3* getPos();
D3DXMATRIX* getWorld();
IDirect3DTexture9* getCurrentTexture();


int getTextureType();
int getRotationType();
void setTexture(int textureType);

private:

void generateWorldMatrix();

D3DXVECTOR3 m_xv3Pos;
D3DXMATRIX m_m4x4World;

IDirect3DTexture9* mp_tCurrentTexture; //pointer to the current texture of that tile

int m_iTextureType;
int m_iRotationType;

int m_iTileID;

//static methods and variables
public:
static void initialize();
static void generateGrid();
static void release();

static void onLostDevice();
static void onResetDevice();

static void drawGrid();

static bool SETUP_VARIABLE;
static bool SETUP_FUNCTION();

private:
static vector<Tile*> m_vTileList;

//static method calls
static void buildIndexBuffer();
static void buildVertexBuffer();
static void buildFX();
static void buildTileBuffers();

//Buffer Member Variables
static IDirect3DIndexBuffer9* m_IndexBuffer;
static IDirect3DVertexBuffer9* m_VertexBuffer;

//FX
static ID3DXEffect* m_sFX;

//Handles
static D3DXHANDLE m_hTech;
static D3DXHANDLE m_hWVP;
static D3DXHANDLE m_hWorldInvTrans;
static D3DXHANDLE m_hLightVecW;
static D3DXHANDLE m_hDiffuseMtrl;
static D3DXHANDLE m_hDiffuseLight;
static D3DXHANDLE m_hAmbientMtrl;
static D3DXHANDLE m_hAmbientLight;
static D3DXHANDLE m_hSpecularMtrl;
static D3DXHANDLE m_hSpecularLight;
static D3DXHANDLE m_hSpecularPower;
static D3DXHANDLE m_hEyePos;
static D3DXHANDLE m_hWorld;
static D3DXHANDLE m_hTex;

//Lighting
static D3DXVECTOR3 m_xv3LightVecW;
static D3DXCOLOR m_xcAmbientMtrl;
static D3DXCOLOR m_xcAmbientLight;
static D3DXCOLOR m_xcDiffuseMtrl;
static D3DXCOLOR m_xcDiffuseLight;
static D3DXCOLOR m_xcSpecularMtrl;
static D3DXCOLOR m_xcSpecularLight;
static float m_fSpecularPower;

//Textures
static IDirect3DTexture9* m_tGrassTile;
static IDirect3DTexture9* m_tPathCornerTile;
static IDirect3DTexture9* m_tPathStraightTile;

};
#endif



Here is my CPP file

#include "Tile.h"
#include "d3dUtil.h"

IDirect3DIndexBuffer9* Tile::m_IndexBuffer = NULL;
IDirect3DVertexBuffer9* Tile::m_VertexBuffer = NULL;

ID3DXEffect* Tile::m_sFX = NULL;
D3DXHANDLE Tile::m_hTech = NULL;
D3DXHANDLE Tile::m_hWVP = NULL;
D3DXHANDLE Tile::m_hWorldInvTrans = NULL;
D3DXHANDLE Tile::m_hLightVecW = NULL;
D3DXHANDLE Tile::m_hDiffuseMtrl = NULL;
D3DXHANDLE Tile::m_hDiffuseLight = NULL;
D3DXHANDLE Tile::m_hAmbientMtrl = NULL;
D3DXHANDLE Tile::m_hAmbientLight = NULL;
D3DXHANDLE Tile::m_hSpecularMtrl = NULL;
D3DXHANDLE Tile::m_hSpecularLight = NULL;
D3DXHANDLE Tile::m_hSpecularPower = NULL;
D3DXHANDLE Tile::m_hEyePos = NULL;
D3DXHANDLE Tile::m_hWorld = NULL;
D3DXHANDLE Tile::m_hTex = NULL;

//Lighting
D3DXVECTOR3 Tile::m_xv3LightVecW = D3DXVECTOR3(0, 0, 0);
D3DXCOLOR Tile::m_xcAmbientMtrl = D3DXCOLOR();
D3DXCOLOR Tile::m_xcAmbientLight = D3DXCOLOR();
D3DXCOLOR Tile::m_xcDiffuseMtrl = D3DXCOLOR();
D3DXCOLOR Tile::m_xcDiffuseLight = D3DXCOLOR();
D3DXCOLOR Tile::m_xcSpecularMtrl = D3DXCOLOR();
D3DXCOLOR Tile::m_xcSpecularLight = D3DXCOLOR();
float Tile::m_fSpecularPower = D3DXCOLOR();

//Textures
IDirect3DTexture9* Tile::m_tGrassTile = NULL;
IDirect3DTexture9* Tile::m_tPathCornerTile = NULL;
IDirect3DTexture9* Tile::m_tPathStraightTile = NULL;

vector<Tile*> Tile::m_vTileList;
bool Tile::SETUP_VARIABLE = Tile::SETUP_FUNCTION();

bool Tile::SETUP_FUNCTION()
{
Tile::m_vTileList.push_back(NULL);
Tile::m_vTileList.pop_back();
return true;
}


/*****************
** Constructors **
*****************/

Tile::Tile(int id)
{
m_xv3Pos.x = 0;
m_xv3Pos.y = 0;
m_xv3Pos.z = 0;

m_iTileID = id;

Tile::m_vTileList.push_back(this);

int a = rand() % 3;

setTexture(a);
}

Tile::~Tile()
{

}

void Tile::setTexture(int textureType)
{
//set the current texture pointer for a tile object to one of the 3 defined textures
switch(textureType)
{
case TT_TileGrass:
mp_tCurrentTexture = Tile::m_tGrassTile;
break;

case TT_TilePathStraight:
mp_tCurrentTexture = Tile::m_tPathStraightTile;
break;

case TT_TilePathCorner:
mp_tCurrentTexture = Tile::m_tPathCornerTile;
break;
}
}

D3DXVECTOR3* Tile::getPos()
{
return &m_xv3Pos;
}

D3DXMATRIX* Tile::getWorld()
{
return &m_m4x4World;
}

IDirect3DTexture9* Tile::getCurrentTexture()
{
return mp_tCurrentTexture;
}

int Tile::getTextureType()
{
return m_iTextureType;
}

int Tile::getRotationType()
{
return m_iRotationType;
}

void Tile::generateWorldMatrix()
{
D3DXMATRIX trans;

D3DXMatrixTranslation(&trans, m_xv3Pos.x, m_xv3Pos.y, m_xv3Pos.z);

m_m4x4World = trans;
int a = 0;
}

/*******************
** Static Methods **
*******************/

void Tile::initialize()
{
D3DXCreateTextureFromFile(gd3dDevice, "..//Textures//Tile_Grass.dds", &Tile::m_tGrassTile);
D3DXCreateTextureFromFile(gd3dDevice, "..//Textures//Tile_PathCorner.dds", &Tile::m_tPathCornerTile);
D3DXCreateTextureFromFile(gd3dDevice, "..//Textures//Tile_PathStraight.dds", &Tile::m_tPathStraightTile);

Tile::m_xv3LightVecW = D3DXVECTOR3(0.0, 0.0f, -1.0f);
Tile::m_xcDiffuseMtrl = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
Tile::m_xcDiffuseLight = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
Tile::m_xcAmbientMtrl = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
Tile::m_xcAmbientLight = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
Tile::m_xcSpecularMtrl = D3DXCOLOR(0.8f, 0.8f, 0.8f, 1.0f);
Tile::m_xcSpecularLight = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);
Tile::m_fSpecularPower = 8.0f;

Tile::buildTileBuffers();
Tile::buildFX();

}

void Tile::generateGrid()
{
float x = -2432.0f;
float z = 1792.0f;
int count = 0;

for(int i = 0; i < 15; i++)
{
for(int j = 0; j < 20; j++)
{
Tile* temp = new Tile(count);
temp->m_xv3Pos.x = x;
temp->m_xv3Pos.z = z;

x += 256.0f;

temp->generateWorldMatrix();

count++;
}

z -= 256.0f; //decriment Z
x = -2432.0f; //reset X
}

GS::wout << "Size: " << Tile::m_vTileList.size() << endl;
}

void Tile::release()
{
ReleaseCOM(Tile::m_IndexBuffer);
ReleaseCOM(Tile::m_VertexBuffer);

ReleaseCOM(Tile::m_sFX);
ReleaseCOM(Tile::m_tGrassTile);
ReleaseCOM(Tile::m_tPathCornerTile);
ReleaseCOM(Tile::m_tPathStraightTile);

for(int i = 0; i < Tile::m_vTileList.size(); i++)
{
delete Tile::m_vTileList;
}
}

void Tile::onLostDevice()
{
Tile::m_sFX->OnLostDevice();
}

void Tile::onResetDevice()
{
Tile::m_sFX->OnResetDevice();
}


void Tile::drawGrid()
{
/********************
** START TILE DRAW **
********************/

gd3dDevice->SetStreamSource(0, Tile::m_VertexBuffer, 0, sizeof(VertexPNT));
gd3dDevice->SetIndices(Tile::m_IndexBuffer);
gd3dDevice->SetVertexDeclaration(VertexPNT::Decl);

//gd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
gd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
gd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

HR(m_sFX->SetTechnique(m_hTech));

Tile::m_sFX->SetValue(m_hLightVecW, &m_xv3LightVecW, sizeof(D3DXVECTOR3));
Tile::m_sFX->SetValue(m_hDiffuseMtrl, &m_xcDiffuseMtrl, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hDiffuseLight, &m_xcDiffuseLight, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hAmbientMtrl, &m_xcAmbientMtrl, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hAmbientLight, &m_xcAmbientLight, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hSpecularLight, &m_xcSpecularLight, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hSpecularMtrl, &m_xcSpecularMtrl, sizeof(D3DXCOLOR));
Tile::m_sFX->SetFloat(m_hSpecularPower, m_fSpecularPower);

for(int j = 0; j < Tile::m_vTileList.size(); j++)
{
D3DXMATRIX wvp = (*Tile::m_vTileList[j]->getWorld()) * DXCI->getView() * DXPI->getProj();
Tile::m_sFX->SetMatrix(Tile::m_hWVP, &wvp);
D3DXMATRIX worldInvTrans;
D3DXMatrixInverse(&worldInvTrans, 0, Tile::m_vTileList[j]->getWorld());
D3DXMatrixTranspose(&worldInvTrans, &worldInvTrans);
Tile::m_sFX->SetMatrix(m_hWorldInvTrans, &worldInvTrans);
Tile::m_sFX->SetTexture(m_hTex, Tile::m_vTileList[j]->getCurrentTexture());
Tile::m_sFX->SetMatrix(m_hWorld, Tile::m_vTileList[j]->getWorld());

UINT numPasses = 0;
Tile::m_sFX->Begin(&numPasses, 0);
for(UINT i = 0; i < numPasses; ++i)
{
Tile::m_sFX->BeginPass(i);
gd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12);
Tile::m_sFX->EndPass();
}
Tile::m_sFX->End();
}
}

void Tile::buildFX()
{
ID3DXBuffer* errors = 0;
D3DXCreateEffectFromFile(gd3dDevice, "..//FX//Tile.fx", 0, 0, D3DXSHADER_DEBUG, 0, &m_sFX, &errors);

if(errors) {
MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);
}

//m_hTech = m_sFX->GetTechniqueByName("TileTech");
Tile::m_hTech = Tile::m_sFX->GetTechniqueByName("DirLightTexTech");
Tile::m_hWVP = Tile::m_sFX->GetParameterByName(0, "gWVP");
Tile::m_hWorldInvTrans = Tile::m_sFX->GetParameterByName(0, "gWorldInvTrans");
Tile::m_hLightVecW = Tile::m_sFX->GetParameterByName(0, "gLightVecW");
Tile::m_hDiffuseMtrl = Tile::m_sFX->GetParameterByName(0, "gDiffuseMtrl");
Tile::m_hDiffuseLight = Tile::m_sFX->GetParameterByName(0, "gDiffuseLight");
Tile::m_hAmbientMtrl = Tile::m_sFX->GetParameterByName(0, "gAmbientMtrl");
Tile::m_hAmbientLight = Tile::m_sFX->GetParameterByName(0, "gAmbientLight");
Tile::m_hSpecularMtrl = Tile::m_sFX->GetParameterByName(0, "gSpecularMtrl");
Tile::m_hSpecularLight = Tile::m_sFX->GetParameterByName(0, "gSpecularLight");
Tile::m_hSpecularPower = Tile::m_sFX->GetParameterByName(0, "gSpecularPower");
Tile::m_hEyePos = Tile::m_sFX->GetParameterByName(0, "gEyePosW");
Tile::m_hWorld = Tile::m_sFX->GetParameterByName(0, "gWorld");
Tile::m_hTex = Tile::m_sFX->GetParameterByName(0, "gTex");

}

void Tile::buildTileBuffers()
{
// Create the vertex buffer.

gd3dDevice->CreateVertexBuffer(24 * sizeof(VertexPNT), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &Tile::m_VertexBuffer, 0);

// Write box vertices to the vertex buffer.
VertexPNT* v = 0;
Tile::m_VertexBuffer->Lock(0, 0, (void**)&v, 0);

float halfSize = 256.0f / 2.0f;

v[0] = VertexPNT(-halfSize, halfSize, -halfSize, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[1] = VertexPNT(-halfSize, halfSize, halfSize, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
v[2] = VertexPNT(halfSize, halfSize, halfSize, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
v[3] = VertexPNT(halfSize, halfSize, -halfSize, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);

Tile::m_VertexBuffer->Unlock();

// Create the vertex buffer.
gd3dDevice->CreateIndexBuffer(36 * sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &Tile::m_IndexBuffer, 0);

// Write box indices to the index buffer.
WORD* i = 0;
Tile::m_IndexBuffer->Lock(0, 0, (void**)&i, 0);

i[0] = 0; i[1] = 1; i[2] = 2;
i[3] = 0; i[4] = 2; i[5] = 3;


Tile::m_IndexBuffer->Unlock();

}



Tile::Initialize() is called at the beginning to setup the buffers and load textures and whatnot.

Tile::generateGrid(); is then call immediately after the initialize to create the grid.

then each frame I call Tile::drawGrid(). Everything else happens internally.

I am planning on talking to my teacher about it tomorrow to see his suggestions on how to make this more efficient, but I figured while I had 24 hours to see if someone had an easy way of explaining it.

If you need anymore information then please let me know!

Thanks
GFT

Share this post


Link to post
Share on other sites
Advertisement
Hello!

Instead of drawing each tile one at a time, which could require hundreds of DrawPrimitive calls, you can indeed batch all the vertices together into a vertex buffer and then issue one single Draw*Primitive call.

The way you are drawing now is inefficient because each DrawPrimitive call only sends 4 (or 6 if you didn't use an index buffer) vertices to the graphics card at a time. The card doesn't batch these together and then draw them all at once ... instead it draws them immediately, so you need to batch them together in your application before sending them.

In order for this to work well, you need to change as little of the vertex data as possible each frame...

Do you lock/unlock the vertex buffer and fiddle with (x,y,z) positions to move the player around the scene or to change the textures on a tile each frame? If so, consider using world and view transforms to move around the scene instead.

One interesting approach you might try is to seperate the u,v coordinates into a second dynamic vertex stream - and keep the data that doesn't need to change each frame (like the tile grid's x,y,z coordinates and normals) in a separate static vertex buffer.

If you don't already use index buffers, I recommend looking into them ... they are a cheap way of drawing polygons that share vertices (like a textured quad, which shares 2 vertices) and are relatively easy to construct.

The pool you select will have a lot of impact on performance too ... for the static vertex buffer and static index buffer I recommend D3DPOOL_MANAGED.


Share this post


Link to post
Share on other sites
Sounds like what you are after is instancing. The idea is that you would have a vertex and an index buffer with all of your tile vertices and indices in it and assign it to one input stream. You then create a buffer that holds the transformations for each individual tile and assign that to a second input stream. By setting the instance data (The transformations) to only be changed every 4 vertices you can use your vertex shader to access the same instance data for all 4 vertices for each tile. This effectively allows you to draw all of your tiles in a single call whilst allowing each discrete tile mesh to access unique instance data.

I believe D3D11 has specific creation flags for input data to allow you to distinguish between vertex and instance data. But you're using Direct3D 9 so I don't think that will be available to you so you'd have to set it up manually.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dom_152
Sounds like what you are after is instancing. The idea is that you would have a vertex and an index buffer with all of your tile vertices and indices in it and assign it to one input stream. You then create a buffer that holds the transformations for each individual tile and assign that to a second input stream. By setting the instance data (The transformations) to only be changed every 4 vertices you can use your vertex shader to access the same instance data for all 4 vertices for each tile. This effectively allows you to draw all of your tiles in a single call whilst allowing each discrete tile mesh to access unique instance data.

I believe D3D11 has specific creation flags for input data to allow you to distinguish between vertex and instance data. But you're using Direct3D 9 so I don't think that will be available to you so you'd have to set it up manually.


I think I understand what you are talking about, I've also had another person explain something similar, but I am still confused on the approach to do it.

I don't get how I take the individual tiles and actually go through the process of putting them all in a single vertex and index buffer, while still allowing me to dynamically say which tile has which rotation and which tile has which texture...

Share this post


Link to post
Share on other sites
Well you would have your 4 vertices and 6 indices that represent a single tile mesh. It's simply a case of creating buffers large enough to hold n * 4 vertices and n * 6 indices where n is the number of tiles. You use SetSource (To stream 0) and SetIndices to set them on the device.

You construct the instance data in much the same way. Imagine you have a simple struct like this:


struct TileInstanceData
{
// The tile's transformation matrix
// You put in the rotation and translation and any other transformations
// you want a tile to have in here
float _11, _21, _31, _41;
float _12, _22, _32, _42;
float _13, _23, _33, _43;
float _14, _24, _34, _44;

// Which texture will this tile use?
float TextureID;
};



You create a matching vertex deceleration so Direct3D can map that struct to your GPU but make sure you instruct Direct3D to place the data in stream 1 (It's part of the vertex declaration process). You then create a vertex buffer large enough to hold n * sizeof(TileInstanceData) and fill it up with filled in copies of that struct; one for each tile. You use SetStreamSource again but this time set it to stream 1.

You need to tell Direct3D how many instances you are drawing and how many sets of instance data each vertex will use. This is done using SetStreamSourceFreq:


d3DDevice->SetStreamSourceFreq(0, D3DSTREAMSOURCE_INDEXEDDATA | numberOfInstances);
d3DDevice->SetStreamSourceFreq(1, D3DSTREAMSOURCE_INSTANCEDATA | 1);



Now you can use a single DrawIndexedPrimitives call to draw everything. You use your vertex shader to access the instance data the same way you access normal vertex data. The usage indices for your instance data will depend on your vertex declaration.

You find a (probably better) explanation on MSDN here.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dom_152
// Which texture will this tile use?
float TextureID;

How would you go about utilizing this value in the shaders? If I understand the way Direct3D works, this would also limit you to 8 (?) textures per draw call, wouldn't it?

Share this post


Link to post
Share on other sites
Quote:
Original post by GregMichael
What are the rotations for each tile...are they arbitary or in 90 degree steps ?


90 degree steps

Share this post


Link to post
Share on other sites
Quote:
Original post by AnthonyB
Quote:
Original post by Dom_152
// Which texture will this tile use?
float TextureID;

How would you go about utilizing this value in the shaders? If I understand the way Direct3D works, this would also limit you to 8 (?) textures per draw call, wouldn't it?


You bind it using the TEXCOORDn semantic where n matches the usage index you designated for it in your vertex declaration. I think the 8 texture limit only exists if you use Direct3D's built in texture stages. You could have an array of Texture2D variables to have more than 8 textures in your pixel shader should your GPU be able to handle it (I believe the upper limit of samplers is 16 in SM 3.0).

Share this post


Link to post
Share on other sites
Quote:
Original post by Dom_152
Quote:
Original post by AnthonyB
Quote:
Original post by Dom_152
// Which texture will this tile use?
float TextureID;

How would you go about utilizing this value in the shaders? If I understand the way Direct3D works, this would also limit you to 8 (?) textures per draw call, wouldn't it?


You bind it using the TEXCOORDn semantic where n matches the usage index you designated for it in your vertex declaration.

In that case, wouldn't the value(s) need to be a series of texture coordinates and not just IDs?

Quote:
I think the 8 texture limit only exists if you use Direct3D's built in texture stages. You could have an array of Texture2D variables to have more than 8 textures in your pixel shader should your GPU be able to handle it (I believe the upper limit of samplers is 16 in SM 3.0).

I didn't remember that the shaders have their own limit on samplers. Shader model 2.0 has a limit of 8 still, but you were right, 3.0/4.0 is 16. Still, this is fairly limiting if you want to use this method in a 2D-only environment with lots of random elements. (Which is why I was asking.)

Oh, and sorry if it seems like I'm somewhat derailing the thread.

Share this post


Link to post
Share on other sites
I appreciate the responses guys!

I did find out one thing that makes my FPS increase significantly. I got rid of the multiple BeginPass and EndPass calls for each tile.

I found out while doing some model imports from my book that there is a handy little method in an ID3DXEffect called "CommitChanges" that allows me to have a single BeginPass and EndPass for all of my tiles. However, I am still calling gd3dDevice->DrawIndexedPrimitive(...) for each tile and I know this should be able to optimized as well, but at least I've got another method of ID3DXEffect under my belt!

So my new drawing method for my tile class now looks like this:


void Tile::drawGrid()
{

/********************
** START TILE DRAW **
********************/

gd3dDevice->SetStreamSource(0, Tile::m_VertexBuffer, 0, sizeof(VertexPNT));
gd3dDevice->SetIndices(Tile::m_IndexBuffer);
gd3dDevice->SetVertexDeclaration(VertexPNT::Decl);

//gd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
gd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
gd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

HR(m_sFX->SetTechnique(m_hTech));

Tile::m_sFX->SetValue(m_hLightVecW, &m_xv3LightVecW, sizeof(D3DXVECTOR3));
Tile::m_sFX->SetValue(m_hDiffuseMtrl, &m_xcDiffuseMtrl, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hDiffuseLight, &m_xcDiffuseLight, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hAmbientMtrl, &m_xcAmbientMtrl, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hAmbientLight, &m_xcAmbientLight, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hSpecularLight, &m_xcSpecularLight, sizeof(D3DXCOLOR));
Tile::m_sFX->SetValue(m_hSpecularMtrl, &m_xcSpecularMtrl, sizeof(D3DXCOLOR));
Tile::m_sFX->SetFloat(m_hSpecularPower, m_fSpecularPower);

UINT numPasses = 0;
Tile::m_sFX->Begin(&numPasses, 0);
for(UINT i = 0; i < numPasses; ++i)
{
Tile::m_sFX->BeginPass(i);

for(int j = 0; j < Tile::m_vTileList.size(); j++)
{
D3DXMATRIX wvp = (*Tile::m_vTileList[j]->getWorld()) * DXCI->getView() * DXPI->getProj();
Tile::m_sFX->SetMatrix(Tile::m_hWVP, &wvp);
D3DXMATRIX worldInvTrans;
D3DXMatrixInverse(&worldInvTrans, 0, Tile::m_vTileList[j]->getWorld());
D3DXMatrixTranspose(&worldInvTrans, &worldInvTrans);
Tile::m_sFX->SetMatrix(m_hWorldInvTrans, &worldInvTrans);
Tile::m_sFX->SetTexture(m_hTex, Tile::m_vTileList[j]->getCurrentTexture());
Tile::m_sFX->SetMatrix(m_hWorld, Tile::m_vTileList[j]->getWorld());

gd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12);
Tile::m_sFX->CommitChanges();
}
Tile::m_sFX->EndPass();
}
Tile::m_sFX->End();

}

Share this post


Link to post
Share on other sites
Quote:
Original post by AnthonyB
Quote:
Original post by Dom_152
Quote:
Original post by AnthonyB
Quote:
Original post by Dom_152
// Which texture will this tile use?
float TextureID;

How would you go about utilizing this value in the shaders? If I understand the way Direct3D works, this would also limit you to 8 (?) textures per draw call, wouldn't it?


You bind it using the TEXCOORDn semantic where n matches the usage index you designated for it in your vertex declaration.

In that case, wouldn't the value(s) need to be a series of texture coordinates and not just IDs?

Quote:
I think the 8 texture limit only exists if you use Direct3D's built in texture stages. You could have an array of Texture2D variables to have more than 8 textures in your pixel shader should your GPU be able to handle it (I believe the upper limit of samplers is 16 in SM 3.0).

I didn't remember that the shaders have their own limit on samplers. Shader model 2.0 has a limit of 8 still, but you were right, 3.0/4.0 is 16. Still, this is fairly limiting if you want to use this method in a 2D-only environment with lots of random elements. (Which is why I was asking.)

Oh, and sorry if it seems like I'm somewhat derailing the thread.


The TEXCOORD semantic is generally used for arbitrary data as well as actual texture coordinates. You would have a normal set of texture coordinates for the vertex and the ID would simply be used as an index to select which texture to sample from.

If you needed to have a very large number of textures the best way would be to use a texture atlas where you have all of the texture you would want for the tiles all in a single texture. You then would need to modify the texture coordinates based on which texture you wanted to use. So if you had 30 256 * 256 sized textures arranged one after the other horizontally the ID value would be used to calculate the texture coordinate offset e.g For ID 4 you do TextureCoordinates.xy + (ID * 256)

Share this post


Link to post
Share on other sites
Quote:
Original post by Dom_152
Quote:
Original post by AnthonyB
Quote:
Original post by Dom_152
Quote:
Original post by AnthonyB
Quote:
Original post by Dom_152
// Which texture will this tile use?
float TextureID;

How would you go about utilizing this value in the shaders? If I understand the way Direct3D works, this would also limit you to 8 (?) textures per draw call, wouldn't it?


You bind it using the TEXCOORDn semantic where n matches the usage index you designated for it in your vertex declaration.

In that case, wouldn't the value(s) need to be a series of texture coordinates and not just IDs?

Quote:
I think the 8 texture limit only exists if you use Direct3D's built in texture stages. You could have an array of Texture2D variables to have more than 8 textures in your pixel shader should your GPU be able to handle it (I believe the upper limit of samplers is 16 in SM 3.0).

I didn't remember that the shaders have their own limit on samplers. Shader model 2.0 has a limit of 8 still, but you were right, 3.0/4.0 is 16. Still, this is fairly limiting if you want to use this method in a 2D-only environment with lots of random elements. (Which is why I was asking.)

Oh, and sorry if it seems like I'm somewhat derailing the thread.


The TEXCOORD semantic is generally used for arbitrary data as well as actual texture coordinates. You would have a normal set of texture coordinates for the vertex and the ID would simply be used as an index to select which texture to sample from.

Is there any special reason for using TEXCOORDs for this data? I can't remember off of the top of my head if there are any alternatives.

Quote:
If you needed to have a very large number of textures the best way would be to use a texture atlas where you have all of the texture you would want for the tiles all in a single texture.

Isn't there a performance cost for using a very large texture? If so, I have no idea how it would compare to the cost of switching textures and calling Draw*Primitive more often.

I appreciate the responses, and again, I apologize to the original poster if this is distracting.

Share this post


Link to post
Share on other sites
Well obviously the larger texture will take more memory and bandwidth as you upload it to the GPU. But once it's there you can keep it there for more or less the entirety of the games lifetime assuming that the tile texture set is the same throughout. Generally switching texture is rather expensive due to the uploading of the data to the GPU and Draw calls work most efficiently on large batches of data so a texture atlas would probably be preferable in most cases where large numbers of unique textures are required.

The reason TEXCOORD is used is because the pixel shader does not accept any data under any other semantic except for position (VPOS or SV_Position in D3D10+), colour (COLOR) and triangle face data (VFACE) so to put any other data we may want to use in the pixel shader we put it under the TEXCOORD semantic.

Share this post


Link to post
Share on other sites
If you are only rotating the tile images in steps of 90 degrees, then you do not need a rotation matrix per tile...just rotate the tile UV's.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement