star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.com/star-3d.git
Log | Files | Refs | README | LICENSE

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