refactor rendering 1/3 - create new mesh structs

Sat, 10 May 2025 18:51:45 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 10 May 2025 18:51:45 +0200
changeset 115
e5f8c99b0987
parent 114
5b91bbab1ac0
child 116
bfb2a7d62047

refactor rendering 1/3 - create new mesh structs

shader/sprite_vtx.glsl file | annotate | diff | comparison | revisions
src/Makefile file | annotate | diff | comparison | revisions
src/ascension/2d/sprite.h file | annotate | diff | comparison | revisions
src/ascension/datatypes.h file | annotate | diff | comparison | revisions
src/ascension/glcontext.h file | annotate | diff | comparison | revisions
src/ascension/mesh.h file | annotate | diff | comparison | revisions
src/ascension/primitives.h file | annotate | diff | comparison | revisions
src/glcontext.c file | annotate | diff | comparison | revisions
src/mesh.c file | annotate | diff | comparison | revisions
src/primitives.c file | annotate | diff | comparison | revisions
src/sprite.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
test/snake/Makefile file | annotate | diff | comparison | revisions
--- a/shader/sprite_vtx.glsl	Sat May 10 15:42:56 2025 +0200
+++ b/shader/sprite_vtx.glsl	Sat May 10 18:51:45 2025 +0200
@@ -1,6 +1,7 @@
 #version 400 core
 
-layout(location = 0) in vec2 position;
+layout(location = 0) in vec2 in_pos;
+layout(location = 1) in vec2 in_uv;
 out vec2 texcoord;
 out vec2 uvcoord;
 
@@ -9,8 +10,9 @@
 uniform mat4 model;
 
 void main(void) {
-    vec4 pos = projection*view*model*vec4(position.xy, 0.0, 1.0);
+    vec4 pos = projection*view*model*vec4(in_pos.xy, 0.0, 1.0);
     gl_Position = pos;
-    uvcoord = position;
-    texcoord = vec2(model[0].x, model[1].y)*position;
+    uvcoord = in_uv;
+    // TODO: we don't need that in the future
+    texcoord = vec2(model[0].x, model[1].y)*in_pos;
 }
--- a/src/Makefile	Sat May 10 15:42:56 2025 +0200
+++ b/src/Makefile	Sat May 10 18:51:45 2025 +0200
@@ -30,7 +30,6 @@
 SRC = context.c glcontext.c filesystem.c error.c \
       window.c shader.c mesh.c texture.c \
       sprite.c \
-      primitives.c \
       camera.c scene.c scene_node.c behavior.c \
       font.c text.c
 
@@ -47,7 +46,7 @@
 
 $(BUILD_DIR)/behavior.o: behavior.c ascension/behavior.h \
  ascension/scene_node.h ascension/datatypes.h ascension/transform.h \
- ascension/error.h
+ ascension/error.h ascension/scene.h ascension/camera.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
@@ -58,19 +57,17 @@
 
 $(BUILD_DIR)/context.o: context.c ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
- ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/texture.h ascension/scene.h ascension/scene_node.h \
- ascension/transform.h ascension/camera.h ascension/input.h \
- ascension/ui/font.h ascension/error.h
+ ascension/shader.h ascension/texture.h ascension/scene.h \
+ ascension/scene_node.h ascension/transform.h ascension/camera.h \
+ ascension/input.h ascension/ui/font.h ascension/error.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \
- ascension/window.h ascension/glcontext.h ascension/primitives.h \
- ascension/mesh.h ascension/shader.h ascension/texture.h \
- ascension/scene.h ascension/scene_node.h ascension/transform.h \
- ascension/camera.h ascension/input.h ascension/ui/font.h \
- ascension/error.h
+ ascension/window.h ascension/glcontext.h ascension/shader.h \
+ ascension/texture.h ascension/scene.h ascension/scene_node.h \
+ ascension/transform.h ascension/camera.h ascension/input.h \
+ ascension/ui/font.h ascension/error.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
@@ -80,94 +77,89 @@
 
 $(BUILD_DIR)/font.o: font.c ascension/error.h ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
- ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/texture.h ascension/scene.h ascension/scene_node.h \
- ascension/transform.h ascension/camera.h ascension/input.h \
- ascension/ui/font.h ascension/filesystem.h ascension/ui/font.h
+ ascension/shader.h ascension/texture.h ascension/scene.h \
+ ascension/scene_node.h ascension/transform.h ascension/camera.h \
+ ascension/input.h ascension/ui/font.h ascension/filesystem.h \
+ ascension/ui/font.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/glcontext.o: glcontext.c ascension/glcontext.h \
- ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/texture.h ascension/error.h
+ ascension/shader.h ascension/texture.h ascension/error.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
-$(BUILD_DIR)/mesh.o: mesh.c ascension/error.h ascension/mesh.h
-	@echo "Compiling $<"
-	$(CC) -o $@ $(CFLAGS) -c $<
-
-$(BUILD_DIR)/primitives.o: primitives.c ascension/primitives.h \
- ascension/mesh.h ascension/error.h
+$(BUILD_DIR)/mesh.o: mesh.c ascension/error.h ascension/mesh.h \
+ ascension/datatypes.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/scene.o: scene.c ascension/error.h ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
- ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/texture.h ascension/scene.h ascension/scene_node.h \
- ascension/transform.h ascension/camera.h ascension/input.h \
- ascension/ui/font.h ascension/scene.h ascension/behavior.h \
- ascension/shader.h ascension/2d.h ascension/2d/sprite.h \
- ascension/2d/../scene_node.h ascension/2d/../texture.h
+ ascension/shader.h ascension/texture.h ascension/scene.h \
+ ascension/scene_node.h ascension/transform.h ascension/camera.h \
+ ascension/input.h ascension/ui/font.h ascension/scene.h \
+ ascension/behavior.h ascension/shader.h ascension/2d.h \
+ ascension/2d/sprite.h ascension/2d/../scene_node.h \
+ ascension/2d/../mesh.h ascension/2d/../datatypes.h \
+ ascension/2d/../texture.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/scene_node.o: scene_node.c ascension/scene_node.h \
  ascension/datatypes.h ascension/transform.h ascension/context.h \
- ascension/window.h ascension/glcontext.h ascension/primitives.h \
- ascension/mesh.h ascension/shader.h ascension/texture.h \
- ascension/scene.h ascension/scene_node.h ascension/camera.h \
- ascension/input.h ascension/ui/font.h
+ ascension/window.h ascension/glcontext.h ascension/shader.h \
+ ascension/texture.h ascension/scene.h ascension/scene_node.h \
+ ascension/camera.h ascension/input.h ascension/ui/font.h \
+ ascension/error.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/shader.o: shader.c ascension/context.h ascension/datatypes.h \
- ascension/window.h ascension/glcontext.h ascension/primitives.h \
- ascension/mesh.h ascension/shader.h ascension/texture.h \
- ascension/scene.h ascension/scene_node.h ascension/transform.h \
- ascension/camera.h ascension/input.h ascension/ui/font.h \
- ascension/error.h ascension/shader.h ascension/filesystem.h
+ ascension/window.h ascension/glcontext.h ascension/shader.h \
+ ascension/texture.h ascension/scene.h ascension/scene_node.h \
+ ascension/transform.h ascension/camera.h ascension/input.h \
+ ascension/ui/font.h ascension/error.h ascension/shader.h \
+ ascension/filesystem.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/sprite.o: sprite.c ascension/2d/sprite.h \
  ascension/2d/../scene_node.h ascension/2d/../datatypes.h \
