Files
gimp/plug-ins/MapObject/mapobject_shade.c
Tor Lillqvist 970ad834d1 libgimp/gimp.def libgimp/gimpui.def libgimp/makefile.{cygwin,msc}
2000-02-15  Tor Lillqvist  <tml@iki.fi>

* libgimp/gimp.def
* libgimp/gimpui.def
* libgimp/makefile.{cygwin,msc}
* app/makefile.{cygwin,msc}
* plug-ins/makefile.{cygwin,msc}: Updates.

* app/datafiles.c (is_script): New Win32-only function, which
tests if a file's extension matches one of the extensions in the
PATHEXT environment variable (which the cmd.exe command
interpreter also uses). This is to avoid starting applications
associated with any random data file the user might have dropped
in the plug-ins folder, while still supporting plug-ins written in
scripting languages.

* app/gimpparasite.c (gimp_parasiterc_save): (Win32:) Cannot
rename to an existing file.

* plug-ins/Lighting/lighting_image.c
* plug-ins/Lighting/lighting_share.c
* plug-ins/MapObject/mapobject_preview.c
* plug-ins/MapObject/mapobject_shade.c: Use G_PI.

* plug-ins/common/gz.c: #ifdef G_OS_WIN32 was used before its
potential definition via glib.h.

* plug-ins/common/jpeg.c: Also recognize Exif files, which are
typically produced by digital cameras. The usually have a .jpg
file name extension, and would thus already match this plug-in,
but add the magic string just in case. They are loaded just fine
by libjpeg even if they don't have the JFIF signature.

* plug-ins/common/tiff.c: Set TIFF warning and error handler, so
we get to pass libtiff's messages through the normal channels.
2000-02-14 22:44:06 +00:00

1222 lines
34 KiB
C

