Jump to content
  • Advertisement
Sign in to follow this  
guitarguy

Need help making a class of blocks

This topic is 3607 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

What im using: Directx 9 in C++ What im trying to do: Im trying to figure out how to make a shape using vertices in C++, and then create multiple objects of that shape that i can move to different places around the screen to make a game with. i havent started my project properly yet, and i thought i'd give this concept a shot by copying and editing one of the directx SDK files that already has a moving 3d model in it, and sticking a class definition in there and putting the shape creation in the class. Im not sure i understand how to accomplish this yet, and im pretty sure i dont know enough about directx vertex creating functions, but ive googled around and there isnt much on making a class of a shape you want using these, theres mostly just creating 1 shape. So if anyone can give me any breif explanations or links on this i'd be very greatful here is a pastebin the source code : http://gamedev.pastebin.com/m204ebefb

Share this post


Link to post
Share on other sites
Advertisement
I just glanced at your code and you've (sort of) got the idea.

Some suggestions:

- let each block "own" it's own vertex buffer. As it is, you have one global vertex buffer which you create and fill multiple times without ever releasing it. Whenever you create something, good practice is to always release it prior to creation if the buffer pointer is not NULL*. You have 2 blocks but only 1 vertex buffer!

- let each block render itself. That way you can change the material or texture of a block and not worry about keeping track of the changes anywhere but within the block itself. The block can set the renderstate appropriate for itself (turn off lighting, render without a texture, render in wireframe rather than solid, not render at all if it's "invisible," etc.)

- rather than creating a whole new vertex buffer for each new position, create a transformation matrix when the position changes and just apply it before rendering.

EDIT: *You can use a macro like:
#define SAFE_RELEASE(x) { if(x != NULL) x->Release(); x = NULL; }

Then, before you create a new vertex buffer:

SAFE_RELEASE(g_pVB);
m_pDevice->CreateVertexBuffer(...);

Share this post


Link to post
Share on other sites
Quote:
Original post by guitarguy
and i meant to paste this under BEGINNERS so if somebody can move this that would be great
Your wish is my command [grin]

Quote:
- let each block render itself.
Whilst this approach does have merit, it is a good example of a "no correct answer" problem with design.

Giving a class complete and total ownership (albeit temporarily) of the pipeline bestows a lot of responsibility on that class behaving correctly and properly.

Alternatively, allowing the class to simply describe how it should be displayed allows it to remain independent of the API used and provides some insurance against it doing something stupid with the pipeline.

The obvious trade-off is that the former is more flexible (anything D3D can do the class can do) but the latter is more restrictive and depends greatly on a well thoughts out and designed method for describing the appropriate rendering.


Go with whichever suits you best, but spare a little time to consider both [wink]

hth
Jack

Share this post


Link to post
Share on other sites
im sorry to bump this topic, but im struggling to figure out how move the code for the vertex buffer into a class without errors, the above posts were helpful, but i cant seem to figure this out, and i cant find much on google about using directx functions with my own classes, so if anybody could show me exactly what im doing wrong that would be absolutely fantastic

heres how the code looks now : http://gamedev.pastebin.com/m5b41bb79

Share this post


Link to post
Share on other sites
Ah, so many things wrong about that code [smile]

Let's get working on it. I'll ignore the issue of things being in the same file, because I assume that you stuck them together to increase readability online.

// This class manages the vertex buffer. Therefore, it's actually
// the 'geometry' of ablock as opposed to the block itself (since the
// block itself would manipulate the block geometry, the transform and the
// block material through intermediary classes, all hidden behind a block
// view class.
class block_geometry
{
public:

// Building the block is the responsibility of the constructor.
// Therefore, you shouldn't need this function at all.
// Also, this function returns an integer, which you ignore whenever
// you call it. So, remove the return value! Yes, I know you're using
// error codes to specify errors, but since you're calling this function
// from a constructor you don't have a choice but to use exceptions
// anyway, so drop the error code idea.
// int buildBlock();

// What does it mean for a position to be a floating-point value?
// You should explain that this is the _vertical_ position only.
// Besides, why are you moving the block around by setting its vertices?
// This is the exact point of matrices! You don't need this function.
// int moveBlock(float newPosition);

// Now, that's interesting. Changing the color? You don't even provide
// the color that it should be changed to! Besides, either you build the
// color into the material (thus, a uniform variable in your shader)
// and therefore don't have to change the block at all, or you have to
// change the components of the entire block and therefore would be
// going faster by simply constructing a new block and making the
// color a constructor argument.
// void changeColor();

// It does not make sense for a block (as you defined it) to dissapear,
// since it does not _appear_ ! Given how you've defined it, it seems
// that your block is merely a polygon mesh, used as a support for a
// material during rendering.
// void disappear();

// So, this is a static function that renders... what? It has no arguments,
// and is not a member: how does it know what to render? At best, the
// function would be a member function, and would render the block.
// static void render();

// This is what we're going to do here: write a 'render' function that
// 'renders' the block. What will actually happen is that the function
// will set the current stream source to the data inside this block,
// so that all rendering performed after that will use the block's data.
// So, the device can 'use()' the block, set the transform, set the
// material, and render (possibly several times to achieve complex
// effects).
void use() const;

// Of course, with this approach, we also need an function to do the
// actual rendering. This one should be called after all other
// render states (material, transform, stream source) are set.
void render() const;

// Cleanup is what a destructor does.
// void cleanup();

// You don't need a comment to tell you that these are constructors and
// destructors.
block_geometry();
~block_geometry();

// Keep non-public things private.
private:

// We don't want the block to be copied around or
// assigned to, because handling vertex buffers is hard.
block_geometry(const block_geometry &);
block_geometry &operator=(const block_geometry &);

// What for?
// int blockNumber;

// This is best handled at the material level.
// int blockColor;

// This is best handled at the transform level.
// float blockPosition;

// What does the 'g_p' here mean? It means that it's a global
// pointer. Well, first, it's not a global variable at all,
// so that part is wrong. Second, do you really need to
// be reminded that it's a pointer? No, you don't! You never
// manipulate vertex buffers by value, therefore you just
// know that it's a pointer. Give it a meaningful name.
// LPDIRECT3DVERTEXBUFFER9 g_pVB;
LPDIRECT3DVERTEXBUFFER9 vertex_buffer;
};

// Summary:

class block_geometry
{
block_geometry(const block_geometry &);
block_geometry &operator=(const block_geometry &);
LPDIRECT3DVERTEXBUFFER9 vertex_buffer;
public:
void use() const;
void render() const;
block_geometry();
~block_geometry();
};



Now, this has thoroughly cleaned up the class interface, so you can move on to better things, such as implementing the methods. There are only three methods that need implementing: 'use', the constructor, and the destructor.



namespace
{
// Don't repeat a magic number in several places : give it
// a name once. Not to mention, 49 is a weird number of sides.
const int sides = 49;
}

// Postcondition : 'vertex_buffer' is a valid vertex buffer
// containing the geometry for the block.
block_geometry::block_geometry()
{
// We do the initialization here instead of a side function,
// and throw an exception if we fail, because otherwise the
// program will crash when trying to use the badly initialized
// block.

if( FAILED( g_pd3dDevice ->
CreateVertexBuffer( (::sides + 1)* 2 * sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &vertex_buffer, NULL ) ) )
{
throw std::exception("Could not create vertex buffer");
}

// So, now the vertex buffer is a valid buffer. But wait! If an exception is
// thrown now, it will never be destroyed! Therefore, we need to wrap everything
// in a try-catch block to handle this.

try
{
CUSTOMVERTEX *contents;

if( FAILED( vertex_buffer->Lock( 0, 0, (void**)&contents, 0 ) ) )
{
throw std::exception("Failed to lock vertex buffer.");
}

// Our vertex buffer is now locked. But wait, if an exception is thrown
// now, it will never be unlocked! Therefore, we need to do everything
// in a "try" block.

try
{
// Use the native integer types where possible.
for( int i = 0; i < ::sides + 1; i++ )
{
// Don't forget that this is a constant.
const float theta = (2 * D3DX_PI * i) / ::sides;
const float cos = cosf(theta);
const float sin = sinf(theta);

// Drop the 'position' thing: it is being handled at the transform
// level. besides, it's shorter this way.
pVertices[2*i+0].position = D3DXVECTOR3( sin,-1.0f, cos );
pVertices[2*i+0].normal = D3DXVECTOR3( sin, 0.0f, cos );
pVertices[2*i+1].position = D3DXVECTOR3( sin, 1.0f, cos );
pVertices[2*i+1].normal = D3DXVECTOR3( sin, 0.0f, cos );
}
}
catch (...)
{
// So, an exception was thrown before we could unlock the buffer.
// Unlock it now, then propagate the exception.
vertex_buffer -> Unlock();
throw;
}

vertex_buffer -> Unlock();
}
catch (...)
{
// So, an exception happened after the buffer was created, but before
// the constructor ended. Meaning we need to destroy the buffer now.
// And then, propagate the exception.
vertex_buffer -> Release();
throw;
}
}

block_geometry::~block_geometry()
{
vertex_buffer -> Release();
}

void block_geometry::use() const
{
g_pd3dDevice->SetStreamSource( 0, vertex_buffer, 0, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
}

void block_geometry::render() const
{
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, ::sides*2 );
}



That would be it. Example usage:

block_geometry bg;

bg.use(); // Sets up the VB with the device.
set_material(); // Should set the texture and shaders
set_position(); // Should set the world matrix
bg.render(); // Renders the block!
set_position(); // Should set another world matrix
bg.render(); // Renders the block elsewhere!

Share this post


Link to post
Share on other sites
thanks i understand this a lot better now. But it went over my head a bit, there was a lot of code techniques that ive never seen before, that i think i should look up before i continue learning

also, what/where are set_material(); and set_position();? do you mean that i should make these myself?
and im not sure where to place "block_geometry bg;", "bg.use();" and "bg.render();", ive tried moving them all around but so far im still getting a blank screen

cheers

Share this post


Link to post
Share on other sites
set_position() should set the world transform for rendering the block. set_material() should set the textures, stage states and shaders for the block.

The definition of the block geometry should be placed somewhere where it will only be executed once (for instance, as a member of a rendering class). The rendering code should be between the 'begin' and 'end' statements, prior to the 'present' statement.

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!