- ascension/2d/../transform.h ascension/2d/../texture.h \
- ascension/context.h ascension/datatypes.h ascension/window.h \
- ascension/glcontext.h ascension/primitives.h ascension/mesh.h \
- ascension/shader.h ascension/texture.h ascension/scene.h \
- ascension/scene_node.h ascension/camera.h ascension/input.h \
- ascension/ui/font.h ascension/glcontext.h
+ ascension/2d/../transform.h ascension/2d/../mesh.h \
+ ascension/2d/../texture.h ascension/context.h ascension/datatypes.h \
+ ascension/window.h ascension/glcontext.h ascension/shader.h \
+ ascension/texture.h ascension/scene.h ascension/scene_node.h \
+ ascension/camera.h ascension/input.h ascension/ui/font.h \
+ ascension/glcontext.h ascension/mesh.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/text.o: text.c ascension/error.h ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
- ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/texture.h ascension/scene.h ascension/scene_node.h \
- ascension/transform.h ascension/camera.h ascension/input.h \
- ascension/ui/font.h ascension/ui/text.h ascension/ui/font.h \
- ascension/ui/../2d/sprite.h ascension/ui/../2d/../scene_node.h \
- ascension/ui/../2d/../texture.h
+ ascension/shader.h ascension/texture.h ascension/scene.h \
+ ascension/scene_node.h ascension/transform.h ascension/camera.h \
+ ascension/input.h ascension/ui/font.h ascension/ui/text.h \
+ ascension/ui/font.h ascension/ui/../2d/sprite.h \
+ ascension/ui/../2d/../scene_node.h ascension/ui/../2d/../mesh.h \
+ ascension/ui/../2d/../datatypes.h ascension/ui/../2d/../texture.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/texture.o: texture.c ascension/error.h ascension/context.h \
  ascension/datatypes.h ascension/window.h ascension/glcontext.h \
- ascension/primitives.h ascension/mesh.h ascension/shader.h \
- ascension/texture.h ascension/scene.h ascension/scene_node.h \
- ascension/transform.h ascension/camera.h ascension/input.h \
- ascension/ui/font.h ascension/texture.h ascension/filesystem.h
+ ascension/shader.h ascension/texture.h ascension/scene.h \
+ ascension/scene_node.h ascension/transform.h ascension/camera.h \
+ ascension/input.h ascension/ui/font.h ascension/texture.h \
+ ascension/filesystem.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
 $(BUILD_DIR)/window.o: window.c ascension/error.h ascension/window.h \
- ascension/datatypes.h ascension/glcontext.h ascension/primitives.h \
- ascension/mesh.h ascension/shader.h ascension/texture.h \
- ascension/scene.h ascension/scene_node.h ascension/transform.h \
- ascension/camera.h ascension/context.h ascension/window.h \
- ascension/input.h ascension/ui/font.h
+ ascension/datatypes.h ascension/glcontext.h ascension/shader.h \
+ ascension/texture.h ascension/scene.h ascension/scene_node.h \
+ ascension/transform.h ascension/camera.h ascension/context.h \
+ ascension/window.h ascension/input.h ascension/ui/font.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<
 
--- a/src/ascension/2d/sprite.h	Sat May 10 15:42:56 2025 +0200
+++ b/src/ascension/2d/sprite.h	Sat May 10 18:51:45 2025 +0200
@@ -29,10 +29,12 @@
 #define ASCENSION_SPRITE_H
 
 #include "../scene_node.h"
+#include "../mesh.h"
 #include "../texture.h"
 
 typedef struct AscSprite {
     AscSceneNode data;
+    AscMesh mesh;
     AscTexture *texture;
 } AscSprite;
 
--- a/src/ascension/datatypes.h	Sat May 10 15:42:56 2025 +0200
+++ b/src/ascension/datatypes.h	Sat May 10 18:51:45 2025 +0200
@@ -71,6 +71,14 @@
 } asc_recti;
 #define asc_recti_new(x, y, w, h) (asc_recti){asc_vec2i_new(x,y), asc_vec2u_new(w,h)}
 
+typedef union asc_vec2f {
+    struct { float x, y; };
+    struct { float width, height; };
+    struct { float u, v; };
+    float data[2];
+} asc_vec2f;
+#define asc_vec2f_new(x, y, z) (asc_vec2f){{(float)x, (float)y}}
+
 typedef union asc_vec3f {
     struct { float x, y, z; };
     struct { float width, height, depth; };
--- a/src/ascension/glcontext.h	Sat May 10 15:42:56 2025 +0200
+++ b/src/ascension/glcontext.h	Sat May 10 18:51:45 2025 +0200
@@ -32,7 +32,6 @@
 
 #include <cx/list.h>
 
-#include "primitives.h"
 #include "shader.h"
 #include "texture.h"
 
@@ -43,11 +42,6 @@
     int depth_size;
 } AscGLContextSettings;
 
-enum AscDefaultPrimitives {
-    ASC_PRIMITIVE_PLANE_IDX = 0,
-    ASC_PRIMITIVE_COUNT
-};
-
 enum AscDefaultTextures2d {
     ASC_TEXTURE_2D_EMPTY_1X1_IDX = 0,
     ASC_TEXTURE_2D_COUNT
@@ -62,7 +56,7 @@
     SDL_Window *window;
     SDL_GLContext glctx;
     CxList *cleanup_funcs;
-    AscMesh primitives[ASC_PRIMITIVE_COUNT];
+    // TODO: find a better way to store the dummy textures
     AscTexture textures_2d[ASC_TEXTURE_2D_COUNT];
     AscTexture textures_rect[ASC_TEXTURE_RECT_COUNT];
     struct {
@@ -71,7 +65,6 @@
 } AscGLContext;
 
 #define asc_active_glctx (&asc_active_window->glctx)
-#define ASC_PRIMITIVE_PLANE (&asc_active_glctx->primitives[ASC_PRIMITIVE_PLANE_IDX])
 #define ASC_TEXTURE_2D_EMPTY_1X1 (&asc_active_glctx->textures_2d[ASC_TEXTURE_2D_EMPTY_1X1_IDX])
 #define ASC_TEXTURE_RECT_EMPTY_1X1 (&asc_active_glctx->textures_rect[ASC_TEXTURE_RECT_EMPTY_1X1_IDX])
 #define ASC_SHADER_SPRITE (&asc_active_glctx->shader.sprite)
--- a/src/ascension/mesh.h	Sat May 10 15:42:56 2025 +0200
+++ b/src/ascension/mesh.h	Sat May 10 18:51:45 2025 +0200
@@ -28,12 +28,61 @@
 #ifndef ASCENSION_MESH_H
 #define ASCENSION_MESH_H
 
+#include "datatypes.h"
+
+enum AscMeshType {
+    ASC_MESH_2D,
+    ASC_MESH_2D_COLORED,
+    ASC_MESH_3D,
+    ASC_MESH_3D_COLORED,
+};
+
 typedef struct AscMesh {
+    // TODO: for batched rendering, VAO and VBO must not be part of the mesh
     unsigned vbo;
     unsigned vao;
-    unsigned vertices;
+    enum AscMeshType type;
+    unsigned vtx_count;
+    float *vtx_data;
 } AscMesh;
 
+typedef struct AscMeshVertex2d {
+    asc_vec2f pos;
+    asc_vec2f uv;
+} AscMeshVertex2d;
+
+typedef struct AscMeshVertex2dColored {
+    asc_vec2f pos;
+    asc_vec2f uv;
+    asc_col4f color;
+} AscMeshVertex2dColored;
+
+typedef struct AscMeshVertex3d {
+    asc_vec3f pos;
+    asc_vec2f uv;
+} AscMeshVertex3d;
+
+typedef struct AscMeshVertex3dColored {
+    asc_vec3f pos;
+    asc_vec2f uv;
+    asc_col4f color;
+} AscMeshVertex3dColored;
+
+
+void asc_mesh_destroy(AscMesh *mesh);
+
+
+/**
+ * Creates a 2D plane.
+ *
+ * @param mesh the mesh to initialize
+ */
+void asc_mesh_plane_2d(AscMesh *mesh);
+
+
+
+// TODO: replace below functions with a batched rendering implementation
+
 /**
  * Allocates VBO and VAO for one or more meshes.
  *
@@ -55,6 +104,6 @@
  *
  * @param mesh the mesh to draw
  */
-void asc_mesh_draw_triangle_strip(AscMesh *mesh);
+void asc_mesh_draw_triangle_strip(const AscMesh *mesh);
 
 #endif //ASCENSION_MESH_H
--- a/src/ascension/primitives.h	Sat May 10 15:42:56 2025 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * Copyright 2023 Mike Becker. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef ASCENSION_PRIMITIVES_H
-#define ASCENSION_PRIMITIVES_H
-
-#include <stdbool.h>
-
-#include "mesh.h"
-
-void asc_primitives_init_plane(AscMesh *mesh);
-
-#endif //ASCENSION_PRIMITIVES_H
--- a/src/glcontext.c	Sat May 10 15:42:56 2025 +0200
+++ b/src/glcontext.c	Sat May 10 18:51:45 2025 +0200
@@ -45,21 +45,6 @@
     }
 }
 
