sstl.c (7811B)
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.h" 19 #include "sstl_c.h" 20 21 #include <rsys/cstr.h> 22 #include <rsys/rsys.h> 23 #include <rsys/float3.h> 24 #include <rsys/stretchy_array.h> 25 26 #include <errno.h> 27 #include <stdio.h> 28 29 /******************************************************************************* 30 * Helper functions 31 ******************************************************************************/ 32 static res_T 33 file_type 34 (struct sstl* sstl, 35 FILE* fp, 36 const char* name, 37 enum sstl_type* type) 38 { 39 char buf[1024]; 40 size_t sz = 0; 41 long fpos = 0; 42 res_T res = RES_OK; 43 44 ASSERT(sstl && fp && name && type); 45 46 if(!file_is_seekable(fp)) { 47 ERROR(sstl, 48 "%s: the file refers to a pipe, a FIFO or a socket. " 49 "Its type (i.e. ASCII or binary) cannot be defined on the fly.\n", 50 name); 51 res = RES_BAD_ARG; 52 goto error; 53 } 54 55 if((fpos = ftell(fp)) < 0) { 56 ERROR(sstl, "%s: unable to query file position -- %s\n", 57 name, strerror(errno)); 58 res = RES_IO_ERR; 59 goto error; 60 } 61 62 /* Search for the NUL character in the first bytes of the file. If there is 63 * one, the file is assumed to be binary. This is a 'simple and stupid', yet 64 * robust method, used for example by some grep implementations */ 65 sz = fread(buf, 1, sizeof(buf), fp); 66 if(memchr(buf, '\0', sz)) { 67 *type = SSTL_BINARY; 68 } else { 69 *type = SSTL_ASCII; 70 } 71 72 if(fseek(fp, fpos, SEEK_SET) < 0) { 73 ERROR(sstl, "%s: unable to set file position -- %s\n", 74 name, strerror(errno)); 75 res = RES_IO_ERR; 76 goto error; 77 } 78 79 exit: 80 return res; 81 error: 82 goto exit; 83 } 84 85 static res_T 86 load_stream 87 (struct sstl* sstl, 88 FILE* fp, 89 const char* name, 90 enum sstl_type type) 91 { 92 res_T res = RES_OK; 93 ASSERT((unsigned)type <= SSTL_NONE__); 94 95 if(!sstl || !fp || !name) { res = RES_BAD_ARG; goto error; } 96 97 if(type == SSTL_NONE__) { 98 res = file_type(sstl, fp, name, &type); 99 if(res != RES_OK) goto error; 100 } 101 102 if((res = str_set(&sstl->filename, name)) != RES_OK) { 103 ERROR(sstl, "Error copying file name '%s' -- %s\n", 104 name, res_to_cstr(res)); 105 goto error; 106 } 107 108 switch(type) { 109 case SSTL_ASCII: res = load_stream_ascii(sstl, fp, name); break; 110 case SSTL_BINARY: res = load_stream_binary(sstl, fp, name); break; 111 default: FATAL("Unreachable code\n"); break; 112 } 113 if(res != RES_OK) goto error; 114 115 exit: 116 return res; 117 error: 118 goto exit; 119 } 120 121 static res_T 122 load(struct sstl* sstl, const char* filename, const enum sstl_type type) 123 { 124 FILE* stream = NULL; 125 res_T res = RES_OK; 126 127 ASSERT((unsigned)type <= SSTL_NONE__); 128 129 if(!sstl || !filename) { res = RES_BAD_ARG; goto error; } 130 131 stream = fopen(filename, "r"); 132 if(!stream) { 133 ERROR(sstl, "Error opening file %s -- %s\n", filename, strerror(errno)); 134 res = RES_IO_ERR; 135 goto error; 136 } 137 138 res = load_stream(sstl, stream, filename, type); 139 if(res != RES_OK) goto error; 140 141 exit: 142 if(stream) CHK(fclose(stream) == 0); 143 return res; 144 error: 145 goto exit; 146 } 147 148 static void 149 sstl_release(ref_T* ref) 150 { 151 struct sstl* sstl; 152 ASSERT(ref); 153 sstl = CONTAINER_OF(ref, struct sstl, ref); 154 str_release(&sstl->filename); 155 str_release(&sstl->name); 156 htable_vertex_release(&sstl->vertex2id); 157 sa_release(sstl->vertices); 158 sa_release(sstl->normals); 159 sa_release(sstl->indices); 160 MEM_RM(sstl->allocator, sstl); 161 } 162 163 /******************************************************************************* 164 * Local functions 165 ******************************************************************************/ 166 res_T 167 register_vertex(struct sstl* sstl, const float v[3]) 168 { 169 struct vertex vtx; 170 unsigned* found = NULL; 171 unsigned id = 0; 172 res_T res = RES_OK; 173 ASSERT(sstl && v); 174 175 /* Check if the input vertex is already registered */ 176 f3_set(vtx.xyz, v); 177 found = htable_vertex_find(&sstl->vertex2id, &vtx); 178 179 if(found) { /* The vertex already exists */ 180 id = *found; 181 182 } else { /* The vertex is a new one */ 183 id = (unsigned)sa_size(sstl->vertices)/3; 184 res = htable_vertex_set(&sstl->vertex2id, &vtx, &id); 185 if(res != RES_OK) goto error; 186 187 /* Add a new vertex */ 188 f3_set(sa_add(sstl->vertices, 3), vtx.xyz); 189 } 190 191 /* Register the vertex index */ 192 sa_push(sstl->indices, id); 193 194 exit: 195 return res; 196 error: 197 goto exit; 198 } 199 200 /******************************************************************************* 201 * Exported functions 202 ******************************************************************************/ 203 res_T 204 sstl_create 205 (struct logger* log, 206 struct mem_allocator* mem_allocator, 207 const int verbose, 208 struct sstl** out_sstl) 209 { 210 struct mem_allocator* allocator = NULL; 211 struct logger* logger = NULL; 212 struct sstl* sstl = NULL; 213 res_T res = RES_OK; 214 215 if(!out_sstl) { 216 res = RES_BAD_ARG; 217 goto error; 218 } 219 220 allocator = mem_allocator ? mem_allocator : &mem_default_allocator; 221 logger = log ? log : LOGGER_DEFAULT; 222 223 sstl = MEM_CALLOC(allocator, 1, sizeof(struct sstl)); 224 if(!sstl) { 225 if(verbose) { 226 logger_print(logger, LOG_ERROR, 227 "Couldn't allocate the Star-STL device.\n"); 228 } 229 res = RES_MEM_ERR; 230 goto error; 231 } 232 233 ref_init(&sstl->ref); 234 sstl->allocator = allocator; 235 sstl->logger = logger; 236 sstl->verbose = verbose; 237 htable_vertex_init(allocator, &sstl->vertex2id); 238 str_init(allocator, &sstl->filename); 239 str_init(allocator, &sstl->name); 240 241 exit: 242 if(out_sstl) *out_sstl = sstl; 243 return res; 244 error: 245 if(sstl) { 246 SSTL(ref_put(sstl)); 247 sstl = NULL; 248 } 249 goto exit; 250 } 251 252 res_T 253 sstl_ref_get(struct sstl* sstl) 254 { 255 if(!sstl) return RES_BAD_ARG; 256 ref_get(&sstl->ref); 257 return RES_OK; 258 } 259 260 res_T 261 sstl_ref_put(struct sstl* sstl) 262 { 263 if(!sstl) return RES_BAD_ARG; 264 ref_put(&sstl->ref, sstl_release); 265 return RES_OK; 266 } 267 268 res_T 269 sstl_load(struct sstl* sstl, const char* filename) 270 { 271 return load(sstl, filename, SSTL_NONE__); 272 } 273 274 res_T 275 sstl_load_ascii(struct sstl* sstl, const char* filename) 276 { 277 return load(sstl, filename, SSTL_ASCII); 278 } 279 280 res_T 281 sstl_load_binary(struct sstl* sstl, const char* filename) 282 { 283 return load(sstl, filename, SSTL_BINARY); 284 } 285 286 res_T 287 sstl_load_stream(struct sstl* sstl, FILE* fp, const char* name) 288 { 289 return load_stream(sstl, fp, name, SSTL_NONE__); 290 } 291 292 res_T 293 sstl_load_stream_ascii(struct sstl* sstl, FILE* fp, const char* name) 294 { 295 return load_stream(sstl, fp, name, SSTL_ASCII); 296 } 297 298 res_T 299 sstl_load_stream_binary(struct sstl* sstl, FILE* fp, const char* name) 300 { 301 return load_stream(sstl, fp, name, SSTL_BINARY); 302 } 303 304 res_T 305 sstl_get_desc(struct sstl* sstl, struct sstl_desc* desc) 306 { 307 if(!sstl || !desc) return RES_BAD_ARG; 308 309 ASSERT(sa_size(sstl->vertices) % 3 == 0); 310 ASSERT(sa_size(sstl->normals) % 3 == 0); 311 ASSERT(sa_size(sstl->indices) % 3 == 0); 312 313 desc->filename = str_cget(&sstl->filename); 314 desc->solid_name = str_len(&sstl->name) ? str_cget(&sstl->name) : NULL; 315 desc->vertices_count = sa_size(sstl->vertices) / 3/*#coords*/; 316 desc->triangles_count = sa_size(sstl->indices) / 3/*#ids*/; 317 desc->vertices = sstl->vertices; 318 desc->indices = sstl->indices; 319 desc->normals = sstl->normals; 320 desc->type = sstl->type; 321 return RES_OK; 322 }