• Advertisement
Sign in to follow this  

Multiple Objects

This topic is 4615 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 learning through the "Intoruction to 3D game programming with DirectX 9.0" book and have got as far as being learning how to draw a spinning cube. After a bit of messing around I figured out how to use D3DXCreateTeapot() to create a teapot and draw that. I can render both but they both spin together and are in the same position. My question is how do I render objects in different places and how do I make them spin seperate to each other? CAn anyone give me a push in the right direction? this is the code im using to draw and rotate the cube:
float currtime = (float)timeGetTime();
			float timedelta = (currtime - lasttime) * 0.001f;

			D3DXMATRIX Rx, Ry;

			static float y = 0.0f;
			static float x = 0.0f;

			D3DXMatrixRotationX(&Rx, x);

			x += timedelta;

			if(x >= 6.28f)
			{
				x = 0.0f;
			}

			D3DXMatrixRotationY(&Ry, y);

			y += timedelta;

			if(y >= 6.28f)
			{
				y = 0.0f;
			}

			D3DXMATRIX p = Rx * Ry;

			d3ddevice->SetTransform(D3DTS_WORLD, &p);

			d3ddevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);

			d3ddevice->BeginScene();
			d3ddevice->SetStreamSource(0, VB, 0, sizeof(MyVertex));
			d3ddevice->SetIndices(IB);
			d3ddevice->SetFVF(MyVertex::FVF);
			d3ddevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
			d3ddevice->EndScene();

			d3ddevice->Present(0,0,0,0);

			lasttime = currtime;

To render the teapot i just use mesh->DrawSubset(0); Thanks for any help

Share this post


Link to post
Share on other sites
Advertisement
When using the fixed function vertex pipeline (i.e. not using vertex shaders), all your untransformed (i.e. non D3DFVF_XYZRHW) vertices are affected by 3 matrices: The world, view and projection matrices. The world matrix controls things such as the object size, position and orientation. So in your case, simply do a separate:
d3ddevice->SetTransform(D3DTS_WORLD, &matrix);
..for each object you have.

Share this post


Link to post
Share on other sites
Are vertices transformed when sent to the vertex buffer, or do they get transformed when they are drawn? I guess it wouldnt really make sense to be transformed when they are drawn, but its good to be sure.

Share this post


Link to post
Share on other sites
Well, they are in fact transformed just when they need to be drawn. This makes sense, since polygons which fail the frustum check (usually a notable percentage of polygons drawn) do not need to go through the vertex processing pipeline at all.

Thinking of vertex shaders, you could also draw a single vertex buffer with different shaders, if the shader would be applied to the vertex buffer, instead of on-the-fly when drawing takes place, the vertex buffer would be modified each time you call SetStreamSource() or wherever you would intend on putting the transformation stage then - not a good idea if you compare the speed of static and dynamic vertex buffers ;)

-Markus-

Share this post


Link to post
Share on other sites
True.

But then how are you supposed to set different transforms for different "world" objects? For instance, if you have 14 rocks, all have different world locations and orientations. How are these applied?

Share this post


Link to post
Share on other sites
Quote:
Original post by Bonehed316
But then how are you supposed to set different transforms for different "world" objects? For instance, if you have 14 rocks, all have different world locations and orientations. How are these applied?

Four options:

1. Multiple draw calls
This is the traditional method; store one rock and 14 matrices. Pair up D3D calls like:


Device->SetStreamSource( ... );
Device->SetIndices( ... );
//etc...
for( int i = 0; i < 14; i++ )
{
Device->SetTransform( D3DTS_WORLD, &rockMatrix[i] );
Device->DrawIndexedPrimitive( ... );
}


2. Extra data
Store 14 copies of similar geometry in your vertex buffer, pre-transformed accordingly. Submit them all as one big draw call.

Not recommended though [smile] - works okay for small sets of static objects, but falls apart when you have large numbers of objects and/or need to update their transformation.

3. Vertex shaders and constants
Some system where you combine #1 and #2 (above) but get the GPU/shader to work out what should be going on. For example, pass various constants/params to the shader so that it can calculate the rotations/translations accordingly.

