| Blitz2D Advanced: 3D Starfields in Blitz 2D
by Shockwave / DBF 
 
 How To Create 3D Starfields Using Blitz 2D
By Shockwave / DBF. Loads of people have asked me how to code
3D stuff using only the B2D command set and as it's my favorite area I decided
that I'd do something to get you started. After you have read this tutorial, if you
have the hunger for more knowledge you can get gratification Here : CLICK
FOR DBF FORUMS If for any reason you want to email me : CLICK
TO EMAIL SHOCKWAVE Indeed a lot of very proficient programmers
never learn the intricacies of 3D programming which is a shame, the internet is
full of baffling information about it and after all, who has the patience to sit
through it? Well, I'll tell you one thing, I certainly don't!  We're going to start simply, before you start
with this tutorial I have assumed that you have a reasanoble knowledge of the
following; 
  
  
    
      | Things You
        Need To Understand Before You Read This; |  
      | Custom Types (At
        the most basic level, you'll need to be able to define types and
        understand how data is stored / accessed in their fields). |  
      | Double
        Buffering (You'll need to
        understand what the Setbuffer
        and Flip
        commands do). |  
      | Loops
        (You'll Need to understand
        them as they effect Types
        and also you'll need to know about While
        /Wend loops). |  
      | Variables
        (Locals, Globals and Constants)
        Also you'll need to understand the concept of Hex
        and also basic mathematical ( + - / * )operators. |  
      | Program Flow (The
        order of execution and Functions). |  
      | Writepixelfast
        (Just a vague idea is ok). |  Ah great, you're still here, well let's begin
to thing about the problems posed in getting a 3D image on a Monitor screen,
take a look at the monitor, it is made up of dots, for our purposes we
understand that every one of these dots has it's own  X and Y co-ordinate. X being Horizontal (Left/Right) Y being Vertical (up/Down) This presents us with no problems if we want
to draw flat images but it presents us with a few brain teasers if we decide to
add another dimension and give everything a  depth co-ordinate as well. There are
generally very well practiced techniques to computing things in 3 dimensions and
then converting them to two dimensions for the purposes of displaying them on a
TV or Monitor. Think back and I'm sure you've seen 3D
starfields before, they present an image made up of many individual dots that
fly towards the screen and then peel away at the last instant before crashing
through the screen ;-) Lets think of what we are trying to achieve then; A multitude of "Stars" slowly flying
into the screen in perspective, the closer the star is, the faster it should
appear to move, just like in real life. The first thing that we need to do then is to
open a screen for our graphics;   	Const	xres	=	640;                        
X Res.	Const	yres	=	480;                        
Y Res.
 Const	depth	=	32;                        
Depth.
 Const	window	=	1;                      
Window.
 Const	nstars	=	800;                      
How Many Stars?
 Const	escape	=	1;                        
Escape Scancode.
 Const	halfx	=	xres/2;                    
For Neatness later on X centre of screen.
 Const	halfy	=	yres/2;                    
For Neatness later on Y centre of screen.
 
 ;======================-----------------------------------------------------------------------------
 ; Open Graphics Screen;
 ;======================-----------------------------------------------------------------------------
 
 Graphics xres,yres,depth,window
 SetBuffer BackBuffer()
 
 Generally I like to use constants for as many things in
my code as I can, they are faster than other types of variable such as Globals,
the only snag is that they can't be changed once they are set up, but for the
things I've used them for here that's ok. I won't go into too much detail on
this bit, suffice to say that I intend this routine to be flexible, able to
change resolutions and the amount of stars by only having to alter one variable. Please bear this in mind when coding 3D in B2D as
things can very quickly get messy if you are a sloppy coder. After the variables are set, I open the screen and set
the double buffer to draw on the logical screen to avoid flicker :-) Lets move onto the next bit; ;=================================------------------------------------------------------------------; Create Type Definition For Star;
 ;=================================------------------------------------------------------------------
 
 Type star
 Field	xpos;
	X position of star.
 Field	ypos;
	Y position of star.
 Field	zpos#;
	Z position of star.
 Field	colour;
	Colour of star.
 End Type
 
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 
 We're going to use Types here for this starfield, they