-static int asc_primitives_init(AscGLContext *context) {
-    asc_dprintf("Create primitives for the GL context of active window.");
-    asc_mesh_allocate_buffers(context->primitives, ASC_PRIMITIVE_COUNT);
-
-    asc_primitives_init_plane(&context->primitives[ASC_PRIMITIVE_PLANE_IDX]);
-    // TODO: more primitives
-
-    return asc_error_catch_all_gl();
-}
-
-static void asc_primitives_destroy(AscGLContext *context) {
-    asc_dprintf("Destroy primitives in GL context of active window.");
-    asc_mesh_free_buffers((AscMesh*)&context->primitives, sizeof(context->primitives) / sizeof(AscMesh));
-}
-
 static int asc_shader_initialize_predefined(AscGLContext *ctx) {
     int ret = 0;
     ret |= asc_shader_sprite_init(&ctx->shader.sprite);
@@ -145,12 +130,6 @@
         glEnable(GL_DEBUG_OUTPUT);
         glDebugMessageCallback(asc_gl_debug_callback, NULL);
 
-        if (asc_primitives_init(ctx)) {
-            asc_error("Creating primitive meshes failed");
-            SDL_GL_DeleteContext(ctx->glctx);
-            return false;
-        }
-
         if (asc_shader_initialize_predefined(ctx)) {
             asc_error("Initializing predefined shaders failed");
             SDL_GL_DeleteContext(ctx->glctx);
@@ -180,7 +159,6 @@
     cxListFree(ctx->cleanup_funcs);
     asc_texture_destroy_predefined(ctx);
     asc_shader_destroy_predefined(ctx);
-    asc_primitives_destroy(ctx);
 
     // destroy the GL context and the window
     SDL_GL_DeleteContext(ctx->glctx);
--- a/src/mesh.c	Sat May 10 15:42:56 2025 +0200
+++ b/src/mesh.c	Sat May 10 18:51:45 2025 +0200
@@ -44,6 +44,7 @@
 }
 
 void asc_mesh_free_buffers(AscMesh *mesh, unsigned count) {
+    if (count == 1 && mesh->vbo == 0) return; // hack to skip this function until we remove it
     asc_dprintf("Free mesh buffers for %u meshes.", count);
     GLuint buffers[count];
     GLuint arrays[count];
@@ -60,11 +61,52 @@
     asc_error_catch_all_gl();
 }
 
-void asc_mesh_draw_triangle_strip(AscMesh *mesh) {
+void asc_mesh_draw_triangle_strip(const AscMesh *mesh) {
     glBindVertexArray(mesh->vao);
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, mesh->vertices);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, mesh->vtx_count);
 #ifndef NDEBUG
     // only unbind in debug mode to detect accidental re-use of the wrong VAO
     glBindVertexArray(0);
 #endif
 }
