commit 7258e67ab614621883bdd98b4bd9a562460fc5d1
parent f9f0af5ede06568db14492f4ba4ca71da3b18765
Author: vaplv <vaplv@free.fr>
Date: Sun, 29 Jun 2014 22:00:21 +0200
Test the obj loader
Diffstat:
| M | cmake/CMakeLists.txt | | | 7 | +++++++ |
| M | src/obj.c | | | 118 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
| A | src/test_obj.c | | | 104 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 209 insertions(+), 20 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -37,6 +37,13 @@ set_target_properties(loader-obj PROPERTIES
rcmake_setup_devel(LoaderObj loader-obj ${VERSION} obj/obj_version.h)
################################################################################
+# Define tests
+################################################################################
+add_executable(test_obj ${OBJ_SOURCE_DIR}/test_obj.c)
+target_link_libraries(test_obj loader-obj)
+add_test(test_obj test_obj)
+
+################################################################################
# Install directories
################################################################################
install(TARGETS loader-obj
diff --git a/src/obj.c b/src/obj.c
@@ -98,9 +98,9 @@ named_group_copy_and_release(struct named_group* dst, struct named_group* src)
#include <rsys/dynamic_array.h>
struct obj {
- struct darray_float positions; /* float3 */
+ struct darray_float positions; /* float4 */
struct darray_float normals; /* float3 */
- struct darray_float texcoords; /* float2 */
+ struct darray_float texcoords; /* float3 */
struct darray_vertex vertices;
struct darray_face faces;
struct darray_named_group groups;
@@ -174,25 +174,42 @@ flush_smooth_group
}
static enum obj_result
-parse_floatX(char** word_tk, const unsigned count, struct darray_float* dst)
+parse_floatX
+ (char** word_tk,
+ const unsigned count_min,
+ const unsigned count_max,
+ const float default_value,
+ struct darray_float* dst)
{
unsigned i;
enum obj_result res = OBJ_OK;
- ASSERT(word_tk && dst);
+ ASSERT(word_tk && dst && count_min <= count_max);
- FOR_EACH(i, 0, count) {
+ FOR_EACH(i, 0, count_max) {
char* real;
float f;
- real = strtok_r(NULL, " ", word_tk);
+ if(NULL == (real = strtok_r(NULL, " ", word_tk)))
+ break;
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;
}
}
+ if(i < count_min) {
+ res = OBJ_BAD_ARGUMENT;
+ goto error;
+ }
+
+ FOR_EACH(i, i, count_max) {
+ if(darray_float_push_back(dst, &default_value)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ }
+
exit:
return res;
error:
@@ -221,16 +238,18 @@ parse_face(struct obj* obj, char** word_tk)
goto error;
/* texcoord index */
id = strtok_r(NULL, "/", &id_tk);
- if(*id != '\0') {
- res = string_to_size_t(id, &vertex.itexcoord);
+ if(id) {
+ 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;
}
- /* 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;
@@ -318,8 +337,8 @@ static enum obj_result
parse_mtllib(struct obj* obj, char** word_tk)
{
char* word;
- size_t imtllib;
- size_t nmtllibs;
+ size_t imtllib = 0;
+ size_t nmtllibs = 0;
enum obj_result res = OBJ_OK;
ASSERT(obj && word_tk);
@@ -346,7 +365,46 @@ parse_mtllib(struct obj* obj, char** word_tk)
exit:
return res;
error:
- CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0);
+ if(imtllib != nmtllibs)
+ CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0);
+ goto exit;
+}
+
+static enum obj_result
+parse_usemtl(struct obj* obj, char** word_tk)
+{
+ char* word;
+ struct named_group* mtl = NULL;
+ size_t nmtls;
+ enum obj_result res = OBJ_OK;
+ ASSERT(obj && word_tk);
+
+ flush_named_group(&obj->usemtls, &obj->faces);
+
+ word= strtok_r(NULL, " ", word_tk);
+ if(!word_tk) {
+ res = OBJ_BAD_ARGUMENT;
+ goto error;
+ }
+
+ nmtls = darray_named_group_size_get(&obj->usemtls);
+ if(darray_named_group_resize(&obj->usemtls, nmtls + 1)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ mtl = darray_named_group_data_get(&obj->usemtls) + nmtls;
+ if(str_set(&mtl->name, word)) {
+ res = OBJ_MEMORY_ERROR;
+ goto error;
+ }
+ mtl->iface = darray_face_size_get(&obj->faces);
+ mtl->nfaces = 0;
+
+exit:
+ return res;
+error:
+ if(mtl)
+ darray_named_group_pop_back(&obj->usemtls);
goto exit;
}
@@ -368,11 +426,11 @@ parse_file(struct obj* obj, const char* path, char* content)
if(word[0] == '#') { /* Comment */
break;
} else if(!strcmp(word, "v")) { /* Vertex position */
- res = parse_floatX(&word_tk, 3, &obj->positions);
+ res = parse_floatX(&word_tk, 3, 4, 1.f, &obj->positions);
} else if(!strcmp(word, "vn")) { /* Vertex normal */
- res = parse_floatX(&word_tk, 3, &obj->normals);
+ res = parse_floatX(&word_tk, 3, 3, 0.f, &obj->normals);
} else if(!strcmp(word, "vt")) { /* Vertex texture coordinates */
- res = parse_floatX(&word_tk, 2, &obj->texcoords);
+ res = parse_floatX(&word_tk, 2, 3, 0.f, &obj->texcoords);
} else if(!strcmp(word, "f") || !strcmp(word, "fo")) { /* face element */
res = parse_face(obj, &word_tk);
} else if(!strcmp(word, "g")) { /* Grouping */
@@ -382,6 +440,7 @@ parse_file(struct obj* obj, const char* path, char* content)
} else if(!strcmp(word, "mtllib")) { /* Mtl library */
res = parse_mtllib(obj, &word_tk);
} else if(!strcmp(word, "usemtl")) { /* Use the mtl library */
+ res = parse_usemtl(obj, &word_tk);
} else {
res = OBJ_BAD_ARGUMENT;
}
@@ -403,6 +462,21 @@ error:
}
static void
+obj_clear(struct obj* obj)
+{
+ ASSERT(obj);
+ darray_float_clear(&obj->positions);
+ darray_float_clear(&obj->normals);
+ darray_float_clear(&obj->texcoords);
+ darray_vertex_clear(&obj->vertices);
+ darray_face_clear(&obj->faces);
+ darray_named_group_clear(&obj->groups);
+ darray_named_group_clear(&obj->usemtls);
+ darray_smooth_group_clear(&obj->smooth_groups);
+ darray_mtllib_clear(&obj->mtllibs);
+}
+
+static void
obj_release(ref_T* ref)
{
struct obj* obj = CONTAINER_OF(ref, struct obj, ref);
@@ -454,6 +528,8 @@ obj_create
darray_mtllib_init(mem_allocator, &obj->mtllibs);
exit:
+ if(obj_out)
+ *obj_out = obj;
return res;
error:
if(obj) {
@@ -489,6 +565,7 @@ obj_load(struct obj* obj, const char* filename)
long file_size = 0;
size_t len = 0;
enum obj_result res = OBJ_OK;
+ (void)len;
if(!obj || !filename) {
res = OBJ_BAD_ARGUMENT;
@@ -516,6 +593,7 @@ obj_load(struct obj* obj, const char* filename)
fclose(file);
file = NULL;
+ obj_clear(obj);
if(OBJ_OK != (res = parse_file(obj, filename, file_content)))
goto error;
diff --git a/src/test_obj.c b/src/test_obj.c
@@ -0,0 +1,104 @@
+#include "obj.h"
+#include <rsys/mem_allocator.h>
+#include <string.h>
+
+static const char* plane_obj =
+ "mtllib master.mtl"
+ "\n"
+ "v 0.0000 2.0000 0.0000\n"
+ "v 0.0000 0.0000 0.0000\n"
+ "v 2.0000 0.0000 0.0000\n"
+ "v 2.0000 2.0000 0.0000\n"
+ "vt 0.0000 1.0000 0.0000\n"
+ "vt 0.0000 0.0000 0.0000\n"
+ "vt 1.0000 0.0000 0.0000\n"
+ "vt 1.0000 1.0000 0.0000\n"
+ "# 4 vertices\n"
+ "\n"
+ "usemtl wood\n"
+ "f 1/1 2/2 3/3 4/4\n"
+ "# 1 element\n";
+
+static const char* cube_obj =
+ "# Cube with a different material applied to each of its faces\n"
+ "mtllib master.mtl\n"
+ "v 0.0000 2.0000 2.0000\n"
+ "v 0.0000 0.0000 2.0000\n"
+ "v 2.0000 0.0000 2.0000\n"
+ "v 2.0000 2.0000 2.0000\n"
+ "v 0.0000 2.0000 0.0000\n"
+ "v 0.0000 0.0000 0.0000\n"
+ "v 2.0000 0.0000 0.0000\n"
+ "v 2.0000 2.0000 0.0000\n"
+ "# 8 vertices\n"
+ "g front\n"
+ "usemtl red\n"
+ "f 1 2 3 4\n"
+ "g back\n"
+ "usemtl blue\n"
+ "f 8 7 6 5\n"
+ "g right\n"
+ "usemtl green\n"
+ "f 4 3 7 8\n"
+ "g top\n"
+ "usemtl gold\n"
+ "f 5 1 4 8\n"
+ "g left\n"
+ "usemtl orange\n"
+ "f 5 6 2 1\n"
+ "g bottom\n"
+ "usemtl purple\n"
+ "f 2 6 7 3\n"
+ "# 6 elements\n";
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct obj* obj;
+ FILE* file;
+ (void)argc, (void)argv;
+
+ file = fopen("test_obj_cube.obj", "w");
+ NCHECK(file, NULL);
+ fwrite(cube_obj, sizeof(char), strlen(cube_obj), file);
+ fclose(file);
+
+ file = fopen("test_obj_plane.obj", "w");
+ NCHECK(file, NULL);
+ fwrite(plane_obj, sizeof(char), strlen(plane_obj), file);
+ fclose(file);
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+
+ CHECK(obj_create(NULL, NULL), OBJ_BAD_ARGUMENT);
+ CHECK(obj_create(&allocator, NULL), OBJ_BAD_ARGUMENT);
+ CHECK(obj_create(NULL, &obj), OBJ_OK);
+
+ CHECK(obj_ref_get(NULL), OBJ_BAD_ARGUMENT);
+ CHECK(obj_ref_get(obj), OBJ_OK);
+ CHECK(obj_ref_put(NULL), OBJ_BAD_ARGUMENT);
+ CHECK(obj_ref_put(obj), OBJ_OK);
+ CHECK(obj_ref_put(obj), OBJ_OK);
+
+ CHECK(obj_create(&allocator, &obj), OBJ_OK);
+
+ CHECK(obj_load(NULL, NULL), OBJ_BAD_ARGUMENT);
+ CHECK(obj_load(obj, NULL), OBJ_BAD_ARGUMENT);
+ CHECK(obj_load(NULL, "test_obj_cube.obj"), OBJ_BAD_ARGUMENT);
+ CHECK(obj_load(obj, "none.obj"), OBJ_IO_ERROR);
+ CHECK(obj_load(obj, "test_obj_cube.obj"), OBJ_OK);
+ CHECK(obj_load(obj, "test_obj_plane.obj"), OBJ_OK);
+
+ CHECK(obj_ref_put(obj), OBJ_OK);
+
+ if(MEM_ALLOCATED_SIZE(&allocator)) {
+ char dump[512];
+ MEM_DUMP(&allocator, dump, sizeof(dump)/sizeof(char));
+ fprintf(stderr, "%s\n", dump);
+ FATAL("Memory leaks\n");
+ }
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}