#ifndef TINOBJ_LOADER_C_H_
#define TINOBJ_LOADER_C_H_
typedef struct {
char *name;
float ambient[3];
float diffuse[3];
float specular[3];
float transmittance[3];
float emission[3];
float shininess;
float ior;
float dissolve;
int illum;
int pad0;
char *ambient_texname;
char *diffuse_texname;
char *specular_texname;
char *specular_highlight_texname;
char *bump_texname;
char *displacement_texname;
char *alpha_texname;
} tinyobj_material_t;
typedef struct {
char *name;
unsigned int face_offset;
unsigned int length;
} tinyobj_shape_t;
typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t;
typedef struct {
unsigned int num_vertices;
unsigned int num_normals;
unsigned int num_texcoords;
unsigned int num_faces;
unsigned int num_face_num_verts;
int pad0;
float *vertices;
float *normals;
float *texcoords;
tinyobj_vertex_index_t *faces;
int *face_num_verts;
int *material_ids;
} tinyobj_attrib_t;
#define TINYOBJ_FLAG_TRIANGULATE (1 << 0)
#define TINYOBJ_INVALID_INDEX (0x80000000)
#define TINYOBJ_SUCCESS (0)
#define TINYOBJ_ERROR_EMPTY (-1)
#define TINYOBJ_ERROR_INVALID_PARAMETER (-2)
#define TINYOBJ_ERROR_FILE_OPERATION (-3)
extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
unsigned int *num_shapes, tinyobj_material_t **materials,
unsigned int *num_materials, const char *buf, unsigned int len,
unsigned int flags);
extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
unsigned int *num_materials_out,
const char *filename);
extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib);
extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib);
extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes);
extern void tinyobj_materials_free(tinyobj_material_t *materials,
unsigned int num_materials);
#ifdef TINYOBJ_LOADER_C_IMPLEMENTATION
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#if defined(TINYOBJ_MALLOC) && defined(TINYOBJ_REALLOC) && defined(TINYOBJ_CALLOC) && defined(TINYOBJ_FREE)
#elif !defined(TINYOBJ_MALLOC) && !defined(TINYOBJ_REALLOC) && !defined(TINYOBJ_CALLOC) && !defined(TINYOBJ_FREE)
#else
#error "Must define all or none of TINYOBJ_MALLOC, TINYOBJ_REALLOC, TINYOBJ_CALLOC and TINYOBJ_FREE."
#endif
#ifndef TINYOBJ_MALLOC
#include <stdlib.h>
#define TINYOBJ_MALLOC malloc
#define TINYOBJ_REALLOC realloc
#define TINYOBJ_CALLOC calloc
#define TINYOBJ_FREE free
#endif
#define TINYOBJ_MAX_FACES_PER_F_LINE (16)
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
#define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10))
#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
static void skip_space(const char **token) {
while ((*token)[0] == ' ' || (*token)[0] == '\t') {
(*token)++;
}
}
static void skip_space_and_cr(const char **token) {
while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') {
(*token)++;
}
}
static int until_space(const char *token) {
const char *p = token;
while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') {
p++;
}
return (int)(p - token);
}
static unsigned int length_until_newline(const char *token, unsigned int n) {
unsigned int len = 0;
for (len = 0; len < n - 1; len++) {
if (token[len] == '\n') {
break;
}
if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) {
break;
}
}
return len;
}
static unsigned int length_until_line_feed(const char *token, unsigned int n) {
unsigned int len = 0;
for (len = 0; len < n; len++) {
if ((token[len] == '\n') || (token[len] == '\r')) {
break;
}
}
return len;
}
static int my_atoi(const char *c) {
int value = 0;
int sign = 1;
if (*c == '+' || *c == '-') {
if (*c == '-') sign = -1;
c++;
}
while (((*c) >= '0') && ((*c) <= '9')) {
value *= 10;
value += (int)(*c - '0');
c++;
}
return value * sign;
}
static int fixIndex(int idx, unsigned int n) {
if (idx > 0) return idx - 1;
if (idx == 0) return 0;
return (int)n + idx;
}
static tinyobj_vertex_index_t parseRawTriple(const char **token) {
tinyobj_vertex_index_t vi;
vi.v_idx = (int)(0x80000000);
vi.vn_idx = (int)(0x80000000);
vi.vt_idx = (int)(0x80000000);
vi.v_idx = my_atoi((*token));
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
}
if ((*token)[0] != '/') {
return vi;
}
(*token)++;
if ((*token)[0] == '/') {
(*token)++;
vi.vn_idx = my_atoi((*token));
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
}
return vi;
}
vi.vt_idx = my_atoi((*token));
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
}
if ((*token)[0] != '/') {
return vi;
}
(*token)++;
vi.vn_idx = my_atoi((*token));
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
}
return vi;
}
static int parseInt(const char **token) {
int i = 0;
skip_space(token);
i = my_atoi((*token));
(*token) += until_space((*token));
return i;
}
static int tryParseDouble(const char *s, const char *s_end, double *result) {
double mantissa = 0.0;
int exponent = 0;
char sign = '+';
char exp_sign = '+';
char const *curr = s;
int read = 0;
int end_not_reached = 0;
if (s >= s_end) {
return 0;
}
if (*curr == '+' || *curr == '-') {
sign = *curr;
curr++;
} else if (IS_DIGIT(*curr)) {
} else {
goto fail;
}
end_not_reached = (curr != s_end);
while (end_not_reached && IS_DIGIT(*curr)) {
mantissa *= 10;
mantissa += (int)(*curr - 0x30);
curr++;
read++;
end_not_reached = (curr != s_end);
}
if (read == 0) goto fail;
if (!end_not_reached) goto assemble;
if (*curr == '.') {
curr++;
read = 1;
end_not_reached = (curr != s_end);
while (end_not_reached && IS_DIGIT(*curr)) {
double frac_value = 1.0;
int f;
for (f = 0; f < read; f++) {
frac_value *= 0.1;
}
mantissa += (int)(*curr - 0x30) * frac_value;
read++;
curr++;
end_not_reached = (curr != s_end);
}
} else if (*curr == 'e' || *curr == 'E') {
} else {
goto assemble;
}
if (!end_not_reached) goto assemble;
if (*curr == 'e' || *curr == 'E') {
curr++;
end_not_reached = (curr != s_end);
if (end_not_reached && (*curr == '+' || *curr == '-')) {
exp_sign = *curr;
curr++;
} else if (IS_DIGIT(*curr)) {
} else {
goto fail;
}
read = 0;
end_not_reached = (curr != s_end);
while (end_not_reached && IS_DIGIT(*curr)) {
exponent *= 10;
exponent += (int)(*curr - 0x30);
curr++;
read++;
end_not_reached = (curr != s_end);
}
if (read == 0) goto fail;
}
assemble :
{
double a = 1.0;
double b = 1.0;
int i;
for (i = 0; i < exponent; i++) {
a = a * 5.0;
}
for (i = 0; i < exponent; i++) {
b = b * 2.0;
}
if (exp_sign == '-') {
a = 1.0 / a;
b = 1.0 / b;
}
*result =
(sign == '+' ? 1 : -1) * (mantissa * a * b);
}
return 1;
fail:
return 0;
}
static float parseFloat(const char **token) {
const char *end;
double val = 0.0;
float f = 0.0f;
skip_space(token);
end = (*token) + until_space((*token));
val = 0.0;
tryParseDouble((*token), end, &val);
f = (float)(val);
(*token) = end;
return f;
}
static void parseFloat2(float *x, float *y, const char **token) {
(*x) = parseFloat(token);
(*y) = parseFloat(token);
}
static void parseFloat3(float *x, float *y, float *z, const char **token) {
(*x) = parseFloat(token);
(*y) = parseFloat(token);
(*z) = parseFloat(token);
}
static unsigned int my_strnlen(const char *s, unsigned int n) {
const char *p = memchr(s, 0, n);
return p ? (unsigned int)(p - s) : n;
}
static char *my_strdup(const char *s, unsigned int max_length) {
char *d;
unsigned int len;
if (s == NULL) return NULL;
len = length_until_line_feed(s, max_length);
d = (char *)TINYOBJ_MALLOC(len + 1);
memcpy(d, s, (unsigned int)(len));
d[len] = '\0';
return d;
}
static char *my_strndup(const char *s, unsigned int len) {
char *d;
unsigned int slen;
if (s == NULL) return NULL;
if (len == 0) return NULL;
slen = my_strnlen(s, len);
d = (char *)TINYOBJ_MALLOC(slen + 1);
if (!d) {
return NULL;
}
memcpy(d, s, slen);
d[slen] = '\0';
return d;
}
char *dynamic_fgets(char **buf, unsigned int *size, FILE *file) {
char *offset;
char *ret;
unsigned int old_size;
if (!(ret = fgets(*buf, (int)*size, file))) {
return ret;
}
if (NULL != strchr(*buf, '\n')) {
return ret;
}
do {
old_size = *size;
*size *= 2;
*buf = (char*)TINYOBJ_REALLOC(*buf, *size);
offset = &((*buf)[old_size - 1]);
ret = fgets(offset, (int)(old_size + 1), file);
} while(ret && (NULL == strchr(*buf, '\n')));
return ret;
}
static void initMaterial(tinyobj_material_t *material) {
int i;
material->name = NULL;
material->ambient_texname = NULL;
material->diffuse_texname = NULL;
material->specular_texname = NULL;
material->specular_highlight_texname = NULL;
material->bump_texname = NULL;
material->displacement_texname = NULL;
material->alpha_texname = NULL;
for (i = 0; i < 3; i++) {
material->ambient[i] = 0.f;
material->diffuse[i] = 0.f;
material->specular[i] = 0.f;
material->transmittance[i] = 0.f;
material->emission[i] = 0.f;
}
material->illum = 0;
material->dissolve = 1.f;
material->shininess = 1.f;
material->ior = 1.f;
}
#define HASH_TABLE_ERROR 1
#define HASH_TABLE_SUCCESS 0
#define HASH_TABLE_DEFAULT_SIZE 10
typedef struct hash_table_entry_t
{
unsigned long hash;
int filled;
int pad0;
long value;
struct hash_table_entry_t* next;
} hash_table_entry_t;
typedef struct
{
unsigned long* hashes;
hash_table_entry_t* entries;
unsigned int capacity;
unsigned int n;
} hash_table_t;
static unsigned long hash_djb2(const unsigned char* str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + (unsigned long)(c);
}
return hash;
}
static void create_hash_table(unsigned int start_capacity, hash_table_t* hash_table)
{
if (start_capacity < 1)
start_capacity = HASH_TABLE_DEFAULT_SIZE;
hash_table->hashes = (unsigned long*) TINYOBJ_MALLOC(start_capacity * sizeof(unsigned long));
hash_table->entries = (hash_table_entry_t*) TINYOBJ_CALLOC(start_capacity, sizeof(hash_table_entry_t));
hash_table->capacity = start_capacity;
hash_table->n = 0;
}
static void destroy_hash_table(hash_table_t* hash_table)
{
TINYOBJ_FREE(hash_table->entries);
TINYOBJ_FREE(hash_table->hashes);
}
static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table)
{
unsigned int start_index = hash % hash_table->capacity;
unsigned int index = start_index;
hash_table_entry_t* start_entry = hash_table->entries + start_index;
unsigned int i;
hash_table_entry_t* entry;
for (i = 1; hash_table->entries[index].filled; i++)
{
if (i >= hash_table->capacity)
return HASH_TABLE_ERROR;
index = (start_index + (i * i)) % hash_table->capacity;
}
entry = hash_table->entries + index;
entry->hash = hash;
entry->filled = 1;
entry->value = value;
if (index != start_index) {
entry->next = start_entry->next;
start_entry->next = entry;
}
return HASH_TABLE_SUCCESS;
}
static int hash_table_insert(unsigned long hash, long value, hash_table_t* hash_table)
{
int ret = hash_table_insert_value(hash, value, hash_table);
if (ret == HASH_TABLE_SUCCESS)
{
hash_table->hashes[hash_table->n] = hash;
hash_table->n++;
}
return ret;
}
static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* hash_table)
{
hash_table_entry_t* entry = hash_table->entries + (hash % hash_table->capacity);
while (entry)
{
if (entry->hash == hash && entry->filled)
{
return entry;
}
entry = entry->next;
}
return NULL;
}
static void hash_table_maybe_grow(unsigned int new_n, hash_table_t* hash_table)
{
unsigned int new_capacity;
hash_table_t new_hash_table;
unsigned int i;
if (new_n <= hash_table->capacity) {
return;
}
new_capacity = 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n);
new_hash_table.hashes = hash_table->hashes = (unsigned long*) TINYOBJ_REALLOC((void*) hash_table->hashes, sizeof(unsigned long) * new_capacity);
new_hash_table.entries = (hash_table_entry_t*) TINYOBJ_CALLOC(new_capacity, sizeof(hash_table_entry_t));
new_hash_table.capacity = new_capacity;
new_hash_table.n = hash_table->n;
for (i = 0; i < hash_table->capacity; i++)
{
hash_table_entry_t* entry = hash_table_find(hash_table->hashes[i], hash_table);
hash_table_insert_value(hash_table->hashes[i], entry->value, &new_hash_table);
}
TINYOBJ_FREE(hash_table->entries);
(*hash_table) = new_hash_table;
}
static int hash_table_exists(const char* name, hash_table_t* hash_table)
{
return hash_table_find(hash_djb2((const unsigned char*)name), hash_table) != NULL;
}
static void hash_table_set(const char* name, unsigned int val, hash_table_t* hash_table)
{
unsigned long hash = hash_djb2((const unsigned char *)name);
hash_table_entry_t* entry = hash_table_find(hash, hash_table);
if (entry)
{
entry->value = (long)val;
return;
}
do
{
hash_table_maybe_grow(hash_table->n + 1, hash_table);
}
while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS);
}
static long hash_table_get(const char* name, hash_table_t* hash_table)
{
hash_table_entry_t* ret = hash_table_find(hash_djb2((const unsigned char*)(name)), hash_table);
return ret->value;
}
static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev,
unsigned int num_materials,
tinyobj_material_t *new_mat) {
tinyobj_material_t *dst;
dst = (tinyobj_material_t *)TINYOBJ_REALLOC(
prev, sizeof(tinyobj_material_t) * (num_materials + 1));
dst[num_materials] = (*new_mat);
return dst;
}
static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out,
unsigned int *num_materials_out,
const char *filename,
hash_table_t* material_table) {
tinyobj_material_t material;
unsigned int buffer_size = 128;
char *linebuf;
FILE *fp;
unsigned int num_materials = 0;
tinyobj_material_t *materials = NULL;
int has_previous_material = 0;
const char *line_end = NULL;
if (materials_out == NULL) {
return TINYOBJ_ERROR_INVALID_PARAMETER;
}
if (num_materials_out == NULL) {
return TINYOBJ_ERROR_INVALID_PARAMETER;
}
(*materials_out) = NULL;
(*num_materials_out) = 0;
fp = fopen(filename, "rt");
if (!fp) {
fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno);
return TINYOBJ_ERROR_FILE_OPERATION;
}
initMaterial(&material);
linebuf = (char*)TINYOBJ_MALLOC(buffer_size);
while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) {
const char *token = linebuf;
line_end = token + strlen(token);
token += strspn(token, " \t");
assert(token);
if (token[0] == '\0') continue;
if (token[0] == '#') continue;
if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
char namebuf[4096];
if (has_previous_material) {
materials = tinyobj_material_add(materials, num_materials, &material);
num_materials++;
} else {
has_previous_material = 1;
}
initMaterial(&material);
token += 7;
#ifdef _MSC_VER
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
#else
sscanf(token, "%s", namebuf);
#endif
material.name = my_strdup(namebuf, (unsigned int) (line_end - token));
if (material_table)
hash_table_set(material.name, num_materials, material_table);
continue;
}
if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) {
float r, g, b;
token += 2;
parseFloat3(&r, &g, &b, &token);
material.ambient[0] = r;
material.ambient[1] = g;
material.ambient[2] = b;
continue;
}
if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) {
float r, g, b;
token += 2;
parseFloat3(&r, &g, &b, &token);
material.diffuse[0] = r;
material.diffuse[1] = g;
material.diffuse[2] = b;
continue;
}
if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) {
float r, g, b;
token += 2;
parseFloat3(&r, &g, &b, &token);
material.specular[0] = r;
material.specular[1] = g;
material.specular[2] = b;
continue;
}
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
float r, g, b;
token += 2;
parseFloat3(&r, &g, &b, &token);
material.transmittance[0] = r;
material.transmittance[1] = g;
material.transmittance[2] = b;
continue;
}
if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) {
token += 2;
material.ior = parseFloat(&token);
continue;
}
if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) {
float r, g, b;
token += 2;
parseFloat3(&r, &g, &b, &token);
material.emission[0] = r;
material.emission[1] = g;
material.emission[2] = b;
continue;
}
if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) {
token += 2;
material.shininess = parseFloat(&token);
continue;
}
if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) {
token += 6;
material.illum = parseInt(&token);
continue;
}
if ((token[0] == 'd' && IS_SPACE(token[1]))) {
token += 1;
material.dissolve = parseFloat(&token);
continue;
}
if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2;
material.dissolve = 1.0f - parseFloat(&token);
continue;
}
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7;
material.ambient_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
token += 7;
material.diffuse_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
token += 7;
material.specular_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
token += 7;
material.specular_highlight_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
token += 9;
material.bump_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) {
token += 6;
material.alpha_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
token += 5;
material.bump_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
token += 5;
material.displacement_texname = my_strdup(token, (unsigned int) (line_end - token));
continue;
}
}
fclose(fp);
if (material.name) {
materials = tinyobj_material_add(materials, num_materials, &material);
num_materials++;
}
(*num_materials_out) = num_materials;
(*materials_out) = materials;
if (linebuf) {
TINYOBJ_FREE(linebuf);
}
return TINYOBJ_SUCCESS;
}
int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
unsigned int *num_materials_out,
const char *filename) {
return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL);
}
typedef enum {
COMMAND_EMPTY,
COMMAND_V,
COMMAND_VN,
COMMAND_VT,
COMMAND_F,
COMMAND_G,
COMMAND_O,
COMMAND_USEMTL,
COMMAND_MTLLIB
} CommandType;
typedef struct {
float vx, vy, vz;
float nx, ny, nz;
float tx, ty;
tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
unsigned int num_f;
int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE];
unsigned int num_f_num_verts;
const char *group_name;
unsigned int group_name_len;
int pad0;
const char *object_name;
unsigned int object_name_len;
int pad1;
const char *material_name;
unsigned int material_name_len;
int pad2;
const char *mtllib_name;
unsigned int mtllib_name_len;
CommandType type;
} Command;
static int parseLine(Command *command, const char *p, unsigned int p_len,
int triangulate) {
char linebuf[4096];
const char *token;
assert(p_len < 4095);
memcpy(linebuf, p, p_len);
linebuf[p_len] = '\0';
token = linebuf;
command->type = COMMAND_EMPTY;
skip_space(&token);
assert(token);
if (token[0] == '\0') {
return 0;
}
if (token[0] == '#') {
return 0;
}
if (token[0] == 'v' && IS_SPACE((token[1]))) {
float x, y, z;
token += 2;
parseFloat3(&x, &y, &z, &token);
command->vx = x;
command->vy = y;
command->vz = z;
command->type = COMMAND_V;
return 1;
}
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
float x, y, z;
token += 3;
parseFloat3(&x, &y, &z, &token);
command->nx = x;
command->ny = y;
command->nz = z;
command->type = COMMAND_VN;
return 1;
}
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
float x, y;
token += 3;
parseFloat2(&x, &y, &token);
command->tx = x;
command->ty = y;
command->type = COMMAND_VT;
return 1;
}
if (token[0] == 'f' && IS_SPACE((token[1]))) {
unsigned int num_f = 0;
tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
token += 2;
skip_space(&token);
while (!IS_NEW_LINE(token[0])) {
tinyobj_vertex_index_t vi = parseRawTriple(&token);
skip_space_and_cr(&token);
f[num_f] = vi;
num_f++;
}
command->type = COMMAND_F;
if (triangulate) {
unsigned int k;
unsigned int n = 0;
tinyobj_vertex_index_t i0 = f[0];
tinyobj_vertex_index_t i1;
tinyobj_vertex_index_t i2 = f[1];
assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
for (k = 2; k < num_f; k++) {
i1 = i2;
i2 = f[k];
command->f[3 * n + 0] = i0;
command->f[3 * n + 1] = i1;
command->f[3 * n + 2] = i2;
command->f_num_verts[n] = 3;
n++;
}
command->num_f = 3 * n;
command->num_f_num_verts = n;
} else {
unsigned int k = 0;
assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
for (k = 0; k < num_f; k++) {
command->f[k] = f[k];
}
command->num_f = num_f;
command->f_num_verts[0] = (int)num_f;
command->num_f_num_verts = 1;
}
return 1;
}
if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
token += 7;
skip_space(&token);
command->material_name = p + (token - linebuf);
command->material_name_len = (unsigned int)length_until_newline(
token, (p_len - (unsigned int)(token - linebuf)) + 1);
command->type = COMMAND_USEMTL;
return 1;
}
if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
token += 7;
skip_space(&token);
command->mtllib_name = p + (token - linebuf);
command->mtllib_name_len = (unsigned int)length_until_newline(
token, p_len - (unsigned int)(token - linebuf)) +
1;
command->type = COMMAND_MTLLIB;
return 1;
}
if (token[0] == 'g' && IS_SPACE((token[1]))) {
token += 2;
command->group_name = p + (token - linebuf);
command->group_name_len = (unsigned int)length_until_newline(
token, p_len - (unsigned int)(token - linebuf)) +
1;
command->type = COMMAND_G;
return 1;
}
if (token[0] == 'o' && IS_SPACE((token[1]))) {
token += 2;
command->object_name = p + (token - linebuf);
command->object_name_len = (unsigned int)length_until_newline(
token, p_len - (unsigned int)(token - linebuf)) +
1;
command->type = COMMAND_O;
return 1;
}
return 0;
}
typedef struct {
unsigned int pos;
unsigned int len;
} LineInfo;
static int is_line_ending(const char *p, unsigned int i, unsigned int end_i) {
if (p[i] == '\0') return 1;
if (p[i] == '\n') return 1;
if (p[i] == '\r') {
if (((i + 1) < end_i) && (p[i + 1] != '\n')) {
return 1;
}
}
return 0;
}
int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
unsigned int *num_shapes, tinyobj_material_t **materials_out,
unsigned int *num_materials_out, const char *buf, unsigned int len,
unsigned int flags) {
LineInfo *line_infos = NULL;
Command *commands = NULL;
unsigned int num_lines = 0;
unsigned int num_v = 0;
unsigned int num_vn = 0;
unsigned int num_vt = 0;
unsigned int num_f = 0;
unsigned int num_faces = 0;
int mtllib_line_index = -1;
tinyobj_material_t *materials = NULL;
unsigned int num_materials = 0;
hash_table_t material_table;
if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER;
if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
tinyobj_attrib_init(attrib);
{
unsigned int i;
unsigned int end_idx = len;
unsigned int prev_pos = 0;
unsigned int line_no = 0;
unsigned int last_line_ending = 0;
for (i = 0; i < end_idx; i++) {
if (is_line_ending(buf, i, end_idx)) {
num_lines++;
last_line_ending = i;
}
}
if (end_idx - last_line_ending > 0) {
num_lines++;
}
if (num_lines == 0) return TINYOBJ_ERROR_EMPTY;
line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines);
for (i = 0; i < end_idx; i++) {
if (is_line_ending(buf, i, end_idx)) {
line_infos[line_no].pos = prev_pos;
line_infos[line_no].len = i - prev_pos;
if ( i > 0 && buf[i-1] == '\r' ) line_infos[line_no].len--;
prev_pos = i + 1;
line_no++;
}
}
if (end_idx - last_line_ending > 0) {
line_infos[line_no].pos = prev_pos;
line_infos[line_no].len = end_idx - 1 - last_line_ending;
}
}
commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines);
create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table);
{
unsigned int i = 0;
for (i = 0; i < num_lines; i++) {
int ret = parseLine(&commands[i], &buf[line_infos[i].pos],
line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE);
if (ret) {
if (commands[i].type == COMMAND_V) {
num_v++;
} else if (commands[i].type == COMMAND_VN) {
num_vn++;
} else if (commands[i].type == COMMAND_VT) {
num_vt++;
} else if (commands[i].type == COMMAND_F) {
num_f += commands[i].num_f;
num_faces += commands[i].num_f_num_verts;
}
if (commands[i].type == COMMAND_MTLLIB) {
mtllib_line_index = (int)i;
}
}
}
}
if (line_infos) {
TINYOBJ_FREE(line_infos);
}
if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name &&
commands[mtllib_line_index].mtllib_name_len > 0) {
char *filename = my_strndup(commands[mtllib_line_index].mtllib_name,
commands[mtllib_line_index].mtllib_name_len);
int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table);
if (ret != TINYOBJ_SUCCESS) {
fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret);
}
TINYOBJ_FREE(filename);
}
{
unsigned int v_count = 0;
unsigned int n_count = 0;
unsigned int t_count = 0;
unsigned int f_count = 0;
unsigned int face_count = 0;
int material_id = -1;
unsigned int i = 0;
attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3);
attrib->num_vertices = (unsigned int)num_v;
attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3);
attrib->num_normals = (unsigned int)num_vn;
attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2);
attrib->num_texcoords = (unsigned int)num_vt;
attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * num_f);
attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
attrib->num_faces = (unsigned int)num_faces;
attrib->num_face_num_verts = (unsigned int)num_f;
attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
for (i = 0; i < num_lines; i++) {
if (commands[i].type == COMMAND_EMPTY) {
continue;
} else if (commands[i].type == COMMAND_USEMTL) {
if (commands[i].material_name &&
commands[i].material_name_len >0)
{
char* material_name_null_term = (char*) TINYOBJ_MALLOC(commands[i].material_name_len + 1);
memcpy((void*) material_name_null_term, (const void*) commands[i].material_name, commands[i].material_name_len);
material_name_null_term[commands[i].material_name_len] = 0;
if (hash_table_exists(material_name_null_term, &material_table))
material_id = (int)hash_table_get(material_name_null_term, &material_table);
else
material_id = -1;
TINYOBJ_FREE(material_name_null_term);
}
} else if (commands[i].type == COMMAND_V) {
attrib->vertices[3 * v_count + 0] = commands[i].vx;
attrib->vertices[3 * v_count + 1] = commands[i].vy;
attrib->vertices[3 * v_count + 2] = commands[i].vz;
v_count++;
} else if (commands[i].type == COMMAND_VN) {
attrib->normals[3 * n_count + 0] = commands[i].nx;
attrib->normals[3 * n_count + 1] = commands[i].ny;
attrib->normals[3 * n_count + 2] = commands[i].nz;
n_count++;
} else if (commands[i].type == COMMAND_VT) {
attrib->texcoords[2 * t_count + 0] = commands[i].tx;
attrib->texcoords[2 * t_count + 1] = commands[i].ty;
t_count++;
} else if (commands[i].type == COMMAND_F) {
unsigned int k = 0;
for (k = 0; k < commands[i].num_f; k++) {
tinyobj_vertex_index_t vi = commands[i].f[k];
int v_idx = fixIndex(vi.v_idx, v_count);
int vn_idx = fixIndex(vi.vn_idx, n_count);
int vt_idx = fixIndex(vi.vt_idx, t_count);
attrib->faces[f_count + k].v_idx = v_idx;
attrib->faces[f_count + k].vn_idx = vn_idx;
attrib->faces[f_count + k].vt_idx = vt_idx;
}
for (k = 0; k < commands[i].num_f_num_verts; k++) {
attrib->material_ids[face_count + k] = material_id;
attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k];
}
f_count += commands[i].num_f;
face_count += commands[i].num_f_num_verts;
}
}
}
{
unsigned int face_count = 0;
unsigned int i = 0;
unsigned int n = 0;
unsigned int shape_idx = 0;
const char *shape_name = NULL;
unsigned int shape_name_len = 0;
const char *prev_shape_name = NULL;
unsigned int prev_shape_name_len = 0;
unsigned int prev_shape_face_offset = 0;
unsigned int prev_face_offset = 0;
tinyobj_shape_t prev_shape = {NULL, 0, 0};
for (i = 0; i < num_lines; i++) {
if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
n++;
}
}
(*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1));
for (i = 0; i < num_lines; i++) {
if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
if (commands[i].type == COMMAND_O) {
shape_name = commands[i].object_name;
shape_name_len = commands[i].object_name_len;
} else {
shape_name = commands[i].group_name;
shape_name_len = commands[i].group_name_len;
}
if (face_count == 0) {
prev_shape_name = shape_name;
prev_shape_name_len = shape_name_len;
prev_shape_face_offset = face_count;
prev_face_offset = face_count;
} else {
if (shape_idx == 0) {
(*shapes)[shape_idx].name = my_strndup(
prev_shape_name, prev_shape_name_len);
(*shapes)[shape_idx].face_offset = prev_shape.face_offset;
(*shapes)[shape_idx].length = face_count - prev_face_offset;
shape_idx++;
prev_face_offset = face_count;
} else {
if ((face_count - prev_face_offset) > 0) {
(*shapes)[shape_idx].name =
my_strndup(prev_shape_name, prev_shape_name_len);
(*shapes)[shape_idx].face_offset = prev_face_offset;
(*shapes)[shape_idx].length = face_count - prev_face_offset;
shape_idx++;
prev_face_offset = face_count;
}
}
prev_shape_name = shape_name;
prev_shape_name_len = shape_name_len;
prev_shape_face_offset = face_count;
}
}
if (commands[i].type == COMMAND_F) {
face_count++;
}
}
if ((face_count - prev_face_offset) > 0) {
unsigned int length = face_count - prev_shape_face_offset;
if (length > 0) {
(*shapes)[shape_idx].name =
my_strndup(prev_shape_name, prev_shape_name_len);
(*shapes)[shape_idx].face_offset = prev_face_offset;
(*shapes)[shape_idx].length = face_count - prev_face_offset;
shape_idx++;
}
} else {
}
(*num_shapes) = shape_idx;
}
if (commands) {
TINYOBJ_FREE(commands);
}
destroy_hash_table(&material_table);
(*materials_out) = materials;
(*num_materials_out) = num_materials;
return TINYOBJ_SUCCESS;
}
void tinyobj_attrib_init(tinyobj_attrib_t *attrib) {
attrib->vertices = NULL;
attrib->num_vertices = 0;
attrib->normals = NULL;
attrib->num_normals = 0;
attrib->texcoords = NULL;
attrib->num_texcoords = 0;
attrib->faces = NULL;
attrib->num_faces = 0;
attrib->face_num_verts = NULL;
attrib->num_face_num_verts = 0;
attrib->material_ids = NULL;
}
void tinyobj_attrib_free(tinyobj_attrib_t *attrib) {
if (attrib->vertices) TINYOBJ_FREE(attrib->vertices);
if (attrib->normals) TINYOBJ_FREE(attrib->normals);
if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords);
if (attrib->faces) TINYOBJ_FREE(attrib->faces);
if (attrib->face_num_verts) TINYOBJ_FREE(attrib->face_num_verts);
if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids);
}
void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes) {
unsigned int i;
if (shapes == NULL) return;
for (i = 0; i < num_shapes; i++) {
if (shapes[i].name) TINYOBJ_FREE(shapes[i].name);
}
TINYOBJ_FREE(shapes);
}
void tinyobj_materials_free(tinyobj_material_t *materials,
unsigned int num_materials) {
unsigned int i;
if (materials == NULL) return;
for (i = 0; i < num_materials; i++) {
if (materials[i].name) TINYOBJ_FREE(materials[i].name);
if (materials[i].ambient_texname) TINYOBJ_FREE(materials[i].ambient_texname);
if (materials[i].diffuse_texname) TINYOBJ_FREE(materials[i].diffuse_texname);
if (materials[i].specular_texname) TINYOBJ_FREE(materials[i].specular_texname);
if (materials[i].specular_highlight_texname)
TINYOBJ_FREE(materials[i].specular_highlight_texname);
if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname);
if (materials[i].displacement_texname)
TINYOBJ_FREE(materials[i].displacement_texname);
if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname);
}
TINYOBJ_FREE(materials);
}
#endif
#endif