mesh, meshgroup, scenefile
[vrlugburz] / src / mesh.c
diff --git a/src/mesh.c b/src/mesh.c
new file mode 100644 (file)
index 0000000..149fbe7
--- /dev/null
@@ -0,0 +1,337 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <float.h>
+#define GL_GLEXT_PROTOTYPES 1
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include "mesh.h"
+
+static int update_mesh_vbo(struct mesh *m);
+static int update_meshgroup_vbo(struct meshgroup *mg);
+
+void init_mesh(struct mesh *m)
+{
+       memset(m, 0, sizeof *m);
+}
+
+void destroy_mesh(struct mesh *m)
+{
+       free(m->varr);
+       free(m->iarr);
+
+       if(m->vbo) {
+               glDeleteBuffers(1, &m->vbo);
+       }
+       if(m->ibo) {
+               glDeleteBuffers(1, &m->ibo);
+       }
+}
+
+void clear_mesh(struct mesh *m)
+{
+       free(m->varr);
+       free(m->iarr);
+
+       m->varr = 0;
+       m->iarr = 0;
+       m->num_verts = m->max_verts = m->num_idx = m->max_idx = 0;
+       m->mtl = 0;
+       m->bbvalid = m->vbovalid = 0;
+}
+
+void init_meshgroup(struct meshgroup *mg)
+{
+       memset(mg, 0, sizeof *mg);
+}
+
+void destroy_meshgroup(struct meshgroup *mg)
+{
+       free(mg->meshes);
+
+       if(mg->vbo) {
+               glDeleteBuffers(1, &mg->vbo);
+       }
+       if(mg->ibo) {
+               glDeleteBuffers(1, &mg->ibo);
+       }
+}
+
+void clear_meshgroup(struct meshgroup *mg)
+{
+       free(mg->meshes);
+
+       mg->meshes = 0;
+       mg->num_meshes = 0;
+       mg->num_verts = mg->num_idx = 0;
+
+       mg->bbvalid = mg->vbovalid = 0;
+}
+
+void calc_mesh_bounds(struct mesh *m)
+{
+       int i;
+       struct vertex *vptr = m->varr;
+
+       m->bb.vmin.x = m->bb.vmin.y = m->bb.vmin.z = FLT_MAX;
+       m->bb.vmax.x = m->bb.vmax.y = m->bb.vmax.z = -FLT_MAX;
+
+       for(i=0; i<m->num_verts; i++) {
+               if(vptr->pos.x < m->bb.vmin.x) m->bb.vmin.x = vptr->pos.x;
+               if(vptr->pos.y < m->bb.vmin.y) m->bb.vmin.y = vptr->pos.y;
+               if(vptr->pos.z < m->bb.vmin.z) m->bb.vmin.z = vptr->pos.z;
+               if(vptr->pos.x > m->bb.vmax.x) m->bb.vmax.x = vptr->pos.x;
+               if(vptr->pos.y > m->bb.vmax.y) m->bb.vmax.y = vptr->pos.y;
+               if(vptr->pos.z > m->bb.vmax.z) m->bb.vmax.z = vptr->pos.z;
+               vptr++;
+       }
+
+       m->bbvalid = 1;
+}
+
+void calc_meshgroup_bounds(struct meshgroup *mg)
+{
+       int i;
+       struct mesh *m;
+
+       mg->bb.vmin.x = mg->bb.vmin.y = mg->bb.vmin.z = FLT_MAX;
+       mg->bb.vmax.x = mg->bb.vmax.y = mg->bb.vmax.z = -FLT_MAX;
+
+       for(i=0; i<mg->num_meshes; i++) {
+               m = mg->meshes[i];
+               if(!m->bbvalid) {
+                       calc_mesh_bounds(m);
+               }
+
+               if(m->bb.vmin.x < mg->bb.vmin.x) mg->bb.vmin.x = m->bb.vmin.x;
+               if(m->bb.vmin.y < mg->bb.vmin.y) mg->bb.vmin.y = m->bb.vmin.y;
+               if(m->bb.vmin.z < mg->bb.vmin.z) mg->bb.vmin.z = m->bb.vmin.z;
+               if(m->bb.vmax.x > mg->bb.vmax.x) mg->bb.vmax.x = m->bb.vmax.x;
+               if(m->bb.vmax.y > mg->bb.vmax.y) mg->bb.vmax.y = m->bb.vmax.y;
+               if(m->bb.vmax.z > mg->bb.vmax.z) mg->bb.vmax.z = m->bb.vmax.z;
+       }
+
+       mg->bbvalid = 1;
+}
+
+int add_mesh_vertex(struct mesh *m, struct vertex *v)
+{
+       void *tmp;
+       int newmax;
+
+       if(m->num_verts >= m->max_verts) {
+               newmax = m->max_verts ? m->max_verts * 2 : 16;
+               if(!(tmp = realloc(m->varr, newmax * sizeof *m->varr))) {
+                       return -1;
+               }
+               m->varr = tmp;
+               m->max_verts = newmax;
+       }
+
+       m->varr[m->num_verts++] = *v;
+       return 0;
+}
+
+int add_mesh_index(struct mesh *m, int idx)
+{
+       void *tmp;
+       int newmax;
+
+       if(m->num_idx >= m->max_idx) {
+               newmax = m->max_idx ? m->max_idx * 2 : 16;
+               if(!(tmp = realloc(m->iarr, newmax * sizeof *m->iarr))) {
+                       return -1;
+               }
+               m->iarr = tmp;
+               m->max_idx = newmax;
+       }
+
+       m->iarr[m->num_idx++] = idx;
+       return 0;
+}
+
+int add_mesh_face(struct mesh *m, int va, int vb, int vc)
+{
+       if(add_mesh_index(m, va) == -1) return -1;
+       if(add_mesh_index(m, vb) == -1) {
+               m->num_idx--;
+               return -1;
+       }
+       if(add_mesh_index(m, vc) == -1) {
+               m->num_idx -= 2;
+               return -1;
+       }
+       return 0;
+}
+
+int add_meshgroup_mesh(struct meshgroup *mg, struct mesh *m)
+{
+       void *tmp;
+       int newmax;
+
+       if(mg->num_meshes >= mg->max_meshes) {
+               newmax = mg->max_meshes ? mg->max_meshes * 2 : 16;
+               if(!(tmp = realloc(mg->meshes, newmax * sizeof *mg->meshes))) {
+                       return -1;
+               }
+               mg->meshes = tmp;
+               mg->max_meshes = newmax;
+       }
+
+       mg->meshes[mg->num_meshes++] = m;
+       return 0;
+}
+
+void draw_mesh(struct mesh *m)
+{
+       if(!m->vbovalid) {
+               if(update_mesh_vbo(m) == -1) {
+                       return;
+               }
+       }
+
+       glEnableVertexAttribArray(MESH_ATTR_VERTEX);
+       glEnableVertexAttribArray(MESH_ATTR_NORMAL);
+       glEnableVertexAttribArray(MESH_ATTR_TANGENT);
+       glEnableVertexAttribArray(MESH_ATTR_TEXCOORD);
+
+       glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
+       glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, 0, sizeof *m->varr, 0);
+       glVertexAttribPointer(MESH_ATTR_NORMAL, 3, GL_FLOAT, 0, sizeof *m->varr, (void*)offsetof(struct vertex, norm));
+       glVertexAttribPointer(MESH_ATTR_TANGENT, 3, GL_FLOAT, 0, sizeof *m->varr, (void*)offsetof(struct vertex, tang));
+       glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, 0, sizeof *m->varr, (void*)offsetof(struct vertex, tex));
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+       if(m->ibo) {
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ibo);
+               glDrawElements(GL_TRIANGLES, m->num_idx, GL_UNSIGNED_INT, 0);
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+       } else {
+               glDrawArrays(GL_TRIANGLES, 0, m->num_verts);
+       }
+
+       glDisableVertexAttribArray(MESH_ATTR_VERTEX);
+       glDisableVertexAttribArray(MESH_ATTR_NORMAL);
+       glDisableVertexAttribArray(MESH_ATTR_TANGENT);
+       glDisableVertexAttribArray(MESH_ATTR_TEXCOORD);
+}
+
+void draw_meshgroup(struct meshgroup *mg)
+{
+       if(!mg->vbovalid) {
+               if(update_meshgroup_vbo(mg) == -1) {
+                       return;
+               }
+       }
+
+       glEnableVertexAttribArray(MESH_ATTR_VERTEX);
+       glEnableVertexAttribArray(MESH_ATTR_NORMAL);
+       glEnableVertexAttribArray(MESH_ATTR_TANGENT);
+       glEnableVertexAttribArray(MESH_ATTR_TEXCOORD);
+
+       glBindBuffer(GL_ARRAY_BUFFER, mg->vbo);
+       glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, 0, sizeof(struct vertex), 0);
+       glVertexAttribPointer(MESH_ATTR_NORMAL, 3, GL_FLOAT, 0, sizeof(struct vertex), (void*)offsetof(struct vertex, norm));
+       glVertexAttribPointer(MESH_ATTR_TANGENT, 3, GL_FLOAT, 0, sizeof(struct vertex), (void*)offsetof(struct vertex, tang));
+       glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, 0, sizeof(struct vertex), (void*)offsetof(struct vertex, tex));
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+       if(mg->ibo) {
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mg->ibo);
+               glDrawElements(GL_TRIANGLES, mg->num_idx, GL_UNSIGNED_INT, 0);
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+       } else {
+               glDrawArrays(GL_TRIANGLES, 0, mg->num_verts);
+       }
+
+       glDisableVertexAttribArray(MESH_ATTR_VERTEX);
+       glDisableVertexAttribArray(MESH_ATTR_NORMAL);
+       glDisableVertexAttribArray(MESH_ATTR_TANGENT);
+       glDisableVertexAttribArray(MESH_ATTR_TEXCOORD);
+}
+
+static int update_mesh_vbo(struct mesh *m)
+{
+       if(m->num_verts <= 0) return -1;
+
+       if(!m->vbo) {
+               glGenBuffers(1, &m->vbo);
+       }
+       glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
+       glBufferData(GL_ARRAY_BUFFER, m->num_verts * sizeof *m->varr, m->varr, GL_STATIC_DRAW);
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+       if(m->num_idx > 0) {
+               if(!m->ibo) {
+                       glGenBuffers(1, &m->ibo);
+               }
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ibo);
+               glBufferData(GL_ELEMENT_ARRAY_BUFFER, m->num_idx * sizeof *m->iarr,
+                               m->iarr, GL_STATIC_DRAW);
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+       }
+       return 0;
+}
+
+static int update_meshgroup_vbo(struct meshgroup *mg)
+{
+       int i;
+       struct vertex *varr, *vptr;
+       unsigned int *iarr = 0, *iptr;
+       struct mesh *m;
+
+       mg->num_verts = mg->num_idx = 0;
+
+       for(i=0; i<mg->num_meshes; i++) {
+               mg->num_verts += mg->meshes[i]->num_verts;
+               mg->num_idx += mg->meshes[i]->num_idx;
+       }
+
+       if(mg->num_verts <= 0) return -1;
+
+       if(!(varr = malloc(mg->num_verts * sizeof *varr))) {
+               fprintf(stderr, "update_meshgroup_vbo: failed to allocate vertex array with %d vertices\n", mg->num_verts);
+               return -1;
+       }
+       if(mg->num_idx > 0) {
+               if(!(iarr = malloc(mg->num_idx * sizeof *iarr))) {
+                       fprintf(stderr, "update_meshgroup_vbo: failed to allocate index array with %d indices\n", mg->num_idx);
+                       free(varr);
+                       return -1;
+               }
+       }
+
+       vptr = varr;
+       iptr = iarr;
+
+       for(i=0; i<mg->num_meshes; i++) {
+               m = mg->meshes[i];
+               memcpy(vptr, m->varr, m->num_verts * sizeof *vptr);
+               vptr += m->num_verts;
+
+               if(iarr) {
+                       memcpy(iptr, m->iarr, m->num_idx * sizeof *iptr);
+                       iptr += m->num_idx;
+               }
+       }
+
+       if(!mg->vbo) {
+               glGenBuffers(1, &mg->vbo);
+       }
+       glBindBuffer(GL_ARRAY_BUFFER, mg->vbo);
+       glBufferData(GL_ARRAY_BUFFER, mg->num_verts * sizeof *varr, varr, GL_STATIC_DRAW);
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+       if(iarr) {
+               if(!mg->ibo) {
+                       glGenBuffers(1, &mg->ibo);
+               }
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mg->ibo);
+               glBufferData(GL_ELEMENT_ARRAY_BUFFER, mg->num_idx * sizeof *iarr, iarr, GL_STATIC_DRAW);
+               glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+       }
+
+       free(varr);
+       free(iarr);
+       return 0;
+}