sgf_scene.c (11671B)
1 /* Copyright (C) 2021, 2024 |Meso|Star> (contact@meso-star.com) 2 * Copyright (C) 2015-2018 EDF S.A., France (syrthes-support@edf.fr) 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #include "sgf.h" 18 #include "sgf_device_c.h" 19 #include "sgf_scene_c.h" 20 21 #include <star/s2d.h> 22 #include <star/s3d.h> 23 24 /******************************************************************************* 25 * Helper functions 26 ******************************************************************************/ 27 static int 28 hit_filter_s3d 29 (const struct s3d_hit* hit, 30 const float org[3], 31 const float dir[3], 32 const float range[2], 33 void* ray_data, 34 void* filter_data) 35 { 36 struct s3d_primitive* prim_from = ray_data; 37 (void)org, (void)dir, (void)range, (void)filter_data; 38 39 if(!ray_data) return 0; 40 /* Discard primitive from which the ray starts from */ 41 if(S3D_PRIMITIVE_EQ(prim_from, &hit->prim)) return 1; 42 /* Ray starts on an edge and intersect the neighbor triangle */ 43 if(hit->distance <= 0) return 1; 44 return 0; 45 } 46 47 static int 48 hit_filter_s2d 49 (const struct s2d_hit* hit, 50 const float org[3], 51 const float dir[3], 52 const float range[2], 53 void* ray_data, 54 void* filter_data) 55 { 56 struct s2d_primitive* prim_from = ray_data; 57 (void)org, (void)dir, (void)range, (void)filter_data; 58 59 if(!ray_data) return 0; 60 /* Discard primitive from which the ray starts from */ 61 if(S2D_PRIMITIVE_EQ(prim_from, &hit->prim)) return 1; 62 /* Ray starts on an edge and intersect the neighbor triangle */ 63 if(hit->distance <= 0) return 1; 64 return 0; 65 } 66 67 static FINLINE int 68 check_scene_desc(const struct sgf_scene_desc* desc) 69 { 70 return desc 71 && desc->get_indices 72 && desc->get_emissivity 73 && desc->get_reflectivity 74 && desc->get_specularity 75 && desc->get_position 76 && desc->nprims 77 && desc->nverts 78 && desc->nbands; 79 } 80 81 static INLINE void 82 scene_release_s3d(struct sgf_scene* scn) 83 { 84 if(scn->geometry.s3d.scn) { 85 S3D(scene_ref_put(scn->geometry.s3d.scn)); 86 scn->geometry.s3d.scn = NULL; 87 } 88 if(scn->geometry.s3d.shape) { 89 S3D(shape_ref_put(scn->geometry.s3d.shape)); 90 scn->geometry.s3d.shape = NULL; 91 } 92 scn->dimensionality = 0; 93 } 94 95 static INLINE res_T 96 scene_init_s3d(struct sgf_scene* scn) 97 { 98 res_T res = RES_OK; 99 ASSERT(scn); 100 101 /* Create Star-3D data structures */ 102 res = s3d_scene_create(scn->dev->s3d, &scn->geometry.s3d.scn); 103 if(res != RES_OK) goto error; 104 res = s3d_shape_create_mesh(scn->dev->s3d, &scn->geometry.s3d.shape); 105 if(res != RES_OK) goto error; 106 res = s3d_mesh_set_hit_filter_function 107 (scn->geometry.s3d.shape, &hit_filter_s3d, NULL); 108 if(res != RES_OK) goto error; 109 res = s3d_scene_attach_shape(scn->geometry.s3d.scn, scn->geometry.s3d.shape); 110 if(res != RES_OK) goto error; 111 scn->dimensionality = 3; 112 113 exit: 114 return res; 115 error: 116 scene_release_s3d(scn); 117 goto exit; 118 } 119 120 static INLINE void 121 scene_release_s2d(struct sgf_scene* scn) 122 { 123 if(scn->geometry.s2d.scn) { 124 S2D(scene_ref_put(scn->geometry.s2d.scn)); 125 scn->geometry.s2d.scn = NULL; 126 } 127 if(scn->geometry.s2d.shape) { 128 S2D(shape_ref_put(scn->geometry.s2d.shape)); 129 scn->geometry.s2d.shape = NULL; 130 } 131 scn->dimensionality = 0; 132 } 133 134 static INLINE res_T 135 scene_init_s2d(struct sgf_scene* scn) 136 { 137 res_T res = RES_OK; 138 ASSERT(scn); 139 140 /* Create Star-3D data structures */ 141 res = s2d_scene_create(scn->dev->s2d, &scn->geometry.s2d.scn); 142 if(res != RES_OK) goto error; 143 res = s2d_shape_create_line_segments(scn->dev->s2d, &scn->geometry.s2d.shape); 144 if(res != RES_OK) goto error; 145 res = s2d_line_segments_set_hit_filter_function 146 (scn->geometry.s2d.shape, &hit_filter_s2d, NULL); 147 if(res != RES_OK) goto error; 148 res = s2d_scene_attach_shape(scn->geometry.s2d.scn, scn->geometry.s2d.shape); 149 if(res != RES_OK) goto error; 150 scn->dimensionality = 2; 151 152 exit: 153 return res; 154 error: 155 scene_release_s2d(scn); 156 goto exit; 157 } 158 159 static res_T 160 scene_setup 161 (struct sgf_scene* scn, 162 const struct sgf_scene_desc* desc, 163 const int dimensionality, 164 const char* caller) 165 { 166 struct s2d_vertex_data vdata2d; 167 struct s3d_vertex_data vdata3d; 168 unsigned iprim, iband, i; 169 double* abs, *emi, *refl, *spec; 170 res_T res = RES_OK; 171 172 if(!scn || !check_scene_desc(desc)) { 173 res = RES_BAD_ARG; 174 goto error; 175 } 176 177 /* Allocate the buffers of material attributes */ 178 #define RESIZE(V, Name) { \ 179 res = darray_double_resize(&(V), desc->nprims*desc->nbands); \ 180 if(res != RES_OK) { \ 181 log_error(scn->dev, "%s: couldn't allocate the "Name" buffer.", caller); \ 182 goto error; \ 183 } \ 184 } (void)0 185 RESIZE(scn->emi, "emissivity"); 186 RESIZE(scn->refl, "reflectivity"); 187 RESIZE(scn->spec, "specularity"); 188 if(desc->get_absorption) RESIZE(scn->abs, "absorption"); 189 #undef RESIZE 190 191 /* Setup the material */ 192 abs = desc->get_absorption ? darray_double_data_get(&scn->abs) : NULL; 193 emi = darray_double_data_get(&scn->emi); 194 refl = darray_double_data_get(&scn->refl); 195 spec = darray_double_data_get(&scn->spec); 196 i = 0; 197 FOR_EACH(iband, 0, desc->nbands) { 198 FOR_EACH(iprim, 0, desc->nprims) { 199 #define FETCH(Dst, Name, Low, Upp) { \ 200 (Dst) = desc->get_##Name(iprim, iband, desc->context); \ 201 if((Dst) < (Low) || (Dst) > (Upp)) { \ 202 log_error(scn->dev, "%s: invalid "STR(Name)" `%g'.\n", caller, (Dst)); \ 203 res = RES_BAD_ARG; \ 204 goto error; \ 205 } \ 206 } (void) 0 207 208 if(abs) FETCH(abs[i], absorption, 0, DBL_MAX); 209 FETCH(emi[i], emissivity, 0, 1); 210 FETCH(refl[i], reflectivity, 0, 1); 211 FETCH(spec[i], specularity, 0, 1); 212 #undef FETCH 213 ++i; 214 }} 215 216 /* Initialise the Star-3D structures */ 217 if(scn->dimensionality != dimensionality) { 218 if(scn->dimensionality == 2) scene_release_s2d(scn); 219 if(scn->dimensionality == 3) scene_release_s3d(scn); 220 221 if(dimensionality == 2) { 222 res = scene_init_s2d(scn); 223 } else { 224 res = scene_init_s3d(scn); 225 } 226 if(res != RES_OK) goto error; 227 } 228 229 /* Setup the geometry */ 230 switch(scn->dimensionality) { 231 case 2: 232 vdata2d.usage = S2D_POSITION; 233 vdata2d.type = S2D_FLOAT2; 234 vdata2d.get = desc->get_position; 235 s2d_line_segments_setup_indexed_vertices(scn->geometry.s2d.shape, 236 desc->nprims, desc->get_indices, desc->nverts, &vdata2d, 1, 237 desc->context); 238 break; 239 case 3: 240 vdata3d.usage = S3D_POSITION; 241 vdata3d.type = S3D_FLOAT3; 242 vdata3d.get = desc->get_position; 243 res = s3d_mesh_setup_indexed_vertices(scn->geometry.s3d.shape, 244 desc->nprims, desc->get_indices, desc->nverts, &vdata3d, 1, 245 desc->context); 246 break; 247 default: FATAL("Unreachable code\n"); break; 248 } 249 250 scn->nbands = desc->nbands; 251 scn->nprims = desc->nprims; 252 scn->has_medium = desc->get_absorption != NULL; 253 254 exit: 255 return res; 256 error: 257 if(scn) { 258 darray_double_clear(&scn->abs); 259 darray_double_clear(&scn->emi); 260 darray_double_clear(&scn->refl); 261 darray_double_clear(&scn->spec); 262 scn->nprims = 0; 263 scn->nbands = 0; 264 scn->has_medium = 0; 265 } 266 goto exit; 267 } 268 269 270 271 static void 272 scene_release(ref_T* ref) 273 { 274 struct sgf_device* dev; 275 struct sgf_scene* scn; 276 ASSERT(ref); 277 scn = CONTAINER_OF(ref, struct sgf_scene, ref); 278 dev = scn->dev; 279 switch(scn->dimensionality) { 280 case 2: scene_release_s2d(scn); break; 281 case 3: scene_release_s3d(scn); break; 282 default: FATAL("Unreachable code\n"); break; 283 } 284 darray_double_release(&scn->abs); 285 darray_double_release(&scn->emi); 286 darray_double_release(&scn->refl); 287 darray_double_release(&scn->spec); 288 MEM_RM(dev->allocator, scn); 289 SGF(device_ref_put(dev)); 290 } 291 292 /******************************************************************************* 293 * Exported functions 294 ******************************************************************************/ 295 res_T 296 sgf_scene_create(struct sgf_device* dev, struct sgf_scene** out_scn) 297 { 298 struct sgf_scene* scn = NULL; 299 res_T res = RES_OK; 300 301 if(!dev || !out_scn) { 302 res = RES_BAD_ARG; 303 goto error; 304 } 305 306 scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct sgf_scene)); 307 if(!scn) { 308 res = RES_MEM_ERR; 309 goto error; 310 } 311 ref_init(&scn->ref); 312 SGF(device_ref_get(dev)); 313 scn->dev = dev; 314 315 /* Initialise the buffers of material properties */ 316 darray_double_init(dev->allocator, &scn->abs); 317 darray_double_init(dev->allocator, &scn->emi); 318 darray_double_init(dev->allocator, &scn->refl); 319 darray_double_init(dev->allocator, &scn->spec); 320 321 exit: 322 if(out_scn) *out_scn = scn; 323 return res; 324 error: 325 if(scn) { 326 SGF(scene_ref_put(scn)); 327 scn = NULL; 328 } 329 goto exit; 330 } 331 332 res_T 333 sgf_scene_ref_get(struct sgf_scene* scn) 334 { 335 if(!scn) return RES_BAD_ARG; 336 ref_get(&scn->ref); 337 return RES_OK; 338 } 339 340 res_T 341 sgf_scene_ref_put(struct sgf_scene* scn) 342 { 343 if(!scn) return RES_BAD_ARG; 344 ref_put(&scn->ref, scene_release); 345 return RES_OK; 346 } 347 348 res_T 349 sgf_scene_setup_3d(struct sgf_scene* scn, const struct sgf_scene_desc* desc) 350 { 351 return scene_setup(scn, desc, 3, FUNC_NAME); 352 } 353 res_T 354 sgf_scene_setup_2d(struct sgf_scene* scn, const struct sgf_scene_desc* desc) 355 { 356 return scene_setup(scn, desc, 2, FUNC_NAME); 357 } 358 359 res_T 360 sgf_scene_primitives_count(struct sgf_scene* scn, unsigned* nprims) 361 { 362 if(!scn || !nprims) return RES_BAD_ARG; 363 *nprims = scn->nprims; 364 return RES_OK; 365 } 366 367 res_T 368 sgf_scene_begin_integration(struct sgf_scene* scn) 369 { 370 int i; 371 res_T res = RES_OK; 372 if(!scn) return RES_BAD_ARG; 373 374 if(scn->dimensionality == 2) { 375 i = scn->geometry.s2d.view == NULL; 376 } else { 377 ASSERT(scn->dimensionality == 3); 378 i = scn->geometry.s3d.view == NULL; 379 } 380 381 if(!i) { 382 log_error(scn->dev, "%s: an integration process is already active.\n", 383 FUNC_NAME); 384 return RES_BAD_OP; 385 } 386 387 if(scn->dimensionality == 2) { 388 res = s2d_scene_view_create(scn->geometry.s2d.scn, 389 S2D_TRACE|S2D_GET_PRIMITIVE, &scn->geometry.s2d.view); 390 } else { 391 res = s3d_scene_view_create(scn->geometry.s3d.scn, 392 S3D_TRACE|S3D_GET_PRIMITIVE, &scn->geometry.s3d.view); 393 } 394 return res; 395 } 396 397 res_T 398 sgf_scene_end_integration(struct sgf_scene* scn) 399 { 400 res_T res = RES_OK; 401 if(!scn) return RES_BAD_ARG; 402 if(scn->dimensionality == 2) { 403 if(!scn->geometry.s2d.view) { 404 res = RES_BAD_OP; 405 } else { 406 res = s2d_scene_view_ref_put(scn->geometry.s2d.view); 407 if(res == RES_OK) scn->geometry.s2d.view = NULL; 408 } 409 } else { 410 if(!scn->geometry.s3d.view) { 411 res = RES_BAD_OP; 412 } else { 413 res = s3d_scene_view_ref_put(scn->geometry.s3d.view); 414 if(res == RES_OK) scn->geometry.s3d.view = NULL; 415 } 416 } 417 if(res != RES_OK) goto error; 418 exit: 419 return res; 420 error: 421 goto exit; 422 } 423