s3d_primitive.c (12418B)
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 #include "s3d_c.h" 19 #include "s3d_device_c.h" 20 #include "s3d_instance.h" 21 #include "s3d_mesh.h" 22 #include "s3d_scene_c.h" 23 #include "s3d_sphere.h" 24 25 #include <rsys/float33.h> 26 27 /******************************************************************************* 28 * Helper functions 29 ******************************************************************************/ 30 static res_T 31 mesh_get_primitive_attrib 32 (const struct geometry* geom, 33 const float* transform, /* Can be NULL => no transform */ 34 const char flip_surface, 35 const struct s3d_primitive* prim, 36 const enum s3d_attrib_usage usage, 37 const float uv[2], 38 struct s3d_attrib* attrib) 39 { 40 const uint32_t* ids; 41 float w; 42 res_T res = RES_OK; 43 ASSERT(geom && geom->type == GEOM_MESH && prim && prim->shape__ == geom); 44 ASSERT(uv && attrib); 45 46 /* Unormalized barycentric coordinates */ 47 w = CLAMP(1.f - uv[0] - uv[1], 0.f, 1.f); 48 if(uv[0] < 0.f || uv[1] < 0.f || uv[0] > 1.f || uv[1] > 1.f 49 || !eq_epsf(w + uv[0] + uv[1], 1.f, 1.e-3f)) { 50 res = RES_BAD_ARG; 51 goto error; 52 } 53 54 /* The mesh haven't the required mesh attrib */ 55 if(usage != S3D_GEOMETRY_NORMAL && !geom->data.mesh->attribs[usage]) { 56 res = RES_BAD_ARG; 57 goto error; 58 } 59 60 /* Out of bound primitive index */ 61 if(prim->prim_id >= mesh_get_ntris(geom->data.mesh)) { 62 res = RES_BAD_ARG; 63 goto error; 64 } 65 ids = mesh_get_ids(geom->data.mesh) + prim->prim_id * 3/*#triangle ids*/; 66 attrib->usage = usage; 67 68 if(usage == S3D_POSITION || usage == S3D_GEOMETRY_NORMAL) { 69 const float* v0, *v1, *v2; 70 const float* pos; 71 attrib->type = S3D_FLOAT3; 72 /* Fetch data */ 73 pos = mesh_get_pos(geom->data.mesh); 74 v0 = pos + ids[0] * 3; 75 v1 = pos + ids[1] * 3; 76 v2 = pos + ids[2] * 3; 77 if(usage == S3D_GEOMETRY_NORMAL) { /* Compute the geometry normal */ 78 float e0[3], e1[3]; 79 /* Build the geometric normal with respect to surface orientation. 80 * Default is Clock Wise */ 81 f3_sub(e0, v2, v0); 82 f3_sub(e1, v1, v0); 83 if(flip_surface) { 84 f3_cross(attrib->value, e1, e0); 85 } else { 86 f3_cross(attrib->value, e0, e1); 87 } 88 if(transform) { /* Transform the normal from local to world space */ 89 float transform_invtrans[9]; 90 f33_invtrans(transform_invtrans, transform); 91 f33_mulf3(attrib->value, transform_invtrans, attrib->value); 92 } 93 } else { /* Interpolate the vertex position */ 94 float tmp[3]; 95 f3_mulf(attrib->value, v0, uv[0]); 96 f3_add(attrib->value, attrib->value, f3_mulf(tmp, v1, uv[1])); 97 f3_add(attrib->value, attrib->value, f3_mulf(tmp, v2, w)); 98 if(transform) { /* Transform the position from local to world space */ 99 f33_mulf3(attrib->value, transform, attrib->value); /* Rotation */ 100 f3_add(attrib->value, attrib->value, transform + 9); /* Translation */ 101 } 102 } 103 } else { 104 const float* attr; 105 const float* v0, *v1, *v2; 106 unsigned i, dim; 107 attrib->type = geom->data.mesh->attribs_type[usage]; 108 /* Fetch attrib data */ 109 dim = s3d_type_get_dimension(attrib->type); 110 attr = mesh_get_attr(geom->data.mesh, usage); 111 v0 = attr + ids[0] * dim; 112 v1 = attr + ids[1] * dim; 113 v2 = attr + ids[2] * dim; 114 /* Interpolate the vertex attribs */ 115 ASSERT(dim <= 4); 116 FOR_EACH(i, 0, dim) { 117 attrib->value[i] = v0[i]*uv[0] + v1[i]*uv[1] + v2[i]*w; 118 } 119 } 120 exit: 121 return res; 122 error: 123 goto exit; 124 } 125 126 static res_T 127 sphere_get_attrib 128 (const struct geometry* geom, 129 const float* transform, /* Can be NULL => no transform */ 130 const char flip_surface, 131 const enum s3d_attrib_usage usage, 132 const float uv[2], 133 struct s3d_attrib* attrib) 134 { 135 res_T res = RES_OK; 136 double phi, cos_theta, sin_theta; 137 float P[3]; 138 float N[3]; 139 ASSERT(geom && geom->type == GEOM_SPHERE); 140 ASSERT(uv && attrib); 141 142 /* Only position and geometry normal are valid sphere attribs */ 143 if(usage != S3D_GEOMETRY_NORMAL && usage != S3D_POSITION) { 144 res = RES_BAD_ARG; 145 goto error; 146 } 147 148 /* Compute the sampled position on the unit sphere that is actually equal to 149 * the normal at this position. */ 150 phi = uv[0] * 2*PI; 151 cos_theta = 1 - 2 * uv[1]; 152 sin_theta = 2 * sqrtf(uv[1] * (1 - uv[1])); 153 N[0] = (float)(cos(phi) * sin_theta); 154 N[1] = (float)(sin(phi) * sin_theta); 155 N[2] = (float)cos_theta; 156 157 if(usage == S3D_GEOMETRY_NORMAL) { 158 if(flip_surface) f3_minus(N, N); 159 if(transform) { /* Transform the normal from local to world space */ 160 float invtrans[9]; 161 f33_invtrans(invtrans, transform); 162 f33_mulf3(attrib->value, invtrans, N); 163 } 164 f3_set(attrib->value, N); 165 } else { 166 ASSERT(usage == S3D_POSITION); 167 /* Compute the sampled position in local space */ 168 f3_mulf(P, N, geom->data.sphere->radius); 169 f3_add(P, P, geom->data.sphere->pos); 170 if(transform) { /* Transform the position from local to world space */ 171 f33_mulf3(P, transform, P); /* Affine */ 172 f3_add(P, P, transform + 9); /* Linear */ 173 } 174 f3_set(attrib->value, P); 175 } 176 177 exit: 178 return res; 179 error: 180 goto exit; 181 } 182 183 static int 184 check_primitive(const struct s3d_primitive* prim) 185 { 186 return prim 187 && prim->geom_id != S3D_INVALID_ID 188 && prim->prim_id != S3D_INVALID_ID 189 && prim->shape__ != NULL 190 && (prim->inst_id != S3D_INVALID_ID || prim->inst__ == NULL); 191 } 192 193 /******************************************************************************* 194 * Exported functions 195 ******************************************************************************/ 196 res_T 197 s3d_primitive_get_attrib 198 (const struct s3d_primitive* prim, 199 const enum s3d_attrib_usage usage, 200 const float uv[2], 201 struct s3d_attrib* attrib) 202 { 203 struct geometry* geom_shape = NULL; 204 const float* transform = NULL; 205 char flip_surface = 0; 206 res_T res = RES_OK; 207 208 if(!check_primitive(prim) || usage == S3D_ATTRIBS_COUNT__ || !uv || !attrib) { 209 res = RES_BAD_ARG; 210 goto error; 211 } 212 213 if(prim->inst__ == NULL) { 214 geom_shape = (struct geometry*)prim->shape__; 215 flip_surface = geom_shape->flip_surface; 216 } else { 217 const struct geometry* geom_inst = (const struct geometry*)prim->inst__; 218 ASSERT(geom_inst->type == GEOM_INSTANCE); 219 ASSERT(prim->inst_id == geom_inst->name); 220 geom_shape = (struct geometry*)prim->shape__; 221 transform = geom_inst->data.instance->transform; 222 ASSERT(geom_shape); 223 flip_surface = geom_inst->flip_surface ^ geom_shape->flip_surface; 224 } 225 ASSERT(prim->geom_id == geom_shape->name); 226 227 if(geom_shape->type == GEOM_SPHERE) { 228 res = sphere_get_attrib 229 (geom_shape, transform, flip_surface, usage, uv, attrib); 230 } else { 231 ASSERT(geom_shape->type == GEOM_MESH); 232 res = mesh_get_primitive_attrib 233 (geom_shape, transform, flip_surface, prim, usage, uv, attrib); 234 } 235 if(res != RES_OK) goto error; 236 237 exit: 238 return res; 239 error: 240 goto exit; 241 } 242 243 res_T 244 s3d_primitive_has_attrib 245 (const struct s3d_primitive* prim, 246 const enum s3d_attrib_usage attr, 247 char* has_attrib) 248 { 249 if(!check_primitive(prim) || !has_attrib 250 || (attr != S3D_GEOMETRY_NORMAL && (unsigned)attr >= S3D_ATTRIBS_COUNT__)) 251 return RES_BAD_ARG; 252 253 if(attr == S3D_GEOMETRY_NORMAL) { 254 *has_attrib = 1; 255 } else { 256 struct geometry* geom_shape = (struct geometry*)prim->shape__; 257 if(geom_shape->type == GEOM_MESH) { 258 *has_attrib = geom_shape->data.mesh->attribs[attr] != NULL; 259 } else { 260 *has_attrib = 0; 261 } 262 } 263 return RES_OK; 264 } 265 266 res_T 267 s3d_primitive_sample 268 (const struct s3d_primitive *prim, 269 const float u, 270 const float v, 271 float st[2]) 272 { 273 struct geometry* geom_shape; 274 double sqrt_u; 275 276 if(!check_primitive(prim) || !st) 277 return RES_BAD_ARG; 278 279 /* Expecting canonic numbers */ 280 if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f) 281 return RES_BAD_ARG; 282 283 geom_shape = (struct geometry*)prim->shape__; 284 switch(geom_shape->type) { 285 case GEOM_MESH: 286 /* Triangular primitive */ 287 sqrt_u = sqrt(u); 288 st[0] = (float)(1.0 - sqrt_u); 289 st[1] = (float)(v * sqrt_u); 290 break; 291 case GEOM_SPHERE: 292 st[0] = u; 293 st[1] = v; 294 break; 295 default: FATAL("Unreachable code\n"); break; 296 } 297 return RES_OK; 298 } 299 300 res_T 301 s3d_primitive_compute_area(const struct s3d_primitive* prim, float* area) 302 { 303 struct geometry* geom; 304 305 if(!check_primitive(prim) || !area) 306 return RES_BAD_ARG; 307 308 geom = (struct geometry*)prim->shape__; 309 if(geom->type == GEOM_SPHERE) { 310 *area = sphere_compute_area(geom->data.sphere); 311 } else if(geom->type == GEOM_MESH) { 312 const uint32_t* ids; 313 const float* pos; 314 const float* v0, *v1, *v2; 315 float E0[3], E1[3], N[3]; 316 317 pos = mesh_get_pos(geom->data.mesh); 318 ids = mesh_get_ids(geom->data.mesh) + prim->prim_id * 3/* #triangle ids */; 319 v0 = pos + ids[0] * 3/* #coords */; 320 v1 = pos + ids[1] * 3/* #coords */; 321 v2 = pos + ids[2] * 3/* #coords */; 322 f3_sub(E0, v1, v0); 323 f3_sub(E1, v2, v0); 324 *area = f3_len(f3_cross(N, E0, E1)) * 0.5f; 325 } else { 326 FATAL("Unreachable code\n"); 327 } 328 return RES_OK; 329 } 330 331 res_T 332 s3d_primitive_get_transform 333 (const struct s3d_primitive* prim, float transform[12]) 334 { 335 if(!check_primitive(prim) || !transform) 336 return RES_BAD_ARG; 337 338 if(!prim->inst__) { 339 f3(transform + 0, 1.f, 0.f, 0.f); 340 f3(transform + 3, 0.f, 1.f, 0.f); 341 f3(transform + 6, 0.f, 0.f, 1.f); 342 f3(transform + 9, 0.f, 0.f, 0.f); 343 } else { 344 struct geometry* geom_inst = (struct geometry*)prim->inst__; 345 ASSERT(geom_inst->type == GEOM_INSTANCE); 346 f3_set(transform + 0, geom_inst->data.instance->transform + 0); 347 f3_set(transform + 3, geom_inst->data.instance->transform + 3); 348 f3_set(transform + 6, geom_inst->data.instance->transform + 6); 349 f3_set(transform + 9, geom_inst->data.instance->transform + 9); 350 } 351 return RES_OK; 352 } 353 354 res_T 355 s3d_triangle_get_vertex_attrib 356 (const struct s3d_primitive* prim, 357 const size_t ivertex, 358 const enum s3d_attrib_usage usage, 359 struct s3d_attrib* attrib) 360 { 361 struct geometry* geom_shape = NULL; 362 const float* transform = NULL; 363 const uint32_t* ids; 364 365 if(!check_primitive(prim) || ivertex > 2 366 || (unsigned)usage >= S3D_ATTRIBS_COUNT__ 367 || !attrib) { 368 return RES_BAD_ARG; 369 } 370 371 geom_shape = (struct geometry*)prim->shape__; 372 ASSERT(prim->geom_id == geom_shape->name); 373 374 if(geom_shape->type != GEOM_MESH) 375 return RES_BAD_ARG; 376 377 if(prim->inst__ != NULL) { 378 const struct geometry* geom_inst = (const struct geometry*)prim->inst__; 379 ASSERT(geom_inst->type == GEOM_INSTANCE); 380 ASSERT(prim->inst_id == geom_inst->name); 381 transform = geom_inst->data.instance->transform; 382 } 383 384 /* The mesh haven't the required mesh attrib */ 385 if(!geom_shape->data.mesh->attribs[usage]) { 386 return RES_BAD_ARG; 387 } 388 389 /* Out of bound primitive index */ 390 if(prim->prim_id >= mesh_get_ntris(geom_shape->data.mesh)) { 391 return RES_BAD_ARG; 392 } 393 ids = mesh_get_ids(geom_shape->data.mesh) + prim->prim_id * 3/*#triangle ids*/; 394 attrib->usage = usage; 395 396 if(usage != S3D_POSITION) { 397 const float* attr; 398 unsigned i, dim; 399 attrib->type = geom_shape->data.mesh->attribs_type[usage]; 400 /* Fetch attrib data */ 401 dim = s3d_type_get_dimension(attrib->type); 402 attr = mesh_get_attr(geom_shape->data.mesh, usage) + ids[ivertex] * dim; 403 FOR_EACH(i, 0, dim) attrib->value[i] = attr[i]; 404 } else { 405 const float* pos; 406 attrib->type = S3D_FLOAT3; 407 /* Fetch data */ 408 pos = mesh_get_pos(geom_shape->data.mesh) + ids[ivertex] * 3; 409 f3_set(attrib->value, pos); 410 if(transform) { /* Transform the position from local to world space */ 411 f33_mulf3(attrib->value, transform, attrib->value); /* Rotation */ 412 f3_add(attrib->value, attrib->value, transform + 9); /* Translation */ 413 } 414 } 415 return RES_OK; 416 } 417