#define PC #include #include #include #ifdef PC #include #include #include #include #include #else #include #include #define PI 3.141592654 #endif #include #include /* Definitions: Sectioned */ // Solid Numbers #define BOX 0 #define PLANE 1 #define QUADRIC 2 // Map Numbers #define CONSTANT 0 #define CYLINDRICAL 1 #define HYBRID 2 #define NORMAL 3 #define PLANAR 4 #define SPHERICAL 5 // Wavelengths #define lUltraViolet 340.0 #define lBlue 460.0 #define lGreen 520.0 #define lRed 700.0 #define lInfraRed 1000.0 /* Classes & Member functions: Heavily heierachial Operators not overloaded to improve efficency Trivial functions inlined */ // Prototypes to prevent problems class fColour; class Bitmap; class Projection; class Arena; extern Arena Scene; class Quality { public: unsigned char Aberration, //Doppler, //Intensity, Sky, HUD, Interpolation, AntiAliasing, Padding[3]; long SkyIndex; float AAThreshold; float Intensity; float Doppler; float HUDScale; Projection *HUDView; Bitmap *HUDMap; }; Quality Globals; // Integer colours class iColour { public: unsigned char b, g, r; inline fColour Convert(void); }; // Float colours class fColour { public: float b, g, r, a; inline iColour Convert(void); }; // Integer to float inline fColour iColour::Convert(void) { fColour c; c.b = (float) b; c.g = (float) g; c.r = (float) r; c.a = (float) 0.0; return c; } // Float to integer; inline iColour fColour::Convert(void) { iColour c; if(b < 255.0) { c.b = (unsigned char) b; } else { c.b = 255; } if(g < 255.0) { c.g = (unsigned char) g; } else { c.g = 255; } if(r < 255.0) { c.r = (unsigned char) r; } else { c.r = 255; } return c; } // VGA Display. ONLY CALL ON IBM-COMPATIBLE PCS #ifdef PC class Display { public: unsigned char *Real; void SetMode(void); inline void SetPage(short p); inline void SetPixel(long u, long v, iColour c); void ResetMode(void); }; void Display::SetMode(void) { union REGS r; r.w.ax = 0x4F02; r.w.bx = 0x0118; int386(0x10, &r, &r); __djgpp_nearptr_enable(); Real = (unsigned char *) (__djgpp_conventional_base + 0xa0000); } inline void Display::SetPage(short p) { union REGS r; r.w.ax = 0x4F05; r.w.bx = 0; r.w.dx = p; int386(0x10, &r, &r); } inline void Display::SetPixel(long u, long v, iColour c) { long a; a = (v * 4 * 1024) + (u * 4); SetPage(a >> 16); a &= 0x0000FFFF; Real[a++] = c.b; Real[a++] = c.g; Real[a] = c.r; } void Display::ResetMode(void) { union REGS r; SetPage(0); r.x.ax = 0x0003; int86(0x10, &r, &r); } Display Screen; #endif // 24 bit colour bitmaps (truevision Targa) class Bitmap { public: unsigned long uMax, vMax; iColour *Pixel; void Initialise(long u, long v); void LoadTGA(char *Name); void SaveTGA(char *Name); void ChromaKey(long u, long v, Bitmap &Insert, long iu0, long iv0, long iu1, long iv1); fColour SmoothColour(float u, float v); fColour Colour(long u, long v); void Line(long u1, long v1, long u2, long v2, iColour c); }; void Bitmap::Initialise(long u, long v) { uMax = u; vMax = v; Pixel = new iColour[uMax * vMax]; } void Bitmap::LoadTGA(char *Name) { FILE *Handle; unsigned char Header[18]; Handle = fopen(Name, "rb"); if(Handle == NULL) { printf("Error: can't open %.32s\n", Name); exit(EXIT_FAILURE); } fseek(Handle, 0, 0); fread(Header, 1, 18, Handle); uMax = ((long) Header[13] << 8) + Header[12]; vMax = ((long) Header[15] << 8) + Header[14]; printf("Loading %.32s (%li x %li)\n", Name, uMax, vMax); Pixel = new iColour[uMax * vMax]; fseek(Handle, 18, 0); fread(Pixel, 3, uMax * vMax, Handle); fclose(Handle); } void Bitmap::SaveTGA(char *Name) { FILE *Handle; unsigned char Header[18]; Header[0] = 0; Header[1] = 0; Header[2] = 2; Header[3] = 0; Header[4] = 0; Header[5] = 0; Header[6] = 0; Header[7] = 0; Header[8] = 0; Header[9] = 0; Header[10] = 0; Header[11] = 0; Header[12] = uMax; Header[13] = ((long) uMax >> 8); Header[14] = vMax; Header[15] = ((long) vMax >> 8); Header[16] = 24; Header[17] = 0; Handle = fopen(Name, "wb"); fseek(Handle, 0, 0); fwrite(Header, 1, 18, Handle); fseek(Handle, 18, 0); fwrite(Pixel, 3, uMax * vMax, Handle); fclose(Handle); #ifndef PC printf("%.32s successfully saved.\n", Name); #endif } void Bitmap::ChromaKey(long u, long v, Bitmap &Insert, long iu0, long iv0, long iu1, long iv1) { iColour c, r; long i, j, k, l; r.b = 0; r.g = 0; r.r = 255; j = vMax - v - 1; l = Insert.vMax - iv0; do { l--; i = u; k = iu0; do { c = Insert.Pixel[k + l*Insert.uMax]; if((c.b == r.b) && (c.g == r.g) && (c.r == r.r)) { } else { c.b = (long) ((long) c.b + Pixel[i + j*uMax].b) >> 1; c.g = (long) ((long) c.g + Pixel[i + j*uMax].g) >> 1; c.r = (long) ((long) c.r + Pixel[i + j*uMax].r) >> 1; Pixel[i + j*uMax] = c; #ifdef PC Screen.SetPixel(i, vMax - j - 1, c); #endif } i++; k++; } while(k < iu1); j--; } while(l > Insert.vMax - iv1); } fColour Bitmap::SmoothColour(float u, float v) { fColour Smoothed; iColour c00, c01, c10, c11; float du, dv; unsigned long iu, iv; u = fmod(u, 1.0); v = fmod(v, 1.0); if(u < 0.0) { u += 1.0; } if(v < 0.0) { v += 1.0; } // Integer coordinates iu = (long) ((float) u * uMax); iv = (long) ((float) v * vMax); c00 = Pixel[iu + (iv * uMax)]; if(Globals.Interpolation) { // Fractional coordinates du = fmod(u * uMax, 1.0); dv = fmod(v * vMax, 1.0); if(iu < uMax - 1) { c10 = Pixel[(iu + 1) + (iv * uMax)]; if(iv < vMax - 1) { c01 = Pixel[iu + ((iv + 1) * uMax)]; c11 = Pixel[(iu + 1) + ((iv + 1) * uMax)]; } else { c01 = Pixel[iu]; c11 = Pixel[iu + 1]; } } else { c10 = Pixel[iv * uMax]; if(iv < vMax - 1) { c01 = Pixel[iu + ((iv + 1) * uMax)]; c11 = Pixel[(iv + 1) * uMax]; } else { c01 = Pixel[iu]; c11 = Pixel[0]; } } Smoothed.b = c00.b*(1-du)*(1-dv) + c01.b*(1-du)*dv + c10.b*du*(1-dv) + c11.b*du*dv; Smoothed.g = c00.g*(1-du)*(1-dv) + c01.g*(1-du)*dv + c10.g*du*(1-dv) + c11.g*du*dv; Smoothed.r = c00.r*(1-du)*(1-dv) + c01.r*(1-du)*dv + c10.r*du*(1-dv) + c11.r*du*dv; return Smoothed; } else { return c00.Convert(); } } fColour Bitmap::Colour(long u, long v) { return Pixel[u + v*uMax].Convert(); } void Bitmap::Line(long u1, long v1, long u2, long v2, iColour c) { float u, v, du, dv; long i, j; du = (float) (u2 - u1); // Stops pathological cases dv = (float) (v2 - v1); i = (long) fabs(dv); if(fabs(du) > fabs(dv)) { i = (long) fabs(du); } if(i == 0) { i = 1; } du /= (float) i; dv /= (float) i; u = u1 - 1; v = v1; j = i; do { Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r += c.r >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g += c.g >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b += c.b >> 1; Screen.SetPixel((long) u, (long) v, c); u += du; v += dv; } while(j--); u = u1 + 1; v = v1; j = i; do { Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r += c.r >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g += c.g >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b += c.b >> 1; Screen.SetPixel((long) u, (long) v, c); u += du; v += dv; } while(j--); u = u1; v = v1 - 1; j = i; do { Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r += c.r >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g += c.g >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b += c.b >> 1; Screen.SetPixel((long) u, (long) v, c); u += du; v += dv; } while(j--); u = u1; v = v1 + 1; j = i; do { Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b >>= 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].r += c.r >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].g += c.g >> 1; Pixel[((long) u) + (vMax - 1 - (long) v) * uMax].b += c.b >> 1; Screen.SetPixel((long) u, (long) v, c); u += du; v += dv; } while(j--); u = u1; v = v1; do { Pixel[((long) u) + (vMax - 1 - (long) v) * uMax] = c; Screen.SetPixel((long) u, (long) v, c); u += du; v += dv; } while(i--); } Bitmap *Texture; Bitmap Symbols; // Mathematical classes -> functions // 3 - Vector class Vector { public: float x, y, z; inline float Norm(void); inline void Normalise(float r); inline float DotProduct(Vector &u); inline void CrossProduct(Vector &u, Vector &v); }; // Elucidean Norm inline float Vector::Norm(void) { return sqrt(x*x + y*y + z*z); } // Set length to r inline void Vector::Normalise(float r) { r /= Norm(); x *= r; y *= r; z *= r; } // this . u inline float Vector::DotProduct(Vector &u) { return u.x*x + u.y*y + u.z*z; } // this = u * v inline void Vector::CrossProduct(Vector &u, Vector &v) { x = (u.y * v.z) - (u.z * v.y); y = (u.z * v.x) - (u.x * v.z); z = (u.x * v.y) - (u.y * v.x); } // 3 dimensional solids // Box || axes class SolidBox { public: Vector Min, Max; void Set(float nx, float ny, float nz, float xx, float xy, float xz); float Intersection(Vector &r, Vector &l); Vector Normal(Vector &l); }; void SolidBox::Set(float nx, float ny, float nz, float xx, float xy, float xz) { Min.x = nx; Min.y = ny; Min.z = nz; Max.x = xx; Max.y = xy; Max.z = xz; } float SolidBox::Intersection(Vector &r, Vector &l) { Vector tMin, tMax; float t; if(r.x) { tMin.x = -(l.x - Min.x) / r.x; tMax.x = -(l.x - Max.x) / r.x; } else { tMin.x = 0.0; tMax.x = 0.0; } if(r.y) { tMin.y = -(l.y - Min.y) / r.y; tMax.y = -(l.y - Max.y) / r.y; } else { tMin.y = 0.0; tMax.y = 0.0; } if(r.z) { tMin.z = -(l.z - Min.z) / r.z; tMax.z = -(l.z - Max.z) / r.z; } else { tMin.z = 0.0; tMax.z = 0.0; } if(tMin.x < tMax.x) { t = tMin.x; tMin.x = tMax.x; tMax.x = t; } if(tMin.y < tMax.y) { t = tMin.y; tMin.y = tMax.y; tMax.y = t; } if(tMin.z < tMax.z) { t = tMin.z; tMin.z = tMax.z; tMax.z = t; } t = 0.0; if((tMin.y >= tMax.x) && (tMax.x >= tMax.y) && (tMin.z >= tMax.x) && (tMax.x >= tMax.z)) { t = -tMax.x; } if((tMin.x >= tMax.y) && (tMax.y >= tMax.x) && (tMin.z >= tMax.y) && (tMax.y >= tMax.z)) { t = -tMax.y; } if((tMin.x >= tMax.z) && (tMax.z >= tMax.x) && (tMin.y >= tMax.z) && (tMax.z >= tMax.y)) { t = -tMax.z; } return t; } Vector SolidBox::Normal(Vector &l) { Vector n; float e[6], eMin; long i, s; // Error squares (closest side to l) e[0] = (l.x - Min.x)*(l.x - Min.x); e[1] = (l.y - Min.y)*(l.y - Min.y); e[2] = (l.z - Min.z)*(l.z - Min.z); e[3] = (l.x - Max.x)*(l.x - Max.x); e[4] = (l.y - Max.y)*(l.y - Max.y); e[5] = (l.z - Max.z)*(l.z - Max.z); // Find mimimum error: i = 1; s = 0; eMin = e[0]; do { if(e[i] < eMin) { s = i; eMin = e[i]; } } while(++i < 6); // Select normal n.x = 0.0; n.y = 0.0; n.z = 0.0; switch(s) { case 0: n.x = -1; break; case 1: n.y = -1; break; case 2: n.z = -1; break; case 3: n.x = 1; break; case 4: n.y = 1; break; case 5: n.z = 1; break; } return n; } // Plane class SolidPlane { public: float cx, cy, cz, k; void Set(float a, float b, float c, float d); float Intersection(Vector &r, Vector &l); Vector Normal(void); }; void SolidPlane::Set(float a, float b, float c, float d) { cx = a; cy = b; cz = c; k = d; } float SolidPlane::Intersection(Vector &r, Vector &l) { float A, B, t; // Surface cx*x + cy*y + cz*z + k = 0; // Transformed by x = -r.x*t + l.x... // to A*t + B = 0 A = -r.x*cx - r.y*cy - r.z*cz; B = l.x*cx + l.y*cy + l.x*cz + k; t = -B/A; return t; } Vector SolidPlane::Normal(void) { Vector n; n.x = cx; n.y = cy; n.z = cz; n.Normalise(1.0); return n; } // Quadric class SolidQuadric { public: float cxx, cxy, cxz, cx, cyy, cyz, cy, czz, cz, k; void Set(float xx, float xy, float xz, float x, float yy, float yz, float y, float zz, float z, float c); float Intersection(Vector &r, Vector &l); Vector Normal(Vector &l); }; void SolidQuadric::Set(float xx, float xy, float xz, float x, float yy, float yz, float y, float zz, float z, float c) { cxx = xx; cxy = xy; cxz = xz; cx = x; cyy = yy; cyz = yz; cy = y; czz = zz; cz = z; k = c; } float SolidQuadric::Intersection(Vector &r, Vector &l) { float A, B, C, t, D; // Surface cxx*x*x+cxy*x*y+cxz*x*z+cx*x+cyy*y*y+cyz*y*z+cy*y+czz*z*z+cz*z+k // Transformed by x = -r.x*t + l.x... // to A*t*t + B*t + C = 0 A = + cxx*r.x*r.x + cxy*r.x*r.y + cxz*r.x*r.z + cyy*r.y*r.y + cyz*r.y*r.z + czz*r.z*r.z; B = - cxx*2.0*r.x*l.x - cxy*(r.x*l.y + r.y*l.x) - cxz*(r.x*l.z + r.z*l.x) - cx*r.x - cyy*2.0*r.y*l.y - cyz*(r.y*l.z + r.z*l.y) - cy*r.y - czz*2.0*r.z*l.z - cz*r.z; C = + cxx*l.x*l.x + cxy*l.x*l.y + cxz*l.x*l.z + cx*l.x + cyy*l.y*l.y + cyz*l.y*l.z + cy*l.y + czz*l.z*l.z + cz*l.z + k; // Discriminant D = B*B - 4*A*C; // Solution if(D > 0) { t = (-B + sqrt(D)) / (2 * A); return t; } else { return 0.0; } } Vector SolidQuadric::Normal(Vector &l) { Vector n; // n = grad(Surface) n.x = 2.0*cxx*l.x + cxy*l.y + cxz*l.z + cx; n.y = cxy*l.x + 2.0*cyy*l.y + cyz*l.z + cy; n.z = cxz*l.z + cyz*l.y + 2.0*czz*l.z + cz; n.Normalise(1); return n; }; // Texture mapping methods // Cylindrical mapping about axis s class MapCylindrical { public: Bitmap *Image; Vector s, o; // Axis & Scale, origin fColour Colour(Vector &l); }; fColour MapCylindrical::Colour(Vector &l) { float u, v; v = (s.x*(l.x - o.x) + s.y*(l.y - o.y) + s.z*(l.z - o.z)) / s.Norm(); // Fixup u = 0.0; return Image->SmoothColour(u, v); } // Hybrid mapping, suitable for boxes class MapHybrid { public: long Image; float Ambient; Vector s, o; // Scale, origin void Set(long i, float sx, float sy, float sz, float ox, float oy, float oz); fColour Colour(Vector &l, Vector &n); }; void MapHybrid::Set(long i, float sx, float sy, float sz, float ox, float oy, float oz) { Image = i; s.x = sx; s.y = sy; s.z = sz; o.x = ox; o.y = oy; o.z = oz; } fColour MapHybrid::Colour(Vector &l, Vector &n) { float u, v; u = s.z*n.x*(l.z - o.z) + s.x*n.y*(l.x - o.x) + s.x*n.z*(l.x - o.x); v = s.y*n.x*(l.y - o.y) + s.z*n.y*(l.z - o.z) + s.y*n.z*(l.y - o.y); return Texture[Image].SmoothColour(u, v); } // Normal mapping, suitable for curved objects class MapNormal { public: Bitmap *Image; Vector s, o; // Axis, origin fColour Colour(Vector &n); }; fColour MapNormal::Colour(Vector &n) { Vector w; float u, v; // Transform normal w.x = s.x*(n.x - o.x); w.y = s.y*(n.y - o.y); w.z = s.z*(n.z - o.z); u = atan2(w.x, w.z) / (2.0 * PI); v = 1 - (atan2(sqrt(w.x*w.x + w.z*w.z), w.y) / PI); return Image->SmoothColour(u, v); } class MapPlanar { public: long Image; float Ambient; Vector su, sv, o; void Set(long i ,float sux, float suy, float suz, float svx, float svy, float svz, float ox, float oy, float oz); fColour Colour(Vector &l); }; void MapPlanar::Set(long i, float sux, float suy, float suz, float svx, float svy, float svz, float ox, float oy, float oz) { su.x = sux; su.y = suy; su.z = suz; sv.x = svx; sv.y = svy; sv.z = svz; o.x = ox; o.y = oy; o.z = oz; Image = i; } fColour MapPlanar::Colour(Vector &l) { float u, v; u = su.x*(l.x - o.x) + su.y*(l.y - o.y) + su.z*(l.z - o.z); v = sv.x*(l.x - o.x) + sv.y*(l.y - o.y) + sv.z*(l.z - o.z); return Texture[Image].SmoothColour(u, v); } // Spherical mapping, suitable for most objects class MapSpherical { public: long Image; float Ambient; Vector s, o; // Axis, origin void Set(long i, float sx, float sy, float sz, float ox, float oy, float oz); fColour Colour(Vector &l); }; void MapSpherical::Set(long i, float sx, float sy, float sz, float ox, float oy, float oz) { Image = i; s.x = sx; s.y = sy; s.z = sz; o.x = ox; o.y = oy; o.z = oz; } fColour MapSpherical::Colour(Vector &l) { Vector w; float u, v; // Transform location w.x = s.x*(l.x - o.x); w.y = s.y*(l.y - o.y); w.z = s.z*(l.z - o.z); u = atan2(w.x, w.z) / (2.0 * PI); v = 1 - (atan2(sqrt(w.x*w.x + w.z*w.z), w.y) / PI); return Texture[Image].SmoothColour(u, v); } class Object { public: void *Solid; void *Map; long SolidType; long MapType; float Intersection(Vector &r, Vector &l); Vector Normal(Vector &l); fColour Texture(Vector &l, Vector &n); }; float Object::Intersection(Vector &r, Vector &l) { switch(SolidType) { case BOX: return ((SolidBox *) Solid)->Intersection(r, l); break; case PLANE: return ((SolidPlane *) Solid)->Intersection(r, l); break; case QUADRIC: return ((SolidQuadric *) Solid)->Intersection(r, l); break; default: exit(EXIT_FAILURE); break; } } Vector Object::Normal(Vector &l) { switch(SolidType) { case BOX: return ((SolidBox *) Solid)->Normal(l); break; case PLANE: return ((SolidPlane *) Solid)->Normal(); break; case QUADRIC: return ((SolidQuadric *) Solid)->Normal(l); break; default: exit(EXIT_FAILURE); break; } } fColour Object::Texture(Vector &l, Vector &n) { switch(MapType) { case CONSTANT: return *((fColour *) Map); break; case CYLINDRICAL: return ((MapCylindrical *) Map)->Colour(l); break; case HYBRID: return ((MapHybrid *) Map)->Colour(l, n); break; case NORMAL: return ((MapNormal *) Map)->Colour(n); break; case PLANAR: return ((MapPlanar *) Map)->Colour(l); break; case SPHERICAL: return ((MapSpherical *) Map)->Colour(l); break; default: exit(EXIT_FAILURE); break; } } class LightSource { public: Vector o; // Origin float b; // Brightness at 1 lightsecond void Set(float ox, float oy, float oz, float b1); float Cast(Vector &l, Vector &n); }; void LightSource::Set(float ox, float oy, float oz, float b1) { o.x = ox; o.y = oy; o.z = oz; b = b1; } float LightSource::Cast(Vector &l, Vector &n) { Vector w; // Transformed coordinates float i; // Illumination w.x = -(l.x - o.x); w.y = -(l.y - o.y); w.z = -(l.z - o.z); i = b * w.DotProduct(n) / pow((w.x*w.x + w.y*w.y + w.z*w.z), 1.5); if(i < 0) { i = 0; } return i; } class Projection { public: Vector l, v, s, f; // Location, Velocity, Sky, Focus Vector u, r; // Up, Right void Construct(void); float Project(float du, float dr, Vector &ray, Vector &o, float &Intensity); }; void Projection::Construct(void) { r.CrossProduct(f,s); u.CrossProduct(f,r); r.Normalise(1.0); u.Normalise(1.0); } class Arena { public: Bitmap Frame; Projection *Camera; Object *Solid; LightSource *Light; char *Name; long Solids, Lights, Cameras, Textures; float AspectRatio; void Run(void); void InsertHUD(Projection &Cam); void RenderFrame(Projection &Cam, Bitmap &Buffer); fColour CastRay(float u, float v, Projection &Cam, Bitmap &Buffer); void Load(char *SceneFile, char *CameraFile); }; float Projection::Project(float du, float dr, Vector &ray, Vector &o, float &Intensity) { Vector n; // Newtonian normal Vector vn; // Velocity normal Vector p; // Perpendicular component to velocity float cosa; // Length of parallel component to velocity float cosb; // Transformed angle float k; n.x = f.x + (du * r.x) + (dr * u.x); // Construct viewcone n.y = f.y + (du * r.y) + (dr * u.y); n.z = f.z + (du * r.z) + (dr * u.z); n.Normalise(1.0); // Clip rays to speed of light if((!Globals.Aberration) || (v.Norm() == 0)) { ray.x = n.x; ray.y = n.y; ray.z = n.z; o = l; Intensity = 1.0; return 1.0; } vn.x = v.x / v.Norm(); vn.y = v.y / v.Norm(); vn.z = v.z / v.Norm(); cosa = n.DotProduct(vn); // Parallel component p.x = n.x - (cosa * vn.x); // Perpendicular component p.y = n.y - (cosa * vn.y); p.z = n.z - (cosa * vn.z); // Aberation formula cosb = (cosa - v.Norm()) / (1 - cosa*v.Norm()); // Find necessary perpendicular length to normalise ray if(p.Norm() > 0) { k = sqrt(1 - (cosb*cosb*(vn.x*vn.x + vn.y*vn.y + vn.z*vn.z))) / p.Norm(); } else { k = 0.0; } // Thus... ray.x = cosb*vn.x + k*p.x; ray.y = cosb*vn.y + k*p.y; ray.z = cosb*vn.z + k*p.z; Intensity = (1 - v.Norm()*cosb)*(1 - v.Norm()*cosb)/sqrt(1 - v.x*v.x - v.y*v.y - v.z*v.z); Intensity = 1.0 + (Intensity - 1.0)*Globals.Intensity; // Partial intensities for clarity if(!Globals.Doppler) { o = l; return 1.0; } o = l; return (1.0 - (v.Norm()*cosb)) / sqrt(1 - v.x*v.x - v.y*v.y - v.z*v.z); } void Arena::Run(void) { long l; char FileName[16]; l = 0; do { //Fixup //Camera[l].Construct(); RenderFrame(Camera[l], Frame); if(Globals.HUD) { InsertHUD(Camera[l]); } sprintf(FileName, "%.32s%.4li.tga", Name, l); Frame.SaveTGA(FileName); } while(++l < Cameras); } void PutString(char *p, long u, long v) { do { switch(*p) { case 0: return; break; case 32: // Space u += 26 * 2; break; case 48: // 0 Scene.Frame.ChromaKey(u, v, Symbols, 0, 0, 32 * 2, 64 * 2 - 40); u += 26 * 2; break; case 49: // 1 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 2, 32 * 2, 64 * 2 * 2 - 40); u += 26 * 2; break; case 50: // 2 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 2 * 2, 32 * 2, 64 * 3 * 2 - 40); u += 26 * 2; break; case 51: // 3 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 3 * 2, 32 * 2, 64 * 4 * 2 - 40); u += 26 * 2; break; case 52: // 4 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 4 * 2, 32 * 2, 64 * 5 * 2 - 40); u += 26 * 2; break; case 53: // 5 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 5 * 2, 32 * 2, 64 * 6 * 2 - 40); u += 26 * 2; break; case 54: // 6 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 6 * 2, 32 * 2, 64 * 7 * 2 - 40); u += 26 * 2; break; case 55: // 7 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 7 * 2, 32 * 2, 64 * 8 * 2 - 40); u += 26 * 2; break; case 56: // 8 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 8 * 2, 32 * 2, 64 * 9 * 2 - 40); u += 26 * 2; break; case 57: // 9 Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 9 * 2, 32 * 2, 64 * 10 * 2 - 40); u += 26 * 2; break; case 61: // = Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 10 * 2, 32 * 2, 64 * 11 * 2 - 40); u += 26 * 2; break; case 99: // c Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 11 * 2, 32 * 2, 64 * 12 * 2 - 40); u += 26 * 2; break; case 46: // . Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 12 * 2, 32 * 2, 64 * 13 * 2 - 40); u += 10 * 2; break; case 103: // g Scene.Frame.ChromaKey(u, v, Symbols, 0, 64 * 13 * 2, 32 * 2, 64 * 14 * 2 - 40); u += 26 * 2; break; default: break; } if(u > Scene.Frame.uMax - (26 * 2)) { u = 26 * 2; v += 44 * 2; if(v > Scene.Frame.vMax - (44 * 2)) { return; } } p++; } while(1); } void Transform(long &u, long &v, Vector t, Bitmap &Frame) { float x, y, z; // Center t.x -= Globals.HUDView->l.x; t.y -= Globals.HUDView->l.y; t.z -= Globals.HUDView->l.z; // Project onto HUDView axes x = t.DotProduct(Globals.HUDView->r)/Scene.AspectRatio; y = t.DotProduct(Globals.HUDView->u); z = t.DotProduct(Globals.HUDView->f) / Globals.HUDView->f.DotProduct(Globals.HUDView->f); // Inverse z projection onto screen u = (long) ((float) ((x / z) - 1) * (float) (Globals.HUDMap->uMax/2)) + Frame.uMax - 32; v = (long) ((float) ((y / z) - 1) * (float) (Globals.HUDMap->vMax/2)) + Frame.vMax - 32; } void PlaceHUDMarkers(Projection &Cam, Bitmap &Frame) { long u1, v1, u2, v2, du, dv; Vector t; iColour c; float k; // Squish camera k = (Cam.f.Norm() + Cam.r.Norm() + Cam.u.Norm())/3.0; Cam.f.Normalise(Cam.f.Norm()/k); Cam.r.Normalise((Frame.uMax/Frame.vMax)*Scene.AspectRatio/k); Cam.u.Normalise(1/k); k = Globals.HUDScale; // Origin Transform(u1, v1, Cam.l, Frame); // Camera c.r = 0; c.g = 0; c.b = 240; // Corners t.x = Cam.l.x + (Cam.f.x + Cam.r.x + Cam.u.x) * k; t.y = Cam.l.y + (Cam.f.y + Cam.r.y + Cam.u.y) * k; t.z = Cam.l.z + (Cam.f.z + Cam.r.z + Cam.u.z) * k; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x -= 2.0*Cam.r.x*k; t.y -= 2.0*Cam.r.y*k; t.z -= 2.0*Cam.r.z*k; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x -= 2.0*Cam.u.x*k; t.y -= 2.0*Cam.u.y*k; t.z -= 2.0*Cam.u.z*k; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x += 2.0*Cam.r.x*k; t.y += 2.0*Cam.r.y*k; t.z += 2.0*Cam.r.z*k; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x += 2.0*Cam.u.x*k; t.y += 2.0*Cam.u.y*k; t.z += 2.0*Cam.u.z*k; Transform(u1, v1, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x -= 2.0*Cam.r.x*k; t.y -= 2.0*Cam.r.y*k; t.z -= 2.0*Cam.r.z*k; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x -= 2.0*Cam.u.x*k; t.y -= 2.0*Cam.u.y*k; t.z -= 2.0*Cam.u.z*k; Transform(u1, v1, t, Frame); Frame.Line(u1, v1, u2, v2, c); t.x += 2.0*Cam.r.x*k; t.y += 2.0*Cam.r.y*k; t.z += 2.0*Cam.r.z*k; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); // Origin Transform(u1, v1, Cam.l, Frame); // Velocity c.r = 0; c.g = 240; c.b = 0; t.x = Cam.l.x + Cam.v.x*k*2; t.y = Cam.l.y + Cam.v.y*k*2; t.z = Cam.l.z + Cam.v.z*k*2; Transform(u2, v2, t, Frame); Frame.Line(u1, v1, u2, v2, c); du = (u2 - u1); dv = (v2 - v1); u1 = u2 - du/3 - dv/6; v1 = v2 - dv/3 + du/6; Frame.Line(u1, v1, u2, v2, c); u1 = u2 - du/3 + dv/6; v1 = v2 - dv/3 - du/6; Frame.Line(u1, v1, u2, v2, c); } void Arena::InsertHUD(Projection &Cam) { float g; char Buffer[256]; sprintf(Buffer, "%3.3fc", Cam.v.Norm()); if(strlen(Buffer) == 2) { PutString(Buffer, Frame.uMax - 26*2*2 - 32, 30); } else { PutString(Buffer, Frame.uMax - (26*2*strlen(Buffer)) + 12, 32); } g = 1/sqrt(1 - Cam.v.Norm()*Cam.v.Norm()); sprintf(Buffer, "g=%3.3g", g); PutString(Buffer, 30, Frame.vMax - 104); Scene.Frame.ChromaKey( Scene.Frame.uMax - Globals.HUDMap->uMax - 32, Scene.Frame.vMax - Globals.HUDMap->vMax - 32, *Globals.HUDMap, 0, 0, Globals.HUDMap->uMax, Globals.HUDMap->vMax); PlaceHUDMarkers(Cam, Frame); } void Arena::RenderFrame(Projection &Cam, Bitmap &Buffer) { fColour c00, c01, c10, c11; float e; long u, v; Cam.Construct(); v = 0; do { u = 0; do { c11 = CastRay((float) u - 0.25, (float) v - 0.25, Cam, Buffer); if(Globals.AntiAliasing) { if(u && v) { // Non-edge - check for aliasing c00 = Buffer.Colour(u-1,v-1); c01 = Buffer.Colour(u-1,v); c10 = Buffer.Colour(u,v-1); e = (c00.r - c01.r)*(c00.r - c01.r) + (c00.r - c10.r)*(c00.r - c10.r) + (c00.r - c11.r)*(c00.r - c11.r) + (c00.g - c01.g)*(c00.g - c01.g) + (c00.g - c10.g)*(c00.g - c10.g) + (c00.g - c11.g)*(c00.g - c11.g) + (c00.b - c01.b)*(c00.b - c01.b) + (c00.b - c10.b)*(c00.b - c10.b) + (c00.b - c11.b)*(c00.b - c11.b); if(e > Globals.AAThreshold) { c00 = CastRay((float) u - 0.75, (float) v - 0.75, Cam, Buffer); c01 = CastRay((float) u - 0.75, (float) v - 0.25, Cam, Buffer); c10 = CastRay((float) u - 0.25, (float) v - 0.75, Cam, Buffer); c11.r = (c00.r + c01.r + c10.r + c11.r) / 4.0; c11.g = (c00.g + c01.g + c10.g + c11.g) / 4.0; c11.b = (c00.b + c01.b + c10.b + c11.b) / 4.0; } } else { // Edge - antialias c00 = CastRay((float) u - 0.75, (float) v - 0.75, Cam, Buffer); c01 = CastRay((float) u - 0.75, (float) v - 0.25, Cam, Buffer); c10 = CastRay((float) u - 0.25, (float) v - 0.75, Cam, Buffer); c11.r = (c00.r + c01.r + c10.r + c11.r) / 4.0; c11.g = (c00.g + c01.g + c10.g + c11.g) / 4.0; c11.b = (c00.b + c01.b + c10.b + c11.b) / 4.0; } } Buffer.Pixel[u + (Buffer.vMax - 1 - v)*Buffer.uMax] = c11.Convert(); #ifdef PC Screen.SetPixel(u, v, c11.Convert()); #endif } while(++u < (signed long) Buffer.uMax); } while(++v < (signed long) Buffer.vMax); } fColour Arena::CastRay(float u, float v, Projection &Cam, Bitmap &Buffer) { fColour c, d; Vector r, o; // Ray, origin Vector l, n; // Location (of intersection), Normal float Doppler; // Spectrum shift float Illumination, Intensity; float su, sv; // Sky coordinates float t, tMax; float cuv, cir; // Off spectrum predictions float sb, sg, sr; // Shifted wavelengths long i, nMax; Doppler = Cam.Project( (2.0*(u / Buffer.uMax) - 1.0)*AspectRatio*Buffer.uMax/Buffer.vMax, 2.0*(v / Buffer.vMax) - 1.0, r, o, Intensity); tMax = -1e5; nMax = -1; i = 0; while(i < Solids) { t = Solid[i].Intersection(r, o); if((tMax < t) && (t < 0)) { tMax = t; nMax = i; } i++; } if(nMax != -1) { l.x = -(r.x * tMax) + o.x; l.y = -(r.y * tMax) + o.y; l.z = -(r.z * tMax) + o.z; n = Solid[nMax].Normal(l); c = Solid[nMax].Texture(l, n); // Dodgy - keep map members in same order! Illumination = ((MapSpherical *) Solid[nMax].Map)->Ambient; i = 0; do { Illumination += Light[i].Cast(l, n); } while(++i < Lights); c.r *= sqrt(Illumination); c.g *= sqrt(Illumination); c.b *= sqrt(Illumination); } else { if(Globals.Sky) { su = atan2(r.x, r.z) / (2.0 * PI); sv = 1.0 - (atan2(sqrt(r.x*r.x + r.z*r.z), r.y) / PI); c = Texture[Globals.SkyIndex].SmoothColour(su, sv); } else { c.r = 0.0; c.b = 0.0; c.g = 0.0; } } // Dimming; if(Globals.Intensity) { c.b /= Intensity; c.g /= Intensity; c.r /= Intensity; } Doppler = 1 + (Doppler - 1) * Globals.Doppler; // Spectrum shift... if(Globals.Doppler) { cuv = 0.5*c.b + 0.25*c.g + 0.125*c.r; cir = 0.5*c.r + 0.25*c.g + 0.125*c.b; sb = lBlue / Doppler; sg = lGreen / Doppler; sr = lRed / Doppler; if(sb < lUltraViolet) { d.b = cuv * sb / lUltraViolet; } else { if(sb < lBlue) { d.b = (c.b - cuv) * (sb - lUltraViolet) / (lBlue - lUltraViolet) + cuv; } else { if(sb < lGreen) { d.b = (c.g - c.b) * (sb - lBlue) / (lGreen - lBlue) + c.b; } else { if(sb < lRed) { d.b = (c.r - c.g) * (sb - lGreen) / (lRed - lGreen) + c.g; } else { if(sb < lInfraRed) { d.b = (cir - c.r) * (sb - lRed) / (lInfraRed - lRed) + c.r; } else { d.b = (lInfraRed / sb) * cir; } } } } } if(sg < lUltraViolet) { d.g = cuv * sg / lUltraViolet; } else { if(sg < lBlue) { d.g = (c.b - cuv) * (sg - lUltraViolet) / (lBlue - lUltraViolet) + cuv; } else { if(sg < lGreen) { d.g = (c.g - c.b) * (sg - lBlue) / (lGreen - lBlue) + c.b; } else { if(sg < lRed) { d.g = (c.r - c.g) * (sg - lGreen) / (lRed - lGreen) + c.g; } else { if(sg < lInfraRed) { d.g = (cir - c.r) * (sg - lRed) / (lInfraRed - lRed) + c.r; } else { d.g = (lInfraRed / sg) * cir; } } } } } if(sr < lUltraViolet) { d.r = cuv * sr / lUltraViolet; } else { if(sr < lBlue) { d.r = (c.b - cuv) * (sr - lUltraViolet) / (lBlue - lUltraViolet) + cuv; } else { if(sr < lGreen) { d.r = (c.g - c.b) * (sr - lBlue) / (lGreen - lBlue) + c.b; } else { if(sr < lRed) { d.r = (c.r - c.g) * (sr - lGreen) / (lRed - lGreen) + c.g; } else { if(sr < lInfraRed) { d.r = (cir - c.r) * (sr - lRed) / (lInfraRed - lRed) + c.r; } else { d.r = (lInfraRed / sr) * cir; } } } } } // Allow for partial redshifts return d; } else { return c; } } char *Find(char *String, char *Token) { char *a; a = strstr(String, Token); if(a == NULL) { printf("Error: Can't find token [ %.32s ] in %.32s\n", Token, String); exit(EXIT_FAILURE); } else { a += strlen(Token); } return a; } void Arena::Load(char *SceneFile, char *CameraFile) { struct stat FileStatus; FILE *Handle; void **Map; long *MapType; char *Input, *Buffer, *a, *b; long i, j, k, L, Maps; Handle = fopen(SceneFile,"rb"); if(Handle == NULL) { printf("Error: File %.32s not found\n", SceneFile); exit(EXIT_FAILURE); } fstat(fileno(Handle), &FileStatus); Input = new char[ FileStatus.st_size + 2 ]; if(Input == NULL) { printf("Error: Out of memory for %.32s\n", SceneFile); exit(EXIT_FAILURE); } fseek(Handle, 0, 0); fread(Input, FileStatus.st_size, 1, Handle); Input[ FileStatus.st_size + 1 ] = 0; printf("Scene file %.32s loaded\nParsing...\n", SceneFile); fclose(Handle); a = Find(Input, "FileName"); a = Find(a, "\""); b = Find(a, "\"") - 1; L = (long) b - (long) a; Name = new char[L + 1]; memcpy(Name, a, L); Name[L] = 0; printf("Output %.32s****.tga\n", Name); a = Find(Input, "Frames"); Frame.uMax = atol(a); a = Find(a, ","); Frame.vMax = atol(a); a = Find(a, ","); AspectRatio = atof(a); printf("Image (%li, %li) Aspect %f\n", Frame.uMax, Frame.vMax, AspectRatio); Frame.Pixel = new iColour[Frame.uMax * Frame.vMax]; // Fixup // For debugging (harmless anyway) memset(Frame.Pixel, 0, 3*Frame.uMax*Frame.vMax); a = Find(Input, "Aberration"); Globals.Aberration = atoi(a); a = Find(Input, "Doppler"); Globals.Doppler = atof(a); a = Find(Input, "Intensity"); Globals.Intensity = atof(a); a = Find(Input, "Sky"); Globals.Sky = atoi(a); a = Find(a, ","); Globals.SkyIndex = atol(a) - 1; a = Find(Input, "HUD"); Globals.HUD = atoi(a); a = Find(Input, "Interpolation"); Globals.Interpolation = atoi(a); a = Find(Input, "AntiAliasing"); Globals.AntiAliasing = atoi(a); a = Find(a, ","); Globals.AAThreshold = atof(a); printf("Globals:\n Aberration %i\n Doppler %g\n Intensity %g\n Sky %i SkyImage %li\n HUD %i\n Interpolation %i\n AntiAliasing %i Threshold %f\n", Globals.Aberration, Globals.Doppler, Globals.Intensity, Globals.Sky, Globals.SkyIndex, Globals.HUD, Globals.Interpolation, Globals.AntiAliasing, Globals.AAThreshold); if(Globals.HUD) { Symbols.LoadTGA("textures/Symbols2.tga"); printf("HUD Symbols loaded\n"); Globals.HUDView = new Projection[1]; a = Find(Input, "HUDView"); b = Find(a, "Location"); b = Find(b, "<"); Globals.HUDView->l.x = atof(b); b = Find(b, ","); Globals.HUDView->l.y = atof(b); b = Find(b, ","); Globals.HUDView->l.z = atof(b); Globals.HUDView->v.x = 0.0; Globals.HUDView->v.y = 0.0; Globals.HUDView->v.z = 0.0; b = Find(a, "Focus"); b = Find(b, "<"); Globals.HUDView->f.x = atof(b); b = Find(b, ","); Globals.HUDView->f.y = atof(b); b = Find(b, ","); Globals.HUDView->f.z = atof(b); b = Find(a, "Sky"); b = Find(b, "<"); Globals.HUDView->s.x = atof(b); b = Find(b, ","); Globals.HUDView->s.y = atof(b); b = Find(b, ","); Globals.HUDView->s.z = atof(b); Globals.HUDMap = new Bitmap[1]; b = Find(a, "Size"); Globals.HUDMap->uMax = atoi(b); b = Find(b, ","); Globals.HUDMap->vMax = atoi(b); b = Find(a, "Scale"); Globals.HUDScale = atof(b); Globals.HUDMap->Initialise(Globals.HUDMap->uMax, Globals.HUDMap->vMax); printf("HUD Map initialised\n"); } a = Find(Input, "Textures"); Textures = atol(a); Texture = new Bitmap[Textures]; if(Texture == NULL) { printf("Error:Cannot initialise %li textures\n", Textures); exit(EXIT_FAILURE); } i = 0; do { a = Find(a, "\""); b = Find(a, "\"") - 1; L = (long) b - (long) a; Buffer = new char[L + 1]; memcpy(Buffer, a, L); Buffer[L] = 0; Texture[i].LoadTGA(Buffer); free(Buffer); a = strstr(a, ","); } while(++i < Textures); a = Find(Input, "Lights"); Lights = atol(a); Light = new LightSource[Lights]; if(Light == NULL) { printf("Error:Cannot initialise %li light(s)\n", Lights); } i = 0; do { a = Find(a, "Light"); j = atol(a) - 1; a = Find(a, "<"); Light[j].o.x = atof(a); a = Find(a, ","); Light[j].o.y = atof(a); a = Find(a, ","); Light[j].o.z = atof(a); a = Find(a, ","); Light[j].b = atof(a); printf("Light %li, brightness %f at < %f, %f, %f >\n", j, Light[j].b, Light[j].o.x, Light[j].o.y, Light[j].o.z); } while(++i < Lights); a = Find(Input, "Maps"); Maps = atol(a); Map = new void *[Maps]; MapType = new long[Maps]; if((Map == NULL) || (MapType == NULL)) { printf("Error:Cannot initialise %li map(s)\n", Maps); exit(EXIT_FAILURE); } i = 0; do { a = Find(a, "Mapping"); j = atol(a) - 1; printf("Map %li ", j); a = Find(a, "Type"); a = Find(a, "<"); if(!strncmp(a, "Planar", 6)) { MapType[j] = PLANAR; } if(!strncmp(a, "Spherical", 9)) { MapType[j] = SPHERICAL; } if(!strncmp(a, "Hybrid", 6)) { MapType[j] = HYBRID; } switch(MapType[j]) { case PLANAR: Map[j] = new MapPlanar[1]; a = Find(a, "Texture"); ((MapPlanar *) Map[j])->Image = atol(a) - 1; a = Find(a, "Ambient"); ((MapPlanar *) Map[j])->Ambient = atof(a); a = Find(a, "<"); ((MapPlanar *) Map[j])->su.x = atof(a); a = Find(a, ","); ((MapPlanar *) Map[j])->su.y = atof(a); a = Find(a, ","); ((MapPlanar *) Map[j])->su.z = atof(a); a = Find(a, "<"); ((MapPlanar *) Map[j])->sv.x = atof(a); a = Find(a, ","); ((MapPlanar *) Map[j])->sv.y = atof(a); a = Find(a, ","); ((MapPlanar *) Map[j])->sv.z = atof(a); a = Find(a, "<"); ((MapPlanar *) Map[j])->o.x = atof(a); a = Find(a, ","); ((MapPlanar *) Map[j])->o.y = atof(a); a = Find(a, ","); ((MapPlanar *) Map[j])->o.z = atof(a); printf("Planar, texture %li, ambient %f\n", ((MapPlanar *) Map[j])->Image, ((MapPlanar *) Map[j])->Ambient); break; case SPHERICAL: Map[j] = new MapSpherical[1]; a = Find(a, "Texture"); ((MapSpherical *) Map[j])->Image = atol(a) - 1; a = Find(a, "Ambient"); ((MapSpherical *) Map[j])->Ambient = atof(a); a = Find(a, "<"); ((MapSpherical *) Map[j])->s.x = atof(a); a = Find(a, ","); ((MapSpherical *) Map[j])->s.y = atof(a); a = Find(a, ","); ((MapSpherical *) Map[j])->s.z = atof(a); a = Find(a, "<"); ((MapSpherical *) Map[j])->o.x = atof(a); a = Find(a, ","); ((MapSpherical *) Map[j])->o.y = atof(a); a = Find(a, ","); ((MapSpherical *) Map[j])->o.z = atof(a); printf("Spherical, texture %li, ambient %f\n", ((MapSpherical *) Map[j])->Image, ((MapSpherical *) Map[j])->Ambient); break; case HYBRID: Map[j] = new MapHybrid[1]; a = Find(a, "Texture"); ((MapHybrid *) Map[j])->Image = atol(a) - 1; a = Find(a, "Ambient"); ((MapHybrid *) Map[j])->Ambient = atof(a); a = Find(a, "<"); ((MapHybrid *) Map[j])->s.x = atof(a); a = Find(a, ","); ((MapHybrid *) Map[j])->s.y = atof(a); a = Find(a, ","); ((MapHybrid *) Map[j])->s.z = atof(a); a = Find(a, "<"); ((MapHybrid *) Map[j])->o.x = atof(a); a = Find(a, ","); ((MapHybrid *) Map[j])->o.y = atof(a); a = Find(a, ","); ((MapHybrid *) Map[j])->o.z = atof(a); printf("Hybrid, texture %li, ambient %f\n", ((MapHybrid *) Map[j])->Image, ((MapHybrid *) Map[j])->Ambient); break; default: printf("Error: Unknown mapping type at %.32s\n", a); exit(EXIT_FAILURE); break; } } while(++i < Maps); a = Find(Input, "Solids"); Solids = atol(a); Solid = new Object[Solids]; if(Solid == NULL) { printf("Error:Cannot initialise %li solid(s)\n", Solids); exit(EXIT_FAILURE); } i = 0; while(i++ < Solids) { a = Find(a, "Solid"); j = atol(a) - 1; a = Find(a, "Type"); a = Find(a, "<"); if(!strncmp(a, "Plane", 5)) { Solid[j].SolidType = PLANE; } if(!strncmp(a, "Quadric", 7)) { Solid[j].SolidType = QUADRIC; } if(!strncmp(a, "Box", 3)) { Solid[j].SolidType = BOX; } switch(Solid[j].SolidType) { case BOX: Solid[j].Solid = new SolidBox[1]; a = Find(a, "<"); ((SolidBox *) Solid[j].Solid)->Min.x = atof(a); a = Find(a, ","); ((SolidBox *) Solid[j].Solid)->Min.y = atof(a); a = Find(a, ","); ((SolidBox *) Solid[j].Solid)->Min.z = atof(a); a = Find(a, "<"); ((SolidBox *) Solid[j].Solid)->Max.x = atof(a); a = Find(a, ","); ((SolidBox *) Solid[j].Solid)->Max.y = atof(a); a = Find(a, ","); ((SolidBox *) Solid[j].Solid)->Max.z = atof(a); a = Find(a, "Map"); k = atol(a) - 1; Solid[j].Map = Map[k]; Solid[j].MapType = MapType[k]; printf("Solid %li Box, map %li\n", j, k); break; case PLANE: Solid[j].Solid = new SolidPlane[1]; a = Find(a, "<"); ((SolidPlane *) Solid[j].Solid)->cx = atof(a); a = Find(a, ","); ((SolidPlane *) Solid[j].Solid)->cy = atof(a); a = Find(a, ","); ((SolidPlane *) Solid[j].Solid)->cz = atof(a); a = Find(a, ","); ((SolidPlane *) Solid[j].Solid)->k = atof(a); a = Find(a, "Map"); k = atol(a) - 1; Solid[j].Map = Map[k]; Solid[j].MapType = MapType[k]; printf("Solid %li Plane, map %li\n", j, k); break; case QUADRIC: Solid[j].Solid = new SolidQuadric[1]; a = Find(a, "<"); ((SolidQuadric *) Solid[j].Solid)->cxx = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cxy = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cxz = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cx = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cyy = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cyz = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cy = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->czz = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->cz = atof(a); a = Find(a, ","); ((SolidQuadric *) Solid[j].Solid)->k = atof(a); a = Find(a, "Map"); k = atol(a) - 1; Solid[j].Map = Map[k]; Solid[j].MapType = MapType[k]; printf("Solid %li Quadric, map %li\n", j, k); break; default: printf("Error: Unknown object type at %.40s\n", a); exit(EXIT_FAILURE); break; } } free(Input); free(Map); free(MapType); Handle = fopen(CameraFile,"rb"); if(Handle == NULL) { printf("Error: File %.32s not found\n", CameraFile); exit(EXIT_FAILURE); } fstat(fileno(Handle), &FileStatus); Input = new char[ FileStatus.st_size + 2]; if(Input == NULL) { printf("Error: Out of memory for %.32s\n", CameraFile); exit(EXIT_FAILURE); } fseek(Handle, 0, 0); fread(Input, FileStatus.st_size, 1, Handle); Input[ FileStatus.st_size + 1 ] = 0; printf("Camera file %.32s loaded\nParsing...\n", CameraFile); fclose(Handle); a = Find(Input, "Cameras"); Cameras = atol(a); Camera = new Projection[Cameras]; if(Camera == NULL) { printf("Error:Cannot initialise %li camera(s)\n", Cameras); exit(EXIT_FAILURE); } i = 0; do { a = Find(a, "Camera"); j = atol(a) - 1; b = Find(a, "Location"); b = Find(b, "<"); Camera[j].l.x = atof(b); b = Find(b, ","); Camera[j].l.y = atof(b); b = Find(b, ","); Camera[j].l.z = atof(b); b = Find(a, "Velocity"); b = Find(b, "<"); Camera[j].v.x = atof(b); b = Find(b, ","); Camera[j].v.y = atof(b); b = Find(b, ","); Camera[j].v.z = atof(b); b = Find(a, "Focus"); b = Find(b, "<"); Camera[j].f.x = atof(b); b = Find(b, ","); Camera[j].f.y = atof(b); b = Find(b, ","); Camera[j].f.z = atof(b); b = Find(a, "Sky"); b = Find(b, "<"); Camera[j].s.x = atof(b); b = Find(b, ","); Camera[j].s.y = atof(b); b = Find(b, ","); Camera[j].s.z = atof(b); } while(++i < Cameras); printf("%s parsed successfully\n", CameraFile); free(Input); } Arena Scene; int main(int argc, char *argv[]) { time_t Start, End; printf("\nRelativistic Raytracer Version 0.2\n Antony Searle 1997\n\n"); switch(argc) { case 2: Scene.Load(argv[1], argv[1]); // Camera info in scene file break; case 3: Scene.Load(argv[1], argv[2]); // Camera info separate break; default: printf("Syntax: SceneFile [CameraFile]\n\n"); exit(EXIT_FAILURE); break; } #ifdef PC Screen.SetMode(); #endif if(Globals.HUD) { // i = Globals.Sky; // Globals.Sky = 0; Scene.RenderFrame(*Globals.HUDView, *Globals.HUDMap); // Globals.Sky = i; } time(&Start); Scene.Run(); time(&End); #ifdef PC //getch(); Screen.ResetMode(); #endif printf("\nRelativistic Raytracer Version 0.2\n Antony Searle 1997\n\n"); switch(argc) { case 2: printf("Run of %s [%li frame(s)]\n", argv[1], Scene.Cameras); break; case 3: printf("Run of %s and %s [%li frame(s)]\n", argv[1], argv[2], Scene.Cameras); break; } printf(" Saved as %s0000.tga -> %s%.4li.tga\n", Scene.Name, Scene.Name, Scene.Cameras - 1); printf(" %g seconds elapsed, %g seconds/frame.\n", difftime(End,Start), difftime(End,Start) / Scene.Cameras); }