commit f9f0af5ede06568db14492f4ba4ca71da3b18765
parent 2c70a8e7a998f0c036ed2abe2c67e868537cb424
Author: vaplv <vaplv@free.fr>
Date: Sat, 28 Jun 2014 16:07:20 +0200
Begin the implementation of the obj file parsing
Diffstat:
| M | src/obj.c | | | 414 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 413 insertions(+), 1 deletion(-)
diff --git a/src/obj.c b/src/obj.c
@@ -1,8 +1,113 @@
+#define _POSIX_C_SOURCE 200112L /* strtok_r support */
+
#include "obj.h"
+
+#include <rsys/dynamic_array_float.h>
#include <rsys/mem_allocator.h>
#include <rsys/ref_count.h>
+#include <rsys/str.h>
+
+struct vertex {
+ size_t iposition;
+ size_t inormal;
+ size_t itexcoord;
+};
+
+struct face {
+ size_t ivertex; /* Index toward the first vertex */
+ size_t nvertices; /* Vertex count */
+};
+
+struct named_group {
+ struct str name;
+ size_t iface; /* Index toward the first face */
+ size_t nfaces; /* Faces count in the group */
+};
+
+struct smooth_group {
+ char is_enabled;
+ size_t iface; /* Index toward the first face */
+ size_t nfaces; /* Faces count in the group */
+};
+
+static INLINE void
+named_group_init(struct mem_allocator* allocator, struct named_group* grp)
+{
+ ASSERT(grp);
+ str_init(allocator, &grp->name);
+ grp->iface = 0;
+ grp->nfaces = 0;
+}
+
+static INLINE void
+named_group_release(struct named_group* grp)
+{
+ ASSERT(grp);
+ str_release(&grp->name);
+}
+
+static INLINE int
+named_group_copy(struct named_group* dst, const struct named_group* src)
+{
+ ASSERT(dst && src);
+ dst->iface = src->iface;
+ dst->nfaces = src->nfaces;
+ return str_copy(&dst->name, &src->name);
+}
+
+static INLINE int
+named_group_copy_and_release(struct named_group* dst, struct named_group* src)
+{
+ ASSERT(dst && src);
+ dst->iface = src->iface;
+ dst->nfaces = src->nfaces;
+ return str_copy_and_release(&dst->name, &src->name);
+}
+
+/* Generate the darray_vertex data structure */
+#define DARRAY_NAME vertex
+#define DARRAY_DATA struct vertex
+#include <rsys/dynamic_array.h>
+
+/* Generate the darray_face data structure */
+#define DARRAY_NAME face
+#define DARRAY_DATA struct face
+#include <rsys/dynamic_array.h>
+
+/* Generate the darray_named_group data structure */
+#define DARRAY_NAME named_group
+#define DARRAY_DATA struct named_group
+#define DARRAY_FUNCTOR_INIT named_group_init
+#define DARRAY_FUNCTOR_RELEASE named_group_release
+#define DARRAY_FUNCTOR_COPY named_group_copy
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE named_group_copy_and_release
+#include <rsys/dynamic_array.h>
+
+/* Generate the darray_smooth_group data structure */
+#define DARRAY_NAME smooth_group
+#define DARRAY_DATA struct smooth_group
+#include <rsys/dynamic_array.h>
+
+/* Generate the darray_mtllib data structure */
+#define DARRAY_NAME mtllib
+#define DARRAY_DATA struct str
+#define DARRAY_FUNCTOR_INIT str_init
+#define DARRAY_FUNCTOR_RELEASE str_release
+#define DARRAY_FUNCTOR_COPY str_copy
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release
+#include <rsys/dynamic_array.h>
struct obj {
+ struct darray_float positions; /* float3 */
+ struct darray_float normals; /* float3 */
+ struct darray_float texcoords; /* float2 */
+ struct darray_vertex vertices;
+ struct darray_face faces;
+ struct darray_named_group groups;
+ struct darray_smooth_group smooth_groups;
+ struct darray_named_group usemtls;
+ struct darray_mtllib mtllibs;
+
ref_T ref;
struct mem_allocator* allocator;
};
@@ -10,11 +115,307 @@ struct obj {
/*******************************************************************************
* Helper functions
******************************************************************************/
+static FINLINE enum obj_result
+string_to_float(const char* str, float* f)
+{
+ char* ptr = NULL;
+ ASSERT(f);
+ if(!str)
+ return OBJ_BAD_ARGUMENT;
+ *f = (float)strtod(str, &ptr);
+ if(ptr == str || *ptr != '\0')
+ return OBJ_BAD_ARGUMENT;
+ return OBJ_OK;
+}
+
+static FINLINE enum obj_result
+string_to_size_t(const char* str, size_t* i)
+{
+ char* ptr = NULL;
+ ASSERT(i);
+ if(!str)
+ return OBJ_BAD_ARGUMENT;
+ *i = (size_t)strtol(str, &ptr, 10);
+ if(ptr == str || *ptr != '\0')
+ return OBJ_BAD_ARGUMENT;
+ return OBJ_OK;
+}
+
+static FINLINE void
+flush_named_group
+ (struct darray_named_group* groups, const struct darray_face* faces)
+{
+ struct named_group* grp;
+ size_t nfaces, ngrps;
+ ASSERT(groups && faces);
+ if(0 == (nfaces = darray_face_size_get(faces)))
+ return;
+ if( 0 == (ngrps = darray_named_group_size_get(groups)))
+ return;
+ grp = darray_named_group_data_get(groups) + (ngrps - 1);
+ ASSERT(grp->iface < nfaces);
+ grp->nfaces =nfaces - grp->iface;
+}
+
+static FINLINE void
+flush_smooth_group
+ (struct darray_smooth_group* groups, const struct darray_face* faces)
+{
+ struct smooth_group* grp;
+ size_t nfaces, ngrps;
+ ASSERT(groups && faces);
+ if(0 == (nfaces = darray_face_size_get(faces)))
+ return;
+ if( 0 == (ngrps = darray_smooth_group_size_get(groups)))
+ return;
+ grp = darray_smooth_group_data_get(groups) + (ngrps - 1);
+ ASSERT(grp->iface < nfaces);
+ grp->nfaces =nfaces - grp->iface;
+}
+
+static enum obj_result
+parse_floatX(char** word_tk, const unsigned count, struct darray_float* dst)
+{
+ unsigned i;
+ enum obj_result res = OBJ_OK;
+ ASSERT(word_tk && dst);
+
+ FOR_EACH(i, 0, count) {
+ char* real;
+ float f;
+ real = strtok_r(NULL, " ", word_tk);
+ res = string_to_float(real, &f);
+ if(res != OBJ_OK)
+ goto error;
+
+ if(darray_float_push_back(dst, &f)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ }
+exit:
+ return res;
+error:
+ FOR_EACH_REVERSE(i, i, 0)
+ darray_float_pop_back(dst);
+ goto exit;
+}
+
+static enum obj_result
+parse_face(struct obj* obj, char** word_tk)
+{
+ struct face face;
+ char* word;
+ enum obj_result res = OBJ_OK;
+ ASSERT(obj && word_tk);
+
+ face.ivertex = darray_vertex_size_get(&obj->vertices);
+ face.nvertices = 0;
+ while((word = strtok_r(NULL, " ", word_tk))) {
+ char* id, *id_tk;
+ struct vertex vertex;
+ /* position index */
+ id = strtok_r(word, "/", &id_tk);
+ res = string_to_size_t(id, &vertex.iposition);
+ if(res != OBJ_OK)
+ goto error;
+ /* texcoord index */
+ id = strtok_r(NULL, "/", &id_tk);
+ if(*id != '\0') {
+ res = string_to_size_t(id, &vertex.itexcoord);
+ if(res != OBJ_OK)
+ goto error;
+ }
+ /* normal index */
+ id = strtok_r(NULL, "/", &id_tk);
+ res = string_to_size_t(id, &vertex.inormal);
+ if(res != OBJ_OK)
+ goto error;
+
+ if(darray_vertex_push_back(&obj->vertices, &vertex)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ ++face.nvertices;
+ }
+
+ if(darray_face_push_back(&obj->faces, &face)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ FOR_EACH_REVERSE(face.nvertices, face.nvertices, 0)
+ darray_vertex_pop_back(&obj->vertices);
+ goto exit;
+}
+
+static enum obj_result
+parse_group(struct obj* obj, char** word_tk)
+{
+ char* word;
+ struct named_group* grp = NULL;
+ size_t ngrps;
+ enum obj_result res = OBJ_OK;
+ ASSERT(obj && word_tk);
+
+ flush_named_group(&obj->groups, &obj->faces);
+
+ word = strtok_r(NULL, " ", word_tk);
+ if(!word) {
+ res = OBJ_BAD_ARGUMENT;
+ goto error;
+ }
+ #define CALL(Func) { if(Func) { res = OBJ_MEMORY_ERROR; goto error; } }(void)0
+ ngrps = darray_named_group_size_get(&obj->groups);
+ CALL(darray_named_group_resize(&obj->groups, ngrps + 1));
+ grp = darray_named_group_data_get(&obj->groups) + ngrps;
+ CALL(str_set(&grp->name, word));
+ while((word = strtok_r(NULL, " ", word_tk))) {
+ CALL(str_append_char(&grp->name, ' '));
+ CALL(str_append(&grp->name, word));
+ }
+ #undef CALL
+ grp->iface = darray_face_size_get(&obj->faces);
+ grp->nfaces = 0;
+exit:
+ return res;
+error:
+ if(grp)
+ darray_named_group_pop_back(&obj->groups);
+ goto exit;
+}
+
+static enum obj_result
+parse_smooth_group(struct obj* obj, char** word_tk)
+{
+ char* word;
+ struct smooth_group grp;
+ enum obj_result res;
+ size_t i;
+ ASSERT(obj && word_tk);
+
+ flush_smooth_group(&obj->smooth_groups, &obj->faces);
+
+ word = strtok_r(NULL, " ", word_tk);
+ res = string_to_size_t(word, &i);
+ if(res != OBJ_OK)
+ return res;
+
+ grp.is_enabled = i != 0;
+ grp.iface = darray_face_size_get(&obj->faces);
+ grp.nfaces = 0;
+
+ if(darray_smooth_group_push_back(&obj->smooth_groups, &grp))
+ return OBJ_MEMORY_ERROR;
+
+ return OBJ_OK;
+}
+
+static enum obj_result
+parse_mtllib(struct obj* obj, char** word_tk)
+{
+ char* word;
+ size_t imtllib;
+ size_t nmtllibs;
+ enum obj_result res = OBJ_OK;
+ ASSERT(obj && word_tk);
+
+ word = strtok_r(NULL, " ", word_tk);
+ if(!word) {
+ res = OBJ_BAD_ARGUMENT;
+ goto error;
+ }
+ nmtllibs = imtllib = darray_mtllib_size_get(&obj->mtllibs);
+ do {
+ struct str* str;
+ if(darray_mtllib_resize(&obj->mtllibs, imtllib + 1)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ str = darray_mtllib_data_get(&obj->mtllibs) + imtllib;
+ ++imtllib;
+ if(str_set(str, word)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ } while((word = strtok_r(NULL, " ", word_tk)));
+
+exit:
+ return res;
+error:
+ CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0);
+ goto exit;
+}
+
+static enum obj_result
+parse_file(struct obj* obj, const char* path, char* content)
+{
+ char* line, *line_tk;
+ unsigned long iline;
+ enum obj_result res = OBJ_OK;
+ ASSERT(obj && path && content);
+
+ line = strtok_r(content, "\n", &line_tk);
+ iline = 1;
+ while(line) {
+ char* word, *word_tk;
+
+ word = strtok_r(line, " ", &word_tk);
+ while(word) {
+ if(word[0] == '#') { /* Comment */
+ break;
+ } else if(!strcmp(word, "v")) { /* Vertex position */
+ res = parse_floatX(&word_tk, 3, &obj->positions);
+ } else if(!strcmp(word, "vn")) { /* Vertex normal */
+ res = parse_floatX(&word_tk, 3, &obj->normals);
+ } else if(!strcmp(word, "vt")) { /* Vertex texture coordinates */
+ res = parse_floatX(&word_tk, 2, &obj->texcoords);
+ } else if(!strcmp(word, "f") || !strcmp(word, "fo")) { /* face element */
+ res = parse_face(obj, &word_tk);
+ } else if(!strcmp(word, "g")) { /* Grouping */
+ res = parse_group(obj, &word_tk);
+ } else if(!strcmp(word, "s")) { /* Smooth group */
+ res = parse_smooth_group(obj, &word_tk);
+ } else if(!strcmp(word, "mtllib")) { /* Mtl library */
+ res = parse_mtllib(obj, &word_tk);
+ } else if(!strcmp(word, "usemtl")) { /* Use the mtl library */
+ } else {
+ res = OBJ_BAD_ARGUMENT;
+ }
+ if(res != OBJ_OK)
+ goto error;
+ word = strtok_r(NULL, " ", &word_tk);
+ }
+ line = strtok_r(NULL, "\n", &line_tk);
+ ++iline;
+ }
+ flush_named_group(&obj->groups, &obj->faces);
+ flush_named_group(&obj->usemtls, &obj->faces);
+ flush_smooth_group(&obj->smooth_groups, &obj->faces);
+exit:
+ return res;
+error:
+ fprintf(stderr, "%s:%lu: error: parsing failed\n", path, iline);
+ goto exit;
+}
+
static void
obj_release(ref_T* ref)
{
struct obj* obj = CONTAINER_OF(ref, struct obj, ref);
ASSERT(ref);
+ darray_float_release(&obj->positions);
+ darray_float_release(&obj->normals);
+ darray_float_release(&obj->texcoords);
+ darray_vertex_release(&obj->vertices);
+ darray_face_release(&obj->faces);
+ darray_named_group_release(&obj->groups);
+ darray_named_group_release(&obj->usemtls);
+ darray_smooth_group_release(&obj->smooth_groups);
+ darray_mtllib_release(&obj->mtllibs);
MEM_FREE(obj->allocator, obj);
}
@@ -42,6 +443,15 @@ obj_create
}
obj->allocator = allocator;
ref_init(&obj->ref);
+ darray_float_init(mem_allocator, &obj->positions);
+ darray_float_init(mem_allocator, &obj->normals);
+ darray_float_init(mem_allocator, &obj->texcoords);
+ darray_vertex_init(mem_allocator, &obj->vertices);
+ darray_face_init(mem_allocator, &obj->faces);
+ darray_named_group_init(mem_allocator, &obj->groups);
+ darray_named_group_init(mem_allocator, &obj->usemtls);
+ darray_smooth_group_init(mem_allocator, &obj->smooth_groups);
+ darray_mtllib_init(mem_allocator, &obj->mtllibs);
exit:
return res;
@@ -106,7 +516,8 @@ obj_load(struct obj* obj, const char* filename)
fclose(file);
file = NULL;
- /* TODO parse file content */
+ if(OBJ_OK != (res = parse_file(obj, filename, file_content)))
+ goto error;
exit:
if(file)
@@ -118,3 +529,4 @@ error:
goto exit;
}
+