4. Instancing
A fairly advanced feature available in the latest-and-greatest hardware. Does pretty much exactly what you want in possibly the most efficient possible way. However, support for it isn't really there yet - so i'd class it more of an optimization opportunity than a base-feature.

hth
Jack

Share this post


Link to post
Share on other sites
So you pretty much have to call DIP for every object, and for every texture/render state, I guess.

Share this post


Link to post
Share on other sites
Quote:
Original post by Bonehed316
So you pretty much have to call DIP for every object, and for every texture/render state, I guess.

Yep[attention]

A lot of work done by engine programmers is on algorithms/arrangements/scheduling to reduce the number of state changes and draw calls that are required to render a given scene. Huuuge performance gains are possible [grin]

hth
Jack

Share this post


Link to post
Share on other sites
Ok I have managed to draw them apart using:


d3ddevice->BeginScene();

d3ddevice->SetStreamSource(0, VB, 0, sizeof(MyVertex));
d3ddevice->SetIndices(IB);
d3ddevice->SetFVF(MyVertex::FVF);

//Cube
D3DXMatrixTranslation(&p, -1.25f, 0.0f, 0.0f);
d3ddevice->SetTransform(D3DTS_WORLD, &p);
d3ddevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);

//Teapot
D3DXMatrixTranslation(&p, 1.25f, 0.0f, 0.0f);
d3ddevice->SetTransform(D3DTS_WORLD, &p);
mesh->DrawSubset(0);

d3ddevice->EndScene();



But Im still confused about rotating each of them seperatly using the code in my first post. :S

Share this post


Link to post
Share on other sites
Well you have two choices. The two rotations can be separate or related. If you just let them spin at the same rate then it's easy:


float currtime = (float)timeGetTime();
float timedelta = (currtime - lasttime) * 0.001f;
D3DXMATRIX Rx, Ry, Translation;

static float y = 0.0f;
static float x = 0.0f;

D3DXMatrixRotationX(&Rx, x);

x += timedelta;

if(x >= 2.0f * D3DX_PI)
x = 0.0f;

D3DXMatrixRotationY(&Ry, y);

y += timedelta;

if(y >= 2 * D3DX_PI)
y = 0.0f;

D3DXMatrixTranslation(&Translation, 1.5f, 0.0f, 0.0f);

D3DXMATRIX p = Rx * Ry * Translation;

d3ddevice->SetTransform(D3DTS_WORLD, &p);

d3ddevice->DrawPrimitive(...);

D3DXMatrixTranslation(&Translation, -1.5f, 0.0f, 0.0f);

p = Rx * Ry * Translation

d3ddevice->SetTransform(D3DTS_WORLD, &p);

mesh->DrawSubset(0);


If you want them to be indepdant of each other I'm afraid it gets a little more complicated. Here's one possiblity:


struct WorldMatrix
{
D3DXVECTOR3 position;
float RotationX, RotationY, RotationZ;
D3DXMATRIX GetMatrix()
{
D3DXMATRIX matrix, rx, ry, rz, translation;

while(RotationX >= 2.0f * D3DX_PI) RotationX -= 2.0f * D3DX_PI;
while(RotationY >= 2.0f * D3DX_PI) RotationY -= 2.0f * D3DX_PI;
while(RotationZ >= 2.0f * D3DX_PI) RotationZ -= 2.0f * D3DX_PI;

D3DXMatrixRotationX(&rx, RotationX);
D3DXMatrixRotationX(&ry, RotationY);
D3DXMatrixRotationX(&rz, RotationZ);
D3DXMatrixTranslation(&translation, position.x, position.y, position.z);
matrix = ry * rx * rz * translation;
return matrix;
}
};


float currtime = (float)timeGetTime();
float timedelta = (currtime - lasttime) * 0.001f;

static WorldMatrix Teapot;
static WorldMatrix Box;

Teapot.RotationX += timedelta;
Box.RotationX += timedelta;

