Skip to content

Instantly share code, notes, and snippets.

@kdrnic
Created October 29, 2019 23:09
Show Gist options
  • Save kdrnic/b3d55d1883798ca37261a368928b8bef to your computer and use it in GitHub Desktop.
Save kdrnic/b3d55d1883798ca37261a368928b8bef to your computer and use it in GitHub Desktop.
#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