taisei/src/resource/model.c

217 lines
5.1 KiB
C
Raw Normal View History

2012-07-15 08:15:47 +02:00
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
2012-07-15 08:15:47 +02:00
* ---
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
*/
#include "model.h"
#include "list.h"
#include "resource.h"
#include "taisei_err.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void bad_reference_error(const char *filename, const char *aux, int n) {
warnx("load_model():\n!- OBJ file '%s': Index %d: bad %s index reference\n", filename, n, aux);
}
static void parse_obj(const char *filename, ObjFileData *data);
static void free_obj(ObjFileData *data);
char* model_path(const char *name) {
return strjoin(MDL_PATH_PREFIX, name, MDL_EXTENSION, NULL);
}
bool check_model_path(const char *path) {
return strendswith(path, MDL_EXTENSION);
}
void* load_model(const char *path, unsigned int flags) {
Model *m = malloc(sizeof(Model));
ObjFileData data;
unsigned int i;
Vertex *verts;
unsigned int ioffset = _vbo.offset;
parse_obj(path, &data);
m->fverts = data.fverts;
m->indices = calloc(data.icount, sizeof(unsigned int));
m->icount = data.icount;
verts = calloc(data.icount, sizeof(Vertex));
#define BADREF(...) { bad_reference_error(__VA_ARGS__); free(verts); free_obj(&data); return NULL; }
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)
BADREF(path, "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)
BADREF(path, "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)
BADREF(path, "normal", ni);
memcpy(verts[i].n, data.normals[ni], sizeof(Vector));
}
m->indices[i] = i+ioffset;
}
#undef BADREF
vbo_add_verts(&_vbo, verts, data.icount);
free(verts);
free_obj(&data);
return m;
}
void unload_model(void *model) { // Does not delete elements from the VBO, so doing this at runtime is leaking VBO space
free(((Model*)model)->indices);
free(model);
}
static void free_obj(ObjFileData *data) {
free(data->xs);
free(data->normals);
free(data->texcoords);
free(data->indices);
}
2017-02-28 18:47:47 +01:00
static void parse_obj(const char *filename, ObjFileData *data) {
2012-07-15 08:15:47 +02:00
FILE *fp = fopen(filename, "rb");
2012-07-15 08:15:47 +02:00
char line[256];
Vector buf;
char mode;
int linen = 0;
2012-07-15 08:15:47 +02:00
memset(data, 0, sizeof(ObjFileData));
2012-07-15 08:15:47 +02:00
while(fgets(line, sizeof(line), fp)) {
linen++;
2012-07-15 08:15:47 +02:00
char *first;
first = strtok(line, " \n");
2012-07-15 08:15:47 +02:00
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') {
2012-07-15 08:15:47 +02:00
buf[0] = atof(strtok(NULL, " \n"));
buf[1] = atof(strtok(NULL, " \n"));
if(mode != 't')
buf[2] = atof(strtok(NULL, " \n"));
2012-07-15 08:15:47 +02:00
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;
int j = 0, jj;
IVector ibuf;
memset(ibuf, 0, sizeof(ibuf));
2012-07-15 08:15:47 +02:00
while((segment = strtok(NULL, " \n"))) {
2012-07-18 13:42:04 +02:00
seg = segment;
j++;
2012-07-15 08:15:47 +02:00
jj = 0;
2012-07-18 13:42:04 +02:00
while(jj < 3) {
2012-07-15 08:15:47 +02:00
ibuf[jj] = atoi(seg);
jj++;
2012-07-18 13:42:04 +02:00
while(*seg != '\0' && *(++seg) != '/');
2012-07-18 13:42:04 +02:00
if(*seg == '\0')
break;
else
seg++;
2012-07-15 08:15:47 +02:00
}
2012-07-15 08:15:47 +02:00
if(strstr(segment, "//")) {
ibuf[2] = ibuf[1];
ibuf[1] = 0;
}
2012-07-15 08:15:47 +02:00
if(jj == 0 || jj > 3 || segment[0] == '/')
errx(-1, "parse_obj():\n!- OBJ file '%s:%d': Parsing error: Corrupt face definition\n", filename,linen);
2012-07-15 08:15:47 +02:00
data->indices = realloc(data->indices, sizeof(IVector)*(++data->icount));
memcpy(data->indices[data->icount-1], ibuf, sizeof(IVector));
}
2012-07-18 13:42:04 +02:00
2012-07-15 08:15:47 +02:00
if(data->fverts == 0)
data->fverts = j;
2012-07-15 08:15:47 +02:00
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);
2012-07-15 08:15:47 +02:00
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);
2012-07-15 08:15:47 +02:00
}
}
2012-08-10 22:09:15 +02:00
fclose(fp);
2012-07-15 08:15:47 +02:00
}
Model* get_model(const char *name) {
return get_resource(RES_MODEL, name, RESF_DEFAULT)->model;
2012-07-15 08:15:47 +02:00
}
void draw_model_p(Model *model) {
GLenum flag = model->fverts == 3 ? GL_TRIANGLES : GL_QUADS;
2012-07-16 18:13:58 +02:00
glMatrixMode(GL_TEXTURE);
glScalef(1,-1,1); // every texture in taisei is actually read vertically mirrored. and I noticed that just now.
2012-07-15 08:15:47 +02:00
glDrawElements(flag, model->icount, GL_UNSIGNED_INT, model->indices);
2012-07-16 18:13:58 +02:00
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
2012-07-15 08:15:47 +02:00
}
2017-02-28 18:47:47 +01:00
void draw_model(const char *name) {
2012-07-15 08:15:47 +02:00
draw_model_p(get_model(name));
}