Legacy GM 3D FPS - I need help with vertex buffers

Salem Holly

Member
I'm making a 3D fps game in gamemaker studio 1.4, and I have gotten really far, but recently I've been optimizing it and have a pretty big fps killer. I'm using d3d_draw functions to draw all the walls. At first it wasn't an issue, because I was just making test levels and didn't need a high number of wall objects. But now that I'm actually making levels, the number of walls is really affecting the FPS. So now I've been experimenting with vertex buffers to draw the walls, and they are a little faster, but I want to take it one step further. I've heard of people using one master object to draw all the wall's vertex buffers as one draw call, rather than have each wall be responsible for it's own vertex buffer/ draw call. That way I can place hundreds of walls and only have one draw call rather than hundreds. Can anyone help me with that? Maybe even provide some kind of idea where to put the code and how to do it. I haven't really found any tutorials that are really helpful, so this is my last resort. If any of you are interested in seeing my project, let me know. I'll upload images and or videos later on. I just keep getting errors while trying to post them at the time being.

Thank you,
Salem
 
Some things to keep in mind:

Everything that you put into a single vertex buffer has to use the same texture(s). The most reliable way to do that is to make your own texture atlas in a seperate image editor. Individual regions of that texture will need to have a border (of the same colors) around them, in order to prevent colors from bleeding across adjacent texture region borders, while using texture interpolation.

Make sure all of your vertices are defined in the same clockwise direction. What this means is that when seen from the direction in which a surface is supposed to be visible, the vertices that make up that surface should be ordered in a clockwise manner.

Make sure you write vertex attributes to your buffer in the same order in which they appear in your vertex format.

For everything that won't change over time (or for things that change in a deterministic way that can be calculated in the vertex shader), just write it into a vertex buffer once at the time that those things should be loaded into the game.

If you are having problems or getting errors, let us know what they specifically are.
 

Salem Holly

Member
Those are good things to know and keep in mind. I have successfully created a vertex buffer cube(without top or bottom), but all the code for it is inside the cube object, so when I place a bunch, there are still a lot of vertex batches or draw calls or whichever one it is.

But things got hairy when I tried using a master control object to draw all of them under one draw call/ vertex batch(add them all to it's own single model). I had a control object that would essentially start the vertex buffer, then it would check if the wall object existed, and then it would run a code like:

//create event
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_textcoord();
format = vertex_format_end();

buff_block1 = vertex_create_buffer();
tex_block1 = -1;


//alarm[1] = 1
if instance_exists(o_wallcobble)
{
vertex_begin(buff_block1, format);
with(o_wallcobble) instance_destroy();
vertex_end(buff_block1) vertex_freeze(buff_block1);
tex_block1 = background_get_texture(uv_wallcobble);
}

Then in the wall object, when it got destroyed, it would call for a script like this

var ixs = image_xscale*32;
var iys = image_yscale*32;

for(var ih = 0; ih < height; ih += 32)

{
for(var iy = 0; iy < ixs; iy += 32)

{
// s4
vertex_position_3d(obj_control_cellar.buff_block1, x, y+iy, z+ih);
vertex_texcoord(obj_control_cellar.buff_block1, 0, 0);
vertex_position_3d(obj_control_cellar.buff_block1, x, y+iy, z+ih+8);
vertex_texcoord(obj_control_cellar.buff_block1, 0.25, 0);

and so on... The logic was that when the wall got destroyed, it would submit it's vertex buffer information to the control object for it to draw(add to it's model), but I kept getting an "illegal vertex buffer specified" error that targeted the "obj_control_cellar.buff_block1" portion. I couldn't find any explanation for it online, and it gave me no reasoning that I could understand. I assume it's because I'm calling to another object, but that's the whole point, so I got stuck

I should note, I got the code for this from the only source of info (tut) I could find on some other thread. The guy didn't give enough info for me to follow through, but he provided fragments of code
 

Yal

šŸ§ *penguin noises*
GMC Elder
Try adding a debug message before and after you instance_destroy the walls, and add a debug message in their destroy event. What I'm thinking is that the following happens:
  1. You destroy the walls
  2. You freeze the buffer, because the walls don't get destroyed until this event finishes
  3. Wall destroy event runs
  4. Buffer now is frozen and won't accept more data
It would be nice if you could verify in what order the code is run to see if this is happening or not.

Instead of destroying the walls being what runs the code to add them to the buffer, you could use a with loop to run the code instead:

Code:
vertex_begin(buff_block1, format);
with(obj_wall_cobble){
  for(var ih = 0; ih < height; ih += 32)
  {
   for(var iy = 0; iy < ixs; iy += 32)
    {
     // s4
    vertex_position_3d(obj_control_cellar.buff_block1, x, y+iy, z+ih);
    vertex_texcoord(obj_control_cellar.buff_block1, 0, 0);
    vertex_position_3d(obj_control_cellar.buff_block1, x, y+iy, z+ih+8);
      // etc etc etc
}

// freeze buffer etc
This has the advantage that you know it's run in the correct order.
 

Salem Holly

Member
Instead of destroying the walls being what runs the code to add them to the buffer, you could use a with loop to run the code instead:

Code:
vertex_begin(buff_block1, format);
with(obj_wall_cobble){
  for(var ih = 0; ih < height; ih += 32)
  {
   for(var iy = 0; iy < ixs; iy += 32)
    {
     // s4
    vertex_position_3d(obj_control_cellar.buff_block1, x, y+iy, z+ih);
    vertex_texcoord(obj_control_cellar.buff_block1, 0, 0);
    vertex_position_3d(obj_control_cellar.buff_block1, x, y+iy, z+ih+8);
      // etc etc etc
}

// freeze buffer etc
This has the advantage that you know it's run in the correct order.
Oh me lord, that was the case. Destroying them and using the destroy event was causing the problem. I did what you said and it worked! Thank you so much, Yal. I'll post a before and after photo later, to show the improvement and stuff, if you wanna see. It feels so good knowing I don't have to worry about that anymore. Again, thank you. This has put my game on halt for a month or two now, but I can finally get back into it
 
Top