Final Project

Walk Through 3D

Programmers:

Project Leader: Gregg Miskelly - 3D code (BSP)
Project Members: Dave Bellia - FPU and VGA, Rob Ewald - Input and Serial communication


Introduction:

In a sentence, our project will enable you to 'walk' around a 3D world with six degrees of freedom using a 3D input device that we make. The concept of a six-degree of freedom 3D world is not a new one. Even at the PC level, games that utilize this kind of technology have existed for two or three years. However, it is always difficult to come up with an input system to take advantage of the power of a six-degree environment. Many of the input systems can be described as awkward at best. The CAVE at Beckman and a want to do something interesting that combined both hardware and software inspired us to come up with a new way of looking around a 3D world. We hope you will like it.
Here's our program: final.zip

Implementation

Hardware Summary:

We will produce a 3D input device. This device will be pen shaped with two buttons on it, and two emitters. Using detectors in a box, we will be able to determine the orientation and position of the pen. This will be calculated by the ECE 249 microcontroller. The two buttons will be used to calibrate and pause the input device. The input device will then communicate with a PC through a Com or Parallel port.

Software Summary:

There are several pieces of software that we must write (this list does not include software that runs on the microcontroller). First, we must have a piece of software to interface with the microcontroller over a serial or parallel port. This data will be input to the control unit of our simulator. The control unit will also accept mouse and keyboard input. The control unit will then render a 3D mesh (probably a proprietary format that will contain a BSP created by importing a DXF) to our current position. We will probably write to an SVGA display.

Block Diagram:

Microcontroller-+      Mouse Input----+
                |                     |
                |   Keyboard Input-+  |
                |                  |  |
+------------------+  +-----------------+
| Serial Interface |--|  Control Unit   |-----+
+------------------+  +-----------------+     |
                        |   |                 |
                        |   |                 |
     Rendered BSP Tree--+   |        +------------------+
                            |        | Recurse BSP Tree |
                            |        +------------------+
                            |                 |
                            |       +--------------------+
        +---Init Disp Sig---+       | 3D Transformations |-+
        |                           +--------------------+ |
        |                                                  |
        |  +-----------------+     +--------------+        |
        +--| SVGA Controller |<----| Draw Polygon |<-------+
           +-----------------+     +--------------+
Block Diagram for DSP renderer
           +------------------+    +---------------+
DXF File---| BSP Tree Creator |----| Split Polygon |---+
           +------------------+    +---------------+   |
                |    |                                 |
                |    +---------------------------------+
                |
                +--------- BSP File

Global Variables and Types:

The data structures for this project are not complex, but they are more complicated then in most projects. For the mesh, we need the polygons to be stored into a BSP tree. Memory is acquired by decrementing the stack pointer by the appropriate amount at load time. Other global variables are our transformation matrix, our current viewpoint, and vectors to the old mouse and keyboard handlers.

Types:

struct Polynode {  // polygon and node to a BSP tree
        Point3D rgPoints[3];
        Vector vNormal;
        struct Polynode *Front, *Back;
}

struct Point3D
{
        float x,y,z;
}

typedef Vector Point3D;

struct Point2D
{
        int x,y;
}

typedef Matrix float[16]; // 4x4 matix
Variables:
Matrix g_ProjM
; ProjM - Projection Matrix, this value is constant 
;
;                             XRES        
;                           ---------   0
;   SK XRES     0           2 ZSCREEN   
;
;                             YRES      
;                           ---------   0
;   0           -SK YRES    2 ZSCREEN   
;
;
;
;   0           0              1        -Zscreen
;
;                              1
;                           -------     0
;   0           0           ZSCREEN     


g_VAngleM       LABEL   real4   
        g_StrifeUV      real4   1.0,0.0,0.0,0.0         ; Strife Vector (to your side)
        g_UpUV          real4   0.0,1.0,0.0,0.0         ; Up Vector
        g_ViewUV        real4   0.0,0.0,1.0,0.0         ; View or Face Vector
                        real4   0.0,0.0,0.0,1.0         ; Remaing data to finish off the Matrix
; VAngleM - Rotation Matrix about the current view angle

g_VPointM       real4   1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0
; VPointM - Computed Each time the position changes
;   1     0     0     -xp
;   0     1     0     -yp
;   0     0     1     -zp                        
;   0     0     0     1

TempMatrix      real4   16      dup(?)

g_BltM          real4   1.0,0.0,0.0,0.0,  0.0,-0.625,0.0,0.0, 2.13333,1.33333,1.0,0.0133333,
        160.0, 100.0, 0.0, 1.0
; Blit Matrix = ProjM*VAngleM*VPointM
;   This Matrix is used to Transform from World Points to ScreenPoints 

g_rgPoints3D    POINT3D NUMPOINTS       dup(<>)
        ; List of 3D Points (each point is 12 bytes)
g_rgBSP         BSPNode NUMPOLYS        dup(<>) 
        ; This is a pointer to the Root of the BSP Tree
g_rgPoints2D    POINT2D NUMPOINTS       dup(<>)
        ; List of transformed Points (each point is 4 bytes)
g_rgPntsStatus  db      4*((NUMPOINTS+15)/16)  dup (?)
        ; Table saying if a point has been transformed (2 bits/point)

ZeroOne         real4   -0.01
        
g_Color - the color of the polygon

Point3D FrontPoint
Point3D BackPoint
PolyNode *g_Root;    // pointer to root node
Matrix g_Matrix;     // 2D to 3D transformation Matrix

Screen Shot

