solstice-solver

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

commit 1732a933072aea3d2557ee88bc38f172f36b7e90
parent 4fbad2c17c7074c1ba25b5c0f41a3b65c6d9abd3
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri, 21 Apr 2017 15:23:49 +0200

Merge remote-tracking branch 'origin/feature_new_shapes' into develop

Diffstat:
Mcmake/CMakeLists.txt | 6++++--
Msrc/ssol.h | 22++++++++++++++++++++--
Msrc/ssol_draw_draft.c | 1+
Msrc/ssol_draw_pt.c | 8++++++--
Msrc/ssol_scene.c | 8++++----
Msrc/ssol_shape.c | 479+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/ssol_shape_c.h | 47++++++++++++++++++++++++++++++++++++++++++++---
Msrc/test_ssol_shape.c | 32+++++++++++++++++++++++++-------
8 files changed, 485 insertions(+), 118 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 0.2 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 @@ -107,6 +107,7 @@ enum ssol_quadric_type { SSOL_QUADRIC_PARABOL, SSOL_QUADRIC_HYPERBOL, SSOL_QUADRIC_PARABOLIC_CYLINDER, + SSOL_QUADRIC_HEMISPHERE, SSOL_QUADRIC_TYPE_COUNT__ }; @@ -171,7 +172,7 @@ static const struct ssol_quadric_parabol SSOL_QUADRIC_PARABOL_NULL = SSOL_QUADRIC_PARABOL_NULL__; struct ssol_quadric_hyperbol { - /* Define (x^2 + y^2) / a^2 - (z - 1/2)^2 / b^2 + 1 = 0 + /* Define (x^2 + y^2) / a^2 - (z - 1/2)^2 / b^2 + 1 = 0; z > 0 * with a^2 = f - f^2; b = f -1/2; f = real_focal / (img_focal + real_focal) */ double img_focal, real_focal; }; @@ -186,6 +187,14 @@ struct ssol_quadric_parabolic_cylinder { static const struct ssol_quadric_parabolic_cylinder SSOL_QUADRIC_PARABOLIC_CYLINDER_NULL = SSOL_QUADRIC_PARABOLIC_CYLINDER_NULL__; +struct ssol_quadric_hemisphere { + /* Define x^2 + y^2 + (z-radius)^2 - radius^2 = 0 with z <= r */ + double radius; +}; +#define SSOL_QUADRIC_HEMISPHERE_NULL__ { -1.0 } +static const struct ssol_quadric_hemisphere SSOL_QUADRIC_HEMISPHERE_NULL = +SSOL_QUADRIC_HEMISPHERE_NULL__; + struct ssol_quadric { enum ssol_quadric_type type; union { @@ -193,12 +202,13 @@ struct ssol_quadric { struct ssol_quadric_parabol parabol; struct ssol_quadric_hyperbol hyperbol; struct ssol_quadric_parabolic_cylinder parabolic_cylinder; + struct ssol_quadric_hemisphere hemisphere; } data; /* 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; }; @@ -401,6 +411,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__; diff --git a/src/ssol_draw_draft.c b/src/ssol_draw_draft.c @@ -72,6 +72,7 @@ Li 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; + break; default: FATAL("Unreachable code"); break; } diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -188,8 +188,12 @@ 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: + 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) { @@ -514,7 +514,7 @@ hit_filter_function } /* Save the nearest intersected quadric point */ - if(sshape->shape->type == SHAPE_PUNCHED && rdata->dst >= dst) { + 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,40 +50,53 @@ 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 */ double lower[2]; double upper[2]; }; +struct get_ctx { + size_t nbvert; + double two_pi_over_nbvert; + double radius; +}; + /******************************************************************************* * 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 FINLINE int +check_hemisphere(const struct ssol_quadric_hemisphere* hemisphere) +{ + return hemisphere && hemisphere->radius > 0; +} + static INLINE int check_quadric(const struct ssol_quadric* quadric) { @@ -92,6 +111,8 @@ check_quadric(const struct ssol_quadric* quadric) return check_hyperbol(&quadric->data.hyperbol); case SSOL_QUADRIC_PARABOLIC_CYLINDER: return check_parabolic_cylinder(&quadric->data.parabolic_cylinder); + case SSOL_QUADRIC_HEMISPHERE: + return check_hemisphere(&quadric->data.hemisphere); default: return 0; } } @@ -110,8 +131,9 @@ check_punched_surface(const struct ssol_punched_surface* punched_surface) size_t i; if(!punched_surface - || punched_surface->nb_carvings == 0 - || !punched_surface->carvings + || (punched_surface->nb_carvings == 0 + && punched_surface->quadric->type != SSOL_QUADRIC_HEMISPHERE) + || (punched_surface->nb_carvings && !punched_surface->carvings) || !punched_surface->quadric || !check_quadric(punched_surface->quadric)) return 0; @@ -235,6 +257,18 @@ parabolic_cylinder_z return (p[1] * p[1]) * pcyl->one_over_4focal; } +static FINLINE double +hemisphere_z + (const double p[2], + const struct priv_hemisphere_data* hemisphere) +{ + const double r2 = p[0] * p[0] + p[1] * p[1]; + const double z2 = hemisphere->sqr_radius - r2; + /* manage numerical unaccuracy */ + ASSERT(z2 >= -hemisphere->sqr_radius * FLT_EPSILON); + return (z2 > 0) ? -sqrt(z2) + hemisphere->radius : hemisphere->radius; +} + static void quadric_mesh_parabol_get_pos(const unsigned ivert, float pos[3], void* ctx) { @@ -244,7 +278,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); @@ -262,7 +296,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); @@ -281,7 +315,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); @@ -290,6 +324,25 @@ quadric_mesh_parabolic_cylinder_get_pos f3_set_d3(pos, p); } +static void +quadric_mesh_hemisphere_get_pos + (const unsigned ivert, float pos[3], void* ctx) +{ + const size_t i = ivert * 2/*#coords per vertex*/; + const struct quadric_mesh_context* msh = ctx; + double p[3]; /* Temporary quadric space position */ + ASSERT(pos && ctx); + p[0] = msh->coords[i + 0]; + p[1] = msh->coords[i + 1]; + p[2] = hemisphere_z(p, &msh->data->hemisphere); + + /* Transform the position in object space */ + d33_muld3(p, msh->transform, p); + d3_add(p, p, msh->transform + 9); + + f3_set_d3(pos, p); +} + static FINLINE int aabb_is_degenerated(const double lower[2], const double upper[2]) { @@ -324,6 +377,92 @@ carvings_compute_aabb } } +static void +carvings_compute_radius + (const struct ssol_carving* carvings, + const size_t ncarvings, + double* radius) +{ + size_t icarving; + double r2 = - DBL_MAX; + ASSERT(carvings && radius); + + if(!ncarvings) { + *radius = DBL_MAX; + return; + } + + FOR_EACH(icarving, 0, ncarvings) { + size_t ivert; + FOR_EACH(ivert, 0, carvings[icarving].nb_vertices) { + double pos[2]; + /* Discard the polygons to subtract */ + if (carvings[icarving].operation == SSOL_SUB) continue; + + carvings[icarving].get(ivert, pos, carvings[icarving].context); + r2 = MMAX(r2, d2_dot(pos, pos)); + } + } + + *radius = sqrt(r2); +} + +static res_T +build_triangulated_disk + (struct darray_double* coords, + struct darray_size_t* ids, + const double radius, + const size_t nsteps) +{ + struct s3dut_mesh_data data; + struct s3dut_mesh* mesh = NULL; + double *c_ptr = NULL; + size_t* i_ptr = NULL; + size_t i; + res_T res = RES_OK; + ASSERT(coords && ids && nsteps && radius > 0); + ASSERT(nsteps < UINT_MAX); + + s3dut_create_hemisphere + (coords->allocator, radius, (unsigned)nsteps, (unsigned)nsteps, &mesh); + if (res != RES_OK) { + fprintf(stderr, "Could not create the hemisphere 3D data.\n"); + goto error; + } + + S3DUT(mesh_get_data(mesh, &data)); + if (!data.nprimitives || !data.nvertices) { + res = RES_BAD_ARG; + goto error; + } + + darray_double_clear(coords); + darray_size_t_clear(ids); + + /* Reserve the memory space for the plane vertices */ + res = darray_double_resize(coords, data.nvertices * 2/*#coords per vertex*/); + if (res != RES_OK) goto error; + + /* Reserve the memory space for the plane indices */ + res = darray_size_t_resize(ids, data.nprimitives * 3/*#ids per triangle*/); + if (res != RES_OK) goto error; + + c_ptr = darray_double_data_get(coords); + FOR_EACH(i, 0, data.nvertices) { + d2_set(c_ptr + i * 2, data.positions + i * 3); /* don't get z */ + } + i_ptr = darray_size_t_data_get(ids); + FOR_EACH(i, 0, data.nprimitives * 3) i_ptr[i] = data.indices[i]; + +exit: + if(mesh) S3DUT(mesh_ref_put(mesh)); + return res; +error: + darray_double_clear(coords); + darray_size_t_clear(ids); + goto exit; +} + static res_T build_triangulated_plane (struct darray_double* coords, @@ -339,7 +478,7 @@ build_triangulated_plane double size_min; double delta; res_T res = RES_OK; - ASSERT(coords && lower && upper && nsteps); + ASSERT(coords && ids && lower && upper && nsteps); ASSERT(!aabb_is_degenerated(lower, upper)); darray_double_clear(coords); @@ -411,7 +550,7 @@ error: } static res_T -clip_triangulated_plane +clip_triangulated_sheet (struct darray_double* coords, struct darray_size_t* ids, struct scpr_mesh* mesh, @@ -551,7 +690,7 @@ 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; d2_set(ctx.lower, lower); d2_set(ctx.upper, upper); @@ -563,8 +702,9 @@ quadric_setup_s3d_shape_rt vdata[1].type = S3D_FLOAT2; vdata[1].get = quadric_mesh_get_uv; - 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[0].get = quadric_mesh_parabol_get_pos; break; @@ -577,6 +717,9 @@ quadric_setup_s3d_shape_rt case SSOL_QUADRIC_PLANE: vdata[0].get = quadric_mesh_plane_get_pos; break; + case SSOL_QUADRIC_HEMISPHERE: + vdata[0].get = quadric_mesh_hemisphere_get_pos; + break; default: FATAL("Unreachable code.\n"); break; } @@ -684,36 +827,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 @@ -764,6 +913,18 @@ quadric_parabolic_cylinder_gradient_local grad[2] = 2 * quad->focal; } +static FINLINE void +quadric_hemisphere_gradient_local + (const struct priv_hemisphere_data* quad, + const double pt[3], + double grad[3]) +{ + ASSERT(pt && grad); + grad[0] = -pt[0]; + grad[1] = -pt[1]; + grad[2] = quad->radius - pt[2]; +} + static FINLINE int quadric_plane_intersect_local (const double org[3], @@ -777,13 +938,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; } @@ -798,17 +959,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; } @@ -822,7 +983,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; @@ -833,12 +994,38 @@ 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; +} + +static FINLINE int +quadric_hemisphere_intersect_local + (const struct priv_hemisphere_data* quad, + const double org[3], + const double dir[3], + const double hint, + double hit_pt[3], + double grad[3], + double* dist) +{ + 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 n = solve_second(a, b, c, hint, 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; return 1; } @@ -852,15 +1039,16 @@ 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; } @@ -869,18 +1057,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->private_data.hemisphere); break; default: FATAL("Unreachable code\n"); break; } @@ -894,28 +1085,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: { + 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->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], @@ -931,21 +1125,25 @@ 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->private_data.hemisphere, org, dir, hint, pt, N, dist); break; default: FATAL("Unreachable code\n"); break; } @@ -1006,9 +1204,19 @@ priv_parabolic_cylinder_data_setup data->one_over_4focal = 1 / (4.0 * parabolic_cylinder->focal); } +static FINLINE void +priv_hemisphere_data_setup + (struct priv_hemisphere_data* data, + const struct ssol_quadric_hemisphere* hemisphere) +{ + ASSERT(data && hemisphere); + data->radius = hemisphere->radius; + data->sqr_radius = hemisphere->radius * hemisphere->radius; +} + 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); @@ -1026,14 +1234,18 @@ priv_quadric_data_setup priv_parabolic_cylinder_data_setup (&priv_data->pcylinder, &quadric->data.parabolic_cylinder); break; + case SSOL_QUADRIC_HEMISPHERE: + priv_hemisphere_data_setup + (&priv_data->hemisphere, &quadric->data.hemisphere); + break; default: FATAL("Unreachable code\n"); break; } } static INLINE size_t -priv_quadric_data_compute_slices_count +priv_quadric_data_compute_slices_count_aabb (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]) { @@ -1066,6 +1278,56 @@ priv_quadric_data_compute_slices_count return nslices; } +static INLINE size_t +priv_quadric_data_compute_slices_count_radius + (const enum ssol_quadric_type type, + const union private_data* priv_data, + const double radius) +{ + size_t nslices; + double pt[2]; + double max_z = -DBL_MAX; + + ASSERT(priv_data && radius > 0); + switch (type) { + case SSOL_QUADRIC_HEMISPHERE: + d2(pt, 0, radius); + max_z = MMAX(max_z, hemisphere_z(pt, &priv_data->hemisphere)); + + nslices = MMIN(50, (size_t)(3 + sqrt(max_z) * 6)); + break; + default: FATAL("Unreachable code\n"); break; + } + + return nslices; +} + +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); +} + + /******************************************************************************* * Local functions ******************************************************************************/ @@ -1087,8 +1349,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); @@ -1111,13 +1373,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 */ @@ -1129,12 +1392,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 && N_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); @@ -1146,14 +1408,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; } @@ -1246,11 +1508,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); } } @@ -1278,6 +1540,7 @@ ssol_punched_surface_setup const struct ssol_punched_surface* psurf) { double lower[2], upper[2]; /* Carvings AABB */ + double radius; struct darray_double coords; struct darray_size_t ids; size_t nslices; @@ -1294,34 +1557,55 @@ ssol_punched_surface_setup } /* Save quadric for further object instancing */ - shape->quadric = *psurf->quadric; - - carvings_compute_aabb(psurf->carvings, psurf->nb_carvings, lower, upper); - if(aabb_is_degenerated(lower, upper)) { - log_error(shape->dev, - "%s: infinite or null punched surface.\n", - FUNC_NAME); - res = RES_BAD_ARG; - goto error; + 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) { + carvings_compute_radius(psurf->carvings, psurf->nb_carvings, &radius); + radius = MMIN(radius, psurf->quadric->data.hemisphere.radius); + lower[0] = lower[1] = -radius; + upper[0] = upper[1] = +radius; + } else { + carvings_compute_aabb(psurf->carvings, psurf->nb_carvings, lower, upper); + if(aabb_is_degenerated(lower, upper)) { + log_error(shape->dev, + "%s: infinite or null punched surface.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } } /* 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); + if(psurf->quadric->type == SSOL_QUADRIC_HEMISPHERE) { + nslices = priv_quadric_data_compute_slices_count_radius + (shape->private_type.quadric, &shape->private_data, radius); + } + else { + nslices = priv_quadric_data_compute_slices_count_aabb + (shape->private_type.quadric, &shape->private_data, lower, upper); + } } - res = build_triangulated_plane(&coords, &ids, lower, upper, nslices); - if(res != RES_OK) goto error; - - res = clip_triangulated_plane - (&coords, &ids, shape->dev->scpr_mesh, psurf->carvings, psurf->nb_carvings); + if(psurf->quadric->type == SSOL_QUADRIC_HEMISPHERE) { + res = build_triangulated_disk(&coords, &ids, radius, nslices); + } else { + res = build_triangulated_plane(&coords, &ids, lower, upper, nslices); + } if(res != RES_OK) goto error; + + if(psurf->nb_carvings) { + res = clip_triangulated_sheet + (&coords, &ids, shape->dev->scpr_mesh, psurf->carvings, psurf->nb_carvings); + if(res != RES_OK) goto error; + } /* Setup the Star-3D shape to ray-trace */ res = quadric_setup_s3d_shape_rt(shape, &coords, &ids, lower, upper, @@ -1400,7 +1684,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; @@ -1410,4 +1694,3 @@ exit: error: goto exit; } - diff --git a/src/ssol_shape_c.h b/src/ssol_shape_c.h @@ -43,10 +43,20 @@ struct priv_pcylinder_data { double one_over_4focal; }; -union priv_quadric_data { +struct priv_hemisphere_data { + double radius; + double sqr_radius; +}; + +union private_data { struct priv_hyperbol_data hyperbol; struct priv_parabol_data parabol; struct priv_pcylinder_data pcylinder; + struct priv_hemisphere_data hemisphere; +}; + +union private_type { + enum ssol_quadric_type quadric; }; struct ssol_shape { @@ -54,14 +64,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 @@ -91,5 +111,26 @@ 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/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/test_ssol_shape.c b/src/test_ssol_shape.c @@ -151,13 +151,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; @@ -196,6 +190,10 @@ main(int argc, char** argv) quadric.data.parabol.focal = 1; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); + punched_surface.nb_carvings = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); + punched_surface.nb_carvings = 1; + quadric.data.parabol.focal = 0; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); quadric.data.parabol.focal = 1; @@ -205,6 +203,10 @@ main(int argc, char** argv) quadric.data.hyperbol.img_focal = 1; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); + punched_surface.nb_carvings = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); + punched_surface.nb_carvings = 1; + quadric.data.hyperbol.real_focal = 0; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); quadric.data.hyperbol.real_focal = 1; @@ -217,10 +219,26 @@ main(int argc, char** argv) quadric.data.parabolic_cylinder.focal = 1; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); + punched_surface.nb_carvings = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); + punched_surface.nb_carvings = 1; + quadric.data.parabolic_cylinder.focal = 0; CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); quadric.data.parabolic_cylinder.focal = 1; + quadric.type = SSOL_QUADRIC_HEMISPHERE; + quadric.data.hemisphere.radius = 10; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); + + punched_surface.nb_carvings = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_OK); + punched_surface.nb_carvings = 1; + + quadric.data.hemisphere.radius = 0; + CHECK(ssol_punched_surface_setup(shape, &punched_surface), RES_BAD_ARG); + quadric.data.hemisphere.radius = 10; + CHECK(ssol_shape_ref_put(shape), RES_OK); CHECK(ssol_device_ref_put(dev), RES_OK);