sstl_ascii.c (7989B)
1 /* Copyright (C) 2015, 2016, 2019, 2021, 2023, 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 Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* strtok_r support */ 17 18 #include "sstl_c.h" 19 20 #include <rsys/cstr.h> 21 #include <rsys/stretchy_array.h> 22 #include <rsys/text_reader.h> 23 24 #include <string.h> 25 26 /******************************************************************************* 27 * Helper functions 28 ******************************************************************************/ 29 static res_T 30 parse_float3 31 (struct sstl* sstl, 32 const struct txtrdr* txtrdr, 33 char* str, 34 char** ctx, 35 float vec[3]) 36 { 37 char* x = NULL; 38 char* y = NULL; 39 char* z = NULL; 40 res_T res = RES_OK; 41 ASSERT(sstl && txtrdr && vec); 42 43 if(!(x = strtok_r(str, " \t", ctx)) 44 || !(y = strtok_r(NULL, " \t", ctx)) 45 || !(z = strtok_r(NULL, " \t", ctx))) { 46 res = RES_BAD_ARG; 47 goto error; 48 } 49 50 if((res = cstr_to_float(x, vec+0)) != RES_OK) goto error; 51 if((res = cstr_to_float(y, vec+1)) != RES_OK) goto error; 52 if((res = cstr_to_float(z, vec+2)) != RES_OK) goto error; 53 54 exit: 55 return res; 56 error: 57 ERROR(sstl, "%s:%lu: invalid vector component\n", 58 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); 59 goto exit; 60 } 61 62 static res_T 63 parse_word 64 (struct sstl* sstl, 65 const struct txtrdr* txtrdr, 66 const char* word, 67 char* str, 68 char** ctx) 69 { 70 char* tk = NULL; 71 ASSERT(sstl && txtrdr && ctx); 72 73 tk = strtok_r(str, " \t", ctx); 74 if(!tk || strcmp(tk, word) != 0) { 75 ERROR(sstl, "%s:%lu: expect the \"%s\" keyword\n", 76 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 77 word); 78 return RES_BAD_ARG; 79 } 80 81 return RES_OK; 82 } 83 84 static INLINE res_T 85 parse_nothing 86 (struct sstl* sstl, 87 const struct txtrdr* txtrdr, 88 char* str, 89 char** ctx) 90 { 91 char* tk = NULL; 92 93 ASSERT(txtrdr && ctx); 94 (void)sstl; /* Avoid "unused variable" warning */ 95 96 tk = strtok_r(str, " \t", ctx); 97 if(tk != NULL) { 98 WARN(sstl, "%s:%lu: unexpected text \"%s\"\n", 99 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); 100 } 101 return RES_OK; 102 } 103 104 static res_T 105 parse_vec3 106 (struct sstl* sstl, 107 struct txtrdr* txtrdr, 108 const char* name, 109 char* str, 110 char** ctx, 111 float vec[3]) 112 { 113 res_T res = RES_BAD_ARG; 114 ASSERT(sstl && txtrdr && name && ctx); 115 116 if((res = parse_word(sstl, txtrdr, name, str, ctx)) != RES_OK) goto error; 117 if((res = parse_float3(sstl, txtrdr, NULL, ctx, vec)) != RES_OK) goto error; 118 if((res = parse_nothing(sstl, txtrdr, NULL, ctx)) != RES_OK) goto error; 119 120 exit: 121 return res; 122 error: 123 goto exit; 124 } 125 126 static res_T 127 parse_facet(struct sstl* sstl, struct txtrdr* txtrdr, char* normal) 128 { 129 float v[3][3] = {0}; 130 float* N = NULL; 131 char* line = NULL; 132 char* ctx = NULL; 133 int i = 0; 134 res_T res = RES_OK; 135 ASSERT(sstl && txtrdr && normal); 136 137 N = sa_add(sstl->normals, 3); 138 if((res = parse_vec3(sstl, txtrdr, "normal", normal, &ctx, N)) != RES_OK) goto error; 139 140 #define READ_LINE { \ 141 if((res = txtrdr_read_line(txtrdr)) != RES_OK) { \ 142 ERROR(sstl, "%s: error reading line -- %s\n", \ 143 txtrdr_get_name(txtrdr), res_to_cstr(res)); \ 144 goto error; \ 145 } \ 146 if(!(line = txtrdr_get_line(txtrdr))) { \ 147 ERROR(sstl, "%s: unexpected end of file\n", txtrdr_get_name(txtrdr)); \ 148 res = RES_BAD_ARG; \ 149 goto error; \ 150 } \ 151 } (void)0 152 153 READ_LINE; 154 if((res = parse_word(sstl, txtrdr, "outer", line, &ctx)) != RES_OK) goto error; 155 if((res = parse_word(sstl, txtrdr, "loop", NULL, &ctx)) != RES_OK) goto error; 156 if((res = parse_nothing(sstl, txtrdr, NULL, &ctx)) != RES_OK) goto error; 157 158 FOR_EACH(i, 0, 3) { 159 READ_LINE; 160 res = parse_vec3(sstl, txtrdr, "vertex", line, &ctx, v[i]); 161 if(res != RES_OK) goto error; 162 163 res = register_vertex(sstl, v[i]); 164 if(res != RES_OK) { 165 ERROR(sstl, "%s:%lu: vertex registration error -- %s\n", 166 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 167 res_to_cstr(res)); 168 goto error; 169 } 170 } 171 172 READ_LINE; 173 if((res = parse_word(sstl, txtrdr, "endloop", line, &ctx)) != RES_OK) goto error; 174 if((res = parse_nothing(sstl, txtrdr, NULL, &ctx)) != RES_OK) goto error; 175 176 READ_LINE; 177 if((res = parse_word(sstl, txtrdr, "endfacet", line, &ctx)) != RES_OK) goto error; 178 if((res = parse_nothing(sstl, txtrdr, NULL, &ctx)) != RES_OK) goto error; 179 180 #undef READ_LINE 181 182 /* If necessary, automatically calculate the surface normal. */ 183 if(!f3_is_normalized(N)) calculate_normal(N, v[0], v[1], v[2]); 184 185 exit: 186 return res; 187 error: 188 goto exit; 189 } 190 191 static res_T 192 parse_solid(struct sstl* sstl, struct txtrdr* txtrdr) 193 { 194 char* line = NULL; 195 char* tk = NULL; 196 char* tk_ctx = NULL; 197 res_T res = RES_OK; 198 ASSERT(sstl && txtrdr); 199 200 line = txtrdr_get_line(txtrdr); 201 ASSERT(line != NULL); 202 203 tk = strtok_r(line, " \t", &tk_ctx); 204 ASSERT(tk); 205 if(strcmp(tk, "solid")) { 206 ERROR(sstl, "%s:%lu: the \"solid [name]\" directive is missing\n", 207 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); 208 res = RES_BAD_ARG; 209 goto error; 210 } 211 212 tk = strtok_r(NULL, "", &tk_ctx); 213 if(tk != NULL && (res = str_set(&sstl->name, tk)) != RES_OK) { 214 ERROR(sstl, "%s:%lu: error duplicating solid name -- %s\n", 215 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 216 res_to_cstr(res)); 217 goto error; 218 } 219 220 for(;;) { 221 if((res = txtrdr_read_line(txtrdr)) != RES_OK) { 222 ERROR(sstl, "%s: error reading line -- %s\n", 223 str_cget(&sstl->name), res_to_cstr(res)); 224 goto error; 225 } 226 227 if((line = txtrdr_get_line(txtrdr)) == NULL) { 228 ERROR(sstl, "%s: the \"endsolid [name]\" directive is missing\n", 229 txtrdr_get_name(txtrdr)); 230 res = RES_BAD_ARG; 231 goto error; 232 } 233 234 tk = strtok_r(line, " \t", &tk_ctx); 235 236 if(!strcmp(tk, "facet")) { 237 res = parse_facet(sstl, txtrdr, strtok_r(NULL, "", &tk_ctx)); 238 if(res != RES_OK) goto error; 239 240 } else if(!strcmp(tk, "endsolid")) { 241 break; /* Stop on "endsolid" directive */ 242 243 } else { 244 ERROR(sstl, "%s:%lu: invalid directive \"%s\"\n", 245 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); 246 res = RES_BAD_ARG; 247 goto error; 248 } 249 } 250 251 exit: 252 return res; 253 error: 254 goto exit; 255 } 256 257 /******************************************************************************* 258 * Local functions 259 ******************************************************************************/ 260 res_T 261 load_stream_ascii 262 (struct sstl* sstl, 263 FILE* stream, 264 const char* stream_name) 265 { 266 struct txtrdr* txtrdr = NULL; 267 res_T res = RES_OK; 268 269 ASSERT(sstl && stream && stream_name); 270 271 clear(sstl); 272 273 res = txtrdr_stream(sstl->allocator, stream, stream_name, '#', &txtrdr); 274 if(res != RES_OK) { 275 ERROR(sstl, "%s: error creating text reader -- %s\n", 276 stream_name, res_to_cstr(res)); 277 goto error; 278 } 279 280 if((res = txtrdr_read_line(txtrdr)) != RES_OK) { 281 ERROR(sstl, "%s: error reading line -- %s\n", stream_name, res_to_cstr(res)); 282 goto error; 283 } 284 285 if(txtrdr_get_cline(txtrdr) != NULL) { /* File is not empty */ 286 if((res = parse_solid(sstl, txtrdr)) != RES_OK) goto error; 287 } 288 289 sstl->type = SSTL_ASCII; 290 291 exit: 292 htable_vertex_purge(&sstl->vertex2id); /* Purge the helper structure */ 293 if(txtrdr) txtrdr_ref_put(txtrdr); 294 return res; 295 error: 296 clear(sstl); 297 goto exit; 298 }