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