Entropie git · main
Entropie festival game; first steps
C 88.5% Markdown 9.1% GLSL 2.3%git clone https://git.christianimmanuel.de/games/Entropie.gitwget https://git.christianimmanuel.de/games/Entropie/archive/Entropie.tar.gzentropie/src/render/mesh.c raw
#include "render/mesh.h"
#include <stdlib.h>
#include <string.h>
#include <SDL3/SDL.h>
static bool grow(void **data, size_t *capacity, size_t needed, size_t elem_size) {
if (needed <= *capacity) return true;
size_t cap = *capacity ? *capacity : 64;
while (cap < needed) cap *= 2;
void *p = realloc(*data, cap * elem_size);
if (!p) return false;
*data = p;
*capacity = cap;
return true;
}
void MeshBuilder_init(MeshBuilder *b) {
memset(b, 0, sizeof(*b));
}
void MeshBuilder_free(MeshBuilder *b) {
free(b->vertices);
free(b->indices);
memset(b, 0, sizeof(*b));
}
GLuint MeshBuilder_vertex(MeshBuilder *b, Vec3 position, Vec3 normal, Vec3 color) {
if (!grow((void **)&b->vertices, &b->vertex_capacity, b->vertex_count + 1, sizeof(Vertex))) {
b->out_of_memory = true;
return 0;
}
b->vertices[b->vertex_count] = (Vertex){ position, normal, color };
return (GLuint)b->vertex_count++;
}
#define MAX_SEGMENTS 96
void MeshBuilder_triangle(MeshBuilder *b, GLuint i0, GLuint i1, GLuint i2) {
if (b->out_of_memory) return;
/* Enforce counter-clockwise winding relative to the vertex normals. */
const Vertex *v0 = &b->vertices[i0], *v1 = &b->vertices[i1], *v2 = &b->vertices[i2];
Vec3 face = vec3_cross(vec3_sub(v1->position, v0->position),
vec3_sub(v2->position, v0->position));
Vec3 normal = vec3_add(vec3_add(v0->normal, v1->normal), v2->normal);
if (vec3_dot(face, normal) < 0.0f) {
GLuint t = i1;
i1 = i2;
i2 = t;
}
if (!grow((void **)&b->indices, &b->index_capacity, b->index_count + 3, sizeof(GLuint))) {
b->out_of_memory = true;
return;
}
b->indices[b->index_count++] = i0;
b->indices[b->index_count++] = i1;
b->indices[b->index_count++] = i2;
}
void MeshBuilder_quad(MeshBuilder *b, GLuint i0, GLuint i1, GLuint i2, GLuint i3) {
MeshBuilder_triangle(b, i0, i1, i2);
MeshBuilder_triangle(b, i2, i3, i0);
}
int MeshBuilder_segments(float length, float cell_size) {
int n = (int)ceilf(length / cell_size);
if (n < 1) n = 1;
if (n > MAX_SEGMENTS) n = MAX_SEGMENTS;
return n;
}
static Vec3 lerp3(Vec3 a, Vec3 b, float t) {
return vec3_add(a, vec3_scale(vec3_sub(b, a), t));
}
void MeshBuilder_gridQuad(MeshBuilder *b, Vec3 p0, Vec3 p1, Vec3 p2, Vec3 p3,
Vec3 normal, Vec3 color, float cell_size) {
const int nu = MeshBuilder_segments(vec3_length(vec3_sub(p1, p0)), cell_size);
const int nv = MeshBuilder_segments(vec3_length(vec3_sub(p3, p0)), cell_size);
const GLuint base = (GLuint)b->vertex_count;
for (int j = 0; j <= nv; j++) {
float v = (float)j / (float)nv;
Vec3 left = lerp3(p0, p3, v), right = lerp3(p1, p2, v);
for (int i = 0; i <= nu; i++)
MeshBuilder_vertex(b, lerp3(left, right, (float)i / (float)nu), normal, color);
}
for (int j = 0; j < nv; j++) {
for (int i = 0; i < nu; i++) {
GLuint a = base + (GLuint)(j * (nu + 1) + i);
GLuint c = a + (GLuint)(nu + 1);
MeshBuilder_quad(b, a, a + 1, c + 1, c);
}
}
}
void MeshBuilder_gridTriangle(MeshBuilder *b, Vec3 p0, Vec3 p1, Vec3 p2,
Vec3 normal, Vec3 color, float cell_size) {
float longest = fmaxf(vec3_length(vec3_sub(p1, p0)),
fmaxf(vec3_length(vec3_sub(p2, p0)), vec3_length(vec3_sub(p2, p1))));
const int n = MeshBuilder_segments(longest, cell_size);
const Vec3 du = vec3_scale(vec3_sub(p1, p0), 1.0f / (float)n);
const Vec3 dv = vec3_scale(vec3_sub(p2, p0), 1.0f / (float)n);
const GLuint base = (GLuint)b->vertex_count;
/* Rows j = 0..n, each with n - j + 1 vertices. */
for (int j = 0; j <= n; j++)
for (int i = 0; i <= n - j; i++)
MeshBuilder_vertex(b, vec3_add(p0, vec3_add(vec3_scale(du, (float)i),
vec3_scale(dv, (float)j))),
normal, color);
GLuint row = base;
for (int j = 0; j < n; j++) {
GLuint next = row + (GLuint)(n - j + 1);
for (int i = 0; i < n - j; i++) {
MeshBuilder_triangle(b, row + i, row + i + 1, next + i);
if (i < n - j - 1)
MeshBuilder_triangle(b, row + i + 1, next + i + 1, next + i);
}
row = next;
}
}
Mesh Mesh_fromBuilder(MeshBuilder *b) {
Mesh m = { 0 };
if (b->out_of_memory || b->index_count == 0) {
SDL_Log("Mesh build failed (out of memory or empty)");
MeshBuilder_free(b);
return m;
}
/* bounding sphere around the middle of the bounding box */
Vec3 lo = b->vertices[0].position, hi = lo;
for (size_t i = 1; i < b->vertex_count; i++) {
Vec3 p = b->vertices[i].position;
lo = vec3(fminf(lo.x, p.x), fminf(lo.y, p.y), fminf(lo.z, p.z));
hi = vec3(fmaxf(hi.x, p.x), fmaxf(hi.y, p.y), fmaxf(hi.z, p.z));
}
m.center = vec3_scale(vec3_add(lo, hi), 0.5f);
for (size_t i = 0; i < b->vertex_count; i++)
m.radius = fmaxf(m.radius, vec3_length(vec3_sub(b->vertices[i].position, m.center)));
glGenVertexArrays(1, &m.vao);
glGenBuffers(1, &m.vbo);
glGenBuffers(1, &m.ebo);
glBindVertexArray(m.vao);
glBindBuffer(GL_ARRAY_BUFFER, m.vbo);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(b->vertex_count * sizeof(Vertex)),
b->vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(b->index_count * sizeof(GLuint)),
b->indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void *)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void *)offsetof(Vertex, normal));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void *)offsetof(Vertex, color));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
m.index_count = (GLsizei)b->index_count;
MeshBuilder_free(b);
return m;
}
void Mesh_draw(const Mesh *mesh) {
if (!mesh->vao) return;
glBindVertexArray(mesh->vao);
glDrawElements(GL_TRIANGLES, mesh->index_count, GL_UNSIGNED_INT, NULL);
glBindVertexArray(0);
}
void Mesh_destroy(Mesh *mesh) {
if (mesh->ebo) glDeleteBuffers(1, &mesh->ebo);
if (mesh->vbo) glDeleteBuffers(1, &mesh->vbo);
if (mesh->vao) glDeleteVertexArrays(1, &mesh->vao);
memset(mesh, 0, sizeof(*mesh));
}