Procedures

  1. BSP Compiler
  2. Main Loop/program control (Main.asm)
  3. Floating Point (float.asm)
  4. VGA Graphics (vga.asm)
  5. 3D Code (3d.asm)
  6. Polygon Code (polygon.asm)
  7. Keyboard Inputs

    InstKey
         Saves the old keyboard interrup vector to the variables "OldVector" and
     installs the new procedure, KeyInterrupt.

    DeInstallKey
         Restores the old keyboard interrupt vector saved by OldVector.

    KeyInterrupt
         Reads in the scan code from port 60h.  First it checks if the key was ESC and
     sets ExitFlag to 1 if so. Otherwise it checks for movement using comparisons to
     divide the keyboard into sections instead of checking each key individually.
     The controls are:
     
     
    Q
    Rotate Counter Clockwise
    W
    Move Forward
    EEE
    Rotate Clockwise
    A
    Rotate Left
    S
    (nothing)
    D
    Rotate Right
    Z
    Sidestep Left
    X
    Move Backward
    C
    Sidestep Right
     
    Additionally, P moves up and L moves down.

    If the key pressed is a valid input, the correct bit is set in either the Movement or Rotation byte flags, or cleared if the interrupt was a key release. The bits are defined as:

    Movement:    x x DOWN UP LEFT RIGHT BACK FORWARD
    Roatation:    x x DOWN UP LEFT RIGHT ROLLCCL ROLLCL

    KeyMovement
        This is called when the keyboard input is to be used to move within the scene.  It first tests the bits of the movement flag to see if there is motion in each axis, where 0 or both bits being set is no movement at all. A copy of the flag is then modified to be used in jump tables to take the appropriate action for each axis. The Rotation flag is tested next using the same procedure. This function calls the SetCameraPosition function with the vector to move in and direction (forward or back) when movement is detected and the RotateAboutAxis with the vector and rotation amout (a constant here) to rotate about when rotation is detected.
     

    Mouse Inputs

    MouseProc
        First INT 33h is called to get the position and button status. The flags LeftStatus and RightStatus are set to correspond to the buton being on (1) or off (0). Next SetCamerAngle is called with the new position of the mouse still in CX and DX. Next SetUnitVector is called to adjust the g_ViewUV vector to the new angle. Then the button status variables are put into one byte,  mouseDir, with bit 1 representing the left button and bit 0 representing the right bit. Since the mouse can only move forward or back, SetCameraPosition is called with the direction in mouseDir and the vector g_ViewUV, the forward facing direction. Finally the new mouse position is saved to the mouseRow and mouseColumn variables, used in SetCameraAngle.

    SetCameraAngle
        Just a wrapper for calling SetYaw and SetPitch.

    SetYaw
        The difference between the current column (CX) and the previous column (mouseColumn) is loaded into the FPU. It is then divided by a scaling constant and the result is the number of radians to rotate. Then RotateAboutVector is called with this data and the g_UpUV vector to rotate about.

    SetPitch
        The difference between the current row (DX) and the previous row (mouseRow) is loaded into the FPU. It is then divided by a scaling constant and the result is the number of radians to rotate. Then RotateAboutVector is called with this data and the g_StrifeUV vector to rotate about.

    SetCameraPosition
        Checks the direction byte passed to it to see if we are moving forward or back and calls the _SetNewX functions to move in each axis.

    SetNewX
        Loads the X portion of the vector to move in and adds it to the current X position. The direction vector can be divided by an constant to slow movement.

    SetNewY and SetNewZ
        Same procedures as X but uses the Y and Z components of the direction vector.

    SetUnitVector
        Using  x = CosX SinY,  y = -SinX, and z = CosX CosY, calculate the new unit vector the camera is facing according to the new angles set by SetCameraAngle. Store the X, Y, and Z results in g_ViewUV.
     

    Serial Input

    SerialProc
        Wrapper for all the serial functions. Calls SerialWrite, SerialRead, InterpetData, and SerialAngle.

    SerialInit
        Called in main, it sets the serial port in COMPORT to 9600 bps, 8 data bits, no parity, 1 stop bit. Uses INT 14h.

    SerialWrite
        Using INT 14h, send the byte FFh out the serial port to the microcontroller. This is interpeted by the controller as a request for position data.

    SerialRead
        Using BIOS interrupt 14h, it reads in 8 bytes of data. They are all A to D conversions of the intensity received by the sensor. After the bytes are stored as byte memory values, there is a small loop that stores the bytes as different word values so they can be loaded by the FPU.

    InterpetData
        Using the mathmatical principle that 4 intersecting spheres = 1 point, find the position of the two ends of the input device. Each sensor reading is proportional to the distance from the sensor to the light. (I = 1/r^2). The equations used to find the points from the distances to the four sensors are:

    y=(A-B)/4y1    x=-[2y12-A-B+2C]/8x1    z=z1 + [D-x2-y2]^0.5

    where A, B, C, and D are the values read from the four sensors and x1,  y1, and z1 are the coordinates of sensor #1. The first loop performs the conversion from intensity to distance, the second finds the location of the two points. The inputs for the equations are the variables Front1-4 and Back1-4. The results are written to g_Xpos, g_Ypos, and g_Zpos.

    SerialAngle
        Using the two points just calculated, find the angle between them. The formulas used are Phi=arctan(y/x) and Theta=arctan[(x2+y2)^.5/z]. The point used is the difference between the two X, Y, and Z values (g_Xpos and Backx). The results are written to g_UpUV and g_StrifeUV.