aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2018-04-27 20:56:00 +0100
committerTomasz Kramkowski <tk@the-tk.com>2018-04-27 20:56:00 +0100
commitbab0824608498d1079d4e9522f3014d3d538aabe (patch)
tree932f082b58fd48f85fbbdfa3f7419d2b3b03eedb
parent3d73622dbd5a9ddc687fe6f42268c760695d7226 (diff)
downloadfaqe-bab0824608498d1079d4e9522f3014d3d538aabe.tar.gz
faqe-bab0824608498d1079d4e9522f3014d3d538aabe.tar.xz
faqe-bab0824608498d1079d4e9522f3014d3d538aabe.zip
Implement basic model loading of FMD format.
The FMD (Faqe Model Data) format is a format designed for faqe. It stores vertex, element and material information and mesh information. This patch provides the basic implementation and use of this format. This patch also implements perspective projection, depth testing and view and model matrices. An example fmd file is provided.
-rw-r--r--.gitignore1
-rw-r--r--Makefile3
-rw-r--r--cube.fmdbin0 -> 948 bytes
-rw-r--r--faqe.c96
-rw-r--r--fmd.c113
-rw-r--r--fmd.h32
-rw-r--r--gl.h7
-rw-r--r--glfunc.h3
-rw-r--r--ieee754.c39
-rw-r--r--ieee754.h6
-rw-r--r--model.c60
-rw-r--r--model.h30
-rw-r--r--test_ieee754.c38
-rw-r--r--vert.glsl9
-rw-r--r--vertex.h16
15 files changed, 404 insertions, 49 deletions
diff --git a/.gitignore b/.gitignore
index ad9f5de..dbb5cfe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
*.o
faqe
+test_ieee754
*.exe
*.dll
diff --git a/Makefile b/Makefile
index 004053e..5ceed54 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) -std=c11 -MMD -MP
LDFLAGS += -Wl,--as-needed
LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBS)) -lm
-OBJ := faqe.o gl.o eprintf.o glprog.o
+OBJ := faqe.o eprintf.o fmd.o gl.o glprog.o ieee754.o model.o
all: $(PROG)
@@ -24,6 +24,7 @@ include $(EPRINTF_PATH)/module.mk
include $(LINMATH_PATH)/module.mk
$(PROG): $(OBJ)
+test_ieee754: ieee754.o
faqe.o: assets.h
deplinks: $(EPRINTF_FILES) $(LINMATH_FILES)
diff --git a/cube.fmd b/cube.fmd
new file mode 100644
index 0000000..6177d5d
--- /dev/null
+++ b/cube.fmd
Binary files differ
diff --git a/faqe.c b/faqe.c
index ddcff69..bc5bd2f 100644
--- a/faqe.c
+++ b/faqe.c
@@ -11,33 +11,37 @@
#include "glprog.h"
#include "linmath.h"
#include "nelem.h"
+#include "model.h"
enum {
WIDTH = 800,
HEIGHT = 600,
};
+# define PI 3.14159265358979323846
+#define FOV (PI * 0.5)
+
+void viewport(GLuint proj, int width, int height)
+{
+ mat4x4 pers;
+ gl_viewport(0, 0, width, height);
+ mat4x4_perspective(pers, FOV, (float)width / (float)height, 0.1, 100);
+ gl_uni_setm4fv(proj, 1, GL_FALSE, pers[0]);
+}
+
int main(int argc, char **argv)
{
bool running = true;
SDL_Window *win;
SDL_GLContext *glc;
- GLuint ebo, vbo, vao, prog;
- GLint ucolor;
- struct vertex {
- vec3 pos;
- vec3 col;
- vec2 uv;
- } verts[] = {
- {{ 0.5, 0.5, 0.0 }, { 1.0, 0.0, 0.0 }, { 1.0, 1.0 }},
- {{ 0.5, -0.5, 0.0 }, { 0.0, 1.0, 0.0 }, { 1.0, 0.0 }},
- {{ -0.5, -0.5, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0 }},
- {{ -0.5, 0.5, 0.0 }, { 1.0, 1.0, 0.0 }, { 0.0, 1.0 }},
- };
- GLuint idxs[] = {
- 0, 1, 3,
- 1, 2, 3,
- };
+ GLuint prog;
+ struct {
+ GLint model, view, proj;
+ } uni;
+ mat4x4 view;
+ FILE *cube_file;
+ struct fmd cube_fmd;
+ struct model cube_model;
setprogname(argc >= 1 && argv[0] != NULL ? argv[0] : "faqe");
@@ -62,37 +66,37 @@ int main(int argc, char **argv)
gl_load((gl_loadfunc *)SDL_GL_GetProcAddress);
- gl_viewport(0, 0, WIDTH, HEIGHT);
-
- gl_buf_gen(1, &vbo);
- gl_buf_gen(1, &ebo);
- gl_va_gen(1, &vao);
-
- gl_va_bind(vao);
-
- gl_buf_bind(GL_ARRAY_BUFFER, vbo);
- gl_buf_data(GL_ARRAY_BUFFER, sizeof verts, verts, GL_STATIC_DRAW);
-
- gl_buf_bind(GL_ELEMENT_ARRAY_BUFFER, ebo);
- gl_buf_data(GL_ELEMENT_ARRAY_BUFFER, sizeof idxs, idxs, GL_STATIC_DRAW);
-
- gl_va_define(0, NELEM(verts[0].pos), GL_FLOAT, GL_FALSE, sizeof *verts, (void *)offsetof(struct vertex, pos));
- gl_va_enable(0);
- gl_va_define(1, NELEM(verts[0].col), GL_FLOAT, GL_FALSE, sizeof *verts, (void *)offsetof(struct vertex, col));
- gl_va_enable(1);
- gl_va_define(2, NELEM(verts[0].uv), GL_FLOAT, GL_FALSE, sizeof *verts, (void *)offsetof(struct vertex, uv));
- gl_va_enable(2);
+ cube_file = fopen("cube.fmd", "rb");
+ if (cube_file == NULL)
+ eprintf("Could not open 'cube.fmd':");
+ fmd_load(&cube_fmd, cube_file);
+ model_load(&cube_model, &cube_fmd);
+ fmd_free(&cube_fmd);
+ fclose(cube_file);
prog = glprog_load(2, (struct shdrdat []){
{ GL_VERTEX_SHADER, vert_glsl.data, vert_glsl.size },
{ GL_FRAGMENT_SHADER, frag_glsl.data, frag_glsl.size },
},
- 1, &(struct unidat){ "ucolor", &ucolor });
+ 3, (struct unidat []){
+ { "model", &uni.model },
+ { "view", &uni.view },
+ { "proj", &uni.proj },
+ });
- gl_poly_mode(GL_FRONT_AND_BACK, GL_LINE);
+ viewport(uni.proj, WIDTH, HEIGHT);
+
+ gl_enable(GL_DEPTH_TEST);
while (running) {
+ mat4x4 model;
SDL_Event ev;
+ float secs;
+
+ secs = SDL_GetTicks() * 0.0001;
+
+ mat4x4_look_at(view, (vec3){ 0, 0, 3 }, (vec3){ 0, 0, 0 }, (vec3){ 0, 1, 0 });
+ gl_uni_setm4fv(uni.view, 1, GL_FALSE, view[0]);
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
@@ -108,19 +112,23 @@ int main(int argc, char **argv)
case SDL_WINDOWEVENT:
switch (ev.window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED:
- gl_viewport(0, 0,
- ev.window.data1,
- ev.window.data2);
+ viewport(uni.proj,
+ ev.window.data1,
+ ev.window.data2);
}
break;
}
}
gl_clearcolor(0.2, 0.3, 0.3, 1.0);
- gl_clear(GL_COLOR_BUFFER_BIT);
+ gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
gl_prog_use(prog);
- gl_va_bind(vao);
- gl_draw_elems(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+ mat4x4_identity(model);
+ mat4x4_rotate_X(model, model, secs * 3);
+ mat4x4_rotate_Y(model, model, secs * 2);
+ mat4x4_rotate_Z(model, model, secs * 4);
+ gl_uni_setm4fv(uni.model, 1, GL_TRUE, model[0]);
+ model_render(&cube_model);
gl_va_bind(0);
SDL_GL_SwapWindow(win);
diff --git a/fmd.c b/fmd.c
new file mode 100644
index 0000000..4d71f0d
--- /dev/null
+++ b/fmd.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 Tomasz Kramkowski <tk@the-tk.com>
+ * SPDX-License-Identifier: MIT
+ */
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "eprintf.h"
+#include "fmd.h"
+#include "ieee754.h"
+#include "nelem.h"
+
+static const char *MAGIC = "FMD001\r\n";
+
+static void checkmagic(FILE *f)
+{
+ for (size_t i = 0; i < strlen(MAGIC); i++)
+ assert(fgetc(f) == MAGIC[i]);
+}
+
+static inline unsigned long betoul(FILE *src)
+{
+ unsigned long ret = 0;
+
+ assert(src != NULL);
+
+ for (int i = 0; i < 4; i++) {
+ int c = fgetc(src);
+ assert(c != EOF);
+ ret += (unsigned long)(c & 0xff) << (3 - i) * 8;
+ }
+
+ return ret;
+}
+
+void readfloats(float *dest, int count, FILE *src)
+{
+ assert(dest != NULL);
+ assert(src != NULL);
+
+ for (int i = 0; i < count; i++)
+ dest[i] = ieee754f(betoul(src));
+}
+
+void fmd_load(struct fmd *fmd, FILE *f)
+{
+ assert(fmd != NULL);
+ assert(f != NULL);
+
+ checkmagic(f);
+
+ fmd->nverts = betoul(f);
+ if (fmd->nverts > 0)
+ fmd->verts = emalloc(fmd->nverts * sizeof *fmd->verts);
+ else
+ fmd->verts = NULL;
+ for (int i = 0; i < fmd->nverts; i++) {
+ struct vertex *v = &fmd->verts[i];
+ readfloats(v->pos, 3, f);
+ readfloats(v->norm, 3, f);
+ readfloats(v->uv, 2, f);
+ }
+ fmd->ntris = betoul(f);
+ if (fmd->ntris > 0)
+ fmd->tris = emalloc(fmd->ntris * sizeof *fmd->tris);
+ else
+ fmd->tris = NULL;
+ for (int i = 0; i < fmd->ntris; i++)
+ for (size_t j = 0; j < NELEM(fmd->tris[j]); j++)
+ fmd->tris[i][j] = betoul(f);
+
+ fmd->nmtls = betoul(f);
+ if (fmd->nmtls > 0)
+ fmd->mtls = emalloc(fmd->nmtls * sizeof *fmd->mtls);
+ else
+ fmd->mtls = NULL;
+ for (int i = 0; i < fmd->nmtls; i++) {
+ unsigned long len = betoul(f);
+ char **m = &fmd->mtls[i];
+ size_t ret;
+
+ *m = emalloc(len + 1);
+
+ ret = fread(*m, len, 1, f);
+ assert(ret == 1);
+ *m[len] = '\0';
+ }
+
+ fmd->nmeshes = betoul(f);
+ if (fmd->nmeshes > 0)
+ fmd->meshes = emalloc(fmd->nmeshes * sizeof *fmd->meshes);
+ else
+ fmd->meshes = NULL;
+ for (int i = 0; i < fmd->nmeshes; i++) {
+ struct fmd_mesh *m = &fmd->meshes[i];
+ m->midx = betoul(f);
+ m->tidx = betoul(f);
+ m->tcnt = betoul(f);
+ }
+}
+
+void fmd_free(struct fmd *fmd)
+{
+ assert(fmd);
+
+ free(fmd->verts);
+ free(fmd->tris);
+ for (int i = 0; i < fmd->nmtls; i++)
+ free(fmd->mtls[i]);
+ free(fmd->mtls);
+ free(fmd->meshes);
+}
diff --git a/fmd.h b/fmd.h
new file mode 100644
index 0000000..af562f9
--- /dev/null
+++ b/fmd.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 Tomasz Kramkowski <tk@the-tk.com>
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FMD_H
+#define FMD_H
+
+#include <stdio.h>
+
+#include "gl.h"
+#include "vertex.h"
+
+typedef GLuint tri[3];
+struct fmd {
+ int nverts;
+ struct vertex *verts;
+ int ntris;
+ tri *tris;
+ int nmtls;
+ char **mtls;
+ int nmeshes;
+ struct fmd_mesh {
+ int midx;
+ int tidx;
+ int tcnt;
+ } *meshes;
+};
+
+void fmd_load(struct fmd *fmd, FILE *f);
+void fmd_free(struct fmd *fmd);
+
+#endif // FMD_H
diff --git a/gl.h b/gl.h
index 30e7862..112fc82 100644
--- a/gl.h
+++ b/gl.h
@@ -32,6 +32,11 @@ typedef double GLdouble;
typedef double GLclampd;
enum {
+ GL_DEPTH_BUFFER_BIT = 0x100,
+ GL_COLOR_BUFFER_BIT = 0x4000,
+};
+
+enum {
GL_NO_ERROR = 0,
GL_FALSE = 0,
GL_TRUE = 1,
@@ -45,10 +50,10 @@ enum {
GL_OUT_OF_MEMORY = 0x0505,
GL_INVALID_FRAMEBUFFER_OPERATION = 0x0506,
GL_CONTEXT_LOST = 0x0507,
+ GL_DEPTH_TEST = 0x0b71,
GL_UNSIGNED_INT = 0x1405,
GL_FLOAT = 0x1406,
GL_LINE = 0x1B01,
- GL_COLOR_BUFFER_BIT = 0x4000,
GL_ARRAY_BUFFER = 0x8892,
GL_ELEMENT_ARRAY_BUFFER = 0x8893,
GL_STREAM_DRAW = 0x88e0,
diff --git a/glfunc.h b/glfunc.h
index 8b5776b..6722567 100644
--- a/glfunc.h
+++ b/glfunc.h
@@ -43,3 +43,6 @@ GL_FUNC(glUniform3fv, void, gl_uni_set3fv, GLint location, GLsizei count, const
GL_FUNC(glUniform4fv, void, gl_uni_set4fv, GLint location, GLsizei count, const GLfloat *value)
GL_FUNC(glUniformMatrix3fv, void, gl_uni_setm3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
GL_FUNC(glUniformMatrix4fv, void, gl_uni_setm4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_FUNC(glGetIntegerv, void, gl_geti, GLenum pname, GLint *data)
+GL_FUNC(glEnable, void, gl_enable, GLenum cap)
+GL_FUNC(glDisable, void, gl_disable, GLenum cap)
diff --git a/ieee754.c b/ieee754.c
new file mode 100644
index 0000000..3b21351
--- /dev/null
+++ b/ieee754.c
@@ -0,0 +1,39 @@
+#include <assert.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "ieee754.h"
+
+// ieee754f: convert f32 to float
+float ieee754f(unsigned long b)
+{
+ bool isneg;
+ int exp;
+ float n;
+
+ isneg = (b >> 31) & 0x1;
+ exp = (b >> 23) & 0xff;
+ n = b & 0x7fffff;
+
+ if (exp == 0xff) {
+ if (n) {
+ /* TODO: work out what a negative NaN means */
+ assert(!isneg);
+ return NAN;
+ } else {
+ return isneg ? -INFINITY : INFINITY;
+ }
+ } else if (exp == 0) {
+ if (n == 0)
+ return isneg ? -0 : 0;
+ exp = -126;
+ } else {
+ n += 0x1p23f;
+ exp -= 127;
+ }
+
+ n = ldexpf(n, exp - 23);
+
+ return isneg ? -n : n;
+}
diff --git a/ieee754.h b/ieee754.h
new file mode 100644
index 0000000..58f886d
--- /dev/null
+++ b/ieee754.h
@@ -0,0 +1,6 @@
+#ifndef IEEE754_H
+#define IEEE754_H
+
+float ieee754f(unsigned long l);
+
+#endif // IEEE754_H
diff --git a/model.c b/model.c
new file mode 100644
index 0000000..7a6a256
--- /dev/null
+++ b/model.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 Tomasz Kramkowski <tk@the-tk.com>
+ * SPDX-License-Identifier: MIT
+ */
+#include <assert.h>
+
+#include "eprintf.h"
+#include "linmath.h"
+#include "model.h"
+#include "nelem.h"
+#include "vertex.h"
+
+void model_load(struct model *mdl, const struct fmd *fmd)
+{
+ GLuint vbo, ebo;
+ struct vertex *v;
+
+ assert(mdl != NULL);
+ assert(fmd != NULL);
+
+ gl_va_gen(1, &mdl->vao);
+ gl_va_bind(mdl->vao);
+
+ gl_buf_gen(1, &vbo);
+ gl_buf_bind(GL_ARRAY_BUFFER, vbo);
+ gl_buf_data(GL_ARRAY_BUFFER, fmd->nverts * sizeof *fmd->verts, fmd->verts, GL_STATIC_DRAW);
+
+ gl_buf_gen(1, &ebo);
+ gl_buf_bind(GL_ELEMENT_ARRAY_BUFFER, ebo);
+ gl_buf_data(GL_ELEMENT_ARRAY_BUFFER, fmd->ntris * sizeof *fmd->tris, fmd->tris, GL_STATIC_DRAW);
+
+ v = &fmd->verts[0];
+ gl_va_define(0, NELEM(v->pos), GL_FLOAT, GL_FALSE, sizeof *v, (void *)offsetof(struct vertex, pos));
+ gl_va_enable(0);
+ gl_va_define(1, NELEM(v->norm), GL_FLOAT, GL_FALSE, sizeof *v, (void *)offsetof(struct vertex, norm));
+ gl_va_enable(1);
+ gl_va_define(2, NELEM(v->uv), GL_FLOAT, GL_FALSE, sizeof *v, (void *)offsetof(struct vertex, uv));
+ gl_va_enable(2);
+
+ // TODO: materials, use a shared material set
+
+ mdl->nmeshes = fmd->nmeshes;
+ mdl->meshes = emalloc(mdl->nmeshes * sizeof *mdl->meshes);
+ for (int i = 0; i < fmd->nmeshes; i++) {
+ struct mesh *m = &mdl->meshes[i];
+ struct fmd_mesh *fm = &fmd->meshes[i];
+ m->mtl = NULL;
+ m->elems.idx = fm->tidx * NELEM(*fmd->tris);
+ m->elems.cnt = fm->tcnt * NELEM(*fmd->tris);
+ }
+}
+
+void model_render(struct model *mdl)
+{
+ gl_va_bind(mdl->vao);
+ for (int i = 0; i < mdl->nmeshes; i++) {
+ struct mesh *m = &mdl->meshes[i];
+ gl_draw_elems(GL_TRIANGLES, m->elems.cnt, GL_UNSIGNED_INT, &((GLuint *)NULL)[m->elems.idx]);
+ }
+}
diff --git a/model.h b/model.h
new file mode 100644
index 0000000..33db3b4
--- /dev/null
+++ b/model.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 Tomasz Kramkowski <tk@the-tk.com>
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef MODEL_H
+#define MODEL_H
+
+#include "gl.h"
+#include "fmd.h"
+
+struct mesh {
+ struct material {
+ GLuint diffuse;
+ } *mtl;
+ struct {
+ GLuint idx;
+ GLuint cnt;
+ } elems;
+};
+
+struct model {
+ struct mesh *meshes;
+ int nmeshes;
+ GLuint vao;
+};
+
+void model_load(struct model *mdl, const struct fmd *fmd);
+void model_render(struct model *mdl);
+
+#endif // MODEL_H
diff --git a/test_ieee754.c b/test_ieee754.c
new file mode 100644
index 0000000..44873c9
--- /dev/null
+++ b/test_ieee754.c
@@ -0,0 +1,38 @@
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ieee754.h"
+
+static unsigned long naiive(float n)
+{
+ union {
+ float f;
+ uint32_t u;
+ } u = { .f = n };
+
+ return u.u;
+}
+
+void test(float a)
+{
+ float b = ieee754f(naiive(a));
+
+ if (!((isnan(a) && isnan(b)) || a == b)) {
+ fprintf(stderr, "%lu: %.40f != %.40f\n", naiive(a), a, b);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(void)
+{
+ test(NAN);
+ test(+INFINITY);
+ test(-INFINITY);
+ test(0x1p127f);
+ test(0x7fffffp-149f);
+
+ for (float n = -0.000000001; n <= 1; n = nextafterf(n, 2))
+ test(n);
+}
diff --git a/vert.glsl b/vert.glsl
index 2ee78d0..d33cc47 100644
--- a/vert.glsl
+++ b/vert.glsl
@@ -2,13 +2,16 @@
// SPDX-License-Identifier: MIT
#version 330 core
layout (location = 0) in vec3 pos;
-layout (location = 1) in vec3 color;
+layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 uv;
out vec3 vcolor;
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 proj;
void main()
{
- gl_Position = vec4(pos, 1.0);
- vcolor = color;
+ gl_Position = proj * view * model * vec4(pos, 1.0);
+ vcolor = normal;
}
diff --git a/vertex.h b/vertex.h
new file mode 100644
index 0000000..7f58ee9
--- /dev/null
+++ b/vertex.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2018 Tomasz Kramkowski <tk@the-tk.com>
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef VERTEX_H
+#define VERTEX_H
+
+#include "linmath.h"
+
+struct vertex {
+ vec3 pos;
+ vec3 norm;
+ vec2 uv;
+};
+
+#endif // VERTEX_H