commit 489391e

uint  ·  2026-07-23 12:56:23 +0000 UTC
parent 4b3b38c
add sokol rendering and mesh setup

Also compiling with C99 for now but thats only to get maths library to
work
7 files changed,  +361, -10
+32, -1
 1@@ -1,7 +1,29 @@
 2 #ifndef RENDERER_H
 3 #define RENDERER_H
 4 
 5-#include <platform/platform.h>
 6+#include "types.h"
 7+#include "platform/platform.h"
 8+
 9+typedef u32 MGMesh;
10+#define MGMESH_INVALID ((MGMesh) 0)
11+
12+typedef struct {
13+	float x;
14+	float y;
15+	float z;
16+
17+	float u;
18+	float v;
19+
20+	u32   col;
21+} MGVertex;
22+
23+typedef struct {
24+	const MGVertex* vtc;  /* vertices */
25+	u32             vtcc; /* vertices count */
26+	const u16*      idc;  /* indecies */
27+	u32             idcc; /* indecies count */
28+} MGMeshDesc;
29 
30 /* create renderer with chosen context
31    (just GL for now) */
32@@ -16,5 +38,14 @@ void renderer_end(void);
33 /* destroy renderer resources */
34 void renderer_destroy(void);
35 
36+/* create mesh from descriptor */
37+MGMesh renderer_mesh_create(const MGMeshDesc* desc);
38+
39+/* draws mesh using specified mvp mat */
40+void renderer_mesh_draw(MGMesh id, const float mvp[16]); /* TODO: use MGMat4 or wtv itll be called */
41+
42+/* destroy mesh */
43+void renderer_mesh_destroy(MGMesh id);
44+
45 #endif /* RENDERER_H */
46 
+69, -0
 1@@ -0,0 +1,69 @@
 2+#ifndef TEST_BLOCK_H
 3+#define TEST_BLOCK_H
 4+
 5+#include "renderer/renderer.h"
 6+
 7+#define RGBA(r, g, b, a) ( \
 8+	((u32)(r)      ) | \
 9+	((u32)(g) <<  8) | \
10+	((u32)(b) << 16) | \
11+	((u32)(a) << 24)   \
12+)
13+
14+MGMesh block_mesh_create(void);
15+
16+static const MGVertex vtc[24] = {
17+	/* north: -z */
18+	{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 0, 255)},
19+	{0.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 0, 255)},
20+	{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 0, 255)},
21+	{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 0, 255)},
22+
23+	/* south: +z */
24+	{1.0f, 0.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 0, 255)},
25+	{1.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 0, 255)},
26+	{0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 0, 255)},
27+	{0.0f, 0.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 0, 255)},
28+
29+	/* west: -x */
30+	{0.0f, 0.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 0, 255, 255)},
31+	{0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 0, 255, 255)},
32+	{0.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(0, 0, 255, 255)},
33+	{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(0, 0, 255, 255)},
34+
35+	/* east: +x */
36+	{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 255, 0, 255)},
37+	{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 255, 0, 255)},
38+	{1.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(255, 255, 0, 255)},
39+	{1.0f, 0.0f, 1.0f, 0.0f, 0.0f, RGBA(255, 255, 0, 255)},
40+
41+	/* bottom: -y */
42+	{0.0f, 0.0f, 1.0f, 0.0f, 0.0f, RGBA(255, 0, 255, 255)},
43+	{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 255, 255)},
44+	{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, RGBA(255, 0, 255, 255)},
45+	{1.0f, 0.0f, 1.0f, 0.0f, 0.0f, RGBA(255, 0, 255, 255)},
46+
47+	/* top: +y */
48+	{0.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(0, 255, 255, 255)},
49+	{0.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 255, 255)},
50+	{1.0f, 1.0f, 1.0f, 0.0f, 0.0f, RGBA(0, 255, 255, 255)},
51+	{1.0f, 1.0f, 0.0f, 0.0f, 0.0f, RGBA(0, 255, 255, 255)}
52+};
53+
54+static const u16 idc[36] = {
55+	0,  1,  2,  0,  2,  3,
56+	4,  5,  6,  4,  6,  7,
57+	8,  9,  10, 8,  10, 11,
58+	12, 13, 14, 12, 14, 15,
59+	16, 17, 18, 16, 18, 19,
60+	20, 21, 22, 20, 22, 23,
61+};
62+
63+MGMesh block_mesh_create(void)
64+{
65+	MGMeshDesc desc = { vtc, 24, idc, 36 };
66+	return renderer_mesh_create(&desc);
67+}
68+
69+#endif /* TEST_BLOCK_H */
70+
+59, -0
 1@@ -0,0 +1,59 @@
 2+#ifndef TEST_SCENE_H
 3+#define TEST_SCENE_H
 4+
 5+#include <HandmadeMath.h>
 6+
 7+#include "util.h"
 8+#include "renderer/renderer.h"
 9+#include "renderer/test_block.h"
