mtool.c (6564B)
1 /* Copyright (C) 2025 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #include "mtool.h" 17 18 #include <star/sstl.h> 19 20 #include <rsys/cstr.h> 21 #include <rsys/double3.h> 22 #include <rsys/double33.h> 23 #include <rsys/float3.h> 24 25 /******************************************************************************* 26 * Helper functions 27 ******************************************************************************/ 28 static void 29 print_description(struct sstl_desc* desc) 30 { 31 float low[3], upp[3]; 32 size_t i; 33 34 ASSERT(desc); 35 36 /* Calculate the mesh AABB */ 37 f3_splat(low, FLT_MAX); 38 f3_splat(upp,-FLT_MAX); 39 FOR_EACH(i, 0, desc->vertices_count) { 40 f3_min(low, low, desc->vertices+3*i); 41 f3_max(upp, upp, desc->vertices+3*i); 42 } 43 44 /* Display the mesh description */ 45 fprintf(stderr, 46 "Type: %s, Vertice count: %ld, Triangle count: %ld\n" 47 "Xrange: [%g %g], Yrange: [%g %g], Zrange: [%g %g]\n", 48 desc->type == SSTL_ASCII ? "ACII" : "BINARY", 49 desc->vertices_count, 50 desc->triangles_count, 51 SPLIT3(low), 52 SPLIT3(upp)); 53 } 54 55 static res_T 56 create_writer(struct mtool* mtool, struct sstl_writer** out_writer) 57 { 58 struct sstl_writer_create_args writer_args = SSTL_WRITER_CREATE_ARGS_DEFAULT; 59 struct sstl_writer* writer = NULL; 60 res_T res = RES_OK; 61 62 ASSERT(mtool && out_writer); 63 64 if(mtool->args.output) { 65 /* Write StL to the destination file */ 66 writer_args.filename = mtool->args.output; 67 writer_args.stream = NULL; 68 69 } else { 70 /* Write StL to stdout */ 71 writer_args.filename = "stdout"; 72 writer_args.stream = stdout; 73 } 74 75 /* The output type is the one defined in the command line. Note that this 76 * option can also be used to define the type of input data provided on stdin. 77 * This is confusing, as the option is used for two files: input and output. 78 * TODO: correct this confusion */ 79 writer_args.type = mtool->args.type; 80 writer_args.triangles_count = (long)mtool->desc.triangles_count; 81 82 if((res = sstl_writer_create(&writer_args, &writer)) != RES_OK) goto error; 83 84 exit: 85 *out_writer = writer; 86 return res; 87 error: 88 if(writer) { SSTL(writer_ref_put(writer)); writer = NULL; } 89 goto exit; 90 } 91 92 static res_T 93 load(struct mtool* mtool, const struct mtool_args* args) 94 { 95 res_T res = RES_OK; 96 ASSERT(mtool && args); 97 98 res = sstl_create(NULL, NULL, args->verbose, &mtool->sstl); 99 if(res != RES_OK) goto error; 100 101 if(args->input) { 102 /* Load data from the provided input file */ 103 if((res = sstl_load(mtool->sstl, args->input)) != RES_OK) goto error; 104 105 } else { 106 /* Data are loaded from stdin. The StL type must be defined because the 107 * standard input is not searchable. */ 108 switch(args->type) { 109 case SSTL_ASCII: 110 res = sstl_load_stream_ascii(mtool->sstl, stdin, "stdin (ASCII)"); 111 break; 112 case SSTL_BINARY: 113 res = sstl_load_stream_binary(mtool->sstl, stdin, "stdin (binary)"); 114 break; 115 default: FATAL("Unreachable code\n"); break; 116 } 117 if(res != RES_OK) goto error; 118 } 119 120 if((res = sstl_get_desc(mtool->sstl, &mtool->desc)) != RES_OK) goto error; 121 122 exit: 123 return res; 124 error: 125 if(mtool->sstl) { SSTL(ref_put(mtool->sstl)); mtool->sstl = NULL; } 126 goto exit; 127 } 128 129 static res_T 130 transform_mesh(struct mtool* mtool) 131 { 132 struct sstl_writer* writer = NULL; 133 size_t itri = 0; 134 res_T res = RES_OK; 135 136 if((res = create_writer(mtool, &writer)) != RES_OK) goto error; 137 138 FOR_EACH(itri, 0, mtool->desc.triangles_count) { 139 struct sstl_facet facet = SSTL_FACET_NULL; 140 141 /* Get the facet */ 142 res = sstl_desc_get_facet(&mtool->desc, itri, &facet); 143 if(res != RES_OK) goto error; 144 145 /* Multiply the vertices by the transformation matrix constructed from the 146 * transformations submitted on the command line */ 147 #define TRANSFORM(Vertex) { \ 148 double vec[3] = {0,0,0}; \ 149 d3_set_f3(vec, Vertex); \ 150 d33_muld3(vec, mtool->args.transform, vec); \ 151 d3_add(vec, mtool->args.transform+9, vec); \ 152 f3_set_d3(Vertex, vec); \ 153 } (void) 0 154 TRANSFORM(facet.vertices[0]); 155 TRANSFORM(facet.vertices[1]); 156 TRANSFORM(facet.vertices[2]); 157 #undef TRANSFORM 158 159 if(mtool->args.reverse_normals) { 160 /* Swap the first two vertices to flip the orientation of the triangle */ 161 SWAP(float, facet.vertices[0][0], facet.vertices[1][0]); 162 SWAP(float, facet.vertices[0][1], facet.vertices[1][1]); 163 SWAP(float, facet.vertices[0][2], facet.vertices[1][2]); 164 165 /* Reverse the normal even if, in reality, Star-StL does not provide it: 166 * it will be automatically calculated during writing from the vertices of 167 * the facet. However, this may not always be true, hence the explicit 168 * reversal of this normal, which allows for robustness against possible 169 * future changes without adding significant computational cost */ 170 f3_minus(facet.normal, facet.normal); 171 } 172 173 /* Write the facet */ 174 if((res = sstl_write_facet(writer, &facet)) != RES_OK) goto error; 175 } 176 177 exit: 178 if(writer) SSTL(writer_ref_put(writer)); 179 return res; 180 error: 181 goto exit; 182 } 183 184 /******************************************************************************* 185 * Local functions 186 ******************************************************************************/ 187 res_T 188 mtool_init(struct mtool* mtool, const struct mtool_args* args) 189 { 190 res_T res = RES_OK; 191 ASSERT(mtool && args); 192 193 *mtool = MTOOL_NULL; 194 195 /* Load the StL */ 196 if((res = load(mtool, args)) != RES_OK) goto error; 197 mtool->args = *args; 198 199 exit: 200 return res; 201 error: 202 mtool_release(mtool); 203 goto exit; 204 } 205 206 void 207 mtool_release(struct mtool* mtool) 208 { 209 ASSERT(mtool); 210 if(mtool->sstl) SSTL(ref_put(mtool->sstl)); 211 mtool_args_release(&mtool->args); 212 } 213 214 res_T 215 mtool_run(struct mtool* mtool) 216 { 217 res_T res = RES_OK; 218 219 if(mtool->args.print_desc) { 220 print_description(&mtool->desc); 221 222 } else { 223 res = transform_mesh(mtool); 224 if(res != RES_OK) goto error; 225 } 226 227 exit: 228 return res; 229 error: 230 goto exit; 231 }