aw_mtl.c (22799B)
1 /* Copyright (C) 2014-2017, 2020-2023 Vincent Forest (vaplv@free.fr) 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 "aw_c.h" 19 20 #include <rsys/cstr.h> 21 #include <rsys/double3.h> 22 #include <rsys/logger.h> 23 #include <rsys/mem_allocator.h> 24 #include <rsys/ref_count.h> 25 #include <rsys/str.h> 26 #include <rsys/text_reader.h> 27 28 #include <float.h> 29 30 #ifdef COMPILER_CL 31 #pragma warning(push) 32 #pragma warning(disable:4706) /* Assignment within a condition */ 33 #endif 34 35 static const char* MSG_PREFIX_INFO = "load-mtl:info: "; 36 static const char* MSG_PREFIX_ERROR = "load-mtl:error: "; 37 static const char* MSG_PREFIX_WARNING = "load-mtl:warning: "; 38 39 enum map_type { 40 MAP_COMMON = BIT(0), 41 MAP_SCALAR = BIT(1), 42 MAP_BUMP = MAP_SCALAR | BIT(2) 43 }; 44 45 /******************************************************************************* 46 * Map data structure 47 ******************************************************************************/ 48 struct map { 49 struct str filename; /* str_len == 0 <=> Not defined */ 50 int options_mask; 51 double image_bias; /* Scalar to add to the image pixels */ 52 double image_scale; /* Scalar to multiply to the image pixels */ 53 double texcoord_bias[3]; 54 double texcoord_scale[3]; 55 double texcoord_turbulence[3]; 56 size_t resolution; /* image size = resolution x resolution */ 57 enum aw_map_channel scalar; /* Channel used to create a scalar texture */ 58 double bump_multiplier; /* Only available on bump maps */ 59 }; 60 61 static INLINE void 62 map_init(struct mem_allocator* allocator, struct map* map) 63 { 64 ASSERT(map); 65 str_init(allocator, &map->filename); 66 map->options_mask = 0; 67 map->image_bias = 0.f; 68 map->image_scale = 1.f; 69 d3_splat(map->texcoord_bias, 0.f); 70 d3_splat(map->texcoord_scale, 1.f); 71 d3_splat(map->texcoord_turbulence, 0.f); 72 map->resolution = 0; 73 map->scalar = AW_MAP_CHANNEL_LUMINANCE; 74 map->bump_multiplier = 1.0f; 75 } 76 77 static INLINE void 78 map_release(struct map* map) 79 { 80 ASSERT(map); 81 str_release(&map->filename); 82 } 83 84 static INLINE void 85 map_copy_pod(struct map* dst, const struct map* src) 86 { 87 ASSERT(dst && src); 88 dst->options_mask = src->options_mask; 89 dst->image_bias = src->image_bias; 90 dst->image_scale = src->image_scale; 91 d3_set(dst->texcoord_bias, src->texcoord_bias); 92 d3_set(dst->texcoord_scale, src->texcoord_scale); 93 d3_set(dst->texcoord_turbulence, src->texcoord_turbulence); 94 dst->resolution = src->resolution; 95 dst->scalar = src->scalar; 96 dst->bump_multiplier = src->bump_multiplier; 97 } 98 99 static INLINE res_T 100 map_copy(struct map* dst, const struct map* src) 101 { 102 map_copy_pod(dst, src); 103 return str_copy(&dst->filename, &src->filename); 104 } 105 106 static INLINE res_T 107 map_copy_and_release(struct map* dst, struct map* src) 108 { 109 map_copy_pod(dst, src); 110 return str_copy_and_release(&dst->filename, &src->filename); 111 } 112 113 static INLINE void 114 map_to_aw_map(const struct map* map, struct aw_map* aw_map) 115 { 116 ASSERT(map && aw_map); 117 aw_map->filename = str_is_empty(&map->filename) ? NULL : str_cget(&map->filename); 118 aw_map->options_mask = map->options_mask; 119 aw_map->image_bias = map->image_bias; 120 aw_map->image_scale = map->image_scale; 121 d3_set(aw_map->texcoord_bias, map->texcoord_bias); 122 d3_set(aw_map->texcoord_scale, map->texcoord_scale); 123 d3_set(aw_map->texcoord_turbulence, map->texcoord_turbulence); 124 aw_map->resolution = map->resolution; 125 aw_map->scalar = map->scalar; 126 aw_map->bump_multiplier = map->bump_multiplier; 127 } 128 129 /******************************************************************************* 130 * Material API 131 ******************************************************************************/ 132 struct material { 133 struct str name; 134 struct aw_color ambient; 135 struct aw_color diffuse; 136 struct aw_color specular; 137 struct aw_color transmission; 138 double specular_exponent; 139 double refraction_index; 140 size_t illumination_model; /* In [0, 10] */ 141 struct map ambient_map; 142 struct map diffuse_map; 143 struct map specular_map; 144 struct map specular_exponent_map; /* Scalar texture */ 145 struct map bump_map; /* Scalar texture with valid bump multiplier */ 146 }; 147 148 static INLINE void 149 material_init(struct mem_allocator* allocator, struct material* mtl) 150 { 151 ASSERT(mtl); 152 memset(mtl, 0, sizeof(struct aw_material)); 153 str_init(allocator, &mtl->name); 154 map_init(allocator, &mtl->ambient_map); 155 map_init(allocator, &mtl->diffuse_map); 156 map_init(allocator, &mtl->specular_map); 157 map_init(allocator, &mtl->specular_exponent_map); 158 map_init(allocator, &mtl->bump_map); 159 } 160 161 static INLINE void 162 material_release(struct material* mtl) 163 { 164 ASSERT(mtl); 165 str_release(&mtl->name); 166 map_release(&mtl->ambient_map); 167 map_release(&mtl->diffuse_map); 168 map_release(&mtl->specular_map); 169 map_release(&mtl->specular_exponent_map); 170 map_release(&mtl->bump_map); 171 } 172 173 static INLINE void 174 material_copy_pod(struct material* dst, const struct material* src) 175 { 176 ASSERT(dst && src); 177 dst->ambient = src->ambient; 178 dst->diffuse = src->diffuse; 179 dst->specular = src->specular; 180 dst->transmission = src->transmission; 181 dst->specular_exponent = src->specular_exponent; 182 dst->refraction_index = src->refraction_index; 183 dst->illumination_model = src->illumination_model; 184 } 185 186 static INLINE res_T 187 material_copy(struct material* dst, const struct material* src) 188 { 189 res_T res = RES_OK; 190 ASSERT(dst && src); 191 material_copy_pod(dst, src); 192 #define CALL(Func) if(RES_OK != (res = Func)) return res 193 CALL(str_copy(&dst->name, &src->name)); 194 CALL(map_copy(&dst->ambient_map, &src->ambient_map)); 195 CALL(map_copy(&dst->diffuse_map, &src->diffuse_map)); 196 CALL(map_copy(&dst->specular_map, &src->specular_map)); 197 CALL(map_copy(&dst->specular_exponent_map, &src->specular_exponent_map)); 198 CALL(map_copy(&dst->bump_map, &src->bump_map)); 199 #undef CALL 200 return RES_OK; 201 } 202 203 static INLINE res_T 204 material_copy_and_release(struct material* dst, struct material* src) 205 { 206 res_T res = RES_OK; 207 ASSERT(dst && src); 208 material_copy_pod(dst, src); 209 #define CALL(Func) if(RES_OK != (res = Func)) return res 210 CALL(str_copy_and_release(&dst->name, &src->name)); 211 CALL(map_copy_and_release(&dst->ambient_map, &src->ambient_map)); 212 CALL(map_copy_and_release(&dst->diffuse_map, &src->diffuse_map)); 213 CALL(map_copy_and_release(&dst->specular_map, &src->specular_map)); 214 CALL(map_copy_and_release 215 (&dst->specular_exponent_map, &src->specular_exponent_map)); 216 CALL(map_copy_and_release(&dst->bump_map, &src->bump_map)); 217 #undef CALL 218 return RES_OK; 219 } 220 221 /* Generate the darray_mtl data structure */ 222 #define DARRAY_NAME material 223 #define DARRAY_DATA struct material 224 #define DARRAY_FUNCTOR_INIT material_init 225 #define DARRAY_FUNCTOR_RELEASE material_release 226 #define DARRAY_FUNCTOR_COPY material_copy 227 #define DARRAY_FUNCTOR_COPY_AND_RELEASE material_copy_and_release 228 #include <rsys/dynamic_array.h> 229 230 /******************************************************************************* 231 * Definition of the aw_mtl opaque data structure 232 ******************************************************************************/ 233 struct aw_mtl { 234 struct darray_material materials; 235 struct material* newmtl; /* Pointer toward the current material */ 236 237 ref_T ref; 238 struct mem_allocator* allocator; 239 struct logger* logger; 240 struct logger logger__; /* Default logger */ 241 int verbose; 242 }; 243 244 /******************************************************************************* 245 * Helper functions 246 ******************************************************************************/ 247 static INLINE void 248 log_msg 249 (const struct aw_mtl* mtl, 250 const enum log_type stream, 251 const char* msg, 252 va_list vargs) 253 { 254 ASSERT(mtl && msg); 255 if(mtl->verbose) { 256 res_T res; (void)res; 257 res = logger_vprint(mtl->logger, stream, msg, vargs); 258 ASSERT(res == RES_OK); 259 } 260 } 261 262 static INLINE void 263 log_err(const struct aw_mtl* mtl, const char* msg, ...) 264 { 265 va_list vargs_list; 266 ASSERT(mtl && msg); 267 268 va_start(vargs_list, msg); 269 log_msg(mtl, LOG_ERROR, msg, vargs_list); 270 va_end(vargs_list); 271 } 272 273 static INLINE void 274 log_warn(const struct aw_mtl* mtl, const char* msg, ...) 275 { 276 va_list vargs_list; 277 ASSERT(mtl && msg); 278 279 va_start(vargs_list, msg); 280 log_msg(mtl, LOG_WARNING, msg, vargs_list); 281 va_end(vargs_list); 282 } 283 284 static res_T 285 parse_newmtl(struct aw_mtl* mtl, char** tk_ctx) 286 { 287 struct material* newmtl = NULL; 288 char* tk = NULL; 289 size_t nmtls; 290 res_T res = RES_OK; 291 ASSERT(mtl && tk_ctx); 292 293 tk = strtok_r(NULL, " \t", tk_ctx); /* Blanks are prohibited in mtl name */ 294 if(!tk) { 295 res = RES_BAD_ARG; 296 goto error; 297 } 298 299 nmtls = darray_material_size_get(&mtl->materials); 300 301 /* Allocate the new material */ 302 res = darray_material_resize(&mtl->materials, nmtls + 1); 303 if(res != RES_OK) goto error; 304 305 /* Fetch the new material */ 306 newmtl = darray_material_data_get(&mtl->materials) + nmtls; 307 308 /* Setup the material name */ 309 res = str_set(&newmtl->name, tk); 310 if(res != RES_OK) goto error; 311 312 /* Set the new material as the material to parse */ 313 mtl->newmtl = newmtl; 314 315 exit: 316 return RES_OK; 317 error: 318 if(newmtl) darray_material_pop_back(&mtl->materials); 319 goto exit; 320 } 321 322 static res_T 323 parse_color(struct aw_mtl* mtl, struct aw_color* col, char** tk_ctx) 324 { 325 char* tk = NULL; 326 res_T res = RES_OK; 327 ASSERT(mtl && col && tk_ctx); 328 329 tk = strtok_r(NULL, " \t", tk_ctx); 330 if(!tk) { 331 res = RES_BAD_ARG; 332 goto error; 333 } 334 335 if(!strcmp(tk, "spectral")) { 336 log_err(mtl, "spectral colors are not supported\n"); 337 res = RES_BAD_ARG; 338 goto error; 339 } 340 341 if(strcmp(tk, "xyz")) { 342 col->color_space = AW_COLOR_RGB; 343 } else { 344 col->color_space = AW_COLOR_XYZ; 345 tk = strtok_r(NULL, " \t", tk_ctx); 346 if(!tk) { 347 res = RES_BAD_ARG; 348 goto error; 349 } 350 } 351 352 res = cstr_to_double(tk, &col->value[0]); 353 if(res != RES_OK) goto error; 354 355 /* If only the first component is defined the second and third components are 356 * assumed to be equal to the first one */ 357 tk = strtok_r(NULL, " \t", tk_ctx); 358 if(!tk) { 359 col->value[1] = col->value[0]; 360 col->value[2] = col->value[0]; 361 } else { 362 res = cstr_to_double(tk, &col->value[1]); 363 if(res != RES_OK) goto error; 364 365 tk = strtok_r(NULL, " \t", tk_ctx); 366 if(!tk) { res = RES_BAD_ARG; goto error; } 367 368 res = cstr_to_double(tk, &col->value[2]); 369 if(res == RES_OK) goto error; 370 } 371 372 exit: 373 return res; 374 error: 375 goto exit; 376 } 377 378 static INLINE res_T 379 parse_size_t 380 (size_t* s, const size_t range_min, const size_t range_max, char** tk_ctx) 381 { 382 unsigned long ul; 383 char* tk = NULL; 384 res_T res = RES_OK; 385 ASSERT(s && tk_ctx && range_min < range_max); 386 387 tk = strtok_r(NULL, "\n", tk_ctx); 388 if(!tk) return res; 389 390 res = cstr_to_ulong(tk, &ul); 391 if(res != RES_OK) return res; 392 if((unsigned long)range_min > ul || (unsigned long)range_max < ul) return res; 393 *s = ul; 394 return RES_OK; 395 } 396 397 static res_T 398 parse_bool_option 399 (int* options_mask, 400 const enum aw_map_flag option, 401 char** tk_ctx) 402 { 403 char* tk; 404 ASSERT(options_mask && tk_ctx); 405 406 tk = strtok_r(NULL, " \t", tk_ctx); 407 if(!tk) return RES_BAD_ARG; 408 409 if(!strcmp(tk, "on")) { 410 *options_mask |= (int)option; 411 } else if(!strcmp(tk, "off")) { 412 *options_mask &= ~((int)option); 413 } else { 414 return RES_BAD_ARG; 415 } 416 return RES_OK; 417 } 418 419 static FINLINE res_T 420 parse_imfchan_option(enum aw_map_channel* channel, char** tk_ctx) 421 { 422 char* tk = NULL; 423 ASSERT(channel && tk_ctx); 424 425 tk = strtok_r(NULL, " \t", tk_ctx); 426 if(!tk) return RES_BAD_ARG; 427 428 if(!strcmp(tk, "r")) { 429 *channel = AW_MAP_CHANNEL_RED; 430 } else if(!strcmp(tk, "g")) { 431 *channel = AW_MAP_CHANNEL_GREEN; 432 } else if(!strcmp(tk, "b")) { 433 *channel = AW_MAP_CHANNEL_BLUE; 434 } else if(!strcmp(tk, "m")) { 435 *channel = AW_MAP_CHANNEL_MATTE; 436 } else if(!strcmp(tk, "l")) { 437 *channel = AW_MAP_CHANNEL_LUMINANCE; 438 } else if(!strcmp(tk, "z")) { 439 *channel = AW_MAP_CHANNEL_DEPTH; 440 } else { 441 return RES_BAD_ARG; 442 } 443 return RES_OK; 444 } 445 446 static res_T 447 parse_map(struct map* map, const enum map_type type, char** tk_ctx) 448 { 449 char* tk = NULL; 450 res_T res = RES_OK; 451 ASSERT(map && tk_ctx); 452 453 for(;;) { 454 tk = strtok_r(NULL, " \t", tk_ctx); 455 if(!tk) { 456 res = RES_BAD_ARG; 457 goto error; 458 } 459 460 if(!strcmp(tk, "-blendu")) { 461 res = parse_bool_option(&map->options_mask, AW_MAP_BLEND_U, tk_ctx); 462 if(res != RES_OK) goto error; 463 } else if(!strcmp(tk, "-blendv")) { 464 res = parse_bool_option(&map->options_mask, AW_MAP_BLEND_V, tk_ctx); 465 if(res != RES_OK) goto error; 466 } else if(!strcmp(tk, "-cc")) { 467 res = parse_bool_option 468 (&map->options_mask, AW_MAP_COLOR_CORRECTION, tk_ctx); 469 if(res != RES_OK) goto error; 470 } else if(!strcmp(tk, "-clamp")) { 471 res = parse_bool_option(&map->options_mask, AW_MAP_CLAMP, tk_ctx); 472 if(res != RES_OK) goto error; 473 } else if(!strcmp(tk, "-imfchan") && (type & MAP_SCALAR)) { 474 res = parse_imfchan_option(&map->scalar, tk_ctx); 475 if(res != RES_OK) goto error; 476 } else if(!strcmp(tk, "-mm")) { /* Image bias and scale */ 477 res = parse_doubleX 478 (&map->image_bias, 1, 1, -DBL_MAX, DBL_MAX, 0.f, tk_ctx); 479 if(res != RES_OK) goto error; 480 res = parse_doubleX 481 (&map->image_scale, 1, 1, -DBL_MAX, DBL_MAX, 0.f, tk_ctx); 482 if(res != RES_OK) goto error; 483 } else if(!strcmp(tk, "-o")) { /* Texcoord offset */ 484 res = parse_doubleX 485 (map->texcoord_bias, 1, 3, -DBL_MAX, DBL_MAX, 0.f, tk_ctx); 486 if(res != RES_OK) goto error; 487 } else if(!strcmp(tk, "-s")) { /* Texcoord scale */ 488 res = parse_doubleX 489 (map->texcoord_scale, 1, 3, -DBL_MAX, DBL_MAX, 1.f, tk_ctx); 490 if(res != RES_OK) goto error; 491 } else if(!strcmp(tk, "-t")) { /* Texcoord turbulence */ 492 res = parse_doubleX 493 (map->texcoord_turbulence, 1, 3, -DBL_MAX, DBL_MAX, 0.f, tk_ctx); 494 if(res != RES_OK) goto error; 495 } else if(!strcmp(tk, "-texres")) { /* Texture resolution */ 496 res = parse_size_t(&map->resolution, 1, SIZE_MAX, tk_ctx); 497 if(res != RES_OK) goto error; 498 } else if(!strcmp(tk, "-bm") && (type == MAP_BUMP)) {/* Bump multiplier */ 499 res = parse_doubleX 500 (&map->bump_multiplier, 1, 1, -DBL_MAX, DBL_MAX, 0.f, tk_ctx); 501 if(res != RES_OK) goto error; 502 } else { /* Map filename */ 503 res = str_set(&map->filename, tk); 504 if(res != RES_OK) goto error; 505 tk = strtok_r(NULL, "", tk_ctx); 506 if(tk) { 507 res = str_append_char(&map->filename, ' '); 508 if(res != RES_OK) goto error; 509 res = str_append(&map->filename, strtok_r(NULL, "", tk_ctx)); 510 if(res != RES_OK) goto error; 511 } 512 break; /* The map is fully parsed */ 513 } 514 } 515 516 exit: 517 return res; 518 error: 519 goto exit; 520 } 521 522 static res_T 523 parse_mtl_line(struct aw_mtl* mtl, struct txtrdr* txtrdr) 524 { 525 char* tk = NULL; 526 char* tk_ctx = NULL; 527 res_T res = RES_OK; 528 ASSERT(mtl && txtrdr); 529 530 tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx); 531 ASSERT(tk); /* A line should exist since it was parsed by txtrdr */ 532 533 if(!strcmp(tk, "newmtl")) { /* Material name declaration */ 534 res = parse_newmtl(mtl, &tk_ctx); 535 } else if(mtl->newmtl == NULL) { 536 res = RES_BAD_ARG; 537 } else if(!strcmp(tk, "Ka")) { /* Ambient reflectivity */ 538 res = parse_color(mtl, &mtl->newmtl->ambient, &tk_ctx); 539 } else if(!strcmp(tk, "Kd")) { /* Diffuse reflectivity */ 540 res = parse_color(mtl, &mtl->newmtl->diffuse, &tk_ctx); 541 } else if(!strcmp(tk, "Ks")) { /* Specular reflectivity */ 542 res = parse_color(mtl, &mtl->newmtl->specular, &tk_ctx); 543 } else if(!strcmp(tk, "Tf")) { /* Transimission filter */ 544 res = parse_color(mtl, &mtl->newmtl->transmission, &tk_ctx); 545 } else if(!strcmp(tk, "Ns")) { /* Specular exponent */ 546 res = parse_doubleX 547 (&mtl->newmtl->specular_exponent, 1, 1, 0, DBL_MAX, 0, &tk_ctx); 548 } else if(!strcmp(tk, "Ni")) { /* Refraction index */ 549 res = parse_doubleX 550 (&mtl->newmtl->refraction_index, 1, 1, 0.001, 10, 0.001, &tk_ctx); 551 } else if(!strcmp(tk, "illum")) { /* Illumination model */ 552 res = parse_size_t(&mtl->newmtl->illumination_model, 0, 10, &tk_ctx); 553 } else if(!strcmp(tk, "map_Ka")) { /* Ambient texture */ 554 res = parse_map(&mtl->newmtl->ambient_map, MAP_COMMON, &tk_ctx); 555 } else if(!strcmp(tk, "map_Kd")) { /* Diffuse texture */ 556 res = parse_map(&mtl->newmtl->diffuse_map, MAP_COMMON, &tk_ctx); 557 } else if(!strcmp(tk, "map_Ks")) { /* Specular texture */ 558 res = parse_map(&mtl->newmtl->specular_map, MAP_COMMON, &tk_ctx); 559 } else if(!strcmp(tk, "map_Ns")) { /* Specular exponent texture */ 560 res = parse_map 561 (&mtl->newmtl->specular_exponent_map, MAP_SCALAR, &tk_ctx); 562 } else if(!strcmp(tk, "bump") || !strcmp(tk, "map_bump")) { /* Bump map */ 563 res = parse_map(&mtl->newmtl->bump_map, MAP_BUMP, &tk_ctx); 564 } else { 565 log_warn(mtl, 566 "%s:%lu: warning: ignored or malformed directive `%s'\n", 567 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); 568 strtok_r(NULL, "", &tk_ctx); /* Discard remaining text */ 569 } 570 if(res != RES_OK) { 571 log_err(mtl, "%s:%lu: parsing failed.\n", 572 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); 573 goto error; 574 } 575 576 tk = strtok_r(NULL, "", &tk_ctx); 577 if(tk) { 578 log_err(mtl, "%s:%lu: unexpected text `%s'.\n", 579 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk); 580 res = RES_BAD_ARG; 581 goto error; 582 } 583 584 exit: 585 return res; 586 error: 587 goto exit; 588 } 589 590 static res_T 591 load_stream(struct aw_mtl* mtl, FILE* stream, const char* stream_name) 592 { 593 struct txtrdr* txtrdr = NULL; 594 res_T res = RES_OK; 595 ASSERT(mtl && stream && stream_name); 596 597 res = txtrdr_stream(mtl->allocator, stream, stream_name, '#', &txtrdr); 598 if(res != RES_OK) { 599 log_err(mtl, "Could not create the text reader.\n"); 600 goto error; 601 } 602 603 for(;;) { 604 res = txtrdr_read_line(txtrdr); 605 if(res != RES_OK) { 606 log_err(mtl, "%s: could not read the line `%lu'.\n", 607 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr)); 608 goto error; 609 } 610 611 if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */ 612 613 res = parse_mtl_line(mtl, txtrdr); 614 if(res != RES_OK) goto error; 615 } 616 617 exit: 618 if(txtrdr) txtrdr_ref_put(txtrdr); 619 return res; 620 error: 621 AW(mtl_clear(mtl)); 622 goto exit; 623 } 624 625 static void 626 mtl_release(ref_T* ref) 627 { 628 struct aw_mtl* mtl = CONTAINER_OF(ref, struct aw_mtl, ref); 629 ASSERT(ref); 630 darray_material_release(&mtl->materials); 631 if(mtl->logger == &mtl->logger__) logger_release(&mtl->logger__); 632 MEM_RM(mtl->allocator, mtl); 633 } 634 635 /******************************************************************************* 636 * Exported functions 637 ******************************************************************************/ 638 res_T 639 aw_mtl_create 640 (struct logger* logger, 641 struct mem_allocator* mem_allocator, 642 const int verbose, 643 struct aw_mtl** mtl_out) 644 { 645 struct mem_allocator* allocator; 646 struct aw_mtl* mtl = NULL; 647 res_T res = RES_OK; 648 649 if(!mtl_out) { 650 res = RES_BAD_ARG; 651 goto error; 652 } 653 allocator = mem_allocator ? mem_allocator : &mem_default_allocator; 654 mtl = MEM_CALLOC(allocator, 1, sizeof(struct aw_mtl)); 655 if(!mtl) { 656 res = RES_MEM_ERR; 657 goto error; 658 } 659 ref_init(&mtl->ref); 660 mtl->allocator = allocator; 661 mtl->verbose = verbose; 662 darray_material_init(allocator, &mtl->materials); 663 664 if(logger) { 665 mtl->logger = logger; 666 } else { 667 res = setup_default_logger(mtl->allocator, &mtl->logger__, 668 MSG_PREFIX_INFO, MSG_PREFIX_ERROR, MSG_PREFIX_WARNING); 669 if(res != RES_OK) goto error; 670 mtl->logger = &mtl->logger__; 671 } 672 673 exit: 674 if(mtl_out) *mtl_out = mtl; 675 return res; 676 error: 677 if(mtl) { 678 AW(mtl_ref_put(mtl)); 679 mtl = NULL; 680 } 681 goto exit; 682 } 683 684 res_T 685 aw_mtl_ref_get(struct aw_mtl* mtl) 686 { 687 if(!mtl) return RES_BAD_ARG; 688 ref_get(&mtl->ref); 689 return RES_OK; 690 } 691 692 res_T 693 aw_mtl_ref_put(struct aw_mtl* mtl) 694 { 695 if(!mtl) return RES_BAD_ARG; 696 ref_put(&mtl->ref, mtl_release); 697 return RES_OK; 698 } 699 700 res_T 701 aw_mtl_load(struct aw_mtl* mtl, const char* filename) 702 { 703 FILE* file = NULL; 704 res_T res = RES_OK; 705 706 if(!mtl || !filename) { 707 res = RES_BAD_ARG; 708 goto error; 709 } 710 711 file = fopen(filename, "r"); 712 if(!file) { 713 log_err(mtl, "Error opening `%s'\n", filename); 714 res = RES_IO_ERR; 715 goto error; 716 } 717 718 res = load_stream(mtl, file, filename); 719 if(res != RES_OK) goto error; 720 721 exit: 722 if(file) fclose(file); 723 return res; 724 error: 725 goto exit; 726 } 727 728 res_T 729 aw_mtl_load_stream(struct aw_mtl* mtl, FILE* stream, const char* stream_name) 730 { 731 res_T res = RES_OK; 732 733 if(!mtl || !stream) { 734 res = RES_BAD_ARG; 735 goto error; 736 } 737 738 res = load_stream(mtl, stream, stream_name ? stream_name : "stream"); 739 if(res != RES_OK) goto error; 740 741 exit: 742 return res; 743 error: 744 goto exit; 745 } 746 747 res_T 748 aw_mtl_clear(struct aw_mtl* mtl) 749 { 750 if(!mtl) return RES_BAD_ARG; 751 darray_material_clear(&mtl->materials); 752 mtl->newmtl = NULL; 753 return RES_OK; 754 } 755 756 res_T 757 aw_mtl_purge(struct aw_mtl* mtl) 758 { 759 if(!mtl) return RES_BAD_ARG; 760 darray_material_purge(&mtl->materials); 761 mtl->newmtl = NULL; 762 return RES_OK; 763 } 764 765 res_T 766 aw_mtl_get_materials_count(struct aw_mtl* mtl, size_t* nmtls) 767 { 768 if(!mtl || !nmtls) 769 return RES_BAD_ARG; 770 *nmtls = darray_material_size_get(&mtl->materials); 771 return RES_OK; 772 } 773 774 res_T 775 aw_mtl_get_material 776 (struct aw_mtl* mtl, 777 const size_t imaterial, 778 struct aw_material* material) 779 { 780 const struct material* mat = NULL; 781 782 if(!mtl || !material || imaterial>=darray_material_size_get(&mtl->materials)) 783 return RES_BAD_ARG; 784 785 mat = darray_material_cdata_get(&mtl->materials) + imaterial; 786 material->name = str_cget(&mat->name); 787 material->ambient = mat->ambient; 788 material->diffuse = mat->diffuse; 789 material->specular = mat->specular; 790 material->transmission = mat->transmission; 791 material->specular_exponent = mat->specular_exponent; 792 material->refraction_index = mat->refraction_index; 793 material->illumination_model = mat->illumination_model; 794 map_to_aw_map(&mat->ambient_map, &material->ambient_map); 795 map_to_aw_map(&mat->diffuse_map, &material->diffuse_map); 796 map_to_aw_map(&mat->specular_map, &material->specular_map); 797 map_to_aw_map(&mat->specular_exponent_map, &material->specular_exponent_map); 798 map_to_aw_map(&mat->bump_map, &material->bump_map); 799 return RES_OK; 800 } 801 802 #ifdef COMPILER_CL 803 #pragma warning(pop) 804 #endif 805