Chapter 3: Graphics

The original code used Mode 13h of the VGA card which was 320x200 and 256 colors. In the conversion code I am using Screen 18 which is 640x480 and I am using 32bit color mode. Using 32bit colors will make the following chapter code look much beter and allow some flexibility in creating both the models and lighting.

Since I am using 32bit colors, I did not add any of the palette switching code that was in the original C code. However, I did create a fake VGA palette, just to have the look anf feel of the old code. In future installments, I will only use the 32bit mode and inline more of the graphics functions to make things run a bit faster.

Here is the include file, black3.bi:

'Black3.bi 
'Library file from Chapter 3 of Black Art of 3D Game Programming by Andre LaMothe
'Converted from C to FreeBasic by Richard Clark
'I have included the C comments as a reference point for the C source.
'Copyright The Waite Group (c) 1995
'==================================================================================
'// I N C L U D E S ///////////////////////////////////////////////////////////

'RDC: None needed.

'// G L O B A L S //////////////////////////////////////////////////////////////
'RDC: We can skip the Mode 13h stuff. We don't need it. We are going to use Screen 18
'for all these programs, i.e. 640x480, 32bit colors. However, we will build a fake
'vga palette so as to implement the palette transformations. This is the base palette
'that we will use to restore the palette if we need to make changes to the working palette.
'RDC: This will be our working vga palette.
Dim Shared As Uinteger vga_palette(0 to 255) = {_
4278190080,4278190248,4278233088,4278233256,4289200128,4289200296,4289221632,4289243304,4283716692,_
4283716860,4283759700,4283759868,4294726740,4294726908,4294769748,4294769916,4278190080,4279505940,_
4280295456,4281084972,4281874488,4282664004,4283453520,4284506208,4285558896,4286611584,4287664272,_
4288716960,4290032820,4291348680,4292927712,4294769916,4278190332,4282384636,4286316796,4290511100,_
4294705404,4294705340,4294705276,4294705216,4294705152,4294721536,4294736896,4294753280,4294769664,_
4290575360,4286381056,4282448896,4278254592,4278254656,4278254716,4278254780,4278254844,4278238460,_
4278222076,4278206716,4286348540,4288445692,4290542844,4292639996,4294737148,4294737116,4294737084,_
4294737052,4294737020,4294745212,4294753404,4294761596,4294769788,4292672636,4290575484,4288478332,_
4286381180,4286381212,4286381244,4286381276,4286381308,4286373116,4286364924,4286356732,4290032892,_
4291081468,4292392188,4293440764,4294751484,4294751464,4294751448,4294751428,4294751412,4294755508,_
4294760628,4294764724,4294769844,4293459124,4292410548,4291099828,4290051252,4290051268,4290051288,_
4290051304,4290051324,4290046204,4290042108,4290036988,4278190192,4280025200,4281860208,4283695216,_
4285530224,4285530196,4285530168,4285530140,4285530112,4285537280,4285544448,4285551616,4285558784,_
4283723776,4281888768,4280053760,4278218752,4278218780,4278218808,4278218836,4278218864,4278211696,_
4278204528,4278197360,4281874544,4282660976,4283709552,4284495984,4285544560,4285544544,4285544532,_
4285544516,4285544504,4285547576,4285551672,4285554744,4285558840,4284510264,4283723832,4282675256,_
4281888824,4281888836,4281888852,4281888864,4281888880,4281884784,4281881712,4281877616,4283453552,_
4283977840,4284502128,4285026416,4285550704,4285550696,4285550688,4285550680,4285550672,4285552720,_
4285554768,4285556816,4285558864,4285034576,4284510288,4283986000,4283461712,4283461720,4283461728,_
4283461736,4283461744,4283459696,4283457648,4283455600,4278190144,4279238720,4280287296,4281335872,_
4282384448,4282384432,4282384416,4282384400,4282384384,4282388480,4282392576,4282396672,4282400768,_
4281352192,4280303616,4279255040,4278206464,4278206480,4278206496,4278206512,4278206528,4278202432,_
4278198336,4278194240,4280295488,4280819776,4281344064,4281868352,4282392640,4282392632,4282392624,_
4282392616,4282392608,4282394656,4282396704,4282398752,4282400800,4281876512,4281352224,4280827936,_
4280303648,4280303656,4280303664,4280303672,4280303680,4280301632,4280299584,4280297536,4281084992,_
4281347136,4281609280,4282133568,4282395712,4282395708,4282395700,4282395696,4282395692,4282396716,_
4282397740,4282399788,4282400812,4282138668,4281614380,4281352236,4281090092,4281090096,4281090100,_
4281090108,4281090112,4281089088,4281087040,4281086016,4278190080,4278190080,4278190080,4278190080,_
4278190080,4278190080,4278190080,4278190080}

