Created
October 29, 2019 23:09
-
-
Save kdrnic/b3d55d1883798ca37261a368928b8bef to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <allegro.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <ctype.h> | |
#include "mesh.h" | |
#include "obj.h" | |
#include "compat.h" | |
#include "vector.h" | |
#include "crc32.h" | |
#include "hashtbl.h" | |
#define MESH_MAX_FACE_VERTS 30 | |
struct MATRIX_f; | |
struct BITMAP; | |
typedef unsigned int mesh_face[MESH_MAX_FACE_VERTS]; | |
typedef char mesh_texture[256]; | |
typedef struct dedup_verts_data | |
{ | |
mesh_face *uv_faces, *pos_faces; | |
int num_uv, num_pos; | |
int num_faces; | |
int *face_numverts; | |
struct rvert *uv_verts, *pos_verts; | |
} dedup_verts_data; | |
struct MATRIX_f; | |
struct hashtable; | |
typedef struct mesh | |
{ | |
char (*sub_names)[256]; | |
int (*sub_faces)[2]; | |
struct MATRIX_f *sub_matrices; | |
struct rvert *verts; | |
mesh_texture *textures; | |
mesh_texture *materials; | |
int *texids; | |
int num_verts, num_faces, num_textures, num_subs; | |
mesh_face *faces; | |
int *face_numverts; | |
struct hashtable *sub_props; | |
unsigned int crc32; | |
} mesh; | |
mesh *load_obj(const char *fn) | |
{ | |
char lin[1024] = {0}; | |
char cmd[1024] = {0}; | |
FILE *obj = fopen(fn, "r"), *mtl; | |
int i; | |
int pass; | |
mesh *m; | |
dedup_verts_data dup; | |
char mtlname[256]; | |
//TODO: dynamic alloc | |
int mtlids[9999] = {0}, n_mtlids = 0; | |
unsigned int crc_state[CRC32_STATE_LEN] = CRC32_STATE0; | |
if(!obj) return 0; | |
m = calloc(1, sizeof(*m)); | |
#define CASE_TOK_BEGIN if(0){} | |
#define CASE_TOK(s) else if(!strcmp(cmd, (s))) | |
for(pass = 0; pass < 2; pass++, fseek(obj, 0, SEEK_SET)){ | |
if(pass){ | |
dup.uv_verts = malloc(sizeof(*dup.uv_verts) * dup.num_uv); | |
dup.pos_verts = malloc(sizeof(*dup.pos_verts) * dup.num_pos); | |
dup.uv_faces = malloc(sizeof(*dup.uv_faces) * dup.num_faces); | |
dup.pos_faces = malloc(sizeof(*dup.pos_faces) * dup.num_faces); | |
dup.face_numverts = malloc(sizeof(*m->face_numverts) * dup.num_faces); | |
m->faces = malloc(sizeof(*m->faces) * dup.num_faces); | |
m->face_numverts = dup.face_numverts; | |
m->num_faces = dup.num_faces; | |
m->textures = malloc(sizeof(*m->textures) * m->num_textures); | |
m->materials = malloc(sizeof(*m->materials) * m->num_textures); | |
m->texids = malloc(sizeof(*m->texids) * m->num_textures); | |
if(m->num_subs){ | |
m->sub_faces = calloc(m->num_subs, sizeof(*m->sub_faces)); | |
m->sub_names = calloc(m->num_subs, sizeof(*m->sub_names)); | |
m->sub_matrices = calloc(m->num_subs, sizeof(*m->sub_matrices)); | |
m->sub_props = calloc(m->num_subs, sizeof(*m->sub_props)); | |
} | |
else{ | |
m->sub_faces = 0; | |
m->sub_names = 0; | |
m->sub_matrices = 0; | |
m->sub_props = 0; | |
} | |
} | |
dup.num_pos = 0; | |
dup.num_uv = 0; | |
dup.num_faces = 0; | |
m->num_textures = 0; | |
m->num_subs = 0; | |
while(fgets(lin, sizeof(lin), obj)){ | |
if(!sscanf(lin, "%s", cmd)) continue; | |
if(pass) m->crc32 = crc32_string(lin, crc_state); | |
CASE_TOK_BEGIN | |
CASE_TOK("v"){ | |
if(pass){ | |
float x, y, z; | |
sscanf( | |
lin, | |
"%*s %f %f %f", | |
&x, | |
&y, | |
&z | |
); | |
dup.pos_verts[dup.num_pos].pos.x = x; | |
dup.pos_verts[dup.num_pos].pos.y = y; | |
dup.pos_verts[dup.num_pos].pos.z = z; | |
} | |
dup.num_pos++; | |
} | |
CASE_TOK("vt"){ | |
if(pass){ | |
sscanf( | |
lin, | |
"%*s %f %f", | |
&dup.uv_verts[dup.num_uv].u, | |
&dup.uv_verts[dup.num_uv].v | |
); | |
} | |
dup.num_uv++; | |
} | |
CASE_TOK("vn"){ | |
//Vertex normals - currently unused. | |
} | |
CASE_TOK("f"){ | |
if(pass){ | |
//TODO: use strtol instead? | |
char *tok; | |
for(tok = strtok(lin, " \n"), i = -1; tok; tok = strtok(0, " \n"), i++){ | |
if(tok == lin) continue; | |
*strchr(tok, '/') = 0; | |
dup.pos_faces[dup.num_faces][i] = atoi(tok) - 1; | |
tok[strlen(tok)] = '/'; | |
dup.uv_faces[dup.num_faces][i] = atoi(strchr(tok, '/') + 1) - 1; | |
} | |
if(m->sub_faces && m->num_subs) m->sub_faces[m->num_subs - 1][1] = dup.num_faces; | |
m->face_numverts[dup.num_faces] = i; | |
} | |
dup.num_faces++; | |
} | |
CASE_TOK("matrix"){ | |
if(pass){ | |
MATRIX_f mat; | |
({ | |
const float a = -1, b = 0, c = 0, d = 2, e = 0, f = 0, g = 1, h = 0, i = 0, j = 1, k = 0, l = 2, m = 0, n = 0, o = 0, p = 1; | |
const MATRIX_f conv = { | |
.v = { | |
{a,b,c}, | |
{e,f,g}, | |
{i,j,k} | |
}, | |
.t = {d,h,l} | |
}; | |
assert(m == 0); | |
assert(n == 0); | |
assert(o == 0); | |
assert(p == 1); | |
sscanf( | |
lin, | |
"%*s %f %f %f %f %f %f %f %f %f %f %f %f", | |
&mat.v[0][0], | |
&mat.v[0][1], | |
&mat.v[0][2], | |
&mat.t[0], | |
&mat.v[1][0], | |
&mat.v[1][1], | |
&mat.v[1][2], | |
&mat.t[1], | |
&mat.v[2][0], | |
&mat.v[2][1], | |
&mat.v[2][2], | |
&mat.t[2] | |
); | |
//matrix_mul_f(&mat, &conv, &mat); | |
}); | |
if(m->sub_matrices && m->num_subs) m->sub_matrices[m->num_subs - 1] = mat; | |
} | |
} | |
CASE_TOK("prop"){ | |
if(pass){ | |
int n1; | |
char propname[256], *val, *c; | |
sscanf(lin, "%*s %255s %n", propname, &n1); | |
c = lin + n1; while(isspace(*c)) c++; | |
val = strdup(lin + n1); | |
for(c = val + strlen(val) - 1; isspace(*c) && c >= val; c--) *c = '\0'; | |
if(m->sub_props && m->num_subs){ | |
if(!m->sub_props[m->num_subs - 1].siz){ | |
ht_init(m->sub_props + m->num_subs - 1, 0); | |
} | |
ht_insert(m->sub_props + m->num_subs - 1, propname, val); | |
} | |
} | |
} | |
CASE_TOK("g"){ | |
//Grouping command - currently unused. | |
} | |
CASE_TOK("usemtl"){ | |
if(pass){ | |
sscanf(lin, "%*s %s", cmd); | |
strcpy(m->materials[m->num_textures], cmd); | |
m->texids[m->num_textures] = dup.num_faces; | |
} | |
m->num_textures++; | |
} | |
CASE_TOK("s"){ | |
//Smoothing group command - currently unused | |
} | |
CASE_TOK("mtllib"){ | |
if(pass){ | |
sscanf(lin, "%*s %s", cmd); | |
replace_filename(mtlname, AL_FILENAME_CAST(fn), cmd, sizeof(mtlname)); | |
} | |
} | |
CASE_TOK("o"){ | |
if(pass){ | |
sscanf(lin, "%*s %s", cmd); | |
strcpy(m->sub_names[m->num_subs], cmd); | |
m->sub_faces[m->num_subs][0] = dup.num_faces; | |
m->sub_faces[m->num_subs][1] = dup.num_faces; | |
} | |
m->num_subs++; | |
} | |
} | |
} | |
m->num_verts = dedup_verts(dup, m->faces, &m->verts); | |
mtl = fopen(mtlname, "r"); | |
if(!mtl){ | |
goto aftermtl; | |
} | |
while(fgets(lin, 1024, mtl)){ | |
if(!sscanf(lin, "%s", cmd)) continue; | |
m->crc32 = crc32_string(lin, crc_state); | |
CASE_TOK_BEGIN | |
CASE_TOK("newmtl"){ | |
sscanf(lin, "%*s %s", cmd); | |
n_mtlids = 0; | |
for(i = 0; i < m->num_textures; i++){ | |
if(!strcmp(m->materials[i], cmd)){ | |
mtlids[n_mtlids++] = i; | |
} | |
} | |
} | |
CASE_TOK("map_Kd"){ | |
sscanf(lin, "%*s %s", cmd); | |
for(i = 0; i < n_mtlids; i++){ | |
strcpy(m->textures[mtlids[i]], cmd); | |
} | |
} | |
} | |
#undef CASE_TOK | |
#undef CASE_TOK_BEGIN | |
aftermtl: | |
//According to NeiloGD origin is bottom left as a proper carthesian plane | |
for(i = 0; i < m->num_verts; i++){ | |
m->verts[i].v *= -1; | |
m->verts[i].v += 1.0; | |
} | |
fclose(obj); | |
fclose(mtl); | |
return m; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment