star-meshtool

Mesh transformation
git clone git://git.meso-star.fr/star-meshtool.git
Log | Files | Refs | README | LICENSE

commit 735625db4b783a8602e7bb832f0ed9a9db6ef0ac
parent ed04d86627ce1844f148c8bd12c980b9fb64a03e
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 20 Nov 2025 11:23:37 +0100

Merge branch 'release_0.3'

Diffstat:
MMakefile | 92++++++++++++++++++++++++++++++++++---------------------------------------------
MREADME.md | 26++++++++++++++++++++------
Mconfig.mk | 32+++++++++++---------------------
Adoc/mesh-tool.1 | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dmake.sh | 70----------------------------------------------------------------------
Msrc/mtool.c | 261++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Asrc/mtool.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/mtool_actions.c | 68--------------------------------------------------------------------
Dsrc/mtool_actions.h | 68--------------------------------------------------------------------
Msrc/mtool_args.c | 262++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Asrc/mtool_args.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mtool_main.c | 44++++++++++++++++++++++++++++++++++++++++++++
Dsrc/mtool_utils.c | 89-------------------------------------------------------------------------------
Dsrc/mtool_utils.h | 49-------------------------------------------------
14 files changed, 665 insertions(+), 614 deletions(-)

diff --git a/Makefile b/Makefile @@ -18,39 +18,27 @@ include config.mk -# Default target -all: build_executable man +default: tool +all: default ################################################################################ # Library building ################################################################################ -SRC = \ - src/mtool.c \ - src/mtool_actions.c\ - src/mtool_args.c\ - src/mtool_utils.c - -# Headers to configure -HDR= \ - src/mtool_version.h - +SRC =\ + src/mtool.c\ + src/mtool_args.c\ + src/mtool_main.c OBJ = $(SRC:.c=.o) DEP = $(SRC:.c=.d) +HDR = src/mtool_version.h -build_executable: .config $(HDR) $(DEP) - @$(MAKE) -fMakefile $$(for i in $(DEP); do echo -f $${i}; done) mesh-tool - -$(DEP) $(OBJ): config.mk - -mesh-tool: $(OBJ) - $(CC) $(CFLAGS) $(DPDC_CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(DPDC_LIBS) +CFLAGS_TOOL = $(CFLAGS) $(INCS) +LDFLAGS_TOOL = $(LDFLAGS) $(LIBS) -.config: config.mk - @if ! $(PKG_CONFIG) --atleast-version $(RSYS_VERSION) rsys; then \ - echo "rsys $(RSYS_VERSION) not found" >&2; exit 1; fi - @if ! $(PKG_CONFIG) --atleast-version $(SSTL_VERSION) sstl; then \ - echo "sstl $(SSTL_VERSION) not found" >&2; exit 1; fi - @echo "config done" > $@ +tool: .config $(DEP) + @$(MAKE) -fMakefile \ + $$(for i in $(DEP); do printf -- '-f %s\n' "$${i}"; done) \ + mesh-tool src/mtool_version.h: config.mk src/mtool_version.h.in sed -e 's/@MTOOL_VERSION_MAJOR@/$(VERSION_MAJOR)/' \ @@ -58,46 +46,46 @@ src/mtool_version.h: config.mk src/mtool_version.h.in -e 's/@MTOOL_VERSION_PATCH@/$(VERSION_PATCH)/' \ $@.in > $@ +$(DEP) $(OBJ): config.mk $(HDR) + +mesh-tool: $(OBJ) + $(CC) $(CFLAGS_TOOL) -o $@ $(OBJ) $(LDFLAGS_TOOL) + +.config: config.mk + $(PKG_CONFIG) --atleast-version $(RSYS_VERSION) rsys + $(PKG_CONFIG) --atleast-version $(SSTL_VERSION) sstl + @echo "config done" > $@ + .SUFFIXES: .c .d .o .c.d: - @$(CC) $(CFLAGS) $(DPDC_CFLAGS) -MM -MT \ - "$(@:.d=.o) $@" $< -MF $@ + @$(CC) $(CFLAGS_TOOL) -MM -MT "$(@:.d=.o) $@" $< -MF $@ .c.o: - $(CC) $(CFLAGS) $(DPDC_CFLAGS) -c $< -o $@ - -################################################################################ -# Man pages -################################################################################ -man: + $(CC) $(CFLAGS_TOOL) -c $< -o $@ ################################################################################ # Installation ################################################################################ -pkg: - sed -e 's#@PREFIX@#$(PREFIX)#g'\ - -e 's#@VERSION@#$(VERSION)#g'\ - mesh-tool.pc.in > mesh-tool.pc - -install: all pkg - @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/bin" mesh-tool - @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/share/doc/mesh-tool" COPYING README.md -# @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/share/man/man1" doc/mesh-tool.1 +install: all + install() { mode="$$1"; prefix="$$2"; shift 2; \ + mkdir -p "$${prefix}"; \ + cp "$$@" "$${prefix}"; \ + printf '%s\n' "$${@}" | while read -r i; do \ + chmod "$${mode}" "$${prefix}/$${i##*/}"; \ + done; \ + }; \ + install 755 "$(DESTDIR)$(BINPREFIX)" mesh-tool; \ + install 644 "$(DESTDIR)$(MANPREFIX)/man1" doc/mesh-tool.1; \ + install 644 "$(DESTDIR)$(PREFIX)/share/doc/mesh-tool" COPYING README.md uninstall: - rm -f "$(DESTDIR)$(PREFIX)/bin/mesh-tool" + rm -f "$(DESTDIR)$(BINPREFIX)/mesh-tool" + rm -f "$(DESTDIR)$(MANPREFIX)/man1/mesh-tool.1" rm -f "$(DESTDIR)$(PREFIX)/share/doc/mesh-tool/COPYING" rm -f "$(DESTDIR)$(PREFIX)/share/doc/mesh-tool/README.md" -# rm -f "$(DESTDIR)$(PREFIX)/share/man/man1/mesh-tool.1" -################################################################################ -# Miscellaneous targets -################################################################################ clean: - rm -f $(HDR) $(OBJ) .config mesh-tool mesh-tool.pc - -distclean: clean - rm -f $(DEP) + rm -f $(HDR) $(OBJ) $(DEP) .config mesh-tool lint: - shellcheck -o all make.sh + mandoc -Tlint -Wall doc/mesh-tool.1 diff --git a/README.md b/README.md @@ -18,17 +18,31 @@ Edit config.mk as needed, then run: ## Release notes +### Version 0.3.0 + +- Add the manual page. +- Add the `-r` option to reverse the normals of the input StL file. +- Update the meaning of the `-a` and `-b` options. + They define not only the format of the output StL file, but also the + format of the input StL file when it is provided on stdin. +- Update from Star-StL 0.5.2 to Star-StL 0.7. + Handle API breaks introduced by the new version. +- Improve the build system. + Simplify it by doing everything in one place (the makefile). + Add macros to control installation sub-directories. + ### Version 0.2.0 -- Add -d to get the description of the geometry (to stderr). -- Add -Sx,y,z to scale the geometry. +- Add `-d` to get the description of the geometry (to stderr). +- Add `-Sx,y,z` to scale the geometry. ### Version 0.1.0 -- Read and write mesh data from stdin/to stdout or from/to files, in ascii STL - as well as in binary STL format. -- Apply any number of transformations on the read data, in the specified order. -- The only transformation currently supported is translate (-Tx,y,z). +- Read and write mesh data from stdin/to stdout or from/to files, in + ascii StL as well as in binary StL format. +- Apply any number of transformations on the read data, in the specified + order. +- The only transformation currently supported is translate (`-Tx,y,z`). ## License diff --git a/config.mk b/config.mk @@ -1,7 +1,8 @@ VERSION_MAJOR = 0 -VERSION_MINOR = 2 +VERSION_MINOR = 3 VERSION_PATCH = 0 VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) + PREFIX = /usr/local LIB_TYPE = SHARED @@ -10,6 +11,9 @@ LIB_TYPE = SHARED BUILD_TYPE = RELEASE #BUILD_TYPE = DEBUG +BINPREFIX = $(PREFIX)/bin +MANPREFIX = $(PREFIX)/share/man + ################################################################################ # Tools ################################################################################ @@ -27,20 +31,10 @@ PCFLAGS_STATIC = --static PCFLAGS = $(PCFLAGS_$(LIB_TYPE)) RSYS_VERSION = 0.14 -RSYS_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags rsys) -RSYS_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs rsys) - -SSTL_VERSION = 0.5.1 -SSTL_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags sstl) -SSTL_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs sstl) +SSTL_VERSION = 0.7 -DPDC_CFLAGS =\ - $(RSYS_CFLAGS)\ - $(SSTL_CFLAGS) -DPDC_LIBS =\ - $(RSYS_LIBS)\ - $(SSTL_LIBS)\ - -lm +INCS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags rsys sstl) +LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs rsys sstl) -lm ################################################################################ # Compilation options @@ -61,9 +55,11 @@ CFLAGS_HARDENED =\ -fstack-protector-strong CFLAGS_COMMON=\ + -std=c89\ -pedantic\ -fvisibility=hidden\ -fstrict-aliasing\ + -fPIE\ $(CFLAGS_HARDENED)\ $(WFLAGS) @@ -71,19 +67,13 @@ CFLAGS_DEBUG = -g $(CFLAGS_COMMON) CFLAGS_RELEASE = -O2 -DNDEBUG $(CFLAGS_COMMON) CFLAGS = $(CFLAGS_$(BUILD_TYPE)) -CFLAGS_SO = $(CFLAGS) -fPIC -CFLAGS_EXE = $(CFLAGS) -fPIE - ################################################################################ # Linker options ################################################################################ LDFLAGS_HARDENED = -Wl,-z,relro,-z,now LDFLAGS_DEBUG = $(LDFLAGS_HARDENED) LDFLAGS_RELEASE = -s $(LDFLAGS_HARDENED) -LDFLAGS = $(LDFLAGS_$(BUILD_TYPE)) - -LDFLAGS_SO = $(LDFLAGS) -shared -Wl,--no-undefined -LDFLAGS_EXE = $(LDFLAGS) -pie +LDFLAGS = $(LDFLAGS_$(BUILD_TYPE)) -pie OCPFLAGS_DEBUG = --localize-hidden OCPFLAGS_RELEASE = --localize-hidden --strip-unneeded diff --git a/doc/mesh-tool.1 b/doc/mesh-tool.1 @@ -0,0 +1,108 @@ +.\" Copyright (C) 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/>. +.Dd November 19, 2025 +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Dt MESH-TOOL 1 +.Os +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh NAME +.Nm mesh-tool +.Nd transform an StL mesh +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh SYNOPSIS +.Nm +.Op Fl abdhrv +.Op Fl i Ar input +.Op Fl o Ar output +.Op Fl S Ar sx , Ns Ar sy , Ns Ar sz +.Op Fl T Ar tx , Ns Ar ty , Ns Ar tz +.Op Fl V Ar verbosity +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh DESCRIPTION +.Nm +aims to transform a mesh stored in StL format. +Multiple transformations can be applied in a single invocation by +repeating the transformation options as needed +.Po +options +.Fl S +and +.Fl T +.Pc . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Assume an ASCII StL format. +.It Fl b +Assumes a binary StL format. +.It Fl d +Output mesh description and exit. +.It Fl h +Output short help and exit. +.It Fl i Ar input +Path to the input StL file. +If not defined, the mesh is read from standard input. +.It Fl o Ar output +Path to the output StL file. +If not defined, the transformed mesh is written to standard output. +.It Fl r +Reverse the normals. +.It Fl S Ar sx , Ns Ar sy , Ns Ar sz +Scale the mesh. +Repeating this option allows you to apply multiple scales. +.It Fl T Ar tx , Ns Ar ty , Ns Ar tz +Translate the mesh. +Repeating this option allows you to apply multiple translations. +.It Fl V Ar verbosity +Set the verbosity level. +Possible values are +.Ql 0 +.Pq no message , +.Ql 1 +.Pq error messages only , +.Ql 2 +.Pq error and warning messages , +and +.Ql 3 +.Pq error, warning and informative messages . +All the messages are written to standard error. +Default is 0. +.It Fl v +Output version information and exit. +.El +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh EXIT STATUS +.Ex -std +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh EXAMPLES +Make the +.Pa bunny.stl +mesh twice as big and move it 5 units along the X axis and 3.14 units +along the Z axis. +Store the transformed mesh in the +.Pa big_move_bunny.stl +file: +.Bd -literal -offset Ds +mesh-tool -i bunny.stl -S 2,2,2 -T 5,0,3.14 -o big_move_bunny.stl +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh SEE ALSO +.Rs +.%T The StL Format: Standard Data Format for Fabbers +.%A Marshall Burns +.%D 1993 +.%U https://www.fabbers.com/tech/STL_Format +.Re diff --git a/make.sh b/make.sh @@ -1,70 +0,0 @@ -#!/bin/sh - -# Copyright (C) 2015, 2016, 2019, 2021, 2023 |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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -set -e - -config_test() -{ - for i in "$@"; do - test=$(basename "${i}" ".c") - test_list="${test_list} ${test}" - printf "%s: src/%s.o\n" "${test}" "${test}" - done - printf "test_bin: %s\n" "${test_list}" -} - -run_test() -{ - for i in "$@"; do - test=$(basename "${i}" ".c") - - printf "%s " "${test}" - if ./"${test}" > /dev/null 2>&1; then - printf "\033[1;32mOK\033[m\n" - else - printf "\033[1;31mError\033[m\n" - fi - done 2> /dev/null -} - -clean_test() -{ - for i in "$@"; do - rm -f "$(basename "${i}" ".c")" - done -} - -install() -{ - prefix=$1 - shift 1 - - mkdir -p "${prefix}" - - for i in "$@"; do - dst="${prefix}/${i##*/}" - - if cmp -s "${i}" "${dst}"; then - printf "Up to date %s\n" "${dst}" - else - printf "Installing %s\n" "${dst}" - cp "${i}" "${prefix}" - fi - done -} - -"$@" diff --git a/src/mtool.c b/src/mtool.c @@ -13,106 +13,219 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "mtool_actions.h" -#include "mtool_utils.h" +#include "mtool.h" #include <star/sstl.h> -#include <rsys/mem_allocator.h> -#include <rsys/logger.h> +#include <rsys/cstr.h> +#include <rsys/double3.h> +#include <rsys/double33.h> #include <rsys/float3.h> -#include <rsys/rsys.h> -int -main(int argc, char** argv) +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +print_description(struct sstl_desc* desc) { - res_T res = RES_OK; - struct ctx ctx = CTX_NULL__; - struct mem_allocator allocator; - struct logger logger; - struct sstl* sstl = NULL; - struct sstl_desc desc; - struct sstl_write_data data; - int allocator_initialized = 0; - int logger_initialized = 0; - int wbin; + float low[3], upp[3]; + size_t i; - ERR(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - allocator_initialized = 1; + ASSERT(desc); - ERR(logger_init(&allocator, &logger)); - logger_initialized = 1; + /* Calculate the mesh AABB */ + f3_splat(low, FLT_MAX); + f3_splat(upp,-FLT_MAX); + FOR_EACH(i, 0, desc->vertices_count) { + f3_min(low, low, desc->vertices+3*i); + f3_max(upp, upp, desc->vertices+3*i); + } - init_ctx(&allocator, &ctx); + /* Display the mesh description */ + fprintf(stderr, + "Type: %s, Vertice count: %ld, Triangle count: %ld\n" + "Xrange: [%g %g], Yrange: [%g %g], Zrange: [%g %g]\n", + desc->type == SSTL_ASCII ? "ACII" : "BINARY", + desc->vertices_count, + desc->triangles_count, + SPLIT3(low), + SPLIT3(upp)); +} +static res_T +create_writer(struct mtool* mtool, struct sstl_writer** out_writer) +{ + struct sstl_writer_create_args writer_args = SSTL_WRITER_CREATE_ARGS_DEFAULT; + struct sstl_writer* writer = NULL; + res_T res = RES_OK; - /* Active loggin for args parsing */ - logger_set_stream(&logger, LOG_ERROR, log_err_fn, NULL); - logger_set_stream(&logger, LOG_WARNING, log_warn_fn, NULL); - logger_set_stream(&logger, LOG_OUTPUT, log_prt_fn, NULL); + ASSERT(mtool && out_writer); - ERR(parse_args(argc, argv, &ctx, &logger, &allocator)); + if(mtool->args.output) { + /* Write StL to the destination file */ + writer_args.filename = mtool->args.output; + writer_args.stream = NULL; - if(ctx.h || ctx.v) { - if(ctx.v) print_version(stdout); - if(ctx.h) usage(stdout); - goto end; + } else { + /* Write StL to stdout */ + writer_args.filename = "stdout"; + writer_args.stream = stdout; } - /* Deactivate some loggin according to the -V arg */ - if(ctx.V < 1) - logger_set_stream(&logger, LOG_ERROR, NULL, NULL); - if(ctx.V < 2) - logger_set_stream(&logger, LOG_WARNING, NULL, NULL); - if(ctx.V < 3) - logger_set_stream(&logger, LOG_OUTPUT, NULL, NULL); - - /* Create SSTL device and load data */ - ERR(sstl_create(&logger, &allocator, ctx.V, &sstl)); - if(ctx.in != NULL) { - ERR(sstl_load(sstl, ctx.in)); + /* The output type is the one defined in the command line. Note that this + * option can also be used to define the type of input data provided on stdin. + * This is confusing, as the option is used for two files: input and output. + * TODO: correct this confusion */ + writer_args.type = mtool->args.type; + writer_args.triangles_count = (long)mtool->desc.triangles_count; + + if((res = sstl_writer_create(&writer_args, &writer)) != RES_OK) goto error; + +exit: + *out_writer = writer; + return res; +error: + if(writer) { SSTL(writer_ref_put(writer)); writer = NULL; } + goto exit; +} + +static res_T +load(struct mtool* mtool, const struct mtool_args* args) +{ + res_T res = RES_OK; + ASSERT(mtool && args); + + res = sstl_create(NULL, NULL, args->verbose, &mtool->sstl); + if(res != RES_OK) goto error; + + if(args->input) { + /* Load data from the provided input file */ + if((res = sstl_load(mtool->sstl, args->input)) != RES_OK) goto error; + } else { - ERR(sstl_load_stream(sstl, stdin)); + /* Data are loaded from stdin. The StL type must be defined because the + * standard input is not searchable. */ + switch(args->type) { + case SSTL_ASCII: + res = sstl_load_stream_ascii(mtool->sstl, stdin, "stdin (ASCII)"); + break; + case SSTL_BINARY: + res = sstl_load_stream_binary(mtool->sstl, stdin, "stdin (binary)"); + break; + default: FATAL("Unreachable code\n"); break; + } + if(res != RES_OK) goto error; } - /* Get access to data */ - ERR(sstl_get_desc(sstl, &desc)); + if((res = sstl_get_desc(mtool->sstl, &mtool->desc)) != RES_OK) goto error; - /* If description requested, print it and quit */ - if(ctx.d) { - if(ctx.d) print_description(&desc); - goto end; +exit: + return res; +error: + if(mtool->sstl) { SSTL(ref_put(mtool->sstl)); mtool->sstl = NULL; } + goto exit; +} + +static res_T +transform_mesh(struct mtool* mtool) +{ + struct sstl_writer* writer = NULL; + size_t itri = 0; + res_T res = RES_OK; + + if((res = create_writer(mtool, &writer)) != RES_OK) goto error; + + FOR_EACH(itri, 0, mtool->desc.triangles_count) { + struct sstl_facet facet = SSTL_FACET_NULL; + + /* Get the facet */ + res = sstl_desc_get_facet(&mtool->desc, itri, &facet); + if(res != RES_OK) goto error; + + /* Multiply the vertices by the transformation matrix constructed from the + * transformations submitted on the command line */ + #define TRANSFORM(Vertex) { \ + double vec[3] = {0,0,0}; \ + d3_set_f3(vec, Vertex); \ + d33_muld3(vec, mtool->args.transform, vec); \ + d3_add(vec, mtool->args.transform+9, vec); \ + f3_set_d3(Vertex, vec); \ + } (void) 0 + TRANSFORM(facet.vertices[0]); + TRANSFORM(facet.vertices[1]); + TRANSFORM(facet.vertices[2]); + #undef TRANSFORM + + if(mtool->args.reverse_normals) { + /* Swap the first two vertices to flip the orientation of the triangle */ + SWAP(float, facet.vertices[0][0], facet.vertices[1][0]); + SWAP(float, facet.vertices[0][1], facet.vertices[1][1]); + SWAP(float, facet.vertices[0][2], facet.vertices[1][2]); + + /* Reverse the normal even if, in reality, Star-StL does not provide it: + * it will be automatically calculated during writing from the vertices of + * the facet. However, this may not always be true, hence the explicit + * reversal of this normal, which allows for robustness against possible + * future changes without adding significant computational cost */ + f3_minus(facet.normal, facet.normal); + } + + /* Write the facet */ + if((res = sstl_write_facet(writer, &facet)) != RES_OK) goto error; } - /* Apply specified actions */ - ERR(apply_actions(&ctx, &desc)); +exit: + if(writer) SSTL(writer_ref_put(writer)); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +mtool_init(struct mtool* mtool, const struct mtool_args* args) +{ + res_T res = RES_OK; + ASSERT(mtool && args); + + *mtool = MTOOL_NULL; - /* Prepare data for writing */ - wbin = (ctx.b ? 1 : (ctx.a ? 0 : desc.read_type == SSTL_BINARY)); - ERR(sstl_pack_write_data(sstl, &data)); + /* Load the StL */ + if((res = load(mtool, args)) != RES_OK) goto error; + mtool->args = *args; + +exit: + return res; +error: + mtool_release(mtool); + goto exit; +} + +void +mtool_release(struct mtool* mtool) +{ + ASSERT(mtool); + if(mtool->sstl) SSTL(ref_put(mtool->sstl)); + mtool_args_release(&mtool->args); +} + +res_T +mtool_run(struct mtool* mtool) +{ + res_T res = RES_OK; + + if(mtool->args.print_desc) { + print_description(&mtool->desc); - /* Write data */ - if(ctx.in != NULL) { - ERR(sstl_write(&data, wbin, ctx.out)); } else { - ERR(sstl_write_stream(&data, wbin, stdout)); + res = transform_mesh(mtool); + if(res != RES_OK) goto error; } -end: - release_ctx(&ctx); - if(sstl) sstl_ref_put(sstl); - if(logger_initialized) logger_release(&logger); - if(allocator_initialized) { - if(MEM_ALLOCATED_SIZE(&allocator) != 0) { - char dump[4096] = { '\0' }; - MEM_DUMP(&allocator, dump, sizeof(dump)); - fprintf(stderr, "%s\n", dump); - fprintf(stderr, "\nMemory leaks: %lu Bytes\n", - (unsigned long)MEM_ALLOCATED_SIZE(&allocator)); - } - mem_shutdown_proxy_allocator(&allocator); - } +exit: return res; error: - goto end; + goto exit; } diff --git a/src/mtool.h b/src/mtool.h @@ -0,0 +1,48 @@ +/* Copyright (C) 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef MTOOL_H +#define MTOOL_H + +#include "mtool_args.h" +#include <rsys/rsys.h> + +/* Forward declarations */ +struct sstl; +struct sstl_writer; + +struct mtool { + struct sstl_desc desc; + struct sstl* sstl; + + struct mtool_args args; +}; +#define MTOOL_NULL__ {SSTL_DESC_NULL, NULL, MTOOL_ARGS_DEFAULT} +static const struct mtool MTOOL_NULL = MTOOL_NULL__; + +extern LOCAL_SYM res_T +mtool_init + (struct mtool* mtool, + const struct mtool_args* args); + +extern LOCAL_SYM void +mtool_release + (struct mtool* mtool); + +extern LOCAL_SYM res_T +mtool_run + (struct mtool* mtool); + +#endif /* MTOOL_H */ diff --git a/src/mtool_actions.c b/src/mtool_actions.c @@ -1,68 +0,0 @@ -/* Copyright (C) 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "mtool_actions.h" - -#include <star/sstl.h> - -#include <rsys/mem_allocator.h> -#include <rsys/logger.h> -#include <rsys/float3.h> -#include <rsys/rsys.h> - - -void -init_ctx(struct mem_allocator* allocator, struct ctx* ctx) -{ - ASSERT(allocator && ctx); - darray_actions_init(allocator, &ctx->actions); -} - -void -release_ctx(struct ctx* ctx) -{ - ASSERT(ctx); - darray_actions_release(&ctx->actions); -} - -res_T -apply_actions(const struct ctx* ctx, struct sstl_desc* desc) -{ - res_T res = RES_OK; - size_t a; - - /* Tanslate if specified */ - for(a = 0; a < darray_actions_size_get(&ctx->actions); a++) { - const struct actions* action = darray_actions_cdata_get(&ctx->actions) + a; - size_t i; - switch(action->type) { - case SCALE: - for(i = 0; i < desc->vertices_count; i++) { - f3_mul(desc->vertices+i*3, action->what.scale, desc->vertices+3*i); - } - break; - - case TRANSLATION: - for(i = 0; i < desc->vertices_count; i++) { - f3_add(desc->vertices+i*3, action->what.translation, desc->vertices+3*i); - } - break; - - default: FATAL("Invalid type"); - } - } - - return res; -} diff --git a/src/mtool_actions.h b/src/mtool_actions.h @@ -1,68 +0,0 @@ -/* Copyright (C) 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef MTOOL_ACTIONS_H -#define MTOOL_ACTIONS_H - -#include <rsys/dynamic_array.h> - -struct mem_allocator; -struct logger; -struct sstl_desc; - -enum action_type { - SCALE, - TRANSLATION -}; - -struct actions { - enum action_type type; - union { - float scale[3]; - float translation[3]; - } what; -}; - -#define DARRAY_NAME actions -#define DARRAY_DATA struct actions -#include <rsys/dynamic_array.h> - -struct ctx { - int a, b, d, h, v, V; - char *in, *out; - struct darray_actions actions; -}; - -#define CTX_NULL__ \ -{ 0, 0, 0, 0, 0, 0, NULL, NULL, {NULL,0,0,NULL} } - -res_T -parse_args - (const int argc, - char** argv, - struct ctx* ctx, - struct logger* logger, - struct mem_allocator* allocator); - -void -init_ctx(struct mem_allocator* allocator, struct ctx* ctx); - -void -release_ctx(struct ctx* ctx); - -res_T -apply_actions(const struct ctx* ctx, struct sstl_desc* desc); - -#endif /* MTOOL_ACTIONS_H */ diff --git a/src/mtool_args.c b/src/mtool_args.c @@ -13,140 +13,168 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "mtool_actions.h" -#include "mtool_utils.h" +#define _POSIX_C_SOURCE 200112L /* getopt support */ + +#include "mtool_args.h" +#include "mtool_version.h" -#include <star/sstl.h> -#include <rsys/mem_allocator.h> -#include <rsys/logger.h> #include <rsys/cstr.h> -#include <rsys/str.h> +#include <rsys/double3.h> + +#include <unistd.h> /* getopt */ + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE void +usage(FILE* stream) +{ + fprintf(stream, +"usage: mesh-tool [-abdhrv] [-i input] [-o output] [-S sx,sy,sz] [-T tx,ty,tz]\n" +" [-V verbosity]\n"); +} + +static INLINE void +version(FILE* stream) +{ + fprintf(stream, + "mesh-tool version %i.%i.%i\n", + MTOOL_VERSION_MAJOR, + MTOOL_VERSION_MINOR, + MTOOL_VERSION_PATCH); +} + +static res_T +apply_scale(struct mtool_args* args, const char* str) +{ + double scale[3] = {0,0,0}; + size_t len = 0; + res_T res = RES_OK; + + ASSERT(args && str); -#include <getopt.h> + res = cstr_to_list_double(str, ',', scale, &len, 3); + if(res == RES_OK && len != 3) res = RES_BAD_ARG; + if(res != RES_OK) goto error; + /* | Sx 0 0 0 | | A, D G J | | A*Sx D*Sx G*Sx J*Sx | + * M = | 0 Sy 0 0 | * | B, E H K | = | B*Sy E*Sy H*Sy K*Sy | + * | 0 0 Sz 0 | | C, F I L | | C*Sz F*Sz I*Sz L*Sz | + * | 0 0 0 1 | | 0, 0 0 1 | | 0 0 0 1 | */ + d3_mul(args->transform+0, args->transform+0, scale); + d3_mul(args->transform+3, args->transform+3, scale); + d3_mul(args->transform+6, args->transform+6, scale); + d3_mul(args->transform+9, args->transform+9, scale); + +exit: + return res; +error: + goto exit; +} + +static res_T +apply_translation(struct mtool_args* args, const char* str) +{ + double translation[3] = {0,0,0}; + size_t len = 0; + res_T res = RES_OK; + + ASSERT(args && str); + + res = cstr_to_list_double(str, ',', translation, &len, 3); + if(res == RES_OK && len != 3) res = RES_BAD_ARG; + if(res != RES_OK) goto error; + + /* | 1 0 0 Tx | | A D G J | | A D G (Tx + J) | + * M = | 0 1 0 Ty | * | B E H K | = | B E H (Ty + K) | + * | 0 0 1 Tz | | C F I L | | C F I (Tz + L) | + * | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 | */ + d3_add(args->transform+9, args->transform+9, translation); + +exit: + return res; +error: + goto exit; +} + +static res_T +parse_verbosity(struct mtool_args* args, const char* str) +{ + unsigned verbosity = 0; + res_T res = RES_OK; + + ASSERT(args && str); + + res = cstr_to_uint(str, &verbosity); + if(res == RES_OK && verbosity > 3) res = RES_BAD_ARG; + if(res != RES_OK) goto error; + + args->verbose = (int)verbosity; + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ res_T -parse_args - (const int argc, - char** argv, - struct ctx* ctx, - struct logger* logger, - struct mem_allocator* allocator) +mtool_args_init + (struct mtool_args* args, + int argc, + char** argv) { + const char option_list[] = "abdhi:o:rS:T:vV:"; int opt = 0; - size_t len = 0; - const char option_list[] = "abdhi:o:S:T:vV:"; res_T res = RES_OK; - ASSERT(argv && ctx && logger && allocator); - (void)allocator; + ASSERT(args && argc && argv); + + *args = MTOOL_ARGS_DEFAULT; - opterr = 0; /* No default error messages */ while((opt = getopt(argc, argv, option_list)) != -1) { switch (opt) { - - case '?': /* Unreconised option */ - { - char* ptr = strchr(option_list, optopt); - res = RES_BAD_ARG; - if(ptr && ptr[1] == ':') { - logger_print(logger, LOG_ERROR, - "Missing argument for option -%c\n", - optopt); - } else { - logger_print(logger, LOG_ERROR, "Invalid option -%c\n", optopt); - } - goto error; - } - - case 'a': - ctx->a = 1; - break; - - case 'b': - ctx->b = 1; - break; - - case 'd': - ctx->d = 1; - break; - + case 'a': args->type = SSTL_ASCII; break; + case 'b': args->type = SSTL_BINARY; break; + case 'd': args->print_desc = 1; break; case 'h': - ctx->h = 1; - break; - - case 'i': - ctx->in = optarg; - break; - - case 'o': - ctx->out = optarg; - break; - - case 'S': - { - struct actions action; - res = cstr_to_list_float(optarg, ',', action.what.scale, &len, 3); - if(res != RES_OK - || len != 3) - { - logger_print(logger, LOG_ERROR, - "Error parsing translation: `%s'\n", optarg); - if(res == RES_OK) res = RES_BAD_ARG; - goto error; - } - action.type = SCALE; - ERR(darray_actions_push_back(&ctx->actions, &action)); - break; - } - - case 'T': - { - struct actions action; - res = cstr_to_list_float(optarg, ',', action.what.translation, &len, 3); - if(res != RES_OK - || len != 3) - { - logger_print(logger, LOG_ERROR, - "Error parsing translation: `%s'\n", optarg); - if(res == RES_OK) res = RES_BAD_ARG; - goto error; - } - action.type = TRANSLATION; - ERR(darray_actions_push_back(&ctx->actions, &action)); - break; - } - + usage(stdout); + args->quit = 1; + goto exit; + case 'i': args->input = optarg; break; + case 'o': args->output = optarg; break; + case 'r': args->reverse_normals = 1; break; + case 'S': res = apply_scale(args, optarg); break; + case 'T': res = apply_translation(args, optarg); break; case 'v': - ctx->v = 1; - break; - - case 'V': - res = cstr_to_int(optarg, &ctx->V); - if(res != RES_OK - || ctx->V < 0 - || ctx->V > 3) - { - logger_print(logger, LOG_ERROR, - "Invalid verbosity level: `%s'\n", optarg); - if(res == RES_OK) res = RES_BAD_ARG; - goto error; - } - break; - + version(stdout); + args->quit = 1; + goto exit; + case 'V': res = parse_verbosity(args, optarg); break; + default: res = RES_BAD_ARG; break; + } + if(res != RES_OK) { + if(optarg) { + fprintf(stderr, "mesh-tool: invalid option argument '%s' -- '%c'\n", + optarg, opt); + } + goto error; } } - /* Check -a and -b not both specified */ - if(ctx->a && ctx->b) { - logger_print(logger, LOG_ERROR, - "Cannot specify -a and -b at the same time.\n"); - res = RES_BAD_ARG; - goto error; - } - -end: +exit: return res; error: - logger_print(logger, LOG_ERROR, "Use option -h to print help.\n"); - goto end; + mtool_args_release(args); + usage(stderr); + goto exit; +} + +void +mtool_args_release(struct mtool_args* args) +{ + ASSERT(args); + *args = MTOOL_ARGS_DEFAULT; } diff --git a/src/mtool_args.h b/src/mtool_args.h @@ -0,0 +1,62 @@ +/* Copyright (C) 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef MTOOL_ARGS_H +#define MTOOL_ARGS_H + +#include <star/sstl.h> /* enum sstl_type */ + +struct mtool_args { + /* Transformation matrix storing the list of transformations, applied in the + * order in which they are submitted on the command line */ + double transform[12]; /* 3x4 column major */ + + const char* input; /* Input filename. NULL <=> stdin */ + const char* output; /* Output filename. NULL <=> stdout */ + + /* Input/output StL type */ + enum sstl_type type; + + int reverse_normals; + int print_desc; + int verbose; + int quit; +}; +#define MTOOL_ARGS_DEFAULT__ { \ + {1,0,0, 0,1,0, 0,0,1, 0,0,0}, /* Transform */ \ + \ + NULL, /* Input */ \ + NULL, /* Output */ \ + \ + SSTL_ASCII, /* Input/output StL type */ \ + \ + 0, /* Reverse normals */ \ + 0, /* Print descriptor */ \ + 0, /* Verbosity */ \ + 0 /* Quit */ \ +} +static const struct mtool_args MTOOL_ARGS_DEFAULT = MTOOL_ARGS_DEFAULT__; + +extern LOCAL_SYM res_T +mtool_args_init + (struct mtool_args* args, + int argc, + char** argv); + +extern LOCAL_SYM void +mtool_args_release + (struct mtool_args* args); + +#endif /* MTOOL_ARGS_H */ diff --git a/src/mtool_main.c b/src/mtool_main.c @@ -0,0 +1,44 @@ +/* Copyright (C) 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "mtool.h" +#include "mtool_args.h" + +#include <rsys/mem_allocator.h> + +int +main(int argc, char** argv) +{ + struct mtool_args args = MTOOL_ARGS_DEFAULT; + struct mtool cmd = MTOOL_NULL; + size_t sz = 0; + res_T res = RES_OK; + + if((res = mtool_args_init(&args, argc, argv)) != RES_OK) goto error; + if(args.quit) goto exit; + + if((res = mtool_init(&cmd, &args)) != RES_OK) goto error; + if((res = mtool_run(&cmd)) != RES_OK) goto error; + +exit: + mtool_release(&cmd); + mtool_args_release(&args); + if((sz = mem_allocated_size()) != 0) { + fprintf(stderr, "Memory leaks: %lu Bytes\n", (unsigned long)sz); + } + return res; +error: + goto exit; +} diff --git a/src/mtool_utils.c b/src/mtool_utils.c @@ -1,89 +0,0 @@ -/* Copyright (C) 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "mtool_utils.h" -#include "mtool_version.h" - -#include <star/sstl.h> -#include <rsys/mem_allocator.h> -#include <rsys/logger.h> -#include <rsys/rsys.h> -#include <rsys/float3.h> - -#include <stdio.h> - -void -log_err_fn - (const char* msg, void* ctx) -{ - ASSERT(msg); - (void)ctx; - fprintf(stderr, "\x1b[31merror:\x1b[0m %s", msg); -} - -void -log_warn_fn - (const char* msg, void* ctx) -{ - ASSERT(msg); - (void)ctx; - fprintf(stderr, "\x1b[33mwarning:\x1b[0m %s", msg); -} - -void -log_prt_fn - (const char* msg, void* ctx) -{ - ASSERT(msg); - (void)ctx; - fprintf(stderr, "\x1b[32moutput:\x1b[0m %s", msg); -} - -void -usage(FILE* stream) -{ - fprintf(stream, "mesh-tool [-abdIv] [-S sx,sy,sz]] [-T tx,ty,tz] [-V verbosity_level]\n"); -} - -void -print_version - (FILE* stream) -{ - ASSERT(stream); - fprintf(stream, - "mesh-tool version %i.%i.%i\n", - MTOOL_VERSION_MAJOR, MTOOL_VERSION_MINOR, MTOOL_VERSION_PATCH); -} - -void -print_description - (struct sstl_desc* desc) -{ - size_t i; - float minb[3], maxb[3]; - f3_splat(minb, FLT_MAX); - f3_splat(maxb, -FLT_MAX); - for(i = 0; i < desc->vertices_count; i++) { - f3_min(minb, minb, desc->vertices+3*i); - f3_max(maxb, maxb, desc->vertices+3*i); - } - fprintf(stderr, - "Type: %s, Vertice count: %ld, Triangle count: %ld\n" - "Xrange: [%g %g], Yrange: [%g %g], Zrange: [%g %g]\n", - (desc->read_type == SSTL_ASCII ? "ACII" : "BINARY"), - desc->vertices_count, desc->triangles_count, - minb[0], maxb[0], minb[1], maxb[1], minb[2], maxb[2]); -} - diff --git a/src/mtool_utils.h b/src/mtool_utils.h @@ -1,49 +0,0 @@ -/* Copyright (C) 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef MTOOL_UTILS_H -#define MTOOL_UTILS_H - -#include <stdio.h> - -struct sstl_desc; - -/* Utility macros */ -#define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0 - -void -log_err_fn - (const char* msg, void* ctx); - -void -log_warn_fn - (const char* msg, void* ctx); - -void -log_prt_fn - (const char* msg, void* ctx); - -void -usage(FILE* stream); - -void -print_version - (FILE* stream); - -void -print_description - (struct sstl_desc* desc); - -#endif /* MTOOL_UTILS_H */