star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

s3d_geometry.c (6595B)


      1 /* Copyright (C) 2015-2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "s3d_device_c.h"
     17 #include "s3d_geometry.h"
     18 #include "s3d_instance.h"
     19 #include "s3d_mesh.h"
     20 #include "s3d_scene_view_c.h"
     21 #include "s3d_sphere.h"
     22 
     23 #include <rsys/mem_allocator.h>
     24 
     25 /*******************************************************************************
     26  * Helper functions
     27  ******************************************************************************/
     28 static FINLINE void
     29 sphere_ray_hit_setup
     30   (const struct RTCIntersectFunctionNArguments* args, const float tfar)
     31 {
     32   struct geometry* geom = args->geometryUserPtr;
     33   struct RTCRayN* rayN;
     34   struct RTCHitN* hitN;
     35   struct RTCHit hit;
     36   struct RTCRay ray;
     37   float Ng[3];
     38   float uv[2];
     39   size_t i;
     40   ASSERT(args && args->primID == 0 && args->N == 1 && args->valid[0] != 0);
     41 
     42   geom = args->geometryUserPtr;
     43   ASSERT(geom && geom->type == GEOM_SPHERE);
     44 
     45   rayN = RAYHITN_GET_RAYN(args->rayhit, args->N);
     46   hitN = RAYHITN_GET_HITN(args->rayhit, args->N);
     47 
     48   rtc_rayN_get_ray(rayN, args->N, 0, &ray);
     49   ray.tfar = tfar;
     50 
     51   Ng[0] = ray.dir_x*tfar + ray.org_x - geom->data.sphere->pos[0];
     52   Ng[1] = ray.dir_y*tfar + ray.org_y - geom->data.sphere->pos[1];
     53   Ng[2] = ray.dir_z*tfar + ray.org_z - geom->data.sphere->pos[2];
     54 
     55   f3_normalize(Ng, Ng);
     56   sphere_normal_to_uv(Ng, uv);
     57 
     58   hit.Ng_x = Ng[0];
     59   hit.Ng_y = Ng[1];
     60   hit.Ng_z = Ng[2];
     61   hit.u = uv[0];
     62   hit.v = uv[1];
     63   hit.primID = 0;
     64   hit.geomID = geom->rtc_id;
     65   FOR_EACH(i, 0, RTC_MAX_INSTANCE_LEVEL_COUNT) {
     66     hit.instID[i] = args->context->instID[i];
     67   }
     68 
     69   /* Filter the intersection if required */
     70   if(geom->data.sphere->filter.func) {
     71     struct RTCFilterFunctionNArguments filter_args;
     72     int valid = 1;
     73 
     74     filter_args.valid = &valid;
     75     filter_args.geometryUserPtr = args->geometryUserPtr;
     76     filter_args.context = args->context;
     77     filter_args.ray = (struct RTCRayN*)&ray;
     78     filter_args.hit = (struct RTCHitN*)&hit;
     79     filter_args.N = args->N;
     80 
     81     rtc_hit_filter_wrapper(&filter_args);
     82     if(!filter_args.valid[0]) return;
     83   }
     84 
     85   RAYN_GRAB(rayN, args->N, 0, float, tfar) = tfar;
     86   rtc_hitN_set_hit(hitN, args->N, 0, &hit);
     87 }
     88 
     89 static void
     90 geometry_release(ref_T* ref)
     91 {
     92   struct geometry* geom;
     93   struct s3d_device* dev;
     94 
     95   geom = CONTAINER_OF(ref, struct geometry, ref);
     96   dev = geom->dev;
     97   switch(geom->type) {
     98     case GEOM_MESH:
     99       if(geom->data.mesh) mesh_ref_put(geom->data.mesh);
    100       break;
    101     case GEOM_INSTANCE:
    102       if(geom->data.instance) instance_ref_put(geom->data.instance);
    103       break;
    104     case GEOM_SPHERE:
    105       if(geom->data.sphere) sphere_ref_put(geom->data.sphere);
    106       break;
    107     default: FATAL("Unreachable code\n"); break;
    108   }
    109   MEM_RM(dev->allocator, geom);
    110   S3D(device_ref_put(dev));
    111 }
    112 
    113 /*******************************************************************************
    114  * Local functions
    115  ******************************************************************************/
    116 res_T
    117 geometry_create
    118   (struct s3d_device* dev,
    119    struct geometry** out_geom)
    120 {
    121   struct geometry* geom = NULL;
    122   res_T res = RES_OK;
    123   ASSERT(dev && out_geom);
    124 
    125   geom = (struct geometry*)MEM_CALLOC
    126     (dev->allocator, 1, sizeof(struct geometry));
    127   if(!geom) {
    128     res = RES_MEM_ERR;
    129     goto error;
    130   }
    131   ref_init(&geom->ref);
    132   S3D(device_ref_get(dev));
    133   geom->dev = dev;
    134   geom->name = S3D_INVALID_ID;
    135   geom->flip_surface = 0;
    136   geom->is_enabled = 1;
    137   geom->type = GEOM_NONE;
    138   geom->data.mesh = NULL;
    139   geom->rtc = NULL;
    140   geom->rtc_id = RTC_INVALID_GEOMETRY_ID;
    141   geom->rtc_build_quality = RTC_BUILD_QUALITY_MEDIUM;
    142 
    143 exit:
    144   *out_geom = geom;
    145   return res;
    146 error:
    147   if(geom) {
    148     geometry_ref_put(geom);
    149     geom = NULL;
    150   }
    151   goto exit;
    152 }
    153 
    154 void
    155 geometry_ref_get(struct geometry* geom)
    156 {
    157   ASSERT(geom);
    158   ref_get(&geom->ref);
    159 }
    160 
    161 void
    162 geometry_ref_put(struct geometry* geom)
    163 {
    164   ASSERT(geom);
    165   ref_put(&geom->ref, geometry_release);
    166 }
    167 
    168 void
    169 geometry_rtc_sphere_bounds(const struct RTCBoundsFunctionArguments* args)
    170 {
    171   struct geometry* geom;
    172   struct sphere sphere;
    173   ASSERT(args && args->primID == 0 && args->timeStep == 0);
    174 
    175   geom = args->geometryUserPtr;
    176   ASSERT(geom && geom->type == GEOM_SPHERE);
    177 
    178   sphere = *geom->data.sphere;
    179   args->bounds_o->lower_x = sphere.pos[0] - sphere.radius;
    180   args->bounds_o->lower_y = sphere.pos[1] - sphere.radius;
    181   args->bounds_o->lower_z = sphere.pos[2] - sphere.radius;
    182   args->bounds_o->upper_x = sphere.pos[0] + sphere.radius;
    183   args->bounds_o->upper_y = sphere.pos[1] + sphere.radius;
    184   args->bounds_o->upper_z = sphere.pos[2] + sphere.radius;
    185 }
    186 
    187 void
    188 geometry_rtc_sphere_intersect(const struct RTCIntersectFunctionNArguments* args)
    189 {
    190   float v[3];
    191   float ray_org[3];
    192   float ray_dir[3];
    193   float A, B, C, D, Q, rcpA, t0, t1;
    194   struct geometry* geom;
    195   struct sphere sphere;
    196   struct RTCRayN* rayN;
    197   ASSERT(args && args->primID == 0 && args->N == 1 && args->valid[0] != 0);
    198 
    199   geom = args->geometryUserPtr;
    200   ASSERT(geom && geom->type == GEOM_SPHERE);
    201 
    202   rayN = RAYHITN_GET_RAYN(args->rayhit, args->N);
    203   ray_org[0] = RAYN_GRAB(rayN, args->N, 0, float, org_x);
    204   ray_org[1] = RAYN_GRAB(rayN, args->N, 0, float, org_y);
    205   ray_org[2] = RAYN_GRAB(rayN, args->N, 0, float, org_z);
    206   ray_dir[0] = RAYN_GRAB(rayN, args->N, 0, float, dir_x);
    207   ray_dir[1] = RAYN_GRAB(rayN, args->N, 0, float, dir_y);
    208   ray_dir[2] = RAYN_GRAB(rayN, args->N, 0, float, dir_z);
    209 
    210   sphere = *geom->data.sphere;
    211   f3_sub(v, ray_org, sphere.pos);
    212   A = f3_dot(ray_dir, ray_dir);
    213   B = 2*f3_dot(v, ray_dir);
    214   C = f3_dot(v, v) - sphere.radius*sphere.radius;
    215   D = B*B - 4*A*C;
    216 
    217   if(D < 0.0f) return;
    218   Q = (float)sqrt(D);
    219   rcpA = 1.f / A;
    220   t0 = 0.5f * rcpA * (-B - Q);
    221   t1 = 0.5f * rcpA * (-B + Q);
    222 
    223   if(RAYN_GRAB(rayN, args->N, 0, float, tnear) < t0
    224   && RAYN_GRAB(rayN, args->N, 0, float, tfar)  > t0) {
    225     sphere_ray_hit_setup(args, t0);
    226   }
    227   if(RAYN_GRAB(rayN, args->N, 0, float, tnear) < t1
    228   && RAYN_GRAB(rayN, args->N, 0, float, tfar)  > t1) {
    229     sphere_ray_hit_setup(args, t1);
    230   }
    231 }
    232