solstice.c (24109B)
1 /* Copyright (C) 2018-2026 |Méso|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 /* close support */ 18 19 #include "solstice.h" 20 #include "solstice_c.h" 21 #include "solstice_args.h" 22 #include "parser/solparser.h" 23 24 #include <rsys/double2.h> 25 26 #include <sys/stat.h> 27 #include <sys/types.h> 28 29 #include <errno.h> 30 #include <stdio.h> 31 #include <fcntl.h> 32 33 /* open/close functions */ 34 #include <unistd.h> 35 36 #include <solstice/ssol.h> 37 38 /******************************************************************************* 39 * Helper functions 40 ******************************************************************************/ 41 static void 42 log_err(const char* msg, void* ctx) 43 { 44 ASSERT(msg); 45 (void)ctx; 46 fprintf(stderr, "\x1b[31merror:\x1b[0m %s", msg); 47 } 48 49 static void 50 log_warn(const char* msg, void* ctx) 51 { 52 ASSERT(msg); 53 (void)ctx; 54 fprintf(stderr, "\x1b[33mwarning:\x1b[0m %s", msg); 55 } 56 57 static void 58 clear_materials(struct htable_material* materials) 59 { 60 struct htable_material_iterator it, end; 61 ASSERT(materials); 62 63 htable_material_begin(materials, &it); 64 htable_material_end(materials, &end); 65 while(!htable_material_iterator_eq(&it, &end)) { 66 struct ssol_material* mtl = *htable_material_iterator_data_get(&it); 67 SSOL(material_ref_put(mtl)); 68 htable_material_iterator_next(&it); 69 } 70 htable_material_clear(materials); 71 } 72 73 static void 74 clear_objects(struct htable_object* objects) 75 { 76 struct htable_object_iterator it, end; 77 ASSERT(objects); 78 79 htable_object_begin(objects, &it); 80 htable_object_end(objects, &end); 81 while(!htable_object_iterator_eq(&it, &end)) { 82 struct ssol_object* obj = *htable_object_iterator_data_get(&it); 83 SSOL(object_ref_put(obj)); 84 htable_object_iterator_next(&it); 85 } 86 htable_object_clear(objects); 87 } 88 89 static void 90 clear_nodes(struct darray_nodes* nodes) 91 { 92 size_t i, n; 93 ASSERT(nodes); 94 n = darray_nodes_size_get(nodes); 95 FOR_EACH(i, 0, n) { 96 solstice_node_ref_put(darray_nodes_data_get(nodes)[i]); 97 } 98 darray_nodes_clear(nodes); 99 } 100 101 static void 102 clear_anchors(struct htable_anchor* anchors) 103 { 104 struct htable_anchor_iterator it, end; 105 ASSERT(anchors); 106 107 htable_anchor_begin(anchors, &it); 108 htable_anchor_end(anchors, &end); 109 while(!htable_anchor_iterator_eq(&it, &end)) { 110 struct solstice_node* node = *htable_anchor_iterator_data_get(&it); 111 solstice_node_ref_put(node); 112 htable_anchor_iterator_next(&it); 113 } 114 htable_anchor_clear(anchors); 115 } 116 117 static res_T 118 auto_look_at 119 (struct ssol_scene* scn, 120 const double fov_x, /* Horizontal field of view in radian */ 121 const double proj_ratio, /* Width / height */ 122 const double up[3], /* Up vector */ 123 double position[3], 124 double target[3]) 125 { 126 float flower[3], fupper[3]; 127 double lower[3], upper[3]; 128 double up_abs[3]; 129 double axis_min[3]; 130 double axis_x[3]; 131 double axis_z[3]; 132 double tmp[3]; 133 double radius; 134 double depth; 135 res_T res; 136 ASSERT(scn && fov_x > 0 && proj_ratio > 0 && up); 137 138 res = ssol_scene_compute_aabb(scn, flower, fupper); 139 if(res != RES_OK) { 140 fprintf(stderr, "Couldn't compute the scene bounding box.\n"); 141 goto error; 142 } 143 144 if(flower[0] > fupper[0] 145 || flower[1] > fupper[1] 146 || flower[2] > fupper[2]) { /* Empty scene */ 147 d3_set(position, SOLSTICE_ARGS_DEFAULT.camera.pos); 148 d3_set(target, SOLSTICE_ARGS_DEFAULT.camera.tgt); 149 goto exit; 150 } 151 152 d3_set_f3(upper, fupper); 153 d3_set_f3(lower, flower); 154 155 /* The target is the scene centroid */ 156 d3_muld(target, d3_add(target, lower, upper), 0.5); 157 158 /* Define which up dimension is minimal and use its unit vector to compute a 159 * vector orthogonal to `up'. This ensures that the unit vector and `up' are 160 * not collinear and thus that their cross product is not a zero vector. */ 161 up_abs[0] = fabs(up[0]); 162 up_abs[1] = fabs(up[1]); 163 up_abs[2] = fabs(up[2]); 164 if(up_abs[0] < up_abs[1]) { 165 if(up_abs[0] < up_abs[2]) d3(axis_min, 1, 0, 0); 166 else d3(axis_min, 0, 0, 1); 167 } else { 168 if(up_abs[1] < up_abs[2]) d3(axis_min, 0, 1, 0); 169 else d3(axis_min, 0, 0, 1); 170 } 171 d3_normalize(axis_x, d3_cross(axis_x, up, axis_min)); 172 d3_normalize(axis_z, d3_cross(axis_z, up, axis_x)); 173 174 /* Approximate whether on the XYZ or the ZYX basis the visible part of the 175 * model is maximise */ 176 if(fabs(d3_dot(axis_x, upper)) < fabs(d3_dot(axis_z, upper))) { 177 SWAP(double, axis_x[0], axis_z[0]); 178 SWAP(double, axis_x[1], axis_z[1]); 179 SWAP(double, axis_x[2], axis_z[2]); 180 } 181 182 /* Ensure that the whole model is visible */ 183 radius = d3_len(d3_sub(tmp, upper, lower)) * 0.5; 184 if(proj_ratio < 1) { 185 depth = radius / sin(fov_x/2.0); 186 } else { 187 depth = radius / sin(fov_x/(2.0*proj_ratio)); 188 } 189 190 /* Define the camera position */ 191 d3_sub(position, target, d3_muld(tmp, axis_z, depth)); 192 193 /* Empirically move the position to find a better point of view */ 194 d3_add(position, position, d3_muld(tmp, up, radius)); /*Empirical offset*/ 195 d3_add(position, position, d3_muld(tmp, axis_x, radius)); /*Empirical offset*/ 196 d3_normalize(tmp, d3_sub(tmp, target, position)); 197 d3_sub(position, target, d3_muld(tmp, tmp, depth)); 198 199 exit: 200 return res; 201 error: 202 goto exit; 203 } 204 205 static res_T 206 setup_camera(struct solstice* solstice, const struct solstice_args* args) 207 { 208 struct ssol_camera* cam = NULL; 209 double proj_ratio = 0; 210 double pos[3], tgt[3]; 211 res_T res = RES_OK; 212 ASSERT(solstice && args); 213 214 res = ssol_camera_create(solstice->ssol, &cam); 215 if(res != RES_OK) { 216 fprintf(stderr, "Could not create the rendering camera.\n"); 217 goto error; 218 } 219 220 proj_ratio = (double)args->img.width / (double)args->img.height; 221 res = ssol_camera_set_proj_ratio(cam, proj_ratio); 222 if(res != RES_OK) { 223 fprintf(stderr, "Invalid image ratio '%g'.\n", proj_ratio); 224 goto error; 225 } 226 227 res = ssol_camera_set_fov(cam, MDEG2RAD(args->camera.fov_x)); 228 if(res != RES_OK) { 229 fprintf(stderr, "Invalid horizontal field of view '%g' degrees.\n", 230 args->camera.fov_x); 231 goto error; 232 } 233 234 if(!args->camera.auto_look_at) { 235 d3_set(pos, args->camera.pos); 236 d3_set(tgt, args->camera.tgt); 237 } else { 238 res = auto_look_at(solstice->scene, MDEG2RAD(args->camera.fov_x), 239 proj_ratio, args->camera.up, pos, tgt); 240 if(res != RES_OK) goto error; 241 } 242 243 res = ssol_camera_look_at(cam, pos, tgt, args->camera.up); 244 if(res != RES_OK) { 245 fprintf(stderr, 246 "Invalid camera point of view:\n" 247 " position = %g %g %g\n" 248 " target = %g %g %g\n" 249 " up = %g %g %g\n", 250 SPLIT3(args->camera.pos), 251 SPLIT3(args->camera.tgt), 252 SPLIT3(args->camera.up)); 253 goto error; 254 } 255 256 exit: 257 solstice->camera = cam; 258 return res; 259 error: 260 if(cam) { 261 SSOL(camera_ref_put(cam)); 262 cam = NULL; 263 } 264 goto exit; 265 } 266 267 static res_T 268 setup_framebuffer(struct solstice* solstice, const struct solstice_args* args) 269 { 270 struct ssol_image* fbuf = NULL; 271 res_T res = RES_OK; 272 ASSERT(solstice && args); 273 274 res = ssol_image_create(solstice->ssol, &fbuf); 275 if(res != RES_OK) { 276 fprintf(stderr, "Could not create the rendering framebuffer.\n"); 277 goto error; 278 } 279 280 res = ssol_image_setup 281 (fbuf, args->img.width, args->img.height, SSOL_PIXEL_DOUBLE3); 282 if(res != RES_OK) { 283 fprintf(stderr, 284 "Could not set the framebuffer definition to %lux%lu.\n", 285 args->img.width, args->img.height); 286 goto error; 287 } 288 289 exit: 290 solstice->framebuffer = fbuf; 291 return res; 292 293 error: 294 if(fbuf) { 295 SSOL(image_ref_put(fbuf)); 296 fbuf = NULL; 297 } 298 goto exit; 299 } 300 301 /* This function differs from scem_sun_position_to_sun_vector because 1) angles 302 * are in degrees VS radians, 2) the conventions on azimuth are different. */ 303 static INLINE void 304 spherical_to_cartesian_sun_dir 305 (const struct solstice_spherical* spherical, double sun_dir[3]) 306 { 307 double cos_azimuth; 308 double sin_azimuth; 309 double cos_elevation; 310 double sin_elevation; 311 double azimuth; 312 double elevation; 313 ASSERT(spherical && sun_dir); 314 315 azimuth = MDEG2RAD(spherical->azimuth); 316 elevation = MDEG2RAD(spherical->elevation); 317 318 cos_azimuth = cos(azimuth); 319 sin_azimuth = sin(azimuth); 320 cos_elevation = cos(elevation); 321 sin_elevation = sin(elevation); 322 323 sun_dir[0] = -(cos_elevation * cos_azimuth); 324 sun_dir[1] = -(cos_elevation * sin_azimuth); 325 sun_dir[2] = -(sin_elevation); 326 } 327 328 static res_T 329 setup_sun_dirs(struct solstice* solstice, const struct solstice_args* args) 330 { 331 struct solstice_sun_dir* sun_dirs = NULL; 332 size_t i; 333 res_T res = RES_OK; 334 ASSERT(solstice && args); 335 336 res = darray_sun_dir_resize(&solstice->sun_dirs, args->nsun_dirs); 337 if(res != RES_OK) { 338 fprintf(stderr, 339 "Could not reserve the list of %lu sun directions.\n", 340 (unsigned long)args->nsun_dirs); 341 goto error; 342 } 343 sun_dirs = darray_sun_dir_data_get(&solstice->sun_dirs); 344 FOR_EACH(i, 0, args->nsun_dirs) { 345 struct solstice_args_sun_dir* r = args->sun_dirs + i; 346 switch(r->type) { 347 case SOLSTICE_ARGS_SPHERICAL: 348 sun_dirs[i].type = SOLSTICE_SUN_DIR_SPHERICAL; 349 sun_dirs[i].u.spherical.azimuth = r->spherical.azimuth; 350 sun_dirs[i].u.spherical.elevation = r->spherical.elevation; 351 break; 352 case SOLSTICE_SUN_DIR_LOCATION_TIME: 353 sun_dirs[i].type = SOLSTICE_SUN_DIR_LOCATION_TIME; 354 sun_dirs[i].u.location_time.time = r->location_time.time; 355 sun_dirs[i].u.location_time.latitude = r->location_time.latitude; 356 sun_dirs[i].u.location_time.longitude = r->location_time.longitude; 357 break; 358 default: FATAL("Invalid enum value.\n"); 359 } 360 } 361 362 exit: 363 return res; 364 error: 365 darray_sun_dir_clear(&solstice->sun_dirs); 366 goto exit; 367 } 368 369 static res_T 370 load_data(struct solstice* solstice, const struct solstice_args* args) 371 { 372 struct solparser_entity_iterator it, end; 373 FILE* file = stdin; 374 const char* name = "stdin"; 375 res_T res = RES_OK; 376 ASSERT(solstice && args); 377 378 if(args->input_filename) { 379 file = fopen(args->input_filename, "r"); 380 if(!file) { 381 fprintf(stderr, "Could not open the file `%s'.\n", args->input_filename); 382 res = RES_IO_ERR; 383 goto error; 384 } 385 name = args->input_filename; 386 } else if(!args->quiet) { 387 fprintf(stderr, 388 "Enter the solar facility data. Type ^Z (i.e. CTRL+z) to stop:\n"); 389 } 390 391 res = solparser_setup(solstice->parser, name, file); 392 if(res != RES_OK) goto error; 393 394 res = solparser_load(solstice->parser); 395 if(res != RES_OK) goto error; 396 397 solparser_entity_iterator_begin(solstice->parser, &it); 398 solparser_entity_iterator_end(solstice->parser, &end); 399 if(solparser_entity_iterator_eq(&it, &end)) { 400 fprintf(stderr, "No entity is defined.\n"); 401 res = RES_BAD_ARG; 402 goto error; 403 } 404 405 exit: 406 if(file && file != stdin) fclose(file); 407 return res; 408 error: 409 goto exit; 410 } 411 412 static res_T 413 setup_receivers(struct solstice* solstice, struct srcvl* srcvl) 414 { 415 size_t i, n; 416 res_T res = RES_OK; 417 ASSERT(solstice && srcvl); 418 419 htable_receiver_clear(&solstice->receivers); 420 darray_receiver_clear(&solstice->rcvs_list); 421 422 n = srcvl_count(srcvl); 423 424 res = darray_receiver_resize(&solstice->rcvs_list, n); 425 if(res != RES_OK) { 426 fprintf(stderr, "Could not reserve memory space for the receivers.\n"); 427 goto error; 428 } 429 430 FOR_EACH(i, 0, n) { 431 struct solstice_receiver* receiver; 432 struct srcvl_receiver rcv; 433 const struct solparser_entity* entity; 434 const char* name; 435 436 receiver = darray_receiver_data_get(&solstice->rcvs_list) + i; 437 438 srcvl_get(srcvl, i, &rcv); 439 entity = solparser_find_entity(solstice->parser, rcv.name); 440 if(!entity) { 441 fprintf(stderr, "Invalid entity `%s'.\n", rcv.name); 442 res = RES_BAD_ARG; 443 goto error; 444 } 445 446 if(entity->type != SOLPARSER_ENTITY_GEOMETRY) { 447 fprintf(stderr, 448 "The entity `%s' is not a geometry. It cannot be a receiver.\n", 449 rcv.name); 450 res = RES_BAD_ARG; 451 goto error; 452 } 453 454 res = str_set(&receiver->name, rcv.name); 455 if(res != RES_OK) { 456 fprintf(stderr, "Could not copy the receiver name.\n"); 457 goto error; 458 } 459 460 receiver->node = NULL; 461 receiver->side = rcv.side; 462 receiver->per_primitive_output = rcv.per_primitive_output; 463 464 name = str_cget(&receiver->name); 465 res = htable_receiver_set(&solstice->receivers, &name, &i); 466 if(res != RES_OK) { 467 fprintf(stderr, 468 "Could not register the receiver `%s' against Solstice.\n", 469 rcv.name); 470 goto error; 471 } 472 } 473 474 exit: 475 return res; 476 error: 477 htable_receiver_clear(&solstice->receivers); 478 darray_receiver_clear(&solstice->rcvs_list); 479 goto exit; 480 } 481 482 static res_T 483 load_receivers(struct solstice* solstice, const struct solstice_args* args) 484 { 485 FILE* file = NULL; 486 struct srcvl* srcvl = NULL; 487 res_T res = RES_OK; 488 ASSERT(solstice && args); 489 490 file = fopen(args->receivers_filename, "r"); 491 if(!file) { 492 fprintf(stderr, "Could not open the list of receivers `%s'.\n", 493 args->receivers_filename); 494 res = RES_IO_ERR; 495 goto error; 496 } 497 498 res = srcvl_create(solstice->allocator, &srcvl); 499 if(res != RES_OK) goto error; 500 res = srcvl_setup_stream(srcvl, args->receivers_filename, file); 501 if(res != RES_OK) goto error; 502 res = srcvl_load(srcvl); 503 if(res != RES_OK) goto error; 504 res = setup_receivers(solstice, srcvl); 505 if(res != RES_OK) goto error; 506 507 exit: 508 if(file) fclose(file); 509 if(srcvl) { 510 srcvl_ref_put(srcvl); 511 srcvl = NULL; 512 } 513 return res; 514 error: 515 goto exit; 516 } 517 518 static res_T 519 open_output_stream(const char* name, const int force_overwriting, FILE** stream) 520 { 521 res_T res = RES_OK; 522 int fd = -1; 523 FILE* fp = NULL; 524 ASSERT(name); 525 526 if(force_overwriting) { 527 fp = fopen(name, "w"); 528 if(!fp) { 529 fprintf(stderr, "Could not open the output file `%s'.\n", name); 530 goto error; 531 } 532 } else { 533 fd = open(name, O_CREAT|O_WRONLY|O_EXCL|O_TRUNC, S_IRUSR|S_IWUSR); 534 if(fd >= 0) { 535 fp = fdopen(fd, "w"); 536 if(fp == NULL) { 537 fprintf(stderr, "Could not open the output file `%s'.\n", name); 538 goto error; 539 } 540 } else if(errno == EEXIST) { 541 fprintf(stderr, 542 "The output file `%s' already exists. Use -f to overwrite it.\n", name); 543 goto error; 544 } else { 545 fprintf(stderr, 546 "Unexpected error while opening the output file `%s'.\n", name); 547 goto error; 548 } 549 } 550 551 exit: 552 *stream = fp; 553 return res; 554 error: 555 res = RES_IO_ERR; 556 if(fp) { 557 CHK(fclose(fp) == 0); 558 fp = NULL; 559 } else if(fd >= 0) { 560 CHK(close(fd) == 0); 561 } 562 goto exit; 563 } 564 565 /******************************************************************************* 566 * Solstice local functions 567 ******************************************************************************/ 568 res_T 569 solstice_init 570 (struct mem_allocator* allocator, 571 const struct solstice_args* args, 572 struct solstice* solstice) 573 { 574 res_T res = RES_OK; 575 ASSERT(solstice && args); 576 577 memset(solstice, 0, sizeof(struct solstice)); 578 htable_material_init(allocator, &solstice->materials); 579 htable_object_init(allocator, &solstice->objects); 580 htable_anchor_init(allocator, &solstice->anchors); 581 htable_receiver_init(allocator, &solstice->receivers); 582 htable_primary_init(allocator, &solstice->primaries); 583 darray_nodes_init(allocator, &solstice->roots); 584 darray_nodes_init(allocator, &solstice->pivots); 585 darray_receiver_init(allocator, &solstice->rcvs_list); 586 darray_sun_dir_init(allocator, &solstice->sun_dirs); 587 588 solstice->allocator = allocator ? allocator : &mem_default_allocator; 589 590 logger_init(solstice->allocator, &solstice->logger); 591 logger_set_stream(&solstice->logger, LOG_ERROR, log_err, NULL); 592 logger_set_stream(&solstice->logger, LOG_WARNING, log_warn, NULL); 593 594 res = ssol_device_create(&solstice->logger, allocator, args->nthreads, 595 args->verbose, &solstice->ssol); 596 if(res != RES_OK) { 597 fprintf(stderr, "Could not create the Solstice Solver device.\n"); 598 goto error; 599 } 600 601 res = ssol_scene_create(solstice->ssol, &solstice->scene); 602 if(res != RES_OK) { 603 fprintf(stderr, "Could not create the Solstice Solver scene.\n"); 604 goto error; 605 } 606 607 res = ssol_material_create_virtual(solstice->ssol, &solstice->mtl_virtual); 608 if(res != RES_OK) { 609 fprintf(stderr, "Could not create the global virtual material.\n"); 610 goto error; 611 } 612 613 res = solparser_create(allocator, &solstice->parser); 614 if(res != RES_OK) { 615 fprintf(stderr, "Could not create the Solstice Parser.\n"); 616 goto error; 617 } 618 619 res = setup_sun_dirs(solstice, args); 620 if(res != RES_OK) goto error; 621 622 switch(args->sun_algorithm) { 623 case SOLSTICE_SUN_DIRECTION_ALGORITHM_MEEUS: 624 solstice->sun_algorithm = SSOL_SUN_DIRECTION_ALGORITHM_MEEUS; 625 break; 626 case SOLSTICE_SUN_DIRECTION_ALGORITHM_PSA: 627 solstice->sun_algorithm = SSOL_SUN_DIRECTION_ALGORITHM_PSA; 628 break; 629 default: FATAL("Invalid sun algorithm.\n"); break; 630 } 631 632 if(args->rng_state_input_filename) { 633 solstice->rng_state_input = fopen(args->rng_state_input_filename, "r"); 634 if(!solstice->rng_state_input) { 635 fprintf(stderr, "Could not open the input RNG state file.\n"); 636 res = RES_IO_ERR; 637 goto error; 638 } 639 } 640 641 if(args->rng_state_output_filename) { 642 res = open_output_stream(args->rng_state_output_filename, 643 args->force_overwriting, &solstice->rng_state_output); 644 if(res != RES_OK) goto error; 645 } 646 647 if(!args->output_filename) { 648 solstice->output = stdout; 649 } else { 650 res = open_output_stream(args->output_filename, args->force_overwriting, 651 &solstice->output); 652 if(res != RES_OK) goto error; 653 } 654 655 res = load_data(solstice, args); 656 if(res != RES_OK) goto error; 657 658 if(args->receivers_filename) { 659 res = load_receivers(solstice, args); 660 if(res != RES_OK) goto error; 661 } 662 663 res = solstice_setup_entities(solstice); 664 if(res != RES_OK) { 665 fprintf(stderr, "Could not setup the Solstice entities.\n"); 666 goto error; 667 } 668 669 res = solstice_create_sun(solstice); 670 if(res != RES_OK) { 671 fprintf(stderr, "Could not setup the Solstice sun.\n"); 672 goto error; 673 } 674 675 res = solstice_create_atmosphere(solstice); 676 if(res != RES_OK) { 677 fprintf(stderr, "Could not setup the Solstice atmosphere.\n"); 678 goto error; 679 } 680 681 solstice->nexperiments = args->nexperiments; 682 solstice->dump_format = args->dump_format; 683 solstice->dump_split_mode = args->dump_split_mode; 684 solstice->dump_paths = args->dump_paths; 685 686 /* If a dni value is provided on the command line, the dni defined in the yaml 687 * description of the sun is no longer used */ 688 if(args->dni > 0) { 689 res = ssol_sun_set_dni(solstice->sun, args->dni); 690 if(res != RES_OK) { 691 fprintf(stderr, "Could not setup the DNI of the sun.\n"); 692 goto error; 693 } 694 } 695 696 solstice->path_tracker = SSOL_PATH_TRACKER_DEFAULT; 697 solstice->path_tracker.infinite_ray_length = args->infinite_ray_length; 698 solstice->path_tracker.sun_ray_length = args->sun_ray_length; 699 700 if(args->rendering) { 701 res = setup_camera(solstice, args); 702 if(res != RES_OK) goto error; 703 res = setup_framebuffer(solstice, args); 704 if(res != RES_OK) goto error; 705 solstice->render_mode = args->render_mode; 706 solstice->spp = args->img.spp; 707 d3_set(solstice->up, args->camera.up); 708 } 709 710 exit: 711 return res; 712 error: 713 solstice_release(solstice); 714 goto exit; 715 } 716 717 void 718 solstice_release(struct solstice* solstice) 719 { 720 ASSERT(solstice); 721 clear_materials(&solstice->materials); 722 clear_objects(&solstice->objects); 723 clear_nodes(&solstice->roots); 724 clear_anchors(&solstice->anchors); 725 /* Don't clear pivots, as they are cleared from some root */ 726 if(solstice->ssol) SSOL(device_ref_put(solstice->ssol)); 727 if(solstice->scene) SSOL(scene_ref_put(solstice->scene)); 728 if(solstice->sun) SSOL(sun_ref_put(solstice->sun)); 729 if(solstice->atmosphere) SSOL(atmosphere_ref_put(solstice->atmosphere)); 730 if(solstice->parser) solparser_ref_put(solstice->parser); 731 if(solstice->camera) SSOL(camera_ref_put(solstice->camera)); 732 if(solstice->framebuffer) SSOL(image_ref_put(solstice->framebuffer)); 733 if(solstice->output && solstice->output != stdout) fclose(solstice->output); 734 if(solstice->mtl_virtual) SSOL(material_ref_put(solstice->mtl_virtual)); 735 if(solstice->rng_state_input) fclose(solstice->rng_state_input); 736 if(solstice->rng_state_output) fclose(solstice->rng_state_output); 737 htable_material_release(&solstice->materials); 738 htable_object_release(&solstice->objects); 739 htable_anchor_release(&solstice->anchors); 740 htable_receiver_release(&solstice->receivers); 741 htable_primary_release(&solstice->primaries); 742 darray_nodes_release(&solstice->roots); 743 darray_nodes_release(&solstice->pivots); 744 darray_receiver_release(&solstice->rcvs_list); 745 darray_sun_dir_release(&solstice->sun_dirs); 746 logger_release(&solstice->logger); 747 } 748 749 res_T 750 solstice_run(struct solstice* solstice) 751 { 752 const struct solstice_sun_dir* sun_dirs = NULL; 753 size_t nsun_dirs = 0; 754 size_t i; 755 int dump; 756 int draw; 757 res_T res = RES_OK; 758 ASSERT(solstice); 759 760 sun_dirs = darray_sun_dir_cdata_get(&solstice->sun_dirs); 761 nsun_dirs = darray_sun_dir_size_get(&solstice->sun_dirs); 762 763 dump = solstice->dump_format != SOLSTICE_ARGS_DUMP_NONE; 764 draw = solstice->framebuffer != NULL; 765 766 if(!nsun_dirs) { 767 const double sun_dir[3] = {0, 0, -1}; 768 769 res = ssol_sun_set_direction(solstice->sun, sun_dir); 770 if(res != RES_OK) { 771 fprintf(stderr, "Could not update the sun direction.\n"); 772 goto error; 773 } 774 775 fprintf(solstice->output, "#--- No Sun direction\n"); 776 777 if(dump) { 778 res = solstice_dump(solstice); 779 if(res != RES_OK) goto error; 780 } else { 781 ASSERT(draw); 782 res = solstice_draw(solstice); 783 if(res != RES_OK) goto error; 784 } 785 } else { 786 FOR_EACH(i, 0, nsun_dirs) { 787 double sun_dir[3]; 788 if(sun_dirs[i].type == SOLSTICE_SUN_DIR_SPHERICAL) { 789 spherical_to_cartesian_sun_dir(&sun_dirs[i].u.spherical, sun_dir); 790 fprintf(solstice->output, "#--- Sun direction: %g %g (%g %g %g)\n", 791 sun_dirs[i].u.spherical.azimuth, sun_dirs[i].u.spherical.elevation, 792 SPLIT3(sun_dir)); 793 res = ssol_sun_set_direction(solstice->sun, sun_dir); 794 if(res != RES_OK) { 795 fprintf(stderr, "Could not update the sun direction.\n"); 796 goto error; 797 } 798 } else { 799 char buf[128]; 800 struct ssol_location loc; 801 const struct solstice_location_time* lt = &sun_dirs[i].u.location_time; 802 ASSERT(sun_dirs[i].type == SOLSTICE_SUN_DIR_LOCATION_TIME); 803 loc.latitude = lt->latitude; 804 loc.longitude = lt->longitude; 805 res = ssol_sun_set_location_and_date(solstice->sun, &loc, <->time); 806 if(res != RES_OK) { 807 fprintf(stderr, "Could not update the sun location and time.\n"); 808 goto error; 809 } 810 res = ssol_sun_get_direction(solstice->sun, sun_dir); 811 if(res != RES_OK) goto error; 812 if(0 == strftime(buf, sizeof(buf), 813 "%Y-%m-%dT%H:%M:%S", &sun_dirs[i].u.location_time.time)) { 814 res = RES_BAD_ARG; 815 goto error; 816 } 817 fprintf(solstice->output, 818 "#--- Sun location and time: %g %g %s (%g %g %g)\n", 819 lt->latitude, lt->longitude, buf, 820 SPLIT3(sun_dir)); 821 } 822 823 res = solstice_update_entities(solstice, sun_dir); 824 if(res != RES_OK) goto error; 825 826 if(draw) { 827 res = solstice_draw(solstice); 828 if(res != RES_OK) goto error; 829 } else if(dump) { 830 res = solstice_dump(solstice); 831 if(res != RES_OK) goto error; 832 } else { 833 res = solstice_solve(solstice); 834 if(res != RES_OK) goto error; 835 } 836 } 837 } 838 839 exit: 840 return res; 841 error: 842 goto exit; 843 } 844