star-3d

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

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