star-phor

Radiative transfer solver for photoreactors.
git clone https://www.edstar.cnrs.fr/git/star-phor.git
Log | Files | Refs | README | LICENSE

sphor_interface.c (20364B)


      1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2024-2026 Clermont Auvergne INP
      3  * Copyright (C) 2024-2026 INSA Lyon
      4  * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux
      5  * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse
      6  * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com)
      7  * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com)
      8  * Copyright (C) 2024-2026 Université de Lorraine
      9  * Copyright (C) 2024-2026 Université Paul Sabatier
     10  * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès
     11  *
     12  * This program is free software: you can redistribute it and/or modify
     13  * it under the terms of the GNU General Public License as published by
     14  * the Free Software Foundation, either version 3 of the License, or
     15  * (at your option) any later version.
     16  *
     17  * This program is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     20  * GNU General Public License for more details.
     21  *
     22  * You should have received a copy of the GNU General Public License
     23  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     24 
     25 #include "sphor_interface.h"
     26 #include "sphor_sources.h"
     27 #include "sphor_c.h"
     28 
     29 #include <rsys/double3.h>
     30 #include <rsys/double2.h>
     31 #include <rsys/float3.h>
     32 #include <rsys/float2.h>
     33 #include <rsys/rsys.h>
     34 #include <star/s3d.h>
     35 #include <star/sphin.h>
     36 #include <star/ssf.h>
     37 
     38 struct sphin_brdf;
     39 struct sphin_sensor_surface;
     40 struct sphin_source_surface;
     41 
     42 /*******************************************************************************
     43  * Local functions
     44  ******************************************************************************/
     45 
     46 /*******************************************************************************
     47  * Interface surface getters
     48  ******************************************************************************/
     49 res_T
     50 primitive_get_brdf
     51   (struct sphor* sphor,
     52    const struct primitive* primitive,
     53    struct sphin_brdf** out_brdf)
     54 {
     55   char* surface_name = NULL;
     56   size_t i = 0;
     57   const struct interface* interface = NULL;
     58   struct sphin_brdf* brdf = NULL;
     59   struct sphin_brdf* found_brdf = NULL;
     60   struct sphin_surface* surface = NULL;
     61   res_T res = RES_OK;
     62 
     63   ASSERT(NULL != sphor);
     64   ASSERT(NULL != primitive);
     65   ASSERT(NULL != out_brdf);
     66 
     67   interface = primitive->interface;
     68 
     69   FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) {
     70     res = sphin_config_get_surface
     71       (sphor->config,
     72        interface->surfaces[(size_t)primitive->side][i],
     73        &surface);
     74     if (RES_OK != res) { goto error; }
     75 
     76     res = sphin_surface_get_brdf(surface, &brdf);
     77     if (RES_OK != res) { goto error; }
     78     if (NULL != brdf){
     79       if (NULL == found_brdf) {
     80         sphin_surface_get_name(surface, &surface_name);
     81         found_brdf = brdf;  /* Keep first non-NULL */
     82       } else {
     83         /* Found second non-NULL BRDF - this is an error/conflict */
     84         char* last_surface_name = NULL;
     85         sphin_surface_get_name(surface, &last_surface_name);
     86         ERROR(sphor,
     87               "Multiple BRDFs defined for same interface side. "
     88               "Conflicting surfaces: '%s' and '%s'.\n",
     89               surface_name, last_surface_name);
     90         res = RES_BAD_ARG;
     91         goto error;
     92       }
     93     }
     94   }
     95 
     96 exit:
     97   *out_brdf = brdf;
     98   return res;
     99 error:
    100   found_brdf = NULL;
    101   goto exit;
    102 }
    103 
    104 res_T
    105 primitive_get_btdf
    106   (struct sphor* sphor,
    107    const struct primitive* primitive,
    108    struct sphin_btdf** out_btdf)
    109 {
    110   char* surface_name = NULL;
    111   size_t i = 0;
    112   const struct interface* interface = NULL;
    113   struct sphin_btdf* btdf = NULL;
    114   struct sphin_btdf* found_btdf = NULL;
    115   struct sphin_surface* surface = NULL;
    116   res_T res = RES_OK;
    117 
    118   ASSERT(NULL != sphor);
    119   ASSERT(NULL != primitive);
    120   ASSERT(NULL != out_btdf);
    121 
    122   interface = primitive->interface;
    123 
    124   FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) {
    125     res = sphin_config_get_surface
    126       (sphor->config,
    127        interface->surfaces[(size_t)primitive->side][i],
    128        &surface);
    129     if (RES_OK != res) { goto error; }
    130 
    131     res = sphin_surface_get_btdf(surface, &btdf);
    132     if (RES_OK != res) { goto error; }
    133     if (NULL != btdf){
    134       if (NULL == found_btdf) {
    135         sphin_surface_get_name(surface, &surface_name);
    136         found_btdf = btdf;  /* Keep first non-NULL */
    137       } else {
    138         /* Found second non-NULL BTDF - this is an error/conflict */
    139         char* last_surface_name = NULL;
    140         sphin_surface_get_name(surface, &last_surface_name);
    141         ERROR(sphor,
    142               "Multiple BTDFs defined for same interface side. "
    143               "Conflicting surfaces: '%s' and '%s'.\n",
    144               surface_name, last_surface_name);
    145         res = RES_BAD_ARG;
    146         goto error;
    147       }
    148     }
    149   }
    150 
    151 exit:
    152   *out_btdf = btdf;
    153   return res;
    154 error:
    155   found_btdf = NULL;
    156   goto exit;
    157 }
    158 
    159 res_T
    160 primitive_get_source
    161   (struct sphor* sphor,
    162    const struct primitive* primitive,
    163    struct sphin_source_surface** out_source)
    164 {
    165   char* surface_name = NULL;
    166   size_t i = 0;
    167   const struct interface* interface = NULL;
    168   struct sphin_source_surface* source = NULL;
    169   struct sphin_source_surface* found_source = NULL;
    170   struct sphin_surface* surface = NULL;
    171   res_T res = RES_OK;
    172 
    173   ASSERT(NULL != sphor);
    174   ASSERT(NULL != primitive);
    175   ASSERT(NULL != out_source);
    176 
    177   interface = primitive->interface;
    178 
    179   FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) {
    180     res = sphin_config_get_surface
    181       (sphor->config,
    182        interface->surfaces[(size_t)primitive->side][i],
    183        &surface);
    184     if (RES_OK != res) { goto error; }
    185     res = sphin_surface_get_source(surface, &source);
    186     if (RES_OK != res) { goto error; }
    187     if (NULL != source){
    188       if (NULL == found_source) {
    189         sphin_surface_get_name(surface, &surface_name);
    190         found_source = source;  /* Keep first non-NULL */
    191       } else {
    192         /* Found second non-NULL sensor - this is an error/conflict */
    193         char* last_surface_name = NULL;
    194         sphin_surface_get_name(surface, &last_surface_name);
    195         ERROR(sphor,
    196               "Multiple surface sources defined for same interface side. "
    197               "Conflicting surfaces: '%s' and '%s'.\n",
    198               surface_name, last_surface_name);
    199         res = RES_BAD_ARG;
    200         goto error;
    201       }
    202     }
    203   }
    204 
    205 exit:
    206   *out_source = source;
    207   return res;
    208 error:
    209   found_source = NULL;
    210   goto exit;
    211 }
    212 
    213 res_T
    214 primitive_get_sensor_surface
    215   (struct sphor* sphor,
    216    const struct primitive* primitive,
    217    struct sphin_sensor_surface** out_sensor)
    218 {
    219   char* surface_name = NULL;
    220   size_t i = 0;
    221   const struct interface* interface = NULL;
    222   struct sphin_sensor_surface* sensor = NULL;
    223   struct sphin_sensor_surface* found_sensor = NULL;
    224   struct sphin_surface* surface = NULL;
    225   res_T res = RES_OK;
    226 
    227   ASSERT(NULL != sphor);
    228   ASSERT(NULL != primitive);
    229   ASSERT(NULL != out_sensor);
    230 
    231   interface = primitive->interface;
    232 
    233   FOR_EACH(i, 0, (size_t)interface->surface_count[(size_t)primitive->side]) {
    234     res = sphin_config_get_surface
    235       (sphor->config,
    236        interface->surfaces[(size_t)primitive->side][i],
    237        &surface);
    238     if (RES_OK != res) { goto error; }
    239     res = sphin_surface_get_sensor(surface, &sensor);
    240     if (RES_OK != res) { goto error; }
    241     if (NULL != sensor){
    242       if (NULL == found_sensor) {
    243         sphin_surface_get_name(surface, &surface_name);
    244         found_sensor = sensor;  /* Keep first non-NULL */
    245       } else {
    246         /* Found second non-NULL sensor - this is an error/conflict */
    247         char* last_surface_name = NULL;
    248         sphin_surface_get_name(surface, &last_surface_name);
    249         ERROR(sphor,
    250               "Multiple sensors defined for same interface side. "
    251               "Conflicting surfaces: '%s' and '%s'.\n",
    252               surface_name, last_surface_name);
    253         res = RES_BAD_ARG;
    254         goto error;
    255       }
    256     }
    257   }
    258 
    259 exit:
    260   *out_sensor = sensor;
    261   return res;
    262 error:
    263   found_sensor = NULL;
    264   goto exit;
    265 }
    266 
    267 /*******************************************************************************
    268  * Interface volume getters
    269  ******************************************************************************/
    270 res_T
    271 primitive_get_in_refraction_index
    272   (struct sphor* sphor,
    273    struct primitive* primitive,
    274    double wavelength,
    275    double* n_real,
    276    double* n_imag)
    277 {
    278   struct sphin_volume* volume = NULL;
    279   struct sphin_refractive_index* refr_ind = NULL;
    280   struct sphin_spectral_property* spec_prop = NULL;
    281   struct sphin_spectral_property_descriptor spec_prop_desc
    282     = SPHIN_SPECTRAL_PROPERTY_DESCRIPTOR_NULL;
    283   res_T res = RES_OK;
    284 
    285   ASSERT(NULL != sphor);
    286   ASSERT(NULL != primitive);
    287   ASSERT(NULL != n_real);
    288   ASSERT(NULL != n_imag);
    289 
    290   /* If the interface side doesnt has a volume defined, the refractive index
    291    * receives the default value of 1 to the real part and 0 to the imaginary
    292    * part */
    293   if (INVALID_ID == primitive->interface->volumes[primitive->side]) {
    294     *n_real = 1;
    295     *n_imag = 0;
    296     goto exit;
    297   }
    298 
    299   res = sphin_config_get_volume
    300     (sphor->config, primitive->interface->volumes[primitive->side], &volume);
    301   if (RES_OK != res) { goto error; }
    302 
    303   res = sphin_volume_get_refractive_index(volume, &refr_ind);
    304   if (RES_OK != res) { goto error; }
    305 
    306   /* If the volume is null, the refractive index
    307    * receives the default value of 1 to the real part and 0 to the imaginary
    308    * part */
    309   if (NULL == refr_ind) {
    310     *n_real = 1;
    311     *n_imag = 0;
    312     goto exit;
    313   }
    314 
    315   /* Real part of the refractive index */
    316   res = sphin_refractive_index_get_n_real(refr_ind, &spec_prop);
    317   if (RES_OK != res) { goto error; }
    318 
    319   if (NULL != spec_prop) {
    320     res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc);
    321     if (RES_OK != res) { goto error; }
    322 
    323     res = sphin_spectral_property_interpolate_at_wavelength
    324       (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_real);
    325     if (RES_OK != res) { goto error; }
    326   }
    327   else { *n_real = 1; }
    328 
    329   /* Imag part of the refractive index */
    330   res = sphin_refractive_index_get_n_imag(refr_ind, &spec_prop);
    331   if (RES_OK != res) { goto error; }
    332 
    333   if (NULL != spec_prop) {
    334     res = sphin_spectral_property_get_desc(spec_prop, &spec_prop_desc);
    335     if (RES_OK != res) { goto error; }
    336 
    337     res = sphin_spectral_property_interpolate_at_wavelength
    338       (spec_prop, wavelength, SPHIN_INTERPOLATION_LINEAR, n_imag);
    339     if (RES_OK != res) { goto error; }
    340   }
    341   else { *n_imag = 0; }
    342 exit:
    343   return res;
    344 error:
    345   goto exit;
    346 }
    347 
    348 res_T
    349 primitive_get_out_refraction_index
    350   (struct sphor* sphor,
    351    struct primitive* primitive,
    352    double wavelength,
    353    double* n_real,
    354    double* n_imag)
    355 {
    356   /* out_primitive is the primitive corresponding to the other side of the
    357    * primitive in the functions' args */
    358   struct primitive out_primitive = PRIMITIVE_NULL;
    359   res_T res = RES_OK;
    360 
    361   ASSERT(NULL != sphor);
    362   ASSERT(NULL != primitive);
    363   ASSERT(NULL != n_real);
    364   ASSERT(NULL != n_imag);
    365 
    366   /* The primitive corresponding to the out side contains the exact same
    367    * interface, but other side_id */
    368   out_primitive.interface = primitive->interface;
    369   out_primitive.side = primitive->side^SPHIN_SIDE_FRONT;
    370 
    371   res = primitive_get_in_refraction_index
    372     (sphor, &out_primitive, wavelength, n_real, n_imag);
    373   return res;
    374 }
    375 
    376 res_T
    377 primitive_get_reflectivity
    378   (struct sphor* sphor,
    379    struct primitive* primitive,
    380    double wavelength,
    381    double cos_theta,
    382    double* out_reflectivity)
    383 {
    384   double eta_i = 0, k_i = 0;
    385   double eta_t = 0, k_t = 0;
    386   double refr_wl = 0;
    387   double reflectivity = 0;
    388   enum sphin_brdf_reflectivity_type reflectivity_type =
    389     SPHIN_BRDF_REFLECTIVITY_NONE__;
    390   struct sphin_brdf* brdf = NULL;
    391   struct sphin_spectral_property* reflectivity_spectrum = NULL;
    392   struct ssf_fresnel* fresnel = NULL;
    393   res_T res = RES_OK;
    394 
    395   ASSERT(NULL != sphor);
    396   ASSERT(NULL != primitive);
    397   ASSERT(NULL != out_reflectivity);
    398 
    399   res = primitive_get_brdf(sphor, primitive, &brdf);
    400   if (RES_OK != res) { goto error; }
    401 
    402   if (NULL == brdf) {
    403     res = RES_BAD_ARG;
    404     goto error;
    405   }
    406 
    407   res = sphin_brdf_get_reflectivity_type(brdf, &reflectivity_type);
    408   if (RES_OK != res) { goto error; }
    409 
    410   switch (reflectivity_type) {
    411     case SPHIN_BRDF_REFLECTIVITY_TABULATED:
    412 
    413       res = sphin_brdf_get_reflectivity_value(brdf, &reflectivity_spectrum);
    414       if (RES_OK != res) { goto error; }
    415 
    416       res = sphin_spectral_property_interpolate_at_wavelength
    417         (reflectivity_spectrum, wavelength,
    418          SPHIN_INTERPOLATION_LINEAR, &refr_wl);
    419       if (RES_OK != res) { goto error; }
    420 
    421       res = ssf_fresnel_create
    422         (sphor->allocator, &ssf_fresnel_constant, &fresnel);
    423       if (RES_OK != res) { goto error; }
    424 
    425       res = ssf_fresnel_constant_setup(fresnel, refr_wl);
    426       if (RES_OK != res) { goto error; }
    427 
    428       break;
    429 
    430     case SPHIN_BRDF_REFLECTIVITY_FRESNEL_DIELECTRIC:
    431 
    432       /* Get refractive index of the medium in the incident side */
    433       res = primitive_get_in_refraction_index
    434         (sphor, primitive, wavelength, &eta_i, &k_i);
    435       if (RES_OK != res) { goto error; }
    436 
    437       /* Get refractive index of the medium in the transmitted side */
    438       res = primitive_get_out_refraction_index
    439         (sphor, primitive, wavelength, &eta_t, &k_t);
    440       if (RES_OK != res) { goto error; }
    441 
    442       res = ssf_fresnel_create
    443         (sphor->allocator, &ssf_fresnel_dielectric_dielectric, &fresnel);
    444       if (RES_OK != res) { goto error; }
    445 
    446       res = ssf_fresnel_dielectric_dielectric_setup(fresnel, eta_i, eta_t);
    447       if (RES_OK != res) { goto error; }
    448 
    449       break;
    450 
    451     case SPHIN_BRDF_REFLECTIVITY_FRESNEL_DIELECTRIC_CONDUCTOR:
    452 
    453       /* Get refractive index of the medium in the incident side */
    454       res = primitive_get_in_refraction_index
    455         (sphor, primitive, wavelength, &eta_i, &k_i);
    456       if (RES_OK != res) { goto error; }
    457 
    458       /* Get refractive index of the medium in the transmitted side */
    459       res = primitive_get_out_refraction_index
    460         (sphor, primitive, wavelength, &eta_t, &k_t);
    461       if (RES_OK != res) { goto error; }
    462 
    463       res = ssf_fresnel_create
    464         (sphor->allocator, &ssf_fresnel_dielectric_conductor, &fresnel);
    465       if (RES_OK != res) { goto error; }
    466 
    467       res = ssf_fresnel_dielectric_conductor_setup(fresnel, eta_i, eta_t, k_t);
    468       if (RES_OK != res) { goto error; }
    469 
    470       break;
    471 
    472     case SPHIN_BRDF_REFLECTIVITY_NONE__:
    473       res = RES_BAD_ARG;
    474       goto error;
    475 
    476     default:
    477       FATAL("Unreachable code\n");
    478       break;
    479   }
    480 
    481   reflectivity = ssf_fresnel_eval(fresnel, cos_theta);
    482   if (reflectivity > 1 || reflectivity < 0) {
    483     res = RES_BAD_ARG;
    484     goto error;
    485   }
    486 
    487 exit:
    488   *out_reflectivity = reflectivity;
    489   if (NULL != fresnel) {
    490     ssf_fresnel_ref_put(fresnel);
    491   }
    492   return res;
    493 error:
    494   goto exit;
    495 }
    496 
    497 res_T
    498 primitive_get_transmissivity
    499   (struct sphor* sphor,
    500    struct primitive* primitive,
    501    double wavelength,
    502    double cos_theta,
    503    double* out_transmissivity)
    504 {
    505   double eta_i = 0, k_i = 0;
    506   double eta_t = 0, k_t = 0;
    507   double transm_wl = 0;
    508   double transmissivity = 0;
    509   enum sphin_btdf_transmissivity_type transmissivity_type =
    510     SPHIN_BTDF_TRANSMISSIVITY_NONE__;
    511   struct sphin_btdf* btdf = NULL;
    512   struct sphin_spectral_property* transmissivity_spectrum = NULL;
    513   struct ssf_fresnel* fresnel = NULL;
    514   res_T res = RES_OK;
    515 
    516   ASSERT(NULL != sphor);
    517   ASSERT(NULL != primitive);
    518   ASSERT(NULL != out_transmissivity);
    519 
    520   res = primitive_get_btdf(sphor, primitive, &btdf);
    521   if (RES_OK != res) { goto error; }
    522 
    523   if (NULL == btdf) {
    524     res = RES_BAD_ARG;
    525     goto error;
    526   }
    527 
    528   res = sphin_btdf_get_transmissivity_type(btdf, &transmissivity_type);
    529   if (RES_OK != res) { goto error; }
    530 
    531   switch (transmissivity_type) {
    532     case SPHIN_BTDF_TRANSMISSIVITY_TABULATED:
    533 
    534       res = sphin_btdf_get_transmissivity_value(btdf, &transmissivity_spectrum);
    535       if (RES_OK != res) { goto error; }
    536 
    537       res = sphin_spectral_property_interpolate_at_wavelength
    538         (transmissivity_spectrum, wavelength,
    539          SPHIN_INTERPOLATION_LINEAR, &transm_wl);
    540       if (RES_OK != res) { goto error; }
    541 
    542       res = ssf_fresnel_create
    543         (sphor->allocator, &ssf_fresnel_constant, &fresnel);
    544       if (RES_OK != res) { goto error; }
    545 
    546       res = ssf_fresnel_constant_setup(fresnel, 1. - transm_wl);
    547       if (RES_OK != res) { goto error; }
    548 
    549       break;
    550 
    551     case SPHIN_BTDF_TRANSMISSIVITY_FRESNEL_DIELECTRIC:
    552 
    553       /* Get refractive index of the medium in the incident side */
    554       res = primitive_get_in_refraction_index
    555         (sphor, primitive, wavelength, &eta_i, &k_i);
    556       if (RES_OK != res) { goto error; }
    557 
    558       /* Get refractive index of the medium in the transmitted side */
    559       res = primitive_get_out_refraction_index
    560         (sphor, primitive, wavelength, &eta_t, &k_t);
    561       if (RES_OK != res) { goto error; }
    562 
    563       res = ssf_fresnel_create
    564         (sphor->allocator, &ssf_fresnel_dielectric_dielectric, &fresnel);
    565       if (RES_OK != res) { goto error; }
    566 
    567       res = ssf_fresnel_dielectric_dielectric_setup(fresnel, eta_i, eta_t);
    568       if (RES_OK != res) { goto error; }
    569 
    570       break;
    571 
    572     case SPHIN_BRDF_REFLECTIVITY_FRESNEL_DIELECTRIC_CONDUCTOR:
    573 
    574       /* Get refractive index of the medium in the incident side */
    575       res = primitive_get_in_refraction_index
    576         (sphor, primitive, wavelength, &eta_i, &k_i);
    577       if (RES_OK != res) { goto error; }
    578 
    579       /* Get refractive index of the medium in the transmitted side */
    580       res = primitive_get_out_refraction_index
    581         (sphor, primitive, wavelength, &eta_t, &k_t);
    582       if (RES_OK != res) { goto error; }
    583 
    584       res = ssf_fresnel_create
    585         (sphor->allocator, &ssf_fresnel_dielectric_conductor, &fresnel);
    586       if (RES_OK != res) { goto error; }
    587 
    588       res = ssf_fresnel_dielectric_conductor_setup(fresnel, eta_i, eta_t, k_t);
    589       if (RES_OK != res) { goto error; }
    590 
    591       break;
    592 
    593     case SPHIN_BRDF_REFLECTIVITY_NONE__:
    594       res = RES_BAD_ARG;
    595       goto error;
    596 
    597     default:
    598       FATAL("Unreachable code\n");
    599       break;
    600   }
    601 
    602   transmissivity = 1 - ssf_fresnel_eval(fresnel, cos_theta);
    603   if (transmissivity > 1 || transmissivity < 0) {
    604     res = RES_BAD_ARG;
    605     goto error;
    606   }
    607 
    608 exit:
    609   *out_transmissivity = transmissivity;
    610   if (NULL != fresnel) {
    611     ssf_fresnel_ref_put(fresnel);
    612   }
    613   return res;
    614 error:
    615   goto exit;
    616 }
    617 
    618 res_T
    619 trace_ray
    620   (const struct sphor* sphor,
    621    const struct ray* ray,
    622    struct intersection* intersection)
    623 {
    624   /* Position, direction and range when calling s3d functions */
    625   float pos[3] = {0}; /* Position */
    626   float dir[3] = {0}; /* Direction */
    627   float rge[2] = {0}; /* Range */
    628 
    629   double normal[3] = {0};
    630   enum sphin_side side_id = SPHIN_SIDE_NONE__;
    631   size_t prim_id = INVALID_ID;
    632   size_t triangle_id;
    633   const struct interface* interface = NULL;
    634   struct s3d_hit hit = S3D_HIT_NULL;
    635   res_T res = RES_OK;
    636 
    637   f3_set_d3(pos, ray->origin);
    638   f3_set_d3(dir, ray->direction);
    639   f2_set_d2(rge, ray->range);
    640 
    641   prim_id = ray->origin_on_prim.primitive.prim_id;
    642 
    643   /* Trace ray using s3d */
    644   res = s3d_scene_view_trace_ray
    645     (sphor->scene_view, pos, dir, rge, &prim_id, &hit);
    646   if (RES_OK != res) { goto error; }
    647 
    648   /* Construct sphor structure using sphor */
    649   if (S3D_HIT_NONE(&hit)) {
    650     *intersection = INTERSECTION_NULL;
    651     goto exit;
    652   }
    653 
    654   triangle_id = hit.prim.prim_id;
    655   interface = &darray_interface_cdata_get(&sphor->interfaces)[triangle_id];
    656   d3_normalize(normal, d3_set_f3(normal, hit.normal));
    657 
    658   side_id =
    659     (d3_dot(ray->direction, normal) < 0) ? SPHIN_SIDE_BACK : SPHIN_SIDE_FRONT;
    660 
    661   intersection->distance = hit.distance;
    662   d2_set_f2(intersection->position.uv, hit.uv);
    663   d3_set(intersection->position.primitive.normal, normal);
    664   intersection->position.primitive.interface = interface;
    665   intersection->position.primitive.prim_id = triangle_id;
    666   intersection->position.primitive.side = side_id;
    667 
    668 exit:
    669   return res;
    670 error:
    671   goto exit;
    672 }
    673 
    674 res_T
    675 ray_update
    676   (struct ray* ray,
    677    const struct intersection* intersection,
    678    const double dir[3])
    679 {
    680   double displacement[3] = {0};
    681   res_T res = RES_OK;
    682 
    683   ASSERT(NULL != intersection);
    684   ASSERT(NULL != ray);
    685 
    686   ray->origin_on_prim = intersection->position;
    687   d3_muld(displacement, ray->direction, intersection->distance);
    688   d3_add(ray->origin, ray->origin, displacement);
    689   d3_set(ray->direction, dir);
    690   return res;
    691 }