/************************************************************ ray trace module ************************************************************/ #include #include "qrt.h" /* #define RAYDEBUG 1 */ OBJ_PTR new_line(), new_obj(); /********************************************************** Diffuse color module. Also compute specular reflections here for efficiency sake - only one lamp eneumerator needed that way. **********************************************************/ DiffColor(color,cinfo,norm,loc,oline) SVECT_PTR color; VECT_PTR norm,loc; CINFO_PTR cinfo; OBJ_PTR oline; { OBJ_PTR lamp, line, CurrObj, Ray_Hit(); float t; register float dist, t1,t2; VECTOR refl, atten; line=new_line(); VectEQ(&(line->loc),loc); lamp=THEWORLD.lamps; while (lamp!=NULL) { # ifdef ROBUST if (lamp->type!=LAMP) Error(INTERNAL_ERROR,601); # endif /* find dir to lamp */ VecSubtract(&(line->vect1),&(lamp->loc),loc); CurrObj=NULL; atten.x = atten.y = atten.z = 1.00; if (def.shadow==TRUE) { /* set light attenuation factor */ CurrObj=Ray_Hit(THEWORLD.stack,line,&t,TRUE,TRUE,&atten); THEWORLD.to_lamp++; } if (CurrObj==NULL) { /* hit nothing ? */ dist = DotProd((line->vect1),(line->vect1)); Normalize(&(line->vect1)); if (cinfo->diff.r>0 || cinfo->diff.g>0 || cinfo->diff.b>0) { t1 = DotProd((*norm),(line->vect1)); if (t1>0) { t2 = lamp->vect1.y*t1/sqrt(dist); color->r+=(short)((lamp->cinfo.amb.r) * atten.x * t2*(cinfo->diff.r)/CNUM); color->g+=(short)((lamp->cinfo.amb.g) * atten.y * t2*(cinfo->diff.g)/CNUM); color->b+=(short)((lamp->cinfo.amb.b) * atten.z * t2*(cinfo->diff.b)/CNUM); } } if (cinfo->sreflect>0) { /* specular */ line->vect1.x = -line->vect1.x; /* reverse line */ line->vect1.y = -line->vect1.y; line->vect1.z = -line->vect1.z; Reflect(&refl,&(line->vect1),norm); t1 = -DotProd(refl,(oline->vect1)); if (t1>0) { t2 = pow(t1,cinfo->sreflect)* /* this is slow !! */ lamp->vect1.y/sqrt(dist); t2 *= (float)cinfo->reflect/(float)CNUM; color->r+=(short)(t2 * atten.x * (lamp->cinfo.amb.r)); color->g+=(short)(t2 * atten.y * (lamp->cinfo.amb.g)); color->b+=(short)(t2 * atten.z * (lamp->cinfo.amb.b)); } } } lamp=lamp->nextobj; } # ifdef RAYDEBUG printf("DIFFCOLOR: r=%d, g=%d, b=%d\n", color->r, color->g, color->b); # endif free(line); } /********************************************************** Ambient color module Really simple - just add the color, no questions asked. **********************************************************/ AmbColor(color, cinfo, norm, loc) SVECT_PTR color; VECT_PTR norm, loc; CINFO_PTR cinfo; { color->r+=(short)((int)cinfo->amb.r*(int)cinfo->diff.r/CNUM); color->g+=(short)((int)cinfo->amb.g*(int)cinfo->diff.g/CNUM); color->b+=(short)((int)cinfo->amb.b*(int)cinfo->diff.b/CNUM); # ifdef RAYDEBUG printf("AMBCOLOR: added r=%d, g=%d, b=%d\n", cinfo->amb.r,cinfo->amb.g,cinfo->amb.b); # endif } /********************************************************** Transmitted color module This is hairy and slow - bends ray around normal vector by ratio of indicies of refraction. line->flag=TRUE if we are inside a piece of glass - this means glass must have a simple structure. 10 Aug 88 - Fixed TransColor so that it works. The original algorithm came from a book, and I had some problems getting it to work. This one came from my head, and seems to work ok. 11 Aug 88 - Added density effects (light is reduced more by thicker glass). **********************************************************/ /* #define TESTTRANS TRUE */ TransColor(color,cinfo,norm,loc,line,inmult) SVECT_PTR color; VECT_PTR norm,loc; OBJ_PTR line; CINFO_PTR cinfo; float inmult; { OBJ_STRUCT newline; SVECTOR col1; VECTOR toadd, negvect1, distance; float index2, multiplier, side1; int maxtrans; /* these vars are used to keep track of the distance traveled through the glass so we can attenuate the light accordingly. */ static VECTOR prevpos; VECTOR displacement; SVECTOR attenuation; float glassdist; # ifdef TRANSDEBUG printf("TRANSCOLOR:\n"); # endif # ifdef RAYDEBUG printf("TRANSCOLOR:\n"); # endif if ((cinfo->trans.r < def.ithreshold) && (cinfo->trans.g < def.ithreshold) && (cinfo->trans.b < def.ithreshold)) return; # ifdef ROBUST if (line->type!=LINE) Error(INTERNAL_ERROR,602); if ((THEWORLD.globindex == 0) || (cinfo->index == 0)) Error(ZERO_INDEX,603); # endif if (line->flag) { /* inside an object going out ? */ index2 = cinfo->index/THEWORLD.globindex; /* find offset from prev position */ VecSubtract(&displacement,loc,&prevpos); /* measure distance */ glassdist = sqrt(DotProd(displacement,displacement)); } else { /* outside an object going in ? */ index2 = THEWORLD.globindex/cinfo->index; /* remember this position */ VectEQ(&prevpos,loc); } /* doesn't currently use new_line() call */ newline.type = LINE; newline.child = newline.nextobj = NULL; /* this ray starts from where we hit glass */ VectEQ(&(newline.loc),loc); VectEqZero(&toadd); VectNegate(&negvect1,&(line->vect1)); side1 = DotProd((*norm),negvect1); VectAddMult(&toadd,-side1,norm,(float)1,&negvect1); VectScale(&toadd,(1-index2)); VectorAdd(&(newline.vect1),&(line->vect1),&toadd); Normalize(&(newline.vect1)); maxtrans = MAX(MAX(cinfo->trans.r,cinfo->trans.g), cinfo->trans.b); /* if we were inside, now we're out */ newline.flag = !(line->flag); # ifdef TESTTRANS if (line->flag) printf("line TRUE\n"); if (newline.flag) printf("newline TRUE\n"); printf("Trans = %d %d %d\n",cinfo->trans.r, cinfo->trans.g, cinfo->trans.b); printf("index2 = %f\n",index2); printf("side1 = %f\n",side1); printf("Dot(toadd,norm) = %f\n", DotProd(toadd,(*norm))); printf("Dot(norm,negvect1) = %f\n", DotProd((*norm),negvect1)); printf("Dot(-norm,newvect) = %f\n", -DotProd((*norm),newline.vect1)); printf("inmult, maxtrans = %f %d\n",inmult,maxtrans); # endif multiplier = inmult * (float)maxtrans/(float)CNUM; Ray_Trace(&newline,&col1,multiplier); THEWORLD.refl_trans++; # ifdef TESTTRANS printf("Col1 = %d %d %d\n",col1.r,col1.g,col1.b); # endif color->r += col1.r; color->g += col1.g; color->b += col1.b; if (line->flag) { /* density effects? */ /* density uses x,y,z not r,g,b cuz its a floating point vector. */ attenuation.r = (int)(cinfo->density.x * glassdist * (float)(color->r)); attenuation.g = (int)(cinfo->density.y * glassdist * (float)(color->g)); attenuation.b = (int)(cinfo->density.z * glassdist * (float)(color->b)); /* don't remove more than original intensity! */ color->r -= MIN(attenuation.r,color->r); color->g -= MIN(attenuation.g,color->g); color->b -= MIN(attenuation.b,color->b); } } /********************************************************** Reflective color module Bounce ray off object and recursively ray trace. **********************************************************/ ReflectColor(color,cinfo,norm,loc,line,inmult) SVECT_PTR color; VECT_PTR norm,loc; OBJ_PTR line; CINFO_PTR cinfo; float inmult; { SVECTOR col1; OBJ_STRUCT newline; float multiplier; int maxmirror; # ifdef ROBUST if (line->type!=LINE) Error(INTERNAL_ERROR,604); # endif if ((cinfo->mirror.r < def.ithreshold) && (cinfo->mirror.g < def.ithreshold) && (cinfo->mirror.b < def.ithreshold)) return; newline.type = LINE; newline.child = newline.nextobj = NULL; VectEQ(&(newline.loc),loc); Reflect(&(newline.vect1),&(line->vect1),norm); maxmirror = MAX(MAX(cinfo->mirror.r,cinfo->mirror.g), cinfo->mirror.b); multiplier = inmult * (float)maxmirror/CNUM; Ray_Trace(&newline,&col1,multiplier); THEWORLD.refl_trans++; color->r+=(int)col1.r*(int)cinfo->mirror.r/CNUM; color->g+=(int)col1.g*(int)cinfo->mirror.g/CNUM; color->b+=(int)col1.b*(int)cinfo->mirror.b/CNUM; # ifdef RAYDEBUG printf("REFLECTCOLOR:\n"); printf(" col1 = %d %d %d\n",col1.r,col1.g,col1.b); # endif } /********************************************************** Computes sky color given line - interpolate between horizon and zenith to find color (user should dither the explitive out of the sky to compensate for lack of color resolution. **********************************************************/ SkyColor(line,color) OBJ_PTR line; SVECT_PTR color; { float length, horiz, zenith; # ifdef ROBUST if (line->type!=LINE) Error(INTERNAL_ERROR,605); # endif if (THEWORLD.sky==NULL) return; # ifdef ROBUST if (THEWORLD.sky->type != SKY) Error(INTERNAL_ERROR,606); # endif length = DotProd(line->vect1,line->vect1); zenith = sqr(line->vect1.y)/length; horiz = (sqr(line->vect1.x)+sqr(line->vect1.z))/length; color->r += (short)(zenith*THEWORLD.skycolor_zenith.r+ horiz *THEWORLD.skycolor_horiz.r); color->g += (short)(zenith*THEWORLD.skycolor_zenith.g+ horiz *THEWORLD.skycolor_horiz.g); color->b += (short)(zenith*THEWORLD.skycolor_zenith.b+ horiz *THEWORLD.skycolor_horiz.b); Dither(color,&((THEWORLD.sky)->cinfo)); # ifdef RAYDEBUG printf("SKYCOLOR:\n"); # endif } /********************************************************** Color dithering rountine - negative dither number will dither all three colors together - positive with perform separate color dithering on all colors. **********************************************************/ #define MINCOL 10 Dither(color,cinfo) SVECT_PTR color; CINFO_PTR cinfo; { register int r,g,b; int PsRand(); if (cinfo->dither==0) return; if (cinfo->dither>0) { r=PsRand(); g=PsRand(); b=PsRand(); if (color->rgbr += (short)(r*cinfo->dither/CNUM); color->g += (short)(g*cinfo->dither/CNUM); color->b += (short)(b*cinfo->dither/CNUM); } else { r=PsRand(); if ((color->r+color->g+color->b)>(3*MINCOL)) r=IABS(r); color->r += (short)(r*cinfo->dither/CNUM); color->g += (short)(r*cinfo->dither/CNUM); color->b += (short)(r*cinfo->dither/CNUM); } } /********************************************************** Returns pointer to object hit by ray. Parameters: CurrObj = root of object tree line = light ray MinT = parameter T for line/obj intersection sflag = if TRUE, stop on first intersection fflag = ALWAYS pass TRUE here. pos1 = position vector for object relative coords pos2 = position vector for object relative coords - changed 11 jun 88 to fix shadow routine - - changed 12 aug 88 to add light attenuation by glass. this is not done correctly, but is better than nothing. **********************************************************/ OBJ_PTR Ray_Hit(CurrObj,line,MinT,sflag,fflag,atten) OBJ_PTR line, CurrObj; float *MinT; short sflag,fflag; VECT_PTR atten; /* light attenuation by glass */ { static OBJ_PTR MinObj; VECTOR loc; OBJ_PTR obj; short collision; static short stop; float t; obj = CurrObj; if (fflag) { # ifdef ROBUST if (line->type!=LINE) Error(INTERNAL_ERROR,607); # endif *MinT=3e30; MinObj=NULL; stop=FALSE; } while (obj!=NULL && !stop) { /* check for object collisions */ collision = (*(ObjData[obj->type].ColTest))(line,obj,&t); if (collision && (obj->remove != NULL)) { FindPos(&loc,line,t); if (Find_Color(obj,obj->remove,&loc,NULL, 1.0, 1.0)) collision = FALSE; } THEWORLD.intersect_tests++; if (collision && (t>SMALL)) { /* did we hit something ? */ # ifdef RAYDEBUG printf("RAY_HIT: Collision t=%f\n",t); # endif if (obj->type!=BBOX) { /* if not BBOX */ THEWORLD.ray_intersects++; if (sflag && t<1) { /* did we hit a transparent object? */ /* PS - this is not right - fix it later */ if ((obj->cinfo.trans.r < def.ithreshold) && (obj->cinfo.trans.g < def.ithreshold) && (obj->cinfo.trans.b < def.ithreshold)) { stop = TRUE; MinObj = obj; return(obj); } else { /* attenuate light if transparent object. This is REALLY screwy and not at all correct, but it is the only simple way to do it. */ if (atten != NULL) { atten->x *= sqr(((float)obj->cinfo.trans.r) / (float)CNUM); atten->y *= sqr(((float)obj->cinfo.trans.g) / (float)CNUM); atten->z *= sqr(((float)obj->cinfo.trans.b) / (float)CNUM); } } } if ((!sflag) && (t<*MinT)) { /* nearest collision ? */ *MinT = t; /* if so, save it */ MinObj = obj; } } else { /* is bbox hit */ THEWORLD.bbox_intersects++; Ray_Hit(obj->child,line,MinT,sflag,FALSE,atten); } } obj=obj->nextobj; } return(MinObj); } /********************************************************** Performs ray tracing in line, fills color structure. Multiplier is a number by which the color output will be mulplied (0..1) so that we can tell when its useless to continue recursivly tracing rays. **********************************************************/ int Ray_Trace(line,color,multiplier) OBJ_PTR line; SVECT_PTR color; float multiplier; { float MinT, divisor; OBJ_PTR MinObj; CINFO cinfo; VECTOR MinLoc, MinNorm; # ifdef ROBUST if (line->type!=LINE) Error(INTERNAL_ERROR,608); # endif color->r = color->g = color->b = 0; /* check here if so little light is added that it doesn't matter */ if (multiplier < def.threshold) return(FALSE); MinObj=Ray_Hit(THEWORLD.stack,line,&MinT,FALSE,TRUE,NULL); if (MinObj!=NULL) { FindPos(&MinLoc,line,MinT); # ifdef RAYDEBUG printf("RAY_TRACE: Collision, x,y,z = %f %f %f\n", MinLoc.x, MinLoc.y, MinLoc.z); # endif /* Find object normal vector */ (*(ObjData[MinObj->type].FindNorm))(&MinNorm,MinObj,&MinLoc); if (DotProd(MinNorm,(line->vect1)) >0) { /* reverse */ MinNorm.x = -MinNorm.x; /* normal if */ MinNorm.y = -MinNorm.y; /* necessary */ MinNorm.z = -MinNorm.z; } /* Find colinfo */ Find_Color(MinObj, MinObj->pattern, &MinLoc, &cinfo, MinObj->xmult, MinObj->ymult ); AmbColor(color,&cinfo,&MinNorm,&MinLoc); DiffColor(color,&cinfo,&MinNorm,&MinLoc,line); TransColor(color,&cinfo,&MinNorm,&MinLoc,line,multiplier); ReflectColor(color,&cinfo,&MinNorm,&MinLoc,line,multiplier); Dither(color,&cinfo); } else { SkyColor(line,color); } if (color->r > CNUM || color->g > CNUM || color->b > CNUM) { divisor = (float)MAX(MAX(color->r,color->g),color->b) / (float)CNUM; color->r = (float)color->r/divisor; color->g = (float)color->g/divisor; color->b = (float)color->b/divisor; } return(MinObj!=NULL); } /********************************************************** Generates line for a given x,y pixel position This fn needs a little work, as currently it produces some distortion around the edge of the screen. **********************************************************/ PixelLine(x, y, line) /* generate equation for a line */ int x,y; OBJ_PTR line; { register float xf, yf; # ifdef ROBUST if (line->type!=LINE) Error(INTERNAL_ERROR,609); # endif xf=((float)(CENTERX-x))/(THEWORLD.flength*ASPECT); yf=((float)(CENTERY-y))/(THEWORLD.flength); VectEQ(&(line->loc),&(THEWORLD.observer->loc)); VectEQ(&(line->vect1),&(THEWORLD.observer->vect1)); VectAddMult(&(line->vect1),xf,&(THEWORLD.obsright), yf,&(THEWORLD.obsup)); line->flag = FALSE; /* redundant? */ } /********************************************************** Not yet implimented **********************************************************/ Region_Trace(xbegin,xend,ybegin,yend) int xbegin, xend, ybegin, yend; { int x,y; } /********************************************************** Sends scan line color data to file - file format is: BYTE 0 : low byte of x resolution BYTE 1 : high byte of x resolution BYTE 2 : low byte of y resolution BYTE 3 : high byte of y resolution Then, for each scan line, the line number is listed (2 bytes), followed by the r,g, and b bytes. **********************************************************/ Dump_Line(lineno,r,g,b) short r[],g[],b[]; int lineno; { int x; #ifndef RAYDEBUG /** line number **/ fputc(((unsigned char)(lineno&(0xff))),THEWORLD.filept); fputc(((unsigned char)(lineno>>8)), THEWORLD.filept); for (x=0; x