10+
11+static MGMesh block = MGMESH_INVALID;
12+
13+void test_scene_create(void)
14+{
15+	block = block_mesh_create();
16+
17+	if (block == MGMESH_INVALID)
18+		mg_die(MG_ERR_REN_CREATE, "Failed to create test block mesh");
19+}
20+
21+void test_scene_draw(MGContext* ctx)
22+{
23+	HMM_Mat4 proj;
24+	HMM_Mat4 view;
25+	HMM_Mat4 model;
26+	HMM_Mat4 view_model;
27+	HMM_Mat4 mvp;
28+	i32 width;
29+	i32 height;
30+	float aspect;
31+
32+	platform_get_drawable_size(ctx, &width, &height);
33+
34+	if (width <= 0 || height <= 0)
35+		return;
36+
37+	aspect = (float)width / (float)height;
38+
39+	proj = HMM_Perspective_RH_NO(HMM_AngleDeg(70.0f), aspect, 0.1f, 100.0f);
40+	view = HMM_LookAt_RH(
41+		HMM_V3(2.5f, 2.0f, 3.0f),
42+		HMM_V3(0.5f, 0.5f, 0.5f),
43+		HMM_V3(0.0f, 1.0f, 0.0f)
44+	);
45+
46+	model = HMM_M4D(1.0f);
47+	view_model = HMM_MulM4(view, model);
48+	mvp = HMM_MulM4(proj, view_model);
49+
50+	renderer_mesh_draw(block, &mvp.Elements[0][0]);
51+}
52+
53+void test_scene_destroy(void)
54+{
55+	renderer_mesh_destroy(block);
56+	block = MGMESH_INVALID;
57+}
58+
59+#endif /* TEST_SCENE_H */
60+
+4, -2
 1@@ -1,8 +1,6 @@
 2 #ifndef UTIL_H
 3 #define UTIL_H
 4 
 5-#include <stdio.h>
 6-
 7 typedef enum { /* TODO: rename to MG_EC_... */
 8 	MG_ERR_EXIT_SUCCESS,
 9 	MG_ERR_EXIT_FAILURE,
10@@ -13,6 +11,10 @@ typedef enum { /* TODO: rename to MG_EC_... */
11 	MG_ERR_GL_CTX_CREATE,
12 	MG_ERR_GL_CTX_ACTIVATE,
13 
14+	MG_ERR_SHADER_CREATE,
15+
16+	MG_ERR_PIPELINE_CREATE,
17+
18 	MG_ERR_REN_CREATE,
19 
20 	MG_ERR_LAST
+1, -1
1@@ -2,7 +2,7 @@ project(
2 	'Magnolia',
3 	'c',
4 	default_options: [
5-		'c_std=c89',
6+		'c_std=c99',
7 		'warning_level=3',
8 	],
9 )
+4, -6
 1@@ -1,8 +1,7 @@
 2-#include <stdlib.h>
 3-
 4 #include "util.h"
 5 #include "platform/platform.h"
 6 #include "renderer/renderer.h"
 7+#include "renderer/test_scene.h"
 8 
 9 int main(void)
10 {
11@@ -14,24 +13,23 @@ int main(void)
12 		1024, 768,  /* w, h */
13 	};
14 
15-	mg_log(LOG_INF, "test log info");
16-	mg_log(LOG_ERR, "test log error");
17-	mg_log(LOG_DBG, "test log debug");
18-
19 	platform_init(&ctx);
20 	platform_window_create(&ctx);
21 	renderer_create(&ctx);
22+	test_scene_create();
23 
24 	while (platform_poll(&ctx)) {
25 		renderer_begin();
26 
27 		/* ... */
28+		test_scene_draw(&ctx);
29 
30 		renderer_end();
31 		platform_present(&ctx);
32 		SDL_Delay(1); /* TODO: replace with target fps */
33 	}
34 
35+	test_scene_destroy();
36 	renderer_destroy();
37 	platform_shutdown(&ctx);
38 	return MG_ERR_EXIT_SUCCESS;
+192, -0
  1@@ -5,16 +5,100 @@
  2 #include "platform/platform.h"
  3 #include "renderer/renderer.h"
  4 
  5+#define MAX_MESHES (512) /* TODO; random value ig */
  6+
  7+typedef struct {
  8+	sg_buffer vb; /* vertex buffer */
  9+	sg_buffer ib; /* index buffer */
 10+	u32       ic; /* index count */
 11+	i8        used;
 12+} RendererMesh;
 13+
 14 typedef struct {
 15 	MGContext* ctx;
 16 	i8         pass; /* basically a bool; in
 17 	                    render pass or not */
 18+
 19+	sg_shader   shader;
 20+	sg_pipeline pipeline;
 21+
 22+	RendererMesh meshes[MAX_MESHES];
 23 } RendererState;
 24 
 25+/* create the shader and render pipeline */
 26+static void create_pipelines(void);
 27+
 28 /* logging function for passing to sokol  */
 29 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user);
 30 
 31 static RendererState ren;
 32+/* TODO: textures */
 33+static const char* vsdr =
 34+"#version 410\n"
 35+"layout(location=0) in vec3 position;\n"
 36+"layout(location=1) in vec2 texcoord0;\n"
 37+"layout(location=2) in vec4 color0;\n"
 38+"\n"
 39+"uniform mat4 mvp;\n"
 40+"\n"
 41+"out vec2 uv;\n"
 42+"out vec4 colour;\n"
 43+"\n"
 44+"void main(void)\n"
 45+"{\n"
 46+"    gl_Position = mvp * vec4(position, 1.0);\n"
 47+"    uv = texcoord0;\n"
 48+"    colour = color0;\n"
 49+"}\n";
 50+static const char* fsdr =
 51+"#version 410\n"
 52+"in vec2 uv;\n"
 53+"in vec4 colour;\n"
 54+"\n"
 55+"out vec4 frag_colour;\n"
 56+"\n"
 57+"void main(void)\n"
 58+"{\n"
 59+"    frag_colour = colour;\n"
 60+"}\n";
 61+
 62+static void create_pipelines(void)
 63+{
 64+	sg_shader_desc shader_desc = {0};
 65+	sg_pipeline_desc pipeline_desc = {0};
 66+
 67+	/* shader descriptor */
 68+	shader_desc.vertex_func.source = vsdr;
 69+	shader_desc.fragment_func.source = fsdr;
 70+	shader_desc.attrs[0].glsl_name = "position";
 71+	shader_desc.attrs[1].glsl_name = "texcoord0";
 72+	shader_desc.attrs[2].glsl_name = "color0";
 73+	shader_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX;
 74+	shader_desc.uniform_blocks[0].size = sizeof(float) * 16; /* size of mvp matrix */
 75+	shader_desc.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140;
 76+	shader_desc.uniform_blocks[0] .glsl_uniforms[0].glsl_name = "mvp";
 77+	shader_desc.uniform_blocks[0] .glsl_uniforms[0].type = SG_UNIFORMTYPE_MAT4;
 78+
 79+	ren.shader = sg_make_shader(&shader_desc);
 80+	if (sg_query_shader_state(ren.shader) != SG_RESOURCESTATE_VALID)
 81+		mg_die(MG_ERR_SHADER_CREATE, "Failed to create shader");
 82+
 83+	/* pipeline descriptor */
 84+	pipeline_desc.shader = ren.shader;
 85+	pipeline_desc.layout.buffers[0].stride = sizeof(MGVertex);
 86+	pipeline_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3;
 87+	pipeline_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
 88+	pipeline_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
 89+	pipeline_desc.index_type = SG_INDEXTYPE_UINT16;
 90+	pipeline_desc.depth.compare = SG_COMPAREFUNC_LESS_EQUAL;
 91+	pipeline_desc.depth.write_enabled = true;
 92+	pipeline_desc.cull_mode = SG_CULLMODE_BACK;
 93+	pipeline_desc.face_winding = SG_FACEWINDING_CCW;
 94+
 95+	ren.pipeline = sg_make_pipeline(&pipeline_desc);
 96+	if (sg_query_pipeline_state(ren.pipeline) != SG_RESOURCESTATE_VALID)
 97+		mg_die(MG_ERR_PIPELINE_CREATE, "Failed to create pipeline");
 98+}
 99 
100 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user)
101 {
102@@ -49,6 +133,8 @@ void renderer_create(MGContext* ctx)
103 
104 	if (!sg_isvalid())
105 		mg_die(MG_ERR_REN_CREATE, "Failed to initialise renderer");
106+
107+	create_pipelines();
108 }
109 
110 void renderer_begin(void)
111@@ -99,9 +185,115 @@ void renderer_end(void)
112 
113 void renderer_destroy(void)
114 {
115+	u32 id;
116+	for (id = 1; id < MAX_MESHES; id++)
117+		renderer_mesh_destroy(id);
118+
119+	sg_destroy_pipeline(ren.pipeline);
120+	sg_destroy_shader(ren.shader);
121+
122 	if (sg_isvalid())
123 		sg_shutdown();
124 	ren.ctx = NULL;
125 	ren.pass = 0;
126 }
127 
128+MGMesh renderer_mesh_create(const MGMeshDesc* desc)
129+{
130+	sg_buffer_desc buffer_desc = {0};
131+	RendererMesh* mesh;
132+	u32 id;
133+
134+	if (!desc || !desc->vtc || !desc->idc || desc->vtcc == 0 || desc->idcc == 0)
135+		return MGMESH_INVALID;
136+
137+	/* find free mesh slot */
138+	for (id = 1; id < MAX_MESHES; id++) {
139+		if (!ren.meshes[id].used)
140+			break;
141+	}
142+
143+	if (id == MAX_MESHES) {
144+		mg_log(LOG_ERR, "Maximum mesh limit reached");
145+		return MGMESH_INVALID;
146+	}
147+
148+	/* vertex buffer */
149+	mesh = &ren.meshes[id];
150+	buffer_desc.data.ptr = desc->vtc;
151+	buffer_desc.data.size = desc->vtcc * sizeof(MGVertex);
152+	mesh->vb = sg_make_buffer(&buffer_desc);
153+
154+	/* index buffer */
155+	buffer_desc = (sg_buffer_desc){0};
156+	buffer_desc.usage.vertex_buffer = false;
157+	buffer_desc.usage.index_buffer = true;
158+	buffer_desc.usage.immutable = true;
159+	buffer_desc.data.ptr = desc->idc;
160+	buffer_desc.data.size = desc->idcc * sizeof(u16);
161+	mesh->ib = sg_make_buffer(&buffer_desc);
162+
163+	i8 valid = sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID &&
164+	           sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID;
165+	if (!valid) {
166+		if (sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID)
167+			sg_destroy_buffer(mesh->vb);
168+		if (sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID)
169+			sg_destroy_buffer(mesh->ib);
170+
171+		*mesh = (RendererMesh){0};
172+		mg_log(LOG_ERR, "Failed to create mesh buffers");
173+		return MGMESH_INVALID;
174+	}
175+
176+	mesh->ic = desc->idcc;
177+	mesh->used = 1;
178+	return id;
179+}
180+
181+void renderer_mesh_draw(MGMesh id, const float mvp[16])
182+{
183+	RendererMesh* mesh;
184+	sg_bindings bindings = {0};
185+	sg_range uniforms;
186+
187+	if (!ren.pass || !mvp)
188+		return;
189+
190+	if (id == MGMESH_INVALID || id >= MAX_MESHES)
191+		return;
192+
193+	mesh = &ren.meshes[id];
194+	if (!mesh->used)
195+		return;
196+
197+	bindings.vertex_buffers[0] = mesh->vb;
198+	bindings.index_buffer = mesh->ib;
199+
200+	uniforms.ptr = mvp;
201+	uniforms.size = sizeof(float) * 16;
202+
203+	sg_apply_pipeline(ren.pipeline);
204+	sg_apply_bindings(&bindings);
205+	sg_apply_uniforms(0, &uniforms);
206+
207+	sg_draw(0, mesh->ic, 1);
208+}
209+
210+void renderer_mesh_destroy(MGMesh id)
211+{
212+	RendererMesh* mesh;
213+
214+	if (id == MGMESH_INVALID || id >= MAX_MESHES)
215+		return;
216+
217+	mesh = &ren.meshes[id];
218+	if (!mesh->used)
219+		return;
220+
221+	sg_destroy_buffer(mesh->ib);
222+	sg_destroy_buffer(mesh->vb);
223+
224+	*mesh = (RendererMesh){0};
225+}
226+