gimp/plug-ins/Lighting/lighting_shade.c

735 lines
18 KiB
C

/*****************/
/* Shading stuff */
/*****************/
#include "config.h"
#include <libgimp/gimp.h>
#include "lighting_main.h"
#include "lighting_image.h"
#include "lighting_shade.h"
static GimpVector3 *triangle_normals[2] = { NULL, NULL };
static GimpVector3 *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 GimpRGB
phong_shade (GimpVector3 *position,
GimpVector3 *viewpoint,
GimpVector3 *normal,
GimpVector3 *lightposition,
GimpRGB *diff_col,
GimpRGB *spec_col,
LightType light_type)
{
GimpRGB ambient_color, diffuse_color, specular_color;
gdouble nl, rv, dist;
GimpVector3 l, v, n, lnormal, h;
/* Compute ambient intensity */
/* ========================= */
n = *normal;
ambient_color = *diff_col;
gimp_rgb_multiply (&ambient_color, mapvals.material.ambient_int);
/* Compute (N*L) term of Phong's equation */
/* ====================================== */
if (light_type == POINT_LIGHT)
gimp_vector3_sub (&l, lightposition, position);
else
l = *lightposition;
dist = gimp_vector3_length (&l);
if (dist != 0.0)
gimp_vector3_mul (&l, 1.0 / dist);
nl = 2.0 * gimp_vector3_inner_product (&n, &l);
lnormal.x = l.x;
lnormal.y = l.y;
lnormal.z = l.z;
gimp_vector3_normalize (&lnormal);
if (nl >= 0.0)
{
/* Compute (R*V)^alpha term of Phong's equation */
/* ============================================ */
gimp_vector3_sub (&v, viewpoint, position);
gimp_vector3_normalize (&v);
gimp_vector3_add (&h, &lnormal, &v);
gimp_vector3_normalize (&h);
rv = MAX (0., gimp_vector3_inner_product (&n, &h));
rv = pow (rv, mapvals.material.highlight);
rv *= nl;
/* Compute diffuse and specular intensity contribution */
/* =================================================== */
diffuse_color = *diff_col;
gimp_rgb_multiply (&diffuse_color, mapvals.material.diffuse_ref);
gimp_rgb_multiply (&diffuse_color, nl);
specular_color = *spec_col;
gimp_rgb_multiply (&specular_color, mapvals.material.specular_ref);
gimp_rgb_multiply (&specular_color, rv);
gimp_rgb_add (&diffuse_color, &specular_color);
gimp_rgb_multiply (&diffuse_color, mapvals.material.diffuse_int);
gimp_rgb_clamp (&diffuse_color);
gimp_rgb_add (&ambient_color, &diffuse_color);
}
gimp_rgb_clamp (&ambient_color);
return ambient_color;
}
/*
static void
get_normal (gdouble xf,
gdouble yf,
GimpVector3 *normal)
{
GimpVector3 v1,v2,n;
gint numvecs=0,x,y,f;
gdouble val,val1=-1.0,val2=-1.0,val3=-1.0,val4=-1.0, xstep,ystep;
x=(gint)(xf+0.5);
y=(gint)(yf+0.5);
xstep=1.0/(gdouble)width;
ystep=1.0/(gdouble)height;
val=mapvals.bumpmax*get_map_value(&bump_region, xf,yf, &f)/255.0;
if (check_bounds(x-1,y)) val1=mapvals.bumpmax*get_map_value(&bump_region, xf-1.0,yf, &f)/255.0 - val;
if (check_bounds(x,y-1)) val2=mapvals.bumpmax*get_map_value(&bump_region, xf,yf-1.0, &f)/255.0 - val;
if (check_bounds(x+1,y)) val3=mapvals.bumpmax*get_map_value(&bump_region, xf+1.0,yf, &f)/255.0 - val;
if (check_bounds(x,y+1)) val4=mapvals.bumpmax*get_map_value(&bump_region, xf,yf+1.0, &f)/255.0 - val;
gimp_vector3_set(normal, 0.0,0.0,0.0);
if (val1!=-1.0 && val4!=-1.0)
{
v1.x=-xstep; v1.y=0.0; v1.z=val1;
v2.x=0.0; v2.y=ystep; v2.z=val4;
n=gimp_vector3_cross_product(&v1,&v2);
gimp_vector3_normalize(&n);
if (n.z<0.0)
n.z=-n.z;
gimp_vector3_add(normal,normal,&n);
numvecs++;
}
if (val1!=-1.0 && val2!=-1.0)
{
v1.x=-xstep; v1.y=0.0; v1.z=val1;
v2.x=0.0; v2.y=-ystep; v2.z=val2;
n=gimp_vector3_cross_product(&v1,&v2);
gimp_vector3_normalize(&n);
if (n.z<0.0)
n.z=-n.z;
gimp_vector3_add(normal,normal,&n);
numvecs++;
}
if (val2!=-1.0 && val3!=-1.0)
{
v1.x=0.0; v1.y=-ystep; v1.z=val2;
v2.x=xstep; v2.y=0.0; v2.z=val3;
n=gimp_vector3_cross_product(&v1,&v2);
gimp_vector3_normalize(&n);
if (n.z<0.0)
n.z=-n.z;
gimp_vector3_add(normal,normal,&n);
numvecs++;
}
if (val3!=-1.0 && val4!=-1.0)
{
v1.x=xstep; v1.y=0.0; v1.z=val3;
v2.x=0.0; v2.y=ystep; v2.z=val4;
n=gimp_vector3_cross_product(&v1,&v2);
gimp_vector3_normalize(&n);
if (n.z<0.0)
n.z=-n.z;
gimp_vector3_add(normal,normal,&n);
numvecs++;
}
gimp_vector3_mul(normal,1.0/(gdouble)numvecs);
gimp_vector3_normalize(normal);
}
*/
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 (GimpVector3, w);
}
for (n = 0; n < 2; n++)
if (triangle_normals[n] != NULL)
g_free (triangle_normals[n]);
if (bumprow != NULL)
{
g_free (bumprow);
bumprow = NULL;
}
if (mapvals.bumpmap_id != -1)
{
bpp = gimp_drawable_bpp(mapvals.bumpmap_id);
}
bumprow = g_new (guchar, w * bpp);
triangle_normals[0] = g_new (GimpVector3, (w << 1) + 2);
triangle_normals[1] = g_new (GimpVector3, (w << 1) + 2);
for (n = 0; n < (w << 1) + 1; n++)
{
gimp_vector3_set (&triangle_normals[0][n], 0.0, 0.0, 1.0);
gimp_vector3_set (&triangle_normals[1][n], 0.0, 0.0, 1.0);
}
for (n = 0; n < w; n++)
{
gimp_vector3_set (&vertex_normals[0][n], 0.0, 0.0, 1.0);
gimp_vector3_set (&vertex_normals[1][n], 0.0, 0.0, 1.0);
gimp_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;
}
}
/********************************************/
/* Compute triangle and then vertex normals */
/********************************************/
void
precompute_normals (gint x1,
gint x2,
gint y)
{
GimpVector3 *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)
{
bpp = gimp_drawable_bpp(mapvals.bumpmap_id);
}
gimp_pixel_rgn_get_row (&bump_region, bumprow, x1, y, x2 - x1);
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] +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] +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] = gimp_vector3_cross_product (&p2, &p1);
triangle_normals[1][i+1] = gimp_vector3_cross_product (&p3, &p2);
gimp_vector3_normalize (&triangle_normals[1][i]);
gimp_vector3_normalize (&triangle_normals[1][i+1]);
i += 2;
}
/* Compute vertex normals */
/* ====================== */
i = 0;
gimp_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)
{
gimp_vector3_add (&normal, &normal, &triangle_normals[0][i-1]);
gimp_vector3_add (&normal, &normal, &triangle_normals[0][i-2]);
nv += 2;
}
if (y < pre_h)
{
gimp_vector3_add (&normal, &normal, &triangle_normals[1][i-1]);
nv++;
}
}
if (n <pre_w)
{
if (y > 0)
{
gimp_vector3_add (&normal, &normal, &triangle_normals[0][i]);
gimp_vector3_add (&normal, &normal, &triangle_normals[0][i+1]);
nv += 2;
}
if (y < pre_h)
{
gimp_vector3_add (&normal, &normal, &triangle_normals[1][i]);
gimp_vector3_add (&normal, &normal, &triangle_normals[1][i+1]);
nv += 2;
}
}
gimp_vector3_mul (&normal, 1.0 / (gdouble) nv);
gimp_vector3_normalize (&normal);
vertex_normals[1][n] = normal;
i += 2;
}
}
/***********************************************************************/
/* Compute the reflected ray given the normalized normal and ins. vec. */
/***********************************************************************/
static GimpVector3
compute_reflected_ray (GimpVector3 *normal,
GimpVector3 *view)
{
GimpVector3 ref;
gdouble nl;
nl = 2.0 * gimp_vector3_inner_product (normal, view);
ref = *normal;
gimp_vector3_mul (&ref, nl);
gimp_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 (GimpVector3 *normal,
gdouble *u,
gdouble *v)
{
static gdouble alpha, fac;
static GimpVector3 cross_prod;
static GimpVector3 firstaxis = { 1.0, 0.0, 0.0 };
static GimpVector3 secondaxis = { 0.0, 1.0, 0.0 };
alpha = acos (-gimp_vector3_inner_product (&secondaxis, normal));
*v = alpha / G_PI;
if (*v==0.0 || *v==1.0)
{
*u = 0.0;
}
else
{
fac = gimp_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 = gimp_vector3_cross_product (&secondaxis, &firstaxis);
if (gimp_vector3_inner_product (&cross_prod, normal) < 0.0)
*u = 1.0 - *u;
}
}
/*********************************************************************/
/* These routines computes the color of the surface at a given point */
/*********************************************************************/
GimpRGB
get_ray_color (GimpVector3 *position)
{
GimpRGB color;
gint x, f;
gdouble xf, yf;
GimpVector3 normal, *p;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.transparent_background && heights[1][x] == 0)
{
gimp_rgb_set_alpha (&color, 0.0);
}
else
{
color = get_image_color (xf, yf, &f);
if (mapvals.lightsource.type == POINT_LIGHT)
p = &mapvals.lightsource.position;
else
p = &mapvals.lightsource.direction;
if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
{
color = phong_shade (position,
&mapvals.viewpoint,
&mapvals.planenormal,
p,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
else
{
normal = vertex_normals[1][(gint) RINT (xf)];
color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
}
return color;
}
GimpRGB
get_ray_color_ref (GimpVector3 *position)
{
GimpRGB color, env_color;
gint x, f;
gdouble xf, yf;
GimpVector3 normal, *p, v, r;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.transparent_background && heights[1][x] == 0)
{
gimp_rgb_set_alpha (&color, 0.0);
}
else
{
color = get_image_color (xf, yf, &f);
if (mapvals.lightsource.type == POINT_LIGHT)
p = &mapvals.lightsource.position;
else
p = &mapvals.lightsource.direction;
if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
{
color = phong_shade (position,
&mapvals.viewpoint,
&mapvals.planenormal,
p,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
else
{
normal = vertex_normals[1][(gint) RINT (xf)];
gimp_vector3_sub (&v, &mapvals.viewpoint,position);
gimp_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));
color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&env_color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
}
return color;
}
GimpRGB
get_ray_color_no_bilinear (GimpVector3 *position)
{
GimpRGB color;
gint x;
gdouble xf, yf;
GimpVector3 normal, *p;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.transparent_background && heights[1][x] == 0)
{
gimp_rgb_set_alpha (&color, 0.0);
}
else
{
color = peek (x, RINT (yf));
if (mapvals.lightsource.type == POINT_LIGHT)
p = &mapvals.lightsource.position;
else
p = &mapvals.lightsource.direction;
if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
{
color = phong_shade (position,
&mapvals.viewpoint,
&mapvals.planenormal,
p,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
else
{
normal = vertex_normals[1][x];
color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
}
return color;
}
GimpRGB
get_ray_color_no_bilinear_ref (GimpVector3 *position)
{
GimpRGB color, env_color;
gint x;
gdouble xf, yf;
GimpVector3 normal, *p, v, r;
pos_to_float (position->x, position->y, &xf, &yf);
x = RINT (xf);
if (mapvals.transparent_background && heights[1][x] == 0)
{
gimp_rgb_set_alpha (&color, 0.0);
}
else
{
color = peek (RINT (xf), RINT (yf));
if (mapvals.lightsource.type == POINT_LIGHT)
p = &mapvals.lightsource.position;
else
p = &mapvals.lightsource.direction;
if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
{
pos_to_float (position->x, position->y, &xf, &yf);
color = peek (RINT (xf), RINT (yf));
gimp_vector3_sub (&v, &mapvals.viewpoint, position);
gimp_vector3_normalize (&v);
r = compute_reflected_ray (&mapvals.planenormal, &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));
color = phong_shade (position,
&mapvals.viewpoint,
&mapvals.planenormal,
p,
&env_color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
else
{
normal = vertex_normals[1][(gint) RINT (xf)];
pos_to_float (position->x, position->y, &xf, &yf);
color = peek (RINT (xf), RINT (yf));
gimp_vector3_sub (&v, &mapvals.viewpoint, position);
gimp_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));
color = phong_shade (position,
&mapvals.viewpoint,
&normal,
p,
&env_color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
}
}
return color;
}