test_s3d_closest_point.c (42452B)
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 #define _POSIX_C_SOURCE 200112L /* exp2f, fabsf, nextafterf */ 19 20 #include "s3d.h" 21 #include "test_s3d_cbox.h" 22 #include "test_s3d_utils.h" 23 24 #include <rsys/float2.h> 25 #include <rsys/float3.h> 26 #include <rsys/float33.h> 27 #include <limits.h> 28 29 #define ON_EDGE_EPSILON 1.e-4f 30 #define POSITION_EPSILON 1.e-3f 31 32 struct closest_pt { 33 float pos[3]; 34 float normal[3]; 35 float dst; 36 unsigned iprim; 37 unsigned igeom; 38 unsigned iinst; 39 }; 40 41 #define CLOSEST_PT_NULL__ { \ 42 {0,0,0}, \ 43 {0,0,0}, \ 44 FLT_MAX, \ 45 S3D_INVALID_ID, \ 46 S3D_INVALID_ID, \ 47 S3D_INVALID_ID \ 48 } 49 50 static const struct closest_pt CLOSEST_PT_NULL = CLOSEST_PT_NULL__; 51 52 /******************************************************************************* 53 * Helper functions 54 ******************************************************************************/ 55 /* Function that computes the point onto the triangle that ensures the minimum 56 * distance between the submitted `pos' and the triangle. Use this routine to 57 * cross check the result of the s3d_scene_view_closest_point function that 58 * internally relies on a more efficient implementation */ 59 static float* 60 closest_point_triangle 61 (const float p[3], /* Input pos */ 62 const float a[3], /* 1st triangle vertex */ 63 const float b[3], /* 2nd triangle vertex */ 64 const float c[3], /* 3rd triangle vertex */ 65 float pt[3]) /* Closest point of pos onto the triangle */ 66 { 67 float N[3]; /* Triangle normal */ 68 float Nab[3], Nbc[3], Nca[3]; /* Edge normals */ 69 float ab[3], ac[3], bc[3]; 70 float ap[3], bp[3], cp[3]; 71 float d1, d2, d3, d4, d5, d6, d; 72 float lab, lac, lbc; 73 CHK(p && a && b && c && pt); 74 75 lab = f3_normalize(ab, f3_sub(ab, b, a)); 76 lac = f3_normalize(ac, f3_sub(ac, c, a)); 77 lbc = f3_normalize(bc, f3_sub(bc, c, b)); 78 79 /* Compute the triangle normal */ 80 f3_cross(N, ac, ab); 81 82 /* Check if the nearest point is the 1st triangle vertex */ 83 f3_sub(ap, p, a); 84 d1 = f3_dot(ab, ap); 85 d2 = f3_dot(ac, ap); 86 if(d1 <= 0 && d2 <= 0) return f3_set(pt, a); 87 88 /* Check if the nearest point is the 2nd triangle vertex */ 89 f3_sub(bp, p, b); 90 d3 = f3_dot(bc, bp); 91 d4 =-f3_dot(ab, bp); 92 if(d3 <= 0 && d4 <= 0) return f3_set(pt, b); 93 94 /* Check if the nearest point is the 3rd triangle vertex */ 95 f3_sub(cp, p, c); 96 d5 =-f3_dot(ac, cp); 97 d6 =-f3_dot(bc, cp); 98 if(d5 <= 0 && d6 <= 0) return f3_set(pt, c); 99 100 /* Check if the nearest point is on the 1st triangle edge */ 101 f3_normalize(Nab, f3_cross(Nab, ab, N)); 102 if(f3_dot(Nab, ap) <= 0) { 103 return f3_add(pt, a, f3_mulf(pt, ab, MMIN(d1, lab))); 104 } 105 106 /* Check if the nearest point is on the 2nd triangle edge */ 107 f3_normalize(Nbc, f3_cross(Nbc, bc, N)); 108 if(f3_dot(Nbc, bp) <= 0) { 109 return f3_add(pt, b, f3_mulf(pt, bc, MMIN(d3, lbc))); 110 } 111 112 /* Check if the nearest point is on the 3rd triangle edge */ 113 f3_normalize(Nca, f3_cross(Nca, ac, N)); 114 f3_minus(Nca, Nca); 115 if(f3_dot(Nca, cp) <= 0) { 116 return f3_add(pt, c, f3_mulf(pt, ac,-MMIN(d5, lac))); 117 } 118 119 /* The nearest point is in the triangle */ 120 f3_normalize(N, N); 121 d = f3_dot(N, ap); 122 return f3_add(pt, p, f3_mulf(pt, N, -d)); 123 } 124 125 static void 126 closest_point_mesh 127 (const float pos[3], 128 const float* verts, 129 const unsigned* ids, 130 const unsigned ntris, 131 const unsigned geom_id, 132 const unsigned inst_id, 133 struct closest_pt* pt) 134 { 135 unsigned itri; 136 CHK(pos && verts && ids && pt); 137 138 *pt = CLOSEST_PT_NULL; 139 pt->igeom = geom_id; 140 pt->iinst = inst_id; 141 pt->dst = FLT_MAX; 142 143 /* Find the closest point on the mesh */ 144 FOR_EACH(itri, 0, ntris) { 145 float v0[3]; 146 float v1[3]; 147 float v2[3]; 148 float closest_pt[3]; 149 float vec[3]; 150 float dst; 151 152 f3_set(v0, verts+ids[itri*3+0]*3); 153 f3_set(v1, verts+ids[itri*3+1]*3); 154 f3_set(v2, verts+ids[itri*3+2]*3); 155 156 closest_point_triangle(pos, v0, v1, v2, closest_pt); 157 dst = f3_len(f3_sub(vec, closest_pt, pos)); 158 159 if(dst < pt->dst) { 160 float E0[3], E1[3]; 161 f3_set(pt->pos, closest_pt); 162 pt->dst = dst; 163 pt->iprim = itri; 164 f3_sub(E0, v1, v0); 165 f3_sub(E1, v2, v0); 166 f3_cross(pt->normal, E1, E0); 167 f3_normalize(pt->normal, pt->normal); 168 } 169 } 170 } 171 172 static void 173 closest_point_sphere 174 (const float pos[3], 175 const float sphere_org[3], 176 const float sphere_radius, 177 const unsigned geom_id, 178 const unsigned inst_id, 179 struct closest_pt* pt) 180 { 181 float vec[3]; 182 float len; 183 CHK(pos && sphere_org && sphere_radius > 0 && pt); 184 185 f3_sub(vec, pos, sphere_org); 186 len = f3_normalize(vec, vec); 187 CHK(len > 0); 188 189 pt->dst = (float)fabs(len - sphere_radius); 190 f3_set(pt->normal, vec); 191 f3_add(pt->pos, sphere_org, f3_mulf(pt->pos, vec, sphere_radius)); 192 pt->iprim = 0; 193 pt->igeom = geom_id; 194 pt->iinst = inst_id; 195 } 196 197 /* Check that `hit' roughly lies on an edge. */ 198 static int 199 hit_on_edge(const struct s3d_hit* hit) 200 { 201 struct s3d_attrib v0, v1, v2, pos; 202 float E0[3], E1[3], N[3]; 203 float tri_2area; 204 float hit_2area0; 205 float hit_2area1; 206 float hit_2area2; 207 float hit_pos[3]; 208 209 CHK(hit && !S3D_HIT_NONE(hit)); 210 211 /* Retrieve the triangle vertices */ 212 CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 0, S3D_POSITION, &v0)==RES_OK); 213 CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 1, S3D_POSITION, &v1)==RES_OK); 214 CHK(s3d_triangle_get_vertex_attrib(&hit->prim, 2, S3D_POSITION, &v2)==RES_OK); 215 216 /* Compute the triangle area * 2 */ 217 f3_sub(E0, v1.value, v0.value); 218 f3_sub(E1, v2.value, v0.value); 219 tri_2area = f3_len(f3_cross(N, E0, E1)); 220 221 /* Compute the hit position */ 222 CHK(s3d_primitive_get_attrib(&hit->prim, S3D_POSITION, hit->uv, &pos) == RES_OK); 223 f3_set(hit_pos, pos.value); 224 225 /* Compute areas */ 226 f3_sub(E0, v0.value, hit_pos); 227 f3_sub(E1, v1.value, hit_pos); 228 hit_2area0 = f3_len(f3_cross(N, E0, E1)); 229 f3_sub(E0, v1.value, hit_pos); 230 f3_sub(E1, v2.value, hit_pos); 231 hit_2area1 = f3_len(f3_cross(N, E0, E1)); 232 f3_sub(E0, v2.value, hit_pos); 233 f3_sub(E1, v0.value, hit_pos); 234 hit_2area2 = f3_len(f3_cross(N, E0, E1)); 235 236 if(hit_2area0 / tri_2area < ON_EDGE_EPSILON 237 || hit_2area1 / tri_2area < ON_EDGE_EPSILON 238 || hit_2area2 / tri_2area < ON_EDGE_EPSILON) 239 return 1; 240 241 return 0; 242 } 243 244 static void 245 check_closest_point 246 (const struct s3d_hit* hit, 247 const struct closest_pt* pt, 248 const int hit_triangle) /* Define if `hit' lies on a triangle */ 249 { 250 struct s3d_attrib attr; 251 float N[3]; 252 253 CHK(hit && pt); 254 CHK(!S3D_HIT_NONE(hit)); 255 256 CHK(s3d_primitive_get_attrib 257 (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK); 258 f3_normalize(N, hit->normal); 259 260 if(!hit_triangle || !hit_on_edge(hit)) { 261 CHK(hit->prim.prim_id == pt->iprim); 262 } 263 264 if(hit->prim.prim_id == pt->iprim 265 && hit->prim.geom_id == pt->igeom 266 && hit->prim.inst_id == pt->iinst) { 267 /* Due to numerical inaccuracies and/or the arbitrary order in which 268 * primitives are treated, 2 points on different primitive can have the 269 * same distance from the query position while their respective 270 * coordinates are not equal wrt POSITION_EPSILON. To avoid wrong 271 * assertion, we thus check the position returned by Star-3D against the 272 * manually computed position only if these positions lies on the same 273 * primitive */ 274 CHK(f3_eq_eps(pt->pos, attr.value, POSITION_EPSILON)); 275 CHK(f3_eq_eps(pt->normal, N, 1.e-4f)); 276 } 277 CHK(eq_epsf(hit->distance, pt->dst, 1.e-3f)); 278 } 279 280 /******************************************************************************* 281 * Cornell box and sphere test 282 ******************************************************************************/ 283 struct instance { 284 float translation[3]; 285 unsigned id; 286 }; 287 288 static void 289 check_closest_point_cbox_sphere 290 (const float pos[3], 291 const float sphere_org[3], 292 const float sphere_radius, 293 const unsigned walls_id, 294 const unsigned sphere_id, 295 const struct instance* instances, 296 const size_t ninstances, 297 struct s3d_hit* hit) 298 { 299 struct closest_pt pt_walls = CLOSEST_PT_NULL; 300 struct closest_pt pt_sphere = CLOSEST_PT_NULL; 301 const struct closest_pt* pt = NULL; 302 CHK(pos && hit); 303 304 if(!ninstances) { 305 closest_point_mesh(pos, cbox_walls, cbox_walls_ids, cbox_walls_ntris, 306 walls_id, S3D_INVALID_ID, &pt_walls); 307 closest_point_sphere(pos, sphere_org, sphere_radius, sphere_id, 308 S3D_INVALID_ID, &pt_sphere); 309 } else { 310 size_t iinst; 311 312 pt_walls.dst = FLT_MAX; 313 FOR_EACH(iinst, 0, ninstances) { 314 struct closest_pt pt_walls_tmp; 315 struct closest_pt pt_sphere_tmp; 316 float pos_instance_space[3]; 317 318 /* Transform query position in instance space */ 319 f3_sub(pos_instance_space, pos, instances[iinst].translation); 320 321 closest_point_mesh(pos_instance_space, cbox_walls, cbox_walls_ids, 322 cbox_walls_ntris, walls_id, instances[iinst].id, &pt_walls_tmp); 323 closest_point_sphere(pos_instance_space, sphere_org, sphere_radius, 324 sphere_id, instances[iinst].id, &pt_sphere_tmp); 325 326 if(pt_walls_tmp.dst < pt_walls.dst) { 327 pt_walls = pt_walls_tmp; 328 /* Transform query closest point in world space */ 329 f3_add(pt_walls.pos, pt_walls.pos, instances[iinst].translation); 330 } 331 if(pt_sphere_tmp.dst < pt_sphere.dst) { 332 pt_sphere = pt_sphere_tmp; 333 /* Transform query closest point in world space */ 334 f3_add(pt_sphere.pos, pt_sphere.pos, instances[iinst].translation); 335 } 336 } 337 } 338 339 if(pt_walls.dst< pt_sphere.dst) { 340 pt = &pt_walls; 341 } else { 342 pt = &pt_sphere; 343 } 344 345 check_closest_point(hit, pt, hit->prim.geom_id == walls_id); 346 } 347 348 static void 349 test_cbox_sphere(struct s3d_device* dev) 350 { 351 struct s3d_hit hit = S3D_HIT_NULL; 352 struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; 353 struct s3d_scene* scn = NULL; 354 struct s3d_shape* walls = NULL; 355 struct s3d_shape* sphere = NULL; 356 struct s3d_shape* inst0 = NULL; 357 struct s3d_shape* inst1 = NULL; 358 struct s3d_scene_view* scnview = NULL; 359 struct instance instances[2]; 360 struct cbox_desc cbox_desc; 361 size_t i; 362 float low[3], upp[3], mid[3], sz[3]; 363 float pos[3]; 364 float sphere_org[3]; 365 float sphere_radius; 366 unsigned walls_id, sphere_id; 367 368 CHK(s3d_scene_create(dev, &scn) == RES_OK); 369 370 /* Setup the cornell box walls */ 371 vdata.usage = S3D_POSITION; 372 vdata.type = S3D_FLOAT3; 373 vdata.get = cbox_get_position; 374 cbox_desc.vertices = cbox_walls; 375 cbox_desc.indices = cbox_walls_ids; 376 CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK); 377 CHK(s3d_shape_get_id(walls, &walls_id) == RES_OK); 378 CHK(s3d_scene_attach_shape(scn, walls) == RES_OK); 379 CHK(s3d_mesh_setup_indexed_vertices(walls, cbox_walls_ntris, cbox_get_ids, 380 cbox_walls_nverts, &vdata, 1, &cbox_desc) == RES_OK); 381 382 /* Compute the Cornell box AABB */ 383 CHK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview) == RES_OK); 384 CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK); 385 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 386 387 /* Setup the sphere at the center of the cornell box */ 388 f3_mulf(mid, f3_add(mid, low, upp), 0.5f); 389 f3_sub(sz, upp, low); 390 f3_set(sphere_org, mid); 391 sphere_radius = MMIN(MMIN(sz[0], sz[1]), sz[2]) * 0.125f; /* 1/8 of the box */ 392 CHK(s3d_shape_create_sphere(dev, &sphere) == RES_OK); 393 CHK(s3d_shape_get_id(sphere, &sphere_id) == RES_OK); 394 CHK(s3d_scene_attach_shape(scn, sphere) == RES_OK); 395 CHK(s3d_sphere_setup(sphere, sphere_org, sphere_radius) == RES_OK); 396 397 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 398 399 /* Check point query on the scene */ 400 FOR_EACH(i, 0, 10000) { 401 /* Randomly generate a point in a bounding box that is 2 times the size of 402 * the scene AABB */ 403 pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]); 404 pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]); 405 pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]); 406 407 CHK(s3d_scene_view_closest_point(scnview, pos, (float)INF, NULL, &hit) == RES_OK); 408 check_closest_point_cbox_sphere(pos, sphere_org, sphere_radius, walls_id, 409 sphere_id, NULL, 0, &hit); 410 } 411 412 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 413 414 /* Instantiate the cbox sphere scene */ 415 CHK(s3d_scene_instantiate(scn, &inst0) == RES_OK); 416 CHK(s3d_scene_instantiate(scn, &inst1) == RES_OK); 417 CHK(s3d_shape_get_id(inst0, &instances[0].id) == RES_OK); 418 CHK(s3d_shape_get_id(inst1, &instances[1].id) == RES_OK); 419 f3_mulf(instances[0].translation, sz, 0.5f); 420 CHK(s3d_instance_translate 421 (inst0, S3D_WORLD_TRANSFORM, instances[0].translation) == RES_OK); 422 f3_mulf(instances[1].translation, sz,-0.5f); 423 CHK(s3d_instance_translate 424 (inst1, S3D_WORLD_TRANSFORM, instances[1].translation) == RES_OK); 425 426 /* Create a new scene with instantiated cbox sphere scenes */ 427 CHK(s3d_scene_ref_put(scn) == RES_OK); 428 CHK(s3d_scene_create(dev, &scn) == RES_OK); 429 CHK(s3d_scene_attach_shape(scn, inst0) == RES_OK); 430 CHK(s3d_scene_attach_shape(scn, inst1) == RES_OK); 431 432 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 433 CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK); 434 f3_mulf(mid, f3_add(mid, low, upp), 0.5f); 435 436 /* Check point query on instances */ 437 FOR_EACH(i, 0, 10000) { 438 /* Randomly generate a point in a bounding box that is 2 times the size of 439 * the scene AABB */ 440 pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]); 441 pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]); 442 pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]); 443 444 CHK(s3d_scene_view_closest_point(scnview, pos, (float)INF, NULL, &hit) == RES_OK); 445 check_closest_point_cbox_sphere(pos, sphere_org, sphere_radius, walls_id, 446 sphere_id, instances, 2/*#instances*/, &hit); 447 } 448 449 /* Clean up */ 450 CHK(s3d_shape_ref_put(inst0) == RES_OK); 451 CHK(s3d_shape_ref_put(inst1) == RES_OK); 452 CHK(s3d_shape_ref_put(walls) == RES_OK); 453 CHK(s3d_shape_ref_put(sphere) == RES_OK); 454 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 455 CHK(s3d_scene_ref_put(scn) == RES_OK); 456 } 457 458 /******************************************************************************* 459 * Sphere test 460 ******************************************************************************/ 461 struct sphere_filter_data { 462 float query_pos[3]; 463 float query_radius; 464 }; 465 466 static int 467 sphere_filter 468 (const struct s3d_hit* hit, 469 const float org[3], 470 const float dir[3], 471 const float range[2], 472 void* query_data, 473 void* filter_data) 474 { 475 struct sphere_filter_data* data = query_data; 476 struct s3d_attrib attr; 477 float pos[3]; 478 float vec[3]; 479 480 CHK(hit && org && dir && range && !S3D_HIT_NONE(hit)); 481 CHK((intptr_t)filter_data == (intptr_t)0xDECAFBAD); 482 CHK(f3_normalize(vec, dir) != 0); 483 484 f3_add(pos, org, f3_mulf(pos, vec, hit->distance)); 485 CHK(s3d_primitive_get_attrib 486 (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK); 487 CHK(f3_eq_eps(attr.value, pos, POSITION_EPSILON)); 488 489 CHK(f3_eq_eps(data->query_pos, org, POSITION_EPSILON)); 490 CHK(range[0] == 0); 491 CHK(range[1] == data->query_radius); 492 493 return 1; 494 } 495 496 static void 497 test_sphere(struct s3d_device* dev) 498 { 499 struct s3d_attrib attr; 500 struct s3d_hit hit = S3D_HIT_NULL; 501 struct s3d_shape* sphere = NULL; 502 struct s3d_scene* scn = NULL; 503 struct s3d_scene_view* scnview = NULL; 504 struct sphere_filter_data filter_data; 505 void* ptr = (void*)((intptr_t)0xDECAFBAD); 506 size_t i; 507 float sphere_pos[3]; 508 float query_pos[3]; 509 float sphere_radius; 510 float pos[3]; 511 float dir[3]; 512 unsigned sphere_id; 513 514 CHK(s3d_scene_create(dev, &scn) == RES_OK); 515 CHK(s3d_shape_create_sphere(dev, &sphere) == RES_OK); 516 CHK(s3d_shape_get_id(sphere, &sphere_id) == RES_OK); 517 CHK(s3d_scene_attach_shape(scn, sphere) == RES_OK); 518 519 f3_splat(sphere_pos, 1); 520 sphere_radius = 2; 521 f3_set(query_pos, sphere_pos); 522 CHK(s3d_sphere_setup(sphere, query_pos, sphere_radius) == RES_OK); 523 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 524 525 /* Check a closest point query exactly at the center of the sphere */ 526 CHK(s3d_scene_view_closest_point 527 (scnview, sphere_pos, (float)INF, NULL, &hit) == RES_OK); 528 CHK(!S3D_HIT_NONE(&hit)); 529 CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) == RES_OK); 530 531 f3_normalize(dir, f3_sub(dir, attr.value, query_pos)); 532 f3_add(pos, attr.value, f3_mulf(pos, dir, -hit.distance)); 533 CHK(hit.distance == sphere_radius); 534 CHK(f3_eq_eps(pos, sphere_pos, POSITION_EPSILON)); 535 536 /* Check the exclusive bound of the search radius */ 537 CHK(s3d_scene_view_closest_point 538 (scnview, sphere_pos, sphere_radius, NULL, &hit) == RES_OK); 539 CHK(S3D_HIT_NONE(&hit)); 540 541 /* Check closest point query on a sphere */ 542 FOR_EACH(i, 0, 10000) { 543 struct closest_pt pt; 544 float Ng[3]; 545 query_pos[0] = sphere_pos[0] + (rand_canonic() * 2 - 1) * sphere_radius; 546 query_pos[1] = sphere_pos[1] + (rand_canonic() * 2 - 1) * sphere_radius; 547 query_pos[2] = sphere_pos[2] + (rand_canonic() * 2 - 1) * sphere_radius; 548 549 CHK(s3d_scene_view_closest_point 550 (scnview, query_pos, (float)INF, NULL, &hit) == RES_OK); 551 CHK(!S3D_HIT_NONE(&hit)); 552 CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) == RES_OK); 553 554 /* Cross check the closest point query result */ 555 closest_point_sphere(query_pos, sphere_pos, sphere_radius, 556 sphere_id, S3D_INVALID_ID, &pt); 557 558 f3_normalize(Ng, hit.normal); 559 560 CHK(pt.dst == hit.distance); 561 CHK(pt.iprim == hit.prim.prim_id); 562 CHK(pt.igeom == hit.prim.geom_id); 563 CHK(pt.iinst == hit.prim.inst_id); 564 CHK(f3_eq_eps(pt.pos, attr.value, POSITION_EPSILON)); 565 CHK(f3_eq_eps(pt.normal, Ng, 1.e-4f)); 566 567 /* Check search radius exclusivity */ 568 CHK(s3d_scene_view_closest_point 569 (scnview, query_pos, hit.distance, NULL, &hit) == RES_OK); 570 CHK(S3D_HIT_NONE(&hit)); 571 hit.distance = nextafterf(hit.distance, 0.f); 572 CHK(s3d_scene_view_closest_point 573 (scnview, query_pos, hit.distance, NULL, &hit) == RES_OK); 574 CHK(!S3D_HIT_NONE(&hit)); 575 } 576 577 /* Check the filtering function */ 578 CHK(s3d_sphere_set_hit_filter_function(sphere, sphere_filter, ptr) == RES_OK); 579 580 f3_splat(query_pos, 10); 581 f3_set(filter_data.query_pos, query_pos); 582 filter_data.query_radius = (float)INF; 583 CHK(s3d_scene_view_closest_point 584 (scnview, query_pos, (float)INF, &filter_data, &hit) == RES_OK); 585 CHK(!S3D_HIT_NONE(&hit)); 586 587 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 588 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 589 CHK(s3d_scene_view_closest_point 590 (scnview, query_pos, (float)INF, &filter_data, &hit) == RES_OK); 591 CHK(S3D_HIT_NONE(&hit)); 592 593 CHK(s3d_shape_ref_put(sphere) == RES_OK); 594 CHK(s3d_scene_ref_put(scn) == RES_OK); 595 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 596 } 597 598 /******************************************************************************* 599 * Cornell box test 600 ******************************************************************************/ 601 enum cbox_geom { 602 CBOX_WALLS, 603 CBOX_TALL_BLOCK, 604 CBOX_SHORT_BLOCK, 605 CBOX_GEOMS_COUNT__ 606 }; 607 608 struct cbox_filter_data { 609 float query_pos[3]; 610 float query_radius; 611 unsigned geom_to_filter[3]; 612 }; 613 614 static int 615 cbox_filter 616 (const struct s3d_hit* hit, 617 const float org[3], 618 const float dir[3], 619 const float range[2], 620 void* query_data, 621 void* filter_data) 622 { 623 struct cbox_filter_data* data = query_data; 624 struct s3d_attrib attr; 625 float pos[3]; 626 float vec[3]; 627 628 CHK(hit && org && dir && range && !S3D_HIT_NONE(hit)); 629 CHK((intptr_t)filter_data == (intptr_t)0xDECAFBAD); 630 CHK(f3_normalize(vec, dir) != 0); 631 632 f3_add(pos, org, f3_mulf(pos, vec, hit->distance)); 633 CHK(s3d_primitive_get_attrib 634 (&hit->prim, S3D_POSITION, hit->uv, &attr) == RES_OK); 635 CHK(f3_eq_eps(attr.value, pos, POSITION_EPSILON)); 636 637 if(!query_data) return 0; 638 639 CHK(f3_eq_eps(data->query_pos, org, POSITION_EPSILON)); 640 CHK(range[0] == 0); 641 CHK(range[1] == data->query_radius); 642 643 return data->geom_to_filter[0] == hit->prim.geom_id 644 || data->geom_to_filter[1] == hit->prim.geom_id 645 || data->geom_to_filter[2] == hit->prim.geom_id; 646 } 647 648 static void 649 check_closest_point_cbox 650 (const float pos[3], 651 const unsigned geom_id[3], 652 struct s3d_hit* hit) 653 { 654 struct closest_pt pt[CBOX_GEOMS_COUNT__] = { 655 CLOSEST_PT_NULL__, CLOSEST_PT_NULL__, CLOSEST_PT_NULL__ 656 }; 657 enum cbox_geom geom; 658 659 CHK(pos && geom_id && hit); 660 661 if(geom_id[CBOX_WALLS] != S3D_INVALID_ID) { /* Are the walls filtered */ 662 closest_point_mesh(pos, cbox_walls, cbox_walls_ids, cbox_walls_ntris, 663 geom_id[CBOX_WALLS], S3D_INVALID_ID, &pt[CBOX_WALLS]); 664 } 665 if(geom_id[CBOX_TALL_BLOCK] != S3D_INVALID_ID) { /* Is the block filtered */ 666 closest_point_mesh(pos, cbox_tall_block, cbox_block_ids, cbox_block_ntris, 667 geom_id[CBOX_TALL_BLOCK], S3D_INVALID_ID, &pt[CBOX_TALL_BLOCK]); 668 } 669 if(geom_id[CBOX_SHORT_BLOCK] != S3D_INVALID_ID) { /* Is the block filtered */ 670 closest_point_mesh(pos, cbox_short_block, cbox_block_ids, cbox_block_ntris, 671 geom_id[CBOX_SHORT_BLOCK], S3D_INVALID_ID, &pt[CBOX_SHORT_BLOCK]); 672 } 673 geom = pt[CBOX_WALLS].dst < pt[CBOX_TALL_BLOCK].dst 674 ? CBOX_WALLS : CBOX_TALL_BLOCK; 675 geom = pt[CBOX_SHORT_BLOCK].dst < pt[geom].dst 676 ? CBOX_SHORT_BLOCK : geom; 677 678 if(pt[geom].dst >= FLT_MAX) { /* All geometries were filtered */ 679 CHK(S3D_HIT_NONE(hit)); 680 } else { 681 check_closest_point(hit, &pt[geom], 1); 682 } 683 } 684 685 static void 686 test_cbox(struct s3d_device* dev) 687 { 688 struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; 689 struct s3d_hit hit = S3D_HIT_NULL; 690 struct s3d_scene* scn = NULL; 691 struct s3d_shape* walls = NULL; 692 struct s3d_shape* tall_block = NULL; 693 struct s3d_shape* short_block = NULL; 694 struct s3d_scene_view* scnview = NULL; 695 struct cbox_desc walls_desc; 696 struct cbox_desc tall_block_desc; 697 struct cbox_desc short_block_desc; 698 struct cbox_filter_data filter_data; 699 void* ptr = (void*)((intptr_t)0xDECAFBAD); 700 float pos[3]; 701 float low[3], upp[3], mid[3]; 702 unsigned geom_id[CBOX_GEOMS_COUNT__]; 703 size_t i; 704 705 /* Create the Star-3D scene */ 706 CHK(s3d_scene_create(dev, &scn) == RES_OK); 707 CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK); 708 CHK(s3d_shape_create_mesh(dev, &tall_block) == RES_OK); 709 CHK(s3d_shape_create_mesh(dev, &short_block) == RES_OK); 710 CHK(s3d_shape_get_id(walls, &geom_id[CBOX_WALLS]) == RES_OK); 711 CHK(s3d_shape_get_id(tall_block, &geom_id[CBOX_TALL_BLOCK]) == RES_OK); 712 CHK(s3d_shape_get_id(short_block, &geom_id[CBOX_SHORT_BLOCK]) == RES_OK); 713 CHK(s3d_mesh_set_hit_filter_function(walls, cbox_filter, ptr) == RES_OK); 714 CHK(s3d_mesh_set_hit_filter_function(tall_block, cbox_filter, ptr) == RES_OK); 715 CHK(s3d_mesh_set_hit_filter_function(short_block, cbox_filter, ptr) == RES_OK); 716 CHK(s3d_scene_attach_shape(scn, walls) == RES_OK); 717 CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK); 718 CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK); 719 720 vdata.usage = S3D_POSITION; 721 vdata.type = S3D_FLOAT3; 722 vdata.get = cbox_get_position; 723 724 /* Setup the Cornell box walls */ 725 walls_desc.vertices = cbox_walls; 726 walls_desc.indices = cbox_walls_ids; 727 CHK(s3d_mesh_setup_indexed_vertices(walls, cbox_walls_ntris, cbox_get_ids, 728 cbox_walls_nverts, &vdata, 1, &walls_desc) == RES_OK); 729 730 /* Setup the Cornell box tall block */ 731 tall_block_desc.vertices = cbox_tall_block; 732 tall_block_desc.indices = cbox_block_ids; 733 CHK(s3d_mesh_setup_indexed_vertices(tall_block, cbox_block_ntris, cbox_get_ids, 734 cbox_block_nverts, &vdata, 1, &tall_block_desc) == RES_OK); 735 736 /* Setup the Cornell box short block */ 737 short_block_desc.vertices = cbox_short_block; 738 short_block_desc.indices = cbox_block_ids; 739 CHK(s3d_mesh_setup_indexed_vertices(short_block, cbox_block_ntris, cbox_get_ids, 740 cbox_block_nverts, &vdata, 1, &short_block_desc) == RES_OK); 741 742 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 743 CHK(s3d_scene_view_get_aabb(scnview, low, upp) == RES_OK); 744 mid[0] = (low[0] + upp[0]) * 0.5f; 745 mid[1] = (low[1] + upp[1]) * 0.5f; 746 mid[2] = (low[2] + upp[2]) * 0.5f; 747 748 /* Filter nothing */ 749 filter_data.geom_to_filter[0] = S3D_INVALID_ID; 750 filter_data.geom_to_filter[1] = S3D_INVALID_ID; 751 filter_data.geom_to_filter[2] = S3D_INVALID_ID; 752 753 /* Check a specific position that exhibits a precision issues of the 754 * closest_point_triangle test routine */ 755 { 756 union { float f; uint32_t ui; } ucast; 757 pos[0] = (ucast.ui = 0xc386cc9a, ucast.f); 758 pos[1] = (ucast.ui = 0x43e635b8, ucast.f); 759 pos[2] = (ucast.ui = 0x4319ab78, ucast.f); 760 f3_set(filter_data.query_pos, pos); 761 filter_data.query_radius = (float)INF; 762 CHK(s3d_scene_view_closest_point 763 (scnview, pos, (float)INF, &filter_data, &hit) == RES_OK); 764 check_closest_point_cbox(pos, geom_id, &hit); 765 } 766 767 /* Check closest point query on Cornell box */ 768 FOR_EACH(i, 0, 10000) { 769 /* Randomly generate a point in a bounding box that is 2 times the size of 770 * the cornell box AABB */ 771 pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]); 772 pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]); 773 pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]); 774 775 CHK(s3d_scene_view_closest_point(scnview, pos, (float)INF, NULL, &hit) == RES_OK); 776 check_closest_point_cbox(pos, geom_id, &hit); 777 } 778 779 /* Filter the Cornell box blocks */ 780 filter_data.geom_to_filter[0] = geom_id[CBOX_TALL_BLOCK]; 781 filter_data.geom_to_filter[1] = geom_id[CBOX_SHORT_BLOCK]; 782 filter_data.geom_to_filter[2] = S3D_INVALID_ID; 783 geom_id[CBOX_TALL_BLOCK] = S3D_INVALID_ID; 784 geom_id[CBOX_SHORT_BLOCK] = S3D_INVALID_ID; 785 786 /* Check closest point query filtering */ 787 FOR_EACH(i, 0, 10000) { 788 /* Randomly generate a point in a bounding box that is 2 times the size of 789 * the cornell box AABB */ 790 pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]); 791 pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]); 792 pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]); 793 794 f3_set(filter_data.query_pos, pos); 795 filter_data.query_radius = (float)INF; 796 797 CHK(s3d_scene_view_closest_point 798 (scnview, pos, (float)INF, &filter_data, &hit) == RES_OK); 799 800 check_closest_point_cbox(pos, geom_id, &hit); 801 } 802 803 /* Clean up */ 804 CHK(s3d_shape_ref_put(walls) == RES_OK); 805 CHK(s3d_shape_ref_put(tall_block) == RES_OK); 806 CHK(s3d_shape_ref_put(short_block) == RES_OK); 807 CHK(s3d_scene_ref_put(scn) == RES_OK); 808 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 809 } 810 811 /******************************************************************************* 812 * Single triangle test 813 ******************************************************************************/ 814 static void 815 triangle_get_ids(const unsigned itri, unsigned ids[3], void* ctx) 816 { 817 (void)ctx; 818 CHK(itri == 0); 819 CHK(ids); 820 ids[0] = 0; 821 ids[1] = 1; 822 ids[2] = 2; 823 } 824 825 static void 826 triangle_get_pos(const unsigned ivert, float pos[3], void* ctx) 827 { 828 float* vertices = ctx; 829 CHK(ctx); 830 CHK(ivert < 3); 831 CHK(pos); 832 switch(ivert) { /* Setup a random triangle */ 833 case 0: f3_set(pos, vertices+0); break; 834 case 1: f3_set(pos, vertices+3); break; 835 case 2: f3_set(pos, vertices+6); break; 836 default: FATAL("Unreachable code\n"); break; 837 } 838 } 839 840 static void 841 test_single_triangle(struct s3d_device* dev) 842 { 843 float vertices[9]; 844 struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; 845 struct s3d_hit hit = S3D_HIT_NULL; 846 struct s3d_scene* scn = NULL; 847 struct s3d_scene_view* view = NULL; 848 struct s3d_shape* msh = NULL; 849 struct s3d_attrib attr; 850 float v0[3], v1[3], v2[3]; 851 float pos[3] = {0,0,0}; 852 float closest_pos[3] = {0,0,0}; 853 float low[3], upp[3], mid[3]; 854 union { float f; uint32_t ui32; } ucast; 855 size_t a, i; 856 857 f3(vertices+0, -0.5f, -0.3f, 0.1f); 858 f3(vertices+3, -0.4f, 0.2f, 0.3f); 859 f3(vertices+6, 0.7f, 0.01f, -0.5f); 860 861 CHK(s3d_scene_create(dev, &scn) == RES_OK); 862 CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK); 863 CHK(s3d_scene_attach_shape(scn, msh) == RES_OK); 864 865 vdata.usage = S3D_POSITION; 866 vdata.type = S3D_FLOAT3; 867 vdata.get = triangle_get_pos; 868 CHK(s3d_mesh_setup_indexed_vertices 869 (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); 870 871 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); 872 873 triangle_get_pos(0, v0, vertices); 874 triangle_get_pos(1, v1, vertices); 875 triangle_get_pos(2, v2, vertices); 876 877 /* Compute the triangle AABB */ 878 low[0] = MMIN(MMIN(v0[0], v1[0]), v2[0]); 879 low[1] = MMIN(MMIN(v0[1], v1[1]), v2[1]); 880 low[2] = MMIN(MMIN(v0[2], v1[2]), v2[2]); 881 upp[0] = MMAX(MMAX(v0[0], v1[0]), v2[0]); 882 upp[1] = MMAX(MMAX(v0[1], v1[1]), v2[1]); 883 upp[2] = MMAX(MMAX(v0[2], v1[2]), v2[2]); 884 mid[0] = (low[0] + upp[0]) * 0.5f; 885 mid[1] = (low[1] + upp[1]) * 0.5f; 886 mid[2] = (low[2] + upp[2]) * 0.5f; 887 888 FOR_EACH(i, 0, 10000) { 889 /* Randomly generate a point in a bounding box that is 10 times the size of 890 * the triangle AABB */ 891 pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f; 892 pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f; 893 pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 5.f; 894 895 CHK(s3d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) == RES_OK); 896 CHK(!S3D_HIT_NONE(&hit)); 897 CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) == RES_OK); 898 899 /* Cross check the closest point query result */ 900 closest_point_triangle(pos, v0, v1, v2, closest_pos); 901 CHK(f3_eq_eps(closest_pos, attr.value, 1.e-4f)); 902 } 903 904 FOR_EACH(i, 0, 10000) { 905 float radius; 906 907 /* Randomly generate a point in a bounding box that is 10 times the size of 908 * the triangle AABB */ 909 pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f; 910 pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f; 911 pos[2] = mid[2] + (rand_canonic() * 2 - 1) * (upp[2] - low[2]) * 5.f; 912 913 CHK(s3d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) == RES_OK); 914 CHK(!S3D_HIT_NONE(&hit)); 915 916 /* Check that the radius is an exclusive upper bound */ 917 radius = hit.distance; 918 CHK(s3d_scene_view_closest_point(view, pos, radius, NULL, &hit) == RES_OK); 919 CHK(S3D_HIT_NONE(&hit)); 920 radius = nextafterf(radius, FLT_MAX); 921 CHK(s3d_scene_view_closest_point(view, pos, radius, NULL, &hit) == RES_OK); 922 CHK(!S3D_HIT_NONE(&hit)); 923 CHK(hit.distance == nextafterf(radius, 0.f)); 924 } 925 CHK(s3d_scene_view_ref_put(view) == RES_OK); 926 927 /* Setup a triangle and a query position that exhibited a precision issue on 928 * the returned barycentric coordinate and check that this bug is now fixed */ 929 ucast.ui32 = 0x40400000; vertices[0] = ucast.f; 930 ucast.ui32 = 0xc1200000; vertices[1] = ucast.f; 931 ucast.ui32 = 0xbfc00000; vertices[2] = ucast.f; 932 ucast.ui32 = 0x40400000; vertices[3] = ucast.f; 933 ucast.ui32 = 0xc1200000; vertices[4] = ucast.f; 934 ucast.ui32 = 0x3fc00000; vertices[5] = ucast.f; 935 ucast.ui32 = 0x3f6d5337; vertices[6] = ucast.f; 936 ucast.ui32 = 0xc0e4b2d5; vertices[7] = ucast.f; 937 ucast.ui32 = 0xbfc00000; vertices[8] = ucast.f; 938 f3(pos, 2, -10, 1); 939 940 CHK(s3d_mesh_setup_indexed_vertices 941 (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); 942 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); 943 CHK(s3d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) == RES_OK); 944 CHK(!S3D_HIT_NONE(&hit)); 945 CHK(0 <= hit.uv[0] && hit.uv[0] <= 1); 946 CHK(0 <= hit.uv[1] && hit.uv[1] <= 1); 947 CHK(hit.uv[0] + hit.uv[1] <= 1); 948 949 CHK(s3d_shape_ref_put(msh) == RES_OK); 950 CHK(s3d_scene_view_ref_put(view) == RES_OK); 951 CHK(s3d_scene_ref_put(scn) == RES_OK); 952 953 /* Check accuracy on a configuration whose analytic distance is known */ 954 FOR_EACH(a, 0, 16) { 955 const float amplitude = exp2f((float)a); 956 const float eps = 5e-6f * amplitude; 957 FOR_EACH(i, 0, 1000) { 958 float A[3], B[3], C[3], AB[3], AC[3], BC[3], N[3], hit_N[3]; 959 int j, n; 960 961 /* Randomly generate a triangle ABC */ 962 FOR_EACH(n, 0, 3) 963 A[n] = (rand_canonic() - 0.5f) * amplitude; 964 do { 965 FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude; 966 } while (f3_eq_eps(A, B, eps)); 967 do { 968 FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude; 969 } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps)); 970 971 f3_sub(AB, B, A); 972 f3_sub(AC, C, A); 973 f3_sub(BC, C, B); 974 f3_cross(N, AC, AB); /* Left hand convention */ 975 f3_normalize(N, N); 976 977 f3_set(vertices + 0, A); 978 f3_set(vertices + 3, B); 979 f3_set(vertices + 6, C); 980 981 CHK(s3d_scene_create(dev, &scn) == RES_OK); 982 CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK); 983 CHK(s3d_scene_attach_shape(scn, msh) == RES_OK); 984 985 vdata.usage = S3D_POSITION; 986 vdata.type = S3D_FLOAT3; 987 vdata.get = triangle_get_pos; 988 CHK(s3d_mesh_setup_indexed_vertices 989 (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); 990 991 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); 992 993 FOR_EACH(j, 0, 1000) { 994 float proj[3]; /* Projection of pos on the line */ 995 float AP[3], BP[3], CP[3], tmp[3]; 996 float closest[3] = {0,0,0}; 997 float u, v, w, h, x, dist, d; 998 999 /* Randomly generate a pos not on the triangle 1000 * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */ 1001 u = 3 * rand_canonic() - 1; 1002 v = 3 * rand_canonic() - 1; 1003 w = 1 - u - v; 1004 h = (2 * rand_canonic() - 1) * amplitude; 1005 f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v))); 1006 f3_add(pos, proj, f3_mulf(pos, N, h)); 1007 f3_sub(AP, proj, A); 1008 f3_sub(BP, proj, B); 1009 f3_sub(CP, proj, C); 1010 1011 /* Compute closest point */ 1012 CHK(s3d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) 1013 == RES_OK); 1014 CHK(!S3D_HIT_NONE(&hit)); 1015 CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) 1016 == RES_OK); 1017 1018 /* Check result 1019 * Due to known uv lack of accuracy we mainly check distance */ 1020 if(u >= 0 && v >= 0 && w >= 0) { 1021 /* proj is inside the triangle and is the closest point */ 1022 f3_set(closest, proj); 1023 dist = fabsf(h); 1024 } else { 1025 /* proj is outside the triangle */ 1026 float lab2 = f3_dot(AB, AB); 1027 float lac2 = f3_dot(AC, AC); 1028 float lbc2 = f3_dot(BC, BC); 1029 if(w >= 0 && u < 0) { 1030 /* proj is closest to either AB or AC */ 1031 x = f3_dot(AP, AB); 1032 if(v < 0 && x > 0) { 1033 /* proj is closest to AB */ 1034 f3_add(closest, A, f3_mulf(tmp, AB, MMIN(1, x / lab2))); 1035 } else { 1036 /* proj is closest to AC */ 1037 f3_add(closest, A, 1038 f3_mulf(tmp, AC, MMIN(1, MMAX(0, f3_dot(AP, AC) / lac2)))); 1039 } 1040 } 1041 else if(u >= 0 && v < 0) { 1042 /* proj is closest to either BC or BA */ 1043 x = f3_dot(BP, BC); 1044 if(w < 0 && x > 0) { 1045 /* proj is closest to BC */ 1046 f3_add(closest, B, f3_mulf(tmp, BC, MMIN(1, x / lbc2))); 1047 } else { 1048 /* proj is closest to BA */ 1049 f3_add(closest, B, 1050 f3_mulf(tmp, AB, -MMIN(1, MMAX(0, -f3_dot(BP, AB) / lab2)))); 1051 } 1052 } 1053 else if(v >= 0 && w < 0) { 1054 /* proj is closest to either CA or CB */ 1055 x = -f3_dot(CP, AC); 1056 if(u < 0 && x > 0) { 1057 /* proj is closest to CA */ 1058 f3_add(closest, C, f3_mulf(tmp, AC, -MMIN(1, x / lac2))); 1059 } else { 1060 /* proj is closest to CB */ 1061 f3_add(closest, C, 1062 f3_mulf(tmp, BC, -MMIN(1, MMAX(0, -f3_dot(CP, BC) / lbc2)))); 1063 } 1064 } 1065 else { FATAL("Unreachable code\n"); } 1066 dist = f3_len(f3_sub(tmp, pos, closest)); 1067 } 1068 CHK(eq_epsf(hit.distance, dist, eps)); 1069 /* Intersection-point's position is less accurate than hit distance */ 1070 d = f3_len(f3_sub(tmp, closest, attr.value)); 1071 CHK(d <= 10 * eps); 1072 f3_normalize(hit_N, hit.normal); 1073 CHK(f3_eq_eps(N, hit_N, FLT_EPSILON)); 1074 } 1075 1076 CHK(s3d_shape_ref_put(msh) == RES_OK); 1077 CHK(s3d_scene_view_ref_put(view) == RES_OK); 1078 CHK(s3d_scene_ref_put(scn) == RES_OK); 1079 } 1080 } 1081 } 1082 1083 static void 1084 test_single_triangle_instantiated(struct s3d_device* dev) 1085 { 1086 union { float f; uint32_t u32; } ucast; 1087 struct s3d_scene* scn = NULL; 1088 struct s3d_shape* shape = NULL; 1089 struct s3d_scene_view* view0 = NULL; 1090 struct s3d_scene_view* view1 = NULL; 1091 struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; 1092 struct s3d_hit hit0 = S3D_HIT_NULL; 1093 struct s3d_hit hit1 = S3D_HIT_NULL; 1094 float transform[12]; 1095 float vertices[9]; 1096 float transformed_vertices[9]; 1097 float query_pos[3]; 1098 1099 vdata.usage = S3D_POSITION; 1100 vdata.type = S3D_FLOAT3; 1101 vdata.get = triangle_get_pos; 1102 1103 /* Setup the query position. The following data are retrieved from a user 1104 * case and are thus setuped as it, in its raw binary format */ 1105 query_pos[0] = (ucast.u32 = 0xc1dc7a9e, ucast.f); 1106 query_pos[1] = (ucast.u32 = 0xc382179f, ucast.f); 1107 query_pos[2] = (ucast.u32 = 0xc32181b0, ucast.f); 1108 1109 f3(vertices+0, -28.5f, -298.5f, 69.964429f); 1110 f3(vertices+3, -27.0f, -298.5f, 69.899651f); 1111 f3(vertices+6, -27.0f, -297.0f, 69.204593f); 1112 1113 /* Setup the triangle transformation */ 1114 f33_rotation(transform, (float)MDEG2RAD(45.0), 0, 0); 1115 f3_splat(transform+9, 0); 1116 1117 /* Transform the triangle directly */ 1118 f33_mulf3(transformed_vertices+0, transform, vertices+0); 1119 f33_mulf3(transformed_vertices+3, transform, vertices+3); 1120 f33_mulf3(transformed_vertices+6, transform, vertices+6); 1121 f3_add(transformed_vertices+0, transformed_vertices+0, transform+9); 1122 f3_add(transformed_vertices+1, transformed_vertices+1, transform+9); 1123 f3_add(transformed_vertices+2, transformed_vertices+2, transform+9); 1124 1125 /* Setup the scene with the pre-transformed triangle */ 1126 CHK(s3d_scene_create(dev, &scn) == RES_OK); 1127 CHK(s3d_shape_create_mesh(dev, &shape) == RES_OK); 1128 CHK(s3d_scene_attach_shape(scn, shape) == RES_OK); 1129 CHK(s3d_mesh_setup_indexed_vertices 1130 (shape, 1, triangle_get_ids, 3, &vdata, 1, transformed_vertices) == RES_OK); 1131 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view0) == RES_OK); 1132 CHK(s3d_scene_ref_put(scn) == RES_OK); 1133 CHK(s3d_shape_ref_put(shape) == RES_OK); 1134 1135 /* Setup the same scene with the transformation performed by Star-3D through 1136 * instantiation */ 1137 CHK(s3d_scene_create(dev, &scn) == RES_OK); 1138 CHK(s3d_shape_create_mesh(dev, &shape) == RES_OK); 1139 CHK(s3d_scene_attach_shape(scn, shape) == RES_OK); 1140 CHK(s3d_mesh_setup_indexed_vertices 1141 (shape, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); 1142 CHK(s3d_shape_ref_put(shape) == RES_OK); 1143 CHK(s3d_scene_instantiate(scn, &shape) == RES_OK); 1144 CHK(s3d_instance_set_transform(shape, transform) == RES_OK); 1145 CHK(s3d_scene_ref_put(scn) == RES_OK); 1146 CHK(s3d_scene_create(dev, &scn) == RES_OK); 1147 CHK(s3d_scene_attach_shape(scn, shape) == RES_OK); 1148 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view1) == RES_OK); 1149 CHK(s3d_scene_ref_put(scn) == RES_OK); 1150 CHK(s3d_shape_ref_put(shape) == RES_OK); 1151 1152 /* Find the closest point */ 1153 CHK(s3d_scene_view_closest_point 1154 (view0, query_pos, (float)INF, NULL, &hit0) == RES_OK); 1155 CHK(s3d_scene_view_closest_point 1156 (view1, query_pos, (float)INF, NULL, &hit1) == RES_OK); 1157 1158 /* Check that the found hits are the same */ 1159 CHK(!S3D_HIT_NONE(&hit0)); 1160 CHK(!S3D_HIT_NONE(&hit1)); 1161 CHK(eq_epsf(hit0.distance, hit1.distance, 1.e-6f)); 1162 1163 CHK(s3d_scene_view_ref_put(view0) == RES_OK); 1164 CHK(s3d_scene_view_ref_put(view1) == RES_OK); 1165 } 1166 1167 /******************************************************************************* 1168 * Miscellaneous test 1169 ******************************************************************************/ 1170 static void 1171 test_api(struct s3d_device* dev) 1172 { 1173 struct s3d_hit hit = S3D_HIT_NULL; 1174 struct s3d_scene* scn = NULL; 1175 struct s3d_scene_view* view = NULL; 1176 float pos[3] = {0,0,0}; 1177 1178 CHK(s3d_scene_create(dev, &scn) == RES_OK); 1179 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); 1180 1181 CHK(s3d_scene_view_closest_point(NULL, pos, 1.f, NULL, &hit) == RES_BAD_ARG); 1182 CHK(s3d_scene_view_closest_point(view, NULL, 1.f, NULL, &hit) == RES_BAD_ARG); 1183 CHK(s3d_scene_view_closest_point(view, pos, 0.f, NULL, &hit) == RES_BAD_ARG); 1184 CHK(s3d_scene_view_closest_point(view, pos, 1.f, NULL, NULL) == RES_BAD_ARG); 1185 CHK(s3d_scene_view_closest_point(view, pos, 1.f, NULL, &hit) == RES_OK); 1186 CHK(S3D_HIT_NONE(&hit)); 1187 1188 CHK(s3d_scene_view_ref_put(view) == RES_OK); 1189 CHK(s3d_scene_view_create(scn, S3D_SAMPLE, &view) == RES_OK); 1190 CHK(s3d_scene_view_closest_point(view, pos, 1.f, NULL, &hit) == RES_BAD_OP); 1191 1192 CHK(s3d_scene_view_ref_put(view) == RES_OK); 1193 CHK(s3d_scene_ref_put(scn) == RES_OK); 1194 } 1195 1196 /******************************************************************************* 1197 * Main function 1198 ******************************************************************************/ 1199 int 1200 main(int argc, char** argv) 1201 { 1202 struct mem_allocator allocator; 1203 struct s3d_device* dev = NULL; 1204 (void)argc, (void)argv; 1205 1206 mem_init_proxy_allocator(&allocator, &mem_default_allocator); 1207 CHK(s3d_device_create(NULL, &allocator, 1, &dev) == RES_OK); 1208 1209 test_api(dev); 1210 test_single_triangle(dev); 1211 test_single_triangle_instantiated(dev); 1212 test_cbox(dev); 1213 test_sphere(dev); 1214 test_cbox_sphere(dev); 1215 1216 CHK(s3d_device_ref_put(dev) == RES_OK); 1217 1218 check_memory_allocator(&allocator); 1219 mem_shutdown_proxy_allocator(&allocator); 1220 CHK(mem_allocated_size() == 0); 1221 return 0; 1222 }