solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

commit e64e4ec43361c69ecf845d60d384edf1061b969c
parent 28309351246550297945b6966634b23bb85f22e6
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue, 11 Apr 2017 10:57:55 +0200

Add analytic shapes to the solver, starting with cylinders.

Diffstat:
Mcmake/CMakeLists.txt | 6++++--
Msrc/ssol.h | 55++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/ssol_draw_draft.c | 9+++++++--
Msrc/ssol_draw_pt.c | 9+++++++--
Msrc/ssol_scene.c | 21++++++++++++++++-----
Msrc/ssol_shape.c | 443+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/ssol_shape_c.h | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/ssol_solver.c | 1+
Msrc/test_ssol_shape.c | 41++++++++++++++++++++++++++++++++++-------
9 files changed, 535 insertions(+), 111 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -26,6 +26,7 @@ option(NO_TEST "Do not build tests" OFF) find_package(RCMake 0.2.3 REQUIRED) find_package(RSys 0.4 REQUIRED) find_package(Star3D 0.4 REQUIRED) +find_package(Star3DUT REQUIRED) find_package(StarCPR REQUIRED) find_package(StarSF 0.1 REQUIRED) find_package(StarSP 0.4 REQUIRED) @@ -38,11 +39,12 @@ include(rcmake_runtime) include_directories( ${RSys_INCLUDE_DIR} ${Star3D_INCLUDE_DIR} + ${Star3DUT_INCLUDE_DIR} ${StarCPR_INCLUDE_DIR} ${StarSF_INCLUDE_DIR} ${StarSP_INCLUDE_DIR}) -rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D StarCPR StarSF StarSP) +rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D Star3DUT StarCPR StarSF StarSP) ################################################################################ # Configure and define targets @@ -104,7 +106,7 @@ rcmake_prepend_path(SSOL_FILES_INC_API ${SSOL_SOURCE_DIR}) rcmake_prepend_path(SSOL_FILES_DOC ${PROJECT_SOURCE_DIR}/../) add_library(solstice-solver SHARED ${SSOL_FILES_SRC} ${SSOL_FILES_INC} ${SSOL_FILES_INC_API}) -target_link_libraries(solstice-solver RSys Star3D StarCPR StarSF StarSP) +target_link_libraries(solstice-solver RSys Star3D Star3DUT StarCPR StarSF StarSP) if(CMAKE_COMPILER_IS_GNUCC) target_link_libraries(solstice-solver m) diff --git a/src/ssol.h b/src/ssol.h @@ -203,7 +203,7 @@ struct ssol_quadric { /* 3x4 column major transformation of the quadric in object space */ double transform[12]; - /* Hint on the how to discretised */ + /* Hint on how to discretise */ size_t slices_count_hint; }; @@ -237,6 +237,41 @@ struct ssol_punched_surface { static const struct ssol_punched_surface SSOL_PUNCHED_SURFACE_NULL = SSOL_PUNCHED_SURFACE_NULL__; +enum ssol_analytic_type { + SSOL_ANALYTIC_CYLINDER, + SSOL_ANALYTIC_SURFACE_TYPES_COUNT__ +}; + +struct ssol_analytic_cylinder { + double radius; + double height; + unsigned nslices; + unsigned nstacks; +}; + +#define SSOL_ANALYTIC_CYLINDER_NULL__ { -1, -1, 0, 0 } +static const struct ssol_analytic_cylinder SSOL_ANALYTIC_CYLINDER_NULL = +SSOL_ANALYTIC_CYLINDER_NULL__; + +struct ssol_analytic_surface { + enum ssol_analytic_type type; + union { + struct ssol_analytic_cylinder cylinder; + } data; + + /* 3x4 column major transformation of the quadric in object space */ + double transform[12]; +}; + +#define SSOL_ANALYTIC_SURFACE_NULL__ { \ + SSOL_ANALYTIC_SURFACE_TYPES_COUNT__, \ + SSOL_ANALYTIC_CYLINDER_NULL__, \ + {1,0,0, 0,1,0, 0,0,1, 0,0,0}, \ +} + +static const struct ssol_analytic_surface SSOL_ANALYTIC_SURFACE_NULL = +SSOL_ANALYTIC_SURFACE_NULL__; + struct ssol_medium { double absorptivity; double refractive_index; @@ -395,6 +430,14 @@ struct ssol_mc_receiver { static const struct ssol_mc_receiver SSOL_MC_RECEIVER_NULL = SSOL_MC_RECEIVER_NULL__; +#define MC_RCV_NONE__ { \ + { -1, -1, -1 }, \ + { -1, -1, -1 }, \ + { -1, -1, -1 }, \ + { -1, -1, -1 }, \ + 0, NULL, NULL \ +} + struct ssol_mc_shape { /* Internal data */ size_t N__; @@ -635,6 +678,11 @@ ssol_shape_create_punched_surface struct ssol_shape** shape); SSOL_API res_T +ssol_shape_create_analytic_surface +(struct ssol_device* dev, + struct ssol_shape** shape); + +SSOL_API res_T ssol_shape_ref_get (struct ssol_shape* shape); @@ -684,6 +732,11 @@ ssol_mesh_setup const unsigned nattribs, void* data); +SSOL_API res_T +ssol_analytic_surface_setup + (struct ssol_shape* shape, + const struct ssol_analytic_surface* analytic_surface); + /******************************************************************************* * Material API - Define the surfacic (e.g.: BRDF) as well as the volumic * (e.g.: refractive index) properties of a geometry. diff --git a/src/ssol_draw_draft.c b/src/ssol_draw_draft.c @@ -65,8 +65,13 @@ Li /* Retrieve and normalized the hit normal */ switch(sshape->shape->type) { - case SHAPE_MESH: f3_normalize(N, hit.normal); break; - case SHAPE_PUNCHED: f3_normalize(N, f3_set_d3(N, ray_data.N)); break; + case SHAPE_MESH: + f3_normalize(N, hit.normal); + break; + case SHAPE_PUNCHED: + case SHAPE_ANALYTIC: + f3_normalize(N, f3_set_d3(N, ray_data.N)); + break; default: FATAL("Unreachable code"); break; } ASSERT(f3_is_normalized(N)); diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -188,8 +188,13 @@ Li(struct ssol_scene* scn, /* Retrieve and normalized the hit normal */ switch(sshape->shape->type) { - case SHAPE_MESH: d3_normalize(N, d3_set_f3(N, hit.normal)); break; - case SHAPE_PUNCHED: d3_normalize(N, ray_data.N); break; + case SHAPE_MESH: + d3_normalize(N, d3_set_f3(N, hit.normal)); + break; + case SHAPE_PUNCHED: + case SHAPE_ANALYTIC: + d3_normalize(N, ray_data.N); + break; default: FATAL("Unreachable code"); break; } diff --git a/src/ssol_scene.c b/src/ssol_scene.c @@ -478,10 +478,10 @@ hit_filter_function /* Project the hit position into the punched shape */ d3_set_f3(dir, dirf); d3_set_f3(org, orgf); - dst = punched_shape_trace_ray(sshape->shape, inst->transform, org, dir, - hit->distance, N); + dst = shape_trace_ray(sshape->shape, inst->transform, org, dir, + hit->distance, N, punched_shape_intersect_local); if(dst >= FLT_MAX) { - /* No projection is found => the ray does not intersect the quadric */ + /* No projection found => the ray does not intersect the quadric */ return 1; } if((float)dst <= rdata->range_min) { @@ -499,6 +499,17 @@ hit_filter_function return 1; } break; + case SHAPE_ANALYTIC: + d3_set_f3(dir, dirf); + d3_set_f3(org, orgf); + dst = shape_trace_ray(sshape->shape, inst->transform, org, dir, + hit->distance, N, analytic_intersect_local); + if(dst >= FLT_MAX) { + /* No projection found => the ray does not intersect the analytic surface */ + return 1; + } + hit_side = d3_dot(dir, N) < 0 ? SSOL_FRONT : SSOL_BACK; + break; default: FATAL("Unreachable code.\n"); break; } ASSERT(hit_side == SSOL_BACK || hit_side == SSOL_FRONT); @@ -513,8 +524,8 @@ hit_filter_function if((inst->receiver_mask & (int)hit_side) == 0) return 1; } - /* Save the nearest intersected quadric point */ - if(sshape->shape->type == SHAPE_PUNCHED && rdata->dst >= dst) { + /* Save the nearest intersected quadric/analytic point */ + if(sshape->shape->type != SHAPE_MESH && rdata->dst >= dst) { d3_set(rdata->N, N); rdata->dst = dst; } diff --git a/src/ssol_shape.c b/src/ssol_shape.c @@ -32,10 +32,16 @@ #include <rsys/math.h> #include <star/scpr.h> +#include <star/s3dut.h> #include <limits.h> /* UINT_MAX constant */ #include <math.h> /* copysign function */ +struct mesh_ctx_s3dut { + struct s3dut_mesh_data data; + double transform[12]; +}; + struct mesh_context { const double* coords; const size_t* ids; @@ -44,7 +50,8 @@ struct mesh_context { struct quadric_mesh_context { const double* coords; const size_t* ids; - const union priv_quadric_data* quadric; + const union private_data* data; + union private_type private_type; const double* transform; /* 3x4 column major matrix */ }; @@ -57,32 +64,32 @@ struct get_ctx { /******************************************************************************* * Helper functions ******************************************************************************/ -static INLINE int +static FINLINE int check_plane(const struct ssol_quadric_plane* plane) { return plane != NULL; } -static INLINE int +static FINLINE int check_parabol(const struct ssol_quadric_parabol* parabol) { return parabol && parabol->focal > 0; } -static INLINE int +static FINLINE int check_hyperbol(const struct ssol_quadric_hyperbol* hyperbol) { return hyperbol && hyperbol->img_focal > 0 && hyperbol->real_focal > 0; } -static INLINE int +static FINLINE int check_parabolic_cylinder (const struct ssol_quadric_parabolic_cylinder* parabolic_cylinder) { return parabolic_cylinder && parabolic_cylinder->focal > 0; } -static INLINE int +static FINLINE int check_hemisphere(const struct ssol_quadric_hemisphere* hemisphere) { return hemisphere && hemisphere->radius > 0; @@ -138,6 +145,28 @@ check_punched_surface(const struct ssol_punched_surface* punched_surface) return 1; } +static FINLINE int +check_cylinder(const struct ssol_analytic_cylinder* cylinder) +{ + return cylinder != NULL + && cylinder->radius > 0 && cylinder->height > 0 + && cylinder->nslices > 2 && cylinder->nstacks > 0; +} + +static INLINE int +check_analytic_surface(const struct ssol_analytic_surface* analytic_surface) +{ + if(!analytic_surface + || analytic_surface->type >= SSOL_ANALYTIC_SURFACE_TYPES_COUNT__) + return 0; + + switch(analytic_surface->type) { + case SSOL_ANALYTIC_CYLINDER: + return check_cylinder(&analytic_surface->data.cylinder); + default: return 0; + } +} + static INLINE int check_shape(const struct ssol_shape* shape) { @@ -255,7 +284,7 @@ quadric_mesh_parabol_get_pos(const unsigned ivert, float pos[3], void* ctx) ASSERT(pos && ctx); p[0] = msh->coords[i+0]; p[1] = msh->coords[i+1]; - p[2] = parabol_z(p, &msh->quadric->parabol); + p[2] = parabol_z(p, &msh->data->parabol); /* Transform the position in object space */ d33_muld3(p, msh->transform, p); @@ -273,7 +302,7 @@ quadric_mesh_hyperbol_get_pos(const unsigned ivert, float pos[3], void* ctx) ASSERT(pos && ctx); p[0] = msh->coords[i+0]; p[1] = msh->coords[i+1]; - p[2] = hyperbol_z(p, &msh->quadric->hyperbol); + p[2] = hyperbol_z(p, &msh->data->hyperbol); /* Transform the position in object space */ d33_muld3(p, msh->transform, p); @@ -292,7 +321,7 @@ quadric_mesh_parabolic_cylinder_get_pos ASSERT(pos && ctx); p[0] = msh->coords[i+0]; p[1] = msh->coords[i+1]; - p[2] = parabolic_cylinder_z(p, &msh->quadric->pcylinder); + p[2] = parabolic_cylinder_z(p, &msh->data->pcylinder); /* Transform the position in object space */ d33_muld3(p, msh->transform, p); @@ -311,7 +340,7 @@ quadric_mesh_hemisphere_get_pos ASSERT(pos && ctx); p[0] = msh->coords[i + 0]; p[1] = msh->coords[i + 1]; - p[2] = hemisphere_z(p, &msh->quadric->hemisphere); + p[2] = hemisphere_z(p, &msh->data->hemisphere); /* Transform the position in object space */ d33_muld3(p, msh->transform, p); @@ -578,13 +607,14 @@ quadric_setup_s3d_shape_rt ntris = (unsigned)darray_size_t_size_get(ids) / 3/*#ids per triangle*/; ctx.coords = darray_double_cdata_get(coords); ctx.ids = darray_size_t_cdata_get(ids); - ctx.transform = shape->quadric.transform; + ctx.transform = shape->transform; vdata.usage = S3D_POSITION; vdata.type = S3D_FLOAT3; vdata.get = NULL; - ctx.quadric = &shape->priv_quadric; - switch (shape->quadric.type) { + ctx.data = &shape->private_data; + ctx.private_type = shape->private_type; + switch (shape->private_type.quadric) { case SSOL_QUADRIC_PARABOL: vdata.get = quadric_mesh_parabol_get_pos; break; @@ -698,36 +728,42 @@ error: } /* Solve a 2nd degree equation. "hint" is used to select among the 2 solutions - * (if applies) the selected solution is then the closest to hint */ + * (if applies) the selected solution is then the closest to hint ans is + * returned in dist[0]. + * If there is a second solution, it is returned in dist[1]. + * Returns the number of roots. */ static int -quadric_solve_second +solve_second (const double a, const double b, const double c, const double hint, - double* dist) + double dist[2]) { - double t = -1; ASSERT(dist && hint >= 0); - if(a == 0) { - if(b != 0) t = -c / b; /* Degenerated case: 1st degree only */ + if(b != 0) { + dist[0] = -c / b; /* Degenerated case: 1st degree only */ + return 1; + } + return 0; /* 0 distance determined */ } else { /* Standard case: 2nd degree */ const double delta = b*b - 4*a*c; if(delta == 0) { - t = -b / (2*a); + dist[0] = -b / (2*a); + return 1; } else { const double sqrt_delta = sqrt(delta); /* Precise formula */ const double t1 = (-b - copysign(sqrt_delta, b)) / (2*a); const double t2 = c / (a*t1); - /* Choose the closest value to hint */ - t = fabs(t1 - hint) < fabs(t2 - hint) ? t1 : t2; + /* dist[0] is the closest value to hint */ + dist[0] = fabs(t1 - hint) < fabs(t2 - hint) ? t1 : t2; + dist[1] = fabs(t1 - hint) < fabs(t2 - hint) ? t2 : t1; + return 2; } } - *dist = t; - return t >= 0; } static FINLINE void @@ -790,6 +826,17 @@ quadric_hemisphere_gradient_local grad[2] = quad->radius - pt[2]; } +static FINLINE void +analytic_cylinder_gradient_local + (const double pt[3], + double grad[3]) +{ + ASSERT(pt && grad); + grad[0] = pt[0]; + grad[1] = pt[1]; + grad[2] = 0; +} + static FINLINE int quadric_plane_intersect_local (const double org[3], @@ -803,13 +850,13 @@ quadric_plane_intersect_local const double a = 0; const double b = dir[2]; const double c = org[2]; - double dst; - int sol = quadric_solve_second(a, b, c, hint, &dst); + double dst[2]; + const int n = solve_second(a, b, c, hint, dst); - if(!sol) return 0; - d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); + if(!n) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dst)); quadric_plane_gradient_local(grad); - *dist = dst; + *dist = *dst; return 1; } @@ -824,17 +871,17 @@ quadric_parabol_intersect_local double* dist) /* in/out: */ { /* Define x^2 + y^2 - 4*focal*z = 0 */ - double dst; + double dst[2]; const double a = dir[0] * dir[0] + dir[1] * dir[1]; const double b = 2 * org[0] * dir[0] + 2 * org[1] * dir[1] - 4 * quad->focal * dir[2]; const double c = org[0] * org[0] + org[1] * org[1] - 4 * quad->focal * org[2]; - const int sol = quadric_solve_second(a, b, c, hint, &dst); + const int n = solve_second(a, b, c, hint, dst); - if(!sol) return 0; - d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); + if(!n) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dst)); quadric_parabol_gradient_local(quad, hit_pt, grad); - *dist = dst; + *dist = *dst; return 1; } @@ -848,7 +895,7 @@ quadric_hyperbol_intersect_local double grad[3], double* dist) { - double dst; + double dst[2]; const double b2 = quad->abs_b * quad->abs_b; const double b2_a2 = b2 * quad->one_over_a_square; const double z0 = quad->g_square + quad->abs_b; @@ -859,12 +906,12 @@ quadric_hyperbol_intersect_local - (org[2] + z0 - quad->g_square) * dir[2]); const double c = b2_a2 * (org[0] * org[0] + org[1] * org[1]) + b2 - (org[2] + z0 - quad->g_square) * (org[2] + z0 - quad->g_square); - const int sol = quadric_solve_second(a, b, c, hint, &dst); + const int n = solve_second(a, b, c, hint, dst); - if(!sol) return 0; - d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); + if(!n) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dst)); quadric_hyperbol_gradient_local(quad, hit_pt, grad); - *dist = dst; + *dist = *dst; return 1; } @@ -878,19 +925,19 @@ quadric_hemisphere_intersect_local double grad[3], double* dist) { - double dst; + double dst[2]; double z0 = -quad->radius; const double a = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; const double b = 2 * (org[0] * dir[0] + org[1] * dir[1] + org[2] * dir[2] + z0 * dir[2]); const double c = org[0] * org[0] + org[1] * org[1] + org[2] * org[2] - quad->sqr_radius + 2 * z0 * org[2] + z0 * z0; - const int sol = quadric_solve_second(a, b, c, hint, &dst); + const int n = solve_second(a, b, c, hint, dst); - if(!sol) return 0; - d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst)); + if(!n) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dst)); quadric_hemisphere_gradient_local(quad, hit_pt, grad); - *dist = dst; + *dist = *dst; return 1; } @@ -904,15 +951,49 @@ quadric_parabolic_cylinder_intersect_local double grad[3], double* dist) { - /* Define y^2 - 4 focal z = 0 */ + double dst[2]; const double a = dir[1] * dir[1]; const double b = 2 * org[1] * dir[1] - 4 * quad->focal * dir[2]; const double c = org[1] * org[1] - 4 * quad->focal * org[2]; - const int sol = quadric_solve_second(a, b, c, hint, dist); + const int n = solve_second(a, b, c, hint, dst); - if(!sol) return 0; - d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dist)); + if(!n) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, *dst)); quadric_parabolic_cylinder_gradient_local(quad, hit_pt, grad); + *dist = *dst; + return 1; +} + +static FINLINE int +analytic_cylinder_intersect_local + (const struct priv_analytic_cylinder* analytic, + const double org[3], + const double dir[3], + const double hint, + double hit_pt[3], + double grad[3], + double* dist) /* in/out: */ +{ + double dst[2]; + const double a = dir[0] * dir[0] + dir[1] * dir[1]; + const double b = 2 * (org[0] * dir[0] + org[1] * dir[1]); + const double c = org[0] * org[0] + org[1] * org[1] + - analytic->radius * analytic->radius; + const int n = solve_second(a, b, c, hint, dst); + + if(!n) return 0; + /* We just solved intersection with an infinite cylinder. + * Now we filter according to cylinder's height. */ + d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst[0])); + if(fabs(hit_pt[2]) > 0.5 * analytic->height) { + if(n == 1) return 0; + d3_add(hit_pt, org, d3_muld(hit_pt, dir, dst[1])); + if(fabs(hit_pt[2]) > 0.5 * analytic->height) return 0; + *dist = dst[1]; + } else { + *dist = dst[0]; + } + analytic_cylinder_gradient_local(hit_pt, grad); return 1; } @@ -921,21 +1002,21 @@ punched_shape_set_z_local(const struct ssol_shape* shape, double pt[3]) { ASSERT(shape && pt); ASSERT(shape->type == SHAPE_PUNCHED); - switch (shape->quadric.type) { + switch (shape->private_type.quadric) { case SSOL_QUADRIC_PLANE: pt[2] = 0; break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: - pt[2] = parabolic_cylinder_z(pt, &shape->priv_quadric.pcylinder); + pt[2] = parabolic_cylinder_z(pt, &shape->private_data.pcylinder); break; case SSOL_QUADRIC_PARABOL: - pt[2] = parabol_z(pt, &shape->priv_quadric.parabol); + pt[2] = parabol_z(pt, &shape->private_data.parabol); break; case SSOL_QUADRIC_HYPERBOL: - pt[2] = hyperbol_z(pt, &shape->priv_quadric.hyperbol); + pt[2] = hyperbol_z(pt, &shape->private_data.hyperbol); break; case SSOL_QUADRIC_HEMISPHERE: - pt[2] = hemisphere_z(pt, &shape->priv_quadric.hemisphere); + pt[2] = hemisphere_z(pt, &shape->private_data.hemisphere); break; default: FATAL("Unreachable code\n"); break; } @@ -949,31 +1030,31 @@ punched_shape_set_normal_local { ASSERT(shape && pt); ASSERT(shape->type == SHAPE_PUNCHED); - switch (shape->quadric.type) { + switch (shape->private_type.quadric) { case SSOL_QUADRIC_PLANE: quadric_plane_gradient_local(normal); break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: quadric_parabolic_cylinder_gradient_local - (&shape->priv_quadric.pcylinder, pt, normal); + (&shape->private_data.pcylinder, pt, normal); break; case SSOL_QUADRIC_PARABOL: quadric_parabol_gradient_local - (&shape->priv_quadric.parabol, pt, normal); + (&shape->private_data.parabol, pt, normal); break; case SSOL_QUADRIC_HYPERBOL: quadric_hyperbol_gradient_local - (&shape->priv_quadric.hyperbol, pt, normal); + (&shape->private_data.hyperbol, pt, normal); break; case SSOL_QUADRIC_HEMISPHERE: quadric_hemisphere_gradient_local - (&shape->priv_quadric.hemisphere, pt, normal); + (&shape->private_data.hemisphere, pt, normal); break; default: FATAL("Unreachable code\n"); break; } } -static FINLINE int +int punched_shape_intersect_local (const struct ssol_shape* shape, const double org[3], @@ -989,31 +1070,56 @@ punched_shape_intersect_local ASSERT(dir[0] || dir[1] || dir[2]); /* Hits on quadrics must be recomputed more accurately */ - switch (shape->quadric.type) { + switch (shape->private_type.quadric) { case SSOL_QUADRIC_PLANE: hit = quadric_plane_intersect_local(org, dir, hint, pt, N, dist); break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: hit = quadric_parabolic_cylinder_intersect_local - (&shape->priv_quadric.pcylinder, org, dir, hint, pt, N, dist); + (&shape->private_data.pcylinder, org, dir, hint, pt, N, dist); break; case SSOL_QUADRIC_PARABOL: hit = quadric_parabol_intersect_local - (&shape->priv_quadric.parabol, org, dir, hint, pt, N, dist); + (&shape->private_data.parabol, org, dir, hint, pt, N, dist); break; case SSOL_QUADRIC_HYPERBOL: hit = quadric_hyperbol_intersect_local - (&shape->priv_quadric.hyperbol, org, dir, hint, pt, N, dist); + (&shape->private_data.hyperbol, org, dir, hint, pt, N, dist); break; case SSOL_QUADRIC_HEMISPHERE: hit = quadric_hemisphere_intersect_local - (&shape->priv_quadric.hemisphere, org, dir, hint, pt, N, dist); + (&shape->private_data.hemisphere, org, dir, hint, pt, N, dist); break; default: FATAL("Unreachable code\n"); break; } return hit; } +int +analytic_intersect_local + (const struct ssol_shape* shape, + const double org[3], + const double dir[3], + const double hint, + double pt[3], + double N[3], + double* dist) +{ + int hit; + ASSERT(shape && org && dir && hint >= 0 && pt && N && dist); + ASSERT(shape->type == SHAPE_ANALYTIC); + ASSERT(dir[0] || dir[1] || dir[2]); + + switch (shape->private_type.analytic) { + case SSOL_ANALYTIC_CYLINDER: + hit = analytic_cylinder_intersect_local + (&shape->private_data.cylinder, org, dir, hint, pt, N, dist); + break; + default: FATAL("Unreachable code.\n"); break; + } + return hit; +} + static void shape_release(ref_T* ref) { @@ -1080,7 +1186,7 @@ priv_hemisphere_data_setup static INLINE void priv_quadric_data_setup - (union priv_quadric_data* priv_data, + (union private_data* priv_data, const struct ssol_quadric* quadric) { ASSERT(priv_data && quadric); @@ -1109,7 +1215,7 @@ priv_quadric_data_setup static INLINE size_t priv_quadric_data_compute_slices_count (const enum ssol_quadric_type type, - const union priv_quadric_data* priv_data, + const union private_data* priv_data, const double lower[2], const double upper[2]) { @@ -1143,11 +1249,11 @@ priv_quadric_data_compute_slices_count double r; d2_set(u, upper); r = d2_dot(u, u); - if (r > priv_data->hemisphere.sqr_radius) + if(r > priv_data->hemisphere.sqr_radius) d2_muld(u, u, sqrt(priv_data->hemisphere.sqr_radius / r)); d2_set(l, lower); r = d2_dot(l, l); - if (r > priv_data->hemisphere.sqr_radius) + if(r > priv_data->hemisphere.sqr_radius) d2_muld(l, l, sqrt(priv_data->hemisphere.sqr_radius / r)); max_z = MMAX (hemisphere_z(l, &priv_data->hemisphere), @@ -1170,6 +1276,120 @@ get_circular(const size_t ivert, double position[2], void* ctx) position[1] = data->radius * sin(a); } +static void +mesh_ctx_s3dut_get_ids(const unsigned itri, unsigned ids[3], void* ctx) +{ + const struct mesh_ctx_s3dut* mesh = ctx; + ASSERT(ids && ctx && itri < mesh->data.nprimitives); + ASSERT(mesh->data.indices[itri * 3 + 0] <= UINT_MAX); + ASSERT(mesh->data.indices[itri * 3 + 1] <= UINT_MAX); + ASSERT(mesh->data.indices[itri * 3 + 2] <= UINT_MAX); + ids[0] = (unsigned)mesh->data.indices[itri * 3 + 0]; + ids[1] = (unsigned)mesh->data.indices[itri * 3 + 1]; + ids[2] = (unsigned)mesh->data.indices[itri * 3 + 2]; +} + +static void +mesh_ctx_s3dut_get_pos(const unsigned ivert, float pos[3], void* ctx) +{ + const struct mesh_ctx_s3dut* mesh = ctx; + double tmp[3]; + ASSERT(pos && ctx && ivert < mesh->data.nvertices); + d3_set(tmp, mesh->data.positions + ivert * 3); + d33_muld3(tmp, mesh->transform, tmp); + d3_add(tmp, mesh->transform + 9, tmp); + f3_set_d3(pos, tmp); +} + +/* Setup the Star-3D shape of the analytic surface to ray-trace */ +static res_T +analytic_setup_s3d_shape_rt + (struct ssol_shape* shape, + double* rt_area) +{ + struct s3dut_mesh* mesh = NULL; + struct mesh_ctx_s3dut mesh_ctx; + struct s3d_vertex_data attrs; + res_T res; + + switch(shape->private_type.analytic) { + case SSOL_ANALYTIC_CYLINDER: + ASSERT(shape->private_data.cylinder.nslices < UINT_MAX); + res = s3dut_create_cylinder( + shape->dev->allocator, + shape->private_data.cylinder.radius, + shape->private_data.cylinder.height, + shape->private_data.cylinder.nslices, + shape->private_data.cylinder.nstacks, + &mesh); + if(res != RES_OK) { + fprintf(stderr, "Could not create the cylinder 3D data.\n"); + goto error; + } + break; + default: FATAL("Unreachable code.\n"); break; + } + + S3DUT(mesh_get_data(mesh, &mesh_ctx.data)); + ASSERT(mesh_ctx.data.nprimitives <= UINT_MAX); + ASSERT(mesh_ctx.data.nvertices <= UINT_MAX); + d33_set(mesh_ctx.transform, shape->transform); + d3_set(mesh_ctx.transform + 9, shape->transform + 9); + + attrs.usage = SSOL_TO_S3D_POSITION; + attrs.type = S3D_FLOAT3; + attrs.get = mesh_ctx_s3dut_get_pos; + + res = s3d_mesh_setup_indexed_vertices + (shape->shape_rt, (unsigned)mesh_ctx.data.nprimitives, + mesh_ctx_s3dut_get_ids, (unsigned)mesh_ctx.data.nvertices, + &attrs, 1, &mesh_ctx); + if(res != RES_OK) { + fprintf(stderr, "Could not setup a Solstice Solver mesh shape.\n"); + goto error; + } + + /* FIXME: use mesh area or analytic area??? */ + *rt_area = mesh_compute_area + ((unsigned)mesh_ctx.data.nprimitives, mesh_ctx_s3dut_get_ids, + (unsigned)mesh_ctx.data.nvertices, attrs.get, &mesh_ctx); + *rt_area = 2 * PI * + shape->private_data.cylinder.radius * + shape->private_data.cylinder.height; +end: + if(mesh) s3dut_mesh_ref_put(mesh); + return RES_OK; +error: + goto end; +} + +static INLINE void +priv_analytic_cylinder_data_setup + (struct priv_analytic_cylinder* priv_data, + const struct ssol_analytic_cylinder* analytic) +{ + ASSERT(priv_data && analytic); + priv_data->radius = analytic->radius; + priv_data->height = analytic->height; + priv_data->nslices = analytic->nslices; + priv_data->nstacks = analytic->nstacks; +} + +static INLINE void +priv_analytic_data_setup + (union private_data* priv_data, + const struct ssol_analytic_surface* analytic) +{ + ASSERT(priv_data && analytic); + switch (analytic->type) { + case SSOL_ANALYTIC_CYLINDER: + priv_analytic_cylinder_data_setup + (&priv_data->cylinder, &analytic->data.cylinder); + break; + default: FATAL("Unreachable code\n"); break; + } +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -1191,8 +1411,8 @@ punched_shape_project_point ASSERT(shape->type == SHAPE_PUNCHED); /* Compute world<->quadric space transformations */ - d33_muld33(R, transform, shape->quadric.transform); - d33_muld3(T, transform, shape->quadric.transform+9); + d33_muld33(R, transform, shape->transform); + d33_muld3(T, transform, shape->transform+9); d3_add(T, T, transform + 9); d33_invtrans(R_invtrans, R); d3_minus(T_inv, T); @@ -1215,13 +1435,14 @@ punched_shape_project_point } double -punched_shape_trace_ray +shape_trace_ray (struct ssol_shape* shape, const double transform[12], /* Shape to world space transformation */ const double org[3], /* World space position near of the ray origin */ const double dir[3], /* World space ray direction */ const double hint_dst, /* Hint on the hit distance */ - double N_quadric[3]) /* World space normal onto the quadric */ + double N_shape[3], /* World space normal onto the shape */ + intersect_local_fn local) /* the intersection function for this shape */ { double R[9]; /* Quadric to world rotation matrix */ double R_invtrans[9]; /* Inverse transpose of R */ @@ -1233,12 +1454,11 @@ punched_shape_trace_ray double N_local[3]; double dst; /* Hit distance */ int valid; - ASSERT(shape && transform && org && N_quadric); - ASSERT(shape->type == SHAPE_PUNCHED); + ASSERT(shape && transform && org && shape); /* Compute world<->quadric space transformations */ - d33_muld33(R, transform, shape->quadric.transform); - d33_muld3(T, transform, shape->quadric.transform+9); + d33_muld33(R, transform, shape->transform); + d33_muld3(T, transform, shape->transform+9); d3_add(T, T, transform + 9); d33_invtrans(R_invtrans, R); d3_minus(T_inv, T); @@ -1250,14 +1470,14 @@ punched_shape_trace_ray /* Transform dir in quadric space */ d3_muld33(dir_local, dir, R_invtrans); - /* Project pos_local onto the quadric and compute its associated normal */ - valid = punched_shape_intersect_local + /* Project pos_local onto the shape and compute its associated normal */ + valid = local (shape, org_local, dir_local, hint_dst, hit_local, N_local, &dst); if(!valid) return INF; - /* Transform the quadric normal in world space */ - d33_muld3(N_quadric, R_invtrans, N_local); - d3_normalize(N_quadric, N_quadric); + /* Transform the shape normal in world space */ + d33_muld3(N_shape, R_invtrans, N_local); + d3_normalize(N_shape, N_shape); return dst; } @@ -1310,6 +1530,14 @@ ssol_shape_create_punched_surface } res_T +ssol_shape_create_analytic_surface + (struct ssol_device* dev, + struct ssol_shape** out_shape) +{ + return shape_create(dev, out_shape, SHAPE_ANALYTIC); +} + +res_T ssol_shape_ref_get(struct ssol_shape* shape) { if(!shape) return RES_BAD_ARG; @@ -1350,11 +1578,11 @@ ssol_shape_get_vertex_attrib /* Transform the fetch attrib */ if(shape->type == SHAPE_PUNCHED) { if(usage == SSOL_POSITION) { - d33_muld3(value, shape->quadric.transform, value); - d3_add(value, shape->quadric.transform + 9, value); + d33_muld3(value, shape->transform, value); + d3_add(value, shape->transform + 9, value); } else if(usage == SSOL_NORMAL) { double R_invtrans[9]; - d33_invtrans(R_invtrans, shape->quadric.transform); + d33_invtrans(R_invtrans, shape->transform); d33_muld3(value, R_invtrans, value); } } @@ -1399,7 +1627,9 @@ ssol_punched_surface_setup } /* Save quadric for further object instancing */ - shape->quadric = *psurf->quadric; + d33_set(shape->transform, psurf->quadric->transform); + d3_set(shape->transform+9, psurf->quadric->transform+9); + shape->private_type.quadric = psurf->quadric->type; if(psurf->quadric->type == SSOL_QUADRIC_HEMISPHERE) { size_t c; @@ -1420,7 +1650,7 @@ ssol_punched_surface_setup tmp_carvings[psurf->nb_carvings].nb_vertices = 1024; ctx.nbvert = tmp_carvings[psurf->nb_carvings].nb_vertices; ctx.two_pi_over_nbvert = 2 * PI / (double)ctx.nbvert; - ctx.radius = shape->quadric.data.hemisphere.radius; + ctx.radius = psurf->quadric->data.hemisphere.radius; tmp_carvings_count = psurf->nb_carvings + 1; } else { @@ -1438,14 +1668,14 @@ ssol_punched_surface_setup } /* Setup internal data */ - priv_quadric_data_setup(&shape->priv_quadric, psurf->quadric); + priv_quadric_data_setup(&shape->private_data, psurf->quadric); /* Define the #slices of the discretized quadric */ if(psurf->quadric->slices_count_hint != SIZE_MAX) { nslices = psurf->quadric->slices_count_hint; } else { nslices = priv_quadric_data_compute_slices_count - (shape->quadric.type, &shape->priv_quadric, lower, upper); + (shape->private_type.quadric, &shape->private_data, lower, upper); } res = build_triangulated_plane(&coords, &ids, lower, upper, nslices); @@ -1535,7 +1765,7 @@ ssol_mesh_setup shape->shape_rt_area = mesh_compute_area(ntris, get_indices, nverts, get_position, data); - /* The Star-3D shape to sample is the same of the one to ray-traced */ + /* The Star-3D shape to sample is the same that the one to ray-trace */ res = s3d_mesh_copy(shape->shape_rt, shape->shape_samp); if(res != RES_OK) goto error; shape->shape_samp_area = shape->shape_rt_area; @@ -1546,3 +1776,38 @@ error: goto exit; } +res_T +ssol_analytic_surface_setup + (struct ssol_shape* shape, + const struct ssol_analytic_surface* asurf) +{ + res_T res = RES_OK; + + if(!check_shape(shape) + || !check_analytic_surface(asurf) + || shape->type != SHAPE_ANALYTIC) { + res = RES_BAD_ARG; + goto error; + } + + /* Save analytic surface for further object instancing */ + d33_set(shape->transform, asurf->transform); + d3_set(shape->transform + 9, asurf->transform + 9); + shape->private_type.analytic = asurf->type; + + /* Setup internal data */ + priv_analytic_data_setup(&shape->private_data, asurf); + + /* Setup the Star-3D shape to ray-trace */ + res = analytic_setup_s3d_shape_rt(shape, &shape->shape_rt_area); + if(res != RES_OK) goto error; + + /* The Star-3D shape to sample is the same that the one to ray-trace */ + res = s3d_mesh_copy(shape->shape_rt, shape->shape_samp); + if(res != RES_OK) goto error; + shape->shape_samp_area = shape->shape_rt_area; +exit: + return res; +error: + goto exit; +} diff --git a/src/ssol_shape_c.h b/src/ssol_shape_c.h @@ -23,6 +23,7 @@ enum shape_type { SHAPE_MESH, SHAPE_PUNCHED, + SHAPE_ANALYTIC, SHAPE_TYPES_COUNT__ }; @@ -48,11 +49,24 @@ struct priv_hemisphere_data { double sqr_radius; }; -union priv_quadric_data { +struct priv_analytic_cylinder { + double radius; + double height; + unsigned nslices; + unsigned nstacks; +}; + +union private_data { struct priv_hyperbol_data hyperbol; struct priv_parabol_data parabol; struct priv_pcylinder_data pcylinder; struct priv_hemisphere_data hemisphere; + struct priv_analytic_cylinder cylinder; +}; + +union private_type { + enum ssol_quadric_type quadric; + enum ssol_analytic_type analytic; }; struct ssol_shape { @@ -60,14 +74,24 @@ struct ssol_shape { struct s3d_shape* shape_rt; /* Star-3D shape to ray-trace */ struct s3d_shape* shape_samp; /* Star-3D shape to sample */ - union priv_quadric_data priv_quadric; - struct ssol_quadric quadric; + union private_data private_data; + union private_type private_type; + double transform[12]; double shape_rt_area, shape_samp_area; struct ssol_device* dev; ref_T ref; }; +typedef int(*intersect_local_fn) + (const struct ssol_shape* shape, + const double org[3], + const double dir[3], + const double hint, + double pt[3], + double N[3], + double* dist); + /* Project pos onto the punched surface and retrieve its associated normal */ extern LOCAL_SYM void punched_shape_project_point @@ -97,5 +121,36 @@ shape_fetched_raw_vertex_attrib const enum ssol_attrib_usage usage, double value[]); +/* Compute ray/punched shape intersection */ +extern LOCAL_SYM int punched_shape_intersect_local + (const struct ssol_shape* shape, + const double org[3], + const double dir[3], + const double hint, + double pt[3], + double N[3], + double* dist); + +/* Compute ray/analytic shape intersection */ +extern LOCAL_SYM int analytic_intersect_local + (const struct ssol_shape* shape, + const double org[3], + const double dir[3], + const double hint, + double pt[3], + double N[3], + double* dist); + +/* Compute ray/shape intersection */ +extern LOCAL_SYM double +shape_trace_ray + (struct ssol_shape* shape, + const double transform[12], /* Shape to world space transformation */ + const double org[3], /* World space position near of the ray origin */ + const double dir[3], /* World space ray direction */ + const double hint_dst, /* Hint on the hit distance */ + double N_quadric[3], /* World space normal onto the quadric */ + intersect_local_fn local); + #endif /* SSOL_SHAPE_C_H */ diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -327,6 +327,7 @@ point_update_from_hit d3_set_f3(pt->pos, tmpf); break; case SHAPE_PUNCHED: + case SHAPE_ANALYTIC: d3_normalize(pt->N, rdata->N); d3_muld(tmp, pt->dir, rdata->dst); d3_add(pt->pos, pt->pos, tmp); diff --git a/src/test_ssol_shape.c b/src/test_ssol_shape.c @@ -36,6 +36,7 @@ main(int argc, char** argv) -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 0.f, -2.f }; const size_t npolygon_verts = sizeof(polygon)/sizeof(double[2]); + struct ssol_analytic_surface analytic = SSOL_ANALYTIC_SURFACE_NULL__; double val[3]; unsigned ids[3]; unsigned i, n; @@ -151,13 +152,7 @@ main(int argc, char** argv) CHECK(ssol_shape_create_punched_surface(dev, NULL), RES_BAD_ARG); CHECK(ssol_shape_create_punched_surface(NULL, &shape), RES_BAD_ARG); CHECK(ssol_shape_create_punched_surface(dev, &shape), RES_OK); - - CHECK(ssol_shape_ref_get(NULL), RES_BAD_ARG); - CHECK(ssol_shape_ref_get(shape), RES_OK); - - CHECK(ssol_shape_ref_put(NULL), RES_BAD_ARG); - CHECK(ssol_shape_ref_put(shape), RES_OK); - + carving.get = get_polygon_vertices; carving.operation = SSOL_AND; carving.nb_vertices = npolygon_verts; @@ -230,6 +225,38 @@ main(int argc, char** argv) quadric.data.hemisphere.radius = 10; CHECK(ssol_shape_ref_put(shape), RES_OK); + + CHECK(ssol_shape_create_analytic_surface(NULL, &shape), RES_BAD_ARG); + CHECK(ssol_shape_create_analytic_surface(dev, NULL), RES_BAD_ARG); + CHECK(ssol_shape_create_analytic_surface(dev, &shape), RES_OK); + + analytic.type = SSOL_ANALYTIC_CYLINDER; + analytic.data.cylinder.height = 10; + analytic.data.cylinder.radius = 10; + analytic.data.cylinder.nslices = 10; + analytic.data.cylinder.nstacks = 10; + CHECK(ssol_analytic_surface_setup(shape, &analytic), RES_OK); + + analytic.data.cylinder.height = 0; + CHECK(ssol_analytic_surface_setup(shape, &analytic), RES_BAD_ARG); + analytic.data.cylinder.height = 10; + + analytic.data.cylinder.radius = 0; + CHECK(ssol_analytic_surface_setup(shape, &analytic), RES_BAD_ARG); + analytic.data.cylinder.radius = 10; + + analytic.data.cylinder.nslices = 0; + CHECK(ssol_analytic_surface_setup(shape, &analytic), RES_BAD_ARG); + analytic.data.cylinder.nslices = 10; + + analytic.data.cylinder.nstacks = 0; + CHECK(ssol_analytic_surface_setup(shape, &analytic), RES_BAD_ARG); + analytic.data.cylinder.nstacks = 10; + + analytic.type = (enum ssol_analytic_type)999; + CHECK(ssol_analytic_surface_setup(shape, &analytic), RES_BAD_ARG); + + CHECK(ssol_shape_ref_put(shape), RES_OK); CHECK(ssol_device_ref_put(dev), RES_OK); check_memory_allocator(&allocator);