'RDC: We will store the font sizes here.  

Dim Shared char_height As Integer = 16 'RDC: Using the default settings for screen 18.
Dim Shared char_width As Integer = 8

'// F U N C T I O N S /////////////////////////////////////////////////////////

'RDC: In the orirignal source this did a lookup into the ROM for the font and was
'used by Print_String to print characters. We really don't need this as we can just 
'print a whole string using Draw String which is faster and easier. However, this may
'be used in the sources elsewhere so I will include it here. The var clr here (and following)
'refers to the vga_palette index which stores all of our colors.
Sub Print_Char (xc As Integer, yc As Integer, c As string, clr As Integer, transparent As Integer)
    '// this function is used to print a character on the screen. 
    'RDC: We are just going to use Draw String for this since it is easier and faster. If the
    'character is not transparent then we'll draw the background in black first.
    If transparent = 0 Then
        'Set background to black.
        Line (xc, yc) - (xc + char_width, yc + char_height), vga_palette(0), BF 'Filled box.
        'Draw the character.
        Draw String (xc, yc), c, vga_palette(clr)
    Else
        'Just draw the chatacter on the screen.
        Draw String (xc, yc), c, vga_palette(clr) 
    EndIf
End Sub '// end Print_Char

'//////////////////////////////////////////////////////////////////////////////

Sub Print_String(x As Integer, y As integer, clr As Integer, c As string, transparent As Integer)
    Dim slen As Integer

    '// this function prints an entire string on the screen with fixed spacing
    '// between each character by calling the Print_Char() function
    'RDC: We are going to just print the whole string here, rather than calling
    'Print_Char since we don't need to do a character lookup like the original source.

    'RDC: Get the length of the string. Not in original source, but we will need it
    'to draw a background and for some error checking.
    slen = Len(c)
    'RDC: Nothing to print so exit.
    If slen > 0 Then
        'RDC: 
        If transparent = 0 Then
            'Set background to black.
            Line (x, y) - (((x + CHAR_WIDTH) * slen), y + CHAR_HEIGHT), vga_palette(0), BF 'Filled box.
            'Draw the character.
            Draw String (x, y), c, vga_palette(clr)
        Else
            'Just draw the chatacter on the screen.
            Draw String (x, y), c, vga_palette(clr) 
        EndIf
    End If
End Sub '// end Print_String

Sub Write_Pixel(x As Integer, y As integer, clr As Integer)
        '// plots the pixel in the desired color a little quicker using binary shifting
        '// to accomplish the multiplications
        '// use the fact that 320*y = 256*y + 64*y = y<<8 + y<<6

        'RDC: Just use Pset here.
        PSet(x, y), vga_palette(clr)
End sub '// end Write_Pixel

'//////////////////////////////////////////////////////////////////////////////

Sub Set_Graphics_Mode
    '// use the video interrupt 10h and the C interrupt function to set
    '// the video mode
    'RDC: We are just going to use screen 18, 32bit color. 
    Screen 18, 32 'We are only going to use 1 page here, since we can use Screenlock to write to screen.
End Sub '// end Set_Graphics_Mode

'/////////////////////////////////////////////////////////////////////////////

Sub Time_Delay(clicks As Integer)
'// this function uses the internal timer to wait a specified number of "clicks"
'// the actual amount of real time is the number of clicks * (time per click)
'// usually the time per click is set to 1/18th of a second or 55ms

Dim dnow As Double

'// get current time

dnow = Timer

'// Wait for number of click to pass

Do
    Sleep 1 'Need to do this so system can run other tasks
Loop Until Timer > (dnow + clicks)

End Sub

'////////////////////////////////////////////////////////////////////////////////

Sub Line_H(x1 As integer, x2 As integer, y As integer, clr As Integer)
    '// draw a horizontal line using the memset function
    '// this function does not do clipping hence x1,x2 and y must all be within
    '// the bounds of the screen
    'RDC: We can just use the FB's line function for this since it will be faster
    'than anything we can code here.

    '// sort x1 and x2, so that x2 > x1

    if x1 > x2 Then
        Swap x1, x2
   End if '// end swap
    '// draw the row of pixels
    Line (x1, y)-(x2, y), vga_palette(clr)
End sub '// end Line_H

'//////////////////////////////////////////////////////////////////////////////

Sub Line_V(y1 As integer, y2 As integer, x As integer, clr As Integer)
    '// draw a vertical line, note that a memset function can no longer be
    '// used since the pixel addresses are no longer contiguous in memory
    '// note that the end points of the line must be on the screen
    'RDC: Again we will just use FB's line method.

    if y1 > y2 Then
        Swap y1, y2
    EndIf '// end swap
    Line (x, y1)-(x, y2), vga_palette(clr)
End sub '// end Line_V

