1 2 3 4 | struct game_memory {
float player_x;
float player_y;
}
|
1 2 3 4 | struct transient_memory {
vertex vertices[1000];
int vertices_count;
}
|
1 2 3 4 | struct entity_chunk {
entity entities[1000];
entity_chunk* next;
}
|
sneiderlein
2. We pass a pointer to the block to the game loop, and the game loop casts the block to the layout/struct it expects: that way it can divide the memory into useful chunks as defined by the struct.
1 2 3 4 5 6 7 | |-----------------------------------| <- Memory we get from the win32 layer. |game_state|other things------------| <- we want to treat it like this. game_state* state = (game_state*) memory; // memory is void*. This is not the actual code from handmade hero. // Those next two lines are the values you would use to create a memory arena for the rest of the memory. void* rest_of_the_memory = memory + sizeof( game_state ); u64 rest_of_the_memory_size = memory_size - sizeof( game_state ); |
1 2 3 4 5 6 7 8 | |game_state|------------------------| push(...) |game_state|x|----------------------| push(...) |game_state|x|x|--------------------| BeginTemporary(...) |game_state|x|x|--------------------| push(...) |game_state|x|x|y|------------------| push(...) |game_state|x|x|y|y|----------------| EndTemporary(...) |game_state|x|x|--------------------| push(...) |game_state|x|x|x|------------------| |
1 2 3 4 5 | |game_state|x|x|--------------------| createSubArena(...) |game_state|x|x|------|-------------| push(sub, ...) |game_state|x|x|y|----|-------------| push(sub, ...) |game_state|x|x|y|y|--|-------------| push(main, ...) |game_state|x|x|y|y|--|x|-----------| |
1 | p_to_the_rest = pointer_to_the_beginning + sizeof(game_state); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static void* allocate(int size, void* start){
// check that it doesn't overflow
assert((game_state->last_pointer + size) <= game_state->memory_size);
// I would dereference the pointer to write to it, right?
*game_state->last_pointer = *start;
// update the position of the last_pointer
game_state->last_pointer += size;
// return pointer to the newly allocated data
return game_state->last_pointer - size;
}
|
1 2 3 4 5 6 7 8 | static void push_vertex(Vertex vertex){
vertices[last_vertex_index++] = vertex;
}
static void render(){
glBufferData(...push all the vertices to GPU...);
// mode first count
glDrawArrays(GL_TRIANGLES, 0, last_vertex_index);
}
|
sneiderlein
5. Or maybe a better Idea would be to do it modern opengl way (how I understand it), where several primitive shapes are created and stored on GPU on initialization, and we could bind a correct VAO and send a transform matrix as a uniform before each draw to change shapes size, rotation and position. Could someone please give an example for this? I'm a little lost on the specifics:
a. Do you need to use some projection matrix like orthographic?
b. Do you need to manage the Z positions of vertices? Or you could somehow rely on drawing order and discard the Depth Buffer completely?
c. Maybe a math library recommendation? GLM is the most mentioned, but I would appreciate some generic free, and more c-style API. So far I found gb_math, and there is even a library called HandmadeMath.
I'm having trouble including these libraries though, I get a lot of undefined identifier errors.
Appreciate your help!
1 2 3 4 5 | foreach(object in draw_queue){
glBindVertexArray(object.vao);
glUniformMatrix4fv(modelMatrix, 1, false, &object.modelMatrix);
glDrawArrays(GL_TRIANGLES, 0, object .last_vertex_index);
}
|
ratchetfreak
A: you don't need a perspective matrix. The vertex shader's output is expected to be in clip space where the geometry to be within the boundaries defined by -w < x, y, z < w. The convention is to use the model matrix to put the vertex data int a world space and then use the view-perspective matrix to transform that into clipspace.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | static void* allocate(int size, void* start){
// Here you compare a pointer and a size.
// You need to either compare 2 pointers or 2 size.
// - replace game_state->memory_size with game_state->last_memory_address
// - or replace game_state->last_pointer with game_state->used_memory;
assert((game_state->last_pointer + size) <= game_state->memory_size);
// You don't want to de-reference pointers here.
// If you de-reference them you modify the content (what the address is pointing to).
// Here we want to modify or store the address of the content.
*game_state->last_pointer = *start;
// You didn't de-reference here which is good.
game_state->last_pointer += size;
// return pointer to the newly allocated data
return game_state->last_pointer - size;
}
// Here is your function "fixed"
typedef struct GameState {
...
void* last_pointer;
void* last_memory_address;
...
} GameState;
...
game_state->last_pointer = memory + sizeof( GameState );
game_state->last_memory_address = game_state->last_pointer + ( memory_size - sizeof( GameState ) );
...
void* allocate( int size ) {
void* result;
assert( game_state->last_pointer + size <= game_state->last_memory_address );
result = game_state->last_pointer;
game_state->last_pointer += size;
return result;
}
// But this seems less intuitive to me than what Casey is doing in handmade hero.
// Memory arena.
typedef struct Data {
void* base; // The first address of "user" memory. It never changes.
size_t reserved; // The amount of "user" memory. It never changes.
size_t used; // The amount of "user" memory used.
} Data;
...
data->base = memory + sizeof( GameState );
data->reserved = memory_size - sizeof( GameState );
data->used = 0;
...
void* allocate( Data* memory, size_t size ) {
void* result = 0;
assert( memory->used + size <= memory->reserved );
if ( memory->used + size <= memory->reserved ) {
result = memory->base + memory->used;
memory->used += size;
}
return result;
}
|
1 2 | #define HANDMADE_MATH_IMPLEMENTATION #include "HandmadeMath.h" |
mrmixer
For example, in your renderer: how many vertex do you think you will have at a maximum ? 100 ? 1000 ? 100000 ?
Don't be afraid to allocate too much during development, on modern computer you virtually have infinite memory.The memory is totally finite.