From bab0824608498d1079d4e9522f3014d3d538aabe Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Fri, 27 Apr 2018 20:56:00 +0100 Subject: 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. --- .gitignore | 1 + Makefile | 3 +- cube.fmd | Bin 0 -> 948 bytes faqe.c | 96 ++++++++++++++++++++++++++---------------------- fmd.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fmd.h | 32 ++++++++++++++++ gl.h | 7 +++- glfunc.h | 3 ++ ieee754.c | 39 ++++++++++++++++++++ ieee754.h | 6 +++ model.c | 60 ++++++++++++++++++++++++++++++ model.h | 30 +++++++++++++++ test_ieee754.c | 38 +++++++++++++++++++ vert.glsl | 9 +++-- vertex.h | 16 ++++++++ 15 files changed, 404 insertions(+), 49 deletions(-) create mode 100644 cube.fmd create mode 100644 fmd.c create mode 100644 fmd.h create mode 100644 ieee754.c create mode 100644 ieee754.h create mode 100644 model.c create mode 100644 model.h create mode 100644 test_ieee754.c create mode 100644 vertex.h 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 Binary files /dev/null and b/cube.fmd 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 + * SPDX-License-Identifier: MIT + */ +#include +#include +#include + +#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 + * SPDX-License-Identifier: MIT + */ +#ifndef FMD_H +#define FMD_H + +#include + +#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 @@ -31,6 +31,11 @@ typedef float GLclampf; 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, @@ -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 +#include +#include +#include + +#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 + * SPDX-License-Identifier: MIT + */ +#include + +#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 + * 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 +#include +#include +#include + +#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 + * SPDX-License-Identifier: MIT + */ +#ifndef VERTEX_H +#define VERTEX_H + +#include "linmath.h" + +struct vertex { + vec3 pos; + vec3 norm; + vec2 uv; +}; + +#endif // VERTEX_H -- cgit v1.2.3-54-g00ecf