solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

ssol_scene.c (16239B)


      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 #include "ssol.h"
     18 #include "ssol_atmosphere_c.h"
     19 #include "ssol_c.h"
     20 #include "ssol_device_c.h"
     21 #include "ssol_instance_c.h"
     22 #include "ssol_material_c.h"
     23 #include "ssol_object_c.h"
     24 #include "ssol_scene_c.h"
     25 #include "ssol_shape_c.h"
     26 #include "ssol_spectrum_c.h"
     27 #include "ssol_sun_c.h"
     28 
     29 #include <rsys/double3.h>
     30 #include <rsys/float2.h>
     31 #include <rsys/float3.h>
     32 #include <rsys/list.h>
     33 #include <rsys/mem_allocator.h>
     34 #include <rsys/rsys.h>
     35 
     36 /*******************************************************************************
     37  * Helper functions
     38  ******************************************************************************/
     39 static void
     40 scene_release(ref_T* ref)
     41 {
     42   struct ssol_device* dev;
     43   struct ssol_scene* scene = CONTAINER_OF(ref, struct ssol_scene, ref);
     44   ASSERT(ref);
     45   dev = scene->dev;
     46   ASSERT(dev && dev->allocator);
     47   SSOL(scene_clear(scene));
     48   if(scene->scn_rt) S3D(scene_ref_put(scene->scn_rt));
     49   if(scene->scn_samp) S3D(scene_ref_put(scene->scn_samp));
     50   if(scene->sun) SSOL(sun_ref_put(scene->sun));
     51   if(scene->atmosphere) SSOL(atmosphere_ref_put(scene->atmosphere));
     52   htable_instance_release(&scene->instances_rt);
     53   htable_instance_release(&scene->instances_samp);
     54   MEM_RM(dev->allocator, scene);
     55   SSOL(device_ref_put(dev));
     56 }
     57 
     58 /*******************************************************************************
     59  * Exported ssol_scene functions
     60  ******************************************************************************/
     61 res_T
     62 ssol_scene_create
     63   (struct ssol_device* dev,
     64    struct ssol_scene** out_scene)
     65 {
     66   struct ssol_scene* scene = NULL;
     67   res_T res = RES_OK;
     68   if(!dev || !out_scene) {
     69     return RES_BAD_ARG;
     70   }
     71 
     72   scene = (struct ssol_scene*)MEM_CALLOC
     73   (dev->allocator, 1, sizeof(struct ssol_scene));
     74   if(!scene) {
     75     res = RES_MEM_ERR;
     76     goto error;
     77   }
     78   htable_instance_init(dev->allocator, &scene->instances_rt);
     79   htable_instance_init(dev->allocator, &scene->instances_samp);
     80   SSOL(device_ref_get(dev));
     81   scene->dev = dev;
     82   ref_init(&scene->ref);
     83 
     84   res = s3d_scene_create(dev->s3d, &scene->scn_rt);
     85   if(res != RES_OK) goto error;
     86   res = s3d_scene_create(dev->s3d, &scene->scn_samp);
     87   if(res != RES_OK) goto error;
     88 
     89   /* default air medium */
     90   ssol_medium_copy(&scene->air, &SSOL_MEDIUM_VACUUM);
     91 
     92 exit:
     93   if(out_scene) *out_scene = scene;
     94   return res;
     95 error:
     96   if(scene) {
     97     SSOL(scene_ref_put(scene));
     98     scene = NULL;
     99   }
    100   goto exit;
    101 }
    102 
    103 res_T
    104 ssol_scene_ref_get(struct ssol_scene* scene)
    105 {
    106   if(!scene) return RES_BAD_ARG;
    107   ref_get(&scene->ref);
    108   return RES_OK;
    109 }
    110 
    111 res_T
    112 ssol_scene_ref_put(struct ssol_scene* scene)
    113 {
    114   if(!scene) return RES_BAD_ARG;
    115   ref_put(&scene->ref, scene_release);
    116   return RES_OK;
    117 }
    118 
    119 res_T
    120 ssol_scene_attach_instance
    121   (struct ssol_scene* scene, struct ssol_instance* instance)
    122 {
    123   enum { ATTACH_S3D, SET_INSTANCE_RT };
    124   unsigned id;
    125   struct ssol_instance** pinst;
    126   int mask = 0;
    127   res_T res;
    128 
    129   if(!scene || !instance) return RES_BAD_ARG;
    130 
    131   /* Attach the instantiated s3d shape to ray-trace to the RT scene */
    132   res = s3d_scene_attach_shape(scene->scn_rt, instance->shape_rt);
    133   if(res != RES_OK) goto error;
    134   mask |= BIT(ATTACH_S3D);
    135 
    136   /* Register the instance against the scene */
    137   S3D(shape_get_id(instance->shape_rt, &id));
    138   pinst = htable_instance_find(&scene->instances_rt, &id);
    139   if(pinst) {
    140     /* already attached */
    141     ASSERT(*pinst == instance); /* cannot be attached to another instance! */
    142     goto exit;
    143   }
    144 
    145   res = htable_instance_set(&scene->instances_rt, &id, &instance);
    146   if(res != RES_OK) goto error;
    147   mask |= BIT(SET_INSTANCE_RT);
    148 
    149   SSOL(instance_ref_get(instance));
    150 
    151 exit:
    152   return res;
    153 error:
    154   if(mask & BIT(ATTACH_S3D)) {
    155     S3D(scene_detach_shape(scene->scn_rt, instance->shape_rt));
    156   }
    157   if(mask & BIT(SET_INSTANCE_RT)) {
    158     const size_t n = htable_instance_erase(&scene->instances_rt, &id);
    159     ASSERT(n == 1); (void)n;
    160   }
    161   goto exit;
    162 }
    163 
    164 res_T
    165 ssol_scene_detach_instance
    166   (struct ssol_scene* scene,
    167    struct ssol_instance* instance)
    168 {
    169   struct ssol_instance** pinst;
    170   struct ssol_instance* inst;
    171   unsigned id;
    172   size_t n;
    173   (void)n, (void)inst;
    174 
    175   if(!scene || !instance) return RES_BAD_ARG;
    176 
    177   /* Retrieve the object instance identifier */
    178   S3D(shape_get_id(instance->shape_rt, &id));
    179 
    180   /* Check that the instance is effectively registered into the scene */
    181   pinst = htable_instance_find(&scene->instances_rt, &id);
    182   if(!pinst) return RES_BAD_ARG;
    183   inst = *pinst;
    184   ASSERT(inst == instance);
    185 
    186   /* Detach the object instance */
    187   n = htable_instance_erase(&scene->instances_rt, &id);
    188   ASSERT(n == 1);
    189   S3D(scene_detach_shape(scene->scn_rt, instance->shape_rt));
    190   SSOL(instance_ref_put(instance));
    191 
    192   return RES_OK;
    193 }
    194 
    195 res_T
    196 ssol_scene_compute_aabb
    197   (const struct ssol_scene* scene, float lower[3], float upper[3])
    198 {
    199   struct s3d_scene_view* view = NULL;
    200   res_T res = RES_OK;
    201 
    202   if(!scene || !lower || !upper) {
    203     res = RES_BAD_ARG;
    204     goto error;
    205   }
    206 
    207   res = s3d_scene_view_create(scene->scn_rt, S3D_GET_PRIMITIVE, &view);
    208   if(res != RES_OK) goto error;
    209   res = s3d_scene_view_get_aabb(view, lower, upper);
    210   if(res != RES_OK) goto error;
    211 
    212 exit:
    213   if(view) S3D(scene_view_ref_put(view));
    214   return res;
    215 error:
    216   goto exit;
    217 }
    218 
    219 res_T
    220 ssol_scene_clear(struct ssol_scene* scene)
    221 {
    222   struct htable_instance_iterator it, it_end;
    223   if(!scene) return RES_BAD_ARG;
    224 
    225   htable_instance_begin(&scene->instances_rt, &it);
    226   htable_instance_end(&scene->instances_rt, &it_end);
    227   while(!htable_instance_iterator_eq(&it, &it_end)) {
    228     struct ssol_instance* inst;
    229     inst = *htable_instance_iterator_data_get(&it);
    230     S3D(scene_detach_shape(scene->scn_rt, inst->shape_rt));
    231     SSOL(instance_ref_put(inst));
    232     htable_instance_iterator_next(&it);
    233   }
    234   htable_instance_clear(&scene->instances_rt);
    235   htable_instance_clear(&scene->instances_samp);
    236   S3D(scene_clear(scene->scn_rt));
    237   S3D(scene_clear(scene->scn_samp));
    238   ssol_medium_clear(&scene->air);
    239   if(scene->sun) SSOL(scene_detach_sun(scene, scene->sun));
    240   if(scene->atmosphere) SSOL(scene_detach_atmosphere(scene, scene->atmosphere));
    241   return RES_OK;
    242 }
    243 
    244 res_T
    245 ssol_scene_attach_sun(struct ssol_scene* scene, struct ssol_sun* sun)
    246 {
    247   if(!scene || ! sun)
    248     return RES_BAD_ARG;
    249   if(sun->scene_attachment || scene->sun) {
    250     /* Already attached: must be linked together */
    251     if(sun->scene_attachment != scene || scene->sun != sun) {
    252       return RES_BAD_ARG;  /* If not detach first! */
    253     } else {
    254       return RES_OK; /* Nothing to change */
    255     }
    256   }
    257   /* no previous attachment */
    258   SSOL(sun_ref_get(sun));
    259   scene->sun = sun;
    260   sun->scene_attachment = scene;
    261   return RES_OK;
    262 }
    263 
    264 res_T
    265 ssol_scene_detach_sun(struct ssol_scene* scene, struct ssol_sun* sun)
    266 {
    267   if(!scene || !sun || !scene->sun || sun->scene_attachment != scene)
    268     return RES_BAD_ARG;
    269 
    270   ASSERT(sun == scene->sun);
    271   sun->scene_attachment = NULL;
    272   scene->sun = NULL;
    273   SSOL(sun_ref_put(sun));
    274   return RES_OK;
    275 }
    276 
    277 
    278 res_T
    279 ssol_scene_attach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm)
    280 {
    281   if(!scene || !atm)
    282     return RES_BAD_ARG;
    283   if(atm->scene_attachment || scene->atmosphere) {
    284     /* already attached: must be linked together */
    285     if(atm->scene_attachment != scene || scene->atmosphere != atm) {
    286       /* if not detach first! */
    287       return RES_BAD_ARG;
    288     } else {
    289       /* nothing to change */
    290       return RES_OK;
    291     }
    292   }
    293   /* no previous attachment */
    294   SSOL(atmosphere_ref_get(atm));
    295   scene->atmosphere = atm;
    296   atm->scene_attachment = scene;
    297   return RES_OK;
    298 }
    299 
    300 res_T
    301 ssol_scene_detach_atmosphere(struct ssol_scene* scene, struct ssol_atmosphere* atm)
    302 {
    303   if(!scene || !atm || !scene->atmosphere || atm->scene_attachment != scene)
    304     return RES_BAD_ARG;
    305 
    306   ASSERT(atm == scene->atmosphere);
    307   atm->scene_attachment = NULL;
    308   scene->atmosphere = NULL;
    309   SSOL(atmosphere_ref_put(atm));
    310   return RES_OK;
    311 }
    312 
    313 res_T
    314 ssol_scene_for_each_instance
    315   (struct ssol_scene* scn,
    316    res_T (*func)(struct ssol_instance* instance, void* ctx),
    317    void* ctx)
    318 {
    319   struct htable_instance_iterator it, end;
    320   res_T res = RES_OK;
    321 
    322   if(!scn || !func) {
    323     res = RES_BAD_ARG;
    324     goto error;
    325   }
    326 
    327   htable_instance_begin(&scn->instances_rt, &it);
    328   htable_instance_end(&scn->instances_rt, &end);
    329   while(!htable_instance_iterator_eq(&it, &end)) {
    330     struct ssol_instance* inst = *htable_instance_iterator_data_get(&it);
    331     htable_instance_iterator_next(&it);
    332 
    333     res = func(inst, ctx);
    334     if(res != RES_OK) goto error;
    335   }
    336 
    337 exit:
    338   return res;
    339 error:
    340   goto exit;
    341 }
    342 
    343 /*******************************************************************************
    344  * Local functions
    345  ******************************************************************************/
    346 res_T
    347 scene_create_s3d_views
    348   (struct ssol_scene* scn,
    349    struct s3d_scene_view** out_view_rt,
    350    struct s3d_scene_view** out_view_samp)
    351 {
    352   struct htable_instance_iterator it, end;
    353   struct s3d_scene_view* view_rt = NULL;
    354   struct s3d_scene_view* view_samp = NULL;
    355   double sampled_area = 0;
    356   double sampled_area_proxy = 0;
    357   int has_sampled = 0;
    358   int has_receiver = 0;
    359   res_T res = RES_OK;
    360   ASSERT(scn && out_view_rt && out_view_samp);
    361 
    362   S3D(scene_clear(scn->scn_samp));
    363   htable_instance_clear(&scn->instances_samp);
    364 
    365   htable_instance_begin(&scn->instances_rt, &it);
    366   htable_instance_end(&scn->instances_rt, &end);
    367 
    368   while(!htable_instance_iterator_eq(&it, &end)) {
    369     struct ssol_instance* inst = *htable_instance_iterator_data_get(&it);
    370     unsigned id;
    371     htable_instance_iterator_next(&it);
    372 
    373     if(inst->receiver_mask) {
    374       has_receiver = 1;
    375     }
    376 
    377     if(!inst->sample) continue;
    378 
    379     sampled_area += inst->shape_rt_area;
    380     sampled_area_proxy += inst->shape_samp_area;
    381 
    382     /* Note that geometries with virtual material can be sampled without risk
    383      * since the solver avoid to shade them and simply pursue the primary ray */
    384     has_sampled = 1;
    385 
    386     /* Attach the instantiated s3d sampling shape to the s3d sampling scene */
    387     res = s3d_scene_attach_shape(scn->scn_samp, inst->shape_samp);
    388     if(res != RES_OK) goto error;
    389 
    390     /* Register the instantiated s3d sampling shape */
    391     S3D(shape_get_id(inst->shape_samp, &id));
    392     ASSERT(!htable_instance_find(&scn->instances_samp, &id));
    393     res = htable_instance_set(&scn->instances_samp, &id, &inst);
    394     if(res != RES_OK) goto error;
    395 
    396     /* Do not get a reference onto the instance since it was already referenced
    397      * by the scene on its attachment */
    398   }
    399 
    400   if(!has_sampled) {
    401     log_error(scn->dev, "No solstice instance to sample.\n");
    402     res = RES_BAD_ARG;
    403     goto error;
    404   }
    405 
    406   if(!has_receiver) {
    407     log_warning(scn->dev, "No receiver is defined.\n");
    408   }
    409 
    410   res = s3d_scene_view_create(scn->scn_rt, S3D_TRACE, &view_rt);
    411   if(res != RES_OK) goto error;
    412   res = s3d_scene_view_create(scn->scn_samp, S3D_SAMPLE, &view_samp);
    413   if(res != RES_OK) goto error;
    414 
    415 exit:
    416   *out_view_rt = view_rt;
    417   *out_view_samp = view_samp;
    418   scn->sampled_area = sampled_area;
    419   scn->sampled_area_proxy = sampled_area_proxy;
    420   return res;
    421 error:
    422   S3D(scene_clear(scn->scn_samp));
    423   htable_instance_clear(&scn->instances_samp);
    424   if(view_rt) {
    425     S3D(scene_view_ref_put(view_rt));
    426     view_rt = NULL;
    427   }
    428   if(view_samp) {
    429     S3D(scene_view_ref_put(view_samp));
    430     view_samp = NULL;
    431   }
    432   goto exit;
    433 }
    434 
    435 /*******************************************************************************
    436  * Local miscellaneous functions
    437  ******************************************************************************/
    438 int
    439 hit_filter_function
    440   (const struct s3d_hit* hit,
    441    const float orgf[3],
    442    const float dirf[3],
    443    const float rangef[2],
    444    void* ray_data,
    445    void* filter_data)
    446 {
    447   struct ssol_instance* inst;
    448   struct ssol_material* mtl;
    449   struct ray_data* rdata = ray_data;
    450   const struct shaded_shape* sshape;
    451   enum ssol_side_flag hit_side = SSOL_INVALID_SIDE;
    452   double org[3], dir[3], N[3], dst = FLT_MAX;
    453   size_t id;
    454   (void)filter_data, (void)rangef;
    455   ASSERT(hit && orgf && dirf);
    456 
    457   /* No ray data => nothing to filter */
    458   if(!ray_data) return 0;
    459   /* Handle numerical imprecision */
    460   if(hit->distance < rangef[0]) return 1;
    461 
    462   /* When searching for closest_point accept any point on the expected object */
    463   if(rdata->point_init_closest_point) {
    464     int reject = (rdata->prim_from.geom_id != hit->prim.geom_id)
    465       || (rdata->prim_from.inst_id != hit->prim.inst_id);
    466     return reject;
    467   }
    468 
    469   /* Retrieve the intersected instance and shaded shape */
    470   inst = *htable_instance_find(&rdata->scn->instances_rt, &hit->prim.inst_id);
    471   id = *htable_shaded_shape_find(&inst->object->shaded_shapes_rt, &hit->prim.geom_id);
    472   sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+id;
    473 
    474   /* Discard self intersection */
    475   switch(sshape->shape->type) {
    476     case SHAPE_MESH:
    477       if(hit->distance <= 1.e-6 /* FIXME hack */
    478       || hit->distance <= rangef[0]
    479       || S3D_PRIMITIVE_EQ(&hit->prim, &rdata->prim_from)) {
    480         /* Discard self intersection for mesh, i.e. when the intersected
    481          * primitive is the primitive from which the ray starts */
    482         return 1;
    483       }
    484       /* No self intersection. Define which side of the primitive is hit.
    485        * Note that incoming direction points inward the primitive */
    486       hit_side = f3_dot(hit->normal, dirf) < 0 ? SSOL_FRONT : SSOL_BACK;
    487       break;
    488     case SHAPE_PUNCHED:
    489       /* Project the hit position into the punched shape */
    490       d3_set_f3(dir, dirf);
    491       d3_set_f3(org, orgf);
    492       dst = shape_trace_ray(sshape->shape, inst->transform, org, dir,
    493         hit->distance, N, punched_shape_intersect_local);
    494       if(dst >= FLT_MAX) {
    495         /* No projection found => the ray does not intersect the quadric */
    496         return 1;
    497       }
    498       if((float)dst <= rangef[0]) {
    499         /* Handle RT numerical imprecision, the hit is below the lower bound
    500          * of the ray range. */
    501         return 1;
    502       }
    503       hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK;
    504       if(inst == rdata->inst_from
    505       && sshape == rdata->sshape_from
    506       && hit_side != rdata->side_from) {
    507         /* The intersected instance is the one from which the ray starts,
    508          * ensure that the ray does not intersect the opposite side of the
    509          * quadric
    510          *
    511          * Note that reversed_ray is intentionally not considered here! */
    512         return 1;
    513       }
    514       break;
    515     default: FATAL("Unreachable code.\n"); break;
    516   }
    517   ASSERT(hit_side == SSOL_BACK || hit_side == SSOL_FRONT);
    518   if(rdata->reversed_ray) {
    519     hit_side = (hit_side == SSOL_FRONT) ? SSOL_BACK : SSOL_FRONT;
    520   }
    521   mtl = hit_side == SSOL_FRONT ? sshape->mtl_front : sshape->mtl_back;
    522   if(mtl->type == SSOL_MATERIAL_VIRTUAL) {
    523     /* Discard all virtual materials */
    524     if(rdata->discard_virtual_materials) return 1;
    525     /* Discard virtual material that are not receivers */
    526     if((inst->receiver_mask & (int)hit_side) == 0) return 1;
    527   }
    528 
    529   /* Save the nearest intersected quadric point */
    530   if(sshape->shape->type != SHAPE_MESH && rdata->dst >= dst) {
    531     d3_set(rdata->N, N);
    532     rdata->dst = dst;
    533   }
    534 
    535   return 0;
    536 }
    537 
    538 res_T
    539 scene_check(const struct ssol_scene* scene, const char* caller)
    540 {
    541   ASSERT(scene && caller);
    542 
    543   if(!scene->sun) {
    544     log_error(scene->dev, "%s: no sun attached.\n", caller);
    545     return RES_BAD_ARG;
    546   }
    547 
    548   if(!scene->sun->spectrum) {
    549     log_error(scene->dev, "%s: sun's spectrum undefined.\n", caller);
    550     return RES_BAD_ARG;
    551   }
    552 
    553   if(scene->sun->dni <= 0) {
    554     log_error(scene->dev, "%s: sun's DNI undefined.\n", caller);
    555     return RES_BAD_ARG;
    556   }
    557   return RES_OK;
    558 }
    559