Teapot.RotationY += timedelta;
Box.RotationY += timedelta;

d3ddevice->SetTransform(D3DTS_WORLD, &Box.GetMatrix());

d3ddevice->DrawPrimitive(...);

d3ddevice->SetTransform(D3DTS_WORLD, &Teapot.GetMatrix());

mesh->DrawSubset(0);






If you don't understand what that second example is doing, post back here and I (or one of many other kind folks) will try to explain.

Hope that helps!

Share this post


Link to post
Share on other sites
Thanks im starting to get it.

I changed the struct so I could set the positon of each object:


struct WorldMatrix
{
D3DXVECTOR3 position;
void SetPosition(float x, float y, float z)
{
position.x = x;
position.y = y;
position.z = z;
}
float RotationX, RotationY, RotationZ;
D3DXMATRIX GetMatrix()
{
D3DXMATRIX matrix, rx, ry, rz, translation;

while(RotationX >= 2.0f * D3DX_PI) RotationX -= 2.0f * D3DX_PI;
while(RotationY >= 2.0f * D3DX_PI) RotationY -= 2.0f * D3DX_PI;
while(RotationZ >= 2.0f * D3DX_PI) RotationZ -= 2.0f * D3DX_PI;

D3DXMatrixRotationX(&rx, RotationX);
D3DXMatrixRotationY(&ry, RotationY);
D3DXMatrixRotationZ(&rz, RotationZ);
D3DXMatrixTranslation(&translation, position.x, position.y, position.z);
matrix = rx * ry * rz * translation;
return matrix;
}
};



And my render function looks like this:

static WorldMatrix Teapot;
static WorldMatrix Box;

Teapot.SetPosition(-2.0f, 0.0f, 0.0f);
Box.SetPosition(2.0f, 0.0f, 0.0f);

Teapot.RotationX += timing;
Teapot.RotationY += timing;

Box.RotationX += timing;
Box.RotationY += timing;

d3ddevice->SetTransform(D3DTS_WORLD, &Box.GetMatrix());

d3ddevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);

d3ddevice->SetTransform(D3DTS_WORLD, &Teapot.GetMatrix());

mesh->DrawSubset(0);



And it works great. I have a couple of questions though:

1. How do I change the speed of rotation?

2. Both objects rotate the same way, can I make them rotate in opposite ways?

3. Should I have a matrix for each object and then transform then render each one?

4. A bit off topic, in a game or engine would each object have a struct that contains its mesh, textures etc. And when you goto render that object you would change render states and transform with each objects matrix and render it, a bit like what the above code does (but without the mesh, textures etc.?

Thanks :)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by mengha
1. How do I change the speed of rotation?

You need to implement some sort of multiplier and/or time-based modelling.

Simply determine how many units (e.g. radians or degrees) you need to change per second. Work out how much time has elapsed as a fraction/multiple of a second. Multiply them together, and this is your rotation independent of frame rate and at whatever speed you want [smile]

Quote:
Original post by mengha
2. Both objects rotate the same way, can I make them rotate in opposite ways?

Use different matrices for each object. Simply a case of constructing different ones using different parameters.

Quote:
Original post by mengha
3. Should I have a matrix for each object and then transform then render each one?

Yes, you could do that. Most of my implementations have always stored 3 rotations, 3 scalars and 3 translations for each object. When it comes to rendering I create a new D3DXMATRIXA16 before rendering.

This is a trivial way of doing it though, you could probably save some time (if it mattered) by caching/storing the matrix rather than recalculating it each time. I had some nifty matrix code that ended up 3x faster than re-constructing it all each time [grin]

Quote:
Original post by mengha
4. A bit off topic, in a game or engine would each object have a struct that contains its mesh, textures etc. And when you goto render that object you would change render states and transform with each objects matrix and render it

Yeah, a game engine must maintain this information - the basic idea is that it will attach the necessary geometry/transforms/materials/effects together; but there is a lot of scope for cutting corners, saving duplication and so on...


hth
Jack

Share this post


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

  • Advertisement