solparser_material.c (23923B)
1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) 2 * Copyright (C) 2016-2018 CNRS 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #define _POSIX_C_SOURCE 200112L /* nextafter support */ 18 19 #include "solparser_c.h" 20 #include <math.h> /* nextafter */ 21 22 /******************************************************************************* 23 * Helper functions 24 ******************************************************************************/ 25 static res_T 26 parse_microfacet 27 (struct solparser* parser, 28 const yaml_node_t* microfacet, 29 enum solparser_microfacet_distribution* distrib) 30 { 31 res_T res = RES_OK; 32 ASSERT(microfacet && distrib); 33 34 if(microfacet->type != YAML_SCALAR_NODE) { 35 log_err(parser, microfacet, 36 "expect the name of a microfacet distribution.\n"); 37 res = RES_BAD_ARG; 38 goto error; 39 } 40 41 if(!strcmp((char*)microfacet->data.scalar.value, "BECKMANN")) { 42 *distrib = SOLPARSER_MICROFACET_BECKMANN; 43 } else if(!strcmp((char*)microfacet->data.scalar.value, "PILLBOX")) { 44 *distrib = SOLPARSER_MICROFACET_PILLBOX; 45 } else { 46 log_err(parser, microfacet, "unknown microfacet distribution `%s'.\n", 47 microfacet->data.scalar.value); 48 res = RES_BAD_ARG; 49 goto error; 50 } 51 exit: 52 return res; 53 error: 54 goto exit; 55 } 56 57 static res_T 58 parse_material_dielectric 59 (struct solparser* parser, 60 yaml_document_t* doc, 61 const yaml_node_t* dielec, 62 struct solparser_material_dielectric_id* out_imtl) 63 { 64 enum { MEDIUM_I, MEDIUM_T, NORMAL_MAP }; 65 struct solparser_material_dielectric* mtl = NULL; 66 size_t imtl = SIZE_MAX; 67 int mask = 0; /* Register the parsed attributes */ 68 intptr_t i, n; 69 res_T res = RES_OK; 70 ASSERT(doc && dielec && out_imtl); 71 72 if(dielec->type != YAML_MAPPING_NODE) { 73 log_err(parser, dielec, 74 "expect a mapping of dielec material attributes.\n"); 75 res = RES_BAD_ARG; 76 goto error; 77 } 78 79 /* Allocate the dielec material */ 80 imtl = darray_dielectric_size_get(&parser->dielectrics); 81 res = darray_dielectric_resize(&parser->dielectrics, imtl + 1); 82 if(res != RES_OK) { 83 log_err(parser, dielec, 84 "could not allocate the dielec material.\n"); 85 goto error; 86 } 87 mtl = darray_dielectric_data_get(&parser->dielectrics) + imtl; 88 89 n = dielec->data.mapping.pairs.top - dielec->data.mapping.pairs.start; 90 FOR_EACH(i, 0, n) { 91 yaml_node_t* key; 92 yaml_node_t* val; 93 94 key = yaml_document_get_node(doc, dielec->data.mapping.pairs.start[i].key); 95 val = yaml_document_get_node(doc, dielec->data.mapping.pairs.start[i].value); 96 if(key->type != YAML_SCALAR_NODE) { 97 log_err(parser, key, "expect a dielec material parameter.\n"); 98 res = RES_BAD_ARG; 99 goto error; 100 } 101 #define SETUP_MASK(Flag, Name) { \ 102 if(mask & BIT(Flag)) { \ 103 log_err(parser, key, \ 104 "the "Name" of the dielectric material is already defined.\n"); \ 105 res = RES_BAD_ARG; \ 106 goto error; \ 107 } \ 108 mask |= BIT(Flag); \ 109 } (void)0 110 if(!strcmp((char*)key->data.scalar.value, "normal_map")) { 111 SETUP_MASK(NORMAL_MAP, "normal_map"); 112 res = parse_image(parser, doc, val, &mtl->normal_map); 113 } else if(!strcmp((char*)key->data.scalar.value, "medium_i")) { 114 SETUP_MASK(MEDIUM_I, "medium_i"); 115 res = parse_medium(parser, doc, val, &mtl->medium_i); 116 } else if(!strcmp((char*)key->data.scalar.value, "medium_t")) { 117 SETUP_MASK(MEDIUM_T, "medium_t"); 118 res = parse_medium(parser, doc, val, &mtl->medium_t); 119 } else { 120 log_err(parser, key, "unknown dielectric parameter `%s'.\n", 121 key->data.scalar.value); 122 res = RES_BAD_ARG; 123 goto error; 124 } 125 if(res != RES_OK) { 126 log_node(parser, key); 127 goto error; 128 } 129 #undef SETUP_MASK 130 } 131 132 #define CHECK_PARAM(Flag, Name) \ 133 if(!(mask & BIT(Flag))) { \ 134 log_err(parser, dielec, \ 135 "the "Name" of the dielectric material is missing.\n"); \ 136 res = RES_BAD_ARG; \ 137 goto error; \ 138 } (void)0 139 CHECK_PARAM(MEDIUM_I, "medium_i"); 140 CHECK_PARAM(MEDIUM_T, "medium_t"); 141 #undef CHECK_PARAM 142 143 exit: 144 out_imtl->i = imtl; 145 return res; 146 error: 147 if(imtl) { 148 darray_dielectric_pop_back(&parser->dielectrics); 149 imtl = SIZE_MAX; 150 } 151 goto exit; 152 } 153 154 static res_T 155 parse_material_matte 156 (struct solparser* parser, 157 yaml_document_t* doc, 158 const yaml_node_t* matte, 159 struct solparser_material_matte_id* out_imtl) 160 { 161 enum { NORMAL_MAP, REFLECTIVITY }; 162 struct solparser_material_matte* mtl = NULL; 163 size_t imtl = SIZE_MAX; 164 intptr_t i, n; 165 int mask = 0; /* Register the parsed attributes */ 166 res_T res = RES_OK; 167 ASSERT(doc && matte && out_imtl); 168 169 if(matte->type != YAML_MAPPING_NODE) { 170 log_err(parser, matte, "expect a mapping of matte material parameters.\n"); 171 res = RES_BAD_ARG; 172 goto error; 173 } 174 175 /* Allocate the matte material */ 176 imtl = darray_matte_size_get(&parser->mattes); 177 res = darray_matte_resize(&parser->mattes, imtl + 1); 178 if(res != RES_OK) { 179 log_err(parser, matte, "could not allocate the matte material.\n"); 180 goto error; 181 } 182 mtl = darray_matte_data_get(&parser->mattes) + imtl; 183 184 n = matte->data.mapping.pairs.top - matte->data.mapping.pairs.start; 185 FOR_EACH(i, 0, n) { 186 yaml_node_t* key; 187 yaml_node_t* val; 188 189 key = yaml_document_get_node(doc, matte->data.mapping.pairs.start[i].key); 190 val = yaml_document_get_node(doc, matte->data.mapping.pairs.start[i].value); 191 if(key->type != YAML_SCALAR_NODE) { 192 log_err(parser, key, "expect a matte material parameter.\n"); 193 res = RES_BAD_ARG; 194 goto error; 195 } 196 197 #define SETUP_MASK(Flag, Name) { \ 198 if(mask & BIT(Flag)) { \ 199 log_err(parser, key, \ 200 "the "Name" of the matte material is already defined.\n"); \ 201 res = RES_BAD_ARG; \ 202 goto error; \ 203 } \ 204 mask |= BIT(Flag); \ 205 } (void)0 206 if(!strcmp((char*)key->data.scalar.value, "normal_map")) { 207 SETUP_MASK(NORMAL_MAP, "normal_map"); 208 res = parse_image(parser, doc, val, &mtl->normal_map); 209 } else if(!strcmp((char*)key->data.scalar.value, "reflectivity")) { 210 SETUP_MASK(REFLECTIVITY, "reflectivity"); 211 res = parse_mtl_data(parser, doc, val, 0, 1, &mtl->reflectivity); 212 } else { 213 log_err(parser, key, "unknown matte parameter `%s'.\n", 214 key->data.scalar.value); 215 res = RES_BAD_ARG; 216 goto error; 217 } 218 if(res != RES_OK) { 219 log_node(parser, key); 220 goto error; 221 } 222 #undef SETUP_MASK 223 } 224 225 if(!(mask & BIT(REFLECTIVITY))) { 226 log_err(parser, matte, "the matte reflectivity is missing.\n"); 227 res = RES_BAD_ARG; 228 goto error; 229 } 230 231 exit: 232 out_imtl->i = imtl; 233 return res; 234 error: 235 if(mtl) { 236 darray_matte_pop_back(&parser->mattes); 237 imtl = SIZE_MAX; 238 } 239 goto exit; 240 } 241 242 static res_T 243 parse_material_mirror 244 (struct solparser* parser, 245 yaml_document_t* doc, 246 const yaml_node_t* mirror, 247 struct solparser_material_mirror_id* out_imtl) 248 { 249 enum { MICROFACET, NORMAL_MAP, REFLECTIVITY, SLOPE_ERROR }; 250 struct solparser_material_mirror* mtl = NULL; 251 size_t imtl = SIZE_MAX; 252 int mask = 0; /* Register the parsed attributes */ 253 intptr_t i, n; 254 res_T res = RES_OK; 255 ASSERT(doc && mirror && out_imtl); 256 257 if(mirror->type != YAML_MAPPING_NODE) { 258 log_err(parser, mirror, 259 "expect a mapping of mirror material attributes.\n"); 260 res = RES_BAD_ARG; 261 goto error; 262 } 263 264 /* Allocate the mirror material */ 265 imtl = darray_mirror_size_get(&parser->mirrors); 266 res = darray_mirror_resize(&parser->mirrors, imtl + 1); 267 if(res != RES_OK) { 268 log_err(parser, mirror, "could not allocate the mirror material.\n"); 269 goto error; 270 } 271 mtl = darray_mirror_data_get(&parser->mirrors) + imtl; 272 273 n = mirror->data.mapping.pairs.top - mirror->data.mapping.pairs.start; 274 FOR_EACH(i, 0, n) { 275 yaml_node_t* key; 276 yaml_node_t* val; 277 278 key = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].key); 279 val = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].value); 280 if(key->type != YAML_SCALAR_NODE) { 281 log_err(parser, key, "expect a mirror material parameter.\n"); 282 res = RES_BAD_ARG; 283 goto error; 284 } 285 286 #define SETUP_MASK(Flag, Name) { \ 287 if(mask & BIT(Flag)) { \ 288 log_err(parser, key, \ 289 "the "Name" of the mirror material is already defined.\n"); \ 290 res = RES_BAD_ARG; \ 291 goto error; \ 292 } \ 293 mask |= BIT(Flag); \ 294 } (void)0 295 if(!strcmp((char*)key->data.scalar.value, "microfacet")) { 296 SETUP_MASK(MICROFACET, "microfacet"); 297 res = parse_microfacet(parser, val, &mtl->ufacet_distrib); 298 } else if(!strcmp((char*)key->data.scalar.value, "normal_map")) { 299 SETUP_MASK(NORMAL_MAP, "normal_map"); 300 res = parse_image(parser, doc, val, &mtl->normal_map); 301 } else if(!strcmp((char*)key->data.scalar.value, "reflectivity")) { 302 SETUP_MASK(REFLECTIVITY, "reflectivity"); 303 res = parse_mtl_data(parser, doc, val, 0, 1, &mtl->reflectivity); 304 } else if(!strcmp((char*)key->data.scalar.value, "slope_error")) { 305 SETUP_MASK(SLOPE_ERROR, "slope_error"); 306 res = parse_mtl_data(parser, doc, val, 0, 1, &mtl->slope_error); 307 } else { 308 log_err(parser, key, "unknown mirror attribute `%s'.\n", 309 key->data.scalar.value); 310 res = RES_BAD_ARG; 311 goto error; 312 } 313 if(res != RES_OK) { 314 log_node(parser, key); 315 goto error; 316 } 317 #undef SETUP_MASK 318 } 319 320 #define CHECK_PARAM(Flag, Name) \ 321 if(!(mask & BIT(Flag))) { \ 322 log_err(parser, mirror, "the mirror "Name" is missing.\n"); \ 323 res = RES_BAD_ARG; \ 324 goto error; \ 325 } (void)0 326 CHECK_PARAM(REFLECTIVITY, "reflectivity"); 327 CHECK_PARAM(SLOPE_ERROR, "slope_error"); 328 #undef CHECK_PARAM 329 330 exit: 331 out_imtl->i = imtl; 332 return res; 333 error: 334 if(mtl) { 335 darray_mirror_pop_back(&parser->mirrors); 336 imtl = SIZE_MAX; 337 } 338 goto exit; 339 } 340 341 static res_T 342 parse_material_thin_dielectric 343 (struct solparser* parser, 344 yaml_document_t* doc, 345 yaml_node_t* thin, 346 struct solparser_material_thin_dielectric_id* out_imtl) 347 { 348 enum { MEDIUM_I, MEDIUM_T, NORMAL_MAP, THICKNESS }; 349 struct solparser_material_thin_dielectric* mtl = NULL; 350 size_t imtl = SIZE_MAX; 351 int mask = 0; /* Register the parsed attributes */ 352 intptr_t i, n; 353 res_T res = RES_OK; 354 ASSERT(doc && thin && out_imtl); 355 356 if(thin->type != YAML_MAPPING_NODE) { 357 log_err(parser, thin, 358 "expect a mapping of thin dielectric material attributes.\n"); 359 res = RES_BAD_ARG; 360 goto error; 361 } 362 363 /* Allocate the thin dielectric material */ 364 imtl = darray_thin_dielectric_size_get(&parser->thin_dielectrics); 365 res = darray_thin_dielectric_resize(&parser->thin_dielectrics, imtl + 1); 366 if(res != RES_OK) { 367 log_err(parser, thin, 368 "could not allocate the thin dielectric material.\n"); 369 goto error; 370 } 371 mtl = darray_thin_dielectric_data_get(&parser->thin_dielectrics) + imtl; 372 373 n = thin->data.mapping.pairs.top - thin->data.mapping.pairs.start; 374 FOR_EACH(i, 0, n) { 375 yaml_node_t* key; 376 yaml_node_t* val; 377 378 key = yaml_document_get_node(doc, thin->data.mapping.pairs.start[i].key); 379 val = yaml_document_get_node(doc, thin->data.mapping.pairs.start[i].value); 380 if(key->type != YAML_SCALAR_NODE) { 381 log_err(parser, key, "expect a thin dielectric material parameter.\n"); 382 res = RES_BAD_ARG; 383 goto error; 384 } 385 386 #define SETUP_MASK(Flag, Name) { \ 387 if(mask & BIT(Flag)) { \ 388 log_err(parser, key, \ 389 "the "Name" of the thin dielectric material is already defined.\n"); \ 390 res = RES_BAD_ARG; \ 391 goto error; \ 392 } \ 393 mask |= BIT(Flag); \ 394 } (void)0 395 if(!strcmp((char*)key->data.scalar.value, "normal_map")) { 396 SETUP_MASK(NORMAL_MAP, "normal_map"); 397 res = parse_image(parser, doc, val, &mtl->normal_map); 398 } else if(!strcmp((char*)key->data.scalar.value, "medium_i")) { 399 SETUP_MASK(MEDIUM_I, "medium_i"); 400 res = parse_medium(parser, doc, val, &mtl->medium_i); 401 } else if(!strcmp((char*)key->data.scalar.value, "medium_t")) { 402 SETUP_MASK(MEDIUM_T, "medium_t"); 403 res = parse_medium(parser, doc, val, &mtl->medium_t); 404 } else if(!strcmp((char*)key->data.scalar.value, "thickness")) { 405 SETUP_MASK(THICKNESS, "thickness"); 406 res = parse_real(parser, val, 0, DBL_MAX, &mtl->thickness); 407 } else { 408 log_err(parser, key, "unknown thin dielectric parameter `%s'.\n", 409 key->data.scalar.value); 410 res = RES_BAD_ARG; 411 goto error; 412 } 413 if(res != RES_OK) { 414 log_node(parser, key); 415 goto error; 416 } 417 #undef SETUP_MASK 418 } 419 420 #define CHECK_PARAM(Flag, Name) \ 421 if(!(mask & BIT(Flag))) { \ 422 log_err(parser, thin, \ 423 "the "Name" of the thin dielectric material is missing.\n"); \ 424 res = RES_BAD_ARG; \ 425 goto error; \ 426 } (void)0 427 CHECK_PARAM(MEDIUM_I, "medium_i"); 428 CHECK_PARAM(MEDIUM_T, "medium_t"); 429 CHECK_PARAM(THICKNESS, "thickness"); 430 #undef CHECK_PARAM 431 432 exit: 433 out_imtl->i = imtl; 434 return res; 435 error: 436 if(mtl) { 437 darray_thin_dielectric_pop_back(&parser->thin_dielectrics); 438 imtl = SIZE_MAX; 439 } 440 goto exit; 441 } 442 443 static res_T 444 parse_material_virtual(struct solparser* parser, yaml_node_t* virtual) 445 { 446 res_T res = RES_OK; 447 ASSERT(virtual); 448 449 if(virtual->type != YAML_SCALAR_NODE 450 || ((char*)virtual->data.scalar.value)[0] != '\0') { 451 log_err(parser, virtual, 452 "virtual materials can have a null scalar value only.\n"); 453 res = RES_BAD_ARG; 454 goto error; 455 } 456 457 exit: 458 return res; 459 error: 460 goto exit; 461 } 462 463 static res_T 464 parse_material_descriptor 465 (struct solparser* parser, 466 yaml_document_t* doc, 467 yaml_node_t* desc, 468 struct solparser_material_id* out_imtl) 469 { 470 enum { DESCRIPTOR }; 471 struct solparser_material* mtl = NULL; 472 intptr_t i, n; 473 int mask = 0; /* Register the parsed attributes */ 474 size_t* pimtl; 475 size_t imtl = SIZE_MAX; 476 res_T res = RES_OK; 477 ASSERT(doc && desc && out_imtl); 478 479 /* Check whether or not the YAML descriptor alias an already created Solstice 480 * material */ 481 pimtl = htable_yaml2sols_find(&parser->yaml2mtls, &desc); 482 if(pimtl) { 483 imtl = *pimtl; 484 goto exit; 485 } 486 487 /* Allocate the solstice material */ 488 imtl = darray_material_size_get(&parser->mtls); 489 res = darray_material_resize(&parser->mtls, imtl + 1); 490 if(res != RES_OK) { 491 log_err(parser, desc, "could not allocate the material descriptor.\n"); 492 goto error; 493 } 494 mtl = darray_material_data_get(&parser->mtls) + imtl; 495 496 if(desc->type != YAML_MAPPING_NODE) { 497 log_err(parser, desc, "expect a material descriptor.\n"); 498 res = RES_BAD_ARG; 499 goto error; 500 } 501 502 n = desc->data.mapping.pairs.top - desc->data.mapping.pairs.start; 503 FOR_EACH(i, 0, n) { 504 yaml_node_t* key; 505 yaml_node_t* val; 506 507 key = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].key); 508 val = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].value); 509 if(key->type != YAML_SCALAR_NODE) { 510 log_err(parser, key, "expect a material name.\n"); 511 res = RES_BAD_ARG; 512 goto error; 513 } 514 515 #define SETUP_MASK(Flag, Name) { \ 516 if(mask & BIT(Flag)) { \ 517 log_err(parser, key, "the material "Name" is already defined.\n"); \ 518 res = RES_BAD_ARG; \ 519 goto error; \ 520 } \ 521 mask |= BIT(Flag); \ 522 } (void)0 523 if(!strcmp((char*)key->data.scalar.value, "dielectric")) { 524 SETUP_MASK(DESCRIPTOR, "descriptor"); 525 mtl->type = SOLPARSER_MATERIAL_DIELECTRIC; 526 res = parse_material_dielectric(parser, doc, val, &mtl->data.dielectric); 527 } else if(!strcmp((char*)key->data.scalar.value, "matte")) { 528 SETUP_MASK(DESCRIPTOR, "descriptor"); 529 mtl->type = SOLPARSER_MATERIAL_MATTE; 530 res = parse_material_matte(parser, doc, val, &mtl->data.matte); 531 } else if(!strcmp((char*)key->data.scalar.value, "mirror")) { 532 SETUP_MASK(DESCRIPTOR, "descriptor"); 533 mtl->type = SOLPARSER_MATERIAL_MIRROR; 534 res = parse_material_mirror(parser, doc, val, &mtl->data.mirror); 535 } else if(!strcmp((char*)key->data.scalar.value, "thin_dielectric")) { 536 SETUP_MASK(DESCRIPTOR, "descriptor"); 537 mtl->type = SOLPARSER_MATERIAL_THIN_DIELECTRIC; 538 res = parse_material_thin_dielectric 539 (parser, doc, val, &mtl->data.thin_dielectric); 540 } else if(!strcmp((char*)key->data.scalar.value, "virtual")) { 541 SETUP_MASK(DESCRIPTOR, "descriptor"); 542 mtl->type = SOLPARSER_MATERIAL_VIRTUAL; 543 res = parse_material_virtual(parser, val); 544 } else { 545 log_err(parser, key, "unknown material descriptor `%s'.\n", 546 key->data.scalar.value); 547 res = RES_BAD_ARG; 548 goto error; 549 } 550 if(res != RES_OK) { 551 log_node(parser, key); 552 goto error; 553 } 554 #undef SETUP_MASK 555 } 556 557 if(!(mask & BIT(DESCRIPTOR))) { 558 log_err(parser, desc, "the material descriptor is missing.\n"); 559 res = RES_BAD_ARG; 560 goto error; 561 } 562 563 /* Cache the material */ 564 res = htable_yaml2sols_set(&parser->yaml2mtls, &desc, &imtl); 565 if(res != RES_OK) { 566 log_err(parser, desc, "could not register the material.\n"); 567 goto error; 568 } 569 570 exit: 571 out_imtl->i = imtl; 572 return res; 573 error: 574 if(mtl) { 575 darray_material_pop_back(&parser->mtls); 576 imtl = SIZE_MAX; 577 } 578 goto exit; 579 } 580 581 /******************************************************************************* 582 * Local functions 583 ******************************************************************************/ 584 res_T 585 parse_material 586 (struct solparser* parser, 587 yaml_document_t* doc, 588 yaml_node_t* mtl, 589 struct solparser_material_double_sided_id* out_imtl2) 590 { 591 enum { FRONT, BACK }; 592 struct solparser_material_double_sided* mtl2 = NULL; 593 size_t imtl2 = SIZE_MAX; 594 intptr_t i, n; 595 int mask = 0; /* Register the parsed attributes */ 596 res_T res = RES_OK; 597 ASSERT(doc && mtl && out_imtl2); 598 599 if(mtl->type != YAML_MAPPING_NODE) { 600 log_err(parser, mtl, "expect a material definition.\n"); 601 res = RES_BAD_ARG; 602 goto error; 603 } 604 605 /* Allocate the double sided material */ 606 imtl2 = darray_material2_size_get(&parser->mtls2); 607 res = darray_material2_resize(&parser->mtls2, imtl2 + 1); 608 if(res != RES_OK) { 609 log_err(parser, mtl, "could not allocate the material.\n"); 610 goto error; 611 } 612 mtl2 = darray_material2_data_get(&parser->mtls2) + imtl2; 613 614 n = mtl->data.mapping.pairs.top - mtl->data.mapping.pairs.start; 615 FOR_EACH(i, 0, n) { 616 yaml_node_t* key; 617 yaml_node_t* val; 618 619 key = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].key); 620 val = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].value); 621 if(key->type != YAML_SCALAR_NODE) { 622 log_err(parser, key, 623 "expect a material descriptor or a double sided material.\n"); 624 res = RES_BAD_ARG; 625 goto error; 626 } 627 628 #define SETUP_MASK(Flag, Name) { \ 629 if(mask & BIT(Flag)) { \ 630 log_err(parser, key, \ 631 "the "Name" material descriptor is already defined.\n"); \ 632 res = RES_BAD_ARG; \ 633 goto error; \ 634 } \ 635 mask |= BIT(Flag); \ 636 } (void)0 637 if(!strcmp((char*)key->data.scalar.value, "front")) { 638 SETUP_MASK(FRONT, "front"); 639 res = parse_material_descriptor(parser, doc, val, &mtl2->front); 640 } else if(!strcmp((char*)key->data.scalar.value, "back")) { 641 SETUP_MASK(BACK, "back"); 642 res = parse_material_descriptor(parser, doc, val, &mtl2->back); 643 } else { 644 SETUP_MASK(FRONT, "front"); 645 SETUP_MASK(BACK, "back"); 646 res = parse_material_descriptor(parser, doc, mtl, &mtl2->front); 647 mtl2->back = mtl2->front; 648 if(res != RES_OK) goto error; /* Discard log_node */ 649 } 650 if(res != RES_OK) { 651 log_node(parser, key); 652 goto error; 653 } 654 #undef SETUP_MASK 655 } 656 657 #define CHECK_PARAM(Flag, Name) \ 658 if(!(mask & BIT(Flag))) { \ 659 log_err(parser, mtl, "the "Name" material descriptor is missing.\n"); \ 660 res = RES_BAD_ARG; \ 661 goto error; \ 662 } (void)0 663 CHECK_PARAM(FRONT, "front"); 664 CHECK_PARAM(BACK, "back"); 665 #undef CHECK_PARAM 666 667 exit: 668 out_imtl2->i = imtl2; 669 return res; 670 error: 671 if(mtl2) { 672 darray_material2_pop_back(&parser->mtls2); 673 imtl2 = SIZE_MAX; 674 } 675 goto exit; 676 } 677