solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

solstice_object.c (22059B)


      1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2016-2018 CNRS
      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 "solstice_c.h"
     18 
     19 #include <solstice/ssol.h>
     20 
     21 #include <rsys/double33.h>
     22 #include <rsys/float3.h>
     23 
     24 #include <star/s3dut.h>
     25 #include <star/sstl.h>
     26 
     27 #include <limits.h>
     28 
     29 #define MAX_POLYCLIPS 16
     30 
     31 struct mesh_ctx_s3dut {
     32   struct s3dut_mesh_data data;
     33   double transform[12];
     34 };
     35 
     36 struct mesh_ctx_sstl {
     37   struct sstl_desc desc;
     38   const double *transform;
     39 };
     40 
     41 /*******************************************************************************
     42  * Helper functions
     43  ******************************************************************************/
     44 static void
     45 mesh_ctx_s3dut_get_ids(const unsigned itri, unsigned ids[3], void* ctx)
     46 {
     47   const struct mesh_ctx_s3dut* mesh = ctx;
     48   ASSERT(ids && ctx && itri < mesh->data.nprimitives);
     49   ASSERT(mesh->data.indices[itri*3+0] <= UINT_MAX);
     50   ASSERT(mesh->data.indices[itri*3+1] <= UINT_MAX);
     51   ASSERT(mesh->data.indices[itri*3+2] <= UINT_MAX);
     52   ids[0] = (unsigned)mesh->data.indices[itri*3+0];
     53   ids[1] = (unsigned)mesh->data.indices[itri*3+1];
     54   ids[2] = (unsigned)mesh->data.indices[itri*3+2];
     55 }
     56 
     57 static void
     58 mesh_ctx_s3dut_get_pos(const unsigned ivert, float pos[3], void* ctx)
     59 {
     60   const struct mesh_ctx_s3dut* mesh = ctx;
     61   double tmp[3];
     62   ASSERT(pos && ctx && ivert < mesh->data.nvertices);
     63   d3_set(tmp, mesh->data.positions + ivert*3);
     64   d33_muld3(tmp, mesh->transform, tmp);
     65   d3_add(tmp, mesh->transform+9, tmp);
     66   f3_set_d3(pos, tmp);
     67 }
     68 
     69 static void
     70 mesh_ctx_sstl_get_ids(const unsigned itri, unsigned ids[3], void* ctx)
     71 {
     72   const struct mesh_ctx_sstl* mesh = ctx;
     73   ASSERT(ids && ctx && itri < mesh->desc.triangles_count);
     74   ASSERT(mesh->desc.indices[itri*3+0] <= UINT_MAX);
     75   ASSERT(mesh->desc.indices[itri*3+1] <= UINT_MAX);
     76   ASSERT(mesh->desc.indices[itri*3+2] <= UINT_MAX);
     77   ids[0] = mesh->desc.indices[itri*3+0];
     78   ids[1] = mesh->desc.indices[itri*3+1];
     79   ids[2] = mesh->desc.indices[itri*3+2];
     80 }
     81 
     82 static void
     83 mesh_ctx_sstl_get_pos(const unsigned ivert, float pos[3], void* ctx)
     84 {
     85   const struct mesh_ctx_sstl* mesh = ctx;
     86   double tmp[3];
     87   ASSERT(pos && ctx && ivert < mesh->desc.vertices_count);
     88   d3_set_f3(tmp, mesh->desc.vertices + ivert * 3);
     89   d33_muld3(tmp, mesh->transform, tmp);
     90   d3_add(tmp, mesh->transform + 9, tmp);
     91   f3_set_d3(pos, tmp);
     92 }
     93 
     94 static void
     95 get_carving_pos(const size_t ivert, double pos[2], void* ctx)
     96 {
     97   const struct solparser_polyclip* polyclip = ctx;
     98   ASSERT(pos && ctx);
     99   solparser_polyclip_get_vertex(polyclip, ivert, pos);
    100 }
    101 
    102 static INLINE enum ssol_clipping_op
    103 solparser_to_ssol_clip_op(const enum solparser_clip_op op)
    104 {
    105   switch(op) {
    106     case SOLPARSER_CLIP_OP_SUB: return SSOL_SUB;
    107     case SOLPARSER_CLIP_OP_AND: return SSOL_AND;
    108     default: FATAL("Unreachable code.\n");
    109   }
    110 }
    111 
    112 static void
    113 get_circular(const size_t ivert, double position[2], void* ctx)
    114 {
    115   struct solparser_circleclip* data = (struct solparser_circleclip*)ctx;
    116   const double a = (double)ivert * 2 * PI / (double)data->segments;
    117   ASSERT(ivert < (size_t)data->segments);
    118   position[0] = data->center[0] + data->radius * cos(a);
    119   position[1] = data->center[1] + data->radius * sin(a);
    120 }
    121 
    122 static res_T
    123 create_ssol_shape_mesh
    124   (struct solstice* solstice,
    125    const double transform[12],
    126    const struct s3dut_mesh* mesh,
    127    struct ssol_shape** out_ssol_shape)
    128 {
    129   struct mesh_ctx_s3dut mesh_ctx;
    130   struct ssol_vertex_data vertex_data = SSOL_VERTEX_DATA_NULL;
    131   struct ssol_shape* ssol_shape = NULL;
    132   res_T res = RES_OK;
    133   ASSERT(solstice && mesh && out_ssol_shape);
    134 
    135   S3DUT(mesh_get_data(mesh, &mesh_ctx.data));
    136   ASSERT(mesh_ctx.data.nprimitives <= UINT_MAX);
    137   ASSERT(mesh_ctx.data.nvertices <= UINT_MAX);
    138   d33_set(mesh_ctx.transform, transform);
    139   d3_set(mesh_ctx.transform+9, transform+9);
    140 
    141   res = ssol_shape_create_mesh(solstice->ssol, &ssol_shape);
    142   if(res != RES_OK) {
    143     fprintf(stderr, "Could not create a Solstice Solver mesh shape.\n");
    144     goto error;
    145   }
    146 
    147   vertex_data.usage = SSOL_POSITION;
    148   vertex_data.get = mesh_ctx_s3dut_get_pos;
    149   res = ssol_mesh_setup(ssol_shape, (unsigned)mesh_ctx.data.nprimitives,
    150     mesh_ctx_s3dut_get_ids, (unsigned)mesh_ctx.data.nvertices, &vertex_data, 1,
    151     &mesh_ctx);
    152   if(res != RES_OK) {
    153     fprintf(stderr, "Could not setup a Solstice Solver mesh shape.\n");
    154     goto error;
    155   }
    156 
    157 exit:
    158   *out_ssol_shape = ssol_shape;
    159   return res;
    160 error:
    161   if(ssol_shape) {
    162     SSOL(shape_ref_put(ssol_shape));
    163     ssol_shape = NULL;
    164   }
    165   goto exit;
    166 }
    167 
    168 static res_T
    169 create_cuboid
    170   (struct solstice* solstice,
    171    const double transform[12],
    172    const struct solparser_shape_cuboid_id cuboid_id,
    173    struct ssol_shape** out_ssol_shape)
    174 {
    175   const struct solparser_shape_cuboid* cuboid;
    176   struct s3dut_mesh* mesh = NULL;
    177   struct ssol_shape* ssol_shape = NULL;
    178   res_T res = RES_OK;
    179   ASSERT(solstice && out_ssol_shape);
    180 
    181   cuboid = solparser_get_shape_cuboid(solstice->parser, cuboid_id);
    182   res = s3dut_create_cuboid(solstice->allocator, cuboid->size[0],
    183     cuboid->size[1], cuboid->size[2], &mesh);
    184   if(res != RES_OK) {
    185     fprintf(stderr, "Could not create the cuboid 3D data.\n");
    186     goto error;
    187   }
    188 
    189   res = create_ssol_shape_mesh(solstice, transform, mesh, &ssol_shape);
    190   if(res != RES_OK) goto error;
    191 
    192 exit:
    193   if(mesh) S3DUT(mesh_ref_put(mesh));
    194   *out_ssol_shape = ssol_shape;
    195   return res;
    196 error:
    197   if(ssol_shape) {
    198     SSOL(shape_ref_put(ssol_shape));
    199     ssol_shape = NULL;
    200   }
    201   goto exit;
    202 }
    203 
    204 
    205 static res_T
    206 create_cylinder
    207   (struct solstice* solstice,
    208    const double transform[12],
    209    const struct solparser_shape_cylinder_id cylinder_id,
    210    struct ssol_shape** out_ssol_shape)
    211 {
    212   const struct solparser_shape_cylinder* cylinder;
    213   struct s3dut_mesh* mesh = NULL;
    214   struct ssol_shape* ssol_shape = NULL;
    215   res_T res = RES_OK;
    216   ASSERT(solstice && out_ssol_shape);
    217 
    218   cylinder = solparser_get_shape_cylinder(solstice->parser, cylinder_id);
    219   ASSERT(cylinder->nslices > 0 && cylinder->nslices < UINT_MAX);
    220   res = s3dut_create_cylinder(solstice->allocator, cylinder->radius,
    221     cylinder->height, (unsigned)cylinder->nslices, (unsigned)cylinder->nstacks, 
    222     &mesh);
    223   if(res != RES_OK) {
    224     fprintf(stderr, "Could not create the cylinder 3D data.\n");
    225     goto error;
    226   }
    227 
    228   res = create_ssol_shape_mesh(solstice, transform, mesh, &ssol_shape);
    229   if(res != RES_OK) goto error;
    230 
    231 exit:
    232   if(mesh) S3DUT(mesh_ref_put(mesh));
    233   *out_ssol_shape = ssol_shape;
    234   return res;
    235 error:
    236   if(ssol_shape) {
    237     SSOL(shape_ref_put(ssol_shape));
    238     ssol_shape = NULL;
    239   }
    240   goto exit;
    241 }
    242 
    243 static res_T
    244 create_sphere
    245   (struct solstice* solstice,
    246    const double transform[12],
    247    const struct solparser_shape_sphere_id sphere_id,
    248    struct ssol_shape** out_ssol_shape)
    249 {
    250   const struct solparser_shape_sphere* sphere;
    251   struct s3dut_mesh* mesh = NULL;
    252   struct ssol_shape* ssol_shape = NULL;
    253   res_T res = RES_OK;
    254   ASSERT(solstice && out_ssol_shape);
    255 
    256   sphere = solparser_get_shape_sphere(solstice->parser, sphere_id);
    257   ASSERT(sphere->nslices > 0 && sphere->nslices < UINT_MAX);
    258   res = s3dut_create_sphere(solstice->allocator, sphere->radius,
    259     (unsigned)sphere->nslices, (unsigned)(sphere->nslices / 2), &mesh);
    260   if(res != RES_OK) {
    261     fprintf(stderr, "Could not create the sphere 3D data.\n");
    262     goto error;
    263   }
    264 
    265   res = create_ssol_shape_mesh(solstice, transform, mesh, &ssol_shape);
    266   if(res != RES_OK) goto error;
    267 
    268 exit:
    269   if(mesh) S3DUT(mesh_ref_put(mesh));
    270   *out_ssol_shape = ssol_shape;
    271   return res;
    272 error:
    273   if(ssol_shape) {
    274     SSOL(shape_ref_put(ssol_shape));
    275     ssol_shape = NULL;
    276   }
    277   goto exit;
    278 }
    279 
    280 static res_T
    281 create_stl
    282   (struct solstice* solstice,
    283    const double transform[12],
    284    const struct solparser_shape_imported_geometry_id stl_id,
    285    struct ssol_shape** out_stl)
    286 {
    287   const struct solparser_shape_imported_geometry* stl;
    288   struct ssol_shape* ssol_shape = NULL;
    289   struct sstl* tmp_stl;
    290   struct sstl_desc tmp_desc;
    291   struct mesh_ctx_sstl mesh_ctx;
    292   struct ssol_vertex_data vertex_data = SSOL_VERTEX_DATA_NULL;
    293   res_T res = RES_OK;
    294   ASSERT(solstice && out_stl);
    295 
    296   stl = solparser_get_shape_stl(solstice->parser, stl_id);
    297   ASSERT(str_cget(&stl->filename));
    298 
    299   res = sstl_create(NULL, solstice->allocator, 1, &tmp_stl);
    300   if(res != RES_OK) {
    301     fprintf(stderr, "Could not create a Solstice Solver STL shape.\n");
    302     goto error;
    303   }
    304   res = sstl_load(tmp_stl, str_cget(&stl->filename));
    305   if(res != RES_OK) goto error;
    306   res = sstl_get_desc(tmp_stl, &tmp_desc);
    307   if(res != RES_OK) goto error;
    308   ASSERT(tmp_desc.triangles_count <= UINT_MAX);
    309   ASSERT(tmp_desc.vertices_count <= UINT_MAX);
    310   mesh_ctx.transform = transform;
    311   mesh_ctx.desc = tmp_desc;
    312 
    313   res = ssol_shape_create_mesh(solstice->ssol, &ssol_shape);
    314   if(res != RES_OK) {
    315     fprintf(stderr, "Could not create the STL mesh shape.\n");
    316     goto error;
    317   }
    318 
    319   vertex_data.usage = SSOL_POSITION;
    320   vertex_data.get = mesh_ctx_sstl_get_pos;
    321   res = ssol_mesh_setup(ssol_shape, (unsigned)tmp_desc.triangles_count,
    322     mesh_ctx_sstl_get_ids, (unsigned)tmp_desc.vertices_count, &vertex_data, 1,
    323     &mesh_ctx);
    324   if(res != RES_OK) {
    325     fprintf(stderr, "Could not setup STL mesh.\n");
    326     goto error;
    327   }
    328 
    329 exit:
    330   if(tmp_stl) SSTL(ref_put(tmp_stl));
    331   *out_stl = ssol_shape;
    332   return res;
    333 error:
    334   if(ssol_shape) {
    335     SSOL(shape_ref_put(ssol_shape));
    336     ssol_shape = NULL;
    337   }
    338   goto exit;
    339 }
    340 
    341 static res_T
    342 create_ssol_shape_punched_surface
    343   (struct solstice* solstice,
    344    const struct darray_polyclip* clips,
    345    struct ssol_quadric* quadric,
    346    struct ssol_shape** out_ssol_shape)
    347 {
    348   struct ssol_shape* ssol_shape = NULL;
    349   struct ssol_carving carvings[MAX_POLYCLIPS];
    350   struct ssol_punched_surface punched_surf = SSOL_PUNCHED_SURFACE_NULL;
    351   size_t iclip, nclips;
    352   res_T res = RES_OK;
    353   ASSERT(solstice && quadric && out_ssol_shape);
    354 
    355   nclips = darray_polyclip_size_get(clips);
    356   if(nclips > MAX_POLYCLIPS) {
    357     fprintf(stderr, "Too many clipping polygons. "
    358 "%lu are submitted while at most %lu can be defined.\n",
    359       (unsigned long)nclips, (unsigned long)MAX_POLYCLIPS);
    360     res = RES_BAD_ARG;
    361     goto error;
    362   }
    363 
    364   FOR_EACH(iclip, 0, nclips) {
    365     const struct solparser_polyclip* clip;
    366     clip = darray_polyclip_cdata_get(clips) + iclip;
    367 
    368     switch(clip->contour_type) {
    369       case SOLPARSER_CLIP_CONTOUR_POLY:
    370         carvings[iclip].get = get_carving_pos;
    371         carvings[iclip].nb_vertices = solparser_polyclip_get_vertices_count(clip);
    372         carvings[iclip].operation = solparser_to_ssol_clip_op(clip->op);
    373         carvings[iclip].context = (void*)clip;
    374         break;
    375       case SOLPARSER_CLIP_CONTOUR_CIRCLE:
    376         carvings[iclip].get = get_circular;
    377         carvings[iclip].nb_vertices = (size_t)clip->circle.segments;
    378         carvings[iclip].operation = solparser_to_ssol_clip_op(clip->op);
    379         carvings[iclip].context = (void*)&clip->circle;
    380         break;
    381       default: FATAL("Unreachable code.\n");
    382     }
    383   }
    384 
    385   punched_surf.quadric = quadric;
    386   punched_surf.carvings = carvings;
    387   punched_surf.nb_carvings = nclips;
    388 
    389   res = ssol_shape_create_punched_surface(solstice->ssol, &ssol_shape);
    390   if(res != RES_OK) {
    391     fprintf(stderr, "Could not create a Solstice Solver punched surface.\n");
    392     goto error;
    393   }
    394 
    395   res = ssol_punched_surface_setup(ssol_shape, &punched_surf);
    396   if(res != RES_OK) {
    397     fprintf(stderr, "Could not setup the Solstice Solver punched surface.\n");
    398     goto error;
    399   }
    400 exit:
    401   *out_ssol_shape = ssol_shape;
    402   return res;
    403 error:
    404   if(ssol_shape) {
    405     SSOL(shape_ref_put(ssol_shape));
    406     ssol_shape = NULL;
    407   }
    408   goto exit;
    409 
    410 }
    411 
    412 static res_T
    413 create_parabol
    414   (struct solstice* solstice,
    415    const double transform[12],
    416    const struct solparser_shape_paraboloid_id id,
    417    struct ssol_shape** out_ssol_shape)
    418 {
    419   const struct solparser_shape_paraboloid* paraboloid;
    420   struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT;
    421   ASSERT(solstice);
    422 
    423   paraboloid = solparser_get_shape_parabol(solstice->parser, id);
    424 
    425   quadric.type = SSOL_QUADRIC_PARABOL;
    426   quadric.data.parabol.focal = paraboloid->focal;
    427   d33_set(quadric.transform, transform);
    428   d3_set(quadric.transform+9, transform+9);
    429   if(paraboloid->nslices > 0) { /* nslices is set */
    430     quadric.slices_count_hint = (size_t)paraboloid->nslices;
    431   }
    432   return create_ssol_shape_punched_surface
    433     (solstice, &paraboloid->polyclips, &quadric, out_ssol_shape);
    434 }
    435 
    436 static res_T
    437 create_parabolic_cylinder
    438   (struct solstice* solstice,
    439    const double transform[12],
    440    const struct solparser_shape_paraboloid_id id,
    441    struct ssol_shape** out_ssol_shape)
    442 {
    443   const struct solparser_shape_paraboloid* paraboloid;
    444   struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT;
    445   ASSERT(solstice);
    446 
    447   paraboloid = solparser_get_shape_parabolic_cylinder(solstice->parser, id);
    448 
    449   quadric.type = SSOL_QUADRIC_PARABOLIC_CYLINDER;
    450   quadric.data.parabolic_cylinder.focal = paraboloid->focal;
    451   d33_set(quadric.transform, transform);
    452   d3_set(quadric.transform+9, transform+9);
    453   if(paraboloid->nslices > 0) { /* nslices is set */
    454     quadric.slices_count_hint = (size_t)paraboloid->nslices;
    455   }
    456 
    457   return create_ssol_shape_punched_surface
    458     (solstice, &paraboloid->polyclips, &quadric, out_ssol_shape);
    459 }
    460 
    461 static res_T
    462 create_hyperbol
    463   (struct solstice* solstice,
    464    const double transform[12],
    465    const struct solparser_shape_hyperboloid_id id,
    466    struct ssol_shape** out_ssol_shape)
    467 {
    468   const struct solparser_shape_hyperboloid* hyperboloid;
    469   struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT;
    470   ASSERT(solstice);
    471 
    472   hyperboloid = solparser_get_shape_hyperbol(solstice->parser, id);
    473 
    474   quadric.type = SSOL_QUADRIC_HYPERBOL;
    475   quadric.data.hyperbol.real_focal = hyperboloid->focals.real;
    476   quadric.data.hyperbol.img_focal = hyperboloid->focals.image;
    477   d33_set(quadric.transform, transform);
    478   d3_set(quadric.transform + 9, transform + 9);
    479   if(hyperboloid->nslices > 0) { /* nslices is set */
    480     quadric.slices_count_hint = (size_t)hyperboloid->nslices;
    481   }
    482 
    483   return create_ssol_shape_punched_surface
    484     (solstice, &hyperboloid->polyclips, &quadric, out_ssol_shape);
    485 }
    486 
    487 static res_T
    488 create_hemisphere
    489   (struct solstice* solstice,
    490    const double transform[12],
    491    const struct solparser_shape_hemisphere_id id,
    492    struct ssol_shape** out_ssol_shape)
    493 {
    494   const struct solparser_shape_hemisphere* hemisphere;
    495   struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT;
    496   ASSERT(solstice);
    497 
    498   hemisphere = solparser_get_shape_hemisphere(solstice->parser, id);
    499 
    500   quadric.type = SSOL_QUADRIC_HEMISPHERE;
    501   quadric.data.hemisphere.radius = hemisphere->radius;
    502   d33_set(quadric.transform, transform);
    503   d3_set(quadric.transform + 9, transform + 9);
    504   if(hemisphere->nslices > 0) { /* nslices is set */
    505     quadric.slices_count_hint = (size_t)hemisphere->nslices;
    506   }
    507 
    508   return create_ssol_shape_punched_surface
    509     (solstice, &hemisphere->polyclips, &quadric, out_ssol_shape);
    510 }
    511 
    512 static res_T
    513 create_plane
    514   (struct solstice* solstice,
    515    const double transform[12],
    516    const struct solparser_shape_plane_id id,
    517    struct ssol_shape** out_ssol_shape)
    518 {
    519   const struct solparser_shape_plane* plane;
    520   struct ssol_quadric quadric = SSOL_QUADRIC_DEFAULT;
    521   ASSERT(solstice);
    522 
    523   plane = solparser_get_shape_plane(solstice->parser, id);
    524   ASSERT(plane->nslices > 0);
    525 
    526   quadric.type = SSOL_QUADRIC_PLANE;
    527   quadric.slices_count_hint = (size_t)plane->nslices;
    528   d33_set(quadric.transform, transform);
    529   d3_set(quadric.transform+9, transform+9);
    530 
    531   return create_ssol_shape_punched_surface
    532     (solstice, &plane->polyclips, &quadric, out_ssol_shape);
    533 }
    534 
    535 static res_T
    536 create_shaded_shape
    537   (struct solstice* solstice,
    538    const struct solparser_object_id obj_id,
    539    struct ssol_material** ssol_front,
    540    struct ssol_material** ssol_back,
    541    struct ssol_shape** ssol_shape)
    542 {
    543   const struct solparser_material_double_sided* mtl2;
    544   const struct solparser_object* obj;
    545   const struct solparser_shape* shape;
    546   double transform[12];
    547   double rotation[3];
    548   res_T res = RES_OK;
    549   ASSERT(solstice && ssol_front && ssol_back && ssol_shape);
    550 
    551   *ssol_front = NULL;
    552   *ssol_back = NULL;
    553   *ssol_shape = NULL;
    554 
    555   obj = solparser_get_object(solstice->parser, obj_id);
    556 
    557   mtl2 = solparser_get_material_double_sided(solstice->parser, obj->mtl2);
    558   res = solstice_create_ssol_material(solstice, mtl2->front, ssol_front);
    559   if(res != RES_OK) goto error;
    560   res = solstice_create_ssol_material(solstice, mtl2->back, ssol_back);
    561   if(res != RES_OK) goto error;
    562 
    563   /* Define the shape transformation */
    564   rotation[0] = MDEG2RAD(obj->rotation[0]);
    565   rotation[1] = MDEG2RAD(obj->rotation[1]);
    566   rotation[2] = MDEG2RAD(obj->rotation[2]);
    567   d33_rotation(transform, rotation[0], rotation[1], rotation[2]);
    568   d3_set(transform+9, obj->translation);
    569 
    570   shape = solparser_get_shape(solstice->parser, obj->shape);
    571   switch(shape->type) {
    572     case SOLPARSER_SHAPE_CUBOID:
    573       res = create_cuboid(solstice, transform, shape->data.cuboid, ssol_shape);
    574       break;
    575     case SOLPARSER_SHAPE_CYLINDER:
    576       res = create_cylinder
    577         (solstice, transform, shape->data.cylinder, ssol_shape);
    578       break;
    579     case SOLPARSER_SHAPE_OBJ:
    580       fprintf(stderr, "`obj' shapes are not supported yet.\n");
    581       res = RES_BAD_ARG;
    582       break;
    583     case SOLPARSER_SHAPE_PARABOL:
    584       res = create_parabol(solstice, transform, shape->data.parabol, ssol_shape);
    585       break;
    586     case SOLPARSER_SHAPE_PARABOLIC_CYLINDER:
    587       res = create_parabolic_cylinder
    588         (solstice, transform, shape->data.parabol, ssol_shape);
    589       break;
    590     case SOLPARSER_SHAPE_HYPERBOL:
    591       res = create_hyperbol(solstice, transform, shape->data.hyperbol, ssol_shape);
    592       break;
    593     case SOLPARSER_SHAPE_HEMISPHERE:
    594       res = create_hemisphere
    595         (solstice, transform, shape->data.hemisphere, ssol_shape);
    596       break;
    597     case SOLPARSER_SHAPE_PLANE:
    598       res = create_plane(solstice, transform, shape->data.plane, ssol_shape);
    599       break;
    600     case SOLPARSER_SHAPE_SPHERE:
    601       res = create_sphere(solstice, transform, shape->data.sphere, ssol_shape);
    602       break;
    603     case SOLPARSER_SHAPE_STL:
    604       res = create_stl(solstice, transform, shape->data.stl, ssol_shape);
    605       break;
    606     default: FATAL("Unreachable code.\n"); break;
    607   }
    608   if(res != RES_OK) goto error;
    609 
    610 exit:
    611   return res;
    612 error:
    613   if(*ssol_front) SSOL(material_ref_put(*ssol_front)), *ssol_front = NULL;
    614   if(*ssol_back) SSOL(material_ref_put(*ssol_back)), *ssol_back = NULL;
    615   if(*ssol_shape) SSOL(shape_ref_put(*ssol_shape)), *ssol_shape = NULL;
    616   goto exit;
    617 }
    618 
    619 /*******************************************************************************
    620  * Local functions
    621  ******************************************************************************/
    622 res_T
    623 solstice_instantiate_geometry
    624   (struct solstice* solstice,
    625    const struct solparser_geometry_id geom_id,
    626    struct ssol_instance** out_inst)
    627 {
    628   struct ssol_instance* inst = NULL;
    629   struct ssol_material* front = NULL;
    630   struct ssol_material* back = NULL;
    631   struct ssol_object** pssol_obj = NULL;
    632   struct ssol_object* ssol_obj = NULL;
    633   struct ssol_object* ssol_obj_new = NULL;
    634   struct ssol_shape* shape = NULL;
    635   int is_attached_to_scn = 0;
    636   int is_registered = 0;
    637   res_T res = RES_OK;
    638   ASSERT(solstice && out_inst);
    639 
    640   pssol_obj = htable_object_find(&solstice->objects, &geom_id.i);
    641   if(pssol_obj) {
    642     ssol_obj = *pssol_obj;
    643   } else {
    644     const struct solparser_geometry* geom;
    645     size_t iobj, nobjs;
    646 
    647     res = ssol_object_create(solstice->ssol, &ssol_obj_new);
    648     if(res != RES_OK) {
    649       fprintf(stderr, "Could not create a Solstice Solver object.\n");
    650       goto error;
    651     }
    652     ssol_obj = ssol_obj_new;
    653 
    654     geom = solparser_get_geometry(solstice->parser, geom_id);
    655     nobjs = solparser_geometry_get_objects_count(geom);
    656     FOR_EACH(iobj, 0, nobjs) {
    657       struct solparser_object_id obj_id;
    658 
    659       obj_id = solparser_geometry_get_object(geom, iobj);
    660       res = create_shaded_shape(solstice, obj_id, &front, &back, &shape);
    661       if(res != RES_OK) goto error;
    662 
    663       res = ssol_object_add_shaded_shape(ssol_obj, shape, front, back);
    664       if(res != RES_OK) {
    665         fprintf(stderr,
    666           "Could not add a shaded shape to a Solstice Solver object.\n");
    667         goto error;
    668       }
    669 
    670       /* Release the shape and its double sided material since they are now own
    671        * by the object */
    672       SSOL(shape_ref_put(shape));
    673       SSOL(material_ref_put(front));
    674       SSOL(material_ref_put(back));
    675       shape = NULL;
    676       front = NULL;
    677       back = NULL;
    678     }
    679   }
    680 
    681   res = ssol_object_instantiate(ssol_obj, &inst);
    682   if(res != RES_OK) {
    683     fprintf(stderr, "Could not instantiate a Solstice Solver object.\n");
    684     goto error;
    685   }
    686 
    687   res = ssol_scene_attach_instance(solstice->scene, inst);
    688   if(res != RES_OK) {
    689     fprintf(stderr,
    690       "Could not attach the instance against the Solstice Solver scene.\n");
    691     goto error;
    692   }
    693   is_attached_to_scn = 1;
    694 
    695   res = htable_object_set(&solstice->objects, &geom_id.i, &ssol_obj);
    696   if(res != RES_OK) {
    697     fprintf(stderr, "Could not register a Solstice Solver object.\n");
    698     goto error;
    699   }
    700   is_registered = 1;
    701 
    702 exit:
    703   *out_inst = inst;
    704   return res;
    705 error:
    706   if(is_attached_to_scn) SSOL(scene_detach_instance(solstice->scene, inst));
    707   if(is_registered) htable_object_erase(&solstice->objects, &geom_id.i);
    708   if(inst) SSOL(instance_ref_put(inst)), inst = NULL;
    709   if(ssol_obj_new) SSOL(object_ref_put(ssol_obj_new));
    710   if(shape) SSOL(shape_ref_put(shape));
    711   if(front) SSOL(material_ref_put(front));
    712   if(back) SSOL(material_ref_put(back));
    713   goto exit;
    714 }
    715