star-3d

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

s3d_geometry.c (6622B)


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