star-stl

Load STereo Lithography (StL) file format
git clone git://git.meso-star.fr/star-stl.git
Log | Files | Refs | README | LICENSE

commit e20bd58ae37440b5293d0b8e20fd2064daba5349
parent 62833bc63bc69a1121d26a5f67f178366470198e
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  6 Jan 2016 11:00:25 +0100

First draft of the load functions

It is neither tested nor finalized.

Diffstat:
Mcmake/CMakeLists.txt | 6+++++-
Msrc/sstl.c | 329+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 334 insertions(+), 1 deletion(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) +# Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) # # This software is a computer program whose purpose is to generate files used # to build the Star-3D library. @@ -63,6 +63,10 @@ rcmake_prepend_path(SSTL_FILES_DOC ${SSTL_SOURCE_DIR}/../) add_library(sstl SHARED ${SSTL_FILES_SRC} ${SSTL_FILES_INC} ${SSTL_FILES_INC_API}) target_link_libraries(sstl RSys) +if(CMAKE_COMPILER_IS_GNUCC) + target_link_libraries(sstl m) +endif() + set(VERSION_MAJOR 0) set(VERSION_MINOR 3) diff --git a/src/sstl.c b/src/sstl.c @@ -28,12 +28,48 @@ #include "sstl.h" +#include <rsys/cstr.h> +#include <rsys/float3.h> +#include <rsys/hash_table.h> #include <rsys/logger.h> #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> +#include <rsys/stretchy_array.h> + +struct solid { + char* name; + size_t* indices; + float* vertices; + float* normals; +}; +static const struct solid SOLID_NULL = { NULL, NULL, NULL, NULL }; + +struct vertex { float xyz[3]; }; + +struct streamer { + FILE* stream; + const char* name; + size_t iline; + char* buf; +}; + +static INLINE char +eq_vertex(const struct vertex* a, const struct vertex* b) +{ + return f3_eq(a->xyz, b->xyz); +} + +#define HTABLE_NAME vertex +#define HTABLE_DATA size_t +#define HTABLE_KEY struct vertex +#define HTABLE_KEY_FUNCTOR_EQ eq_vertex +#include <rsys/hash_table.h> struct sstl { int verbose; + struct htable_vertex vertex2id; + struct solid* solids; + struct logger* logger; struct mem_allocator* allocator; ref_T ref; @@ -43,6 +79,271 @@ struct sstl { * Helper functions ******************************************************************************/ static void +streamer_init(struct streamer* streamer, FILE* stream, const char* name) +{ + ASSERT(streamer && stream && name); + memset(streamer, 0, sizeof(struct streamer)); + streamer->stream = stream; + streamer->name = name; + streamer->iline = 1; +} + +static void +streamer_release(struct streamer* streamer) +{ + ASSERT(streamer); + sa_release(streamer->buf); +} + +static char* +streamer_read_line(struct streamer* streamer) +{ + const size_t buf_chunk = 256; + char* line; + ASSERT(streamer); + + for(;(line=fgets(streamer->buf, (int)sa_size(streamer->buf), streamer->stream)); + ++streamer->iline) { + /* Ensure that the whole line is read */ + while(!strrchr(line, '\n')) { + char* remain = sa_add(streamer->buf, buf_chunk); + if(!remain) { + FATAL("Not enough memory: couldn't resize the stream buffer.\n"); + } + line = streamer->buf; + if(!fgets(remain, (int)buf_chunk, streamer->stream)) /* EOF */ + break; + } + if(strcspn(streamer->buf, " \t") != 0) /* Not empty line */ + break; + } + ++streamer->iline; + return line; +} + +static void +solid_clear(struct solid* solid) +{ + ASSERT(solid); + sa_release(solid->name); + sa_release(solid->vertices); + sa_release(solid->indices); + sa_release(solid->normals); +} + +static void +print_log + (const struct sstl* sstl, const enum log_type type, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(sstl && msg); + if(sstl->verbose) { + res_T res; (void)res; + va_start(vargs_list, msg); + res = logger_vprint(sstl->logger, type, msg, vargs_list); + ASSERT(res == RES_OK); + va_end(vargs_list); + } +} + +static INLINE res_T +parse_float3 + (struct sstl* sstl, + char* str, + float vert[3], + const char* filename, + const size_t iline) +{ + char* tk; + int i; + res_T res = RES_OK; + ASSERT(str && vert && filename); + + tk = strtok(str, " \t"); + FOR_EACH(i, 0, 3) { + if(!tk) { + print_log(sstl, LOG_ERROR, "%s:%lu: expecting 3D coordinates.\n", + filename, (unsigned long)iline); + return RES_BAD_ARG; + } + + res = cstr_to_float(tk, vert + i); + if(res != RES_OK) { + print_log(sstl, LOG_ERROR, "%s:%lu: invalid coordinate \"%s\".\n", + filename, (unsigned long)iline, tk); + return res; + } + tk = strtok(NULL, " \t"); + } + tk = strtok(NULL, "\0"); + if(tk) { + print_log(sstl, LOG_WARNING, "%s:%lu: unexpected directive \"%s\".\n", + filename, (unsigned long)iline, tk); + } + return RES_OK; +} + +static res_T +load_stream(struct sstl* sstl, FILE* stream, const char* stream_name) +{ + res_T res = RES_OK; + struct streamer streamer; + struct solid solid = SOLID_NULL; + char* line = NULL; + char* tk; + size_t* id; + + ASSERT(sstl && stream); + streamer_init(&streamer, stream, stream_name); + + if(!sstl || !stream) { + res = RES_BAD_ARG; + goto error; + } + + while((line = streamer_read_line(&streamer))) { + + /* Parse the solid name */ + if(strcmp(strtok(line, " \t"), "solid")) { + print_log(sstl, LOG_ERROR, + "%s:%lu: missing the \"solid NAME\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + tk = strtok(NULL, "\0"); + solid.name = sa_add(solid.name, strlen(tk)+1/*NULL char*/); + if(!solid.name) { + print_log(sstl, LOG_ERROR, + "Not enough memory: couldn't allocate the name of the solid.\n"); + res = RES_MEM_ERR; + goto error; + } + + for(;;) { /* Parse the solid facets */ + float normal[3]; + size_t facet[3]; + int ivertex; + + solid = SOLID_NULL; + + line = streamer_read_line(&streamer); + if(!(line = streamer_read_line(&streamer))) { + print_log(sstl, LOG_ERROR, "%s:%lu: missing directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + + tk = strtok(line, " \t"); + + /* Stop on "endsolid" directive */ + if(!strcmp(tk, "endsolid")) + break; + + /* Parse the facet normal directive */ + if(strcmp(tk, "facet") || strcmp(strtok(NULL, " \t"), "normal")) { + print_log(sstl, LOG_ERROR, + "%s:%lu: missing the \"facet normal X Y Z\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + res = parse_float3 + (sstl, strtok(NULL, "\0"), normal, streamer.name, streamer.iline); + if(res != RES_OK) goto error; + + /* Parse the Outer loop directive */ + if(!(line = streamer_read_line(&streamer)) + || strcmp(strtok(line, " \t"), "outer") + || strcmp(strtok(NULL, " \t"), "loop")) { + print_log(sstl, LOG_ERROR, + "%s:%lu: missing the \"outer loop\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + if(strcspn(strtok(NULL, " \0"), " \t")) { /* Invalid remaining chars */ + print_log(sstl, LOG_WARNING, + "%s:%lu: malformed \"outer loop\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + + /* Parse the facet vertices. Assume that only 3 vertices are submitted */ + FOR_EACH(ivertex, 0, 3) { + struct vertex pos; + line = streamer_read_line(&streamer); + if(!line || !strcmp(strtok(line, " \t"), "vertex")) { + print_log(sstl, LOG_ERROR, + "%s:%lu: missing a \"vertex X Y Z\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + res = parse_float3 + (sstl, strtok(NULL, "\0"), pos.xyz, streamer.name, streamer.iline); + if(res != RES_OK) goto error; + + id = htable_vertex_find(&sstl->vertex2id, &pos); + if(id) { + facet[ivertex] = *id; + } else { + facet[ivertex] = sa_size(solid.indices); + res = htable_vertex_set(&sstl->vertex2id, &pos, facet + ivertex); + if(res != RES_OK) { + print_log(sstl, LOG_ERROR, + "%s:%lu: couldn't register a vertex position.\n", + streamer.name, (unsigned long)streamer.iline); + } + f3_set(sa_add(solid.vertices, 3), pos.xyz); + } + } + + if(!f3_is_normalized(normal)) { /* Compute normal from facet vertices */ + float v0[3], v1[3]; + f3_sub(v0, solid.vertices + facet[1]*3, solid.vertices + facet[0]*3); + f3_sub(v1, solid.vertices + facet[2]*3, solid.vertices + facet[0]*3); + f3_cross(normal, v0, v1); + f3_normalize(normal, normal); + } + f3_set(sa_add(solid.normals, 3), normal); + id = sa_add(solid.indices, 3); + id[0] = facet[0]; + id[1] = facet[1]; + id[2] = facet[2]; + + streamer_read_line(&streamer); + if(!line || strcmp(strtok(line, " \t"), "endloop")) { + print_log(sstl, LOG_ERROR, + "%s:%lu: missing the \"endloop\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + res = RES_BAD_ARG; + goto error; + } + if(strcspn(strtok(NULL, " \0"), " \t")) { /* Invalid remaining chars */ + print_log(sstl, LOG_WARNING, + "%s:%lu: malformed \"endloop\" directive.\n", + streamer.name, (unsigned long)streamer.iline); + } + } + + if(strcmp(strtok(NULL, "\0"), streamer.name)) { + print_log(sstl, LOG_WARNING, + "%s:%lu: inconsistent \"endsolid\" name.\n", + streamer.name, (unsigned long)streamer.iline); + } + solid_clear(&solid); /* FIXME register the solid against sstl */ + } +exit: + streamer_release(&streamer); + return res; +error: + goto exit; +} + +static void sstl_release(ref_T* ref) { struct sstl* sstl; @@ -88,6 +389,7 @@ sstl_create sstl->allocator = allocator; sstl->logger = logger; sstl->verbose = verbose; + htable_vertex_init(allocator, &sstl->vertex2id); exit: if(out_sstl) *out_sstl = sstl; @@ -117,3 +419,30 @@ sstl_ref_put(struct sstl* sstl) } +res_T +sstl_load(struct sstl* sstl, const char* filename) +{ + FILE* file; + res_T res = RES_OK; + + if(!sstl || !filename) + return RES_BAD_ARG; + + file = fopen(filename, "r"); + if(!file) { + print_log(sstl, LOG_ERROR, "Error opening `%s'.\n", filename); + return RES_IO_ERR; + } + res = load_stream(sstl, file, filename); + fclose(file); + return res; +} + +res_T +sstl_load_stream(struct sstl* sstl, FILE* stream) +{ + if(!sstl || !stream) return RES_BAD_ARG; + return load_stream(sstl, stream, "STREAM"); +} + +