/*****************/
/* Shading stuff */
/*****************/
#include "mapobject_shade.h"
gdouble bx1,by1,bx2,by2;
get_ray_color_func get_ray_color;
typedef struct {
gdouble u,v;
gdouble t;
GimpVector3 s;
GimpVector3 n;
gint face;
} FaceIntersectInfo;
/*****************/
/* Phong shading */
/*****************/
GckRGB phong_shade(GimpVector3 *pos,GimpVector3 *viewpoint,GimpVector3 *normal,GimpVector3 *light,
GckRGB *diff_col,GckRGB *spec_col,gint type)
{
GckRGB ambientcolor,diffusecolor,specularcolor;
gdouble NL,RV,dist;
GimpVector3 L,NN,V,N;
/* Compute ambient intensity */
/* ========================= */
N=*normal;
ambientcolor=*diff_col;
gck_rgb_mul(&ambientcolor,mapvals.material.ambient_int);
/* Compute (N*L) term of Phong's equation */
/* ====================================== */
if (type==POINT_LIGHT)
gimp_vector3_sub(&L,light,pos);
else
L=*light;
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);
if (NL>=0.0)
{
/* Compute (R*V)^alpha term of Phong's equation */
/* ============================================ */
gimp_vector3_sub(&V,viewpoint,pos);
gimp_vector3_normalize(&V);
gimp_vector3_mul(&N,NL);
gimp_vector3_sub(&NN,&N,&L);
RV=gimp_vector3_inner_product(&NN,&V);
RV=pow(RV,mapvals.material.highlight);
/* Compute diffuse and specular intensity contribution */
/* =================================================== */
diffusecolor=*diff_col;
gck_rgb_mul(&diffusecolor,mapvals.material.diffuse_ref);
gck_rgb_mul(&diffusecolor,NL);
specularcolor=*spec_col;
gck_rgb_mul(&specularcolor,mapvals.material.specular_ref);
gck_rgb_mul(&specularcolor,RV);
gck_rgb_add(&diffusecolor,&specularcolor);
gck_rgb_mul(&diffusecolor,mapvals.material.diffuse_int);
gck_rgb_clamp(&diffusecolor);
gck_rgb_add(&ambientcolor,&diffusecolor);
}
return(ambientcolor);
}
gint plane_intersect(GimpVector3 *dir,GimpVector3 *viewp,GimpVector3 *ipos,gdouble *u,gdouble *v)
{
static gdouble det,det1,det2,det3,t;
imat[0][0]=dir->x; imat[1][0]=dir->y; imat[2][0]=dir->z;
/* Compute determinant of the first 3x3 sub matrix (denominator) */
/* ============================================================= */
det=imat[0][0]*imat[1][1]*imat[2][2]+imat[0][1]*imat[1][2]*imat[2][0]+
imat[0][2]*imat[1][0]*imat[2][1]-imat[0][2]*imat[1][1]*imat[2][0]-
imat[0][0]*imat[1][2]*imat[2][1]-imat[2][2]*imat[0][1]*imat[1][0];
/* If the determinant is non-zero, a intersection point exists */
/* =========================================================== */
if (det!=0.0)
{
/* Now, lets compute the numerator determinants (wow ;) */
/* ==================================================== */
det1=imat[0][3]*imat[1][1]*imat[2][2]+imat[0][1]*imat[1][2]*imat[2][3]+
imat[0][2]*imat[1][3]*imat[2][1]-imat[0][2]*imat[1][1]*imat[2][3]-
imat[1][2]*imat[2][1]*imat[0][3]-imat[2][2]*imat[0][1]*imat[1][3];
det2=imat[0][0]*imat[1][3]*imat[2][2]+imat[0][3]*imat[1][2]*imat[2][0]+
imat[0][2]*imat[1][0]*imat[2][3]-imat[0][2]*imat[1][3]*imat[2][0]-
imat[1][2]*imat[2][3]*imat[0][0]-imat[2][2]*imat[0][3]*imat[1][0];
det3=imat[0][0]*imat[1][1]*imat[2][3]+imat[0][1]*imat[1][3]*imat[2][0]+
imat[0][3]*imat[1][0]*imat[2][1]-imat[0][3]*imat[1][1]*imat[2][0]-
imat[1][3]*imat[2][1]*imat[0][0]-imat[2][3]*imat[0][1]*imat[1][0];
/* Now we have the simultanous solutions. Lets compute the unknowns */
/* (skip u&v if t is <0, this means the intersection is behind us) */
/* ================================================================ */
t=det1/det;
if (t>0.0)
{
*u=1.0+((det2/det)-0.5);
*v=1.0+((det3/det)-0.5);
ipos->x=viewp->x+t*dir->x;
ipos->y=viewp->y+t*dir->y;
ipos->z=viewp->z+t*dir->z;
return(TRUE);
}
}
return(FALSE);
}
/**********************************************************************************/
/* These routines computes the color of the surface of the plane at a given point */
/**********************************************************************************/
GckRGB get_ray_color_plane(GimpVector3 *pos)
{
GckRGB color=background;
static gint inside=FALSE;
static GimpVector3 ray,spos;
static gdouble vx,vy;
/* Construct a line from our VP to the point */
/* ========================================= */
gimp_vector3_sub(&ray,pos,&mapvals.viewpoint);
gimp_vector3_normalize(&ray);
/* Check for intersection. This is a quasi ray-tracer. */
/* =================================================== */
if (plane_intersect(&ray,&mapvals.viewpoint,&spos,&vx,&vy)==TRUE)
{
color=get_image_color(vx,vy,&inside);
if (color.a!=0.0 && inside==TRUE && mapvals.lightsource.type!=NO_LIGHT)
{
/* Compute shading at this point */
/* ============================= */
color=phong_shade(&spos,&mapvals.viewpoint,&mapvals.normal,
&mapvals.lightsource.position,&color,
&mapvals.lightsource.color,mapvals.lightsource.type);
gck_rgb_clamp(&color);
}
}
if (color.a==0.0)
color=background;
return(color);
}
/***********************************************************************/
/* Given the NorthPole, Equator and a third vector (normal) compute */
/* the conversion from spherical oordinates to image space coordinates */
/***********************************************************************/
void sphere_to_image(GimpVector3 *normal,gdouble *u,gdouble *v)
{
static gdouble alpha,fac;
static GimpVector3 cross_prod;
alpha=acos(-gimp_vector3_inner_product(&mapvals.secondaxis,normal));
*v=alpha/G_PI;
if (*v==0.0 || *v==1.0) *u=0.0;
else
{
fac=gimp_vector3_inner_product(&mapvals.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(&mapvals.secondaxis,&mapvals.firstaxis);
if (gimp_vector3_inner_product(&cross_prod,normal)<0.0)
*u=1.0-*u;
}
}
/***************************************************/
/* Compute intersection point with sphere (if any) */
/***************************************************/
gint sphere_intersect(GimpVector3 *dir,GimpVector3 *viewp,GimpVector3 *spos1,GimpVector3 *spos2)
{
static gdouble alpha,beta,tau,s1,s2,tmp;
static GimpVector3 t;
gimp_vector3_sub(&t,&mapvals.position,viewp);
alpha=gimp_vector3_inner_product(dir,&t);
beta=gimp_vector3_inner_product(&t,&t);
tau=alpha*alpha-beta+mapvals.radius*mapvals.radius;
if (tau>=0.0)
{
tau=sqrt(tau);
s1=alpha+tau;
s2=alpha-tau;
if (s2<s1)
{
tmp=s1;
s1=s2;
s2=tmp;
}
spos1->x=viewp->x+s1*dir->x;
spos1->y=viewp->y+s1*dir->y;
spos1->z=viewp->z+s1*dir->z;
spos2->x=viewp->x+s2*dir->x;
spos2->y=viewp->y+s2*dir->y;
spos2->z=viewp->z+s2*dir->z;
return(TRUE);
}
return(FALSE);
}
/***********************************************************************************/
/* These routines computes the color of the surface of the sphere at a given point */
/***********************************************************************************/
GckRGB get_ray_color_sphere(GimpVector3 *pos)
{
GckRGB color=background;
static GckRGB color2;
static gint inside=FALSE;
static GimpVector3 normal,ray,spos1,spos2;
static gdouble vx,vy;
/* Check if ray is within the bounding box */
/* ======================================= */
if (pos->x<bx1 || pos->x>bx2 || pos->y<by1 || pos->y>by2)
return(color);
/* Construct a line from our VP to the point */
/* ========================================= */
gimp_vector3_sub(&ray,pos,&mapvals.viewpoint);
gimp_vector3_normalize(&ray);
/* Check for intersection. This is a quasi ray-tracer. */
/* =================================================== */
if (sphere_intersect(&ray,&mapvals.viewpoint,&spos1,&spos2)==TRUE)
{
/* Compute spherical to rectangular mapping */
/* ======================================== */
gimp_vector3_sub(&normal,&spos1,&mapvals.position);
gimp_vector3_normalize(&normal);
sphere_to_image(&normal,&vx,&vy);
color=get_image_color(vx,vy,&inside);
/* Check for total transparency... */
/* =============================== */
if (color.a<1.0)
{
/* Hey, we can see through here! */
/* Lets see what's on the other side.. */
/* =================================== */
color=phong_shade(&spos1,
&mapvals.viewpoint,
&normal,
&mapvals.lightsource.position,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color);
gimp_vector3_sub(&normal,&spos2,&mapvals.position);
gimp_vector3_normalize(&normal);
sphere_to_image(&normal,&vx,&vy);
color2=get_image_color(vx,vy,&inside);
/* Make the normal point inwards */
/* ============================= */
gimp_vector3_mul(&normal,-1.0);
color2=phong_shade(&spos2,
&mapvals.viewpoint,
&normal,
&mapvals.lightsource.position,
&color2,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color2);
if (mapvals.transparent_background==FALSE && color2.a<1.0)
{
color2.r = (color2.r*color2.a)+(background.r*(1.0-color2.a));
color2.g = (color2.g*color2.a)+(background.g*(1.0-color2.a));
color2.b = (color2.b*color2.a)+(background.b*(1.0-color2.a));
color2.a = 1.0;
}
/* Compute a mix of the first and second colors */
/* ============================================ */
color.r = color.r*color.a+(1.0-color.a)*color2.r;
color.g = color.g*color.a+(1.0-color.a)*color2.g;
color.b = color.b*color.a+(1.0-color.a)*color2.b;
color.a = color.a+color2.a;
gck_rgba_clamp(&color);
}
else if (color.a!=0.0 && inside==TRUE && mapvals.lightsource.type!=NO_LIGHT)
{
/* Compute shading at this point */
/* ============================= */
color=phong_shade(&spos1,
&mapvals.viewpoint,
&normal,
&mapvals.lightsource.position,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color);
}
}
if (color.a==0.0)
color=background;
return(color);
}
/***************************************************/
/* Transform the corners of the bounding box to 2D */
/***************************************************/
void compute_bounding_box(void)
{
GimpVector3 p1,p2;
gdouble t;
GimpVector3 dir;
p1=mapvals.position;
p1.x-=(mapvals.radius+0.01);
p1.y-=(mapvals.radius+0.01);
p2=mapvals.position;
p2.x+=(mapvals.radius+0.01);
p2.y+=(mapvals.radius+0.01);
gimp_vector3_sub(&dir,&p1,&mapvals.viewpoint);
gimp_vector3_normalize(&dir);
if (dir.z!=0.0)
{
t=(-1.0*mapvals.viewpoint.z)/dir.z;
p1.x=(mapvals.viewpoint.x+t*dir.x);
p1.y=(mapvals.viewpoint.y+t*dir.y);
}
gimp_vector3_sub(&dir,&p2,&mapvals.viewpoint);
gimp_vector3_normalize(&dir);
if (dir.z!=0.0)
{
t=(-1.0*mapvals.viewpoint.z)/dir.z;
p2.x=(mapvals.viewpoint.x+t*dir.x);
p2.y=(mapvals.viewpoint.y+t*dir.y);
}
bx1=p1.x;
by1=p1.y;
bx2=p2.x;
by2=p2.y;
}
/* These two were taken from the Mesa source. Mesa is written */
/* and is (C) by Brian Paul. vecmulmat() performs a post-mul by */
/* a 4x4 matrix to a 1x4(3) vector. rotmat() creates a matrix */
/* that by post-mul will rotate a 1x4(3) vector the given angle */
/* about the given axis. */
/* ============================================================ */
void vecmulmat(GimpVector3 *u,GimpVector3 *v,gfloat m[16])
{
gfloat v0=v->x, v1=v->y, v2=v->z;
#define M(row,col) m[col*4+row]
u->x = v0 * M(0,0) + v1 * M(1,0) + v2 * M(2,0) + M(3,0);
u->y = v0 * M(0,1) + v1 * M(1,1) + v2 * M(2,1) + M(3,1);
u->z = v0 * M(0,2) + v1 * M(1,2) + v2 * M(2,2) + M(3,2);
#undef M
}
void rotatemat(gfloat angle,GimpVector3 *v,gfloat m[16])
{
/* This function contributed by Erich Boleyn (erich@uruk.org) */
gfloat mag, s, c;
gfloat xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
gfloat IdentityMat[16];
gint cnt;
s = sin( angle * (G_PI / 180.0) );
c = cos( angle * (G_PI / 180.0) );
mag = sqrt( v->x*v->x + v->y*v->y + v->z*v->z );
if (mag == 0.0) {
/* generate an identity matrix and return */
for (cnt=0;cnt<16;cnt++)
IdentityMat[cnt]=0.0;
IdentityMat[0] = 1.0;
IdentityMat[5] = 1.0;
IdentityMat[10] = 1.0;
IdentityMat[15] = 1.0;
memcpy(m, IdentityMat, sizeof(gfloat)*16);
return;
}
v->x /= mag;
v->y /= mag;
v->z /= mag;
#define M(row,col) m[col*4+row]
xx = v->x * v->x;
yy = v->y * v->y;
zz = v->z * v->z;
xy = v->x * v->y;
yz = v->y * v->z;
zx = v->z * v->x;
xs = v->x * s;
ys = v->y * s;
zs = v->z * s;
one_c = 1.0F - c;
M(0,0) = (one_c * xx) + c;
M(0,1) = (one_c * xy) - zs;
M(0,2) = (one_c * zx) + ys;
M(0,3) = 0.0F;
M(1,0) = (one_c * xy) + zs;
M(1,1) = (one_c * yy) + c;
M(1,2) = (one_c * yz) - xs;
M(1,3) = 0.0F;
M(2,0) = (one_c * zx) - ys;
M(2,1) = (one_c * yz) + xs;
M(2,2) = (one_c * zz) + c;
M(2,3) = 0.0F;
M(3,0) = 0.0F;
M(3,1) = 0.0F;
M(3,2) = 0.0F;
M(3,3) = 1.0F;
#undef M
}
/* Transpose the matrix m. If m is orthogonal (like a rotation matrix), */
/* this is equal to the inverse of the matrix. */
/* ==================================================================== */
void transpose_mat(gfloat m[16])
{
gint i,j;
gfloat t;
for (i=0;i<4;i++)
{
for (j=0;j<i;j++)
{
t = m[j*4+i];
m[j*4+i] = m[i*4+j];
m[i*4+j] = t;
}
}
}
/* Compute the matrix product c=a*b */
/* ================================ */
void matmul(gfloat a[16],gfloat b[16],gfloat c[16])
{
gint i,j,k;
gfloat value;
#define A(row,col) a[col*4+row]
#define B(row,col) b[col*4+row]
#define C(row,col) c[col*4+row]
for (i=0;i<4;i++)
{
for (j=0;j<4;j++)
{
value = 0.0;
for (k=0;k<4;k++)
value += A(i,k)*B(k,j);
C(i,j) = value;
}
}
#undef A
#undef B
#undef C
}
void ident_mat(gfloat m[16])
{
gint i,j;
#define M(row,col) m[col*4+row]
for (i=0;i<4;i++)
{
for (j=0;j<4;j++)
{
if (i==j)
M(i,j) = 1.0;
else
M(i,j) = 0.0;
}
}
#undef M
}
gboolean intersect_rect(gdouble u,gdouble v,gdouble w,
GimpVector3 viewp,GimpVector3 dir,
FaceIntersectInfo *face_info)
{
gboolean result = FALSE;
gdouble u2,v2;
if (dir.z!=0.0)
{
u2 = u / 2.0;
v2 = v / 2.0;
face_info->t = (w-viewp.z)/dir.z;
face_info->s.x = viewp.x + face_info->t*dir.x;
face_info->s.y = viewp.y + face_info->t*dir.y;
face_info->s.z = w;
if (face_info->s.x>=-u2 && face_info->s.x<=u2 &&
face_info->s.y>=-v2 && face_info->s.y<=v2)
{
face_info->u = (face_info->s.x + u2)/u;
face_info->v = (face_info->s.y + v2)/v;
result = TRUE;
}
}
return(result);
}
gboolean intersect_box(GimpVector3 scale, GimpVector3 viewp, GimpVector3 dir,
FaceIntersectInfo *face_intersect)
{
GimpVector3 v,d,tmp,axis[3];
FaceIntersectInfo face_tmp;
gboolean result = FALSE;
gfloat m[16];
gint i = 0;
gimp_vector3_set(&axis[0], 1.0,0.0,0.0);
gimp_vector3_set(&axis[1], 0.0,1.0,0.0);
gimp_vector3_set(&axis[2], 0.0,0.0,1.0);
/* Front side */
/* ========== */
if (intersect_rect(scale.x,scale.y,scale.z/2.0,viewp,dir,&face_intersect[i])==TRUE)
{
face_intersect[i].face = 0;
gimp_vector3_set(&face_intersect[i++].n, 0.0,0.0,1.0);
result = TRUE;
}
/* Back side */
/* ========= */
if (intersect_rect(scale.x,scale.y,-scale.z/2.0,viewp,dir,&face_intersect[i])==TRUE)
{
face_intersect[i].face = 1;
gimp_vector3_set(&face_intersect[i++].n, 0.0,0.0,-1.0);
face_intersect[i].u = 1.0 - face_intersect[i].u;
face_intersect[i].v = 1.0 - face_intersect[i].v;
result = TRUE;
}
/* Check if we've found the two possible intersection points */
/* ========================================================= */
if (i<2)
{
/* Top: Rotate viewpoint and direction into rectangle's local coordinate system */
/* ============================================================================ */
rotatemat(90, &axis[0], m);
vecmulmat(&v,&viewp,m);
vecmulmat(&d,&dir,m);
if (intersect_rect(scale.x,scale.z,scale.y/2.0,v,d,&face_intersect[i])==TRUE)
{
face_intersect[i].face = 2;
transpose_mat(m);
vecmulmat(&tmp, &face_intersect[i].s, m);
face_intersect[i].s = tmp;
gimp_vector3_set(&face_intersect[i++].n, 0.0,-1.0,0.0);
result = TRUE;
}
}
/* Check if we've found the two possible intersection points */
/* ========================================================= */
if (i<2)
{
/* Bottom: Rotate viewpoint and direction into rectangle's local coordinate system */
/* =============================================================================== */
rotatemat(90, &axis[0], m);
vecmulmat(&v,&viewp,m);
vecmulmat(&d,&dir,m);
if (intersect_rect(scale.x,scale.z,-scale.y/2.0,v,d,&face_intersect[i])==TRUE)
{
face_intersect[i].face = 3;
transpose_mat(m);
vecmulmat(&tmp, &face_intersect[i].s, m);
face_intersect[i].s = tmp;
face_intersect[i].v = 1.0 - face_intersect[i].v;
gimp_vector3_set(&face_intersect[i++].n, 0.0,1.0,0.0);
result = TRUE;
}
}
/* Check if we've found the two possible intersection points */
/* ========================================================= */
if (i<2)
{
/* Left side: Rotate viewpoint and direction into rectangle's local coordinate system */
/* ================================================================================== */
rotatemat(90, &axis[1], m);
vecmulmat(&v,&viewp,m);
vecmulmat(&d,&dir,m);
if (intersect_rect(scale.z,scale.y,scale.x/2.0,v,d,&face_intersect[i])==TRUE)
{
face_intersect[i].face = 4;
transpose_mat(m);
vecmulmat(&tmp, &face_intersect[i].s, m);
face_intersect[i].s = tmp;
gimp_vector3_set(&face_intersect[i++].n, 1.0,0.0,0.0);
result = TRUE;
}
}
/* Check if we've found the two possible intersection points */
/* ========================================================= */
if (i<2)
{
/* Right side: Rotate viewpoint and direction into rectangle's local coordinate system */
/* =================================================================================== */
rotatemat(90, &axis[1], m);
vecmulmat(&v,&viewp,m);
vecmulmat(&d,&dir,m);
if (intersect_rect(scale.z,scale.y,-scale.x/2.0,v,d,&face_intersect[i])==TRUE)
{
face_intersect[i].face = 5;
transpose_mat(m);
vecmulmat(&tmp, &face_intersect[i].s, m);
face_intersect[i].u = 1.0 - face_intersect[i].u;
gimp_vector3_set(&face_intersect[i++].n, -1.0,0.0,0.0);
result = TRUE;
}
}
/* Sort intersection points */
/* ======================== */
if (face_intersect[0].t>face_intersect[1].t)
{
face_tmp = face_intersect[0];
face_intersect[0] = face_intersect[1];
face_intersect[1] = face_tmp;
}
return(result);
}
GckRGB get_ray_color_box(GimpVector3 *pos)
{
GimpVector3 lvp,ldir,vp,p,dir,ns,nn;
GckRGB color, color2;
gfloat m[16];
gint i;
FaceIntersectInfo face_intersect[2];
color=background;
vp = mapvals.viewpoint;
p = *pos;
/* Translate viewpoint so that the box has its origin */
/* at its lower left corner. */
/* ================================================== */
vp.x = vp.x - mapvals.position.x;
vp.y = vp.y - mapvals.position.y;
vp.z = vp.z - mapvals.position.z;
p.x = p.x - mapvals.position.x;
p.y = p.y - mapvals.position.y;
p.z = p.z - mapvals.position.z;
/* Compute direction */
/* ================= */
gimp_vector3_sub(&dir,&p,&vp);
gimp_vector3_normalize(&dir);
/* Compute inverse of rotation matrix and apply it to */
/* the viewpoint and direction. This transforms the */
/* observer into the local coordinate system of the box */
/* ==================================================== */
memcpy(m,rotmat,sizeof(gfloat)*16);
transpose_mat(m);
vecmulmat(&lvp,&vp,m);
vecmulmat(&ldir,&dir,m);
/* Ok. Now the observer is in the space where the box is located */
/* with its lower left corner at the origin and its axis aligned */
/* to the cartesian basis. Check if the transformed ray hits it. */
/* ============================================================= */
face_intersect[0].t = 1000000.0;
face_intersect[1].t = 1000000.0;
if (intersect_box(mapvals.scale,lvp,ldir,face_intersect)==TRUE)
{
/* We've hit the box. Transform the hit points and */
/* normals back into the world coordinate system */
/* =============================================== */
for (i=0;i<2;i++)
{
vecmulmat(&ns,&face_intersect[i].s,rotmat);
vecmulmat(&nn,&face_intersect[i].n,rotmat);
ns.x = ns.x + mapvals.position.x;
ns.y = ns.y + mapvals.position.y;
ns.z = ns.z + mapvals.position.z;
face_intersect[i].s = ns;
face_intersect[i].n = nn;
}
color = get_box_image_color(face_intersect[0].face,
face_intersect[0].u,face_intersect[0].v);
/* Check for total transparency... */
/* =============================== */
if (color.a<1.0)
{
/* Hey, we can see through here! */
/* Lets see what's on the other side.. */
/* =================================== */
color=phong_shade(
&face_intersect[0].s,
&mapvals.viewpoint,
&face_intersect[0].n,
&mapvals.lightsource.position,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color);
color2 = get_box_image_color(face_intersect[1].face,
face_intersect[1].u,face_intersect[1].v);
/* Make the normal point inwards */
/* ============================= */
gimp_vector3_mul(&face_intersect[1].n,-1.0);
color2=phong_shade(
&face_intersect[1].s,
&mapvals.viewpoint,
&face_intersect[1].n,
&mapvals.lightsource.position,
&color2,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color2);
if (mapvals.transparent_background==FALSE && color2.a<1.0)
{
color2.r = (color2.r*color2.a)+(background.r*(1.0-color2.a));
color2.g = (color2.g*color2.a)+(background.g*(1.0-color2.a));
color2.b = (color2.b*color2.a)+(background.b*(1.0-color2.a));
color2.a = 1.0;
}
/* Compute a mix of the first and second colors */
/* ============================================ */
color.r = color.r*color.a+(1.0-color.a)*color2.r;
color.g = color.g*color.a+(1.0-color.a)*color2.g;
color.b = color.b*color.a+(1.0-color.a)*color2.b;
color.a = color.a+color2.a;
gck_rgba_clamp(&color);
}
else if (color.a!=0.0 && mapvals.lightsource.type!=NO_LIGHT)
{
color=phong_shade(
&face_intersect[0].s,
&mapvals.viewpoint,
&face_intersect[0].n,
&mapvals.lightsource.position,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color);
}
}
else
{
if (mapvals.transparent_background==TRUE)
color.a = 0.0;
}
return(color);
}
gboolean intersect_circle(GimpVector3 vp,GimpVector3 dir,gdouble w,
FaceIntersectInfo *face_info)
{
gboolean result = FALSE;
gdouble r,d;
#define sqr(a) a*a
if (dir.y!=0.0)
{
face_info->t = (w-vp.y)/dir.y;
face_info->s.x = vp.x + face_info->t*dir.x;
face_info->s.y = w;
face_info->s.z = vp.z + face_info->t*dir.z;
r = sqrt(sqr(face_info->s.x) + sqr(face_info->s.z));
if (r<=mapvals.cylinder_radius)
{
d = 2.0*mapvals.cylinder_radius;
face_info->u = (face_info->s.x+mapvals.cylinder_radius)/d;
face_info->v = (face_info->s.z+mapvals.cylinder_radius)/d;
result = TRUE;
}
}
#undef sqr
return(result);
}
gdouble compute_angle(gdouble x,gdouble y)
{
gdouble a = 0;
/* Check which quadrant we're in and correct angle */
/* =============================================== */
if (y==0.0)
{
if (x<0)
a = 0;
else
a = G_PI;
}
else
{
if (x!=0.0)
a = atan(y/x);
else
{
if (y>0.0)
a = G_PI/2.0;
else
a = 1.5 * G_PI;
}
if (y<0.0 && x>0.0) /* 4th quad, a is negative */
a = 2.0*G_PI + a;
else if (y<0.0 && x<0.0) /* 3rd quad, a is positive */
a = G_PI + a;
else if (y>0.0 && x<0.0) /* 2nd quad, a is negative */
a = G_PI + a;
}
return(a);
}
gboolean intersect_cylinder(GimpVector3 vp,GimpVector3 dir,FaceIntersectInfo *face_intersect)
{
gdouble a,b,c,d,e,f,tmp,l;
gboolean result = FALSE;
gint i;
#define sqr(a) a*a
a = sqr(dir.x) + sqr(dir.z);
b = 2.0*(vp.x*dir.x+vp.z*dir.z);
c = sqr(vp.x)+sqr(vp.z)-sqr(mapvals.cylinder_radius);
d = sqr(b)-4.0*a*c;
if (d>=0.0)
{
e = sqrt(d);
f = 2.0*a;
if (f!=0.0)
{
result = TRUE;
face_intersect[0].t = (-b+e)/f;
face_intersect[1].t = (-b-e)/f;
if (face_intersect[0].t>face_intersect[1].t)
{
tmp = face_intersect[0].t;
face_intersect[0].t = face_intersect[1].t;
face_intersect[1].t = tmp;
}
for (i=0;i<2;i++)
{
face_intersect[i].s.x = vp.x + face_intersect[i].t * dir.x;
face_intersect[i].s.y = vp.y + face_intersect[i].t * dir.y;
face_intersect[i].s.z = vp.z + face_intersect[i].t * dir.z;
face_intersect[i].n = face_intersect[i].s;
face_intersect[i].n.y = 0.0;
gimp_vector3_normalize(&face_intersect[i].n);
l = mapvals.cylinder_length/2.0;
face_intersect[i].u = compute_angle(face_intersect[i].s.x,face_intersect[i].s.z)/(2.0*G_PI);
face_intersect[i].v = (face_intersect[i].s.y+l)/mapvals.cylinder_length;
/* Mark hitpoint as on the cylinder hull */
/* ===================================== */
face_intersect[i].face = 0;
/* Check if we're completely off the cylinder axis */
/* =============================================== */
if (face_intersect[i].s.y>l || face_intersect[i].s.y<-l)
{
/* Check if we've hit a cap */
/* ======================== */
if (face_intersect[i].s.y>l)
{
if (intersect_circle(vp,dir,l,&face_intersect[i])==FALSE)
result = FALSE;
else
{
face_intersect[i].face = 1;
gimp_vector3_set(&face_intersect[i].n, 0.0, 1.0, 0.0);
}
}
else
{
if (intersect_circle(vp,dir,-l,&face_intersect[i])==FALSE)
result = FALSE;
else
{
face_intersect[i].face = 2;
gimp_vector3_set(&face_intersect[i].n, 0.0, -1.0, 0.0);
}
}
}
}
}
}
#undef sqr
return(result);
}
GckRGB get_cylinder_color(gint face, gdouble u, gdouble v)
{
GckRGB color;
gint inside;
if (face==0)
color = get_image_color(u,v,&inside);
else
color = get_cylinder_image_color (face-1,u,v);
return(color);
}
GckRGB get_ray_color_cylinder(GimpVector3 *pos)
{
GimpVector3 lvp,ldir,vp,p,dir,ns,nn;
GckRGB color, color2;
gfloat m[16];
gint i;
FaceIntersectInfo face_intersect[2];
color=background;
vp = mapvals.viewpoint;
p = *pos;
vp.x = vp.x - mapvals.position.x;
vp.y = vp.y - mapvals.position.y;
vp.z = vp.z - mapvals.position.z;
p.x = p.x - mapvals.position.x;
p.y = p.y - mapvals.position.y;
p.z = p.z - mapvals.position.z;
/* Compute direction */
/* ================= */
gimp_vector3_sub(&dir,&p,&vp);
gimp_vector3_normalize(&dir);
/* Compute inverse of rotation matrix and apply it to */
/* the viewpoint and direction. This transforms the */
/* observer into the local coordinate system of the box */
/* ==================================================== */
memcpy(m,rotmat,sizeof(gfloat)*16);
transpose_mat(m);
vecmulmat(&lvp,&vp,m);
vecmulmat(&ldir,&dir,m);
if (intersect_cylinder(lvp,ldir,face_intersect)==TRUE)
{
/* We've hit the cylinder. Transform the hit points and */
/* normals back into the world coordinate system */
/* ==================================================== */
for (i=0;i<2;i++)
{
vecmulmat(&ns,&face_intersect[i].s,rotmat);
vecmulmat(&nn,&face_intersect[i].n,rotmat);
ns.x = ns.x + mapvals.position.x;
ns.y = ns.y + mapvals.position.y;
ns.z = ns.z + mapvals.position.z;
face_intersect[i].s = ns;
face_intersect[i].n = nn;
}
color = get_cylinder_color(face_intersect[0].face,
face_intersect[0].u,face_intersect[0].v);
/* Check for total transparency... */
/* =============================== */
if (color.a<1.0)
{
/* Hey, we can see through here! */
/* Lets see what's on the other side.. */
/* =================================== */
color=phong_shade(
&face_intersect[0].s,
&mapvals.viewpoint,
&face_intersect[0].n,
&mapvals.lightsource.position,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color);
color2 = get_cylinder_color(face_intersect[1].face,
face_intersect[1].u,face_intersect[1].v);
/* Make the normal point inwards */
/* ============================= */
gimp_vector3_mul(&face_intersect[1].n,-1.0);
color2=phong_shade(
&face_intersect[1].s,
&mapvals.viewpoint,
&face_intersect[1].n,
&mapvals.lightsource.position,
&color2,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color2);
if (mapvals.transparent_background==FALSE && color2.a<1.0)
{
color2.r = (color2.r*color2.a)+(background.r*(1.0-color2.a));
color2.g = (color2.g*color2.a)+(background.g*(1.0-color2.a));
color2.b = (color2.b*color2.a)+(background.b*(1.0-color2.a));
color2.a = 1.0;
}
/* Compute a mix of the first and second colors */
/* ============================================ */
color.r = color.r*color.a+(1.0-color.a)*color2.r;
color.g = color.g*color.a+(1.0-color.a)*color2.g;
color.b = color.b*color.a+(1.0-color.a)*color2.b;
color.a = color.a+color2.a;
gck_rgba_clamp(&color);
}
else if (color.a!=0.0 && mapvals.lightsource.type!=NO_LIGHT)
{
color=phong_shade(
&face_intersect[0].s,
&mapvals.viewpoint,
&face_intersect[0].n,
&mapvals.lightsource.position,
&color,
&mapvals.lightsource.color,
mapvals.lightsource.type);
gck_rgba_clamp(&color);
}
}
else
{
if (mapvals.transparent_background==TRUE)
color.a = 0.0;
}
return(color);
}