/* ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º º º Recursive Ray Tracing Program º º º º Renders Reflective And Transmissive Objects º º º º Uses Spheres, Prisms and Tetrahedrons for Bounding Objects º º º º Creates 160x100 16-bit Animation Sequences º º º º (c) 1990, 1991 Christopher D. Watkins º º º º 'C' conversion by Larry Sharp º º º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ */ #include "stdio.h" #include "stdlib.h" #include "dos.h" #include "conio.h" #include "malloc.h" #include "mem.h" #include "math.h" #include "string.h" #include "defs.h" #include "globals.h" #include "mathb.h" #include "graphb.h" /* *********************************************************************** * * * Declare Constants and Variables * * * *********************************************************************** */ typedef char Name[32]; Word ScanXRes; /* Stats */ Word ScanYRes; float XAspDivFocLen; float YAspDivFocLen; Word Xo, Yo; Word CenterX, CenterY; TDA ViewVec; Byte NumberOfFrames; TDA LoclWgt; /* Environment */ TDA ReflWgt; TDA TranWgt; TDA MinWgt; TDA MaxWgt; Byte MaxDepth; Boolean LampReflects; /* Lamps */ TDA LampRefl; float DistEffect; float FocalLength; /* Observer */ TDA ObsPos; float ObsRotate; float ObsTilt; TDA ViewDir; TDA ViewU, ViewV; TDA HorCol; /* Sky Horizon to Zenith Coloration */ TDA ZenCol; Boolean Clouds; Boolean SkyExists; TDA Tile1; /* Checkerboard tiling coloration */ TDA Tile2; float Tile; float OceanWaveAmpl; float OceanWavePhase; float PoolWaveAmpl; float PoolWavePhase; float PoolWaveXPos; float PoolWaveYPos; float WaveAmpl; float WavePhase; float WaveXPos; float WaveYPos; float WaveZPos; Boolean GroundExists; TDA TetraExtent1; TDA TetraExtent2; TDA TetraExtent3; TDA TetraExtent4; Boolean BoundingSphereTest; Boolean BoundingPrismTest; Boolean BoundingTetraTest; #define MaxMaterial 20 /* Maximum Types of Materials */ typedef struct { Name MType; Name Textur; TDA AmbRfl; TDA DifRfl; TDA SpcRfl; float Gloss; TDA Trans; float Index; } MaterialList; static MaterialList far *Matl; #define MaxTexture 10 /* Maximum Number of Textures */ #define Smooth 1 /* Texture Number */ #define Checker 2 #define Grit 3 #define Marble 4 #define Wood 5 #define Sheetrock 6 #define Particle 7 #define OceanWaves 8 #define PoolWaves 9 #define Waves 10 #define MaxShapeType 8 /* Maximum Number of Shape Types */ #define Ground 0 /* Ground is Shape Type 0 - a high speed calc */ #define Lamp 1 /* Shape Type Number */ #define Triangle 2 #define Parallelogram 3 #define Circles 4 #define Ring 5 #define Sphere 6 #define Cone 7 #define Cylinder 8 #define MaxLamp 10 #define MaxTriangle 512 /* Max Count of Objects for any one Shape Type */ #define MaxParallelogram 50 #define MaxCircles 50 #define MaxRing 50 #define MaxSphere 256 #define MaxCone 50 #define MaxCylinder 50 typedef struct { Byte MtlNum; Byte TexNum; } GroundList; typedef struct { TDA Loc; float Rad; float RadSqr; TDA Intens; } LampList; typedef struct { TDA Loc; TDA v1; TDA v2; TDA Norm; float NdotLoc; Byte MtlNum; Byte TexNum; } TriangleList; typedef struct { TDA Loc; TDA v1; TDA v2; TDA Norm; float NdotLoc; Byte MtlNum; Byte TexNum; } ParallelogramList; typedef struct { TDA Loc; TDA v1; TDA v2; TDA Norm; float NdotLoc; float Radius; Byte MtlNum; Byte TexNum; } CircleList; typedef struct { TDA Loc; TDA v1; TDA v2; TDA Norm; float NdotLoc; float Rad1; float Rad2; Byte MtlNum; Byte TexNum; } RingList; typedef struct { TDA Loc; /* special case quadratic */ float Rad; float RadSqr; Byte MtlNum; Byte TexNum; } SphereList; typedef struct { TDA BaseLoc; float BaseRad; float BaseD; TDA ApexLoc; float ApexRad; TDA U, V, W; /* vector along cone axis */ float Height; float Slope; float MinD; float MaxD; Boolean InSNrm; /* inward facing surface normal */ Byte MtlNum; Byte TexNum; } QuadraticList; typedef QuadraticList ConeList; typedef QuadraticList CylinderList; int ObjCnt[MaxShapeType+1]; GroundList Gnd; static LampList far *Lmp; static TriangleList far *Tri; static ParallelogramList far *Para; static CircleList far *Cir; static RingList far *Rng; static SphereList far *Sphr; static ConeList far *Con; static CylinderList far *Cyl; FILE *DiskFile; FILE *TextDiskFile; /* *********************************************************************** * * * Pixel Buffer for a 160 x 100 Frame of Animation * * * *********************************************************************** */ static Byte far *Red_Plane; /* Red Components of Pixels */ static Byte far *Green_Plane; /* Green Components of Pixels */ static Byte far *Blue_Plane; /* Blue Components of Pixels */ #define xy (yc*160)+xc void PutGrayScalePixel(Word xc, Word yc, TDIA Intens) { Byte Col; Red_Plane[xy]=(Intens[0]&255)>>2; Green_Plane[xy]=(Intens[1]&255)>>2; Blue_Plane[xy]=(Intens[2]&255)>>2; Col=((Intens[0]+Intens[1]+Intens[2])/3)>>2; Plot(xc, yc, Col); } void GrayScalePixel(Word xc, Word yc, TDIA Intens) { Byte Col; Col=((Intens[0]+Intens[1]+Intens[2])/3)>>2; Plot(xc, yc, Col); } void Clear_Planes() { _fmemset(Red_Plane, 0, 16000); _fmemset(Green_Plane, 0, 16000); _fmemset(Blue_Plane, 0, 16000); } /* *********************************************************************** * * * Clear all Variables * * * *********************************************************************** ClearMemory - clear all variables */ void ClearQuadratic(QuadraticList *List) { VecNull(List->BaseLoc); List->BaseRad=0.0; List->BaseD=0.0; VecNull(List->ApexLoc); List->ApexRad=0.0; VecNull(List->U); VecNull(List->V); VecNull(List->W); List->Height=0.0; List->Slope=0.0; List->MinD=0.0; List->MaxD=0.0; List->InSNrm=false; List->MtlNum=0; List->TexNum=0; } void ClearMemory() { int i; VecNull(LoclWgt); VecNull(ReflWgt); VecNull(TranWgt); VecNull(MinWgt); VecNull(MaxWgt); MaxDepth=0; LampReflects=false; /* will the surface of a lamp reflect light? */ VecNull(LampRefl); /* percentage of light a lamp reflects */ DistEffect=0.0; /* percentage of distance effect on lamp */ FocalLength=0.0; VecNull(ObsPos); ObsRotate=0.0; ObsTilt=0.0; VecNull(ViewDir); VecNull(ViewU); VecNull(ViewV); VecNull(HorCol); VecNull(ZenCol); Clouds=false; SkyExists=false; VecNull(Tile1); VecNull(Tile2); Tile=0.0; OceanWaveAmpl=0.0; OceanWavePhase=0.0; PoolWaveAmpl=0.0; PoolWavePhase=0.0; PoolWaveXPos=0.0; PoolWaveYPos=0.0; WaveAmpl=0.0; WavePhase=0.0; WaveXPos=0.0; WaveYPos=0.0; WaveZPos=0.0; GroundExists=false; VecNull(TetraExtent1); VecNull(TetraExtent2); VecNull(TetraExtent3); VecNull(TetraExtent4); BoundingSphereTest=false; BoundingPrismTest=false; BoundingTetraTest=false; for(i=0; i<=MaxMaterial; i++) { strcpy(Matl[i].MType, ""); strcpy(Matl[i].Textur, ""); VecNull(Matl[i].AmbRfl); VecNull(Matl[i].DifRfl); VecNull(Matl[i].SpcRfl); Matl[i].Gloss=0.0; VecNull(Matl[i].Trans); Matl[i].Index=0.0; } for(i=0; i<=MaxShapeType; i++) ObjCnt[i]=0; Gnd.MtlNum=0; Gnd.TexNum=0; for(i=0; i<=MaxLamp; i++) { VecNull(Lmp[i].Loc); Lmp[i].Rad=0.0; Lmp[i].RadSqr=0.0; VecNull(Lmp[i].Intens); } for(i=0; i<=MaxTriangle; i++) { VecNull(Tri[i].Loc); VecNull(Tri[i].v1); VecNull(Tri[i].v2); VecNull(Tri[i].Norm); Tri[i].NdotLoc=0.0; Tri[i].MtlNum=0; Tri[i].TexNum=0; } for(i=0; i<=MaxParallelogram; i++) { VecNull(Para[i].Loc); VecNull(Para[i].v1); VecNull(Para[i].v2); VecNull(Para[i].Norm); Para[i].NdotLoc=0.0; Para[i].MtlNum=0; Para[i].TexNum=0; } for(i=0; i<=MaxCircles; i++) { VecNull(Cir[i].Loc); VecNull(Cir[i].v1); VecNull(Cir[i].v2); VecNull(Cir[i].Norm); Cir[i].NdotLoc=0.0; Cir[i].Radius=0.0; Cir[i].MtlNum=0; Cir[i].TexNum=0; } for(i=0; i<=MaxRing; i++) { VecNull(Rng[i].Loc); VecNull(Rng[i].v1); VecNull(Rng[i].v2); VecNull(Rng[i].Norm); Rng[i].NdotLoc=0.0; Rng[i].Rad1=0.0; Rng[i].Rad2=0.0; Rng[i].MtlNum=0; Rng[i].TexNum=0; } for(i=0; i<=MaxSphere; i++) { VecNull(Sphr[i].Loc); Sphr[i].Rad=0.0; Sphr[i].RadSqr=0.0; Sphr[i].MtlNum=0; Sphr[i].TexNum=0; } for(i=0; i<=MaxCone; i++) ClearQuadratic(&Con[i]); for(i=0; i<=MaxCylinder; i++) ClearQuadratic(&Cyl[i]); } /* *********************************************************************** * * * Load an *.RT File * * * *********************************************************************** */ char Buf1[256], Buf2[256], Buf3[256], Buf4[256], Buf5[256]; int dummy; Name MtlName; FILE *InFile; void Clear_Buffers() { strset(Buf1, 0); strset(Buf2, 0); strset(Buf3, 0); strset(Buf4, 0); strset(Buf5, 0); } void LoadTDA(TDA A) { Clear_Buffers(); fscanf(InFile, "%s %s %s %s %s", Buf1, Buf2, Buf3, Buf4, Buf5); A[0]=atof(Buf3); A[1]=atof(Buf4); A[2]=atof(Buf5); } void LoadReal(float *a) { Clear_Buffers(); fscanf(InFile, "%s %s %s", Buf1, Buf2, Buf3); *a=atof(Buf3); } void LoadInteger(int *a) { Clear_Buffers(); fscanf(InFile, "%s %s %s", Buf1, Buf2, Buf3); *a=atoi(Buf3); } void LoadWord(Word *a) { Clear_Buffers(); fscanf(InFile, "%s %s %s", Buf1, Buf2, Buf3); *a=atoi(Buf3); } void LoadByte(Byte *a) { Clear_Buffers(); fscanf(InFile, "%s %s %s", Buf1, Buf2, Buf3); *a=atoi(Buf3); } void LoadText(Name a) { Clear_Buffers(); fscanf(InFile, "%s %s %s", Buf1, Buf2, Buf3); strcpy(a, Buf3); } Boolean LoadBoolean() { Name a; LoadText(a); if((a[0]=='T') || (a[0]=='t')) return(true); else return(false); } void LoadRTHeader() { Byte t, cnt; XRes=0; YRes=0; ScanXRes=0; ScanYRes=0; NumberOfFrames=0; cnt=0; do { Clear_Buffers(); fscanf(InFile, "%s", Buf1); if(!strcmp(Buf1, "STATS")) { LoadWord(&ScanXRes); LoadWord(&ScanYRes); ++cnt; } if(!strcmp(Buf1, "FRAMES")) { LoadByte(&NumberOfFrames); ++cnt; } } while(cnt<2); } #define Small 1E-01 /* Constants for removal of precision error effects */ TDA PrecCor={1.0, 1.0, 1.0}; Byte MtlCount; /* Number of Materials Loaded */ void GetViewDir(float Angl, float Tilt, TDA View, TDA U, TDA V) { float Phi, Theta; float x, y, z; Phi=Radians(Angl); Theta=Radians(Tilt); x=cos(Theta)*sin(Phi); y=cos(Theta)*cos(Phi); z=-sin(Theta); Vec(x, y, z, View); x=cos(Phi); y=-sin(Phi); z=0.0; Vec(x, y, z, U); x=sin(Theta)*sin(Phi); y=sin(Theta)*cos(Phi); z=cos(Theta); Vec(x, y, z, V); } void GetMatlNum(char Mat[], Byte *MatNum) { Byte i; /* Associate Materials to Objects */ for(i=1; i<=MtlCount; i++) { if(!strcmp(Matl[i].MType, Mat)) *MatNum=i; } } void GetTexNum(char Tex[], Byte *TexNum) { if(!strcmp(Tex, "SMOOTH")) *TexNum=Smooth; else if(!strcmp(Tex, "CHECKER")) *TexNum=Checker; else if(!strcmp(Tex, "GRIT")) *TexNum=Grit; else if(!strcmp(Tex, "MARBLE")) *TexNum=Marble; else if(!strcmp(Tex, "WOOD")) *TexNum=Wood; else if(!strcmp(Tex, "SHEETROCK")) *TexNum=Sheetrock; else if(!strcmp(Tex, "PARTICLE")) *TexNum=Particle; else if(!strcmp(Tex, "OCEANWAVES")) *TexNum=OceanWaves; else if(!strcmp(Tex, "POOLWAVES")) *TexNum=PoolWaves; else if(!strcmp(Tex, "WAVES")) *TexNum=Waves; } void OrientQuadratic(QuadraticList *List) { TDA Temp; float RTmp; /* Inward facing normal */ if((List->BaseRad<0.0) || (List->ApexRad<0.0)) { List->BaseRad=fabs(List->BaseRad); List->ApexRad=fabs(List->ApexRad); List->InSNrm=true; } else List->InSNrm=false; VecSub(List->ApexLoc, List->BaseLoc, List->W); List->Height=VecLen(List->W); VecNormalize(List->W); List->Slope=(List->ApexRad-List->BaseRad)/List->Height; List->BaseD=-VecDot(List->BaseLoc, List->W); Vec(0.0, 0.0, 1.0, Temp); RTmp=fabs(fabs(VecDot(Temp, List->W))-1.0); if(RTmpW, Temp, List->U); VecCross(List->U, List->W, List->V); VecNormalize(List->U); VecNormalize(List->V); List->MinD=VecDot(List->W, List->BaseLoc); List->MaxD=VecDot(List->W, List->ApexLoc); if(List->MaxDMinD) { RTmp=List->MaxD; List->MaxD=List->MinD; List->MinD=RTmp; } } void GetDataForFrame() { float Radial, Hgt; TDA ShapeLoc, TempLoc; TDA vec1, vec2, vec3; TDA pt1, pt2, pt3, pt4; Byte MtlNumber, TexNumber; MtlCount=0; do { Clear_Buffers(); fscanf(InFile, "%s", Buf1); if(!strcmp(Buf1, "ENVIRONMENT")) { LoadTDA(LoclWgt); LoadTDA(ReflWgt); LoadTDA(TranWgt); LoadTDA(MinWgt); LoadTDA(MaxWgt); LoadByte(&MaxDepth); } if(!strcmp(Buf1, "LAMPS")) { LampReflects=LoadBoolean(); LoadTDA(LampRefl); LoadReal(&DistEffect); } if(!strcmp(Buf1, "OBSERVER")) { LoadReal(&FocalLength); LoadTDA(ObsPos); LoadReal(&ObsRotate); LoadReal(&ObsTilt); GetViewDir(ObsRotate, ObsTilt, ViewDir, ViewU, ViewV); } if(!strcmp(Buf1, "SKY")) { LoadTDA(HorCol); LoadTDA(ZenCol); Clouds=LoadBoolean(); SkyExists=true; } if(!(strcmp(Buf1, "MATERIAL"))) { MtlCount+=1; LoadText(Matl[MtlCount].MType); LoadText(Matl[MtlCount].Textur); LoadTDA(Matl[MtlCount].AmbRfl); LoadTDA(Matl[MtlCount].DifRfl); LoadTDA(Matl[MtlCount].SpcRfl); LoadReal(&Matl[MtlCount].Gloss); LoadTDA(Matl[MtlCount].Trans); LoadReal(&Matl[MtlCount].Index); if(!strcmp(Matl[MtlCount].Textur, "CHECKER")) { LoadTDA(Tile1); LoadTDA(Tile2); LoadReal(&Tile); } if(!strcmp(Matl[MtlCount].Textur, "OCEANWAVES")) { LoadReal(&OceanWaveAmpl); LoadReal(&OceanWavePhase); } if(!strcmp(Matl[MtlCount].Textur, "POOLWAVES")) { LoadReal(&PoolWaveAmpl); LoadReal(&PoolWavePhase); LoadReal(&PoolWaveXPos); LoadReal(&PoolWaveYPos); } if(!strcmp(Matl[MtlCount].Textur, "WAVES")) { LoadReal(&WaveAmpl); LoadReal(&WavePhase); LoadReal(&WaveXPos); LoadReal(&WaveYPos); LoadReal(&WaveZPos); } } if(!strcmp(Buf1, "GROUND")) { GroundExists=true; ++ObjCnt[Ground]; LoadText(MtlName); GetMatlNum(MtlName, &Gnd.MtlNum); GetTexNum(Matl[Gnd.MtlNum].Textur, &Gnd.TexNum); } if(!strcmp(Buf1, "LAMP")) { ++ObjCnt[Lamp]; LoadTDA(Lmp[ObjCnt[Lamp]].Loc); LoadReal(&Lmp[ObjCnt[Lamp]].Rad); Lmp[ObjCnt[Lamp]].RadSqr=SqrFP(Lmp[ObjCnt[Lamp]].Rad); LoadTDA(Lmp[ObjCnt[Lamp]].Intens); } if(!strcmp(Buf1, "TRIANGLE")) { ++ObjCnt[Triangle]; LoadTDA(Tri[ObjCnt[Triangle]].Loc); LoadTDA(Tri[ObjCnt[Triangle]].v1); LoadTDA(Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); LoadText(MtlName); GetMatlNum(MtlName, &Tri[ObjCnt[Triangle]].MtlNum); GetTexNum(Matl[Tri[ObjCnt[Triangle]].MtlNum].Textur, &Tri[ObjCnt[Triangle]].TexNum); } if(!strcmp(Buf1, "PARALLELOGRAM")) { ++ObjCnt[Parallelogram]; LoadTDA(Para[ObjCnt[Parallelogram]].Loc); LoadTDA(Para[ObjCnt[Parallelogram]].v1); LoadTDA(Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); LoadText(MtlName); GetMatlNum(MtlName, &Para[ObjCnt[Parallelogram]].MtlNum); GetTexNum(Matl[Para[ObjCnt[Parallelogram]].MtlNum].Textur, &Para[ObjCnt[Parallelogram]].TexNum); } if(!strcmp(Buf1, "CIRCLE")) { ++ObjCnt[Circles]; LoadTDA(Cir[ObjCnt[Circles]].Loc); LoadTDA(Cir[ObjCnt[Circles]].v1); VecNormalize(Cir[ObjCnt[Circles]].v1); LoadTDA(Cir[ObjCnt[Circles]].v2); VecNormalize(Cir[ObjCnt[Circles]].v2); VecCross(Cir[ObjCnt[Circles]].v1, Cir[ObjCnt[Circles]].v2, Cir[ObjCnt[Circles]].Norm); VecNormalize(Cir[ObjCnt[Circles]].Norm); Cir[ObjCnt[Circles]].NdotLoc=VecDot(Cir[ObjCnt[Circles]].Norm, Cir[ObjCnt[Circles]].Loc); LoadReal(&Cir[ObjCnt[Circles]].Radius); LoadText(MtlName); GetMatlNum(MtlName, &Cir[ObjCnt[Circles]].MtlNum); GetTexNum(Matl[Cir[ObjCnt[Circles]].MtlNum].Textur, &Cir[ObjCnt[Circles]].TexNum); } if(!strcmp(Buf1, "RING")) { ++ObjCnt[Ring]; LoadTDA(Rng[ObjCnt[Ring]].Loc); LoadTDA(Rng[ObjCnt[Ring]].v1); VecNormalize(Rng[ObjCnt[Ring]].v1); LoadTDA(Rng[ObjCnt[Ring]].v2); VecNormalize(Rng[ObjCnt[Ring]].v2); VecCross(Rng[ObjCnt[Ring]].v1, Rng[ObjCnt[Ring]].v2, Rng[ObjCnt[Ring]].Norm); VecNormalize(Rng[ObjCnt[Ring]].Norm); Rng[ObjCnt[Ring]].NdotLoc=VecDot(Rng[ObjCnt[Ring]].Norm, Rng[ObjCnt[Ring]].Loc); LoadReal(&Rng[ObjCnt[Ring]].Rad1); LoadReal(&Rng[ObjCnt[Ring]].Rad2); LoadText(MtlName); GetMatlNum(MtlName, &Rng[ObjCnt[Ring]].MtlNum); GetTexNum(Matl[Rng[ObjCnt[Ring]].MtlNum].Textur, &Rng[ObjCnt[Ring]].TexNum); } if(!strcmp(Buf1, "SPHERE")) { ++ObjCnt[Sphere]; LoadTDA(Sphr[ObjCnt[Sphere]].Loc); LoadReal(&Sphr[ObjCnt[Sphere]].Rad); Sphr[ObjCnt[Sphere]].RadSqr=SqrFP(Sphr[ObjCnt[Sphere]].Rad); LoadText(MtlName); GetMatlNum(MtlName, &Sphr[ObjCnt[Sphere]].MtlNum); GetTexNum(Matl[Sphr[ObjCnt[Sphere]].MtlNum].Textur, &Sphr[ObjCnt[Sphere]].TexNum); } if(!strcmp(Buf1, "CONE")) { ++ObjCnt[Cone]; /* hýxý - 2rýyý + hýzý = 0 */ LoadTDA(Con[ObjCnt[Cone]].BaseLoc); LoadReal(&Con[ObjCnt[Cone]].BaseRad); LoadTDA(Con[ObjCnt[Cone]].ApexLoc); LoadReal(&Con[ObjCnt[Cone]].ApexRad); OrientQuadratic(&Con[ObjCnt[Cone]]); LoadText(MtlName); GetMatlNum(MtlName, &Con[ObjCnt[Cone]].MtlNum); GetTexNum(Matl[Con[ObjCnt[Cone]].MtlNum].Textur, &Con[ObjCnt[Cone]].TexNum); } if(!strcmp(Buf1, "CYLINDER")) { ++ObjCnt[Cylinder]; /* hýxý - 2rýyý + hýzý = 0 */ LoadTDA(Cyl[ObjCnt[Cylinder]].BaseLoc); LoadReal(&Cyl[ObjCnt[Cylinder]].BaseRad); LoadTDA(Cyl[ObjCnt[Cylinder]].ApexLoc); Cyl[ObjCnt[Cylinder]].ApexRad=Cyl[ObjCnt[Cylinder]].BaseRad; OrientQuadratic(&Cyl[ObjCnt[Cylinder]]); LoadText(MtlName); GetMatlNum(MtlName, &Cyl[ObjCnt[Cylinder]].MtlNum); GetTexNum(Matl[Cyl[ObjCnt[Cylinder]].MtlNum].Textur, &Cyl[ObjCnt[Cylinder]].TexNum); } if(!strcmp(Buf1, "BOX")) { LoadTDA(ShapeLoc); /* Constructed of 6 parallelograms */ LoadTDA(vec1); LoadTDA(vec2); LoadTDA(vec3); LoadText(MtlName); GetMatlNum(MtlName, &MtlNumber); GetTexNum(Matl[MtlNumber].Textur, &TexNumber); ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec1, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec3, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec3, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec1, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Vec(0.0, vec2[1], 0.0, TempLoc); VecAdd(TempLoc, Para[ObjCnt[Parallelogram]].Loc, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec3, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec2, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec2, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec3, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Vec(vec1[0], 0.0, 0.0, TempLoc); VecAdd(TempLoc, Para[ObjCnt[Parallelogram]].Loc, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec2, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec1, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec1, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec2, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Vec(0.0, 0.0, vec3[2], TempLoc); VecAdd(TempLoc, Para[ObjCnt[Parallelogram]].Loc, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; } if(!strcmp(Buf1, "PYRAMID")) { LoadTDA(ShapeLoc); /* Constructed of 1 parallelogram */ LoadTDA(vec1); /* and 4 triangles */ LoadTDA(vec2); LoadReal(&Hgt); LoadText(MtlName); GetMatlNum(MtlName, &MtlNumber); GetTexNum(Matl[MtlNumber].Textur, &TexNumber); ++ObjCnt[Parallelogram]; VecCopy(ShapeLoc, Para[ObjCnt[Parallelogram]].Loc); VecCopy(vec2, Para[ObjCnt[Parallelogram]].v1); VecCopy(vec1, Para[ObjCnt[Parallelogram]].v2); VecCross(Para[ObjCnt[Parallelogram]].v1, Para[ObjCnt[Parallelogram]].v2, Para[ObjCnt[Parallelogram]].Norm); VecNormalize(Para[ObjCnt[Parallelogram]].Norm); Para[ObjCnt[Parallelogram]].NdotLoc=VecDot(Para[ObjCnt[Parallelogram]].Norm, Para[ObjCnt[Parallelogram]].Loc); Para[ObjCnt[Parallelogram]].MtlNum=MtlNumber; Para[ObjCnt[Parallelogram]].TexNum=TexNumber; ++ObjCnt[Triangle]; VecCopy(ShapeLoc, Tri[ObjCnt[Triangle]].Loc); VecCopy(vec1, Tri[ObjCnt[Triangle]].v1); Vec(0.5*vec1[0], 0.5*vec2[1], Hgt, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; ++ObjCnt[Triangle]; Tri[ObjCnt[Triangle]].Loc[0]=ShapeLoc[0]+vec1[0]; Tri[ObjCnt[Triangle]].Loc[1]=ShapeLoc[1]+vec2[1]; Tri[ObjCnt[Triangle]].Loc[2]=ShapeLoc[2]; VecScalMult(-1.0, vec1, Tri[ObjCnt[Triangle]].v1); Vec(-0.5*vec1[0], -0.5*vec2[1], Hgt, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; ++ObjCnt[Triangle]; Tri[ObjCnt[Triangle]].Loc[0]=ShapeLoc[0]+vec1[0]; Tri[ObjCnt[Triangle]].Loc[1]=ShapeLoc[1]+vec2[1]; Tri[ObjCnt[Triangle]].Loc[2]=ShapeLoc[2]; Vec(-0.5*vec1[0], -0.5*vec2[1], Hgt, Tri[ObjCnt[Triangle]].v1); VecScalMult(-1.0, vec2, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; ++ObjCnt[Triangle]; VecCopy(ShapeLoc, Tri[ObjCnt[Triangle]].Loc); Vec(0.5*vec1[0], 0.5*vec2[1], Hgt, Tri[ObjCnt[Triangle]].v1); VecCopy(vec2, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; } if(!strcmp(Buf1, "TETRA")) { LoadTDA(ShapeLoc); /* Contructed of 4 triangles */ LoadReal(&Hgt); LoadReal(&Radial); LoadText(MtlName); GetMatlNum(MtlName, &MtlNumber); GetTexNum(Matl[MtlNumber].Textur, &TexNumber); Vec(ShapeLoc[0], ShapeLoc[1], ShapeLoc[2]+Hgt, pt1); Vec(ShapeLoc[0], ShapeLoc[1]+Radial, ShapeLoc[2], pt2); Vec(ShapeLoc[0]-Radial*0.707, ShapeLoc[1]-Radial*0.707, ShapeLoc[2], pt3); Vec(ShapeLoc[0]+Radial*0.707, ShapeLoc[1]-Radial*0.707, ShapeLoc[2], pt4); ++ObjCnt[Triangle]; VecCopy(pt3, Tri[ObjCnt[Triangle]].Loc); VecSub(pt1, pt3, Tri[ObjCnt[Triangle]].v1); VecSub(pt2, pt3, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; ++ObjCnt[Triangle]; VecCopy(pt2, Tri[ObjCnt[Triangle]].Loc); VecSub(pt1, pt2, Tri[ObjCnt[Triangle]].v1); VecSub(pt4, pt2, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; ++ObjCnt[Triangle]; VecCopy(pt4, Tri[ObjCnt[Triangle]].Loc); VecSub(pt1, pt4, Tri[ObjCnt[Triangle]].v1); VecSub(pt3, pt4, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; ++ObjCnt[Triangle]; VecCopy(pt3, Tri[ObjCnt[Triangle]].Loc); VecSub(pt2, pt3, Tri[ObjCnt[Triangle]].v1); VecSub(pt4, pt3, Tri[ObjCnt[Triangle]].v2); VecCross(Tri[ObjCnt[Triangle]].v1, Tri[ObjCnt[Triangle]].v2, Tri[ObjCnt[Triangle]].Norm); VecNormalize(Tri[ObjCnt[Triangle]].Norm); Tri[ObjCnt[Triangle]].NdotLoc=VecDot(Tri[ObjCnt[Triangle]].Norm, Tri[ObjCnt[Triangle]].Loc); Tri[ObjCnt[Triangle]].MtlNum=MtlNumber; Tri[ObjCnt[Triangle]].TexNum=TexNumber; } if(!strcmp(Buf1, "TETRAEXTENTS")) { LoadTDA(TetraExtent1); /* Extents for bounding tetrahedron */ LoadTDA(TetraExtent2); LoadTDA(TetraExtent3); LoadTDA(TetraExtent4); BoundingTetraTest=true; } if(!strcmp(Buf1, "BOUNDINGSPHERETEST")) BoundingSphereTest=true; if(!strcmp(Buf1, "BOUNDINGPRISMTEST")) BoundingPrismTest=true; } while(strcmp(Buf1, "ENDFRAME")); } /* *********************************************************************** * * * Calculate Directions of Reflected Rays * * * *********************************************************************** CalcDirOfReflRay - calculate the direction of a reflected ray CalcDirOfTranRay - calculate the direction of a transmitted ray */ void CalcDirOfReflRay(TDA Dir, TDA SrfNrm, TDA ReflRay) { float Tmp; Tmp=-2.0*VecDot(Dir, SrfNrm); VecAddScalMult(Tmp, SrfNrm, Dir, ReflRay); } void CalcDirOfTranRay(TDA Dir, TDA SrfNrm, Byte Mtl, TDA TranRay) { float ni=1.000; /* air */ float NdotV, nmult; /* N=SrfNrm V=Dir */ TDA cosV, sinT, temp; float lsinT, NdotT; float nt; /* Based on Snell's Law of Refraction n1 x sin(é1) = n2 x sin(é2) */ VecScalMult(-1.0, Dir, Dir); /* Flip for calculations */ NdotV=VecDot(SrfNrm, Dir); nt=Matl[Mtl].Index; if(NdotV>0.0) nmult=ni/nt; else nmult=nt; /* nt/ni where ni=1 */ VecScalMult(NdotV, SrfNrm, cosV); VecSub(cosV, Dir, temp); VecScalMult(nmult, temp, sinT); lsinT=VecDot(sinT, sinT); if(lsinT>=1.0) VecNull(TranRay); /* internal reflections */ else { NdotT=sqrt(1.0-lsinT); if(NdotV<0.0) NdotT=-NdotT; VecScalMult(NdotT, SrfNrm, temp); VecSub(sinT, temp, TranRay); } } void QuadraticSrfNrm(TDA IntrPt, TDA SrfNrm, QuadraticList *List) { float t; TDA ProjPt;; t=-(VecDot(IntrPt, List->W)+List->BaseD); /* Project IntrPt onto the */ VecAddScalMult(t, List->W, IntrPt, ProjPt); /* plane of the Base */ VecSub(ProjPt, List->BaseLoc, SrfNrm); /* The surface normal is a vector */ VecNormalize(SrfNrm); /* from BaseLoc through the point */ /* projected, plus slope times W */ VecAddScalMult(-List->Slope, List->W, SrfNrm, SrfNrm); VecNormalize(SrfNrm); if(List->InSNrm) /* Inward facing normal */ VecNegate(SrfNrm); } void GetSrfNrm(int Shp, int Obj, TDA IntrPt, TDA SrfNrm) { switch(Shp) { case Ground : Vec(0.0, 0.0, 1.0, SrfNrm); break; /* lamp = | IntrPt - Loc | */ case Lamp : VecSub(IntrPt, Lmp[Obj].Loc, SrfNrm); VecNormalize(SrfNrm); break; /* triangle = | v1 X v2 | */ case Triangle : VecCopy(Tri[Obj].Norm, SrfNrm); break; /* parallelogram = |v1 X v2 | */ case Parallelogram : VecCopy(Para[Obj].Norm, SrfNrm); break; /* circles = | v1 X v2 | */ case Circles : VecCopy(Cir[Obj].Norm, SrfNrm); break; /* ring = | v1 X v2 | */ case Ring : VecCopy(Rng[Obj].Norm, SrfNrm); break; /* sphere = | IntrPt - Loc | */ case Sphere : VecSub(IntrPt, Sphr[Obj].Loc, SrfNrm); VecNormalize(SrfNrm); break; case Cone : QuadraticSrfNrm(IntrPt, SrfNrm, &Con[Obj]); break; case Cylinder : QuadraticSrfNrm(IntrPt, SrfNrm, &Cyl[Obj]); break; } } /* *********************************************************************** * * * Intersection of Ray with Objects * * * *********************************************************************** GetIntrPt - find the intersection point given a point, direction and dist GetSrfNrm - find the surface normal given the point of intersection Intersect - determine if an object has been hit by a ray */ void GetIntrPt(TDA Pt, TDA Dir, float Dist, TDA IntrPt) { VecAddScalMult(Dist, Dir, Pt, IntrPt); } FDA gu, gv; Boolean EvenCrossings(Byte Sides) { Byte i, j; Word crossings; crossings=0; for(i=0; i=0)) || ((gv[j]<0) && (gv[i]>=0))) { if((gu[i]>=0) && (gu[j]>=0)) ++crossings; else { if((gu[i]>=0) || (gu[j]>=0)) { if((gu[i]-gv[i]*(gu[j]-gu[i])/(gv[j]-gv[i]))>0) ++crossings; } } } } if((crossings%2)==0) return(true); else return(false); } TDA delta; void SetUpTriangle(Byte p1, Byte p2, int Obj) { gu[0]=-delta[p1]; gv[0]=-delta[p2]; gu[1]=Tri[Obj].v1[p1]-delta[p1]; gv[1]=Tri[Obj].v1[p2]-delta[p2]; gu[2]=Tri[Obj].v2[p1]-delta[p1]; gv[2]=Tri[Obj].v2[p2]-delta[p2]; } void SetUpParallelogram(Byte p1, Byte p2, int Obj) { gu[0]=-delta[p1]; gv[0]=-delta[p2]; gu[1]=Para[Obj].v1[p1]-delta[p1]; gv[1]=Para[Obj].v1[p2]-delta[p2]; gu[2]=Para[Obj].v2[p1]+Para[Obj].v1[p1]-delta[p1]; gv[2]=Para[Obj].v2[p2]+Para[Obj].v1[p2]-delta[p2]; gu[3]=Para[Obj].v2[p1]-delta[p1]; gv[3]=Para[Obj].v2[p2]-delta[p2]; } float t1, t2; float QuadraticIntersectionCheck() { float intersection; if((!(t1>Small)) && (!(t2>Small))) intersection=-1.0; else { if(t1>t2) { if(t2Small) t2=t1; } intersection=t2; } return(intersection); } TDA IntrPoint, temp; float a, b, c, d; float disc, sroot; float t; float QuadraticIntersection(QuadraticList *List, TDA Pt, TDA Dir) { TDA NewPnt, NewDir; float SqrSlope; /* Parts of this conical-section intersection routine are based on */ /* ideas from Mark Terrence VandeWettering's MTV raytracer */ VecSub(Pt, List->BaseLoc, temp); /* Get the coordinates of the ray */ NewPnt[0]=VecDot(temp, List->U); /* origin in the objects space. */ NewPnt[1]=VecDot(temp, List->V); NewPnt[2]=VecDot(temp, List->W); NewDir[0]=VecDot(Dir, List->U); NewDir[1]=VecDot(Dir, List->V); NewDir[2]=VecDot(Dir, List->W); SqrSlope=SqrFP(List->Slope); a=SqrFP(NewDir[0])+SqrFP(NewDir[1])-SqrFP(NewDir[2])*SqrSlope; b=2.0*(NewPnt[0]*NewDir[0]+ NewPnt[1]*NewDir[1]- NewDir[2]*(NewPnt[2]*SqrSlope-List->BaseRad*List->Slope)); c=SqrFP(NewPnt[0])+SqrFP(NewPnt[1])-SqrFP(NewPnt[2]*List->Slope+List->BaseRad); if(a==0) { if(b==0) return(-1.0); t2=-c/b; if(t2t2) { /* make t1 the nearest root */ t=t1; t1=t2; t2=t; } } } } if(t1>Small) { /* Hit object */ GetIntrPt(Pt, Dir, t1, IntrPoint); d=VecDot(List->W, IntrPoint); if((!(dMinD)) && (!(d>List->MaxD))) return(t1); } if(t2>Small) { /* Hit object */ GetIntrPt(Pt, Dir, t2, IntrPoint); d=VecDot(List->W, IntrPoint); if((!(dMinD)) && (!(d>List->MaxD))) return(t2); } return(-1.0); } float Intersect(TDA Pt, TDA Dir, int Shp, int Obj) { float intersection; float rad, dot; float pos1, pos2; switch(Shp) { case Ground : if(Dir[2]==0.0) intersection=-1.0; else { t=-Pt[2]/Dir[2]; if(t>Small) intersection=t; else intersection=-1.0; } break; case Lamp : VecSub(Lmp[Obj].Loc, Pt, temp); b=VecDot(Dir, temp)*-2.0; c=VecDot(temp, temp)-Lmp[Obj].RadSqr; disc=SqrFP(b)-4.0*c; if(disc<=0.0) intersection=-1.0; else { sroot=sqrt(disc); t1=(-b-sroot)*0.5; t2=(-b+sroot)*0.5; intersection=QuadraticIntersectionCheck(); } break; case Triangle : dot=VecDot(Tri[Obj].Norm, Dir); if(fabs(dot)fabs(Tri[Obj].Norm[1])) && (fabs(Tri[Obj].Norm[0])>fabs(Tri[Obj].Norm[2]))) SetUpTriangle(1, 2, Obj); else { if(fabs(Tri[Obj].Norm[1])>=fabs(Tri[Obj].Norm[2])) SetUpTriangle(0, 2, Obj); else SetUpTriangle(0, 1, Obj); } } if(EvenCrossings(3)) intersection=-1.0; else intersection=t; break; case Parallelogram : dot=VecDot(Para[Obj].Norm, Dir); if(fabs(dot)fabs(Para[Obj].Norm[1])) && (fabs(Para[Obj].Norm[0])>fabs(Para[Obj].Norm[2]))) SetUpParallelogram(1, 2, Obj); else { if(fabs(Para[Obj].Norm[1])>=fabs(Para[Obj].Norm[2])) SetUpParallelogram(0, 2, Obj); else SetUpParallelogram(0, 1, Obj); } } if(EvenCrossings(4)) intersection=-1.0; else intersection=t; break; case Circles : dot=VecDot(Cir[Obj].Norm, Dir); if(fabs(dot)Cir[Obj].Radius) intersection=-1.0; else intersection=t; } break; case Ring : dot=VecDot(Rng[Obj].Norm, Dir); if(fabs(dot)Rng[Obj].Rad2)) intersection=-1.0; else intersection=t; } break; case Sphere : VecSub(Sphr[Obj].Loc, Pt, temp); b=VecDot(Dir, temp)*-2.0; c=VecDot(temp, temp)-Sphr[Obj].RadSqr; disc=SqrFP(b)-4.0*c; if(disc<=0.0) intersection=-1.0; else { sroot=sqrt(disc); t1=(-b-sroot)*0.5; t2=(-b+sroot)*0.5; intersection=QuadraticIntersectionCheck(); } break; case Cone : intersection=QuadraticIntersection(&Con[Obj], Pt, Dir); break; case Cylinder : intersection=QuadraticIntersection(&Cyl[Obj], Pt, Dir); break; } return(intersection); } /* *********************************************************************** * * * Initial Eye-to-Pixel Ray Calculation * * * *********************************************************************** GetInitialDir - calculation of initial eye-to-pixel ray */ void GetInitialDir(float i, float j, TDA Dir) { float x, y; TDA EyeToPixVec; x=(i-(float)CenterX)*XAspDivFocLen; y=((float)CenterY-j)*YAspDivFocLen; VecLinComb(x, ViewU, y, ViewV, EyeToPixVec); VecAdd(ViewVec, EyeToPixVec, Dir); VecNormalize(Dir); } /* *********************************************************************** * * * Bounding Objects Scheme to Reduce the Number of Intersection Tests * * * *********************************************************************** BoundingBoxes - find bounding box for an object GetMinimumAndMaximumPoints - find min and max points for all objects */ void QuadraticBound(QuadraticList *List, TDA Minimum, TDA Maximum) { TDA Qmin, Qmax; float MaxRad; VecMin(List->BaseLoc, List->ApexLoc, Qmin); VecMax(List->BaseLoc, List->ApexLoc, Qmax); MaxRad=MAX(List->BaseRad, List->ApexRad); Minimum[0]=Qmin[0]-MaxRad; Minimum[1]=Qmin[1]-MaxRad; Minimum[2]=Qmin[2]-MaxRad; Maximum[0]=Qmax[0]+MaxRad; Maximum[1]=Qmax[1]+MaxRad; Maximum[2]=Qmax[2]+MaxRad; } void BoundingBoxes(int Shp, int Obj, TDA Minimum, TDA Maximum) { TDA p2, p3, p4; switch(Shp) { case Triangle : VecAdd(Tri[Obj].Loc, Tri[Obj].v1, p2); VecAdd(Tri[Obj].Loc, Tri[Obj].v2, p3); Minimum[0]=MIN3(Tri[Obj].Loc[0], p2[0], p3[0]); Minimum[1]=MIN3(Tri[Obj].Loc[1], p2[1], p3[1]); Minimum[2]=MIN3(Tri[Obj].Loc[2], p2[2], p3[2]); Maximum[0]=MAX3(Tri[Obj].Loc[0], p2[0], p3[0]); Maximum[1]=MAX3(Tri[Obj].Loc[1], p2[1], p3[1]); Maximum[2]=MAX3(Tri[Obj].Loc[2], p2[2], p3[2]); break; case Parallelogram : VecAdd(Para[Obj].Loc, Para[Obj].v1, p2); VecAdd(Para[Obj].Loc, Para[Obj].v2, p3); VecAdd3(Para[Obj].Loc, Para[Obj].v1, Para[Obj].v2, p4); Minimum[0]=MIN4(Para[Obj].Loc[0], p2[0], p3[0], p4[0]); Minimum[1]=MIN4(Para[Obj].Loc[1], p2[1], p3[1], p4[1]); Minimum[2]=MIN4(Para[Obj].Loc[2], p2[2], p3[2], p4[2]); Maximum[0]=MAX4(Para[Obj].Loc[0], p2[0], p3[0], p4[0]); Maximum[1]=MAX4(Para[Obj].Loc[1], p2[1], p3[1], p4[1]); Maximum[2]=MAX4(Para[Obj].Loc[2], p2[2], p3[2], p4[2]); break; case Circles : Vec(-Cir[Obj].Radius, -Cir[Obj].Radius, -Cir[Obj].Radius, Minimum); Vec(Cir[Obj].Radius, Cir[Obj].Radius, Cir[Obj].Radius, Maximum); VecAdd(Minimum, Cir[Obj].Loc, Minimum); VecAdd(Maximum, Cir[Obj].Loc, Maximum); break; case Ring : Vec(-Rng[Obj].Rad2, -Rng[Obj].Rad2, -Rng[Obj].Rad2, Minimum); Vec(Rng[Obj].Rad2, Rng[Obj].Rad2, Rng[Obj].Rad2, Maximum); VecAdd(Minimum, Rng[Obj].Loc, Minimum); VecAdd(Maximum, Rng[Obj].Loc, Maximum); break; case Sphere : Vec(-Sphr[Obj].Rad, -Sphr[Obj].Rad, -Sphr[Obj].Rad, Minimum); Vec(Sphr[Obj].Rad, Sphr[Obj].Rad, Sphr[Obj].Rad, Maximum); VecAdd(Minimum, Sphr[Obj].Loc, Minimum); VecAdd(Maximum, Sphr[Obj].Loc, Maximum); break; case Cone : QuadraticBound(&Con[Obj], Minimum, Maximum); break; case Cylinder : QuadraticBound(&Cyl[Obj], Minimum, Maximum); break; } } void GetMinAndMaxPoints(TDA Minimum, TDA Maximum) { int ShapeNum, ObjectNum; TDA MinPt, MaxPt; VecNull(MinPt); VecNull(MaxPt); VecNull(Minimum); VecNull(Maximum); /* find min and max coords, don't include ground or lamps */ for(ShapeNum=2; ShapeNum<=MaxShapeType; ShapeNum++) { for(ObjectNum=1; ObjectNum<=ObjCnt[ShapeNum]; ObjectNum++) { BoundingBoxes(ShapeNum, ObjectNum, MinPt, MaxPt); if((ShapeNum==2) && (ObjectNum==1)) { VecCopy(MinPt, Minimum); VecCopy(MaxPt, Maximum); } else { VecMin(MinPt, Minimum, Minimum); VecMax(MaxPt, Maximum, Maximum); } } } /* decrease minimum and increase maximum to */ /* compensate for precision error */ VecSub(Minimum, PrecCor, Minimum); VecAdd(Maximum, PrecCor, Maximum); } /* *********************************************************************** * * * Bounding Sphere * * * *********************************************************************** CreateBoundingSphere - setup bounding sphere */ typedef struct{ TDA Center; float RadSqr; } SphereType; void CreateBoundingSphere(SphereType *sphere) { TDA Minimum, Maximum; TDA temp; GetMinAndMaxPoints(Minimum, Maximum); VecSub(Maximum, Minimum, temp); /* find center of bounding sphere */ VecScalMult(0.5, temp, temp); VecAdd(Minimum, temp, sphere->Center); sphere->RadSqr=VecDot(temp, temp); /* find square of the radius of the */ /* bounding sphere - note Sqr(Sqrt()) */ } /* *********************************************************************** * * * Bounding Prism * * * *********************************************************************** CreateBoundingPrism - setup bounding prism */ typedef struct{ TDA Loc; TDA v1; TDA v2; TDA Norm; float NdotLoc; } PrismFace; typedef PrismFace PrismType[7]; void CreateBoundingPrism(PrismType Prism) { Byte i, j; float dot[7]; TDA Minimum, Maximum; TDA vec1, vec2, vec3; TDA span; TDA PrismLoc, temploc; TDA InitDir; PrismFace tempface; float tempdot; GetMinAndMaxPoints(Minimum, Maximum); /* find the 3 vectors that represent the edges of the bounding Prism and the Prism's location */ VecNull(vec1); VecNull(vec2); VecNull(vec3); VecSub(Maximum, Minimum, span); Vec(span[0], 0.0, 0.0, vec1); Vec(0.0, span[1], 0.0, vec2); Vec(0.0, 0.0, span[2], vec3); VecCopy(Minimum, PrismLoc); /* find 6 parallelograms that make up the faces of the bounding Prism */ for(i=1; i<7; i++) { VecNull(Prism[i].Loc); VecNull(Prism[i].v1); VecNull(Prism[i].v2); VecNull(Prism[i].Norm); Prism[i].NdotLoc=0.0; } VecCopy(PrismLoc, Prism[1].Loc); VecCopy(vec1, Prism[1].v1); VecCopy(vec3, Prism[1].v2); VecCross(Prism[1].v1, Prism[1].v2, Prism[1].Norm); VecNormalize(Prism[1].Norm); Prism[1].NdotLoc=VecDot(Prism[1].Norm, Prism[1].Loc); VecCopy(PrismLoc, Prism[2].Loc); VecCopy(vec3, Prism[2].v1); VecCopy(vec1, Prism[2].v2); VecCross(Prism[2].v1, Prism[2].v2, Prism[2].Norm); VecNormalize(Prism[2].Norm); Vec(0.0, vec2[1], 0.0, temploc); VecAdd(temploc, Prism[2].Loc, Prism[2].Loc); Prism[2].NdotLoc=VecDot(Prism[2].Norm, Prism[2].Loc); VecCopy(PrismLoc, Prism[3].Loc); VecCopy(vec3, Prism[3].v1); VecCopy(vec2, Prism[3].v2); VecCross(Prism[3].v1, Prism[3].v2, Prism[3].Norm); VecNormalize(Prism[3].Norm); Prism[3].NdotLoc=VecDot(Prism[3].Norm, Prism[3].Loc); VecCopy(PrismLoc, Prism[4].Loc); VecCopy(vec2, Prism[4].v1); VecCopy(vec3, Prism[4].v2); VecCross(Prism[4].v1, Prism[4].v2, Prism[4].Norm); VecNormalize(Prism[4].Norm); Vec(vec1[0], 0.0, 0.0, temploc); VecAdd(temploc, Prism[4].Loc, Prism[4].Loc); Prism[4].NdotLoc=VecDot(Prism[4].Norm, Prism[4].Loc); VecCopy(PrismLoc, Prism[5].Loc); VecCopy(vec2, Prism[5].v1); VecCopy(vec1, Prism[5].v2); VecCross(Prism[5].v1, Prism[5].v2, Prism[5].Norm); VecNormalize(Prism[5].Norm); Prism[5].NdotLoc=VecDot(Prism[5].Norm, Prism[5].Loc); VecCopy(PrismLoc, Prism[6].Loc); VecCopy(vec1, Prism[6].v1); VecCopy(vec2, Prism[6].v2); VecCross(Prism[6].v1, Prism[6].v2, Prism[6].Norm); VecNormalize(Prism[6].Norm); Vec(0.0, 0.0, vec3[2], temploc); VecAdd(temploc, Prism[6].Loc, Prism[6].Loc); Prism[6].NdotLoc=VecDot(Prism[6].Norm, Prism[6].Loc); /* Order Prisms for the intersection testing based on the direction of the initial eye ray -> this is the creation of the priority queue */ GetInitialDir((float)ScanXRes/2, (float)ScanYRes/2, InitDir); for(i=1; i<7; i++) dot[i]=VecDot(InitDir, Prism[i].Norm); /* Bubble sort based on closest opposite direction since rays approach closest opposite direction is when dot approaches -1.0 */ for(j=1; j<7; j++) { for(i=1; i<6; i++) { if(dot[i]>dot[i+1]) { memcpy(&tempface, &Prism[i], sizeof(PrismFace)); /* swap */ memcpy(&Prism[i], &Prism[i+1], sizeof(PrismFace)); memcpy(&Prism[i+1], &tempface, sizeof(PrismFace)); tempdot=dot[i]; dot[i]=dot[i+1]; dot[i+1]=tempdot; } } } } /* *********************************************************************** * * * Bounding Tetrahedron * * * *********************************************************************** CreateBoundingTetra - setup bounding tetrahedron from loaded TetraExtents */ typedef struct{ TDA Loc; TDA v1; TDA v2; TDA Norm; float NdotLoc; } TetraFace; typedef TetraFace TetraType[5]; void CreateBoundingTetra(TetraType Tetra) { Byte i, j; float dot[5]; TDA TetraLoc; TDA InitDir; TDA temp; TetraFace tempface; float tempdot; /* Loaded are 4 extents for a bounding tetrahedron - TetraExtent */ /* increase size of extents to compensate for precision error */ VecScalMult(1.1, TetraExtent1, TetraExtent1); VecScalMult(1.1, TetraExtent2, TetraExtent2); VecScalMult(1.1, TetraExtent3, TetraExtent3); VecScalMult(1.1, TetraExtent4, TetraExtent4); /* find 4 triangles that make up the faces of the bounding Tetrahedron */ for(i=1; i<5; i++) { VecNull(Tetra[i].Loc); VecNull(Tetra[i].v1); VecNull(Tetra[i].v2); VecNull(Tetra[i].Norm); Tetra[i].NdotLoc=0.0; } VecAdd3(TetraExtent1, TetraExtent2, TetraExtent3, temp); VecScalMult(1.0/3.0, temp, TetraLoc); VecAdd(TetraLoc, TetraExtent1, Tetra[1].Loc); VecSub(TetraExtent2, TetraExtent1, Tetra[1].v1); VecSub(TetraExtent4, TetraExtent1, Tetra[1].v2); VecCross(Tetra[1].v1, Tetra[1].v2, Tetra[1].Norm); VecNormalize(Tetra[1].Norm); Tetra[1].NdotLoc=VecDot(Tetra[1].Norm, Tetra[1].Loc); VecAdd(TetraLoc, TetraExtent2, Tetra[2].Loc); VecSub(TetraExtent3, TetraExtent2, Tetra[2].v1); VecSub(TetraExtent4, TetraExtent2, Tetra[2].v2); VecCross(Tetra[2].v1, Tetra[2].v2, Tetra[2].Norm); VecNormalize(Tetra[2].Norm); Tetra[2].NdotLoc=VecDot(Tetra[2].Norm, Tetra[2].Loc); VecAdd(TetraLoc, TetraExtent3, Tetra[3].Loc); VecSub(TetraExtent1, TetraExtent3, Tetra[3].v1); VecSub(TetraExtent4, TetraExtent3, Tetra[3].v2); VecCross(Tetra[3].v1, Tetra[3].v2, Tetra[3].Norm); VecNormalize(Tetra[3].Norm); Tetra[3].NdotLoc=VecDot(Tetra[3].Norm, Tetra[3].Loc); VecAdd(TetraLoc, TetraExtent1, Tetra[4].Loc); VecSub(TetraExtent3, TetraExtent1, Tetra[4].v1); VecSub(TetraExtent2, TetraExtent1, Tetra[4].v2); VecCross(Tetra[4].v1, Tetra[4].v2, Tetra[4].Norm); VecNormalize(Tetra[4].Norm); Tetra[4].NdotLoc=VecDot(Tetra[4].Norm, Tetra[4].Loc); /* Order Tetrahedrons for the intersection testing based on the direction of the initial eye ray -> this is the creation of the priority queue */ GetInitialDir((float)ScanXRes/2, (float)ScanYRes/2, InitDir); for(i=1; i<5; i++) dot[i]=VecDot(InitDir, Tetra[i].Norm); /* Bubble sort based on closest opposite direction since rays approach closest opposite direction is when dot approaches -1.0 */ for(j=1; j<5; j++) { for(i=1; i<4; i++) { if(dot[i]>dot[i+1]) { memcpy(&tempface, &Tetra[i], sizeof(TetraFace)); /* swap */ memcpy(&Tetra[i], &Tetra[i+1], sizeof(TetraFace)); memcpy(&Tetra[i+1], &tempface, sizeof(TetraFace)); tempdot=dot[i]; dot[i]=dot[i+1]; dot[i+1]=tempdot; } } } } /* *********************************************************************** * * * Shoot Ray (use bounding objects) and Record Nearest Hit * * * *********************************************************************** ShootRay - check ray Intersect against all objects and return nearest Bounding Sphere and Bounding Prism or Bounding Sphere and Bounding Tetrahedron or Bounding Sphere or Bounding Prism or Bounding Tetrahedron */ SphereType BoundingSphere; PrismType BoundingPrism; TetraType BoundingTetra; void InitBoundingObjects() { if(BoundingSphereTest) CreateBoundingSphere(&BoundingSphere); if(BoundingPrismTest) CreateBoundingPrism(BoundingPrism); if(BoundingTetraTest) CreateBoundingTetra(BoundingTetra); } Boolean InitialRay; void SetUpTri(Byte i, Byte p1, Byte p2) { gu[0]=-delta[p1]; gv[0]=-delta[p2]; gu[1]=BoundingTetra[i].v1[p1]-delta[p1]; gv[1]=BoundingTetra[i].v1[p2]-delta[p2]; gu[2]=BoundingTetra[i].v2[p1]-delta[p1]; gv[2]=BoundingTetra[i].v2[p2]-delta[p2]; } void SetUpPara(Byte i, Byte p1, Byte p2) { gu[0]=-delta[p1]; gv[0]=-delta[p2]; gu[1]=BoundingPrism[i].v1[p1]-delta[p1]; gv[1]=BoundingPrism[i].v1[p2]-delta[p2]; gu[2]=BoundingPrism[i].v2[p1]+BoundingPrism[i].v1[p1]-delta[p1]; gv[2]=BoundingPrism[i].v2[p2]+BoundingPrism[i].v1[p2]-delta[p2]; gu[3]=BoundingPrism[i].v2[p1]-delta[p1]; gv[3]=BoundingPrism[i].v2[p2]-delta[p2]; } Boolean HitBoundingSphere; void DoBoundingSphereTest(TDA Start, TDA Dir) { VecSub(BoundingSphere.Center, Start, temp); /* check hit with */ b=VecDot(Dir, temp)*-2.0; /* bounding sphere */ c=VecDot(temp, temp)-BoundingSphere.RadSqr; disc=SqrFP(b)-4.0*c; if(disc<=0.0) HitBoundingSphere=false; else { sroot=sqrt(disc); t1=(-b-sroot)*0.5; t2=(-b+sroot)*0.5; if((!(t1>Small)) && (!(t2>Small))) HitBoundingSphere=false; else HitBoundingSphere=true; } } float dot; float pos1, pos2; Boolean HitBoundingPrism; void DoBoundingPrismTest(TDA Start, TDA Dir) { Byte i, last; if(InitialRay) { InitialRay=false; last=4; } else last=7; i=1; do { dot=VecDot(BoundingPrism[i].Norm, Dir); if(fabs(dot)fabs(BoundingPrism[i].Norm[1])) && (fabs(BoundingPrism[i].Norm[0])>fabs(BoundingPrism[i].Norm[2]))) SetUpPara(i, 1, 2); else { if(!(fabs(BoundingPrism[i].Norm[1])fabs(BoundingTetra[i].Norm[1])) && (fabs(BoundingTetra[i].Norm[0])>fabs(BoundingTetra[i].Norm[2]))) SetUpTri(i, 1, 2); else { if(!(fabs(BoundingTetra[i].Norm[1])Small) { if(*Dist==-1.0) { *ObjHit=true; *Dist=NewDist; *Shp=ShapeNum; *Obj=ObjectNum; } else { /* find closest object */ if(NewDist<*Dist) { *Dist=NewDist; *Shp=ShapeNum; *Obj=ObjectNum; } } } } } } void IntersectGroundTest(TDA Start, TDA Dir, int *Shp, int *Obj, float *Dist, Boolean *ObjHit) { float NewDist; *ObjHit=false; NewDist=Intersect(Start, Dir, Ground, 1); if(NewDist>Small) { *ObjHit=true; *Dist=NewDist; *Shp=Ground; *Obj=1; } } void ShootRay(TDA Start, TDA Dir, int *Shp, int *Obj, float *Dist, Boolean *ObjHit) { Byte i; *Shp=-1; *Obj=-1; *Dist=-1.0; *ObjHit=false; if((!BoundingSphereTest) && (!BoundingPrismTest) && (!BoundingTetraTest)) IntersectTest(Start, Dir, Shp, Obj, Dist, ObjHit); else if(BoundingSphereTest) { DoBoundingSphereTest(Start, Dir); if((HitBoundingSphere) && (BoundingPrismTest)) { DoBoundingPrismTest(Start, Dir); if(HitBoundingPrism) IntersectTest(Start, Dir, Shp, Obj, Dist, ObjHit); else if(GroundExists) IntersectGroundTest(Start, Dir, Shp, Obj, Dist, ObjHit); } else if((HitBoundingSphere) && (BoundingTetraTest)) { DoBoundingTetraTest(Start, Dir); if(HitBoundingTetra) IntersectTest(Start, Dir, Shp, Obj, Dist, ObjHit); else if(GroundExists) IntersectGroundTest(Start, Dir, Shp, Obj, Dist, ObjHit); } else if(HitBoundingSphere) IntersectTest(Start, Dir, Shp, Obj, Dist, ObjHit); else if(GroundExists) IntersectGroundTest(Start, Dir, Shp, Obj, Dist, ObjHit); } else if(BoundingPrismTest) { DoBoundingPrismTest(Start, Dir); if(HitBoundingPrism) IntersectTest(Start, Dir, Shp, Obj, Dist, ObjHit); else if(GroundExists) IntersectGroundTest(Start, Dir, Shp, Obj, Dist, ObjHit); } else if(BoundingTetraTest) { DoBoundingTetraTest(Start, Dir); if(HitBoundingTetra) IntersectTest(Start, Dir, Shp, Obj, Dist, ObjHit); else if(GroundExists) IntersectGroundTest(Start, Dir, Shp, Obj, Dist, ObjHit); } } /* *********************************************************************** * * * Calculate Contribution of Local Color Model at Intersection Point * * * *********************************************************************** GetLoclCol - calculate ambient, diffuse, reflection and specular reflection */ #define MaxNoise 28 Word NoiseMatrix[MaxNoise][MaxNoise][MaxNoise]; void InitNoise() { Byte x, y, z; Byte i, j, k; randomize(); for(x=0; x<=MaxNoise-1; x++) { for(y=0; y<=MaxNoise-1; y++) { for(z=0; z<=MaxNoise-1; z++) { NoiseMatrix[x][y][z] = random(12000); if(x==MaxNoise-1) i=0; else i=x; if(y==MaxNoise-1) j=0; else j=y; if(z==MaxNoise-1) k=0; else k=z; NoiseMatrix[x][y][z]=NoiseMatrix[i][j][k]; } } } } int Noise(float x, float y, float z) { /* harmonic and random functions combined to create a noise function based on Perlin's (1985) noise function - ideas found in Alan Watt's - Fundamentals of Three-Dimensional Computer Graphics */ int ix, iy, iz; float ox, oy, oz; int p000, p001; int p010, p011; int p100, p101; int p110, p111; int p00, p01; int p10, p11; int p0, p1; int d00, d01; int d10, d11; int d0, d1; int d; x=fabs(x); y=fabs(y); z=fabs(z); ix=Trunc(x)%MaxNoise; iy=Trunc(y)%MaxNoise; iz=Trunc(z)%MaxNoise; ox=x-(int)x; oy=y-(int)y; oz=z-(int)z; p000=NoiseMatrix[ix][iy][iz]; p001=NoiseMatrix[ix][iy][iz+1]; p010=NoiseMatrix[ix][iy+1][iz]; p011=NoiseMatrix[ix][iy+1][iz+1]; p100=NoiseMatrix[ix+1][iy][iz]; p101=NoiseMatrix[ix+1][iy][iz+1]; p110=NoiseMatrix[ix+1][iy+1][iz]; p111=NoiseMatrix[ix+1][iy+1][iz+1]; d00=p100-p000; d01=p101-p001; d10=p110-p010; d11=p111-p011; p00=Trunc((float)d00*ox)+p000; p01=Trunc((float)d01*ox)+p001; p10=Trunc((float)d10*ox)+p010; p11=Trunc((float)d11*ox)+p011; d0=p10-p00; d1=p11-p01; p0=Trunc((float)d0*oy)+p00; p1=Trunc((float)d1*oy)+p01; d=p1-p0; return((Trunc((float)d*oz)+p0)); } void MarbleTex(TDA Pt, TDA RGB) { float i, d; float x, y, z; UnVec(Pt, &x, &y, &z); x*=0.2; d=x+0.0006*(float)Noise(x, y*0.1, z*0.1); d*=(float)(Trunc(d)%25); i=0.5+0.05*fabs(d-10.0-20.0*(float)Trunc(d*0.05)); if (i > 1.0) i = 1.0; Vec(i, i, i, RGB); } void WoodTex(TDA Pt, TDA RGB) { float i, d; float x, y, z; UnVec(Pt, &x, &y, &z); x*=0.2; d=x+0.0002*(float)Noise(x, y*0.1, z*0.1); d*=(float)(Trunc(d)%25); i=0.7+0.05*fabs(d-10.0-20.0*(float)Trunc(d*0.05)); if (i > 1.0) i = 1.0; Vec(i, i, i, RGB); } Byte TextureNumbs(int Shp, int Obj) { Byte tex; switch(Shp) { case Ground : tex=Gnd.TexNum; break; case Triangle : tex=Tri[Obj].TexNum; break; case Parallelogram : tex=Para[Obj].TexNum; break; case Circles : tex=Cir[Obj].TexNum; break; case Ring : tex=Rng[Obj].TexNum; break; case Sphere : tex=Sphr[Obj].TexNum; break; case Cone : tex=Con[Obj].TexNum; break; case Cylinder : tex=Cyl[Obj].TexNum; break; } return(tex); } Byte MaterialNumbs(int Shp, int Obj) { Byte mtl; switch(Shp) { case Ground : mtl=Gnd.MtlNum; break; case Triangle : mtl=Tri[Obj].MtlNum; break; case Parallelogram : mtl=Para[Obj].MtlNum; break; case Circles : mtl=Cir[Obj].MtlNum; break; case Ring : mtl=Rng[Obj].MtlNum; break; case Sphere : mtl=Sphr[Obj].MtlNum; break; case Cone : mtl=Con[Obj].MtlNum; break; case Cylinder : mtl=Cyl[Obj].MtlNum; break; } return(mtl); } void Texture(TDA IntrPt, int Tex, TDA Texturing) { int x, y, z, rt; float lev, lev1, lev2, lev3, r; switch(Tex) { case Checker : x=Round(fabs(IntrPt[0])*Tile)%10000; y=Round(fabs(IntrPt[1])*Tile)%10000; z=Round(fabs(IntrPt[2])*Tile)%10000; if(((x+y+z)%2)==1) VecCopy(Tile1, Texturing); else VecCopy(Tile2, Texturing); break; case Grit : rt=random(32767); r=(float) rt/32768.0; lev=r*0.2+0.8; Vec(lev, lev, lev, Texturing); break; case Marble : MarbleTex(IntrPt, Texturing); break; case Wood : WoodTex(IntrPt, Texturing); break; case Sheetrock : rt=random(32767); r=(float) rt/32768.0; lev=r*0.1+0.9; Vec(lev, lev, lev, Texturing); break; case Particle : rt=random(32767); r=(float) rt/32768.0; lev1=r*0.15+0.85; lev2=r*0.15+0.85; lev3=r*0.15+0.85; Vec(lev1, lev2, lev3, Texturing); break; } } Boolean ObjHitTransmissive(Byte Mtl) { if((Matl[Mtl].Trans[0]==0.0) && (Matl[Mtl].Trans[1]==0.0) && (Matl[Mtl].Trans[2]==0.0)) return(false); else return(true); } void GetLoclCol(int Shp, int Obj, TDA Dir, TDA IntrPt, TDA SrfNrm, float Dist, TDA LoclCol) { int Mtl, Tex, Src; Boolean ObjHit, HitItself; float IntensFactor; TDA LmpDir; TDA Addition; TDA Total; float Lamb; TDA Spec, Diff; TDA Temp; float cone, Glint; int ShadShp; int ShadObj; float ShadDist; TDA ShadIntrPt; TDA ShadSrfNrm; float Alpha; TDA ColorTexture; if(Shp==Lamp) { if(DistEffect==0.0) IntensFactor=1.0; else IntensFactor=(1.0-DistEffect)+DistEffect*(-VecDot(SrfNrm, Dir)/sqrt(Dist)); VecScalMult(IntensFactor, Lmp[Obj].Intens, LoclCol); } else { Mtl=MaterialNumbs(Shp, Obj); /* Ambient Component */ VecCopy(Matl[Mtl].AmbRfl, Total); for(Src=1; Src<=ObjCnt[Lamp]; Src++) { VecSub(Lmp[Src].Loc, IntrPt, LmpDir); VecNormalize(LmpDir); ShootRay(IntrPt, LmpDir, &ShadShp, &ShadObj, &ShadDist, &ObjHit); /* There is no need to check beyond lamp since the closest object is returned. If the shadow feeler hits the lamp that is in the direction of the lamp (itself) then the hit is ignored. If the shadow feeler hits a transmissive object, then the lamp's contribution is attenutated by à and by the transmission factor - no refraction in the object is taken into account. Note that à represents a percentage of transmission based on the angle between LmpDir and the obstructing object's surface normal and that à also has a constant component. */ if((ObjHit) && ((ShadShp==Lamp) && (ShadObj==Src))) HitItself=true; else HitItself=false; if((!(ObjHit)) || HitItself) { /* Unshadowed surface */ Lamb=VecDot(SrfNrm, LmpDir); /* N - L */ if(Lamb<=0.0) { VecNull(Spec); VecNull(Diff); } else { /* Diffuse Component */ VecElemMult(Lamb, Matl[Mtl].DifRfl, Lmp[Src].Intens, Diff); /* Specular Component */ VecSub(LmpDir, Dir, Temp); VecScalMult(0.5, Temp, Temp); /* H = (L + V) / 2 */ VecNormalize(Temp); cone=VecDot(SrfNrm, Temp); if(cone>0.5) { Glint=exp(Matl[Mtl].Gloss*Log(cone)); VecElemMult(Glint, Matl[Mtl].SpcRfl, Lmp[Src].Intens, Spec); } else VecNull(Spec); } VecAdd(Diff, Spec, Addition); VecAdd(Total, Addition, Total); } else if(ShadShp!=Lamp) { Mtl=MaterialNumbs(ShadShp, ShadObj); if(ObjHitTransmissive(Mtl)) { /* Light through transmissive object */ GetIntrPt(IntrPt, LmpDir, ShadDist, ShadIntrPt); GetSrfNrm(ShadShp, ShadObj, ShadIntrPt, ShadSrfNrm); Alpha=0.7*fabs(VecDot(LmpDir, ShadSrfNrm))+0.2; VecElemMult(Alpha, Lmp[Src].Intens, Matl[Mtl].Trans, Addition); VecAdd(Total, Addition, Total); } Mtl=MaterialNumbs(Shp, Obj); } } Tex=TextureNumbs(Shp, Obj); if((Tex==Smooth) || (Tex==OceanWaves) || (Tex==PoolWaves) || (Tex==Waves)) VecCopy(Total, LoclCol); /* don't modify color */ else { Texture(IntrPt, Tex, ColorTexture); VecElemMult(1.0, Total, ColorTexture, LoclCol); } } } /* *********************************************************************** * * * Calculate Sky * * * *********************************************************************** Sky - blend a sky color from the horizon to the zenith CloudySky - noise funciton clouds with blended sky color */ void Sky(TDA Dir, TDA Col) { float small2=1E-03; float sin2, cos2; float x2, y2, z2; x2=SqrFP(Dir[0]); y2=SqrFP(Dir[1]); z2=SqrFP(Dir[2]); if(z2==0) z2=small2; sin2=z2/(x2+y2+z2); cos2=1.0-sin2; VecLinComb(cos2, HorCol, sin2, ZenCol, Col); } void CloudySky(TDA Dir, TDA SkyCol) { float small2=1E-03; float sin2, cos2; float x2, y2, z2; TDA Col; TDA Col1, Col2; float x, y, z; float d, f; UnVec(Dir, &x, &y, &z); x2=SqrFP(Dir[0]); y2=SqrFP(Dir[1]); z2=SqrFP(Dir[2]); if(z2==0) z2=small2; sin2=z2/(x2+y2+z2); cos2=1.0-sin2; VecLinComb(cos2, HorCol, sin2, ZenCol, Col); x*=10.0; y*=10.0; z*=400.0; d=z+0.0006*Noise(x, y, z); d*=(float)(Trunc(d)%25); f=0.06*fabs(d-10.0-20.0*(float)Trunc(d*0.05)); if(f>1.0) f=1.0; Vec(Col[2], Col[2], Col[2], Col1); /* Grey scale white for clouds */ /* based on blue level */ VecCopy(Col, Col2); /* color = sky blue */ VecLinComb(f, Col1, 1.0-f, Col2, SkyCol); } /* *********************************************************************** * * * Recursive Ray Tracer * * * *********************************************************************** TraceRay - perform recursive ray tracing */ void Comb(TDA A, TDA B, TDA C, TDA D, TDA E, TDA F, TDA Col) { TDA T1, T2, T3; VecElemMult(1.0, A, B, T1); VecElemMult(1.0, C, D, T2); VecElemMult(1.0, E, F, T3); VecAdd3(T1, T2, T3, Col); } Boolean WgtMin(TDA TotWgt) { if((TotWgt[0]<=MinWgt[0])&& (TotWgt[1]<=MinWgt[1])&& (TotWgt[2]<=MinWgt[2])) return(true); else return(false); } Boolean MaterialSpecular(int Shp, Byte Mtl) { if(!(Shp==Lamp)) { if((Matl[Mtl].SpcRfl[0]==0.0)&& (Matl[Mtl].SpcRfl[1]==0.0)&& (Matl[Mtl].SpcRfl[2]==0.0)) return(false); else return(true); } else return(true); } Boolean MaterialTransmissive(int Shp, Byte Mtl) { if(!(Shp==Lamp)) { if((Matl[Mtl].Trans[0]==0.0)&& (Matl[Mtl].Trans[1]==0.0)&& (Matl[Mtl].Trans[2]==0.0)) return(false); else return(true); } else return(false); } void TraceRay(TDA Start, TDA Dir, TDA TotWgt, Byte Depth, TDA Col) { TDA LoclCol, ReflCol, TranCol; TDA ReflDir, TranDir, Wgt; TDA IntrPt, SrfNrm; int Shp, Obj; float Dist; Byte Mtl, Tex; float f1, f2, f3, f4; float Ampl, Dampen, Dampen2; TDA OnesVec={1.0, 1.0, 1.0}; TDA Temp; Boolean ObjHit; Boolean Dummy; ShootRay(Start, Dir, &Shp, &Obj, &Dist, &ObjHit); if(ObjHit) { GetIntrPt(Start, Dir, Dist, IntrPt); GetSrfNrm(Shp, Obj, IntrPt, SrfNrm); Tex=TextureNumbs(Shp, Obj); if(Tex==OceanWaves) { /* Bump Surface Normal for Texture */ f1=sin(Radians(IntrPt[0]+IntrPt[1]+OceanWavePhase)); f2=sin(Radians(2.5*IntrPt[0]+IntrPt[1]+OceanWavePhase)); f3=sin(Radians(IntrPt[0]+1.7*IntrPt[1]+OceanWavePhase)); f4=sin(Radians(1.5*IntrPt[0]+4.1*IntrPt[1]+OceanWavePhase)); Ampl=OceanWaveAmpl*(f1+f2+f3+f4)*0.25; SrfNrm[0]+=Ampl; SrfNrm[1]+=Ampl; VecNormalize(SrfNrm); } if(Tex==PoolWaves) { /* Bump Surface Normal for Texture */ Dampen=sqrt(SqrFP(IntrPt[0]-PoolWaveXPos)+SqrFP(IntrPt[1]-PoolWaveYPos)); Dampen2=Dampen*1E-02; if(Dampen2<1.0) Dampen2=1.0; Ampl=PoolWaveAmpl*sin(Radians(2.5*Dampen+PoolWavePhase))/Dampen2; SrfNrm[0]+=Ampl; SrfNrm[1]+=Ampl; VecNormalize(SrfNrm); } if(Tex==Waves) { /* Bump Surface Normal for Texture */ Dampen=sqrt(SqrFP(IntrPt[0]-WaveXPos)+ SqrFP(IntrPt[1]-WaveYPos)+ SqrFP(IntrPt[2]-WaveZPos)); Dampen2=Dampen*1E-02; if(Dampen2<1.0) Dampen2=1.0; Ampl=WaveAmpl*cos(Radians(60.0*Dampen+WavePhase))/Dampen2; SrfNrm[0]+=Ampl; SrfNrm[1]+=Ampl; SrfNrm[2]+=Ampl; VecNormalize(SrfNrm); } if((Shp==Lamp) && (!(LampReflects))) GetLoclCol(Shp, Obj, Dir, IntrPt, SrfNrm, Dist, Col); else { GetLoclCol(Shp, Obj, Dir, IntrPt, SrfNrm, Dist, LoclCol); if((Depth==MaxDepth) || (WgtMin(TotWgt))) VecElemMult(1.0, LoclCol, LoclWgt, Col); else { if((Shp!=Lamp) || ((Shp==Lamp) && LampReflects)) { Mtl=MaterialNumbs(Shp, Obj); if(MaterialSpecular(Shp, Mtl)) { CalcDirOfReflRay(Dir, SrfNrm, ReflDir); VecElemMult(1.0, TotWgt, ReflWgt, Wgt); TraceRay(IntrPt, ReflDir, Wgt, Depth+1, ReflCol); if(Shp!=Lamp) { VecSub(OnesVec, Matl[Mtl].Trans, Temp); VecElemMult(1.0, ReflCol, Temp, ReflCol); } } else VecNull(ReflCol); if(MaterialTransmissive(Shp, Mtl)) { /* take ray through object and exit the other side */ CalcDirOfTranRay(Dir, SrfNrm, Mtl, TranDir); ShootRay(IntrPt, TranDir, &Shp, &Obj, &Dist, &Dummy); GetIntrPt(IntrPt, TranDir, Dist, IntrPt); GetSrfNrm(Shp, Obj, IntrPt, SrfNrm); CalcDirOfTranRay(TranDir, SrfNrm, Mtl, TranDir); VecElemMult(1.0, TotWgt, TranWgt, Wgt); TraceRay(IntrPt, TranDir, Wgt, Depth+1, TranCol); VecElemMult(1.0, TranCol, Matl[Mtl].Trans, TranCol); } else VecNull(TranCol); } else { VecNull(ReflCol); VecNull(TranCol); } if((Shp==Lamp) && LampReflects) { VecSub(OnesVec, LampRefl, Temp); VecElemMult(1.0, Temp, LoclCol, LoclCol); VecElemMult(1.0, LampRefl, ReflCol, ReflCol); VecAdd(LoclCol, ReflCol, Col); } else Comb(LoclCol, LoclWgt, ReflCol, ReflWgt, TranCol, TranWgt, Col); } } } else { if(SkyExists) { if(Clouds) CloudySky(Dir, Col); else Sky(Dir, Col); } else VecNull(Col); } } /* *********************************************************************** * * * General Screen and Viewing Vector Calculations * * * *********************************************************************** */ void PreCalculation() { float Scale; XAspDivFocLen=Asp/FocalLength; YAspDivFocLen=1.0/FocalLength; CenterX=ScanXRes>>1; CenterY=ScanYRes>>1; Scale=(float)CenterX; VecScalMult(Scale, ViewDir, ViewVec); } Palette_Register PalArray; void ClearScreen() { Set_Graphics_Mode(ScanXRes, ScanYRes); Init_Palette(PalArray); Set_Palette(PalArray); } /* *********************************************************************** * * * RGB File Buffer * * * *********************************************************************** */ #define MaxBufLen 3*ScanXRes Word BufLen; Byte Buffer[3072]; Word BufIndex; void InitLineBuffer() { for(BufIndex=0; BufIndex>2; /* Red */ Buffer[BufIndex++]=(Colr[1]&255)>>2; /* Green */ Buffer[BufIndex++]=(Colr[2]&255)>>2; /* Blue */ } void WriteLineBufferToRGBFile() { fwrite(Buffer, 3*XRes, 1, RGBFile); BufIndex=0; } /* *********************************************************************** * * * Allocated Memory Management * * * *********************************************************************** */ void Allocate_Memory() { Matl=farcalloc(MaxMaterial, sizeof(MaterialList)); Lmp=farcalloc(MaxLamp, sizeof(LampList)); Tri=farcalloc(MaxTriangle, sizeof(TriangleList)); Para=farcalloc(MaxParallelogram, sizeof(ParallelogramList)); Cir=farcalloc(MaxCircles, sizeof(CircleList)); Rng=farcalloc(MaxRing, sizeof(RingList)); Sphr=farcalloc(MaxSphere, sizeof(SphereList)); Con=farcalloc(MaxCone, sizeof(ConeList)); Cyl=farcalloc(MaxCylinder, sizeof(CylinderList)); if((Matl==NULL) || (Lmp==NULL) || (Tri==NULL) || (Para==NULL) || (Cir==NULL) || (Rng==NULL) || (Sphr==NULL) || (Con==NULL) || (Cyl==NULL)) { Exit_Graphics(); printf("Cannot allocate enough memory!\n\n Hit any key to exit."); getch(); exit(1); } Red_Plane=farmalloc(16000); Green_Plane=farmalloc(16000); Blue_Plane=farmalloc(16000); if((Red_Plane==NULL) || (Green_Plane==NULL) || (Blue_Plane==NULL)) { Exit_Graphics(); printf("Cannot allocate enough memory for planes!\n\n Hit any key to exit."); getch(); exit(1); } } void Free_Memory() { farfree(Matl); farfree(Lmp); farfree(Tri); farfree(Para); farfree(Cir); farfree(Rng); farfree(Sphr); farfree(Con); farfree(Cyl); farfree(Red_Plane); farfree(Green_Plane); farfree(Blue_Plane); } /* *********************************************************************** * * * Scan Pixel Display * * * *********************************************************************** */ Boolean SingleFrame; void Scan() { TDA InitialDir; TDA Col; TDIA Colr; Word Xp, Yp; Word X, Y; Boolean Inside; PreCalculation(); randomize(); for(Yp=0; Yp1.0) Col[0]=1.0; if(Col[1]>1.0) Col[1]=1.0; if(Col[2]>1.0) Col[2]=1.0; VecScalMultInt(255.0, Col, Colr); if(SingleFrame) { AddToLineBuffer(Colr); GrayScalePixel(Xp, Yp, Colr); } else PutGrayScalePixel(Xp, Yp, Colr); } if(SingleFrame) WriteLineBufferToRGBFile(); } } Name InFileName; /* .RT scene descriptions for frames file */ Name TempFileName; /* .TMP file written here and read in Process.C */ Name RGBFileName; /* .RGB file for large non-animated images - Disp.C */ void GetSceneFile() { Byte x, y; textcolor(YELLOW); textbackground(BLUE); gotoxy(1, 8); cprintf("Enter File Name -> "); x=wherex(); y=wherey(); textcolor(WHITE+BLINK); cprintf("%s", "BOUNCE"); textcolor(YELLOW); gotoxy(x, y); while(!(kbhit())); cprintf(" "); gotoxy(x, y); gets(InFileName); if(!(strcmp(InFileName, ""))) strcpy(InFileName, "BOUNCE"); strupr(InFileName); strcpy(TempFileName, InFileName); strcpy(RGBFileName, InFileName); strcat(InFileName, ".RT"); strcat(TempFileName, ".TMP"); strcat(RGBFileName, ".RGB"); } FILE *TempFile; #define XY (y*160)+x void WriteTMPFile() { Word LineBuf[160]; Word i; Word xc, yc; Word x, y; Word ColorNum; Word rc, gc, bc; for(y=0; y<100; y++) { for(i=0; i<160; i++) LineBuf[i]=0; for(x=0; x<160; x++) { ColorNum=0; rc=Red_Plane[XY]&62; /* 32 levels of each color */ gc=Green_Plane[XY]&62; /* yields 32768 colors */ bc=Blue_Plane[XY]&62; ColorNum=((rc>>1)|(gc<<4)|(bc<<9)); LineBuf[x]=ColorNum; } fwrite(LineBuf, sizeof(Word), 160, TempFile); } } /* *********************************************************************** * * * Main Program * * * *********************************************************************** */ Palette_Register PalArray; Byte FrameNum, LastFrameNum; void main() { Title(); printf("Recursive Ray Tracing Program\n\n"); printf("Program by Christopher D. Watkins\n\n"); printf("'C' Conversion by Larry Sharp\n\n"); Allocate_Memory(); Clear_Planes(); InitNoise(); /* Noise function for Wood and Marble Textures */ ClearMemory(); InitLineBuffer(); GetSceneFile(); InFile=fopen(InFileName, "rt"); /* Scene .RT file */ if(InFile==NULL) { ungetch(32); Exit_Graphics(); printf("Can't open .RT file %s.\n", InFileName); exit(1); } LoadRTHeader(); ClearScreen(); if((NumberOfFrames==1) || (!(ScanXRes<=160)) || (!(ScanYRes<=100))) SingleFrame=true; else SingleFrame=false; if(SingleFrame) { RGBFile=fopen(RGBFileName, "wb"); /* .RGB image file */ if(RGBFile==NULL) { ungetch(32); Exit_Graphics(); printf("Can't open .RGB file.\n"); exit(1); } InitLineBuffer(); /* Buffers for RGB File */ PutRGBHeader(); GetDataForFrame(); PreCalculation(); InitBoundingObjects(); Scan(); fclose(RGBFile); fclose(InFile); Free_Memory(); } else { TempFile=fopen(TempFileName, "wb"); /* .TMP temproray file */ if(TempFile==NULL) { ungetch(32); Exit_Graphics(); printf("Can't open .RGB file.\n"); exit(1); } LastFrameNum=NumberOfFrames; putc(LastFrameNum, TempFile); for(FrameNum=0; FrameNum