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.