home *** CD-ROM | disk | FTP | other *** search
/ Cutting-Edge 3D Game Programming with C++ / CE3DC++.ISO / BOOK / CHAP03 / STARF.CPP < prev    next >
C/C++ Source or Header  |  1996-04-13  |  12KB  |  462 lines

  1. //
  2. // File Name: Starf.CPP
  3. //
  4. // Description: 3-D starfield simulation
  5. //
  6. // Author: John A. De Goes 
  7. //
  8. // Project: Cutting Edge 3D Game Programming
  9. //
  10.  
  11. // -------------------------------------------------------------
  12. // | Global include files:                                     |
  13. // -------------------------------------------------------------
  14.  
  15. #include <Mem.h>
  16. #include <Dos.h>
  17. #include <Math.h>
  18. #include <Time.h>
  19. #include <Ctype.h>
  20. #include <Conio.h>
  21. #include <Stdio.h>
  22. #include <Stdlib.h>
  23. #include <String.h>
  24. #include <Iostream.h>
  25. #include <Graphics.h>
  26.  
  27. // -------------------------------------------------------------
  28. // | Local include files:                                      |
  29. // -------------------------------------------------------------
  30.  
  31. #include "Starf.hpp"
  32. #include "OutPort.HPP"
  33.  
  34. // -------------------------------------------------------------
  35. // | Global variables, constants:                              |
  36. // -------------------------------------------------------------
  37.  
  38. // Scaling values for perspective projection:
  39. float Hscale = 120;
  40. float Vscale = -120;
  41.  
  42. // The X, Y center of the screen:
  43. int XCENTER = 160;  // The X center of the screen
  44. int YCENTER = 100;  // The Y center of the screen
  45.  
  46. // The clipping rectangle:
  47. int XMIN = 0, XMAX = 319, YMIN = 0, YMAX = 199;
  48.  
  49. // The real-mode to protected-mode converter:
  50. unsigned int BaseDs = 0u;
  51.  
  52. // The Z step value of the viewer:
  53. float ZStep;
  54.  
  55. // Distance traveled in one second:
  56. float const DISTANCEPERSECOND = 5;
  57.  
  58. // The Z value of the viewer:
  59. float ViewZ = 0;
  60.  
  61. // View matrix and stars:
  62. Star *Cluster; Matrix3D ViewM;
  63.  
  64. // Star count:
  65. unsigned int MAXSTARS = 1000;
  66.  
  67. // The clock speed:
  68. const short unsigned int CLOCKSPEED = 10000;
  69.  
  70. // Clock herz per second:
  71. const unsigned int HZ = 1193180 / CLOCKSPEED; // 119.3 cycles per 
  72.                            // second
  73. // Trig tables:
  74. float CosTable[NUMBEROFDEGREES];
  75. float SinTable[NUMBEROFDEGREES];
  76.  
  77. // -------------------------------------------------------------
  78. // | Local classes/structs:                                    |
  79. // -------------------------------------------------------------
  80.  
  81. void Star::Project()
  82.    {
  83.    // If star is far away, adjust
  84.    // "Visible" flag accordingly:
  85.    if (Point3D.Wz > 200)
  86.       Visible = 0;
  87.    else if (Point3D.Wz < 1.0F)
  88.        {
  89.        Visible = 0;
  90.        // Un-comment the following lines for an infinite
  91.        // starfield:
  92.        // Initialize(random(300)-150, random(300)-150, 
  93.        // (random(4000)+100));
  94.        }
  95.    else {
  96.     // Else star is probably visible - project:
  97.     Visible = 1;
  98.     float X = Point3D.Wx;
  99.     float Y = Point3D.Wy;
  100.     float Z = Point3D.Wz;
  101.     float OneOverZ = 120.0F / Z;
  102.     Point2D.X = X * OneOverZ + XCENTER;
  103.     Point2D.Y = Y * OneOverZ + YCENTER;
  104.     }
  105.    }
  106.  
  107. void Star::DrawPoint(unsigned char *Buffer)
  108.    {
  109.    // Create temporary screen points:
  110.    int Sx = Point2D.X;
  111.    int Sy = Point2D.Y;
  112.    // Make sure point is in clipping boundary:
  113.    if ((Sx >= XMIN) && (Sx <= XMAX) && 
  114.        (Sy >= YMIN) && (Sy <= YMAX))
  115.       {
  116.       // Find memory location:
  117.       short unsigned int Index = (short int)(Sy * 320 + Sx);
  118.       // Shade according to distance:
  119.       Buffer[Index] = Point3D.Wz / 4;
  120.       } 
  121.    }
  122.    
  123. // Initializes the star position
  124. void inline Star::Initialize(float Nx, float Ny, float Nz)
  125.    {
  126.    Point3D.Wx = Nx;
  127.    Point3D.Wy = Ny;
  128.    Point3D.Wz = Nz;
  129.    }
  130.  
  131. // Display the star on the linear buffer "Buffer"
  132. void Star::Show(unsigned char *Buffer)
  133.    {
  134.    // Project point:
  135.    Project();
  136.    // If star is still visible...
  137.    if (Visible == 1)
  138.       {
  139.       // ...draw it:
  140.       DrawPoint(Buffer);
  141.       }
  142.    }   
  143.  
  144. // A generic timer class:
  145. class Timer {
  146. protected:
  147. unsigned long StartTime, CurrentTime, FunctionTime, Lapse;
  148. int UpdateFlag, AttachFlag;
  149. void (*CallFunction) ();
  150. public:
  151. Timer ()
  152. {
  153. AttachFlag = 0;
  154. UpdateFlag = 0;
  155. StartTime = 0;
  156. }
  157. // A member to start the clock:
  158. void Start ()
  159.    {
  160.    StartTime = clock ();
  161.    UpdateFlag = 1;
  162.    }
  163. // A member to reset the clock:
  164. void Reset ()
  165.    {
  166.    StartTime = clock ();
  167.    UpdateFlag = 1;
  168.    }
  169. // A member to get elapsed time:
  170. unsigned long Time ()
  171.    {
  172.    if ( UpdateFlag )
  173.       {
  174.       CurrentTime = clock();
  175.       return (CurrentTime - StartTime);
  176.       }
  177.    return 0;   
  178.    }
  179. void Update ();
  180. void Attach(void (*NewFunction)(), long Delay );
  181. void operator ++ () { Update (); }
  182. void operator ++ (int) { Update (); }
  183. };
  184.  
  185. void Timer::Update ()
  186.    {
  187.    CurrentTime = clock();
  188.    if (AttachFlag)
  189.       {
  190.       if (CurrentTime >= FunctionTime)
  191.          {
  192.          FunctionTime = CurrentTime + Lapse;
  193.          CallFunction ();
  194.          }
  195.       }   
  196.    }
  197.    
  198. // Function that will attach a function to specified time
  199. // intervals.
  200. void Timer::Attach(void (*NewFunction)(), long Delay )
  201.    {
  202.    Timer::CallFunction = NewFunction;
  203.    Lapse = Delay;
  204.    CurrentTime = clock();
  205.    FunctionTime = CurrentTime + Delay;
  206.    AttachFlag = 1;
  207.    }   
  208.    
  209. // -------------------------------------------------------------
  210. // | Function section:                                         |
  211. // -------------------------------------------------------------
  212.  
  213. // Initiate math function - call to initialize trig tables
  214. void InitMath()
  215.    {
  216.    long double Unit = (long double)6.28 / 
  217.               (long double)NUMBEROFDEGREES;
  218.    for (unsigned int i=0; i<NUMBEROFDEGREES; i++)
  219.        {
  220.        long double Degree = (long double)i;
  221.        CosTable[i] = cos(Unit * Degree);
  222.        SinTable[i] = sin(Unit * Degree);
  223.        }
  224.    }
  225.  
  226. // Abort funtion
  227. void Abort(char *String, int N = 1)
  228.    {
  229.    cout << String;
  230.    exit(N);
  231.    }
  232.  
  233. void SetPalReg ( long Index, char Red, char Green, char Blue )
  234.    {
  235.    REGS Regs;
  236.    Regs.w.ax  = 0x1010;
  237.    Regs.x.ebx = Index;
  238.    Regs.h.ch  = Red;
  239.    Regs.h.cl  = Green;
  240.    Regs.h.dh  = Blue;
  241.    int386 ( 0x10, &Regs, &Regs );
  242.    }
  243.  
  244.  
  245. // Sets the palette to shades of gray
  246. void SetPalette()
  247.    {
  248.    short unsigned int Color1, Color2, Color3;
  249.    for (short unsigned int Index = 0; Index < 256; Index++)
  250.        {
  251.        // Update three colors:
  252.        Color1 = ( unsigned short ) ( ( 256 >> 2 ) - Index );
  253.        Color2 = ( unsigned short ) ( ( 256 >> 2 ) - Index );
  254.        Color3 = ( unsigned short ) ( ( 256 >> 2 ) - Index );
  255.        SetPalReg ( Index, Color1, Color2, Color3 );
  256.        }
  257.    }
  258.  
  259. // Changes the speed of the internal clock
  260. void ChangeClock ( unsigned short int Value )
  261.    {
  262.    unsigned char LowByte =  ( unsigned char ) 
  263.                  ( Value & 0x00FF );
  264.    unsigned char HighByte = ( unsigned char )
  265.                  ( ( Value >> 8 ) & 0x00FF );
  266.    // Send control word to 8253's control register:
  267.    outportbr ( 0x43, 0x3C );
  268.    // Set the new counter time:
  269.    outportbr ( 0x40, LowByte );
  270.    outportbr ( 0x40, HighByte );
  271.    }
  272.    
  273. // Initializes real-mode addresses
  274. int InitDPMI(void)
  275.   {
  276.   REGS Register;
  277.   // Do not proceed if BaseDs has already been initialized
  278.   if (BaseDs == 0)
  279.      {
  280.      // Get the base linear address for DS.
  281.      Register.w.bx = _DS;
  282.      Register.w.ax = 0x0006;
  283.      int386(0x31, &Register, &Register);
  284.      // If we encounter an error, return zero
  285.      if (Register.x.cflag)
  286.         return 0;
  287.      // Multiply by 65,536 and mask out un-wanted bits
  288.      BaseDs = ((unsigned int)(Register.w.cx) << 16) | Register.w.dx;
  289.  
  290.      Register.w.bx = _DS;
  291.      Register.w.ax = 0x0008;
  292.      Register.w.cx = Register.w.dx = 0xFFFF;
  293.      int386(0x31, &Register, &Register);
  294.      return !Register.x.cflag;
  295.      }
  296.   else
  297.       return 1;
  298.   }
  299.  
  300. // Call this one to get a real-mode address  
  301. unsigned int GetAddress(unsigned int RMLocation)
  302.    {
  303.    if ((BaseDs == 0) && (InitDPMI() == 0))
  304.       return 0;
  305.    return (RMLocation - BaseDs);
  306.    }
  307.  
  308. // Call this one to get a real-mode address   
  309. // Don't call this one without calling InitDPMI
  310. unsigned int ConvertAddress(unsigned int Address)
  311.    {
  312.    return Address - BaseDs;
  313.    }
  314.  
  315. // Sets video mode - performs no error checking (e.g wrong
  316. // monitor.)   
  317. void SetMode(short int mode)
  318.    {
  319.    REGS regs;
  320.    regs.w.ax = mode;
  321.    regs.h.ah = 0;
  322.    int386(0x10, ®s, ®s);
  323.    } 
  324.  
  325.    
  326. // Main function:
  327. void main(void)
  328.    {
  329.    // Pointer to video memory:
  330.    unsigned char *Screen = (unsigned char *)GetAddress(0xA0000UL);
  331.    // Screen buffer:
  332.    unsigned char *Buffer;
  333.    // Misc. variables:
  334.    int Input = 1, Xr = 0, Yr = 0, Zr = 0;
  335.    // Allociate memory:
  336.    Buffer = new unsigned char[64000];
  337.    Cluster = new Star [ MAXSTARS ];
  338.    // Timer and ElapsedTime variables:
  339.    Timer T; float ElapsedTime = 100;
  340.    // Abort if not enough memory:
  341.    if ((Buffer == NULL) || (Cluster == NULL))
  342.       {
  343.       Abort("\nNot enough memory to run application\n");
  344.       }
  345.    // Set video mode and palette:     
  346.    SetMode(0x13);
  347.    SetPalette();
  348.    // Initiate trig tables:
  349.    InitMath();
  350.    // Change the system clock to a higher resolution:
  351.    ChangeClock ( ( short ) CLOCKSPEED );
  352.    // Start the timer:
  353.    T.Start ();
  354.    // Loop until esc key pressed:
  355.    while (Input != 27)
  356.      {
  357.      // Calculate step based on speed of computer.
  358.      // Uses following algorithm:
  359.      //
  360.      // (1) convert DISTANCEPERSECOND to 
  361.      //     a distance per herz value.
  362.      // (2) multiply this value by the time
  363.      //     it takes to draw each frame
  364.      
  365.      ZStep = ( float ) DISTANCEPERSECOND / 
  366.           ( float ) HZ * ( float ) ElapsedTime;
  367.      // Handle keyboard input:
  368.      if (kbhit())
  369.         {
  370.         Input = getch();
  371.         switch (Input)
  372.          {
  373.          case ('X'):
  374.               ++Xr;
  375.               break;
  376.          case ('x'):
  377.               --Xr;
  378.               break;
  379.          case ('Y'):
  380.               ++Yr;
  381.               break;
  382.          case ('y'):
  383.               --Yr;
  384.               break;
  385.          case ('Z'):
  386.               ++Zr;
  387.               break;
  388.          case ('z'):
  389.               --Zr;
  390.               break;                    
  391.          case ('f'):
  392.               ViewZ = ZStep;
  393.               break;
  394.          case ('b'):
  395.               ViewZ = -ZStep;
  396.               break;
  397.          case ('s'):
  398.               ViewZ = 0.0f;
  399.               break;
  400.          case ('v'):
  401.               {
  402.               gotoxy(15, 12);
  403.               cout << "Version 0.1a";
  404.               getch();
  405.               break;
  406.               }
  407.          default:
  408.               break;     
  409.          }
  410.         }
  411.      // Clear buffer:
  412.      setmem(Buffer, 64000, 0);
  413.      // Initialize viewing matrix:
  414.      ViewM.Initialize();
  415.      // Perform transformations:
  416.      ViewM.Rotate (-Xr, -Yr, -Zr);
  417.      ViewM.Translate ( 0, 0, -ViewZ );
  418.      // Loop through list of stars:
  419.      for (unsigned int Index=0; Index<MAXSTARS; Index++)
  420.          {
  421.          // Transform and draw stars:
  422.          Cluster[Index].Transform(ViewM);
  423.          Cluster[Index].Show(Buffer);
  424.          }
  425.      // Move data into viewport:    
  426.      memmove(Screen, Buffer, 64000);
  427.  
  428.      // Calculate time it took to draw frame
  429.      // and reset the timer:
  430.      ElapsedTime = ( float ) T.Time ();
  431.      T.Reset ();
  432.      }
  433.    // Perform a skewing demo:      
  434.    while (!kbhit())
  435.      {
  436.      // Clear buffer:
  437.      setmem(Buffer, 64000, 0);
  438.      // Initialize viewing matrix:
  439.      ViewM.Initialize();
  440.      // Perform rotations and translations:
  441.      ViewM.Rotate(0, 0, 10);
  442.      ViewM.Shear(-0.1, -0.1);
  443.      // Loop through list of stars:
  444.      for (unsigned int Index=0; Index<MAXSTARS; Index++)
  445.          {
  446.          // Transform and draw stars:
  447.          Cluster[Index].Transform(ViewM);
  448.          Cluster[Index].Show(Buffer);
  449.          }
  450.      // Move data into viewport:    
  451.      memmove(Screen, Buffer, 64000);
  452.      }
  453.    getch();
  454.    // Unlock memory:
  455.    delete [] Buffer;
  456.    delete [] Cluster;
  457.    // Set mode to text mode:
  458.    SetMode(0x03);
  459.    // Restore the system clocks original setting:
  460.    ChangeClock ( 0xFFFF );
  461.    }
  462.