ultimatepp/uppdev/GLShadow/load3ds.c
cxl 351994a6cc Adding uppdev....
git-svn-id: svn://ultimatepp.org/upp/trunk@328 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-08-15 08:36:24 +00:00

396 lines
11 KiB
C

/* load 3ds file
*
* written by Alexander Zaprjagaev
* frustum@public.tsu.ru
*
* usage:
* float *load_3ds(char *name,int *num_vertex);
* 1,2,3 - xyz
* 4,5,6 - normal
* 7,8 - st
*/
#include <math.h>
#include <stdio.h>
#include <malloc.h>
#define CHUNK_MAIN 0x4d4d
#define CHUNK_OBJMESH 0x3d3d
#define CHUNK_OBJBLOCK 0x4000
#define CHUNK_TRIMESH 0x4100
#define CHUNK_VERTLIST 0x4110
#define CHUNK_FACELIST 0x4120
#define CHUNK_MAPLIST 0x4140
#define CHUNK_SMOOLIST 0x4150
typedef struct {
float x,y,z;
} vector_t;
typedef struct {
float u,v;
} uvmap_t;
typedef struct {
vector_t *vertex;
int num_vertex;
uvmap_t *uvmap;
int num_uvmap;
int *face;
int *smoothgroup;
vector_t *normal;
int num_face;
} trimesh_t;
typedef struct {
trimesh_t *trimesh;
int num_trimesh;
} mesh_t;
typedef int (*process_chunk)(FILE *file,
unsigned short type,int size,void *data);
/*
*/
static void vector_copy(vector_t *a,vector_t *b) {
b->x = a->x;
b->y = a->y;
b->z = a->z;
}
static void vector_add(vector_t *a,vector_t *b,vector_t *c) {
c->x = a->x + b->x;
c->y = a->y + b->y;
c->z = a->z + b->z;
}
static void vector_sub(vector_t *a,vector_t *b,vector_t *c) {
c->x = a->x - b->x;
c->y = a->y - b->y;
c->z = a->z - b->z;
}
static void vector_cross(vector_t *a,vector_t *b,vector_t *c) {
c->x = a->y * b->z - a->z * b->y;
c->y = a->z * b->x - a->x * b->z;
c->z = a->x * b->y - a->y * b->x;
}
static float vector_normalize(vector_t *a,vector_t *b) {
float length = sqrt(a->x * a->x + a->y * a->y + a->z * a->z);
if(length) {
float ilength = 1.0 / length;
b->x = a->x * ilength;
b->y = a->y * ilength;
b->z = a->z * ilength;
return length;
}
b->x = 0.0;
b->y = 0.0;
b->z = 0.0;
return 0.0;
}
/*
*/
static void skeep_bytes(FILE *file,int bytes) {
fseek(file,bytes,SEEK_CUR);
}
static int skeep_string(FILE *file) {
int i = 0;
while(fgetc(file) != '\0') i++;
return ++i;
}
static unsigned short read_ushort(FILE *file) {
unsigned short ret;
fread(&ret,1,sizeof(unsigned short),file);
return ret;
}
static int read_int(FILE *file) {
int ret;
fread(&ret,1,sizeof(int),file);
return ret;
}
static float read_float(FILE *file) {
float ret;
fread(&ret,1,sizeof(float),file);
return ret;
}
static int read_chunk(FILE *file,process_chunk chunk_func,void *data) {
unsigned short chunk_type;
int chunk_size,content_size;
chunk_type = read_ushort(file);
chunk_size = read_int(file);
content_size = chunk_size - 6;
if(!chunk_func(file,chunk_type,content_size,data))
skeep_bytes(file,content_size);
return chunk_size;
}
static int read_chunks(FILE *file,int bytes,
process_chunk chunk_func,void *data) {
int bytes_read = 0;
while(bytes_read < bytes)
bytes_read += read_chunk(file,chunk_func,data);
if(bytes_read != bytes)
fprintf(stderr,"expected %d bytes but read %d\n",bytes_read,bytes);
return bytes_read;
}
/*
*/
static int process_smoolist(FILE *file,unsigned short type,
int size,void *data) {
int i;
trimesh_t *trimesh = (trimesh_t*)data;
if(type == CHUNK_SMOOLIST) {
trimesh->smoothgroup = malloc(sizeof(int) * trimesh->num_face);
for(i = 0; i < trimesh->num_face; i++) {
trimesh->smoothgroup[i] = read_int(file);
}
return 1;
}
return 0;
}
static int process_trimesh(FILE *file,unsigned short type,
int size,void *data) {
int i;
trimesh_t *trimesh = (trimesh_t*)data;
if(type == CHUNK_VERTLIST) { /* vertlist */
trimesh->num_vertex = read_ushort(file);
trimesh->vertex = malloc(sizeof(vector_t) * trimesh->num_vertex);
for(i = 0; i < trimesh->num_vertex; i++) {
trimesh->vertex[i].x = read_float(file);
trimesh->vertex[i].y = read_float(file);
trimesh->vertex[i].z = read_float(file);
}
return 1;
} else if(type == CHUNK_MAPLIST) { /* maplist */
trimesh->num_uvmap = read_ushort(file);
trimesh->uvmap = malloc(sizeof(uvmap_t) * trimesh->num_uvmap);
for(i = 0; i < trimesh->num_uvmap; i++) {
trimesh->uvmap[i].u = read_float(file);
trimesh->uvmap[i].v = 1.0 - read_float(file);
}
return 1;
} else if(type == CHUNK_FACELIST) { /* facelist */
int bytes_left;
trimesh->num_face = read_ushort(file);
trimesh->face = malloc(sizeof(int) * trimesh->num_face * 3);
for(i = 0; i < trimesh->num_face * 3; i += 3) {
trimesh->face[i + 0] = read_ushort(file);
trimesh->face[i + 1] = read_ushort(file);
trimesh->face[i + 2] = read_ushort(file);
read_ushort(file);
}
bytes_left = size - trimesh->num_face * sizeof(unsigned short) * 4 - 2;
if(bytes_left > 0)
read_chunks(file,bytes_left,process_smoolist,(void*)trimesh);
return 1;
}
return 0;
}
static int process_objblock(FILE *file,unsigned short type,
int size,void *data) {
mesh_t *mesh = (mesh_t*)data;
if(type == CHUNK_TRIMESH) {
mesh->num_trimesh++;
if(mesh->trimesh == NULL) mesh->trimesh = malloc(sizeof(trimesh_t));
else mesh->trimesh = realloc(mesh->trimesh,
sizeof(trimesh_t) * mesh->num_trimesh);
memset(&mesh->trimesh[mesh->num_trimesh - 1],0,sizeof(trimesh_t));
read_chunks(file,size,process_trimesh,
(void*)&mesh->trimesh[mesh->num_trimesh - 1]);
return 1;
}
return 0;
}
static int process_objmesh(FILE *file,unsigned short type,
int size,void *data) {
if(type == CHUNK_OBJBLOCK) {
size -= skeep_string(file);
read_chunks(file,size,process_objblock,data);
return 1;
}
return 0;
}
static int process_main(FILE *file,unsigned short type,
int size,void *data) {
if(type == CHUNK_OBJMESH) {
read_chunks(file,size,process_objmesh,data);
return 1;
}
return 0;
}
/*
*/
static void create_trimesh(trimesh_t *trimesh) {
int i;
uvmap_t *uvmap;
vector_t *vertex,*normal_face,*normal_vertex;
int *face,*vertex_count,**vertex_face,*smoothgroup;
/* calc normals */
normal_face = calloc(1,sizeof(vector_t) * trimesh->num_face);
normal_vertex = calloc(1,sizeof(vector_t) * trimesh->num_face * 3);
vertex_count = calloc(1,sizeof(int) * trimesh->num_face * 3);
vertex_face = calloc(1,sizeof(int*) * trimesh->num_face * 3);
trimesh->normal = normal_vertex;
vertex = trimesh->vertex;
face = trimesh->face;
smoothgroup = trimesh->smoothgroup;
for(i = 0; i < trimesh->num_face; i++) {
int j = i * 3;
vector_t a,b;
int v0 = face[j + 0];
int v1 = face[j + 1];
int v2 = face[j + 2];
vertex_count[v0]++;
vertex_count[v1]++;
vertex_count[v2]++;
vector_sub(&vertex[v1],&vertex[v0],&a);
vector_sub(&vertex[v2],&vertex[v0],&b);
vector_cross(&a,&b,&normal_face[i]);
vector_normalize(&normal_face[i],&normal_face[i]);
}
for(i = 0; i < trimesh->num_face * 3; i++) {
vertex_face[i] = malloc(sizeof(int) * (vertex_count[i] + 1));
vertex_face[i][0] = vertex_count[i];
}
for(i = 0; i < trimesh->num_face; i++) {
int j = i * 3;
int v0 = face[j + 0];
int v1 = face[j + 1];
int v2 = face[j + 2];
vertex_face[v0][vertex_count[v0]--] = i;
vertex_face[v1][vertex_count[v1]--] = i;
vertex_face[v2][vertex_count[v2]--] = i;
}
for(i = 0; i < trimesh->num_face; i++) {
int j = i * 3,k;
int v0 = face[j + 0];
int v1 = face[j + 1];
int v2 = face[j + 2];
for(k = 1; k <= vertex_face[v0][0]; k++) {
int l = vertex_face[v0][k];
if(i == l || (smoothgroup && smoothgroup[i] & smoothgroup[l]))
vector_add(&normal_vertex[j + 0],&normal_face[l],
&normal_vertex[j + 0]);
}
for(k = 1; k <= vertex_face[v1][0]; k++) {
int l = vertex_face[v1][k];
if(i == l || (smoothgroup && smoothgroup[i] & smoothgroup[l]))
vector_add(&normal_vertex[j + 1],&normal_face[l],
&normal_vertex[j + 1]);
}
for(k = 1; k <= vertex_face[v2][0]; k++) {
int l = vertex_face[v2][k];
if(i == l || (smoothgroup && smoothgroup[i] & smoothgroup[l]))
vector_add(&normal_vertex[j + 2],&normal_face[l],
&normal_vertex[j + 2]);
}
}
for(i = 0; i < trimesh->num_face * 3; i++) {
vector_normalize(&normal_vertex[i],&normal_vertex[i]);
free(vertex_face[i]);
}
free(normal_face);
free(vertex_count);
free(vertex_face);
/* create linear arrays for vertex and uvmap */
vertex = calloc(1,sizeof(vector_t) * trimesh->num_face * 3);
uvmap = calloc(1,sizeof(uvmap_t) * trimesh->num_face * 3);
for(i = 0; i < trimesh->num_face * 3; i += 3) {
vector_copy(&trimesh->vertex[trimesh->face[i + 0]],&vertex[i + 0]);
vector_copy(&trimesh->vertex[trimesh->face[i + 1]],&vertex[i + 1]);
vector_copy(&trimesh->vertex[trimesh->face[i + 2]],&vertex[i + 2]);
}
free(trimesh->vertex);
trimesh->vertex = vertex;
trimesh->num_vertex = trimesh->num_face * 3;
if(trimesh->uvmap) {
for(i = 0; i < trimesh->num_face * 3; i += 3) {
uvmap[i + 0].u = trimesh->uvmap[trimesh->face[i + 0]].u;
uvmap[i + 0].v = trimesh->uvmap[trimesh->face[i + 0]].v;
uvmap[i + 1].u = trimesh->uvmap[trimesh->face[i + 1]].u;
uvmap[i + 1].v = trimesh->uvmap[trimesh->face[i + 1]].v;
uvmap[i + 2].u = trimesh->uvmap[trimesh->face[i + 2]].u;
uvmap[i + 2].v = trimesh->uvmap[trimesh->face[i + 2]].v;
}
free(trimesh->uvmap);
}
trimesh->uvmap = uvmap;
trimesh->num_uvmap = trimesh->num_face * 3;
free(trimesh->face);
trimesh->face = NULL;
}
static float *create_mesh(mesh_t *mesh,int *num_vertex) {
int i,j;
float *vertex;
for(i = 0, *num_vertex = 0; i < mesh->num_trimesh; i++) {
create_trimesh(&mesh->trimesh[i]);
*num_vertex += mesh->trimesh[i].num_face;
}
*num_vertex *= 3;
vertex = malloc(sizeof(float) * *num_vertex * 8);
for(i = 0, j = 0; i < mesh->num_trimesh; i++) {
int k;
for(k = 0; k < mesh->trimesh[i].num_face * 3; k++, j += 8) {
vertex[j + 0] = mesh->trimesh[i].vertex[k].x;
vertex[j + 1] = mesh->trimesh[i].vertex[k].y;
vertex[j + 2] = mesh->trimesh[i].vertex[k].z;
vertex[j + 3] = mesh->trimesh[i].normal[k].x;
vertex[j + 4] = mesh->trimesh[i].normal[k].y;
vertex[j + 5] = mesh->trimesh[i].normal[k].z;
vertex[j + 6] = mesh->trimesh[i].uvmap[k].u;
vertex[j + 7] = mesh->trimesh[i].uvmap[k].v;
}
if(mesh->trimesh[i].vertex) free(mesh->trimesh[i].vertex);
if(mesh->trimesh[i].normal) free(mesh->trimesh[i].normal);
if(mesh->trimesh[i].uvmap) free(mesh->trimesh[i].uvmap);
if(mesh->trimesh[i].face) free(mesh->trimesh[i].face);
if(mesh->trimesh[i].smoothgroup) free(mesh->trimesh[i].smoothgroup);
}
free(mesh->trimesh);
return vertex;
}
/*
*/
float *load_3ds(char *name,int *num_vertex) {
FILE *file;
mesh_t *mesh;
float *vertex;
unsigned short chunk_type;
int chunk_size,content_size;
file = fopen(name,"rb");
if(!file) {
fprintf(stderr,"error open %s file\n",name);
return NULL;
}
chunk_type = read_ushort(file);
chunk_size = read_int(file);
content_size = chunk_size - 6;
if(chunk_type != CHUNK_MAIN) {
fprintf(stderr,"wrong main chunk\n");
fclose(file);
return NULL;
}
mesh = calloc(1,sizeof(mesh_t));
read_chunks(file,content_size,process_main,(void*)mesh);
fclose(file);
vertex = create_mesh(mesh,num_vertex);
free(mesh);
return vertex;
}