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