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