test_s2d_scene_view2.c (9809B)
1 /* Copyright (C) 2016-2021, 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 #include "s2d.h" 17 #include "test_s2d_utils.h" 18 19 #include <rsys/float2.h> 20 #include <rsys/stretchy_array.h> 21 22 #include <string.h> 23 24 #define NSAMPS 10000 25 26 struct ray_data { 27 struct s2d_primitive prim; 28 float ray_org[2]; 29 float ray_dir[2]; 30 float ray_range[2]; 31 }; 32 33 static INLINE float* 34 ran_semi_disk_cos_local(float samp[2]) 35 { 36 samp[0] = rand_canonic() * 2.f - 1.f; 37 samp[1] = (float)sqrt(1 - samp[0]*samp[0]); 38 return samp; 39 } 40 41 static INLINE float* 42 ran_semi_disk_cos(const float up[2], float samp[2]) 43 { 44 float tmp[2], v[2]; 45 CHK(f2_is_normalized(samp) == 1); 46 ran_semi_disk_cos_local(tmp); 47 v[0] = -up[1] * tmp[0] + up[0]*tmp[1]; 48 v[1] = up[0] * tmp[0] + up[1]*tmp[1];; 49 CHK(f2_is_normalized(v) == 1); 50 return f2_set(samp, v); 51 } 52 53 static int 54 discard_self_hit 55 (const struct s2d_hit* hit, 56 const float org[2], 57 const float dir[2], 58 const float range[2], 59 void* ray_data, 60 void* filter_data) 61 { 62 struct ray_data* data = ray_data; 63 CHK(hit != NULL); 64 CHK(org != NULL); 65 CHK(dir != NULL); 66 CHK(range != NULL); 67 CHK((intptr_t)filter_data == (intptr_t)0xDECAFBAD); 68 if(!ray_data) return 0; 69 CHK(f2_eq(data->ray_org, org)); 70 CHK(f2_eq(data->ray_dir, dir)); 71 CHK(f2_eq(data->ray_range, range)); 72 return S2D_PRIMITIVE_EQ(&data->prim, &hit->prim); 73 } 74 75 static struct s2d_shape* 76 create_circle_shape 77 (struct s2d_device* dev, 78 const float radius, 79 const float center[2], 80 const unsigned nsteps) 81 { 82 float* positions = NULL; 83 unsigned* indices = NULL; 84 struct s2d_shape* shape; 85 struct s2d_vertex_data vdata; 86 struct line_segments_desc desc = { NULL, NULL }; 87 const double step = 2.0*PI/(double)nsteps; 88 unsigned i; 89 90 CHK(s2d_shape_create_line_segments(dev, &shape) == RES_OK); 91 CHK(s2d_line_segments_set_hit_filter_function 92 (shape, discard_self_hit, (void*)0xDECAFBAD) == RES_OK); 93 94 CHK(nsteps > 4); 95 CHK(center != NULL); 96 CHK(sa_add(positions, nsteps*2/*#coords per vertex*/) != NULL); 97 CHK(sa_add(indices, nsteps*2/*#ids per segment*/) != NULL); 98 99 FOR_EACH(i, 0, nsteps) { 100 const double theta = i*step; 101 const double x = radius*cos(theta) + center[0]; 102 const double y = radius*sin(theta) + center[1]; 103 positions[i*2 + 0] = (float)x; 104 positions[i*2 + 1] = (float)y; 105 } 106 107 FOR_EACH(i, 0, nsteps) { 108 indices[i*2 + 0] = i; 109 indices[i*2 + 1] = (i+1) % nsteps; 110 } 111 112 desc.vertices = positions; 113 desc.indices = indices; 114 115 vdata.type = S2D_FLOAT2; 116 vdata.usage = S2D_POSITION; 117 vdata.get = line_segments_get_position; 118 119 CHK(s2d_line_segments_setup_indexed_vertices 120 (shape, nsteps, line_segments_get_ids, nsteps, &vdata, 1, (void*)&desc) 121 == RES_OK); 122 123 CHK(s2d_shape_flip_contour(shape) == RES_OK); 124 125 sa_release(positions); 126 sa_release(indices); 127 return shape; 128 } 129 130 int 131 main(int argc, char** argv) 132 { 133 struct mem_allocator allocator; 134 struct ray_data ray_data; 135 struct s2d_device* dev; 136 struct s2d_scene* scn; 137 struct s2d_scene_view* scnview; 138 struct s2d_shape* shape; 139 struct s2d_primitive prim; 140 unsigned shape_nsteps[3] = { 16, 8, 5 }; 141 unsigned shape_ids[3]; 142 int* shape_prims[3] ={ NULL, NULL, NULL }; 143 int* scene_prims = NULL; 144 size_t i; 145 size_t nprims; 146 float tmp[2]; 147 float sum, sum_sqr; 148 float E, V, SE; 149 float area, length; 150 int ishape; 151 (void)argc, (void)argv; 152 153 mem_init_proxy_allocator(&allocator, &mem_default_allocator); 154 155 CHK(s2d_device_create(NULL, &allocator, 0, &dev) == RES_OK); 156 CHK(s2d_scene_create(dev, &scn) == RES_OK); 157 158 CHK(sa_add(shape_prims[0], shape_nsteps[0]) != NULL); 159 CHK(sa_add(shape_prims[1], shape_nsteps[1]) != NULL); 160 CHK(sa_add(shape_prims[2], shape_nsteps[2]) != NULL); 161 CHK(sa_add 162 (scene_prims, shape_nsteps[0]+shape_nsteps[1]+shape_nsteps[2]) != NULL); 163 164 shape = create_circle_shape(dev, 1.f, f2(tmp, 0.f, 0.f), shape_nsteps[0]); 165 CHK(s2d_shape_get_id(shape, &shape_ids[0]) == RES_OK); 166 CHK(shape_ids[0] != S2D_INVALID_ID); 167 CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); 168 CHK(s2d_shape_ref_put(shape) == RES_OK); 169 170 shape = create_circle_shape(dev, 0.5f, f2(tmp, 2.f, 0.5f), shape_nsteps[1]); 171 CHK(s2d_shape_get_id(shape, &shape_ids[1]) == RES_OK); 172 CHK(shape_ids[1] != S2D_INVALID_ID); 173 CHK(shape_ids[1] != shape_ids[0]); 174 CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); 175 CHK(s2d_shape_ref_put(shape) == RES_OK); 176 177 shape = create_circle_shape(dev, 0.25f, f2(tmp, 1.5f, -0.25f), shape_nsteps[2]); 178 CHK(s2d_shape_get_id(shape, &shape_ids[2]) == RES_OK); 179 CHK(shape_ids[2] != S2D_INVALID_ID); 180 CHK(shape_ids[2] != shape_ids[0]); 181 CHK(shape_ids[2] != shape_ids[1]); 182 CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); 183 CHK(s2d_shape_ref_put(shape) == RES_OK); 184 185 CHK(s2d_scene_view_create 186 (scn, S2D_SAMPLE|S2D_GET_PRIMITIVE|S2D_TRACE, &scnview) == RES_OK); 187 188 /* Test sampling */ 189 memset(shape_prims[0], 0, sa_size(shape_prims[0])*sizeof(shape_prims[0][0])); 190 memset(shape_prims[1], 0, sa_size(shape_prims[1])*sizeof(shape_prims[1][0])); 191 memset(shape_prims[2], 0, sa_size(shape_prims[2])*sizeof(shape_prims[2][0])); 192 memset(scene_prims, 0, sa_size(scene_prims)*sizeof(scene_prims[0])); 193 FOR_EACH(i, 0, 1024) { 194 struct s2d_attrib attr; 195 float s; 196 CHK(s2d_scene_view_sample(scnview, rand_canonic(), rand_canonic(), &prim, &s) 197 == RES_OK); 198 CHK(s2d_primitive_get_attrib(&prim, S2D_POSITION, s, &attr) == RES_OK); 199 CHK(attr.type == S2D_FLOAT2); 200 201 FOR_EACH(ishape, 0, 3) if(prim.geom_id == shape_ids[ishape]) break; 202 CHK(ishape != 3); 203 204 /* Mark the shape primitive as sampled */ 205 CHK(prim.prim_id < shape_nsteps[ishape]); 206 shape_prims[ishape][prim.prim_id] = 1; 207 208 /* Mark the scene primitive as sampled */ 209 CHK(prim.scene_prim_id < sa_size(scene_prims)); 210 scene_prims[prim.scene_prim_id] = 1; 211 } 212 213 /* Check that all primitives were sampled */ 214 FOR_EACH(i, 0, sa_size(shape_prims[0])) CHK(shape_prims[0][i] == 1); 215 FOR_EACH(i, 0, sa_size(shape_prims[1])) CHK(shape_prims[1][i] == 1); 216 FOR_EACH(i, 0, sa_size(shape_prims[2])) CHK(shape_prims[2][i] == 1); 217 FOR_EACH(i, 0, sa_size(scene_prims)) CHK(scene_prims[i] == 1); 218 219 /* Check iteration */ 220 memset(shape_prims[0], 0, sa_size(shape_prims[0])*sizeof(shape_prims[0][0])); 221 memset(shape_prims[1], 0, sa_size(shape_prims[1])*sizeof(shape_prims[1][0])); 222 memset(shape_prims[2], 0, sa_size(shape_prims[2])*sizeof(shape_prims[2][0])); 223 memset(scene_prims, 0, sa_size(scene_prims)*sizeof(scene_prims[0])); 224 CHK(s2d_scene_view_primitives_count(scnview, &nprims) == RES_OK); 225 CHK(sa_size(scene_prims) == nprims); 226 FOR_EACH(i, 0, nprims) { 227 228 CHK(s2d_scene_view_get_primitive(scnview, (unsigned)i, &prim) == RES_OK); 229 FOR_EACH(ishape, 0, 3) if(prim.geom_id == shape_ids[ishape]) break; 230 CHK(ishape != 3); 231 232 /* Mark the shape primitive as visited */ 233 CHK(prim.prim_id < shape_nsteps[ishape]); 234 shape_prims[ishape][prim.prim_id] = 1; 235 236 /* Mark the scene primitive as visited */ 237 CHK(prim.scene_prim_id < sa_size(scene_prims)); 238 scene_prims[prim.scene_prim_id] = 1; 239 } 240 241 /* Check that all primitives were visited */ 242 FOR_EACH(i, 0, sa_size(shape_prims[0])) CHK(shape_prims[0][i] == 1); 243 FOR_EACH(i, 0, sa_size(shape_prims[1])) CHK(shape_prims[1][i] == 1); 244 FOR_EACH(i, 0, sa_size(shape_prims[2])) CHK(shape_prims[2][i] == 1); 245 FOR_EACH(i, 0, sa_size(scene_prims)) CHK(scene_prims[i] == 1); 246 247 /* Check the ray tracing by numerically compute PI*S/P aka 4V/S in 2D */ 248 sum = sum_sqr = 0; 249 FOR_EACH(i, 0, NSAMPS) { 250 const float range[2] = { 0.f, FLT_MAX }; 251 struct s2d_attrib attr; 252 struct s2d_hit hit; 253 float P[2], N[2]; 254 float s; 255 256 CHK(s2d_scene_view_sample 257 (scnview, rand_canonic(), rand_canonic(), &prim, &s) == RES_OK); 258 259 CHK(s2d_primitive_get_attrib(&prim, S2D_POSITION, s, &attr) == RES_OK); 260 CHK(attr.type == S2D_FLOAT2); 261 f2_set(P, attr.value); 262 263 CHK(s2d_primitive_get_attrib(&prim, S2D_GEOMETRY_NORMAL, s, &attr) == RES_OK); 264 CHK(attr.type == S2D_FLOAT2); 265 CHK(f2_normalize(N, attr.value) != 0.f); 266 267 f2_normalize(tmp, f2(tmp, 1, 1)); 268 ran_semi_disk_cos(N, tmp); 269 270 f2_set(ray_data.ray_org, P); 271 f2_set(ray_data.ray_dir, tmp); 272 f2_set(ray_data.ray_range, range); 273 ray_data.prim = prim; 274 CHK(s2d_scene_view_trace_ray(scnview, P, tmp, range, &ray_data, &hit) == RES_OK); 275 CHK(S2D_HIT_NONE(&hit) == 0); 276 277 sum += hit.distance; 278 sum_sqr += hit.distance*hit.distance; 279 } 280 281 CHK(s2d_scene_view_compute_contour_length(scnview, &length) == RES_OK); 282 CHK(s2d_scene_view_compute_area(scnview, &area) == RES_OK); 283 284 E = sum / (float)NSAMPS; 285 V = sum_sqr / (float)NSAMPS - E*E; 286 SE = (float)sqrt(V/(float)NSAMPS); 287 printf("PI*S / P = %g ~ %g +/- %g\n",(float)PI*area / length, E, SE); 288 CHK(eq_epsf((float)PI*area / length, E, 3*SE) == 1); 289 290 CHK(s2d_scene_view_ref_put(scnview) == RES_OK); 291 292 CHK(s2d_scene_ref_put(scn) == RES_OK); 293 CHK(s2d_device_ref_put(dev) == RES_OK); 294 295 sa_release(scene_prims); 296 sa_release(shape_prims[0]); 297 sa_release(shape_prims[1]); 298 sa_release(shape_prims[2]); 299 300 check_memory_allocator(&allocator); 301 mem_shutdown_proxy_allocator(&allocator); 302 CHK(mem_allocated_size() == 0); 303 304 return 0; 305 } 306