- Simple rectangle drawing

- We just add to a frame of quads which the renderer deals with in gfx_update
	- draw_quad, draw_rect, draw_rect_rotated
	- Currently just implemented with legacy opengl, so we can't do custom shading
- Added third party code from lodepng so we can load pngs without any other dependencies (no C includes)
- ALLOCATOR_REALLOCATE
- Basic Vector2, Vector3, Vector4 stuff, and some math utilities
- Added <math.h> dependency for now.
- Graphics renderer frontend layer
- Naive get_random() implementation
This commit is contained in:
Charlie 2024-06-29 13:27:37 +02:00
parent 8489421dbf
commit c61f216d37
13 changed files with 7373 additions and 103 deletions

View file

@ -11,6 +11,10 @@ typedef struct Context_Extra {
// This needs to be defined before oogabooga if we want extra stuff in context
#define CONTEXT_EXTRA Context_Extra
#define GFX_RENDERER GFX_RENDERER_D3D11
#define ENTRY_PROC start // This is "entry" by default but we're not like all the other girls so we call it start
#include "oogabooga/oogabooga.c"

View file

@ -7,7 +7,7 @@ pushd build
mkdir release
pushd release
clang -o cgame.exe ../../build.c -Ofast -DRELEASE -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter
clang -o cgame.exe ../../build.c -Ofast -DRELEASE -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32
popd
popd

40
entry.c
View file

@ -1,31 +1,37 @@
int entry(int argc, char **argv) {
int start(int argc, char **argv) {
window.title = fixed_string("My epic game");
//window.width = 400;
//window.height = 400;
//window.x = 200;
//window.y = 200;
window.width = 400;
window.height = 400;
window.x = 200;
window.y = 200;
window.x = 0;
window.y = 0;
seed_for_random = os_get_current_cycle_count();
float64 last_time = os_get_current_time_in_seconds();
while (!window.should_close) {
reset_temporary_storage();
context.allocator = temp;
print("%d, %d, %d, %d\n", window.x, window.y, window.width, window.height);
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(-0.5f, -0.5f);
glVertex2f(0.5f, -0.5f);
glVertex2f(0.5f, 0.5f);
glVertex2f(-0.5f, 0.5f);
glEnd();
os_update();
float64 now = os_get_current_time_in_seconds();
float64 delta = now - last_time;
last_time = now;
// Print some random FPS samples every now and then
if ((get_random() % 4000) == 3)
print("%2.f FPS\n", 1.0/delta);
draw_rect_rotated(v2(-.25f, -.25f), v2(.5f, .5f), COLOR_RED, v2(.25, .25), (f32)now);
Vector2 hover_position = v2_rotate_point_around_pivot(v2(-.5, -.5), v2(0, 0), -(f32)now);
Vector2 local_pivot = v2(.125f, .125f);
draw_rect(v2_sub(hover_position, local_pivot), v2(.25f, .25f), COLOR_GREEN);
gfx_update();
}
return 0;

View file

@ -40,8 +40,15 @@ void printf(const char* fmt, ...);
#define assert_line(line, cond, ...) if (!(cond)) { printf("Assertion failed in file " __FILE__ " on line " STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); os_break(); }
#define assert(cond, ...) assert_line(__LINE__, cond, __VA_ARGS__);
#ifdef RELEASE
#undef assert
#define assert(...)
#endif
#define cast(t) (t)
#define ZERO(t) (t){0}
///
// Compiler specific stuff
#ifdef _MSC_VER
@ -106,6 +113,7 @@ typedef struct Nothing {int nothing;} Nothing;
typedef enum Allocator_Message {
ALLOCATOR_ALLOCATE,
ALLOCATOR_DEALLOCATE,
ALLOCATOR_REALLOCATE,
} Allocator_Message;
typedef void*(*Allocator_Proc)(u64, void*, Allocator_Message);

View file

@ -1,12 +1,38 @@
/*
Usage:
Just call draw_xxx procedures anywhere in the frame when you want something to be drawn that frame.
// Examples:
// Verbose
Draw_Quad quad;
quad.bottom_left = v2(x, y);
quad.top_left = v2(x, y);
quad.top_right = v2(x, y);
quad.bottom_right = v2(x, y);
quad.color = v4(r, g, b, a);
draw_quad(quad);
// Basic rect. Bottom left at X=-0.25, Y=-0.5 with a size of W=0.5, H=0.5
draw_rect(v2(-0.25, -0.5), v2(0.5, 0.5), COLOR_GREEN);
// Rotated rect. Bottom left at X=-0.25, Y=-0.5 with a size of W=0.5, H=0.5
// With a centered pivot (half size) and a rotation of 2.4 radians
// If pivot is v2(0, 0), the rectangle will rotate around it's bottom left.
draw_rect_rotated(v2(-0.25, -0.5), v2(0.5, 0.5), COLOR_GREEN, v2(0.25, 0.25), 2.4f);
*/
#define QUADS_PER_BLOCK 1024
typedef struct Draw_Quad {
float left;
float right;
float bottom;
float top;
Vector2 bottom_left, top_left, top_right, bottom_right;
// r, g, b, a
Vector4 color;
} Draw_Quad;
@ -19,6 +45,71 @@ typedef struct Draw_Quad_Block {
} Draw_Quad_Block;
typedef struct Draw_Frame {
Draw_Quad_Block first_quad_block;
Draw_Quad_Block first_block;
Draw_Quad_Block *current;
} Draw_Frame;
// This frame is passed to the platform layer and rendered in os_update.
// Resets every frame.
Draw_Frame draw_frame = ZERO(Draw_Frame);
void draw_quad(Draw_Quad quad) {
if (!draw_frame.current) draw_frame.current = &draw_frame.first_block;
assert(draw_frame.current->num_quads <= QUADS_PER_BLOCK);
if (draw_frame.current->num_quads == QUADS_PER_BLOCK) {
draw_frame.current->next = cast(Draw_Quad_Block*)talloc(sizeof(Draw_Quad_Block));
draw_frame.current = draw_frame.current->next;
draw_frame.current->next = 0;
draw_frame.current->num_quads = 0;
}
draw_frame.current->quad_buffer[draw_frame.current->num_quads] = quad;
draw_frame.current->num_quads += 1;
}
void draw_rect(Vector2 position, Vector2 size, Vector4 color) {
const float32 left = position.x;
const float32 right = position.x + size.x;
const float32 bottom = position.y;
const float32 top = position.y+size.y;
Draw_Quad q;
q.bottom_left = v2(left, bottom);
q.top_left = v2(left, top);
q.top_right = v2(right, top);
q.bottom_right = v2(right, bottom);
q.color = color;
draw_quad(q);
}
void draw_rect_rotated(Vector2 position, Vector2 size, Vector4 color, Vector2 local_pivot, float32 rotation) {
const float32 left = position.x;
const float32 right = position.x + size.x;
const float32 bottom = position.y;
const float32 top = position.y+size.y;
Vector2 bottom_left = v2(left, bottom);
Vector2 top_left = v2(left, top);
Vector2 top_right = v2(right, top);
Vector2 bottom_right = v2(right, bottom);
Vector2 pivot = v2(position.x + local_pivot.x, position.y + local_pivot.y);
Draw_Quad q;
q.bottom_left = v2_rotate_point_around_pivot(bottom_left, pivot, rotation);
q.top_left = v2_rotate_point_around_pivot(top_left, pivot, rotation);
q.top_right = v2_rotate_point_around_pivot(top_right, pivot, rotation);
q.bottom_right = v2_rotate_point_around_pivot(bottom_right, pivot, rotation);
q.color = color;
draw_quad(q);
}
#define COLOR_RED ((Vector4){1.0, 0.0, 0.0, 1.0})
#define COLOR_GREEN ((Vector4){0.0, 1.0, 0.0, 1.0})
#define COLOR_BLUE ((Vector4){0.0, 0.0, 1.0, 1.0})

View file

View file

@ -0,0 +1,76 @@
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
#include "GL/gl.h"
HDC hdc;
typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALEXTPROC) (int interval);
void gfx_init() {
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0, 0, 0, 0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
24,
8,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
hdc = GetDC(window._os_handle);
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
HGLRC hglrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hglrc);
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
assert(wglSwapIntervalEXT, "Could not load wglSwapIntervalEXT");
wglSwapIntervalEXT(0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void gfx_update() {
Draw_Quad_Block *block = &draw_frame.first_block;
glBegin(GL_QUADS);
while (block != 0) {
for (u64 i = 0; i < block->num_quads; i++) {
Draw_Quad q = block->quad_buffer[i];
glColor4f(q.color.r, q.color.g, q.color.b, q.color.a);
glVertex2f(v2_expand(q.bottom_left));
glVertex2f(v2_expand(q.top_left));
glVertex2f(v2_expand(q.top_right));
glVertex2f(v2_expand(q.bottom_right));
}
block = block->next;
}
glEnd();
SwapBuffers(hdc);
glClearColor(window.clear_color.r, window.clear_color.g, window.clear_color.b, window.clear_color.a);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, window.width, window.height);
draw_frame = ZERO(Draw_Frame);
}

View file

@ -1,61 +1,94 @@
#define PI32 3.14159265359f
#define PI64 3.14159265358979323846
#define TAU32 (2.0f * PI32)
#define TAU64 (2.0 * PI64)
#define RAD_PER_DEG (PI64 / 180.0)
#define DEG_PER_RAD (180.0 / PI64)
#define to_radians (degrees) (((float32)degrees)*(float32)RAD_PER_DEG)
#define to_degrees (radians) (((float32)radians)*(float32)DEG_PER_RAD)
#define to_radians64(degrees) (((float64)degrees)*(float64)RAD_PER_DEG)
#define to_degrees64(radians) (((float64)radians)*(float64)DEG_PER_RAD)
#define to_radians32 to_radians
#define to_degrees32 to_degrees
typedef struct Vector2 {
union {float x, r;};
union {float y, g;};
union {float32 x, r;};
union {float32 y, g;};
} Vector2;
typedef Vector2 v2;
inline Vector2 v2(float32 x, float32 y) { return (Vector2){x, y}; }
#define v2_expand(v) (v).x, (v).y
typedef struct Vector3 {
union {float x, r;};
union {float y, g;};
union {float z, b;};
union {float32 x, r;};
union {float32 y, g;};
union {float32 z, b;};
} Vector3;
typedef Vector3 v3;
inline Vector3 v3(float32 x, float32 y, float32 z) { return (Vector3){x, y, z}; }
#define v3_expand(v) (v).x, (v).y, (v).z
typedef struct Vector4 {
union {float x, r;};
union {float y, g;};
union {float z, b;};
union {float w, a;};
union {float32 x, r, left; };
union {float32 y, g, bottom;};
union {float32 z, b, right; };
union {float32 w, a, top; };
} Vector4;
typedef Vector4 v4;
inline Vector4 v4(float32 x, float32 y, float32 z, float32 w) { return (Vector4){x, y, z, w}; }
#define v4_expand(v) (v).x, (v).y, (v).z, (v).w
// #Simd #Speed
Vector2 v2_add(Vector2 a, Vector2 b) {
return (v2){a.x + b.x, a.y + b.y};
return v2(a.x + b.x, a.y + b.y);
}
Vector3 v3_add(Vector3 a, Vector3 b) {
return (v3){a.x + b.x, a.y + b.y, a.z + b.z};
return v3(a.x + b.x, a.y + b.y, a.z + b.z);
}
Vector4 v4_add(Vector4 a, Vector4 b) {
return (v4){a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w};
return v4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
Vector2 v2_sub(Vector2 a, Vector2 b) {
return (v2){a.x - b.x, a.y - b.y};
return v2(a.x - b.x, a.y - b.y);
}
Vector3 v3_sub(Vector3 a, Vector3 b) {
return (v3){a.x - b.x, a.y - b.y, a.z - b.z};
return v3(a.x - b.x, a.y - b.y, a.z - b.z);
}
Vector4 v4_sub(Vector4 a, Vector4 b) {
return (v4){a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w};
return v4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}
Vector2 v2_mul(Vector2 a, Vector2 b) {
return (v2){a.x * b.x, a.y * b.y};
return v2(a.x * b.x, a.y * b.y);
}
Vector3 v3_mul(Vector3 a, Vector3 b) {
return (v3){a.x * b.x, a.y * b.y, a.z * b.z};
return v3(a.x * b.x, a.y * b.y, a.z * b.z);
}
Vector4 v4_mul(Vector4 a, Vector4 b) {
return (v4){a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w};
return v4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
Vector2 v2_div(Vector2 a, Vector2 b) {
return (v2){a.x / b.x, a.y / b.y};
return v2(a.x / b.x, a.y / b.y);
}
Vector3 v3_div(Vector3 a, Vector3 b) {
return (v3){a.x / b.x, a.y / b.y, a.z / b.z};
return v3(a.x / b.x, a.y / b.y, a.z / b.z);
}
Vector4 v4_div(Vector4 a, Vector4 b) {
return (v4){a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w};
return v4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
}
Vector2 v2_rotate_point_around_pivot(Vector2 point, Vector2 pivot, float32 rotation_radians) {
float32 s = sin(rotation_radians);
float32 c = cos(rotation_radians);
point.x -= pivot.x;
point.y -= pivot.y;
float32 x_new = point.x * c - point.y * s;
float32 y_new = point.x * s + point.y * c;
point.x = x_new + pivot.x;
point.y = y_new + pivot.y;
return point;
}

View file

@ -28,6 +28,9 @@ void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message
case ALLOCATOR_DEALLOCATE: {
return 0;
}
case ALLOCATOR_REALLOCATE: {
return 0;
}
}
return 0;
}
@ -373,6 +376,29 @@ void heap_dealloc(void *p) {
os_spinlock_unlock(heap_lock);
}
void* heap_allocator_proc(u64 size, void *p, Allocator_Message message) {
switch (message) {
case ALLOCATOR_ALLOCATE: {
return heap_alloc(size);
break;
}
case ALLOCATOR_DEALLOCATE: {
assert(is_pointer_valid(p), "Invalid pointer passed to heap allocator deallocate");
heap_dealloc(p);
return 0;
}
case ALLOCATOR_REALLOCATE: {
assert(is_pointer_valid(p), "Invalid pointer passed to heap allocator reallocate");
Heap_Allocation_Metadata *meta = (Heap_Allocation_Metadata*)((u64)p)-sizeof(Heap_Allocation_Metadata);
void *new = heap_alloc(size);
memcpy(new, p, min(size, meta->size));
heap_dealloc(p);
return new;
}
}
return 0;
}
///
///
// Temporary storage
@ -401,6 +427,9 @@ void* temp_allocator_proc(u64 size, void *p, Allocator_Message message) {
case ALLOCATOR_DEALLOCATE: {
return 0;
}
case ALLOCATOR_REALLOCATE: {
return 0;
}
}
return 0;
}

View file

@ -1,4 +1,30 @@
// This needs to be included before dependencies
#include "base.c"
///
///
// Dependencies
///
#include <math.h>
// Custom allocators for lodepng
void* lodepng_malloc(size_t size) {
return context.allocator.proc((u64)size, 0, ALLOCATOR_ALLOCATE);
}
void* lodepng_realloc(void* ptr, size_t new_size) {
return context.allocator.proc((u64)new_size, 0, ALLOCATOR_REALLOCATE);
}
void lodepng_free(void* ptr) {
context.allocator.proc(0, ptr, ALLOCATOR_DEALLOCATE);
}
#define LODEPNG_NO_COMPILE_ALLOCATORS
#include "third_party/lodepng.c"
/////
#if !defined(DEBUG) && !defined(RELEASE)
#ifdef _DEBUG
@ -28,12 +54,34 @@
#error "Current OS not supported!";
#endif
#include "base.c"
#define GFX_RENDERER_D3D11 0
#define GFX_RENDERER_VULKAN 1
#define GFX_RENDERER_METAL 2
#define GFX_RENDERER_LEGACY_OPENGL 3 // #Temporary #Cleanup
// #Temporary #Cleanup
#undef GFX_RENDERER
#define GFX_RENDERER GFX_RENDERER_LEGACY_OPENGL
#ifndef GFX_RENDERER
// #Portability
#ifdef OS_WINDOWS
#define GFX_RENDERER GFX_RENDERER_D3D11
#elif defined (OS_LINUX)
#define GFX_RENDERER GFX_RENDERER_VULKAN
#elif defined (OS_MAC)
#define GFX_RENDERER GFX_RENDERER_METAL
#endif
#endif
#include "string.c"
#include "string_format.c"
#include "os_interface.c"
#include "random.c"
#include "linmath.c"
#include "memory.c"
@ -41,6 +89,19 @@
#include "drawing.c"
// #Portability
#if GFX_RENDERER == GFX_RENDERER_D3D11
#include "gfx_impl_d3d11.c"
#elif GFX_RENDERER == GFX_RENDERER_VULKAN
#error "We only have a D3D11 renderer at the moment"
#elif GFX_RENDERER == GFX_RENDERER_METAL
#error "We only have a D3D11 renderer at the moment"
#elif GFX_RENDERER == GFX_RENDERER_LEGACY_OPENGL
#include "gfx_impl_legacy_opengl.c"
#else
#error "Unknown renderer defined in GFX_RENDERER"
#endif
#ifdef OS_WINDOWS
#include "os_impl_windows.c"
#elif defined (OS_LINUX)
@ -54,6 +115,7 @@
void oogabooga_init(u64 program_memory_size) {
os_init(program_memory_size);
gfx_init();
heap_init();
temporary_storage_init();
}

View file

@ -5,19 +5,7 @@
void* heap_alloc(u64);
void heap_dealloc(void*);
void* heap_allocator_proc(u64 size, void *p, Allocator_Message message) {
switch (message) {
case ALLOCATOR_ALLOCATE: {
return heap_alloc(size);
break;
}
case ALLOCATOR_DEALLOCATE: {
heap_dealloc(p);
return 0;
}
}
return 0;
}
LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wparam, LPARAM lparam) {
@ -32,22 +20,13 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
break;
case WM_MOVE:
break;
default:
return DefWindowProc(passed_window, message, wparam, lparam);
}
return 0;
}
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
#include "GL/gl.h"
HDC hdc;
void os_init(u64 program_memory_size) {
SYSTEM_INFO si;
@ -153,34 +132,7 @@ void os_init(u64 program_memory_size) {
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
// #Temporary #Cleanup
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0, 0, 0, 0, 0, 0,
0, 0,
0, 0, 0, 0, 0,
24,
8,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
hdc = GetDC(window._os_handle);
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
HGLRC hglrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hglrc);
}
bool os_grow_program_memory(u64 new_size) {
@ -447,10 +399,7 @@ void os_update() {
last_window = window;
SwapBuffers(hdc);
glClearColor(window.clear_color.r, window.clear_color.g, window.clear_color.b, window.clear_color.a);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, window.width, window.height);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {

13
oogabooga/random.c Normal file
View file

@ -0,0 +1,13 @@
// This is a naive implementation for now (LCG)
// Distribution will probably suck.
#define RAND_MAX_64 0xFFFFFFFFFFFFFFFFull
#define MULTIPLIER 6364136223846793005ull
#define INCREMENT 1ull
u64 seed_for_random = 1;
u64 get_random() {
seed_for_random = seed_for_random * MULTIPLIER + INCREMENT;
return seed_for_random;
}

6999
oogabooga/third_party/lodepng.c vendored Normal file

File diff suppressed because it is too large Load diff