solstice-solver

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

commit 998abd07d9867e6cfeb298c644bc2bd2d2d0e67f
parent a31ae00092eb444e4c9f4deae0182104cfb2ae0c
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 18 Apr 2017 13:49:47 +0200

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

Diffstat:
Msrc/ssol.h | 49+++++++++++++++++++++++++++++++++++++++++--------
Msrc/ssol_draw_draft.c | 37+++++++++++++++++++++++++++++--------
Msrc/ssol_draw_pt.c | 35+++++++++++++++++++++++++----------
Msrc/ssol_image.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/ssol_material.c | 217+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/ssol_material_c.h | 51+++++++++++++++++++++++++++------------------------
Msrc/ssol_param_buffer.c | 43+++++++++++++++++++++++++++++++++----------
Msrc/ssol_shape.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/ssol_solver.c | 13+++++++++++--
Msrc/test_ssol_draw.c | 14+++++++++-----
Msrc/test_ssol_image.c | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/test_ssol_materials.h | 64+++++++++++++++-------------------------------------------------
Msrc/test_ssol_param_buffer.c | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
13 files changed, 613 insertions(+), 260 deletions(-)

diff --git a/src/ssol.h b/src/ssol.h @@ -92,6 +92,16 @@ enum ssol_pixel_format { SSOL_PIXEL_FORMATS_COUNT__ }; +enum ssol_filter_mode { + SSOL_FILTER_LINEAR, + SSOL_FILTER_NEAREST +}; + +enum ssol_address_mode { + SSOL_ADDRESS_CLAMP, + SSOL_ADDRESS_REPEAT +}; + enum ssol_quadric_type { SSOL_QUADRIC_PLANE, SSOL_QUADRIC_PARABOL, @@ -229,16 +239,27 @@ struct ssol_medium { #define SSOL_MEDIUM_VACUUM__ { 0, 1 } static const struct ssol_medium SSOL_MEDIUM_VACUUM = SSOL_MEDIUM_VACUUM__; +struct ssol_surface_fragment { + double dir[3]; /* World space incoming direction. Point forward the surface */ + double P[3]; /* World space position */ + double Ng[3]; /* Normalized world space geometry normal */ + double Ns[3]; /* Normalized world space shading normal */ + double uv[2]; /* Texture coordinates */ + double dPdu[3]; /* Partial derivative of the position in u */ + double dPdv[3]; /* Partial derivative of the position in v */ +}; + +#define SSOL_SURFACE_FRAGMENT_NULL__ \ + {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0}, {0,0,0}, {0,0,0}} +static const struct ssol_surface_fragment SSOL_SURFACE_FRAGMENT_NULL = + SSOL_SURFACE_FRAGMENT_NULL__; + typedef void (*ssol_shader_getter_T) (struct ssol_device* dev, struct ssol_param_buffer* buf, - const double wavelength, /* In nanometer */ - const double P[3], /* World space position */ - const double Ng[3], /* World space geometry normal */ - const double Ns[3], /* World space shading normal */ - const double uv[2], /* Texture coordinates */ - const double w[3], /* Incoming direction. Point toward the surface */ + const double wavelength, + const struct ssol_surface_fragment* fragment, double* val); /* Returned value */ /* Dielectric material shader */ @@ -523,12 +544,21 @@ ssol_image_get_layout SSOL_API res_T ssol_image_map (const struct ssol_image* image, - void** memory); + char** memory); SSOL_API res_T ssol_image_unmap (const struct ssol_image* image); +SSOL_API res_T +ssol_image_sample + (const struct ssol_image* image, + const enum ssol_filter_mode filter, + const enum ssol_address_mode address_u, + const enum ssol_address_mode address_v, + const double uv[2], + void* val); + /* Helper function that matches the `ssol_write_pixels_T' functor type */ SSOL_API res_T ssol_image_write @@ -870,7 +900,10 @@ SSOL_API void* ssol_param_buffer_allocate (struct ssol_param_buffer* buf, const size_t size, - const size_t alignment); /* Power of 2 in [1, 64] */ + const size_t alignment, /* Power of 2 in [1, 64] */ + /* Functor to invoke on the allocated memory priorly to its destruction. + * May be NULL */ + void (*release)(void*)); /* Retrieve the address of the first allocated parameter */ SSOL_API void* diff --git a/src/ssol_draw_draft.c b/src/ssol_draw_draft.c @@ -17,6 +17,7 @@ #include "ssol_camera.h" #include "ssol_device_c.h" #include "ssol_draw.h" +#include "ssol_material_c.h" #include "ssol_object_c.h" #include "ssol_scene_c.h" #include "ssol_shape_c.h" @@ -41,6 +42,7 @@ Li double val[3]) { const float range[2] = {0, FLT_MAX}; + struct ssol_surface_fragment frag; struct ray_data ray_data = RAY_DATA_NULL; struct s3d_hit hit; ASSERT(scn && view && org && dir && val); @@ -52,9 +54,12 @@ Li d3_splat(val, 0); } else { struct ssol_instance* inst; + struct ssol_material* mtl; const struct shaded_shape* sshape; size_t isshape; - float N[3]={0}; + double o[3], wi[3]; + double N[3]={0}; + double cos_Ns_wi; /* Retrieve the hit shaded shape */ inst = *htable_instance_find(&scn->instances_rt, &hit.prim.inst_id); @@ -65,12 +70,27 @@ 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: 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; } - ASSERT(f3_is_normalized(N)); - d3_splat(val, fabs(f3_dot(N, dir))); + + d3_set_f3(o, org); + d3_set_f3(wi, dir); + d3_normalize(wi, wi); + if(d3_dot(N, wi) < 0) { + mtl = sshape->mtl_front; + } else { + mtl = sshape->mtl_back; + d3_minus(N, N); + } + + surface_fragment_setup(&frag, o, wi, N, &hit.prim, hit.uv); + material_shade_normal(mtl, &frag, 1/*TODO wavelength*/, frag.Ns); + + ASSERT(d3_is_normalized(frag.Ns)); + cos_Ns_wi = d3_dot(frag.Ns, d3_minus(wi, wi)); + d3_splat(val, MMAX(cos_Ns_wi, 0)); } } @@ -84,14 +104,14 @@ draw_pixel const float pix_sz[2], /* Normalized pixel size */ const size_t nsamples, double pixel[3], - void* ctx) + void* data) { - struct darray_float* samples = ctx; + struct darray_float* samples = data; float samp[2]; float ray_org[3], ray_dir[3]; double sum[3] = {0, 0, 0}; size_t i; - ASSERT(scn && cam && view && pix_coords && pix_sz && nsamples && pixel && ctx); + ASSERT(scn && cam && view && pix_coords && pix_sz && nsamples && pixel && data); (void)ithread; FOR_EACH(i, 0, nsamples) { @@ -135,6 +155,7 @@ ssol_draw_draft if(!scn || !spp) return RES_BAD_ARG; darray_float_init(scn->dev->allocator, &samples); + res = darray_float_reserve(&samples, spp * 2/*#dimensions*/); if(res != RES_OK) goto error; diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -108,13 +108,12 @@ sun_lighting d3_minus(wi, sun->direction); /* The point look backward the sun */ - if(d3_dot(wi, N) < 0) return 0.0; + cos_wi_N = d3_dot(wi, N); + if(cos_wi_N < 0 || eq_eps(cos_wi_N, 0, 1.e-6)) return 0.0; R = ssf_bsdf_eval(bsdf, wo, N, wi); if(R <= 0) return 0.0; - cos_wi_N = d3_dot(wi, N); - f3_set_d3(ray_dir, wi); S3D(scene_view_trace_ray(view, ray_org, ray_dir, ray_range, ray_data, &hit)); if(S3D_HIT_NONE(&hit)) return R * cos_wi_N; @@ -135,7 +134,7 @@ Li(struct ssol_scene* scn, struct ssol_instance* inst; struct ssol_material* mtl; const struct shaded_shape* sshape; - struct surface_fragment frag; + struct ssol_surface_fragment frag; size_t isshape; double throughput = 1.0; double wi[3], o[3], uv[3]; @@ -144,6 +143,7 @@ Li(struct ssol_scene* scn, double L = 0; double R; double pdf; + double cos_wi_Ng; const float ray_range[2] = {0, FLT_MAX}; float ray_org[3]; float ray_dir[3]; @@ -203,9 +203,14 @@ Li(struct ssol_scene* scn, } surface_fragment_setup(&frag, o, wo, N, &hit.prim, hit.uv); + material_shade_normal(mtl, &frag, 1/*TODO wlen*/, frag.Ns); + + /* Shaded normal may look backward the outgoing direction */ + if(d3_dot(frag.Ns, wo) > 0) break; + SSF(bsdf_clear(ctx->bsdf)); - res = material_shade_rendering - (mtl, &frag, 1/*TODO wavelength*/, &medium, ctx->bsdf); + res = material_setup_bsdf + (mtl, &frag, 1/*TODO wavelength*/, &medium, 1/*Rendering*/, ctx->bsdf); if(res != RES_OK) goto error; /* Update the ray */ @@ -218,19 +223,29 @@ Li(struct ssol_scene* scn, d3_minus(wo, wo); if(scn->sun) { L += throughput * sun_lighting - (scn->sun, view, &ray_data, ctx->bsdf, wo, N, ray_org); + (scn->sun, view, &ray_data, ctx->bsdf, wo, frag.Ns, ray_org); } + /* Sampling a bounce direction */ R = ssf_bsdf_sample(ctx->bsdf, ctx->rng, wo, frag.Ns, wi, &type, &pdf); ASSERT(0 <= R && R <= 1); + + /* Due to the shading normal, the sampled direction may point in the wrong + * direction wrt the sampled BSDF component. */ + cos_wi_Ng = d3_dot(frag.Ng, wi); + if((cos_wi_Ng > 0 && (type & SSF_TRANSMISSION)) + || (cos_wi_Ng < 0 && (type & SSF_REFLECTION))) { + R = 0; + } + f3_set_d3(ray_dir, wi); if(type & SSF_TRANSMISSION) material_get_next_medium(mtl, &medium, &medium); if(!russian_roulette) { - throughput *= fabs(d3_dot(wi, N)) * R; + throughput *= fabs(d3_dot(wi, frag.Ns)) * R; } else { if(ssp_rng_canonical(ctx->rng) >= R) break; - throughput *= d3_dot(wi, N); + throughput *= fabs(d3_dot(wi, frag.Ns)); } if(throughput <= 0) break; @@ -271,7 +286,7 @@ draw_pixel ctx = darray_thread_context_data_get(thread_ctxs) + ithread; FOR_EACH(isample, 0, nsamples) { - const int MAX_NFAILURES = 10; + const int MAX_NFAILURES = 100; double weight[3]; float samp[2]; /* Pixel sample */ float ray_org[3], ray_dir[3]; diff --git a/src/ssol_image.c b/src/ssol_image.c @@ -13,6 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L /* nextafter support */ + #include "ssol.h" #include "ssol_image_c.h" #include "ssol_device_c.h" @@ -21,11 +23,35 @@ #include <rsys/ref_count.h> #include <rsys/rsys.h> +#include <math.h> #include <string.h> /******************************************************************************* * Helper functions ******************************************************************************/ +static INLINE double +map_address(const double address, const enum ssol_address_mode mode) +{ + double dbl; + double i; + switch(mode) { + case SSOL_ADDRESS_CLAMP: dbl = CLAMP(address, 0, nextafter(1,0)); break; + case SSOL_ADDRESS_REPEAT: + dbl = modf(address, &i); + if(dbl < 0) dbl = 1.0+dbl; + break; + default: FATAL("Unreachable code.\n"); break; + } + return dbl; +} + +static INLINE const char* +get_pixel(const struct ssol_image* img, const size_t x, const size_t y) +{ + ASSERT(img && x < img->size[0] && y < img->size[1]); + return img->mem + y*img->pitch + x*ssol_sizeof_pixel_format(img->format); +} + static void image_release(ref_T* ref) { @@ -136,7 +162,7 @@ ssol_image_get_layout } res_T -ssol_image_map(const struct ssol_image* img, void** mem) +ssol_image_map(const struct ssol_image* img, char** mem) { if(!img || !mem) return RES_BAD_ARG; *mem = img->mem; @@ -151,6 +177,57 @@ res_T ssol_image_unmap(const struct ssol_image* img) } res_T +ssol_image_sample + (const struct ssol_image* img, + const enum ssol_filter_mode filter, + const enum ssol_address_mode address_u, + const enum ssol_address_mode address_v, + const double uv[2], + void* val) +{ + double* z00, *z01, *z10, *z11; + double x0, y0, x1, y1; + double texsz[2]; + double s, t; + double* pix = val; + double integer; + + if(!img || !uv || !val) return RES_BAD_ARG; + + /* Only double3 pixel format is currently supported */ + if(img->format != SSOL_PIXEL_DOUBLE3) return RES_BAD_ARG; + + x0 = map_address(uv[0], address_u) * (double)img->size[0]; + y0 = map_address(uv[1], address_v) * (double)img->size[1]; + + switch(filter) { + case SSOL_FILTER_NEAREST: + z00 = (double*)get_pixel(img, (size_t)x0, (size_t)y0); + pix[0] = z00[0]; + pix[1] = z00[1]; + pix[2] = z00[2]; + break; + case SSOL_FILTER_LINEAR: + texsz[0] = 1.0/(double)img->size[0]; + texsz[1] = 1.0/(double)img->size[1]; + x1 = map_address(uv[0] + texsz[0], address_u) * (double)img->size[0]; + y1 = map_address(uv[1] + texsz[1], address_v) * (double)img->size[1]; + z00 = (double*)get_pixel(img, (size_t)x0, (size_t)y0); + z01 = (double*)get_pixel(img, (size_t)x0, (size_t)y1); + z10 = (double*)get_pixel(img, (size_t)x1, (size_t)y0); + z11 = (double*)get_pixel(img, (size_t)x1, (size_t)y1); + s = modf(x0, &integer); + t = modf(y0, &integer); + pix[0] = (1-s)*((1-t)*z00[0] + t*z01[0]) + s*((1-t)*z10[0] + t*z11[0]); + pix[1] = (1-s)*((1-t)*z00[1] + t*z01[1]) + s*((1-t)*z10[1] + t*z11[1]); + pix[2] = (1-s)*((1-t)*z00[2] + t*z01[2]) + s*((1-t)*z10[2] + t*z11[2]); + break; + default: FATAL("Unreachable code.\n"); break; + } + return RES_OK; +} + +res_T ssol_image_write (void* image, const size_t origin[2], diff --git a/src/ssol_material.c b/src/ssol_material.c @@ -18,8 +18,10 @@ #include "ssol_material_c.h" #include "ssol_device_c.h" -#include <rsys/double3.h> #include <rsys/double2.h> +#include <rsys/double3.h> +#include <rsys/double33.h> +#include <rsys/float2.h> #include <rsys/float3.h> #include <rsys/float33.h> #include <rsys/ref_count.h> @@ -33,10 +35,23 @@ /******************************************************************************* * Helper functions ******************************************************************************/ +static void +shade_normal_default + (struct ssol_device* dev, + struct ssol_param_buffer* buf, + const double wlen, + const struct ssol_surface_fragment* frag, + double* val) /* Returned value */ +{ + ASSERT(frag && val); + (void)dev, (void)buf, (void)wlen; + d3_set(val, frag->Ns); +} + static res_T -dielectric_shade +setup_dielectric_bsdf (const struct ssol_material* mtl, - const struct surface_fragment* fragment, + const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const struct ssol_medium* medium, struct ssf_bsdf* bsdf) @@ -44,18 +59,11 @@ dielectric_shade struct ssf_bxdf* brdf = NULL; struct ssf_bxdf* btdf = NULL; struct ssf_fresnel* fresnel = NULL; - const struct ssol_dielectric_shader* shader; double eta_i, eta_t; - double N[3]; res_T res = RES_OK; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC); ASSERT(medium && bsdf); - - shader = &mtl->data.dielectric; - - /* Fetch material attribs */ - shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, fragment->Ng, - fragment->Ns, fragment->uv, fragment->dir, N); + (void)wavelength, (void)fragment; if(!MEDIA_EQ(medium, &mtl->out_medium)) { log_error(mtl->dev, "Inconsistent medium description.\n"); @@ -91,27 +99,21 @@ error: } static res_T -matte_shade +setup_matte_bsdf (const struct ssol_material* mtl, - const struct surface_fragment* fragment, + const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ struct ssf_bsdf* bsdf) { struct ssf_bxdf* brdf = NULL; - const struct ssol_matte_shader* shader; - double normal[3]; double reflectivity; res_T res; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MATTE); ASSERT(bsdf); - shader = &mtl->data.matte; - /* Fetch material attribs */ - shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, normal); - shader->reflectivity(mtl->dev, mtl->buf, wavelength, fragment->pos, - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, &reflectivity); + mtl->data.matte.reflectivity + (mtl->dev, mtl->buf, wavelength, fragment, &reflectivity); /* Setup the BRDF */ res = ssf_bxdf_create(mtl->dev->allocator, &ssf_lambertian_reflection, &brdf); @@ -131,9 +133,9 @@ error: } static res_T -mirror_shade +setup_mirror_bsdf (const struct ssol_material* mtl, - const struct surface_fragment* fragment, + const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const int rendering, struct ssf_bsdf* bsdf) @@ -141,23 +143,17 @@ mirror_shade struct ssf_bxdf* brdf = NULL; struct ssf_fresnel* fresnel = NULL; struct ssf_microfacet_distribution* distrib = NULL; - const struct ssol_mirror_shader* shader; - double normal[3]; double roughness; double reflectivity; res_T res; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MIRROR); ASSERT(bsdf); - shader = &mtl->data.mirror; - /* Fetch material attribs */ - shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, normal); - shader->reflectivity(mtl->dev, mtl->buf, wavelength, fragment->pos, - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, &reflectivity); - shader->roughness(mtl->dev, mtl->buf, wavelength, fragment->pos, - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, &roughness); + mtl->data.mirror.reflectivity + (mtl->dev, mtl->buf, wavelength, fragment, &reflectivity); + mtl->data.mirror.roughness + (mtl->dev, mtl->buf, wavelength, fragment, &roughness); /* Setup the fresnel term */ res = ssf_fresnel_create(mtl->dev->allocator, &ssf_fresnel_constant, &fresnel); @@ -207,15 +203,13 @@ error: } static res_T -thin_dielectric_shade +setup_thin_dielectric_bsdf (const struct ssol_material* mtl, - const struct surface_fragment* fragment, + const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ struct ssf_bsdf* bsdf) { struct ssf_bxdf* bxdf = NULL; - const struct ssol_thin_dielectric_shader* shader; - double N[3]; double thickness; double absorptivity; double eta_i; @@ -223,12 +217,8 @@ thin_dielectric_shade res_T res = RES_OK; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_THIN_DIELECTRIC); ASSERT(bsdf); + (void)wavelength, (void)fragment; - shader = &mtl->data.thin_dielectric.shader; - - /* Fetch material attribs */ - shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, N); eta_i = mtl->out_medium.refractive_index; eta_t = mtl->data.thin_dielectric.slab_medium.refractive_index; absorptivity = mtl->data.thin_dielectric.slab_medium.absorptivity; @@ -253,39 +243,6 @@ error: goto exit; } -static INLINE res_T -shade - (const struct ssol_material* mtl, - const struct surface_fragment* fragment, - const double wavelength, /* In nanometer */ - const int rendering, /* Is material used for rendering */ - const struct ssol_medium* medium, - struct ssf_bsdf* bsdf) -{ - res_T res = RES_OK; - ASSERT(mtl); - - /* Specific material shading */ - switch(mtl->type) { - case SSOL_MATERIAL_DIELECTRIC: - res = dielectric_shade - (mtl, fragment, wavelength, medium, bsdf); - break; - case SSOL_MATERIAL_MATTE: - res = matte_shade(mtl, fragment, wavelength, bsdf); - break; - case SSOL_MATERIAL_MIRROR: - res = mirror_shade(mtl, fragment, wavelength, rendering, bsdf); - break; - case SSOL_MATERIAL_THIN_DIELECTRIC: - res = thin_dielectric_shade(mtl, fragment, wavelength, bsdf); - break; - case SSOL_MATERIAL_VIRTUAL: /* Nothing to shade */ break; - default: FATAL("Unreachable code\n"); break; - } - return res; -} - static INLINE int check_shader_dielectric(const struct ssol_dielectric_shader* shader) { @@ -366,6 +323,7 @@ ssol_material_create material->type = type; material->in_medium = SSOL_MEDIUM_VACUUM; material->out_medium = SSOL_MEDIUM_VACUUM; + material->normal = shade_normal_default; exit: if (out_material) *out_material = material; @@ -461,9 +419,10 @@ ssol_dielectric_setup || !check_medium(outside_medium) || !check_medium(inside_medium)) return RES_BAD_ARG; - material->data.dielectric = *shader; + material->data.dielectric.dummy = 1; material->out_medium = *outside_medium; material->in_medium = *inside_medium; + material->normal = shader->normal; return RES_OK; } @@ -475,7 +434,9 @@ ssol_mirror_setup || material->type != SSOL_MATERIAL_MIRROR || !check_shader_mirror(shader)) return RES_BAD_ARG; - material->data.mirror = *shader; + material->normal = shader->normal; + material->data.mirror.reflectivity = shader->reflectivity; + material->data.mirror.roughness = shader->roughness; return RES_OK; } @@ -487,7 +448,8 @@ ssol_matte_setup || material->type != SSOL_MATERIAL_MATTE || !check_shader_matte(shader)) return RES_BAD_ARG; - material->data.matte = *shader; + material->normal = shader->normal; + material->data.matte.reflectivity = shader->reflectivity; return RES_OK; } @@ -506,11 +468,11 @@ ssol_thin_dielectric_setup || !check_medium(slab_medium) || thickness < 0) return RES_BAD_ARG; - material->data.thin_dielectric.shader = *shader; material->data.thin_dielectric.slab_medium = *slab_medium; material->data.thin_dielectric.thickness = thickness; material->out_medium = *outside_medium; material->in_medium = *outside_medium; + material->normal = shader->normal; return RES_OK; } @@ -526,7 +488,7 @@ ssol_material_create_virtual ******************************************************************************/ void surface_fragment_setup - (struct surface_fragment* fragment, + (struct ssol_surface_fragment* fragment, const double pos[3], const double dir[3], const double normal[3], @@ -535,31 +497,68 @@ surface_fragment_setup { struct s3d_attrib attr; char has_texcoord, has_normal; + struct s3d_attrib uvs[3]; + struct s3d_attrib P[3]; + double duv1[2], duv2[2]; + double dP1[3], dP2[3]; + double det; ASSERT(fragment && pos && dir && primitive && uv); /* Assume that the submitted normal look forward the incoming dir */ ASSERT(d3_dot(normal, dir) <= 0); - /* Setup the incoming direction */ - d3_set(fragment->dir, dir); - - /* Setup the surface position */ - d3_set(fragment->pos, pos); + d3_set(fragment->dir, dir); /* Setup the incoming direction */ + d3_set(fragment->P, pos); /* Setup the surface position */ + d3_normalize(fragment->Ng, normal); /* Normalize the geometry normal */ - /* Normalize the geometry normal */ - d3_set(fragment->Ng, normal); - d3_normalize(fragment->Ng, fragment->Ng); + /* Retrieve the position of the triangle vertices */ + S3D(triangle_get_vertex_attrib(primitive, 0, S3D_POSITION, &P[0])); + S3D(triangle_get_vertex_attrib(primitive, 1, S3D_POSITION, &P[1])); + S3D(triangle_get_vertex_attrib(primitive, 2, S3D_POSITION, &P[2])); /* Retrieve the tex coord */ S3D(primitive_has_attrib(primitive, SSOL_TO_S3D_TEXCOORD, &has_texcoord)); if (!has_texcoord) { d2_set_f2(fragment->uv, uv); + uvs[0].type = uvs[1].type = uvs[2].type = S3D_FLOAT2; + uvs[0].usage = uvs[1].usage = uvs[2].usage = SSOL_TO_S3D_TEXCOORD; + f2(uvs[0].value, 1, 0); + f2(uvs[1].value, 0, 1); + f2(uvs[2].value, 0, 0); } else { S3D(primitive_get_attrib(primitive, SSOL_TO_S3D_TEXCOORD, uv, &attr)); + S3D(triangle_get_vertex_attrib(primitive, 0, SSOL_TO_S3D_TEXCOORD, &uvs[0])); + S3D(triangle_get_vertex_attrib(primitive, 1, SSOL_TO_S3D_TEXCOORD, &uvs[1])); + S3D(triangle_get_vertex_attrib(primitive, 2, SSOL_TO_S3D_TEXCOORD, &uvs[2])); ASSERT(attr.type == S3D_FLOAT2); d2_set_f2(fragment->uv, attr.value); } + /* Compute the partial derivatives. */ + duv1[0] = uvs[1].value[0] - uvs[0].value[0]; + duv1[1] = uvs[1].value[1] - uvs[0].value[1]; + duv2[0] = uvs[2].value[0] - uvs[0].value[0]; + duv2[1] = uvs[2].value[1] - uvs[0].value[1]; + dP1[0] = P[1].value[0] - P[0].value[0]; + dP1[1] = P[1].value[1] - P[0].value[1]; + dP1[2] = P[1].value[2] - P[0].value[2]; + dP2[0] = P[2].value[0] - P[0].value[0]; + dP2[1] = P[2].value[1] - P[0].value[1]; + dP2[2] = P[2].value[2] - P[0].value[2]; + det = duv1[0]*duv2[1] - duv1[1]*duv2[0]; + if(det == 0) { /* Handle zero determinant */ + double basis[9]; + d33_basis(basis, fragment->Ng); + d3_set(fragment->dPdu, basis + 0); + d3_set(fragment->dPdv, basis + 3); + } else { + double a[3], b[3]; + d3_sub(fragment->dPdu, d3_muld(a, dP1, duv2[1]), d3_muld(b, dP2, duv1[1])); + d3_sub(fragment->dPdv, d3_muld(a, dP2, duv1[0]), d3_muld(b, dP1, duv2[0])); + d3_divd(fragment->dPdu, fragment->dPdu, det); + d3_divd(fragment->dPdv, fragment->dPdv, det); + } + /* Retrieve and normalize the shading normal in world space */ S3D(primitive_has_attrib(primitive, SSOL_TO_S3D_NORMAL, &has_normal)); if (!has_normal) { @@ -593,28 +592,50 @@ surface_fragment_setup } } -res_T -material_shade +void +material_shade_normal (const struct ssol_material* mtl, - const struct surface_fragment* fragment, - const double wavelength, /* In nanometer */ - const struct ssol_medium* medium, - struct ssf_bsdf* bsdf) + const struct ssol_surface_fragment* frag, + const double wavelength, + double N[3]) { - return shade(mtl, fragment, wavelength, 0, medium, bsdf); + ASSERT(mtl && frag && N); + mtl->normal(mtl->dev, mtl->buf, wavelength, frag, N); } res_T -material_shade_rendering +material_setup_bsdf (const struct ssol_material* mtl, - const struct surface_fragment* fragment, + const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const struct ssol_medium* medium, + const int rendering, /* Is BSDF used for rendering */ struct ssf_bsdf* bsdf) { - return shade(mtl, fragment, wavelength, 1, medium, bsdf); + res_T res = RES_OK; + ASSERT(mtl); + + switch(mtl->type) { + case SSOL_MATERIAL_DIELECTRIC: + res = setup_dielectric_bsdf + (mtl, fragment, wavelength, medium, bsdf); + break; + case SSOL_MATERIAL_MATTE: + res = setup_matte_bsdf(mtl, fragment, wavelength, bsdf); + break; + case SSOL_MATERIAL_MIRROR: + res = setup_mirror_bsdf(mtl, fragment, wavelength, rendering, bsdf); + break; + case SSOL_MATERIAL_THIN_DIELECTRIC: + res = setup_thin_dielectric_bsdf(mtl, fragment, wavelength, bsdf); + break; + case SSOL_MATERIAL_VIRTUAL: /* Nothing to shade */ break; + default: FATAL("Unreachable code\n"); break; + } + return res; } + res_T material_get_next_medium (const struct ssol_material* mtl, diff --git a/src/ssol_material_c.h b/src/ssol_material_c.h @@ -27,19 +27,20 @@ struct ssol_device; ( ((A)->refractive_index == (B)->refractive_index) \ && ((A)->absorptivity == (B)->absorptivity)) -struct surface_fragment { - double dir[3]; /* World space incoming direction */ - double pos[3]; /* World space position */ - double Ng[3]; /* Normalized world space primitive normal */ - double Ns[3]; /* Normalized world space shading normal */ - double uv[2]; /* Texture coordinates */ +struct dielectric { + int dummy; +}; + +struct matte { + ssol_shader_getter_T reflectivity; +}; + +struct mirror { + ssol_shader_getter_T reflectivity; + ssol_shader_getter_T roughness; }; -#define SURFACE_FRAGMENT_NULL__ {{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0}} -static const struct surface_fragment SURFACE_FRAGMENT_NULL = - SURFACE_FRAGMENT_NULL__; struct thin_dielectric { - struct ssol_thin_dielectric_shader shader; struct ssol_medium slab_medium; double thickness; }; @@ -47,10 +48,12 @@ struct thin_dielectric { struct ssol_material { enum ssol_material_type type; + ssol_shader_getter_T normal; + union { - struct ssol_dielectric_shader dielectric; - struct ssol_matte_shader matte; - struct ssol_mirror_shader mirror; + struct dielectric dielectric; + struct matte matte; + struct mirror mirror; struct thin_dielectric thin_dielectric; } data; @@ -64,28 +67,27 @@ struct ssol_material { extern LOCAL_SYM void surface_fragment_setup - (struct surface_fragment* fragment, + (struct ssol_surface_fragment* fragment, const double pos[3], const double dir[3], const double normal[3], const struct s3d_primitive* primitive, const float uv[2]); -extern LOCAL_SYM res_T -material_shade +extern LOCAL_SYM void +material_shade_normal (const struct ssol_material* mtl, - const struct surface_fragment* fragment, - const double wavelength, /* In nanometer */ - const struct ssol_medium* medium, /* Current medium */ - struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ + const struct ssol_surface_fragment* fragment, + const double wavelength, + double N[3]); -/* Material shading for rendering purposes */ extern LOCAL_SYM res_T -material_shade_rendering +material_setup_bsdf (const struct ssol_material* mtl, - const struct surface_fragment* fragment, + const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ - const struct ssol_medium* medium, + const struct ssol_medium* medium, /* Current medium */ + const int rendering, /* Is material used for rendering purposes */ struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ extern LOCAL_SYM res_T @@ -95,3 +97,4 @@ material_get_next_medium struct ssol_medium* next_medium); #endif /* SSOL_MATERIAL_C_H */ + diff --git a/src/ssol_param_buffer.c b/src/ssol_param_buffer.c @@ -22,15 +22,19 @@ #define DEFAULT_ALIGNMENT 64 struct param { - size_t size; /* In Bytes */ - size_t offset; /* In Bytes */ + void* mem; + void (*release)(void*); }; +#define DARRAY_NAME param +#define DARRAY_DATA struct param +#include <rsys/dynamic_array.h> + struct ssol_param_buffer { char* pool; size_t capacity; size_t size; - + struct darray_param params; ref_T ref; struct ssol_device* dev; }; @@ -45,8 +49,10 @@ param_buffer_release(ref_T* ref) struct ssol_device* dev; ASSERT(ref); buf = CONTAINER_OF(ref, struct ssol_param_buffer, ref); + SSOL(param_buffer_clear(buf)); dev = buf->dev; if(buf->pool) MEM_RM(dev->allocator, buf->pool); + darray_param_release(&buf->params); MEM_RM(dev->allocator, buf); SSOL(device_ref_put(dev)); } @@ -78,6 +84,8 @@ ssol_param_buffer_create ref_init(&buf->ref); buf->capacity = capacity; buf->size = 0; + darray_param_init(dev->allocator, &buf->params); + buf->pool = MEM_ALLOC_ALIGNED(dev->allocator, capacity, DEFAULT_ALIGNMENT); if(!buf->pool) { res = RES_MEM_ERR; @@ -115,25 +123,32 @@ void* ssol_param_buffer_allocate (struct ssol_param_buffer* buf, const size_t size, /* In Bytes */ - const size_t align) /* Must be a power of 2 in [1, 64] */ + const size_t align, /* Must be a power of 2 in [1, 64] */ + void (*release)(void*)) /* May be NULL */ { + struct param param = { NULL, NULL }; size_t offset; - void* mem = NULL; + res_T res = RES_OK; if(!buf || !size || !IS_POW2(align) || align > DEFAULT_ALIGNMENT) goto error; offset = ALIGN_SIZE(buf->size, align); if(offset + size > buf->capacity) goto error; - - mem = buf->pool + offset; - ASSERT(IS_ALIGNED(mem, align)); + + param.mem = buf->pool + offset; + param.release = release; + ASSERT(IS_ALIGNED(param.mem, align)); + + res = darray_param_push_back(&buf->params, &param); + if(res != RES_OK) goto error; + buf->size = offset + size; exit: - return mem; + return param.mem; error: - mem = NULL; + param.mem = NULL; goto exit; } @@ -147,7 +162,15 @@ ssol_param_buffer_get(struct ssol_param_buffer* buf) res_T ssol_param_buffer_clear(struct ssol_param_buffer* buf) { + size_t i; if(!buf) return RES_BAD_ARG; + + /* Release the parameter */ + FOR_EACH(i, 0, darray_param_size_get(&buf->params)) { + struct param* param = darray_param_data_get(&buf->params)+i; + if(param->release) param->release(param->mem); + } + darray_param_clear(&buf->params); buf->size = 0; return RES_OK; } diff --git a/src/ssol_shape.c b/src/ssol_shape.c @@ -46,6 +46,8 @@ struct quadric_mesh_context { const size_t* ids; const union priv_quadric_data* quadric; const double* transform; /* 3x4 column major matrix */ + double lower[2]; + double upper[2]; }; /******************************************************************************* @@ -163,6 +165,20 @@ mesh_get_pos(const size_t ivert, double pos[2], void* ctx) } static void +quadric_mesh_get_uv(const unsigned ivert, float uv[2], void* ctx) +{ + const size_t i = ivert*2/*#coords per vertex*/; + const struct quadric_mesh_context* msh = ctx; + double tmp[2]; + ASSERT(uv && ctx); + tmp[0] = (msh->coords[i+0] - msh->lower[0]) / (msh->upper[0] - msh->lower[0]); + tmp[1] = (msh->coords[i+1] - msh->lower[1]) / (msh->upper[1] - msh->lower[1]); + + uv[0] = (float)tmp[0]; + uv[1] = (float)tmp[1]; +} + +static void quadric_mesh_get_ids(const unsigned itri, unsigned ids[3], void* ctx) { const size_t i = itri*3/*#ids per triangle*/; @@ -514,53 +530,62 @@ quadric_setup_s3d_shape_rt (const struct ssol_shape* shape, const struct darray_double* coords, const struct darray_size_t* ids, + const double lower[2], + const double upper[2], struct s3d_shape* s3dshape, double* rt_area) { struct quadric_mesh_context ctx; - struct s3d_vertex_data vdata; + struct s3d_vertex_data vdata[2]; unsigned nverts; unsigned ntris; res_T res; - ASSERT(shape && coords && ids && s3dshape && rt_area); + ASSERT(shape && coords && ids && lower && upper && s3dshape && rt_area); ASSERT(darray_double_size_get(coords)%2 == 0); ASSERT(darray_size_t_size_get(ids)%3 == 0); ASSERT(darray_double_size_get(coords)/2 <= UINT_MAX); ASSERT(darray_size_t_size_get(ids)/3 <= UINT_MAX); + ASSERT(!aabb_is_degenerated(lower, upper)); nverts = (unsigned)darray_double_size_get(coords) / 2/*#coords per vertex*/; 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; + d2_set(ctx.lower, lower); + d2_set(ctx.upper, upper); + + vdata[0].usage = S3D_POSITION; + vdata[0].type = S3D_FLOAT3; + vdata[0].get = NULL; + + vdata[1].usage = SSOL_TO_S3D_TEXCOORD; + vdata[1].type = S3D_FLOAT2; + vdata[1].get = quadric_mesh_get_uv; - vdata.usage = S3D_POSITION; - vdata.type = S3D_FLOAT3; - vdata.get = NULL; ctx.quadric = &shape->priv_quadric; switch (shape->quadric.type) { case SSOL_QUADRIC_PARABOL: - vdata.get = quadric_mesh_parabol_get_pos; + vdata[0].get = quadric_mesh_parabol_get_pos; break; case SSOL_QUADRIC_HYPERBOL: - vdata.get = quadric_mesh_hyperbol_get_pos; + vdata[0].get = quadric_mesh_hyperbol_get_pos; break; case SSOL_QUADRIC_PARABOLIC_CYLINDER: - vdata.get = quadric_mesh_parabolic_cylinder_get_pos; + vdata[0].get = quadric_mesh_parabolic_cylinder_get_pos; break; case SSOL_QUADRIC_PLANE: - vdata.get = quadric_mesh_plane_get_pos; + vdata[0].get = quadric_mesh_plane_get_pos; break; default: FATAL("Unreachable code.\n"); break; } res = s3d_mesh_setup_indexed_vertices - (s3dshape, ntris, quadric_mesh_get_ids, nverts, &vdata, 1, &ctx); + (s3dshape, ntris, quadric_mesh_get_ids, nverts, vdata, 2, &ctx); if(res != RES_OK) return res; - ASSERT(vdata.get); *rt_area = mesh_compute_area - (ntris, quadric_mesh_get_ids, nverts, vdata.get, &ctx); + (ntris, quadric_mesh_get_ids, nverts, vdata[0].get, &ctx); return RES_OK; } @@ -571,31 +596,41 @@ quadric_setup_s3d_shape_samp (const struct ssol_quadric* quadric, const struct darray_double* coords, const struct darray_size_t* ids, + const double lower[2], + const double upper[2], struct s3d_shape* shape, double *samp_area) { struct quadric_mesh_context ctx; - struct s3d_vertex_data vdata; + struct s3d_vertex_data vdata[2]; unsigned nverts; unsigned ntris; res_T res; - ASSERT(coords && ids && shape); + ASSERT(coords && ids && shape && ids && lower && samp_area); ASSERT(darray_double_size_get(coords)%2 == 0); ASSERT(darray_size_t_size_get(ids)%3 == 0); ASSERT(darray_double_size_get(coords)/2 <= UINT_MAX); ASSERT(darray_size_t_size_get(ids)/3 <= UINT_MAX); + ASSERT(!aabb_is_degenerated(lower, upper)); nverts = (unsigned)darray_double_size_get(coords) / 2/*#coords per vertex*/; 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 = quadric->transform; + d2_set(ctx.lower, lower); + d2_set(ctx.upper, upper); + + vdata[0].usage = S3D_POSITION; + vdata[0].type = S3D_FLOAT3; + vdata[0].get = quadric_mesh_plane_get_pos; + + vdata[1].usage = SSOL_TO_S3D_TEXCOORD; + vdata[1].type = S3D_FLOAT2; + vdata[1].get = quadric_mesh_get_uv; - vdata.usage = S3D_POSITION; - vdata.type = S3D_FLOAT3; - vdata.get = quadric_mesh_plane_get_pos; res = s3d_mesh_setup_indexed_vertices - (shape, ntris, quadric_mesh_get_ids, nverts, &vdata, 1, &ctx); + (shape, ntris, quadric_mesh_get_ids, nverts, vdata, 2, &ctx); if(res != RES_OK) return res; *samp_area = mesh_compute_area (ntris, quadric_mesh_get_ids, nverts, quadric_mesh_plane_get_pos, &ctx); @@ -1289,13 +1324,13 @@ ssol_punched_surface_setup if(res != RES_OK) goto error; /* Setup the Star-3D shape to ray-trace */ - res = quadric_setup_s3d_shape_rt - (shape, &coords, &ids, shape->shape_rt, &shape->shape_rt_area); + res = quadric_setup_s3d_shape_rt(shape, &coords, &ids, lower, upper, + shape->shape_rt, &shape->shape_rt_area); if(res != RES_OK) goto error; /* Setup the Star-3D shape to sample */ - res = quadric_setup_s3d_shape_samp - (psurf->quadric, &coords, &ids, shape->shape_samp, &shape->shape_samp_area); + res = quadric_setup_s3d_shape_samp(psurf->quadric, &coords, &ids, lower, + upper, shape->shape_samp, &shape->shape_samp_area); if(res != RES_OK) goto error; exit: diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -360,7 +360,7 @@ point_shade double dir[3]) { struct ssol_material* mtl; - struct surface_fragment frag; + struct ssol_surface_fragment frag; double r = 1; double wi[3], pdf; int type; @@ -383,7 +383,7 @@ point_shade /* Shade the surface fragment */ mtl = point_get_material(pt); SSF(bsdf_clear(bsdf)); - res = material_shade(mtl, &frag, pt->wl, medium, bsdf); + res = material_setup_bsdf(mtl, &frag, pt->wl, medium, 0, bsdf); if(res != RES_OK) return res; /* By convention, Star-SF assumes that incoming and reflected @@ -393,8 +393,17 @@ point_shade if(d3_dot(wi, frag.Ns) <= 0) { r = 0; } else { + double cos_dir_Ng; r = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &type, &pdf); ASSERT(0 <= r && r <= 1); + + /* Due to the shading normal, the sampled direction may point in the wrong + * direction wrt the sampled BSDF component. */ + cos_dir_Ng = d3_dot(frag.Ng, dir); + if((cos_dir_Ng > 0 && (type & SSF_TRANSMISSION)) + || (cos_dir_Ng < 0 && (type & SSF_REFLECTION))) { + r = 0; + } } pt->incoming_weight = pt->weight; pt->absorptivity_loss_before = pt->absorptivity_loss; diff --git a/src/test_ssol_draw.c b/src/test_ssol_draw.c @@ -183,11 +183,13 @@ main(int argc, char** argv) struct ssol_camera* cam; struct ssol_scene* scn; struct ssol_sun* sun; - unsigned char* pixels = NULL; + struct image img; + uint8_t* pixels; const double pos[3] = {278.0, -1000.0, 273.0}; const double tgt[3] = {278.0, 0.0, 273.0}; const double up[3] = {0.0, 0.0, 1.0}; double dir[3]; + size_t pitch; res_T (*draw_func) (struct ssol_scene* scn, struct ssol_camera* cam, @@ -233,8 +235,10 @@ main(int argc, char** argv) CHECK(ssol_sun_set_dni(sun, 1000), RES_OK); CHECK(ssol_scene_attach_sun(scn, sun), RES_OK); - pixels = MEM_CALLOC(&allocator, HEIGHT, PITCH); - NCHECK(pixels, NULL); + pitch = WIDTH * sizeof_image_format(IMAGE_RGB8); + image_init(&allocator, &img); + image_setup(&img, WIDTH, HEIGHT, pitch, IMAGE_RGB8, NULL); + pixels = (uint8_t*)img.pixels; CHECK(draw_func(NULL, NULL, 0, 0, 0, NULL, pixels), RES_BAD_ARG); CHECK(draw_func(scn, NULL, 0, 0, 0, NULL, pixels), RES_BAD_ARG); @@ -301,9 +305,9 @@ main(int argc, char** argv) CHECK(draw_func(NULL, cam, WIDTH, WIDTH, 4, write_RGB8, pixels), RES_BAD_ARG); CHECK(draw_func(scn, cam, WIDTH, HEIGHT, 4, write_RGB8, pixels), RES_OK); - CHECK(image_ppm_write_stream(stdout, WIDTH, HEIGHT, 3, pixels), RES_OK); + CHECK(image_write_ppm_stream(&img, 0, stdout), RES_OK); - MEM_RM(&allocator, pixels); + CHECK(image_release(&img), RES_OK); CHECK(ssol_device_ref_put(dev), RES_OK); CHECK(ssol_camera_ref_put(cam), RES_OK); CHECK(ssol_scene_ref_put(scn), RES_OK); diff --git a/src/test_ssol_image.c b/src/test_ssol_image.c @@ -16,6 +16,94 @@ #include "ssol.h" #include "test_ssol_utils.h" + +#include <rsys/double2.h> +#include <rsys/double3.h> + +static void +check_sampling(struct ssol_device* dev) +{ + struct ssol_image_layout layout; + struct ssol_image* img; + size_t pixsz; + char* mem; + double uv[2]; + double pix[3]; + double tmp[3]; + + CHECK(ssol_image_create(dev, &img), RES_OK); + CHECK(ssol_image_setup(img, 4, 2, SSOL_PIXEL_DOUBLE3), RES_OK); + CHECK(ssol_image_get_layout(img, &layout), RES_OK); + + pixsz = ssol_sizeof_pixel_format(layout.pixel_format); + + CHECK(ssol_image_map(img, &mem), RES_OK); + d3((double*)(mem + layout.offset + 0*pixsz + 0*layout.row_pitch), 1, 0, 0); + d3((double*)(mem + layout.offset + 1*pixsz + 0*layout.row_pitch), 1, 0, 0); + d3((double*)(mem + layout.offset + 2*pixsz + 0*layout.row_pitch), 1, 1, 0); + d3((double*)(mem + layout.offset + 3*pixsz + 0*layout.row_pitch), 1, 1, 0); + d3((double*)(mem + layout.offset + 0*pixsz + 1*layout.row_pitch), 1, 0, 1); + d3((double*)(mem + layout.offset + 1*pixsz + 1*layout.row_pitch), 1, 0, 1); + d3((double*)(mem + layout.offset + 2*pixsz + 1*layout.row_pitch), 0, 1, 1); + d3((double*)(mem + layout.offset + 3*pixsz + 1*layout.row_pitch), 0, 1, 1); + CHECK(ssol_image_unmap(img), RES_OK); + + #define CLAMPED SSOL_ADDRESS_CLAMP + #define REPEAT SSOL_ADDRESS_REPEAT + #define NEAREST SSOL_FILTER_NEAREST + #define LINEAR SSOL_FILTER_LINEAR + + d2_splat(uv, 0); + CHECK(ssol_image_sample(NULL, NEAREST, CLAMPED, CLAMPED, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, NULL, NULL), RES_BAD_ARG); + CHECK(ssol_image_sample(NULL, NEAREST, CLAMPED, CLAMPED, uv, NULL), RES_BAD_ARG); + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, NULL), RES_BAD_ARG); + CHECK(ssol_image_sample(NULL, NEAREST, CLAMPED, CLAMPED, NULL, pix), RES_BAD_ARG); + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, NULL, pix), RES_BAD_ARG); + CHECK(ssol_image_sample(NULL, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_BAD_ARG); + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,0,0)), 1); + + uv[0] = 1.0/4.0; + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,0,0)), 1); + uv[0] = 2.0/4.0; + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,1,0)), 1); + uv[0] = 3.0/4.0; + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,1,0)), 1); + + uv[0] = -1.0/4.0; + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,0,0)), 1); + CHECK(ssol_image_sample(img, NEAREST, REPEAT, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,1,0)), 1); + uv[0] = 4.0/4.0; + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,1,0)), 1); + CHECK(ssol_image_sample(img, NEAREST, REPEAT, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,0,0)), 1); + + uv[1] = 1.0/2.0; + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,0,1,1)), 1); + uv[1] = 2.0/2.0; + CHECK(ssol_image_sample(img, NEAREST, REPEAT, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,0,1)), 1); + CHECK(ssol_image_sample(img, NEAREST, REPEAT, REPEAT, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,0,0)), 1); + CHECK(ssol_image_sample(img, NEAREST, CLAMPED, REPEAT, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,1,1,0)), 1); + + uv[0] = 1.0/4.0 + 1.0/8.0; + uv[1] = 0.0/2.0 + 1.0/4.0; + CHECK(ssol_image_sample(img, LINEAR, CLAMPED, CLAMPED, uv, pix), RES_OK); + CHECK(d3_eq(pix, d3(tmp,0.75,0.5,0.5)), 1); + + CHECK(ssol_image_ref_put(img), RES_OK); +} + int main(int argc, char** argv) { @@ -26,7 +114,7 @@ main(int argc, char** argv) struct ssol_image_layout layout = SSOL_IMAGE_LAYOUT_NULL; size_t org[2]; size_t sz[2]; - void* mem; + char* mem; size_t i, x, y; (void) argc, (void) argv; @@ -103,7 +191,7 @@ main(int argc, char** argv) FOR_EACH(y, 0, layout.height) { const double* row = (const double*) - (((char*)mem + layout.offset) + y * layout.row_pitch); + ((mem + layout.offset) + y * layout.row_pitch); FOR_EACH(x, 0, layout.width) { const double* pixel = row + x*3; if(y < 8) { @@ -117,7 +205,7 @@ main(int argc, char** argv) CHECK(pixel[2], 2); } } else { - if(x < 8) { + if(x < 8) { CHECK(pixel[0], 3); CHECK(pixel[1], 3); CHECK(pixel[2], 3); @@ -129,8 +217,12 @@ main(int argc, char** argv) } } } + CHECK(ssol_image_unmap(NULL), RES_BAD_ARG); + CHECK(ssol_image_unmap(img), RES_OK); CHECK(ssol_image_ref_put(img), RES_OK); + + check_sampling(dev); CHECK(ssol_device_ref_put(dev), RES_OK); check_memory_allocator(&allocator); diff --git a/src/test_ssol_materials.h b/src/test_ssol_materials.h @@ -25,16 +25,12 @@ get_shader_normal (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { int i; - (void)dev, (void)buf, (void)wavelength, (void)P, (void)Ng, (void)uv, (void)w; - FOR_EACH(i, 0, 3) val[i] = Ns[i]; + (void)dev, (void)buf, (void)wavelength; + FOR_EACH(i, 0, 3) val[i] = frag->Ns[i]; } static INLINE void @@ -42,15 +38,10 @@ get_shader_reflectivity (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void)dev, (void)buf, (void)wavelength; - (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + (void)dev, (void)buf, (void)wavelength, (void)frag; *val = 1; } @@ -60,15 +51,10 @@ get_shader_reflectivity_2 (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void) dev, (void) buf, (void) wavelength; - (void) P, (void) Ng, (void) Ns, (void) uv, (void) w; + (void)dev, (void)buf, (void)wavelength, (void)frag; *val = REFLECTIVITY; } #endif @@ -78,15 +64,10 @@ get_shader_roughness (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void)dev, (void)buf, (void)wavelength; - (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + (void)dev, (void)buf, (void)wavelength, (void)frag; *val = 0; } @@ -95,15 +76,10 @@ get_shader_absorption (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void)dev, (void)buf, (void)wavelength; - (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + (void)dev, (void)buf, (void)wavelength, (void)frag; *val = 0; } @@ -112,15 +88,10 @@ get_shader_thickness (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void)dev, (void)buf, (void)wavelength; - (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + (void)dev, (void)buf, (void)wavelength, (void)frag; *val = 1; } @@ -129,15 +100,10 @@ get_shader_refractive_index (struct ssol_device* dev, struct ssol_param_buffer* buf, const double wavelength, - const double P[3], - const double Ng[3], - const double Ns[3], - const double uv[2], - const double w[3], + const struct ssol_surface_fragment* frag, double* val) { - (void)dev, (void)buf, (void)wavelength; - (void)P, (void)Ng, (void)Ns, (void)uv, (void) w; + (void)dev, (void)buf, (void)wavelength, (void)frag; *val = 1.5; } diff --git a/src/test_ssol_param_buffer.c b/src/test_ssol_param_buffer.c @@ -19,18 +19,30 @@ #include <rsys/logger.h> #include <limits.h> +struct param { + char* name; + double d; + int i; + void* ptr; + struct ssol_image* img; +}; + +static void +param_release(void* mem) +{ + struct param* param = mem; + ASSERT(param); + if(param->img) SSOL(image_ref_put(param->img)); +} + int main(int argc, char** argv) { - struct param { - char* name; - double d; - int i; - void* ptr; - }* param; + struct param* param; struct mem_allocator allocator; struct ssol_device* dev; struct ssol_param_buffer* pbuf; + struct ssol_image* img; size_t sz, al; void* mem; (void)argc, (void)argv; @@ -53,14 +65,14 @@ main(int argc, char** argv) sz = sizeof(intptr_t); al = ALIGNOF(intptr_t); - CHECK(mem = ssol_param_buffer_allocate(NULL, 0, 0), NULL); - CHECK(mem = ssol_param_buffer_allocate(pbuf, 0, 0), NULL); - CHECK(mem = ssol_param_buffer_allocate(NULL, sz, 0), NULL); - CHECK(mem = ssol_param_buffer_allocate(pbuf, sz, 0), NULL); - CHECK(mem = ssol_param_buffer_allocate(NULL, 0, al), NULL); - CHECK(mem = ssol_param_buffer_allocate(pbuf, 0, al), NULL); - CHECK(mem = ssol_param_buffer_allocate(NULL, sz, al), NULL); - NCHECK(mem = ssol_param_buffer_allocate(pbuf, sz, al), NULL); + CHECK(mem = ssol_param_buffer_allocate(NULL, 0, 0, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(pbuf, 0, 0, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(NULL, sz, 0, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(pbuf, sz, 0, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(NULL, 0, al, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(pbuf, 0, al, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(NULL, sz, al, NULL), NULL); + NCHECK(mem = ssol_param_buffer_allocate(pbuf, sz, al, NULL), NULL); *(intptr_t*)mem = 0xDECAFBAD; CHECK(*(intptr_t*)ssol_param_buffer_get(pbuf), 0xDECAFBAD); @@ -74,7 +86,7 @@ main(int argc, char** argv) sz = strlen("Foo") + 1; al = 4; - NCHECK(mem = ssol_param_buffer_allocate(pbuf, sz, al), NULL); + NCHECK(mem = ssol_param_buffer_allocate(pbuf, sz, al, NULL), NULL); strcpy(mem, "Foo"); CHECK(strcmp(ssol_param_buffer_get(pbuf), "Foo"), 0); strcpy(mem, "Bar"); @@ -85,12 +97,13 @@ main(int argc, char** argv) sz = sizeof(struct param); al = ALIGNOF(struct param); - NCHECK(param = ssol_param_buffer_allocate(pbuf, sz, al), NULL); - NCHECK(param->name = ssol_param_buffer_allocate(pbuf, 7, 64), NULL); + NCHECK(param = ssol_param_buffer_allocate(pbuf, sz, al, NULL), NULL); + NCHECK(param->name = ssol_param_buffer_allocate(pbuf, 7, 64, NULL), NULL); strcpy(param->name, "0123456"); - NCHECK(param->ptr = ssol_param_buffer_allocate(pbuf, 4, 16), NULL); + NCHECK(param->ptr = ssol_param_buffer_allocate(pbuf, 4, 16, NULL), NULL); param->d = PI; param->i = -123; + param->img = NULL; strcpy(param->ptr, "abc"); NCHECK(param = ssol_param_buffer_get(pbuf), NULL); @@ -102,6 +115,46 @@ main(int argc, char** argv) CHECK(strcmp(param->name, "0123456"), 0); CHECK(strcmp(param->ptr, "abc"), 0); + CHECK(ssol_param_buffer_clear(pbuf), RES_OK); + + sz = sizeof(struct param); + al = ALIGNOF(struct param); + CHECK(ssol_image_create(dev, &img), RES_OK); + CHECK(ssol_image_setup(img, 1280, 720, SSOL_PIXEL_DOUBLE3), RES_OK); + NCHECK(param = ssol_param_buffer_allocate(pbuf, sz, al, &param_release), NULL); + param->d = PI; + param->i = -123; + param->name = NULL; + param->ptr = NULL; + param->img = img; + CHECK(ssol_image_ref_get(img), RES_OK); + + NCHECK(param = ssol_param_buffer_allocate(pbuf, sz, al, &param_release), NULL); + param->d = 123.456; + param->i = -1; + param->name = NULL; + param->ptr = NULL; + param->img = img; + CHECK(ssol_image_ref_get(img), RES_OK); + + NCHECK(param = ssol_param_buffer_allocate(pbuf, sz, al, &param_release), NULL); + param->d = 0.1; + param->i = 789; + param->name = NULL; + param->ptr = NULL; + param->img = img; + CHECK(ssol_image_ref_get(img), RES_OK); + + CHECK(ssol_param_buffer_clear(pbuf), RES_OK); + + NCHECK(param = ssol_param_buffer_allocate(pbuf, sz, al, &param_release), NULL); + param->d = 0.1; + param->i = 789; + param->name = NULL; + param->ptr = NULL; + param->img = img; + CHECK(ssol_image_ref_get(img), RES_OK); + CHECK(ssol_param_buffer_ref_get(NULL), RES_BAD_ARG); CHECK(ssol_param_buffer_ref_get(pbuf), RES_OK); CHECK(ssol_param_buffer_ref_put(NULL), RES_BAD_ARG); @@ -109,9 +162,10 @@ main(int argc, char** argv) CHECK(ssol_param_buffer_ref_put(pbuf), RES_OK); CHECK(ssol_param_buffer_create(dev, 8, &pbuf), RES_OK); - NCHECK(mem = ssol_param_buffer_allocate(pbuf, 2, 1), NULL); - CHECK(mem = ssol_param_buffer_allocate(pbuf, 1, 16), NULL); + NCHECK(mem = ssol_param_buffer_allocate(pbuf, 2, 1, NULL), NULL); + CHECK(mem = ssol_param_buffer_allocate(pbuf, 1, 16, NULL), NULL); + CHECK(ssol_image_ref_put(img), RES_OK); CHECK(ssol_param_buffer_ref_put(pbuf), RES_OK); CHECK(ssol_device_ref_put(dev), RES_OK);