star-stl

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

commit 549e1b3e2b791a6fd8f5ee34185db75138440195
parent a907a559315166cb6612e3154344c340d4494fb9
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  9 Apr 2025 18:10:45 +0200

Parse binary format

Simply loads data in binary StL format. Encoding of little endian input
data is managed independently of host architecture.

This function has not been tested and is not used. It is currently an
internal routine that needs to be integrated into the API.

Diffstat:
MMakefile | 5+++--
Msrc/sstl_ascii.c | 15++++-----------
Asrc/sstl_binary.c | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sstl_c.h | 26++++++++++++++++++++++++++
4 files changed, 177 insertions(+), 13 deletions(-)

diff --git a/Makefile b/Makefile @@ -25,7 +25,7 @@ LIBNAME = $(LIBNAME_$(LIB_TYPE)) ################################################################################ # Library building ################################################################################ -SRC = src/sstl.c src/sstl_ascii.c +SRC = src/sstl.c src/sstl_ascii.c src/sstl_binary.c OBJ = $(SRC:.c=.o) DEP = $(SRC:.c=.d) @@ -130,7 +130,8 @@ test: build_tests @$(SHELL) make.sh config_test $(TEST_SRC) > $@ clean_test: - rm -f corner.stl corner_bin.stl test_basic.stl test_basic2.stl + rm -f 1_triangle.stl 1_triangle_no_normal.stl 1_triangle_with_noise.stl + rm -f empty.stl test.stl $(SHELL) make.sh clean_test $(TEST_SRC) $(TEST_DEP): config.mk sstl-local.pc diff --git a/src/sstl_ascii.c b/src/sstl_ascii.c @@ -18,8 +18,7 @@ #include "sstl_c.h" #include <rsys/cstr.h> -#include <rsys/float3.h> -#include <rsys/hash_table.h> +#include <rsys/stretchy_array.h> #include <rsys/text_reader.h> #include <string.h> @@ -180,15 +179,9 @@ parse_facet(struct sstl* sstl, struct txtrdr* txtrdr, char* normal) #undef READ_LINE - if(!f3_is_normalized(N)) { - /* If necessary, automatically calculate the surface normal. - * Vertices are CCW and the normal follows the right handed rule */ - float E0[3], E1[3]; - f3_sub(E0, v[1], v[0]); - f3_sub(E1, v[2], v[0]); - f3_cross(N, E0, E1); - f3_normalize(N, N); - } + /* If necessary, automatically calculate the surface normal. */ + if(!f3_is_normalized(N)) calculate_normal(N, v[0], v[1], v[2]); + exit: return res; error: diff --git a/src/sstl_binary.c b/src/sstl_binary.c @@ -0,0 +1,144 @@ +/* Copyright (C) 2015, 2016, 2019, 2021, 2023, 2025 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sstl_c.h" + +#include <rsys/cstr.h> +#include <rsys/stretchy_array.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE res_T +parse_header(struct sstl* sstl, FILE* fp, const char* name) +{ + char header[80]; + ASSERT(sstl && fp && fp); + + if(fread(header, sizeof(header), 1, fp) != sizeof(header)) { + ERROR(sstl, "%s: invalid header\n", name); + return RES_BAD_ARG; + } + return RES_OK; +} + +static INLINE res_T +parse_triangle_count + (struct sstl* sstl, + FILE* fp, + const char* name, + uint32_t* ntri) +{ + char bytes[4]; /* Little endian */ + ASSERT(sstl && fp && name && ntri); + + if(fread(bytes, sizeof(bytes), 1, fp) != sizeof(bytes)) { + ERROR(sstl, "%s: invalid triangle count\n", name); + return RES_BAD_ARG; + } + + /* Ensure encoding in host byte order */ + *ntri = + (uint32_t)(bytes[0]<<0) + | (uint32_t)(bytes[1]<<8) + | (uint32_t)(bytes[2]<<16) + | (uint32_t)(bytes[3]<<24); + return RES_OK; +} + +static INLINE res_T +parse_triangle + (struct sstl* sstl, + FILE* fp, + const char* name, + const uint32_t itri) /* Triangle identifier */ +{ + char bytes[4/*#bytes*/*12/*#vectors*/+2/*attribute*/]; + union { uint32_t ui32; float f; } ucast; + float* N = NULL; /* Normal */ + float v[3][3] = {0}; /* Vertices */ + int i = 0; + res_T res = RES_OK; + ASSERT(sstl && fp && name); + + if(fread(bytes, sizeof(bytes), 1, fp) != sizeof(bytes)) { + ERROR(sstl, "%s: invalid triangle %i\n", name, itri); + res = RES_BAD_ARG; + goto error; + } + + #define CAST(Bytes) \ + ((ucast.ui32 = \ + (uint32_t)((Bytes)[0]<<0) \ + | (uint32_t)((Bytes)[1]<<8) \ + | (uint32_t)((Bytes)[2]<<16) \ + | (uint32_t)((Bytes)[3]<<24)), \ + ucast.f) + + N = sa_add(sstl->normals, 3); + N[0] = CAST(bytes+0); + N[1] = CAST(bytes+4); + N[2] = CAST(bytes+8); + + FOR_EACH(i, 0, 3) { + v[i][0] = CAST(bytes+(i+1)*12+0); + v[i][1] = CAST(bytes+(i+1)*12+4); + v[i][2] = CAST(bytes+(i+1)*12+8); + + res = register_vertex(sstl, v[i]); + if(res != RES_OK) { + ERROR(sstl, "%s: vertex registration error -- %s\n", + name, res_to_cstr(res)); + res = RES_BAD_ARG; + goto error; + } + } + + /* If necessary, automatically calculate the surface normal. */ + if(!f3_is_normalized(N)) calculate_normal(N, v[0], v[1], v[2]); + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +load_stream_binary(struct sstl* sstl, FILE* fp, const char* name) +{ + uint32_t ntris = 0; + uint32_t i = 0; + res_T res = RES_OK; + ASSERT(sstl && fp && name); + + clear(sstl); + + if((res = parse_header(sstl, fp, name)) != RES_OK) goto error; + if((res = parse_triangle_count(sstl, fp, name, &ntris)) != RES_OK) goto error; + + FOR_EACH(i, 0, ntris) { + if((res = parse_triangle(sstl, fp, name, i)) != RES_OK) goto error; + } + +exit: + htable_vertex_purge(&sstl->vertex2id); /* Purge the helper structure */ + return res; +error: + clear(sstl); + goto exit; +} diff --git a/src/sstl_c.h b/src/sstl_c.h @@ -16,6 +16,7 @@ #ifndef SSTL_C_H #define SSTL_C_H +#include <rsys/float3.h> #include <rsys/hash_table.h> #include <rsys/logger.h> #include <rsys/ref_count.h> @@ -83,6 +84,25 @@ clear(struct sstl* sstl) htable_vertex_clear(&sstl->vertex2id); } +static INLINE float* +calculate_normal + (float N[3], + const float v0[3], + const float v1[3], + const float v2[3]) +{ + float E0[3], E1[3]; + ASSERT(N && v0 && v1 && v2); + + /* Vertices are CCW and the normal follows the right handed rule */ + f3_sub(E0, v1, v0); + f3_sub(E1, v2, v0); + f3_cross(N, E0, E1); + f3_normalize(N, N); + + return N; +} + extern LOCAL_SYM res_T load_stream_ascii (struct sstl* sstl, @@ -90,6 +110,12 @@ load_stream_ascii const char* stream_name); extern LOCAL_SYM res_T +load_stream_binary + (struct sstl* sstl, + FILE* stream, + const char* stream_name); + +extern LOCAL_SYM res_T register_vertex (struct sstl* sstl, const float v[3]);