/*****************/ /* 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; GckVector3 s; GckVector3 n; gint face; } FaceIntersectInfo; /*****************/ /* Phong shading */ /*****************/ GckRGB phong_shade(GckVector3 *pos,GckVector3 *viewpoint,GckVector3 *normal,GckVector3 *light, GckRGB *diff_col,GckRGB *spec_col,gint type) { GckRGB ambientcolor,diffusecolor,specularcolor; gdouble NL,RV,dist; GckVector3 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) gck_vector3_sub(&L,light,pos); else L=*light; dist=gck_vector3_length(&L); if (dist!=0.0) gck_vector3_mul(&L,1.0/dist); NL=2.0*gck_vector3_inner_product(&N,&L); if (NL>=0.0) { /* Compute (R*V)^alpha term of Phong's equation */ /* ============================================ */ gck_vector3_sub(&V,viewpoint,pos); gck_vector3_normalize(&V); gck_vector3_mul(&N,NL); gck_vector3_sub(&NN,&N,&L); RV=gck_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(GckVector3 *dir,GckVector3 *viewp,GckVector3 *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(GckVector3 *pos) { GckRGB color=background; static gint inside=FALSE; static GckVector3 ray,spos; static gdouble vx,vy; /* Construct a line from our VP to the point */ /* ========================================= */ gck_vector3_sub(&ray,pos,&mapvals.viewpoint); gck_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(GckVector3 *normal,gdouble *u,gdouble *v) { static gdouble alpha,fac; static GckVector3 cross_prod; alpha=acos(-gck_vector3_inner_product(&mapvals.secondaxis,normal)); *v=alpha/M_PI; if (*v==0.0 || *v==1.0) *u=0.0; else { fac=gck_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*M_PI); cross_prod=gck_vector3_cross_product(&mapvals.secondaxis,&mapvals.firstaxis); if (gck_vector3_inner_product(&cross_prod,normal)<0.0) *u=1.0-*u; } } /***************************************************/ /* Compute intersection point with sphere (if any) */ /***************************************************/ gint sphere_intersect(GckVector3 *dir,GckVector3 *viewp,GckVector3 *spos1,GckVector3 *spos2) { static gdouble alpha,beta,tau,s1,s2,tmp; static GckVector3 t; gck_vector3_sub(&t,&mapvals.position,viewp); alpha=gck_vector3_inner_product(dir,&t); beta=gck_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 (s2x=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(GckVector3 *pos) { GckRGB color=background; static GckRGB color2; static gint inside=FALSE; static GckVector3 normal,ray,spos1,spos2; static gdouble vx,vy; /* Check if ray is within the bounding box */ /* ======================================= */ if (pos->xx>bx2 || pos->yy>by2) return(color); /* Construct a line from our VP to the point */ /* ========================================= */ gck_vector3_sub(&ray,pos,&mapvals.viewpoint); gck_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 */ /* ======================================== */ gck_vector3_sub(&normal,&spos1,&mapvals.position); gck_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); gck_vector3_sub(&normal,&spos2,&mapvals.position); gck_vector3_normalize(&normal); sphere_to_image(&normal,&vx,&vy); color2=get_image_color(vx,vy,&inside); /* Make the normal point inwards */ /* ============================= */ gck_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) { GckVector3 p1,p2; gdouble t; GckVector3 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); gck_vector3_sub(&dir,&p1,&mapvals.viewpoint); gck_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); } gck_vector3_sub(&dir,&p2,&mapvals.viewpoint); gck_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(GckVector3 *u,GckVector3 *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,GckVector3 *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 * (M_PI / 180.0) ); c = cos( angle * (M_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;jt = (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(GckVector3 scale, GckVector3 viewp, GckVector3 dir, FaceIntersectInfo *face_intersect) { GckVector3 v,d,tmp,axis[3]; FaceIntersectInfo face_tmp; gboolean result = FALSE; gfloat m[16]; gint i = 0; gck_vector3_set(&axis[0], 1.0,0.0,0.0); gck_vector3_set(&axis[1], 0.0,1.0,0.0); gck_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; gck_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; gck_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; gck_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; gck_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; gck_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; gck_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(GckVector3 *pos) { GckVector3 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 */ /* ================= */ gck_vector3_sub(&dir,&p,&vp); gck_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 */ /* ============================= */ gck_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(GckVector3 vp,GckVector3 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 = M_PI; } else { if (x!=0.0) a = atan(y/x); else { if (y>0.0) a = M_PI/2.0; else a = 1.5 * M_PI; } if (y<0.0 && x>0.0) /* 4th quad, a is negative */ a = 2.0*M_PI + a; else if (y<0.0 && x<0.0) /* 3rd quad, a is positive */ a = M_PI + a; else if (y>0.0 && x<0.0) /* 2nd quad, a is negative */ a = M_PI + a; } return(a); } gboolean intersect_cylinder(GckVector3 vp,GckVector3 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; gck_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*M_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; gck_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; gck_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(GckVector3 *pos) { GckVector3 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 */ /* ================= */ gck_vector3_sub(&dir,&p,&vp); gck_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 */ /* ============================= */ gck_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); }