commit dab7136
uint
·
2026-07-30 00:25:02 +0000 UTC
parent ba23b8e
dynamic textures with MGTexture
4 files changed,
+140,
-67
+11,
-2
1@@ -8,6 +8,9 @@
2 typedef u32 MGMesh;
3 #define MGMESH_INVALID ((MGMesh) 0)
4
5+typedef u32 MGTexture;
6+#define MGTEXTURE_INVALID ((MGTexture) 0)
7+
8 typedef struct {
9 float x;
10 float y;
11@@ -42,11 +45,17 @@ void renderer_destroy(void);
12 /* create mesh from descriptor */
13 MGMesh renderer_mesh_create(const MGMeshDesc* desc);
14
15-/* draws mesh using specified mvp mat */
16-void renderer_mesh_draw(MGMesh id, const MGMat4* mvp);
17+/* draws mesh using specified texture and mat */
18+void renderer_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp);
19
20 /* destroy mesh */
21 void renderer_mesh_destroy(MGMesh id);
22
23+/* load texture from file */
24+MGTexture renderer_texture_load(const char* path);
25+
26+/* destroy texture */
27+void renderer_texture_destroy(MGTexture id);
28+
29 #endif /* RENDERER_H */
30
+8,
-3
1@@ -8,14 +8,18 @@
2 #include "renderer/test_block.h"
3
4 static MGMesh block = MGMESH_INVALID;
5+static MGTexture block_texture = MGTEXTURE_INVALID;
6 static MGCamera cam;
7
8 void test_scene_create(void)
9 {
10 block = block_mesh_create();
11-
12 if (block == MGMESH_INVALID)
13- mg_die(MG_ERR_REN_CREATE, "Failed to create test block mesh");
14+ mg_die(MG_ERR_MESH_CREATE, "Failed to create test block mesh");
15+
16+ block_texture = renderer_texture_load("assets/resourcepacks/default/blocks/brick.png");
17+ if (block_texture == MGTEXTURE_INVALID)
18+ mg_die(MG_ERR_TEXTURE_LOAD, "Failed to load test block texture");
19
20 camera_init(&cam, MG_V3(3.0f, 3.0f, 3.0f));
21 cam.yaw = MG_AngleDeg(-140.0f);
22@@ -45,12 +49,13 @@ void test_scene_draw(MGContext* ctx)
23
24 mvp = MG_MulM4(proj, MG_MulM4(view, model));
25
26- renderer_mesh_draw(block, &mvp);
27+ renderer_mesh_draw(block, block_texture, &mvp);
28 }
29
30 void test_scene_destroy(void)
31 {
32 renderer_mesh_destroy(block);
33+ renderer_texture_destroy(block_texture);
34 block = MGMESH_INVALID;
35 }
36
+2,
-0
1@@ -15,6 +15,8 @@ typedef enum { /* TODO: rename to MG_EC_... */
2
3 MG_ERR_PIPELINE_CREATE,
4
5+ MG_ERR_MESH_CREATE,
6+
7 MG_ERR_TEXTURE_LOAD,
8 MG_ERR_TEXTURE_CREATE,
9 MG_ERR_SAMPLER_CREATE,
+119,
-62
1@@ -7,6 +7,7 @@
2 #include "renderer/renderer.h"
3
4 #define MAX_MESHES (512) /* TODO; random value ig */
5+#define MAX_TEXTURES (512) /* TODO; random value ig */
6
7 typedef struct {
8 sg_buffer vb; /* vertex buffer */
9@@ -15,6 +16,12 @@ typedef struct {
10 i8 used;
11 } RendererMesh;
12
13+typedef struct {
14+ sg_image image;
15+ sg_view view;
16+ i8 used;
17+} RendererTexture;
18+
19 typedef struct {
20 MGContext* ctx;
21 i8 pass; /* basically a bool; in
22@@ -22,18 +29,17 @@ typedef struct {
23
24 sg_shader shader;
25 sg_pipeline pipeline;
26+ sg_sampler sampler;
27
28- sg_image texture;
29- sg_view texture_view;
30- sg_sampler sampler;
31-
32- RendererMesh meshes[MAX_MESHES];
33+ RendererMesh meshes[MAX_MESHES];
34+ RendererTexture textures[MAX_MESHES];
35 } RendererState;
36
37 /* create the shader and render pipeline */
38 static void create_pipelines(void);
39
40-static void create_texture(void);
41+/* create texture sampler */
42+static void create_sampler(void);
43
44 /* logging function for passing to sokol */
45 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user);
46@@ -120,54 +126,19 @@ static void create_pipelines(void)
47 mg_die(MG_ERR_PIPELINE_CREATE, "Failed to create pipeline");
48 }
49
50-static void create_texture(void)
51+static void create_sampler(void)
52 {
53- sg_image_desc image_desc = {0};
54- sg_sampler_desc sampler_desc = {0};
55- sg_view_desc view_desc = {0};
56- stbi_uc* pixels;
57- int width;
58- int height;
59- int chanells;
60-
61- pixels = stbi_load(
62- /* "assets/resourcepacks/default/blocks/brick.png", */
63- /* "assets/resourcepacks/default/blocks/dirt.png", */
64- "assets/smile_test.png",
65- &width, &height, &chanells, 4
66- );
67-
68- if (!pixels)
69- mg_die(MG_ERR_TEXTURE_LOAD, "failed to load texture");
70-
71- /* image */
72- image_desc.width = width;
73- image_desc.height = height;
74- image_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
75- image_desc.data.mip_levels[0].ptr = pixels;
76- image_desc.data.mip_levels[0].size = width * height * 4;
77- ren.texture = sg_make_image(&image_desc);
78- stbi_image_free(pixels);
79+ sg_sampler_desc desc = {0};
80
81- if (sg_query_image_state(ren.texture) != SG_RESOURCESTATE_VALID)
82- mg_die(MG_ERR_TEXTURE_CREATE, "failed to create texture");
83+ desc.min_filter = SG_FILTER_NEAREST;
84+ desc.mag_filter = SG_FILTER_NEAREST;
85+ desc.wrap_u = SG_WRAP_REPEAT;
86+ desc.wrap_v = SG_WRAP_REPEAT;
87
88- /* texture view */
89- view_desc.texture.image = ren.texture;
90- ren.texture_view = sg_make_view(&view_desc);
91-
92- if (sg_query_view_state(ren.texture_view) != SG_RESOURCESTATE_VALID)
93- mg_die(MG_ERR_VIEW_CREATE, "failed to texture view");
94-
95- /* sampler */
96- sampler_desc.min_filter = SG_FILTER_NEAREST;
97- sampler_desc.mag_filter = SG_FILTER_NEAREST;
98- sampler_desc.wrap_u = SG_WRAP_REPEAT;
99- sampler_desc.wrap_v = SG_WRAP_REPEAT;
100- ren.sampler = sg_make_sampler(&sampler_desc);
101+ ren.sampler = sg_make_sampler(&desc);
102
103 if (sg_query_sampler_state(ren.sampler) != SG_RESOURCESTATE_VALID)
104- mg_die(MG_ERR_SAMPLER_CREATE, "failed to create sampler");
105+ mg_die(MG_ERR_SAMPLER_CREATE, "Failed to create sampler");
106 }
107
108 static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln, const char* file, void* user)
109@@ -205,7 +176,7 @@ void renderer_create(MGContext* ctx)
110 mg_die(MG_ERR_REN_CREATE, "Failed to initialise renderer");
111
112 create_pipelines();
113- create_texture();
114+ create_sampler();
115 }
116
117 void renderer_begin(void)
118@@ -257,13 +228,15 @@ void renderer_end(void)
119 void renderer_destroy(void)
120 {
121 u32 id;
122+
123+ /* the values could be different in future so
124+ probably should keep them separate */
125 for (id = 1; id < MAX_MESHES; id++)
126 renderer_mesh_destroy(id);
127+ for (id = 1; id < MAX_TEXTURES; id++)
128+ renderer_texture_destroy(id);
129
130- sg_destroy_view(ren.texture_view);
131 sg_destroy_sampler(ren.sampler);
132- sg_destroy_image(ren.texture);
133-
134 sg_destroy_pipeline(ren.pipeline);
135 sg_destroy_shader(ren.shader);
136
137@@ -327,25 +300,30 @@ MGMesh renderer_mesh_create(const MGMeshDesc* desc)
138 return id;
139 }
140
141-void renderer_mesh_draw(MGMesh id, const MGMat4* mvp)
142+void renderer_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp)
143 {
144- RendererMesh* mesh;
145+ RendererMesh* rmesh;
146+ RendererTexture* rtexture;
147 sg_bindings bindings = {0};
148 sg_range uniforms;
149
150 if (!ren.pass || !mvp)
151 return;
152
153- if (id == MGMESH_INVALID || id >= MAX_MESHES)
154+ if (mesh == MGMESH_INVALID || mesh >= MAX_MESHES)
155 return;
156
157- mesh = &ren.meshes[id];
158- if (!mesh->used)
159+ if (texture == MGTEXTURE_INVALID || texture >= MAX_TEXTURES)
160 return;
161
162- bindings.vertex_buffers[0] = mesh->vb;
163- bindings.index_buffer = mesh->ib;
164- bindings.views[0] = ren.texture_view;
165+ rmesh = &ren.meshes[mesh];
166+ rtexture = &ren.textures[texture];
167+ if (!rmesh->used || !rtexture->used)
168+ return;
169+
170+ bindings.vertex_buffers[0] = rmesh->vb;
171+ bindings.index_buffer = rmesh->ib;
172+ bindings.views[0] = rtexture->view;
173 bindings.samplers[0] = ren.sampler;
174
175 uniforms.ptr = &mvp->Elements[0][0];
176@@ -355,7 +333,7 @@ void renderer_mesh_draw(MGMesh id, const MGMat4* mvp)
177 sg_apply_bindings(&bindings);
178 sg_apply_uniforms(0, &uniforms);
179
180- sg_draw(0, mesh->ic, 1);
181+ sg_draw(0, rmesh->ic, 1);
182 }
183
184 void renderer_mesh_destroy(MGMesh id)
185@@ -375,3 +353,82 @@ void renderer_mesh_destroy(MGMesh id)
186 *mesh = (RendererMesh){0};
187 }
188
189+MGTexture renderer_texture_load(const char* path)
190+{
191+ sg_image_desc image_desc = {0};
192+ sg_view_desc view_desc = {0};
193+ RendererTexture* texture;
194+ stbi_uc* pixels;
195+ int width;
196+ int height;
197+ int channels;
198+ u32 id;
199+
200+ if (!path)
201+ return MGTEXTURE_INVALID;
202+
203+ /* find free texture slot */
204+ for (id = 1; id < MAX_TEXTURES; id++) {
205+ if (!ren.textures[id].used)
206+ break;
207+ }
208+
209+ if (id == MAX_TEXTURES) {
210+ mg_log(LOG_ERR, "Maximum texture limit reached");
211+ return MGTEXTURE_INVALID;
212+ }
213+
214+ texture = &ren.textures[id];
215+ pixels = stbi_load(path, &width, &height, &channels, 4);
216+ if (!pixels) {
217+ mg_log(LOG_ERR, "Failed to load texture: %s", path);
218+ return MGTEXTURE_INVALID;
219+ }
220+
221+ image_desc.width = width;
222+ image_desc.height = height;
223+ image_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
224+ image_desc.data.mip_levels[0].ptr = pixels;
225+ image_desc.data.mip_levels[0].size =width * height * 4;
226+
227+ texture->image = sg_make_image(&image_desc);
228+ stbi_image_free(pixels);
229+ if (sg_query_image_state(texture->image) != SG_RESOURCESTATE_VALID) {
230+ *texture = (RendererTexture){0};
231+ mg_log(LOG_ERR, "Failed to create texture: %s", path);
232+ return MGTEXTURE_INVALID;
233+ }
234+
235+ view_desc.texture.image = texture->image;
236+ texture->view = sg_make_view(&view_desc);
237+
238+ if (sg_query_view_state(texture->view) != SG_RESOURCESTATE_VALID) {
239+ sg_destroy_image(texture->image);
240+ *texture = (RendererTexture){0};
241+ mg_log(LOG_ERR, "Failed to create texture view: %s", path);
242+ return MGTEXTURE_INVALID;
243+ }
244+
245+ texture->used = 1;
246+
247+ return id;
248+}
249+
250+void renderer_texture_destroy(MGTexture id)
251+{
252+ RendererTexture* texture;
253+
254+ if (id == MGTEXTURE_INVALID || id >= MAX_TEXTURES)
255+ return;
256+
257+ texture = &ren.textures[id];
258+
259+ if (!texture->used)
260+ return;
261+
262+ sg_destroy_view(texture->view);
263+ sg_destroy_image(texture->image);
264+
265+ *texture = (RendererTexture){0};
266+}
267+