PIKApp/plug-ins/lighting/lighting-shade.c

857 lines
23 KiB
C

/*****************/
/* Shading stuff */
/*****************/
#include "config.h"
#include <libpika/pika.h>
#include "lighting-main.h"
#include "lighting-image.h"
#include "lighting-shade.h"
static PikaVector3 *triangle_normals[2] = { NULL, NULL };
static PikaVector3 *vertex_normals[3] = { NULL, NULL, NULL };
static gdouble *heights[3] = { NULL, NULL, NULL };
static gdouble xstep, ystep;
static guchar *bumprow = NULL;
static gint pre_w = -1;
static gint pre_h = -1;
/*****************/
/* Phong shading */
/*****************/
static PikaRGB
phong_shade (PikaVector3 *position,
PikaVector3 *viewpoint,
PikaVector3 *normal,
PikaVector3 *lightposition,
PikaRGB *diff_col,
PikaRGB *light_col,
LightType light_type)
{
PikaRGB diffuse_color, specular_color;
gdouble nl, rv, dist;
PikaVector3 l, v, n, lnormal, h;
/* Compute ambient intensity */
/* ========================= */
n = *normal;
/* Compute (N*L) term of Phong's equation */
/* ====================================== */
if (light_type == POINT_LIGHT)
pika_vector3_sub (&l, lightposition, position);
else
{
l = *lightposition;
pika_vector3_normalize (&l);
}
dist = pika_vector3_length (&l);
if (dist != 0.0)
pika_vector3_mul (&l, 1.0 / dist);
nl = MAX (0., 2.0 * pika_vector3_inner_product (&n, &l));
lnormal = l;
pika_vector3_normalize (&lnormal);
if (nl >= 0.0)
{
/* Compute (R*V)^alpha term of Phong's equation */
/* ============================================ */
pika_vector3_sub (&v, viewpoint, position);
pika_vector3_normalize (&v);
pika_vector3_add (&h, &lnormal, &v);
pika_vector3_normalize (&h);
rv = MAX (0.01, pika_vector3_inner_product (&n, &h));
rv = pow (rv, mapvals.material.highlight);
rv *= nl;
/* Compute diffuse and specular intensity contribution */
/* =================================================== */
diffuse_color = *light_col;
pika_rgb_multiply (&diffuse_color, mapvals.material.diffuse_int);
diffuse_color.r *= diff_col->r;
diffuse_color.g *= diff_col->g;
diffuse_color.b *= diff_col->b;
pika_rgb_multiply (&diffuse_color, nl);
specular_color = *light_col;
if (mapvals.material.metallic) /* for metals, specular color = diffuse color */
{
specular_color.r *= diff_col->r;
specular_color.g *= diff_col->g;
specular_color.b *= diff_col->b;
}
pika_rgb_multiply (&specular_color, mapvals.material.specular_ref);
pika_rgb_multiply (&specular_color, rv);
pika_rgb_add (&diffuse_color, &specular_color);
pika_rgb_clamp (&diffuse_color);
}
pika_rgb_clamp (&diffuse_color);
return diffuse_color;
}
void
precompute_init (gint w,
gint h)
{
gint n;
gint bpp=1;
xstep = 1.0 / (gdouble) width;
ystep = 1.0 / (gdouble) height;
pre_w = w;
pre_h = h;
for (n = 0; n < 3; n++)
{
if (vertex_normals[n] != NULL)
g_free (vertex_normals[n]);
if (heights[n] != NULL)
g_free (heights[n]);
heights[n] = g_new (gdouble, w);
vertex_normals[n] = g_new (PikaVector3, w);
}
for (n = 0; n < 2; n++)
if (triangle_normals[n] != NULL)
g_free (triangle_normals[n]);
g_clear_pointer (&bumprow, g_free);
if (mapvals.bumpmap_id != -1)
{
PikaDrawable *drawable = pika_drawable_get_by_id (mapvals.bumpmap_id);
bpp = pika_drawable_get_bpp (drawable);
}
bumprow = g_new (guchar, w * bpp);
triangle_normals[0] = g_new (PikaVector3, (w << 1) + 2);
triangle_normals[1] = g_new (PikaVector3, (w << 1) + 2);
for (n = 0; n < (w << 1) + 1; n++)
{
pika_vector3_set (&triangle_normals[0][n], 0.0, 0.0, 1.0);
pika_vector3_set (&triangle_normals[1][n], 0.0, 0.0, 1.0);
}
for (n = 0; n < w; n++)
{
pika_vector3_set (&vertex_normals[0][n], 0.0, 0.0, 1.0);
pika_vector3_set (&vertex_normals[1][n], 0.0, 0.0, 1.0);
pika_vector3_set (&vertex_normals[2][n], 0.0, 0.0, 1.0);
heights[0][n] = 0.0;
heights[1][n] = 0.0;
heights[2][n] = 0.0;
}
}
/* Interpol linearly height[2] and triangle_normals[1]
* using the next row
*/
void
interpol_row (gint x1,
gint x2,
gint y)
{
PikaVector3 p1, p2, p3;
gint n, i;
guchar *map = NULL;
gint bpp = 1;
guchar *bumprow1 = NULL;
guchar *bumprow2 = NULL;
if (mapvals.bumpmap_id != -1)
{
bumpmap_setup (pika_drawable_get_by_id (mapvals.bumpmap_id));
bpp = babl_format_get_bytes_per_pixel (bump_format);
}
bumprow1 = g_new0 (guchar, pre_w * bpp);
bumprow2 = g_new0 (guchar, pre_w * bpp);
gegl_buffer_get (bump_buffer, GEGL_RECTANGLE (x1, y, x2 - x1, 1), 1.0,
bump_format, bumprow1,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
gegl_buffer_get (bump_buffer, GEGL_RECTANGLE (x1, y - 1, x2 - x1, 1), 1.0,
bump_format, bumprow2,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
if (mapvals.bumpmaptype > 0)
{
switch (mapvals.bumpmaptype)
{
case 1:
map = logmap;
break;
case 2:
map = sinemap;
break;
default:
map = spheremap;
break;
}
}
for (n = 0; n < (x2 - x1); n++)
{
gdouble diff;
guchar mapval;
guchar mapval1, mapval2;
if (bpp > 1)
{
mapval1 = (guchar)((float)((bumprow1[n * bpp] +bumprow1[n * bpp +1] + bumprow1[n * bpp + 2])/3.0 )) ;
mapval2 = (guchar)((float)((bumprow2[n * bpp] +bumprow2[n * bpp +1] + bumprow2[n * bpp + 2])/3.0 )) ;
}
else
{
mapval1 = bumprow1[n * bpp];
mapval2 = bumprow2[n * bpp];
}
diff = mapval1 - mapval2;
mapval = (guchar) CLAMP (mapval1 + diff, 0.0, 255.0);
if (mapvals.bumpmaptype > 0)
{
heights[1][n] = (gdouble) mapvals.bumpmax * (gdouble) map[mapval1] / 255.0;
heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) map[mapval] / 255.0;
}
else
{
heights[1][n] = (gdouble) mapvals.bumpmax * (gdouble) mapval1 / 255.0;
heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) mapval / 255.0;
}
}
i = 0;
for (n = 0; n < (x2 - x1 - 1); n++)
{
/* heights rows 1 and 2 are inverted */
p1.x = 0.0;
p1.y = ystep;
p1.z = heights[1][n] - heights[2][n];
p2.x = xstep;
p2.y = ystep;
p2.z = heights[1][n+1] - heights[2][n];
p3.x = xstep;
p3.y = 0.0;
p3.z = heights[2][n+1] - heights[2][n];
triangle_normals[1][i] = pika_vector3_cross_product (&p2, &p1);
triangle_normals[1][i+1] = pika_vector3_cross_product (&p3, &p2);
pika_vector3_normalize (&triangle_normals[1][i]);
pika_vector3_normalize (&triangle_normals[1][i+1]);
i += 2;
}
g_free (bumprow1);
g_free (bumprow2);
}
/********************************************/
/* Compute triangle and then vertex normals */
/********************************************/
void
precompute_normals (gint x1,
gint x2,
gint y)
{
PikaVector3 *tmpv, p1, p2, p3, normal;
gdouble *tmpd;
gint n, i, nv;
guchar *map = NULL;
gint bpp = 1;
guchar mapval;
/* First, compute the heights */
/* ========================== */
tmpv = triangle_normals[0];
triangle_normals[0] = triangle_normals[1];
triangle_normals[1] = tmpv;
tmpv = vertex_normals[0];
vertex_normals[0] = vertex_normals[1];
vertex_normals[1] = vertex_normals[2];
vertex_normals[2] = tmpv;
tmpd = heights[0];
heights[0] = heights[1];
heights[1] = heights[2];
heights[2] = tmpd;
if (mapvals.bumpmap_id != -1)
{
bumpmap_setup (pika_drawable_get_by_id (mapvals.bumpmap_id));
bpp = babl_format_get_bytes_per_pixel (bump_format);
}
gegl_buffer_get (bump_buffer, GEGL_RECTANGLE (x1, y, x2 - x1, 1), 1.0,
bump_format, bumprow,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
if (mapvals.bumpmaptype > 0)
{
switch (mapvals.bumpmaptype)
{
case 1:
map = logmap;
break;
case 2:
map = sinemap;
break;
default:
map = spheremap;
break;
}
for (n = 0; n < (x2 - x1); n++)
{
if (bpp > 1)
{
mapval = (guchar)((float)((bumprow[n * bpp + 0] +
bumprow[n * bpp + 1] +
bumprow[n * bpp + 2]) /3.0));
}
else
{
mapval = bumprow[n * bpp];
}
heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) map[mapval] / 255.0;
}
}
else
{
for (n = 0; n < (x2 - x1); n++)
{
if (bpp > 1)
{
mapval = (guchar)((float)((bumprow[n * bpp + 0] +
bumprow[n * bpp + 1] +
bumprow[n * bpp + 2]) / 3.0));
}
else
{
mapval = bumprow[n * bpp];
}
heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) mapval / 255.0;
}
}
/* Compute triangle normals */
/* ======================== */
i = 0;
for (n = 0; n < (x2 - x1 - 1); n++)
{
p1.x = 0.0;
p1.y = ystep;
p1.z = heights[2][n] - heights[1][n];
p2.x = xstep;
p2.y = ystep;
p2.z = heights[2][n+1] - heights[1][n];
p3.x = xstep;
p3.y = 0.0;
p3.z = heights[1][n+1] - heights[1][n];
triangle_normals[1][i] = pika_vector3_cross_product (&p2, &p1);
triangle_normals[1][i+1] = pika_vector3_cross_product (&p3, &p2);
pika_vector3_normalize (&triangle_normals[1][i]);
pika_vector3_normalize (&triangle_normals[1][i+1]);
i += 2;
}
/* Compute vertex normals */
/* ====================== */
i = 0;
pika_vector3_set (&normal, 0.0, 0.0, 0.0);
for (n = 0; n < (x2 - x1 - 1); n++)
{
nv = 0;
if (n > 0)
{
if (y > 0)
{
pika_vector3_add (&normal, &normal, &triangle_normals[0][i-1]);
pika_vector3_add (&normal, &normal, &triangle_normals[0][i-2]);
nv += 2;
}
if (y < pre_h)
{
pika_vector3_add (&normal, &normal, &triangle_normals[1][i-1]);
nv++;
}
}
if (n < pre_w)
{
if (y > 0)
{
pika_vector3_add (&normal, &normal, &triangle_normals[0][i]);
pika_vector3_add (&normal, &normal, &triangle_normals[0][i+1]);
nv += 2;
}
if (y < pre_h)
{
pika_vector3_add (&normal, &normal, &triangle_normals[1][i]);
pika_vector3_add (&normal, &normal, &triangle_normals[1][i+1]);
nv += 2;
}
}
pika_vector3_mul (&normal, 1.0 / (gdouble) nv);
pika_vector3_normalize (&normal);
vertex_normals[1][n] = normal;
i += 2;
}
}
/***********************************************************************/
/* Compute the reflected ray given the normalized normal and ins. vec. */
/***********************************************************************/
static PikaVector3
compute_reflected_ray (PikaVector3 *normal,
PikaVector3 *view)
{
PikaVector3 ref;
gdouble nl;
nl = 2.0 * pika_vector3_inner_product (normal, view);
ref = *normal;
pika_vector3_mul (&ref, nl);
pika_vector3_sub (&ref, &ref, view);
return ref;
}
/************************************************************************/
/* Given the NorthPole, Equator and a third vector (normal) compute */
/* the conversion from spherical coordinates to image space coordinates */
/************************************************************************/
static void
sphere_to_image (PikaVector3 *normal,
gdouble *u,
gdouble *v)
{
static gdouble alpha, fac;
static PikaVector3 cross_prod;
static PikaVector3 firstaxis = { 1.0, 0.0, 0.0 };
static PikaVector3 secondaxis = { 0.0, 1.0, 0.0 };
alpha = acos (-pika_vector3_inner_product (&secondaxis, normal));
*v = alpha / G_PI;
if (*v == 0.0 || *v == 1.0)
{
*u = 0.0;
}
else
{
fac = pika_vector3_inner_product (&firstaxis, normal) / sin (alpha);
/* Make sure that we map to -1.0..1.0 (take care of rounding errors) */
/* ================================================================= */
if (fac > 1.0)
fac = 1.0;
else if (fac < -1.0)
fac = -1.0;
*u = acos (fac) / (2.0 * G_PI);
cross_prod = pika_vector3_cross_product (&secondaxis, &firstaxis);
if (pika_vector3_inner_product (&cross_prod, normal) < 0.0)
*u = 1.0 - *u;
}
}
/*********************************************************************/
/* These routines computes the color of the surface at a given point */
/*********************************************************************/
PikaRGB
get_ray_color (PikaVector3 *position)
{
PikaRGB color;
PikaRGB color_int;
PikaRGB color_sum;
PikaRGB light_color;
gint x, f;
gdouble xf, yf;
PikaVector3 normal, *p;
gint k;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.transparent_background && heights[1][x] == 0)
{
pika_rgb_set_alpha (&color_sum, 0.0);
}
else
{
color = get_image_color (xf, yf, &f);
color_sum = color;
pika_rgb_multiply (&color_sum, mapvals.material.ambient_int);
for (k = 0; k < NUM_LIGHTS; k++)
{
if (! mapvals.lightsource[k].active ||
mapvals.lightsource[k].type == NO_LIGHT)
continue;
else if (mapvals.lightsource[k].type == POINT_LIGHT)
p = &mapvals.lightsource[k].position;
else
p = &mapvals.lightsource[k].direction;
color_int = mapvals.lightsource[k].color;
pika_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
if (mapvals.bump_mapped == FALSE ||
mapvals.bumpmap_id == -1)
{
light_color = phong_shade (position,
&mapvals.viewpoint,
&mapvals.planenormal,
p,
&color,
&color_int,
mapvals.lightsource[k].type);
}
else
{
normal = vertex_normals[1][(gint) RINT (xf)];
light_color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&color,
&color_int,
mapvals.lightsource[k].type);
}
pika_rgb_add (&color_sum, &light_color);
}
}
pika_rgb_clamp (&color_sum);
return color_sum;
}
PikaRGB
get_ray_color_ref (PikaVector3 *position)
{
PikaRGB color_sum;
PikaRGB color_int;
PikaRGB light_color;
PikaRGB color, env_color;
gint x, f;
gdouble xf, yf;
PikaVector3 normal, *p, v, r;
gint k;
gdouble tmpval;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.bump_mapped == FALSE ||
mapvals.bumpmap_id == -1)
{
normal = mapvals.planenormal;
}
else
{
normal = vertex_normals[1][(gint) RINT (xf)];
}
pika_vector3_normalize (&normal);
if (mapvals.transparent_background && heights[1][x] == 0)
{
pika_rgb_set_alpha (&color_sum, 0.0);
}
else
{
color = get_image_color (xf, yf, &f);
color_sum = color;
pika_rgb_multiply (&color_sum, mapvals.material.ambient_int);
for (k = 0; k < NUM_LIGHTS; k++)
{
p = &mapvals.lightsource[k].direction;
if (! mapvals.lightsource[k].active ||
mapvals.lightsource[k].type == NO_LIGHT)
continue;
else if (mapvals.lightsource[k].type == POINT_LIGHT)
p = &mapvals.lightsource[k].position;
color_int = mapvals.lightsource[k].color;
pika_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
light_color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&color,
&color_int,
mapvals.lightsource[0].type);
}
pika_vector3_sub (&v, &mapvals.viewpoint, position);
pika_vector3_normalize (&v);
r = compute_reflected_ray (&normal, &v);
/* Get color in the direction of r */
/* =============================== */
sphere_to_image (&r, &xf, &yf);
env_color = peek_env_map (RINT (env_width * xf),
RINT (env_height * yf));
tmpval = mapvals.material.diffuse_int;
mapvals.material.diffuse_int = 0.;
light_color = phong_shade (position,
&mapvals.viewpoint,
&normal,
&r,
&color,
&env_color,
DIRECTIONAL_LIGHT);
mapvals.material.diffuse_int = tmpval;
pika_rgb_add (&color_sum, &light_color);
}
pika_rgb_clamp (&color_sum);
return color_sum;
}
PikaRGB
get_ray_color_no_bilinear (PikaVector3 *position)
{
PikaRGB color;
PikaRGB color_int;
PikaRGB color_sum;
PikaRGB light_color;
gint x;
gdouble xf, yf;
PikaVector3 normal, *p;
gint k;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.transparent_background && heights[1][x] == 0)
{
pika_rgb_set_alpha (&color_sum, 0.0);
}
else
{
color = peek (x, RINT (yf));
color_sum = color;
pika_rgb_multiply (&color_sum, mapvals.material.ambient_int);
for (k = 0; k < NUM_LIGHTS; k++)
{
p = &mapvals.lightsource[k].direction;
if (! mapvals.lightsource[k].active ||
mapvals.lightsource[k].type == NO_LIGHT)
continue;
else if (mapvals.lightsource[k].type == POINT_LIGHT)
p = &mapvals.lightsource[k].position;
color_int = mapvals.lightsource[k].color;
pika_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
if (mapvals.bump_mapped == FALSE ||
mapvals.bumpmap_id == -1)
{
light_color = phong_shade (position,
&mapvals.viewpoint,
&mapvals.planenormal,
p,
&color,
&color_int,
mapvals.lightsource[k].type);
}
else
{
normal = vertex_normals[1][x];
light_color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&color,
&color_int,
mapvals.lightsource[k].type);
}
pika_rgb_add (&color_sum, &light_color);
}
}
pika_rgb_clamp (&color_sum);
return color_sum;
}
PikaRGB
get_ray_color_no_bilinear_ref (PikaVector3 *position)
{
PikaRGB color_sum;
PikaRGB color_int;
PikaRGB light_color;
PikaRGB color, env_color;
gint x;
gdouble xf, yf;
PikaVector3 normal, *p, v, r;
gint k;
gdouble tmpval;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.bump_mapped == FALSE ||
mapvals.bumpmap_id == -1)
{
normal = mapvals.planenormal;
}
else
{
normal = vertex_normals[1][(gint) RINT (xf)];
}
pika_vector3_normalize (&normal);
if (mapvals.transparent_background && heights[1][x] == 0)
{
pika_rgb_set_alpha (&color_sum, 0.0);
}
else
{
color = peek (RINT (xf), RINT (yf));
color_sum = color;
pika_rgb_multiply (&color_sum, mapvals.material.ambient_int);
for (k = 0; k < NUM_LIGHTS; k++)
{
p = &mapvals.lightsource[k].direction;
if (!mapvals.lightsource[k].active
|| mapvals.lightsource[k].type == NO_LIGHT)
continue;
else if (mapvals.lightsource[k].type == POINT_LIGHT)
p = &mapvals.lightsource[k].position;
color_int = mapvals.lightsource[k].color;
pika_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
light_color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&color,
&color_int,
mapvals.lightsource[0].type);
}
pika_vector3_sub (&v, &mapvals.viewpoint, position);
pika_vector3_normalize (&v);
r = compute_reflected_ray (&normal, &v);
/* Get color in the direction of r */
/* =============================== */
sphere_to_image (&r, &xf, &yf);
env_color = peek_env_map (RINT (env_width * xf),
RINT (env_height * yf));
tmpval = mapvals.material.diffuse_int;
mapvals.material.diffuse_int = 0.;
light_color = phong_shade (position,
&mapvals.viewpoint,
&normal,
&r,
&color,
&env_color,
DIRECTIONAL_LIGHT);
mapvals.material.diffuse_int = tmpval;
pika_rgb_add (&color_sum, &light_color);
}
pika_rgb_clamp (&color_sum);
return color_sum;
}