'//////////////////////////////////////////////////////////////////////////////
'RDC: For clr here we need an RGB color to put into the palette array, hence it is a UInteger.
Sub Write_Color_Reg(index As integer, clr As UInteger)
    '// this function is used to write a color register with the RGB value
    '// within "color"
    '// tell vga card which color register to update
    'RDC: We are just going to update our palette array here.

    If (index >= 0) And (index <= UBound(vga_palette)) Then
        vga_palette(index) = clr
    EndIf

End sub '// end Write_Color_Reg

'///////////////////////////////////////////////////////////////////////////////

Function Read_Color_Reg(index As integer) As UInteger
    '// this function reads the RGB triple out of a palette register and places it
    '// into "color"
    'RDC: We are just going to get out value from the vag_palette array.

    If (index >= 0) And (index <= UBound(vga_palette)) Then
        Return vga_palette(index)
    Else
        Return 0
    EndIf

End function '// end Read_Color_Reg

'///////////////////////////////////////////////////////////////////////////////

Sub Read_Palette(start_reg As integer, end_reg As integer, the_palette() As UInteger)
    '// this function will read the palette registers in the range start_reg to
    '// end_reg and save them into the appropriate positions in the_palette

    Dim index As integer '// used for looping

    '// read all the registers
    for index = start_reg to end_reg
       '// read the color register
        the_palette(index) = Read_Color_Reg(index)
   Next '// end for index
End sub '// end Read_Palette

'///////////////////////////////////////////////////////////////////////////////

Sub Write_Palette(start_reg As integer, end_reg As integer, the_palette() As UInteger)
    '// this function will write to the color registers using the data in the
    '// sen palette structure
    'RDC: We are just updating the vga_palette array here.

    Dim index As integer '// used for looping

    '// write all the registers

    for index = start_reg to end_reg
       '// write the color registers using the data from the sent palette

        Write_Color_Reg(index, the_palette(index))

    Next '// end for index

End sub '// end Write_Palette

'///////////////////////////////////////////////////////////////////////////////

Sub Draw_Rectangle(x1 As integer, y1 As integer, x2 As integer, y2 As integer, clr As integer)
    '// this function will draw a rectangle from (x1,y1) - (x2,y2)
    '// it is assumed that each endpoint is within the screen boundaries
    '// and (x1,y1) is the upper left hand corner and (x2,y2) is the lower
    '// right hand corner
    'RDC: We are just going to use the box function of line here.
    Line (x1, y1)-(x2, y2), vga_palette(clr), B
End sub '// end Draw_Rectangle

'////////////////////////////////////////////////////////////////////////////////

Sub Fill_Screen(clr As Integer)
    '// this function will fill the entire screen with the sent color
    '// use the inline assembler for speed
    'RDC: We are just going to use CLS for this.
    Color , vga_palette(clr)
    Cls

End sub '// end Fill_Screen

'RDC: I am not including the ModeZ functions since they are the same as the Mode 13h functions.

Here is the main code, Mode18.bas:

'// MODE13.C - A demo of all the mode 13h functions for this chapter
'RDC: Mode18: sing screen 18 here, 640x480 rather than 320x200. Hence,
'I am calling this Mode18 rather than Mode13.

'// I N C L U D E S ///////////////////////////////////////////////////////////
#include "black3.bi" '/ the header file for this module

Dim index As Integer
Dim save_palette(0 To 255) As UInteger

Randomize Timer

'/ set the graphics mode to mode 13h
'RDC: Will be screen 18
Set_Graphics_Mode

'// show some text

Print_String 0, 0, 15, "Hit a key to see text printing...", 0 
Sleep

'// print a few hundred strings on the screen

For index = 0  To 1000
    Print_String Rnd * (639 - Len("This is a demo of text printing")), Rnd * 479, Rnd * 254, "This is a demo of text printing", 1
Next
Print_String 0, 0, 15, "Hit a key to see screen filling...", 0
Sleep

'// fill the screen dark grey

Fill_Screen 8
Print_String 0, 0, 15, "Hit a key to see pixel plotting...", 0
Sleep

'// plot 10000 random pixels

for index = 0 To 10000
    Write_Pixel Rnd * 639, Rnd * 479, 12
Next
Print_String 0, 0, 15, "Hit a key to see lines...", 0
Sleep

'// draw 1000 randomly positioned horizontal and vertical lines

for index = 0 To 1000
    Line_H Rnd * 639, Rnd * 639, Rnd * 479, Rnd * 254
    Line_V Rnd * 479, Rnd * 479, Rnd * 639, Rnd * 254
Next '// end for index

Print_String 0, 0, 15, "Hit a key to end program...", 0
Sleep

'Since we are doing this in 32bit color mode, we can't do palette shifts
'so they won't be in ths program. However, in FB 256 color mode, you can shift 
'palettes much like you could in the old VGA cards.