are not as fast as arrays but the differences are negligible and the neatness
that they provide makes them more that worthwhile, as I have already hinted
earlier on we have three positional fields in this type; XPOS (Horizontal) YPOS (Vertical) ZPOS (Depth) These will be the three factors concerning the stars
position in our 3D world. Point the forefinger of your left hand at the screen. Point your thumb upwards. Point your middle finger to the side. Forefinger is Z, Thumb is Y
 Middle finger is X, if you
imagine your fingers have a length of 1 then you can begin to understand how a
point can be described as; Z=100, Y=100,
X=20 One hundred middle fingers across, 100 thumbs up, 20
forefingers in. Using this co-ordinate system it is also quite possible to have
negative numbers too. Basically this is how 3D objects are created at their most
simple level, vertices (the word vertices comes from vertex meaning point).  Each
point has an X,Y,Z co-ordinate, just like our stars will have! Also there is another field; COLOUR It's actually cool that Blitz recognises the Americal
version of the word "colour" as a reserved word, freeing it up for use
in variables for Brits like me :-p We're going to assign the field "Colour"
one of several values to denote what colour it should be drawn in, I thought it
would be nice to have all different shades of star. Let's move on and actually create the stars then; ;==============-------------------------------------------------------------------------------------; Create Stars;
 ;==============-------------------------------------------------------------------------------------
 
 For loop	=	1 To nstars
 starfield.star		=	New star
 starfield\xpos		=	Rand(-10000,10000);        
Large X To Be Divided By Z.
 starfield\ypos		=	Rand(-10000,10000);        
Large Y To Be Divided By Z.
 starfield\zpos#		=	Rnd(0,30);                        
Z,the Depth into the screen.
 starfield\colour	=	Rand(1,7);                        
Seven Possible colours.
 Next
 
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 Here we create the stars and put values into them,
you'll notice a few things about this, firstly  X and Y are HUGE ranges
 compared
to the Z. The way I'd like you to think about this is a large box that is a lot
taller and wider than it is deep having 800 points placed inside it. (X,Y,Z)
There is a good reason to use these huge numbers for X and Y. Basically it's for
perspective. The real revelation here should be that  Z  is a
floating
point number, if you take the number 10,000
and divide it by 30
you get; 333. If you multiply 333*2
you get 666,
it's not a satanic reference but it's as near as damnit to our X resolution
(640). So let's say that X =
-10000 and Z = 30,
X/Z would be -333 This is how we convert
a 3D co-ordinate into a 2D one, we divide
X by Z and Y by Z.  Imagine that X
was 1000 and Z
was 30 and we divide
100/30 we get 33, so in actual fact we convert
a 3D world co-ordinate of 1000 into a display co-ordinate of 33 and of course if
we happen to be decreasing the Z progressively to 0 our dot will move off the
screen as the result changes. That's perspective in a nutshell really and as soon as
you can visualise that concept you're sorted. Here's a simple little equation; DISPLAYX = (WORLDX / WORLDZ)+XOFFSET DISPLAYY = (WORLDY / WORLDZ)+YOFFSET Stuck? Go back and re-read! Understand? Good, let's move on; ;===========----------------------------------------------------------------------------------------; Main Loop;
 ;===========----------------------------------------------------------------------------------------
 
 While Not KeyDown(escape);         
Loop Starts.
 starfield();                        
Do Starfield.
 Flip;                                
Double Buffer.
 Cls;                                
Clear The Screen.
 Wend;                            
Loop Ends.
 End
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 
 The above code is just the main loop, if I have to
explain that you should be reading a newbies tutorial!
 However, we have now come to the real meat of the
thing! Yes folks, it's time to compute the stars and draw them; ;=======================--------------------------------------------------------------------------
 ; 3D Starfield Function;
 ;=======================--------------------------------------------------------------------------
 
 Function starfield()
 LockBuffer;                                                
