From 13e5f310a75d32977430bc4b0741541dccd7d84f Mon Sep 17 00:00:00 2001 From: laochailan Date: Sun, 15 Jul 2012 08:15:47 +0200 Subject: [PATCH] OBJ model loader implemented. --- CMakeLists.txt | 1 + models/test.obj | 40 +++++++ src/CMakeLists.txt | 1 + src/main.c | 16 +-- src/resource/model.c | 224 ++++++++++++++++++++++++++++++++++++++++ src/resource/model.h | 53 ++++++++++ src/resource/resource.c | 41 ++++++-- src/resource/resource.h | 7 +- src/stages/stage2.c | 5 +- src/stageutils.c | 4 +- src/stageutils.h | 16 +-- 11 files changed, 371 insertions(+), 37 deletions(-) create mode 100644 models/test.obj create mode 100644 src/resource/model.c create mode 100644 src/resource/model.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c5518f7..5cdf9773 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ install(DIRECTORY gfx DESTINATION ${DATA_DIR} FILES_MATCHING PATTERN "*.ttf") install(DIRECTORY sfx DESTINATION ${DATA_DIR}) install(DIRECTORY shader DESTINATION ${DATA_DIR}) +install(DIRECTORY models DESTINATION ${DATA_DIR}) # uninstall target configure_file( diff --git a/models/test.obj b/models/test.obj new file mode 100644 index 00000000..f16d139d --- /dev/null +++ b/models/test.obj @@ -0,0 +1,40 @@ +# Blender v2.63 (sub 0) OBJ File: '' +# www.blender.org +mtllib test.mtl +o Cube +v 1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -0.999999 +v 0.999999 1.000000 1.000001 +v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +vt 0.000000 0.500000 +vt 0.000000 0.250000 +vt 0.250000 0.250000 +vt 0.250000 0.500000 +vt 0.750000 0.500000 +vt 0.500000 0.500000 +vt 0.500000 0.250000 +vt 0.750000 0.250000 +vt 1.000000 0.500000 +vt 1.000000 0.249999 +vt 0.250000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 0.750000 +vt 0.250000 0.750000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn -0.000000 -0.000000 1.000000 +vn -1.000000 -0.000000 -0.000000 +vn 0.000000 0.000000 -1.000000 +usemtl Material_untitled +s off +f 1/1/1 2/2/1 3/3/1 4/4/1 +f 5/5/2 8/6/2 7/7/2 6/8/2 +f 1/9/3 5/5/3 6/8/3 2/10/3 +f 2/11/4 6/12/4 7/7/4 3/3/4 +f 3/3/5 7/7/5 8/6/5 4/4/5 +f 5/13/6 1/14/6 4/4/6 8/6/6 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 950cbf25..e9a019a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRCs resource/font.c resource/shader.c resource/audio.c + resource/model.c ${BISON_cfgparser_OUTPUTS} ${FLEX_cfgscanner_OUTPUTS}) diff --git a/src/main.c b/src/main.c index b90bda1f..b7cfd5ec 100644 --- a/src/main.c +++ b/src/main.c @@ -31,22 +31,8 @@ void init_gl() { void taisei_shutdown() { printf("\nshutdown:\n"); - if(resources.state & RS_SfxLoaded) { - printf("-- alutExit()\n"); - alutExit(); - } - printf("-- freeing textures\n"); - delete_textures(); - - printf("-- freeing FBOs\n"); - delete_fbo(&resources.fbg[0]); - delete_fbo(&resources.fbg[1]); - delete_fbo(&resources.fsec); - printf("-- freeing VBOs\n"); - delete_vbo(&_vbo); - printf("-- freeing shaders\n"); - delete_shaders(); + free_resources(); SDL_FreeSurface(display); SDL_Quit(); diff --git a/src/resource/model.c b/src/resource/model.c new file mode 100644 index 00000000..c84913a5 --- /dev/null +++ b/src/resource/model.c @@ -0,0 +1,224 @@ +/* + * This software is licensed under the terms of the MIT-License + * See COPYING for further information. + * --- + * Copyright (C) 2011, Lukas Weber + */ + +#include "model.h" +#include "list.h" +#include "resource.h" +#include "taisei_err.h" +#include +#include +#include + +void parse_obj(char *filename, ObjFileData *data) { + FILE *fp = fopen(filename, "rb"); + + char line[256]; + char cbuf[128]; + Vector buf; + char mode; + int linen = 0; + + memset(data, 0, sizeof(ObjFileData)); + + while(fgets(line, sizeof(line), fp)) { + linen++; + + char *first; + first = strtok(line, " \n"); + + if(strcmp(first, "v") == 0) + mode = 'v'; + else if(strcmp(first, "vt") == 0) + mode = 't'; + else if(strcmp(first, "vn") == 0) + mode = 'n'; + else if(strcmp(first, "f") == 0) + mode = 'f'; + else + mode = 0; + + if(mode != 0 && mode != 'f') { + buf[0] = atof(strtok(NULL, " \n")); + buf[1] = atof(strtok(NULL, " \n")); + if(mode != 't') + buf[2] = atof(strtok(NULL, " \n")); + + switch(mode) { + case 'v': + data->xs = realloc(data->xs, sizeof(Vector)*(++data->xcount)); + memcpy(data->xs[data->xcount-1], buf, sizeof(Vector)); + break; + case 't': + data->texcoords = realloc(data->texcoords, sizeof(Vector)*(++data->tcount)); + memcpy(data->texcoords[data->tcount-1], buf, sizeof(Vector)); + break; + case 'n': + data->normals = realloc(data->normals, sizeof(Vector)*(++data->ncount)); + memcpy(data->normals[data->ncount-1], buf, sizeof(Vector)); + break; + } + } else if(mode == 'f') { + char *segment, *seg; + char *ctmp; + int j = 0, jj; + IVector ibuf; + memset(ibuf, 0, sizeof(ibuf)); + + while((segment = strtok(NULL, " \n"))) { + memset(cbuf, 0, sizeof(cbuf)); + strncpy(cbuf, segment, sizeof(cbuf)); + j++; + + jj = 0; + while((seg = strtok_r(jj == 0 ? cbuf : NULL, "/", &ctmp))) { + if(jj >= 3) + break; + + ibuf[jj] = atoi(seg); + + jj++; + } + + if(strstr(segment, "//")) { + ibuf[2] = ibuf[1]; + ibuf[1] = 0; + } + + if(jj == 0 || jj > 3 || segment[0] == '/') + errx(-1, "parse_obj():\n!- OBJ file '%s:%d': Parsing error: Corrupt face definition\n", filename,linen); + + data->indices = realloc(data->indices, sizeof(IVector)*(++data->icount)); + memcpy(data->indices[data->icount-1], ibuf, sizeof(IVector)); + } + + if(data->fverts == 0) + data->fverts = j; + + if(data->fverts != j) + errx(-1, "parse_obj():\n!- OBJ file '%s:%d': Parsing error: face vertex count must stay the same in the whole file\n", filename, linen); + + if(data->fverts != 3 && data->fverts != 4) + errx(-1, "parse_obj():\n!- OBJ file '%s:%d': Parsing error: face vertex count must be either 3 or 4\n", filename, linen); + } + } +} + +static inline void bad_reference_error(char *filename, char *aux, int n) { + errx(-1, "load_model():\n!- OBJ file '%s': Index %d: bad %s index reference\n", filename, n, aux); +} + +Model *load_model(char *filename) { + Model *m = create_element((void **)&resources.models, sizeof(Model)); + + ObjFileData data; + unsigned int i; + + Vertex *verts; + unsigned int ioffset = _vbo.offset; + + char *beg = strstr(filename, "models/") + 7; + char *end = strrchr(filename, '.'); + + m->name = malloc(end - beg + 1); + memset(m->name, 0, end-beg + 1); + strncpy(m->name, beg, end-beg); + + + parse_obj(filename, &data); + + m->fverts = data.fverts; + m->indices = calloc(data.icount, sizeof(int)); + m->icount = data.icount; + + verts = calloc(data.icount, sizeof(Vertex)); + + + memset(verts, 0, data.icount*sizeof(Vertex)); + for(i = 0; i < data.icount; i++) { + int xi, ni, ti; + + xi = data.indices[i][0]-1; + if(xi < 0 || xi >= data.xcount) + bad_reference_error(filename, "vertex", i); + + memcpy(verts[i].x, data.xs[xi], sizeof(Vector)); + + if(data.tcount) { + ti = data.indices[i][1]-1; + if(ti < 0 || ti >= data.tcount) + bad_reference_error(filename, "texcoord", i); + + verts[i].s = data.texcoords[ti][0]; + verts[i].t = data.texcoords[ti][1]; + } + + if(data.ncount) { + ni = data.indices[i][2]-1; + if(ni < 0 || ni >= data.ncount) + bad_reference_error(filename, "normal", ni); + + memcpy(verts[i].n, data.normals[ni], sizeof(Vector)); + } + + m->indices[i] = i+ioffset; + } + + vbo_add_verts(&_vbo, verts, data.icount); + + printf("-- loaded '%s' as '%s'\n", filename, m->name); + + free(verts); + free(data.xs); + free(data.normals); + free(data.texcoords); + free(data.indices); + return m; +} + +Model *get_model(char *name) { + Model *m, *res = NULL; + for(m = resources.models; m; m = m->next) { + if(strcmp(m->name, name) == 0) + res = m; + } + + if(res == NULL) + errx(-1,"get_model():\n!- cannot load model '%s'", name); + + return res; +} + +void draw_model_p(Model *model) { + GLenum flag; + switch(model->fverts) { + case 3: + flag = GL_TRIANGLES; + break; + case 4: + flag = GL_QUADS; + break; + default: + errx(-1, "draw_model_p():\n!- Model '%s': invalid face vertex count"); + } + + glDrawElements(flag, model->icount, GL_UNSIGNED_INT, model->indices); +} + +void draw_model(char *name) { + draw_model_p(get_model(name)); +} + +void delete_model(void **models, void *model) { + free(((Model *)model)->name); + free(((Model *)model)->indices); + + delete_element(models, model); +} + +void delete_models() { // Does not delete elements from the VBO, so doing this at runtime is leaking VBO space + delete_all_elements((void **)&resources.models, delete_model); +} \ No newline at end of file diff --git a/src/resource/model.h b/src/resource/model.h new file mode 100644 index 00000000..7bdb289e --- /dev/null +++ b/src/resource/model.h @@ -0,0 +1,53 @@ +/* + * This software is licensed under the terms of the MIT-License + * See COPYING for further information. + * --- + * Copyright (C) 2011, Lukas Weber + */ + +#ifndef MODEL_H +#define MODEL_H + +#include "matrix.h" + +typedef int IVector[3]; + +typedef struct ObjFileData ObjFileData; +struct ObjFileData { + Vector *xs; + int xcount; + + Vector *normals; + int ncount; + + Vector *texcoords; + int tcount; + + IVector *indices; + int icount; + + int fverts; +}; + +typedef struct Model Model; +struct Model { + struct Model *next; + struct Model *prev; + + char *name; + + unsigned int *indices; + int icount; + + int fverts; +}; + +Model *load_model(char *filename); + +Model *get_model(char *name); + +void draw_model_p(Model *model); +void draw_model(char *name); +void delete_models(); // Does not delete elements from the VBO, so doing this at runtime is leaking VBO space + +#endif \ No newline at end of file diff --git a/src/resource/resource.c b/src/resource/resource.c index 3edbf89d..bce2b028 100644 --- a/src/resource/resource.c +++ b/src/resource/resource.c @@ -41,6 +41,8 @@ void recurse_dir(char *path) { load_sound(buf); } else if(strcmp(dp->d_name + strlen(dp->d_name)-4, ".sha") == 0) { load_shader(buf); + } else if(strcmp(dp->d_name + strlen(dp->d_name)-4, ".obj") == 0) { + load_model(buf); } free(buf); @@ -89,17 +91,40 @@ void load_resources() { resources.state |= RS_ShaderLoaded; } + if(!(resources.state & RS_ModelsLoaded)) { + printf("- models:\n"); + strcpy(path, get_prefix()); + strcat(path, "models"); + recurse_dir(path); + + resources.state |= RS_ModelsLoaded; + } } -void free_resources() { - delete_textures(); - delete_animations(); - - if(!tconfig.intval[NO_SHADER]) - delete_shaders(); - - if(!tconfig.intval[NO_AUDIO]) { +void free_resources() { + if(resources.state & RS_SfxLoaded) { + printf("-- freeing sounds\n"); delete_sounds(); + printf("-- alutExit()\n"); alutExit(); } + + printf("-- freeing textures\n"); + delete_textures(); + + printf("-- freeing models\n"); + delete_animations(); + + printf("-- freeing VBOs\n"); + delete_vbo(&_vbo); + + if(resources.state & RS_ShaderLoaded) { + printf("-- freeing FBOs\n"); + delete_fbo(&resources.fbg[0]); + delete_fbo(&resources.fbg[1]); + delete_fbo(&resources.fsec); + + printf("-- freeing shaders\n"); + delete_shaders(); + } } diff --git a/src/resource/resource.h b/src/resource/resource.h index 38c52901..2482ed56 100644 --- a/src/resource/resource.h +++ b/src/resource/resource.h @@ -15,13 +15,15 @@ #include "audio.h" #include "shader.h" #include "font.h" +#include "model.h" typedef struct Resources Resources; typedef enum ResourceState { RS_GfxLoaded = 1, RS_SfxLoaded = 2, - RS_ShaderLoaded = 4 + RS_ShaderLoaded = 4, + RS_ModelsLoaded = 8 } ResourceState; enum { @@ -35,6 +37,7 @@ struct Resources { Animation *animations; Sound *sounds; Shader *shaders; + Model *models; ALuint sndsrc[SNDSRC_COUNT]; @@ -45,5 +48,5 @@ struct Resources { extern Resources resources; void load_resources(); - +void free_resources(); #endif diff --git a/src/stages/stage2.c b/src/stages/stage2.c index dda79763..0879bc03 100644 --- a/src/stages/stage2.c +++ b/src/stages/stage2.c @@ -30,13 +30,13 @@ void stage2_bg_tunnel_draw(Vector pos) { glPushMatrix(); glTranslatef(pos[0], pos[1], pos[2]); + glBindTexture(GL_TEXTURE_2D, get_tex("stage1/border")->gltex); for(i = 0; i < n; i++) { glPushMatrix(); glRotatef(360/n*i, 0, 1, 0); glTranslatef(0,0,-r); glScalef(2*r/tan((n-2)*M_PI/n), 3000, 1); - glBindTexture(GL_TEXTURE_2D, get_tex("stage1/border")->gltex); - + draw_quad(); glPopMatrix(); } @@ -45,6 +45,7 @@ void stage2_bg_tunnel_draw(Vector pos) { glPopMatrix(); glDisable(GL_TEXTURE_2D); + } void stage2_fog(int fbonum) { diff --git a/src/stageutils.c b/src/stageutils.c index 95efa13f..bd75b8d1 100644 --- a/src/stageutils.c +++ b/src/stageutils.c @@ -16,8 +16,8 @@ void init_stage3d(Stage3D *s) { s->projangle = 45; } -void add_model(Stage3D *s, ModelDrawRule draw, ModelPositionRule pos) { - s->models = realloc(s->models, (++s->msize)*sizeof(Model)); +void add_model(Stage3D *s, SegmentDrawRule draw, SegmentPositionRule pos) { + s->models = realloc(s->models, (++s->msize)*sizeof(StageSegment)); s->models[s->msize - 1].draw = draw; s->models[s->msize - 1].pos = pos; diff --git a/src/stageutils.h b/src/stageutils.h index 45912589..1fc62508 100644 --- a/src/stageutils.h +++ b/src/stageutils.h @@ -10,19 +10,19 @@ #include "matrix.h" -typedef struct Model Model; +typedef struct StageSegment StageSegment; -typedef void (*ModelDrawRule)(Vector pos); -typedef Vector **(*ModelPositionRule)(Vector q, float maxrange); // returns NULL-terminated array +typedef void (*SegmentDrawRule)(Vector pos); +typedef Vector **(*SegmentPositionRule)(Vector q, float maxrange); // returns NULL-terminated array -struct Model { - ModelDrawRule draw; - ModelPositionRule pos; +struct StageSegment { + SegmentDrawRule draw; + SegmentPositionRule pos; }; typedef struct Stage3D Stage3D; struct Stage3D { - Model *models; + StageSegment *models; int msize; // Camera @@ -36,7 +36,7 @@ struct Stage3D { void init_stage3d(Stage3D *s); -void add_model(Stage3D *s, ModelDrawRule draw, ModelPositionRule pos); +void add_model(Stage3D *s, SegmentDrawRule draw, SegmentPositionRule pos); void set_perspective(Stage3D *s, float near, float far); void draw_stage3d(Stage3D *s, float maxrange);