test_s3d_trace_ray.c (18109B)
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 */ 17 18 #include "s3d.h" 19 #include "test_s3d_camera.h" 20 #include "test_s3d_cbox.h" 21 #include "test_s3d_utils.h" 22 23 #include <rsys/float2.h> 24 #include <rsys/float3.h> 25 #include <rsys/image.h> 26 27 #include <string.h> 28 29 #define IMG_WIDTH 640 30 #define IMG_HEIGHT 480 31 32 struct ray_data { 33 float ray_org[3]; 34 float ray_dir[3]; 35 float ray_range[2]; 36 }; 37 38 static int 39 filter_func 40 (const struct s3d_hit* hit, 41 const float pos[3], 42 const float dir[3], 43 const float range[2], 44 void* ray_data, 45 void* filter_data) 46 { 47 struct ray_data* data = ray_data; 48 CHK(hit != NULL); 49 CHK(pos != NULL); 50 CHK(dir != NULL); 51 CHK(range != NULL); 52 CHK(ray_data != NULL); 53 CHK((uintptr_t)filter_data == 0xDECAFBAD); 54 CHK(S3D_HIT_NONE(hit) == 0); 55 CHK(f3_eq(pos, data->ray_org)); 56 CHK(f3_eq(dir, data->ray_dir)); 57 CHK(f2_eq(range, data->ray_range)); 58 return hit->prim.prim_id % 2 == 0; 59 } 60 61 static void 62 triangle_get_ids(const unsigned itri, unsigned ids[3], void* ctx) 63 { 64 (void)ctx; 65 CHK(itri == 0); 66 CHK(ids); 67 ids[0] = 0; 68 ids[1] = 1; 69 ids[2] = 2; 70 } 71 72 static void 73 triangle_get_pos(const unsigned ivert, float pos[3], void* ctx) 74 { 75 float* vertices = ctx; 76 CHK(ctx); 77 CHK(ivert < 3); 78 CHK(pos); 79 switch (ivert) { /* Setup a random triangle */ 80 case 0: f3_set(pos, vertices + 0); break; 81 case 1: f3_set(pos, vertices + 3); break; 82 case 2: f3_set(pos, vertices + 6); break; 83 default: FATAL("Unreachable code\n"); break; 84 } 85 } 86 87 int 88 main(int argc, char** argv) 89 { 90 struct mem_allocator allocator; 91 struct image img; 92 struct s3d_device* dev; 93 struct s3d_hit hit; 94 struct s3d_scene* scn; 95 struct s3d_scene* scn2; 96 struct s3d_scene_view* scnview; 97 struct s3d_shape* inst; 98 struct s3d_shape* walls; 99 struct s3d_shape* walls_copy; 100 struct s3d_shape* tall_block; 101 struct s3d_shape* short_block; 102 struct s3d_vertex_data attribs; 103 struct s3d_primitive prims[30]; 104 struct camera cam; 105 struct cbox_desc desc; 106 unsigned ntris, nverts; 107 size_t nprims; 108 size_t ix, iy; 109 float transform[12]; 110 float vec[3]; 111 float lower[3], upper[3]; 112 float pos[3], tgt[3], up[3]; 113 float org[3] = { 0.f, 0.f, 0.f }; 114 float dir[3] = { 0.f, 1.f, 0.f }; 115 float range[2] = { 0.f, FLT_MAX }; 116 unsigned inst_id; 117 unsigned walls_id; 118 unsigned tall_block_id; 119 unsigned short_block_id; 120 size_t a, i; 121 char filter = 0; 122 123 mem_init_proxy_allocator(&allocator, &mem_default_allocator); 124 125 if(argc > 1 && !strcmp(argv[1], "filter")) { 126 filter = 1; 127 } 128 129 image_init(&allocator, &img); 130 CHK(image_setup 131 (&img, IMG_WIDTH, IMG_HEIGHT, IMG_WIDTH*3, IMAGE_RGB8, NULL) == RES_OK); 132 133 CHK(s3d_device_create(NULL, &allocator, 0, &dev) == RES_OK); 134 CHK(s3d_scene_create(dev, &scn) == RES_OK); 135 136 /* Trace ray in empty scene */ 137 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 138 CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_OK); 139 CHK(S3D_HIT_NONE(&hit)); 140 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 141 142 attribs.usage = S3D_POSITION; 143 attribs.type = S3D_FLOAT3; 144 attribs.get = cbox_get_position; 145 146 ntris = cbox_walls_ntris; 147 nverts = cbox_walls_nverts; 148 desc.vertices = cbox_walls; 149 desc.indices = cbox_walls_ids; 150 CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK); 151 CHK(s3d_mesh_setup_indexed_vertices 152 (walls, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK); 153 CHK(s3d_scene_attach_shape(scn, walls) == RES_OK); 154 CHK(s3d_shape_ref_put(walls) == RES_OK); 155 156 CHK(s3d_scene_view_create(scn, S3D_TRACE, &scnview) == RES_OK); 157 CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, NULL, NULL, NULL) == RES_BAD_ARG); 158 CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, NULL, NULL, NULL) == RES_BAD_ARG); 159 CHK(s3d_scene_view_trace_ray(NULL, org, NULL, NULL, NULL, NULL) == RES_BAD_ARG); 160 CHK(s3d_scene_view_trace_ray(scnview, org, NULL, NULL, NULL, NULL) == RES_BAD_ARG); 161 CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, NULL, NULL, NULL) == RES_BAD_ARG); 162 CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, NULL, NULL, NULL) == RES_BAD_ARG); 163 CHK(s3d_scene_view_trace_ray(NULL, org, dir, NULL, NULL, NULL) == RES_BAD_ARG); 164 CHK(s3d_scene_view_trace_ray(scnview, org, dir, NULL, NULL, NULL) == RES_BAD_ARG); 165 CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, range, NULL, NULL) == RES_BAD_ARG); 166 CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, range, NULL, NULL) == RES_BAD_ARG); 167 CHK(s3d_scene_view_trace_ray(NULL, org, NULL, range, NULL, NULL) == RES_BAD_ARG); 168 CHK(s3d_scene_view_trace_ray(scnview, org, NULL, range, NULL, NULL) == RES_BAD_ARG); 169 CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, range, NULL, NULL) == RES_BAD_ARG); 170 CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, range, NULL, NULL) == RES_BAD_ARG); 171 CHK(s3d_scene_view_trace_ray(NULL, org, dir, range, NULL, NULL) == RES_BAD_ARG); 172 CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, NULL) == RES_BAD_ARG); 173 CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 174 CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 175 CHK(s3d_scene_view_trace_ray(NULL, org, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 176 CHK(s3d_scene_view_trace_ray(scnview, org, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 177 CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, NULL, NULL, &hit) == RES_BAD_ARG); 178 CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, NULL, NULL, &hit) == RES_BAD_ARG); 179 CHK(s3d_scene_view_trace_ray(NULL, org, dir, NULL, NULL, &hit) == RES_BAD_ARG); 180 CHK(s3d_scene_view_trace_ray(scnview, org, dir, NULL, NULL, &hit) == RES_BAD_ARG); 181 CHK(s3d_scene_view_trace_ray(NULL, NULL, NULL, range, NULL, &hit) == RES_BAD_ARG); 182 CHK(s3d_scene_view_trace_ray(scnview, NULL, NULL, range, NULL, &hit) == RES_BAD_ARG); 183 CHK(s3d_scene_view_trace_ray(NULL, org, NULL, range, NULL, &hit) == RES_BAD_ARG); 184 CHK(s3d_scene_view_trace_ray(scnview, org, NULL, range, NULL, &hit) == RES_BAD_ARG); 185 CHK(s3d_scene_view_trace_ray(NULL, NULL, dir, range, NULL, &hit) == RES_BAD_ARG); 186 CHK(s3d_scene_view_trace_ray(scnview, NULL, dir, range, NULL, &hit) == RES_BAD_ARG); 187 CHK(s3d_scene_view_trace_ray(NULL, org, dir, range, NULL, &hit) == RES_BAD_ARG); 188 CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_OK); 189 CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_OK); 190 f3(dir, 1.f, 1.f, 1.f); 191 CHK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit) == RES_BAD_ARG); 192 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 193 194 f3(dir, 0.f, 1.f, 0.f); 195 CHK(s3d_scene_clear(scn) == RES_OK); 196 197 /* Update the inst with the CBox tall block mesh */ 198 ntris = cbox_block_ntris; 199 nverts = cbox_block_nverts; 200 desc.vertices = cbox_short_block; 201 desc.indices = cbox_block_ids; 202 CHK(s3d_shape_create_mesh(dev, &tall_block) == RES_OK); 203 CHK(s3d_shape_get_id(tall_block, &tall_block_id) == RES_OK); 204 CHK(s3d_mesh_setup_indexed_vertices 205 (tall_block, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK); 206 CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK); 207 208 /* Update the inst vertices */ 209 desc.vertices = cbox_tall_block; 210 CHK(s3d_mesh_setup_indexed_vertices 211 (tall_block, ntris, S3D_KEEP, nverts, &attribs, 1, &desc) == RES_OK); 212 213 /* Create a the CBox short block inst */ 214 desc.vertices = cbox_short_block; 215 CHK(s3d_shape_create_mesh(dev, &short_block) == RES_OK); 216 CHK(s3d_shape_get_id(short_block, &short_block_id) == RES_OK); 217 CHK(s3d_mesh_setup_indexed_vertices 218 (short_block, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK); 219 CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK); 220 221 /* Instantiate the scene */ 222 CHK(s3d_scene_instantiate(scn, &inst) == RES_OK); 223 CHK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview) == RES_OK); 224 CHK(s3d_scene_view_primitives_count(scnview, &nprims) == RES_OK); 225 CHK(nprims == 20); 226 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 227 CHK(s3d_shape_get_id(inst, &inst_id) == RES_OK); 228 229 /* Create the CBox walls */ 230 desc.indices = cbox_walls_ids; 231 desc.vertices = cbox_walls; 232 nverts = cbox_walls_nverts; 233 ntris = cbox_walls_ntris; 234 CHK(s3d_shape_create_mesh(dev, &walls) == RES_OK); 235 CHK(s3d_shape_get_id(walls, &walls_id) == RES_OK); 236 CHK(s3d_mesh_setup_indexed_vertices 237 (walls, ntris, cbox_get_ids, nverts, &attribs, 1, &desc) == RES_OK); 238 CHK(s3d_scene_attach_shape(scn, walls) == RES_OK); 239 240 /* Check that the ids are all different */ 241 CHK(walls_id != short_block_id); 242 CHK(walls_id != tall_block_id); 243 CHK(short_block_id != tall_block_id); 244 245 /* Attach the CBox instance to a scene */ 246 CHK(s3d_scene_create(dev, &scn2) == RES_OK); 247 f3(org, -100.f, 0.f, -2.f); 248 CHK(s3d_scene_attach_shape(scn2, inst) == RES_OK); 249 CHK(s3d_instance_set_position(inst, org) == RES_OK); 250 251 CHK(s3d_shape_enable(inst, 0) == RES_OK); 252 CHK(s3d_scene_view_create(scn2, S3D_TRACE|S3D_SAMPLE, &scnview) == RES_OK); 253 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 254 255 CHK(s3d_shape_enable(inst, 1) == RES_OK); 256 CHK(s3d_scene_view_create(scn2, S3D_TRACE, &scnview) == RES_OK); 257 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 258 259 CHK(s3d_shape_create_mesh(dev, &walls_copy) == RES_OK); 260 CHK(s3d_mesh_copy(walls, walls_copy) == RES_OK); 261 CHK(s3d_shape_ref_put(walls) == RES_OK); 262 CHK(s3d_shape_get_id(walls_copy, &walls_id) == RES_OK); 263 if(filter) { 264 CHK(s3d_mesh_set_hit_filter_function 265 (walls_copy, filter_func, (void*)(uintptr_t)0xDECAFBAD) == RES_OK); 266 } 267 268 CHK(s3d_scene_clear(scn) == RES_OK); 269 CHK(s3d_scene_attach_shape(scn, walls_copy) == RES_OK); 270 CHK(s3d_scene_attach_shape(scn, short_block) == RES_OK); 271 CHK(s3d_scene_attach_shape(scn, tall_block) == RES_OK); 272 273 CHK(s3d_scene_view_create(scn2, S3D_TRACE|S3D_GET_PRIMITIVE, &scnview) == RES_OK); 274 CHK(s3d_scene_view_primitives_count(scnview, &nprims) == RES_OK); 275 CHK(nprims == 30); 276 277 CHK(s3d_scene_view_get_aabb(scnview, lower, upper) == RES_OK); 278 CHK(eq_epsf(lower[0], -100.f, 1.e-6f) == 1); 279 CHK(eq_epsf(lower[1], 0.f, 1.e-6f) == 1); 280 CHK(eq_epsf(lower[2], -2.f, 1.e-6f) == 1); 281 CHK(eq_epsf(upper[0], 452.f, 1.e-6f) == 1); 282 CHK(eq_epsf(upper[1], 559.f, 1.e-6f) == 1); 283 CHK(eq_epsf(upper[2], 546.f, 1.e-6f) == 1); 284 285 FOR_EACH(i, 0, nprims) { 286 size_t j; 287 CHK(s3d_scene_view_get_primitive(scnview, (unsigned)i, prims + i) == RES_OK); 288 CHK(S3D_PRIMITIVE_EQ(prims + i, &S3D_PRIMITIVE_NULL) == 0); 289 FOR_EACH(j, 0, i) { 290 CHK(S3D_PRIMITIVE_EQ(prims + i, prims + j) == 0); 291 } 292 293 CHK(s3d_primitive_get_transform(prims + i, transform) == RES_OK); 294 CHK(f3_eq(transform + 0, f3(vec, 1.f, 0.f, 0.f)) == 1); 295 CHK(f3_eq(transform + 3, f3(vec, 0.f, 1.f, 0.f)) == 1); 296 CHK(f3_eq(transform + 6, f3(vec, 0.f, 0.f, 1.f)) == 1); 297 CHK(f3_eq(transform + 9, f3(vec, -100.f, 0.f, -2.f)) == 1); 298 } 299 300 f3(pos, 178.f, -1000.f, 273.f); 301 f3(tgt, 178.f, 0.f, 273.f); 302 f3(up, 0.f, 0.f, 1.f); 303 camera_init(&cam, pos, tgt, up, (float)PI*0.25f, 304 (float)IMG_WIDTH/(float)IMG_HEIGHT); 305 FOR_EACH(iy, 0, IMG_HEIGHT) { 306 float pixel[2]; 307 308 pixel[1] = (float)iy/(float)IMG_HEIGHT; 309 FOR_EACH(ix, 0, IMG_WIDTH) { 310 struct ray_data ray_data; 311 const size_t ipix = (iy*IMG_WIDTH + ix) * 3/*RGB*/; 312 313 pixel[0] = (float)ix/(float)IMG_WIDTH; 314 camera_ray(&cam, pixel, org, dir); 315 316 f3_set(ray_data.ray_org, org); 317 f3_set(ray_data.ray_dir, dir); 318 f2_set(ray_data.ray_range, range); 319 CHK(s3d_scene_view_trace_ray 320 (scnview, org, dir, range, &ray_data, &hit) == RES_OK); 321 322 if(S3D_HIT_NONE(&hit)) { 323 ((uint8_t*)img.pixels)[ipix+0] = 0; 324 ((uint8_t*)img.pixels)[ipix+1] = 0; 325 ((uint8_t*)img.pixels)[ipix+2] = 0; 326 } else { 327 float N[3], len, dot, col[3] = { 1.f, 1.f, 1.f }; 328 struct s3d_attrib attr; 329 330 CHK(hit.prim.inst_id == inst_id); 331 CHK(hit.prim.geom_id == walls_id 332 || hit.prim.geom_id == tall_block_id 333 || hit.prim.geom_id == short_block_id); 334 CHK(hit.prim.geom_id < 10); 335 336 CHK(s3d_primitive_get_transform(&hit.prim, transform) == RES_OK); 337 CHK(f3_eq(transform + 0, f3(vec, 1.f, 0.f, 0.f)) == 1); 338 CHK(f3_eq(transform + 3, f3(vec, 0.f, 1.f, 0.f)) == 1); 339 CHK(f3_eq(transform + 6, f3(vec, 0.f, 0.f, 1.f)) == 1); 340 CHK(f3_eq(transform + 9, f3(vec, -100.f, 0.f, -2.f)) == 1); 341 342 CHK(s3d_primitive_get_attrib 343 (&hit.prim, S3D_POSITION, hit.uv, &attr) == RES_OK); 344 CHK(attr.type == S3D_FLOAT3); 345 CHK(attr.usage == S3D_POSITION); 346 f3_add(pos, f3_mulf(pos, dir, hit.distance), org); 347 CHK(f3_eq_eps 348 (pos, attr.value, 1.e-3f/*Sic O_o!! Really bad precision!*/)); 349 350 len = f3_normalize(N, hit.normal); 351 CHK(len != 0); 352 353 CHK(s3d_primitive_get_attrib 354 (&hit.prim, S3D_GEOMETRY_NORMAL, hit.uv, &attr) == RES_OK); 355 CHK(attr.type == S3D_FLOAT3); 356 CHK(attr.usage == S3D_GEOMETRY_NORMAL); 357 f3_normalize(attr.value, attr.value); 358 CHK(f3_eq_eps(attr.value, N, 1.e-6f) == 1); 359 360 CHK(hit.prim.scene_prim_id >= hit.prim.prim_id); 361 CHK(hit.prim.scene_prim_id < 30); 362 363 if(hit.prim.geom_id == walls_id) { 364 if(hit.prim.prim_id == 4 || hit.prim.prim_id == 5) { 365 col[0] = 1.f, col[1] = 0.f, col[2] = 0.f; 366 } else if(hit.prim.prim_id == 6 || hit.prim.prim_id == 7) { 367 col[0] = 0.f, col[1] = 1.f, col[2] = 0.f; 368 } 369 } 370 371 dot = f3_dot(N, dir); 372 if(dot < 0.f) 373 dot = f3_dot(f3_minus(N, N), dir); 374 375 ((uint8_t*)img.pixels)[ipix+0] = (unsigned char)(dot * col[0] * 255.f); 376 ((uint8_t*)img.pixels)[ipix+1] = (unsigned char)(dot * col[1] * 255.f); 377 ((uint8_t*)img.pixels)[ipix+2] = (unsigned char)(dot * col[2] * 255.f); 378 } 379 } 380 } 381 CHK(s3d_scene_view_ref_put(scnview) == RES_OK); 382 383 CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK); 384 image_release(&img); 385 386 CHK(s3d_shape_ref_put(inst) == RES_OK); 387 CHK(s3d_shape_ref_put(short_block) == RES_OK); 388 CHK(s3d_shape_ref_put(tall_block) == RES_OK); 389 CHK(s3d_shape_ref_put(walls_copy) == RES_OK); 390 CHK(s3d_scene_ref_put(scn) == RES_OK); 391 CHK(s3d_scene_ref_put(scn2) == RES_OK); 392 393 /* Check accuracy on a configuration whose analytic distance is known */ 394 FOR_EACH(a, 0, 16) { 395 const float amplitude = exp2f((float)a); 396 const float eps = 5e-6f * amplitude; 397 float vertices[9]; 398 struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; 399 struct s3d_scene_view* view = NULL; 400 struct s3d_shape* msh = NULL; 401 FOR_EACH(i, 0, 1000) { 402 float A[3], B[3], C[3], AB[3], AC[3], N[3]; 403 int j, n; 404 405 /* Randomly generate a triangle ABC */ 406 FOR_EACH(n, 0, 3) 407 A[n] = (rand_canonic() - 0.5f) * amplitude; 408 do { 409 FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude; 410 } while (f3_eq_eps(A, B, eps)); 411 do { 412 FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude; 413 } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps)); 414 415 f3_sub(AB, B, A); 416 f3_sub(AC, C, A); 417 f3_cross(N, AB, AC); 418 f3_normalize(N, N); 419 420 f3_set(vertices + 0, A); 421 f3_set(vertices + 3, B); 422 f3_set(vertices + 6, C); 423 424 CHK(s3d_scene_create(dev, &scn) == RES_OK); 425 CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK); 426 CHK(s3d_scene_attach_shape(scn, msh) == RES_OK); 427 428 vdata.usage = S3D_POSITION; 429 vdata.type = S3D_FLOAT3; 430 vdata.get = triangle_get_pos; 431 CHK(s3d_mesh_setup_indexed_vertices 432 (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK); 433 434 CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK); 435 436 FOR_EACH(j, 0, 1000) { 437 float proj[3]; /* Projection of pos on the line */ 438 float tmp[3]; 439 float u, v, w, h; 440 441 /* Randomly generate a pos not on the triangle 442 * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */ 443 u = 3 * rand_canonic() - 1; 444 v = 3 * rand_canonic() - 1; 445 w = 1 - u - v; 446 h = (2 * rand_canonic() - 1) * amplitude; 447 f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v))); 448 f3_add(pos, proj, f3_mulf(pos, N, h)); 449 450 /* Raytrace from pos towards proj */ 451 f3_mulf(dir, N, (h > 0 ? -1.f : 1.f)); 452 f3_normalize(dir, dir); 453 CHK(s3d_scene_view_trace_ray(view, pos, dir, range, NULL, &hit) 454 == RES_OK); 455 456 /* Check result */ 457 if(u < 0 || v < 0 || w < 0) { 458 if(!S3D_HIT_NONE(&hit)) 459 CHK(u >= -FLT_EPSILON && v >= -FLT_EPSILON && w >= -FLT_EPSILON); 460 } else { 461 if(S3D_HIT_NONE(&hit)) 462 CHK(u <= FLT_EPSILON || v <= FLT_EPSILON || w <= FLT_EPSILON); 463 } 464 if(!S3D_HIT_NONE(&hit)) { 465 struct s3d_attrib attr; 466 float d; 467 CHK(eq_epsf(hit.distance, fabsf(h), eps)); 468 CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr) 469 == RES_OK); 470 /* Intersection-point's position is less accurate than hit distance */ 471 d = f3_len(f3_sub(tmp, attr.value, proj)); 472 CHK(d <= 10 * eps); 473 } 474 } 475 476 CHK(s3d_shape_ref_put(msh) == RES_OK); 477 CHK(s3d_scene_view_ref_put(view) == RES_OK); 478 CHK(s3d_scene_ref_put(scn) == RES_OK); 479 } 480 } 481 482 CHK(s3d_device_ref_put(dev) == RES_OK); 483 484 check_memory_allocator(&allocator); 485 mem_shutdown_proxy_allocator(&allocator); 486 CHK(mem_allocated_size() == 0); 487 488 return 0; 489 } 490