+
+void asc_mesh_destroy(AscMesh *mesh) {
+    asc_mesh_free_buffers(mesh, 1);
+    free(mesh->vtx_data);
+}
+
+void asc_mesh_plane_2d(AscMesh *mesh) {
+    if (mesh->vbo == 0) {
+        asc_mesh_allocate_buffers(mesh, 1);
+    }
+    free(mesh->vtx_data);
+    asc_dprintf("Create plane in VBO %u and VAO %u", mesh->vbo, mesh->vao);
+    mesh->vtx_count = 4;
+    AscMeshVertex2d data[4] = {
+        {
+            .pos = {{0.0f, 0.0f}},
+            .uv = {{0.0f, 0.0f}},
+        } , // bottom left
+        {
+            .pos = {{0.0f, 1.0f}},
+            .uv = {{0.0f, 1.0f}},
+        }, // top left
+        {
+            .pos = {{1.0f, 0.0f}},
+            .uv = {{1.0f, 0.0f}},
+        }, // bottom right
+        {
+            .pos = {{1.0f, 1.0f}},
+            .uv = {{1.0f, 1.0f}},
+        } // top right
+    };
+    mesh->vtx_data = malloc(sizeof(data));
+    memcpy(mesh->vtx_data, data, sizeof(data));
+    glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(data), mesh->vtx_data, GL_STATIC_DRAW);
+    glBindVertexArray(mesh->vao);
+    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(AscMeshVertex2d), (void*)offsetof(AscMeshVertex2d, pos));
+    glEnableVertexAttribArray(0);
+    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(AscMeshVertex2d), (void*)offsetof(AscMeshVertex2d, uv));
+    glEnableVertexAttribArray(1);
+}
--- a/src/primitives.c	Sat May 10 15:42:56 2025 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * Copyright 2023 Mike Becker. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ascension/primitives.h"
-#include "ascension/error.h"
-
-#include <GL/glew.h>
-
-void asc_primitives_init_plane(AscMesh *mesh) {
-    if (mesh->vbo == 0) {
-        if (mesh->vao > 0) {
-            asc_dprintf("!!! Mesh with VAO %u has no VBO - this is most likely a programming error !!!", mesh->vao);
-        }
-        asc_mesh_allocate_buffers(mesh, 1);
-    }
-    asc_dprintf("Create plane in VBO %u and VAO %u", mesh->vbo, mesh->vao);
-    mesh->vertices = 4;
-    float data[8] = {
-            0.0f, 0.0f, // bottom left
-            0.0f, 1.0f,    // top left
-            1.0f, 0.0f, // bottom right
-            1.0f, 1.0f    // top right
-    };
-    glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
-    glBindVertexArray(mesh->vao);
-    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
-    glEnableVertexAttribArray(0);
-}
--- a/src/sprite.c	Sat May 10 15:42:56 2025 +0200
+++ b/src/sprite.c	Sat May 10 18:51:45 2025 +0200
@@ -32,8 +32,12 @@
 
 #include <GL/glew.h>
 
+#include "ascension/mesh.h"
+
 static void asc_sprite_destroy(AscSceneNode *node) {
-    ((AscSprite *) node)->texture->refcount--;
+    AscSprite *sprite = (AscSprite *) node;
+    asc_mesh_destroy(&sprite->mesh);
+    sprite->texture->refcount--;
 }
 
 AscSceneNode *asc_sprite_create(struct asc_sprite_create_args args) {
@@ -52,6 +56,7 @@
     node->destroy_func = asc_sprite_destroy;
 
     node->position = asc_vec3f_new(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
+    // TODO: do not use scale, add mesh params instead
     node->scale = asc_vec3f_new(
         args.width == 0 ? args.texture->width : args.width,
         args.height == 0 ? args.texture->height : args.height,
@@ -59,6 +64,9 @@
     );
     asc_node_update_transform(node);
 
+    // initialize mesh
+    asc_mesh_plane_2d(&sprite->mesh);
+
     return node;
 }
 
@@ -80,5 +88,5 @@
     }
 
     // Draw mesh
