/* * model.c * * Basic functions to handle models * * (c) 2008 Thomas White * * thrust3d - a silly game * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "types.h" #include "model.h" #include "utils.h" #include "texture.h" #include "render.h" /* Maximum number of vertices per primitive */ #define MAX_VERTICES 4096 ModelContext *model_init() { ModelContext *ctx; ctx = malloc(sizeof(ModelContext)); if ( ctx == NULL ) return NULL; ctx->num_models = 0; ctx->models = NULL; return ctx; } static int model_add(ModelContext *ctx, Model *model) { ctx->models = realloc(ctx->models, (ctx->num_models+1)*sizeof(Model *)); if ( ctx->models == NULL ) { return 1; } ctx->models[ctx->num_models] = model; ctx->num_models++; return 0; } static Model *model_new(const char *name) { Model *model; model = malloc(sizeof(Model)); if ( model == NULL ) return NULL; model->num_primitives = 0; model->attrib_total = ATTRIB_NONE; model->primitives = NULL; model->name = strdup(name); return model; } static Primitive *model_add_primitive(Model *model, GLenum type, GLfloat *vertices, GLfloat *normals, GLfloat *texcoords, int n, PrimitiveAttrib attribs, GLfloat r, GLfloat g, GLfloat b, char *texture) { Primitive *p; p = malloc(sizeof(Primitive)); if ( p == NULL ) return NULL; p->vertices = malloc(n * sizeof(GLfloat) * 3); if ( p->vertices == NULL ) { free(p); return NULL; } p->normals = malloc(n * sizeof(GLfloat) * 3); if ( p->normals == NULL ) { free(p); free(p->vertices); return NULL; } p->texcoords = malloc(n * sizeof(GLfloat) * 2); if ( p->texcoords == NULL ) { free(p); free(p->vertices); free(p->normals); return NULL; } /* Copy the vertices into memory */ memcpy(p->vertices, vertices, 3*n*sizeof(GLfloat)); memcpy(p->normals, normals, 3*n*sizeof(GLfloat)); memcpy(p->texcoords, texcoords, 2*n*sizeof(GLfloat)); p->num_vertices = n; p->type = type; p->attribs = attribs; p->col_r = r; p->col_g = g; p->col_b = b; p->texture = texture; model->attrib_total = model->attrib_total | attribs; model->primitives = realloc(model->primitives, sizeof(Primitive *) * (model->num_primitives+1)); model->primitives[model->num_primitives] = p; model->num_primitives++; return 0; } static void model_calculate_normals(GLfloat *vertices, GLfloat *normals, int first, int last, int centre, int v1, int v2) { GLfloat ax, ay, az; GLfloat bx, by, bz; GLfloat nx, ny, nz, n; unsigned int i; ax = vertices[3*v1+0] - vertices[3*centre+0]; ay = vertices[3*v1+1] - vertices[3*centre+1]; az = vertices[3*v1+2] - vertices[3*centre+2]; bx = vertices[3*v2+0] - vertices[3*centre+0]; by = vertices[3*v2+1] - vertices[3*centre+1]; bz = vertices[3*v2+2] - vertices[3*centre+2]; nx = ay*bz - az*by; ny = - ax*bz + az*bx; nz = ax*by - ay*bx; n = sqrtf(nx*nx + ny*ny + nz*nz); nx = nx / n; ny = ny / n; nz = nz / n; for ( i=first; i<=last; i++ ) { normals[3*i+0] = nx; normals[3*i+1] = ny; normals[3*i+2] = nz; } } static int model_load_obj(ModelContext *ctx, const char *name, RenderContext *render) { FILE *fh; char tmp[64]; Model *model; int num_vertices; GLfloat *vertices; GLfloat *normals; GLfloat *texcoords; GLfloat vtmp[3*MAX_VERTICES]; GLfloat vntmp[3*MAX_VERTICES]; int n_vtmp, n_vntmp; char *texture; snprintf(tmp, 63, "%s/models/%s.obj", DATADIR, name); fh = fopen(tmp, "r"); if ( fh == NULL ) { return -1; } /* Zip through and find all the vertices */ n_vtmp = 0; n_vntmp = 0; while ( !feof(fh) ) { char line[1024]; GLfloat x, y, z; unsigned int v1, v2, v3; GLfloat texx, texy; size_t s; texx = 0.0; texy = 0.0; /* Default texture coordinates */ fgets(line, 1023, fh); s = 0; for ( ; s= n_vtmp ) { fprintf(stderr, "Vertex index is too high (%i/%i)\n", vnum, n_vtmp); continue; } if ( nnum >= n_vntmp ) { fprintf(stderr, "Normal index is too high (%i/%i)\n", nnum, n_vntmp); continue; } if ( num_vertices < MAX_VERTICES ) { vertices[3*num_vertices+0] = vtmp[3*vnum+0]; vertices[3*num_vertices+1] = vtmp[3*vnum+1]; vertices[3*num_vertices+2] = vtmp[3*vnum+2]; normals[3*num_vertices+0] = vntmp[3*nnum+0]; normals[3*num_vertices+1] = vntmp[3*nnum+1]; normals[3*num_vertices+2] = vntmp[3*nnum+2]; texcoords[2*num_vertices+0] = 0.0; texcoords[2*num_vertices+1] = 0.0; num_vertices++; } else { fprintf(stderr, "Too many vertices\n"); } free(sp[0]); free(sp[1]); free(sp[2]); free(sp); } else if ( nslash == 0 ) { vnum = atoi(bits[i]); if ( num_vertices < MAX_VERTICES ) { vertices[3*num_vertices+0] = vtmp[3*vnum+0]; vertices[3*num_vertices+1] = vtmp[3*vnum+1]; vertices[3*num_vertices+2] = vtmp[3*vnum+2]; normals[3*num_vertices+0] = 1.0; normals[3*num_vertices+1] = 0.0; normals[3*num_vertices+2] = 0.0; num_vertices++; } else { fprintf(stderr, "Too many vertices\n"); } } } model_add_primitive(model, GL_POLYGON, vertices, normals, texcoords, num_vertices, ATTRIB_NONE, 1.0, 1.0, 1.0, texture); num_vertices = 0; } for ( i=0; i 0 ) { model_add_primitive(model, type, vertices, normals, texcoords, num_vertices, attribs, col_r, col_g, col_b, texture); num_vertices = 0; type = GL_TRIANGLES; attribs = ATTRIB_NONE; texture = NULL; } } if ( strncmp(line, "QUADS", 5) == 0 ) { type = GL_QUADS; } if ( strncmp(line, "TRIANGLES", 9) == 0 ) { type = GL_TRIANGLES; } if ( sscanf(line, "%f %f %f %f %f", &forget, &forget, &forget, &x, &y) == 5 ) { texx = x; texy = y; } if ( sscanf(line, "%f %f %f", &x, &y, &z) == 3 ) { vertices[3*num_vertices+0] = x; vertices[3*num_vertices+1] = y; vertices[3*num_vertices+2] = z; texcoords[2*num_vertices+0] = texx; texcoords[2*num_vertices+1] = texy; num_vertices++; if ( (type == GL_QUADS) && ((num_vertices % 4)==0) ) { model_calculate_normals(vertices, normals, num_vertices-4, num_vertices-1, num_vertices-4, num_vertices-3, num_vertices-2); } if ( (type == GL_TRIANGLES) && ((num_vertices % 3)==0) ) { model_calculate_normals(vertices, normals, num_vertices-3, num_vertices-1, num_vertices-3, num_vertices-2, num_vertices-1); } if ( num_vertices > MAX_VERTICES ) { fprintf(stderr, "Too many vertices in primitive\n"); return 1; } } if ( sscanf(line, "pulse %f %f %f", &r, &g, &b) == 3 ) { attribs = attribs | ATTRIB_COLOUR; attribs = attribs | ATTRIB_PULSE; col_r = r; col_g = g; col_b = b; } if ( sscanf(line, "colour %f %f %f", &r, &g, &b) == 3 ) { attribs = attribs | ATTRIB_COLOUR; col_r = r; col_g = g; col_b = b; } if ( strncmp(line, "texture", 7) == 0 ) { if ( strlen(line) < 9 ) { fprintf(stderr, "Invalid texture specification\n"); return 1; } texture = strdup(line+8); chomp(texture); if ( texture_lookup(render, texture) == NULL ) { texture_load(render, texture); if ( texture_lookup(render, texture) == NULL ) { fprintf(stderr, "Couldn't find texture %s\n", texture); } } } } fclose(fh); return model_add(ctx, model); } static Model *model_lookup(ModelContext *ctx, const char *name) { int i, found; found = 0; for ( i=0; inum_models; i++ ) { if ( strcmp(ctx->models[i]->name, name) == 0 ) { found = 1; break; } } if ( found == 0 ) { return NULL; } return ctx->models[i]; } ModelInstance *model_instance_new(ModelContext *ctx, const char *name, RenderContext *render) { ModelInstance *instance; instance = malloc(sizeof(ModelInstance)); instance->model = model_lookup(ctx, name); if ( instance->model == NULL ) { /* Couldn't find model, so try to load it */ model_load(ctx, name, render); instance->model = model_lookup(ctx, name); if ( instance->model == NULL ) { model_load_obj(ctx, name, render); instance->model = model_lookup(ctx, name); if ( instance->model == NULL ) { free(instance); printf("Couldn't find model %s\n", name); return NULL; } } } instance->vx = 0.0; instance->vy = 0.0; instance->vz = 0.0; instance->yaw = 0.0; instance->yawspeed = 0.0; return instance; }