home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cutting-Edge 3D Game Programming with C++
/
CE3DC++.ISO
/
BOOK
/
CHAP03
/
STARF.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1996-04-13
|
12KB
|
462 lines
//
// File Name: Starf.CPP
//
// Description: 3-D starfield simulation
//
// Author: John A. De Goes
//
// Project: Cutting Edge 3D Game Programming
//
// -------------------------------------------------------------
// | Global include files: |
// -------------------------------------------------------------
#include <Mem.h>
#include <Dos.h>
#include <Math.h>
#include <Time.h>
#include <Ctype.h>
#include <Conio.h>
#include <Stdio.h>
#include <Stdlib.h>
#include <String.h>
#include <Iostream.h>
#include <Graphics.h>
// -------------------------------------------------------------
// | Local include files: |
// -------------------------------------------------------------
#include "Starf.hpp"
#include "OutPort.HPP"
// -------------------------------------------------------------
// | Global variables, constants: |
// -------------------------------------------------------------
// Scaling values for perspective projection:
float Hscale = 120;
float Vscale = -120;
// The X, Y center of the screen:
int XCENTER = 160; // The X center of the screen
int YCENTER = 100; // The Y center of the screen
// The clipping rectangle:
int XMIN = 0, XMAX = 319, YMIN = 0, YMAX = 199;
// The real-mode to protected-mode converter:
unsigned int BaseDs = 0u;
// The Z step value of the viewer:
float ZStep;
// Distance traveled in one second:
float const DISTANCEPERSECOND = 5;
// The Z value of the viewer:
float ViewZ = 0;
// View matrix and stars:
Star *Cluster; Matrix3D ViewM;
// Star count:
unsigned int MAXSTARS = 1000;
// The clock speed:
const short unsigned int CLOCKSPEED = 10000;
// Clock herz per second:
const unsigned int HZ = 1193180 / CLOCKSPEED; // 119.3 cycles per
// second
// Trig tables:
float CosTable[NUMBEROFDEGREES];
float SinTable[NUMBEROFDEGREES];
// -------------------------------------------------------------
// | Local classes/structs: |
// -------------------------------------------------------------
void Star::Project()
{
// If star is far away, adjust
// "Visible" flag accordingly:
if (Point3D.Wz > 200)
Visible = 0;
else if (Point3D.Wz < 1.0F)
{
Visible = 0;
// Un-comment the following lines for an infinite
// starfield:
// Initialize(random(300)-150, random(300)-150,
// (random(4000)+100));
}
else {
// Else star is probably visible - project:
Visible = 1;
float X = Point3D.Wx;
float Y = Point3D.Wy;
float Z = Point3D.Wz;
float OneOverZ = 120.0F / Z;
Point2D.X = X * OneOverZ + XCENTER;
Point2D.Y = Y * OneOverZ + YCENTER;
}
}
void Star::DrawPoint(unsigned char *Buffer)
{
// Create temporary screen points:
int Sx = Point2D.X;
int Sy = Point2D.Y;
// Make sure point is in clipping boundary:
if ((Sx >= XMIN) && (Sx <= XMAX) &&
(Sy >= YMIN) && (Sy <= YMAX))
{
// Find memory location:
short unsigned int Index = (short int)(Sy * 320 + Sx);
// Shade according to distance:
Buffer[Index] = Point3D.Wz / 4;
}
}
// Initializes the star position
void inline Star::Initialize(float Nx, float Ny, float Nz)
{
Point3D.Wx = Nx;
Point3D.Wy = Ny;
Point3D.Wz = Nz;
}
// Display the star on the linear buffer "Buffer"
void Star::Show(unsigned char *Buffer)
{
// Project point:
Project();
// If star is still visible...
if (Visible == 1)
{
// ...draw it:
DrawPoint(Buffer);
}
}
// A generic timer class:
class Timer {
protected:
unsigned long StartTime, CurrentTime, FunctionTime, Lapse;
int UpdateFlag, AttachFlag;
void (*CallFunction) ();
public:
Timer ()
{
AttachFlag = 0;
UpdateFlag = 0;
StartTime = 0;
}
// A member to start the clock:
void Start ()
{
StartTime = clock ();
UpdateFlag = 1;
}
// A member to reset the clock:
void Reset ()
{
StartTime = clock ();
UpdateFlag = 1;
}
// A member to get elapsed time:
unsigned long Time ()
{
if ( UpdateFlag )
{
CurrentTime = clock();
return (CurrentTime - StartTime);
}
return 0;
}
void Update ();
void Attach(void (*NewFunction)(), long Delay );
void operator ++ () { Update (); }
void operator ++ (int) { Update (); }
};
void Timer::Update ()
{
CurrentTime = clock();
if (AttachFlag)
{
if (CurrentTime >= FunctionTime)
{
FunctionTime = CurrentTime + Lapse;
CallFunction ();
}
}
}
// Function that will attach a function to specified time
// intervals.
void Timer::Attach(void (*NewFunction)(), long Delay )
{
Timer::CallFunction = NewFunction;
Lapse = Delay;
CurrentTime = clock();
FunctionTime = CurrentTime + Delay;
AttachFlag = 1;
}
// -------------------------------------------------------------
// | Function section: |
// -------------------------------------------------------------
// Initiate math function - call to initialize trig tables
void InitMath()
{
long double Unit = (long double)6.28 /
(long double)NUMBEROFDEGREES;
for (unsigned int i=0; i<NUMBEROFDEGREES; i++)
{
long double Degree = (long double)i;
CosTable[i] = cos(Unit * Degree);
SinTable[i] = sin(Unit * Degree);
}
}
// Abort funtion
void Abort(char *String, int N = 1)
{
cout << String;
exit(N);
}
void SetPalReg ( long Index, char Red, char Green, char Blue )
{
REGS Regs;
Regs.w.ax = 0x1010;
Regs.x.ebx = Index;
Regs.h.ch = Red;
Regs.h.cl = Green;
Regs.h.dh = Blue;
int386 ( 0x10, &Regs, &Regs );
}
// Sets the palette to shades of gray
void SetPalette()
{
short unsigned int Color1, Color2, Color3;
for (short unsigned int Index = 0; Index < 256; Index++)
{
// Update three colors:
Color1 = ( unsigned short ) ( ( 256 >> 2 ) - Index );
Color2 = ( unsigned short ) ( ( 256 >> 2 ) - Index );
Color3 = ( unsigned short ) ( ( 256 >> 2 ) - Index );
SetPalReg ( Index, Color1, Color2, Color3 );
}
}
// Changes the speed of the internal clock
void ChangeClock ( unsigned short int Value )
{
unsigned char LowByte = ( unsigned char )
( Value & 0x00FF );
unsigned char HighByte = ( unsigned char )
( ( Value >> 8 ) & 0x00FF );
// Send control word to 8253's control register:
outportbr ( 0x43, 0x3C );
// Set the new counter time:
outportbr ( 0x40, LowByte );
outportbr ( 0x40, HighByte );
}
// Initializes real-mode addresses
int InitDPMI(void)
{
REGS Register;
// Do not proceed if BaseDs has already been initialized
if (BaseDs == 0)
{
// Get the base linear address for DS.
Register.w.bx = _DS;
Register.w.ax = 0x0006;
int386(0x31, &Register, &Register);
// If we encounter an error, return zero
if (Register.x.cflag)
return 0;
// Multiply by 65,536 and mask out un-wanted bits
BaseDs = ((unsigned int)(Register.w.cx) << 16) | Register.w.dx;
Register.w.bx = _DS;
Register.w.ax = 0x0008;
Register.w.cx = Register.w.dx = 0xFFFF;
int386(0x31, &Register, &Register);
return !Register.x.cflag;
}
else
return 1;
}
// Call this one to get a real-mode address
unsigned int GetAddress(unsigned int RMLocation)
{
if ((BaseDs == 0) && (InitDPMI() == 0))
return 0;
return (RMLocation - BaseDs);
}
// Call this one to get a real-mode address
// Don't call this one without calling InitDPMI
unsigned int ConvertAddress(unsigned int Address)
{
return Address - BaseDs;
}
// Sets video mode - performs no error checking (e.g wrong
// monitor.)
void SetMode(short int mode)
{
REGS regs;
regs.w.ax = mode;
regs.h.ah = 0;
int386(0x10, ®s, ®s);
}
// Main function:
void main(void)
{
// Pointer to video memory:
unsigned char *Screen = (unsigned char *)GetAddress(0xA0000UL);
// Screen buffer:
unsigned char *Buffer;
// Misc. variables:
int Input = 1, Xr = 0, Yr = 0, Zr = 0;
// Allociate memory:
Buffer = new unsigned char[64000];
Cluster = new Star [ MAXSTARS ];
// Timer and ElapsedTime variables:
Timer T; float ElapsedTime = 100;
// Abort if not enough memory:
if ((Buffer == NULL) || (Cluster == NULL))
{
Abort("\nNot enough memory to run application\n");
}
// Set video mode and palette:
SetMode(0x13);
SetPalette();
// Initiate trig tables:
InitMath();
// Change the system clock to a higher resolution:
ChangeClock ( ( short ) CLOCKSPEED );
// Start the timer:
T.Start ();
// Loop until esc key pressed:
while (Input != 27)
{
// Calculate step based on speed of computer.
// Uses following algorithm:
//
// (1) convert DISTANCEPERSECOND to
// a distance per herz value.
// (2) multiply this value by the time
// it takes to draw each frame
ZStep = ( float ) DISTANCEPERSECOND /
( float ) HZ * ( float ) ElapsedTime;
// Handle keyboard input:
if (kbhit())
{
Input = getch();
switch (Input)
{
case ('X'):
++Xr;
break;
case ('x'):
--Xr;
break;
case ('Y'):
++Yr;
break;
case ('y'):
--Yr;
break;
case ('Z'):
++Zr;
break;
case ('z'):
--Zr;
break;
case ('f'):
ViewZ = ZStep;
break;
case ('b'):
ViewZ = -ZStep;
break;
case ('s'):
ViewZ = 0.0f;
break;
case ('v'):
{
gotoxy(15, 12);
cout << "Version 0.1a";
getch();
break;
}
default:
break;
}
}
// Clear buffer:
setmem(Buffer, 64000, 0);
// Initialize viewing matrix:
ViewM.Initialize();
// Perform transformations:
ViewM.Rotate (-Xr, -Yr, -Zr);
ViewM.Translate ( 0, 0, -ViewZ );
// Loop through list of stars:
for (unsigned int Index=0; Index<MAXSTARS; Index++)
{
// Transform and draw stars:
Cluster[Index].Transform(ViewM);
Cluster[Index].Show(Buffer);
}
// Move data into viewport:
memmove(Screen, Buffer, 64000);
// Calculate time it took to draw frame
// and reset the timer:
ElapsedTime = ( float ) T.Time ();
T.Reset ();
}
// Perform a skewing demo:
while (!kbhit())
{
// Clear buffer:
setmem(Buffer, 64000, 0);
// Initialize viewing matrix:
ViewM.Initialize();
// Perform rotations and translations:
ViewM.Rotate(0, 0, 10);
ViewM.Shear(-0.1, -0.1);
// Loop through list of stars:
for (unsigned int Index=0; Index<MAXSTARS; Index++)
{
// Transform and draw stars:
Cluster[Index].Transform(ViewM);
Cluster[Index].Show(Buffer);
}
// Move data into viewport:
memmove(Screen, Buffer, 64000);
}
getch();
// Unlock memory:
delete [] Buffer;
delete [] Cluster;
// Set mode to text mode:
SetMode(0x03);
// Restore the system clocks original setting:
ChangeClock ( 0xFFFF );
}