-    asc_mesh_draw_triangle_strip(ASC_PRIMITIVE_PLANE);
+    asc_mesh_draw_triangle_strip(&node->mesh);
 }
--- a/src/text.c	Sat May 10 15:42:56 2025 +0200
+++ b/src/text.c	Sat May 10 18:51:45 2025 +0200
@@ -73,6 +73,7 @@
 static void asc_text_destroy(AscSceneNode *node) {
     AscText *text = (AscText*) node;
     AscSprite *sprite = (AscSprite*) node;
+    asc_mesh_destroy(&sprite->mesh);
     asc_texture_destroy(sprite->texture, 1);
     assert(sprite->texture->refcount == 0);
     free(sprite->texture);
@@ -81,15 +82,18 @@
 
 AscSceneNode *asc_text_create(struct asc_text_create_args args) {
     AscText *text = calloc(1, sizeof(AscText));
-    AscSceneNode *node = (AscSceneNode*) text;
+    AscSceneNode *node = &text->base.data;
 
+    // node properties
+    asc_scene_node_name(node, args.name);
     node->render_group = ASC_RENDER_GROUP_SPRITE_BLEND;
     node->destroy_func = asc_text_destroy;
     node->update_func = asc_text_update;
-
-    node->flags = args.alignment;
     node->position = asc_vec3f_new(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
     node->scale.depth = 1.f;
+
+    // text properties
+    node->flags = args.alignment; // use flags variable to save some space
     text->max_width = args.max_width;
     text->font = asc_active_font;
     text->color = asc_context.ink;
@@ -99,8 +103,8 @@
         text->text = cx_mutstr(strdup(args.text));
     }
 
-    // initialize
-    asc_scene_node_name(node, args.name);
+    // initialize mesh and texture
+    asc_mesh_plane_2d(&text->base.mesh);
     text->base.texture = malloc(sizeof(AscTexture));
     asc_texture_init_rectangle(text->base.texture, 1);
     asc_text_update(node);
--- a/test/snake/Makefile	Sat May 10 15:42:56 2025 +0200
+++ b/test/snake/Makefile	Sat May 10 18:51:45 2025 +0200
@@ -47,8 +47,7 @@
 $(BUILD_DIR)/snake.o: snake.c ../../src/ascension/core.h \
  ../../src/ascension/error.h ../../src/ascension/context.h \
  ../../src/ascension/datatypes.h ../../src/ascension/window.h \
- ../../src/ascension/glcontext.h ../../src/ascension/primitives.h \
- ../../src/ascension/mesh.h ../../src/ascension/shader.h \
+ ../../src/ascension/glcontext.h ../../src/ascension/shader.h \
  ../../src/ascension/texture.h ../../src/ascension/scene.h \
  ../../src/ascension/scene_node.h ../../src/ascension/transform.h \
  ../../src/ascension/camera.h ../../src/ascension/input.h \
@@ -56,6 +55,8 @@
  ../../src/ascension/ui.h ../../src/ascension/ui/text.h \
  ../../src/ascension/ui/font.h ../../src/ascension/ui/../2d/sprite.h \
  ../../src/ascension/ui/../2d/../scene_node.h \
+ ../../src/ascension/ui/../2d/../mesh.h \
+ ../../src/ascension/ui/../2d/../datatypes.h \
  ../../src/ascension/ui/../2d/../texture.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -c $<

mercurial