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