GMS 2 Vertex buffers: extend after vertex_end? Drawing a trace curve

Erik Leppen

Member
How do I extend a vertex buffer with additional vertex data after it has been closed with vertex_end, and without removing all existing vertex data in it?

Basically what I want is a moving instance drawing its trajectory. Because drawing all segments every step is way too slow, I want to use a vertex buffer for this, where every step I only add the last step and then in the draw event, submit the whole thing.

But this doesn't seem to work. Basically my approach is:
Create: vb = vertex_create_buffer(...)
Step: vertex_begin(vb); add vertex data; vertex_end(vb);
Draw: vertex_submit(vb);
But this doesn't work; if I show_debug_message(vertex_get_number(vb)), I get the same number every step, instead of seeing it growing. The manual says that vertex_begin clears the old data, but I don't want that. I want to extend a vertex buffer. I tried removing vertex_end, but then I cannot submit it for drawing. I tried reading into "normal" buffers, but the docs lack a decent example of how to combine normal buffers and vertex buffers, and I can't find a good tutorial on the forums. Also it feels like overkill to learn to use this whole technology just for drawing an instance's trace curve.

There must be a simple way for this, right? (I don't want to use a surface, as I want a vector-based solution.)
 

Simon Gust

Member
I don't know of any way to edit a vertex buffer after being closed.
So buffers I guess.
Basically, you fill the buffer the same way you would fill a vertex buffer.

I assume you're just using positions for your vertex format.
Code:
// format
vertex_format_begin();
vertex_format_add_position();
vformat = vertex_format_end();

// buffer
buffer = buffer_create(256, buffer_grow, 4);
buffer_write(buffer, buffer_f32, x1);
buffer_write(buffer, buffer_f32, y1);

buffer_write(buffer, buffer_f32, x2);
buffer_write(buffer, buffer_f32, y2);

buffer_write(buffer, buffer_f32, x3);
buffer_write(buffer, buffer_f32, y3);

var size = buffer_tell(buffer);
buffer_resize(buffer, size);

// combine
vbuffer = vertex_create_buffer_from_buffer(buffer, vformat);
This will create a line with 3 points (if you've selected pr_linestrip)

When you need another line, you edit the buffer and since it's a grow buffer, it should just work as long as you call buffer_tell to trim it back.
 

Ido-f

Member
I don't know if it works, but there is a function called "buffer_copy_from_vertex_buffer".
Maybe you can use that copy your closed vertex buffer into another buffer, and then create a new vertex buffer using vertex_create_buffer_from_buffer, similar to Simon Gust's suggestion.
I'd expect you'd also need to use vertex_delete_buffer on the original vertex buffer, if you don't need it, to prevent a memory leak.
 

Bart

WiseBart
I'm surprised that you can only do a single vertex_begin/vertex_end per vertex buffer. Didn't know that.

I'd also go for the above options. Use a buffer, always add the new data in there and recreate the vertex buffer every step based on the contents of the buffer.

Just to add one more thing to the above answers: vertex_create_buffer_from_buffer_ext is a function that may be useful.
It allows you to use an offset and the number of vertices (in case you don't want to resize the buffer or just want to draw part(s) of the trajectory).
 

Erik Leppen

Member
I tried the approach by Simon Gust and with some adaptations, it seems to work. Thanks to all who contributed! :D

Apparently it's not possible to use his solution with a vertex format with only position. I got an error message saying it also needs color data (yay for descriptive error messages). With some testing I discovered (indeed, this information is not in the manual), that color data consists of four 8-bit integers (buffer_s8 and buffer_u8 both work, somehow): red, green, blue, alpha.

So my code right now (stripped from all game-specific stuff) is

ev_create:
Code:
//create format
vertex_format_begin();
vertex_format_add_position();
vertex_format_add_color();
vformat = vertex_format_end();

//create buffer
buffer = buffer_create(256, buffer_grow, 1);

//create vertex buffer
vbuffer = undefined;
ev_step:

Code:
//extend buffer
buffer_write(buffer, buffer_f32, xprevious);
buffer_write(buffer, buffer_f32, yprevious);
buffer_write(buffer, buffer_s8, color_get_red(co));
buffer_write(buffer, buffer_s8, color_get_green(co));
buffer_write(buffer, buffer_s8, color_get_blue(co));
buffer_write(buffer, buffer_s8, 255); //alpha
buffer_write(buffer, buffer_f32, x);
buffer_write(buffer, buffer_f32, y);
buffer_write(buffer, buffer_s8, color_get_red(co));
buffer_write(buffer, buffer_s8, color_get_green(co));
buffer_write(buffer, buffer_s8, color_get_blue(co));
buffer_write(buffer, buffer_s8, 255); //alpha

//remove old vertex buffer
if not is_undefined(vbuffer) {
    vertex_delete_buffer(vbuffer);
    vbuffer = undefined;
}

//create vertex buffer
vbuffer = vertex_create_buffer_from_buffer(buffer, vformat, -1);
ev_draw:

Code:
vertex_submit(vbuffer, pr_linelist, -1);
 
If you make a shader which doesn't use colour data, you should be able to get by with a vertex format and buffer that doesn't contain colour information.
 
Top