Lock The Screen For WPF.
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 For starfield.star	=	Each	star
 tx=(starfield\xpos/starfield\zpos#)+halfx;                                
Get X Screen Co-Ord (X/Z)+Centre
 ty=(starfield\ypos/starfield\zpos#)+halfy;                                
Get Y Screen Co-Ord (Y/Z)+Centre
 If tx>0 And tx0 And tyIs Star On Screen?
 ;---------------------------------------------------------------------------------------------------
 If starfield\colour = 1 mulv=$080606:mulv2=$040303;    
If Colour 1 Select Red.
 If starfield\colour = 2 mulv=$060806:mulv2=$030403;    
If Colour 2 Select Green.
 If starfield\colour = 3 mulv=$080608:mulv2=$030304;    
If Colour 3 Select Blue.
 If starfield\colour = 4 mulv=$080808:mulv2=$040404;    
If Colour 4 Select White.
 If starfield\colour = 5
mulv=$080608:mulv2=$040304;     If Colour 5 Select Purple.
 If starfield\colour = 6 mulv=$080800:mulv2=$040403;    
If Colour 6 Select Yellow.
 If starfield\colour = 7 mulv=$060808:mulv2=$030404;    
If Colour 7 Select Cyan.
 ;---------------------------------------------------------------------------------------------------
 mul=Int(30-starfield\zpos#);                                             
Invert Z Co-Ords (For colour multiply).
 ;---------------------------------------------------------------------------------------------------
 mulv=mulv*mul;            
Generate Bright Colour.
 mulv2=mulv2*mul;        
Generate Dark Colour.
 ;---------------------------------------------------------------------------------------------------
 WritePixelFast tx,ty,mulv;                  
Draw Bright Centre.
 WritePixelFast tx-1,ty,mulv2;            
Dark Left.
 WritePixelFast tx+1,ty,mulv2;            
Dark Right.
 WritePixelFast tx,ty-1,mulv2;            
Dark Up.
 WritePixelFast tx,ty+1,mulv2;            
Dark Down.
 starfield\zpos#=starfield\zpos#-.2;     Move Star.
 Else;                        
Generate New Star If Off Screen;
 ;---------------------------------------------------------------------------------------------------
 starfield\xpos		=	Rand(-10000,10000);     Large X To Be Divided By Z.
 starfield\ypos		=	Rand(-10000,10000);     Large Y To Be Divided By Z.
 starfield\zpos#=30
 End If
 Next
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 UnlockBuffer; Free The Screen.
 End Function
 That' a big chunk of code and it's plenty to get your
head around no matter how well commented it appears to be so let's step through
it bit by bit, firstly if you've used  Writepixelfast before then you'll know
that  Blitz requires you to lock the screen before you use it, it's a simple
thing to do and just use the  Lockbuffer() command, and in fact the only other
thing to be careful of is that all your drawing operations are actually on the
screen, I know it sounds stupid but you should make sure that you  do not WPF
outside the screen or  you'll most probably crash your IDE. Next we loop through each one of our stars,  bearing in
mind that they all have 3D co-ordinates at the moment, the first thing we
 need
to do is to  convert them to 2D
co-ordinates;     tx=(starfield\xpos/starfield\zpos#)+halfx;                                 Get X Screen
Co-Ord (X/Z)+Centrety=(starfield\ypos/starfield\zpos#)+halfy;                                
Get Y Screen
Co-Ord (Y/Z)+Centre
 
 As I described above, now please note that  negative
values are perfectly acceptable but if we  add the centre co-ordinates (halfx and
halfy) to them then we bring them all on screen and
 centre them around the
middle. Because we need to be careful that the star is onscreen
we need to check;         If tx>0 And tx0 And tyIs Star On Screen?
 and scan down the program a few lines         Else;                        
Generate New Star If Off Screen;;---------------------------------------------------------------------------------------------------
 starfield\xpos		=	Rand(-10000,10000);     Large X To Be Divided By Z.
 starfield\ypos		=	Rand(-10000,10000);     Large Y To Be Divided By Z.
 starfield\zpos#=30
 End If
 
 If  it's not on screen we don't draw and
 we assume
that it has flown  off screen so we
 generate a new X+Y position (to stop it
repeating) and we  set the Z to 30 (maximum depth). Let's assume that our transformed point is onscreen
though and we're going to draw it! :-)            
If starfield\colour = 1 mulv=$080606:mulv2=$040303;     If Colour 1 Select Red.If
starfield\colour = 2 mulv=$060806:mulv2=$030403;     If Colour 2 Select Green.
 If
starfield\colour = 3 mulv=$080608:mulv2=$030304;     If Colour 3 Select Blue.
 If
starfield\colour = 4 mulv=$080808:mulv2=$040404;     If Colour 4 Select White.
 If
starfield\colour = 5 mulv=$080608:mulv2=$040304;     If Colour 5 Select Purple.
 If
starfield\colour = 6 mulv=$080800:mulv2=$040403;     If Colour 6 Select Yellow.
 If
starfield\colour = 7 mulv=$060808:mulv2=$030404;     If Colour 7 Select Cyan.
 
 First of all, please don't be frightened by that,
remember that we had a type field called colour? Well, depending on this colour
we're just going to set a basic colour for our star, there are 7 possiblities
here as indicated by the comments, now this is how the colour component of
Writepixelfast is made up; Writepixelfast  XPOS , YPOS,
$000000 Notice how it breaks down into 3 blocks? Well each
block is a byte and can represent 256 different
numbers (0 to 255) Need to know how? Ok, well in decimal you can represent
10 values with each digit; 0123456789  In Hex you can have 16 values with one digit; $ 0123456789ABCDEF The $ before
a number signifies it is in hex.  So in Decimal; 9*9 = 81 In Hex; F*F ($FF) =255 FF is obviously a
lot more than 00,
so basically with these byte pairs we can describe colours for Writepixelfast.
In the starfield code they are small numbers because we're
going to be multiplying
them by the Stars Z co-ordinate so they get brighter
as they fly into the screen! But we've hit another brick wall there as we said
earlier that the Z needs to decrease to move the stars towards us so if we
multiply then they will get dimmer as they fly towards us which is not the
result we want... To invert this I've used this code;            
mul=Int(30-starfield\zpos#);                                              Invert Z Co-Ords (For colour multiply).
 Now, please  note that the INT command removes the
numbers sign, ie it changes negative into positive, calculations in brackets are
performed FIRST, remember the rule of  BODMAS (Brackets, of, divide,
multiply, addition, subtraction) Imagine if the
 Z co-ordinate is  25 so it would
be quite far into the screen, we  subtract 30 from it (the depth of our starfield) like this; 30-25 = -5 and then turn it into a whole positive number int(-5) = 5 So our  -25 has
 effectively turned into a 5 which is
useful when we do this;            
mulv=mulv*mul;             		Generate Bright
Colour.mulv2=mulv2*mul;         	Generate Dark Colour.
 
 This is how we generate our colours. Don't understand? Read it again. Understand? Good, carry on;                
WritePixelFast tx,ty,mulv;                  
Draw Bright Centre.WritePixelFast tx-1,ty,mulv2;            
Dark Left.
 WritePixelFast tx+1,ty,mulv2;            
Dark Right.
 WritePixelFast tx,ty-1,mulv2;            
Dark Up.
 WritePixelFast tx,ty+1,mulv2;            
Dark Down.
 
 The Above draws the stars.... Below moves them :-)                    
starfield\zpos#=starfield\zpos#-.2;     Move Star.
 We  just subtract a small amount
 from the Z co-ordinate
of each star, when the perspective throws them off the screen don't worry, we'll
reset the Z and make a new one :) Don't forget to use Unlockbuffer to free the
screenbuffer again by the way. Well done, here's the finished code;   ;=========================--------------------------------------------------------------------------; (C) Shockwave / DBF 2003
 ;=========================--------------------------------------------------------------------------
 ;
 ; Tutorial and code created by Shockwave / DBF (C) 2003.
 ; For More Sample Code visit the DBF development forums;
 ;
 ; http://pub161.ezboard.com/bdbf
 ;
 ; Or The Blitzcoder forums;
 ;
 ; www.Blitzcoder.com
 ;
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 ;
 ; This Routine Draws A 3D Starfield Using  Only B2D Commands, It Is Based Around Types And
 ; Writepixelfast.
 ;
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 
 	Const	xres	=	640;	X Res.Const	yres	=	480;	Y Res.
 Const	depth	=	32;		Depth.
 Const	window	=	1;		Window.
 Const	nstars	=	800;	How Many Stars?
 Const	escape	=	1;		Escape Scancode.
 Const	halfx	=	xres/2;	For Neatness later on X centre of screen.
 Const	halfy	=	yres/2; For Neatness later on Y centre of screen.
 
 ;======================-----------------------------------------------------------------------------
 ; Open Graphics Screen;
 ;======================-----------------------------------------------------------------------------
 
 Graphics xres,yres,depth,window
 SetBuffer BackBuffer()
 
 ;=================================------------------------------------------------------------------
 ; Create Type Definition For Star;
 ;=================================------------------------------------------------------------------
 
 Type star
 Field	xpos;	X position of star.
 Field	ypos;	Y position of star.
 Field	zpos#;	Z position of star.
 Field	colour;	Colour of star.
 End Type
 
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 
 
 ;==============-------------------------------------------------------------------------------------
 ; Create Stars;
 ;==============-------------------------------------------------------------------------------------
 
 For loop	=	1 To nstars
 starfield.star		=	New star
 starfield\xpos		=	Rand(-10000,10000); Large X To Be Divided By Z.
 starfield\ypos		=	Rand(-10000,10000); Large Y To Be Divided By Z.
 starfield\zpos#		=	Rnd(0,30);			Z, the Depth into the screen.
 starfield\colour	=	Rand(1,7);			Seven Possible colours.
 Next
 
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 
 ;===========----------------------------------------------------------------------------------------
 ; Main Loop;
 ;===========----------------------------------------------------------------------------------------
 
 While Not KeyDown(escape); 	Loop Starts.
 starfield();			Do Starfield.
 Flip; 					Double Buffer.
 Cls; 					Clear The Screen.
 Wend;						Loop Ends.
 End
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 
 
 ;=======================--------------------------------------------------------------------------
 ; 3D Starfield Function;
 ;=======================--------------------------------------------------------------------------
 
 Function starfield()
 LockBuffer; Lock The Screen For WPF.
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 For starfield.star	=	Each	star
 tx=(starfield\xpos/starfield\zpos#)+halfx; Get X Screen Co-Ord (X/Z)+Centre
 ty=(starfield\ypos/starfield\zpos#)+halfy; Get Y Screen Co-Ord (Y/Z)+Centre
 If tx>0 And tx0 And ty
;---------------------------------------------------------------------------------------------------
 If starfield\colour = 1 mulv=$080606:mulv2=$040303; If Colour 1 Select Red.
 If starfield\colour = 2 mulv=$060806:mulv2=$030403; If Colour 2 Select Green.
 If starfield\colour = 3 mulv=$080608:mulv2=$030304; If Colour 3 Select Blue.
 If starfield\colour = 4 mulv=$080808:mulv2=$040404; If Colour 4 Select White.
 If starfield\colour = 5 mulv=$080608:mulv2=$040304; If Colour 5 Select Purple.
 If starfield\colour = 6 mulv=$080800:mulv2=$040403; If Colour 6 Select Yellow.
 If starfield\colour = 7 mulv=$060808:mulv2=$030404; If Colour 7 Select Cyan.
 ;---------------------------------------------------------------------------------------------------
 mul=Int(30-starfield\zpos#); Invert Z Co-Ords (For colour multiply).
 ;---------------------------------------------------------------------------------------------------
 mulv=mulv*mul; 		Generate Bright Colour.
 mulv2=mulv2*mul; 	Generate Dark Colour.
 ;---------------------------------------------------------------------------------------------------
 WritePixelFast tx,ty,mulv;		Draw Bright Centre.
 WritePixelFast tx-1,ty,mulv2; 	Dark Left.
 WritePixelFast tx+1,ty,mulv2; 	Dark Right.
 WritePixelFast tx,ty-1,mulv2;	Dark Up.
 WritePixelFast tx,ty+1,mulv2;	Dark Down.
 
 starfield\zpos#=starfield\zpos#-.2; Move Star.
 ;---------------------------------------------------------------------------------------------------
 Else; Generate New Star If Off Screen;
 ;---------------------------------------------------------------------------------------------------
 starfield\xpos		=	Rand(-10000,10000); Large X To Be Divided By Z.
 starfield\ypos		=	Rand(-10000,10000); Large Y To Be Divided By Z.
 starfield\zpos#=30
 End If
 Next
 ;---------------------------------------------------------------------------------------------------
 ;---------------------------------------------------------------------------------------------------
 UnlockBuffer; Free The Screen.
 End Function
 For a printable copy of this article, please click HERE.
 
   
 
 |