Code archives/3D Graphics - Misc/Spacegame tech demo

This code has been declared by its author to be Public Domain code.

Download source code

Spacegame tech demo by Krischan
(Posted 18 years ago)
This is a small demo made of parts from my current project. It demonstrates a simple Freelancer-like steering and a planet with atmosphere glow. When you get close to the planet the atmosphere effect shows up and it even includes a check if you are at the dayside or nightside and fades the stars at the dayside. The glowing ring always keeps the same size as the planet. Have fun!

Steering: Mouse, Arrows
Red Cross: Flight direction
Green Box: Target direction
SPACE = Wireframe on/off
AppTitle "Planet Atmosphere Glow Demo"
; by Krischan webmaster(at)jaas.de

; Constants
Const ScreenWidth%			= 800			; Screen width
Const ScreenHeight%			= 600			; Screen height
Const ColorDepth%			= 32			; Color depth
Const ScreenMode%			= 2				; Screen Mode
Const MouseSpeed#			= 0.1			; Mousespeed
Const CameraSmoothness#		= 10			; Smoothness of movement
Const Scale#				= 12.756		; Approx. the diameter of earth (1 unit = 1000km)
Const GameTime%				= 20			; Game speed
Const CursorSize%			= 33			; Size of cursor and crosshair image
Const TurnSpeed#			= 2.0			; Turnspeed of player
Const RollSpeed#			= 1.0			; Roll speed
Const MaxRollAngle#			= 20.0			; Maximum roll angle
Const RingDetail%			= 60			; Number of segments

; Glow Color scheme
Const R1%=192,G1%=224,B1%=255				; Surface near color
Const R2%=128,G2%=160,B2%=255				; Surface far color

; Variables
Global CameraSpeed#			= Scale/10.0	; Movement speed
Global TargetDistance#		= 100.0*Scale	; Mousetarget distance to player
Global FrameTime			= MilliSecs()	; Initialize Frame Timer
Global Period%				= 1000/75.0		; Calc frame period (here: 75FPS)

; help variables and objects
Global MX%,MY%								; Mouse position
Global Player%,MouseTarget%,Cam%,Ship%		; Player
Global Cursor%,Cross%						; Images
Global Planet%,Light%,Sun%,StarBox%			; Scene objects
Global GlowPivot%							; Pivot of Glow
Global Glow1%,Glow2%						; Glow rings
Global GlowScale#							; Glow scale indicator
Global Tween#								; Frame tween
Global MoveSpeed#							; movespeed in km/h

; Init 3D
Graphics3D ScreenWidth,ScreenHeight,ColorDepth,ScreenMode

; Init Player and Scene
InitPlayer()
InitScene()

; Center mouse and hide pointer
MoveMouse ScreenWidth/2,ScreenHeight/2
HidePointer

; Main loop
While Not KeyHit(1)
	
	; Frame tweening
	Tween#=Float(MilliSecs()-FrameTime)/Float(GameTime) : FrameTime=MilliSecs()
	
	; SPACE = Wireframe
	If KeyHit(57) Then wf%=1-wf : WireFrame wf
	
	; Get Distance player to planet
	Local distance#=EntityDistance(Cam,Planet)
	
	; Update atmosphere glow
	UpdateAtmosphere(distance)
	
	; Attach Starbox to camera
	PositionEntity StarBox,EntityX(Player),EntityY(Player),EntityZ(Player)
	
	;Sun points To Player
	PointEntity Sun,Player
	
	UpdateWorld
	RenderWorld
	
	;Move Player
	Movement(GlowScale,distance)
	
	; Draw cursor and crosshair by chasing the mousetarget
	CameraProject Cam,EntityX(MouseTarget,1),EntityY(MouseTarget,1),EntityZ(MouseTarget,1)
	DrawImage Cursor,MX-(CursorSize/2),MY-(CursorSize/2)
	DrawImage Cross,ProjectedX()-(CursorSize/2),ProjectedY()-(CursorSize/2)
	
	; Statistics
	Text 0, 0,"Triangles rendered....: "+TrisRendered()
	Text 0,15,"Distance To Surface...: "+Int((distance-Scale)*1000)+" km"
	Text 0,30,"Move speed............: "+MoveSpeed+" km/h"
	
	Flip 0
	
Wend

End

; The functions that creates the mesh and surface for a ring
Function CreateRing(fx%=0,blend%=0)
	
	Local a1#,a2#,a3#,a4#,angle%
	Local v0%,v1%,v2%,v3%
	
	Local mesh=CreateMesh()
	Local surf=CreateSurface(mesh)
	
	; Ring FX
	If fx>0 Then EntityFX mesh,fx
	If blend>0 Then EntityBlend mesh,blend
	
	Return mesh
	
End Function

; Re-creates the ring vertices and triangles with different values
Function UpdateRing(mesh%,radius1#=1.0,radius2#=2.0,segments%=360,r1%=255,g1%=255,b1%=255,alpha1#=1.0,r2%=0,g2%=0,b2%=0,alpha2#=1.0,scale#=0.0)
	
	Local a1#,a2#,angle%
	Local v0%,v1%,v2%,v3%,v%
	
	; get and clear the surface
	Local surf=GetSurface(mesh,1)
	ClearSurface surf,1,1
	
	; Limit segments
	If segments>360 Then segments=360
	
	; Create ring
	For angle=0 To segments
		
		a1=angle*360.0/segments
		a2=angle*360.0/segments+180.0/segments
		
		; Calc vertex points
		v0=AddVertex(surf,radius1*Cos(a1),radius1*Sin(a1),0,0,0)
		v1=AddVertex(surf,radius2*Cos(a2),radius2*Sin(a2),scale,1,1)
		
		; Color
		VertexColor surf,v0,r1,g1,b1,alpha1
		VertexColor surf,v1,r2,g2,b2,alpha2
		
	Next
	
	; Create Triangles
	For v=0 To CountVertices(surf)-3
		
		AddTriangle(surf,v,v+1,v+2)
		
	Next
	
	Return mesh
	
End Function

; Initialize player, camera
Function InitPlayer()
	
	Local i%
	
	; Player pivot
	Player=CreatePivot()
	PositionEntity Player,-Scale*2,0,Scale*2
	
	; Ship mesh
	Ship=CreateCube(Player)
	EntityFX Ship,1
	ScaleEntity Ship,0.2,0.05,0.2
	PositionEntity Ship,0,-0.5,1.5
	EntityOrder Ship,-1000
	
	; Mousetarget in space
	MouseTarget=CreatePivot(Ship)
	MoveEntity MouseTarget,0,0,TargetDistance
	
	; Camera
	Cam=CreateCamera(Player)
	PositionEntity Cam,0,0,0
	CameraRange Cam,0.01,1000*Scale
	
	; Create cursor image
	Cursor=CreateImage(CursorSize,CursorSize)
	SetBuffer ImageBuffer(Cursor)
	For i=0 To 2
		Color 0,Int(255.0/(1+i)),0
		Rect i,i,CursorSize-(2*i),CursorSize-(2*i),0
	Next
	
	; Create crosshair image
	Cross=CreateImage(CursorSize,CursorSize)
	SetBuffer ImageBuffer(Cross)
	Color 255,0,0
	Line (CursorSize-1)/2.0,0,(CursorSize-1)/2.0,CursorSize
	Line 0,(CursorSize-1)/2.0,CursorSize,(CursorSize-1)/2.0
	
	; reset buffer and color
	SetBuffer BackBuffer()
	Color 255,255,255
	
End Function

; Initialize scene
Function InitScene()
	
	Local startex%,i%,col%,rgb%
	
	; Planet
	Planet=CreateSphere(60)
	ScaleEntity Planet,Scale,Scale,Scale
	EntityColor Planet,32,192,64
	
	; Directional Sunlight
	Light=CreateLight(1)
	PositionEntity Light,0,0,-Scale*200
	LightRange Light,200*Scale
	AmbientLight 16,16,16
	
	; Sun
	Sun=CreateQuad()
	ScaleEntity Sun,Scale*10,Scale*10,Scale*10
	EntityFX Sun,1
	EntityColor Sun,255,255,192
	EntityParent Sun,Light
	PositionEntity Sun,0,0,0
	EntityBlend Sun,3
	EntityTexture Sun,CreateSunTexture()
	
	; Simple Starbox
	StarBox=CreateCube()
	startex=CreateTexture(1024,1024)
	LockBuffer TextureBuffer(startex)
	For i=1 To 1000
		col=Rand(0,255)
		rgb=col*$10000+col*$100+col
		WritePixelFast Rand(0,1023),Rand(0,1023),rgb,TextureBuffer(startex)
	Next
	UnlockBuffer TextureBuffer(startex)
	EntityTexture StarBox,startex
	ScaleEntity StarBox,10,10,10
	EntityOrder StarBox,1
	EntityFX StarBox,1
	FlipMesh StarBox
	
	; Glow
	GlowPivot=CreatePivot()
	Glow1=CreateRing(1+2+16+32,1)
	Glow2=CreateRing(1+2+16+32,3)
	EntityParent Glow1,GlowPivot
	EntityParent Glow2,GlowPivot
	EntityOrder Glow1,1
	EntityOrder Glow2,1
	
	; Player points to planet first
	PointEntity Player,Planet
	TurnEntity Player,0,-20,-90
	
End Function

; Update atmosphere glow and background color
Function UpdateAtmosphere(distance#)
	
	Local s2#,s2d#,aa#,bb#,cc#
	Local angle#,intensity#,clscol#,horizon#
	
	; Calculate ring scale with the help of two right-angled triangles and trigonometry
	s2#=Scale^2
	s2d#=s2/distance
	GlowScale#=(Sqr(s2+(Scale/Tan(90-(90-(90-ATan(Sqr((distance-(s2d))*(s2d))/(distance-(s2d)))))))^2))/Scale
	
	; Calculcate the sun light angle (1 = exactly between sun and planet, 0 = exactly behind the planet)
	aa#=EntityDistance(Cam,Planet)
	bb#=EntityDistance(Planet,Light)
	cc#=EntityDistance(Cam,Light)
	angle#=ACos((aa^2+cc^2-bb^2)/(2*aa*cc))/180.0
	If angle>1 Then angle=1
	If angle<0 Then angle=0
	
	; Calculate the glow intensity according to sun light angle and distance to planet
	intensity#=1-(1.0/Exp(GlowScale*angle))
	If intensity<0 Then intensity=0
	If intensity>1 Then intensity=1
	
	; Calculate the horizon glow scale multiplicator
	horizon#=1-(1.2/Exp(GlowScale*angle/2.0))
	If horizon<0 Then horizon=0
	If horizon>1 Then horizon=1
	
	; Update ring intensity according to sun light angle and distance to planet
	UpdateRing(Glow1,0.6*Scale,0.01*Scale,RingDetail,R1*intensity,G1*intensity,B1*intensity,angle,R2*(angle-intensity),G2*(angle-intensity),B2*(angle-intensity),   0,Scale/3.0)
	UpdateRing(Glow2,(1.0-(horizon/10.0))*Scale,(1.05+(horizon/10.0))*Scale,RingDetail,R1*intensity,G1*intensity,B1*intensity,1.0-horizon,R2*angle,G2*angle,B2*angle,0,horizon*2)
	
	; Scale the rings and always point to player
	ScaleEntity GlowPivot,GlowScale,GlowScale,GlowScale
	PointEntity GlowPivot,Player
	
	; Calculate the background color to simulate atmosphere penetration
	clscol#=(1-(5.0/Exp(GlowScale*angle/2.0)))*intensity
	If clscol<0 Then clscol=0
	If clscol>1 Then clscol=1
	CameraClsColor Cam,R2*clscol,G2*clscol,B2*clscol
	
	; Change Starbox alpha
	EntityAlpha StarBox,1-clscol
	
End Function

; Player movement
Function Movement(scale#,distance#)
	
	Local roll#,mox#,moz#,cx#,cz#
	Local t1#,t2#
	
	; get mouse position
	MX=MouseX()
	MY=MouseY()
	
	; Movement with speed limit
	cx=(KeyDown(205)-KeyDown(203))*CameraSpeed
	cz=(KeyDown(200)-KeyDown(208))*CameraSpeed
	
	; Arrow left/right = roll
	If KeyDown(203) Then roll=RollSpeed
	If KeyDown(205) Then roll=-RollSpeed
	
	; Normalize Mouse position (-1 to +1)
	t1=Normalize(MY,0,ScreenHeight,-1,1)
	t2=Normalize(MX,0,ScreenWidth,1,-1)
	
	; Slower cursor movement in the center of the screen
	;If t1<0 Then t1=(Abs(t1)^2.0)*-1 Else t1=t1^2.0
	;If t2<0 Then t2=(Abs(t2)^2.0)*-1 Else t2=t2^2.0
	
	; Rotate ship mesh and turn player pivot
	RotateEntity Ship,t1*MaxRollAngle,t2*MaxRollAngle,t2*MaxRollAngle*2
	TurnEntity Player,t1*TurnSpeed*Tween,t2*TurnSpeed*Tween,roll*TurnSpeed*Tween
	
	; Move the player forward/backward
	MoveEntity Player,0,0,(cz*1.0/(scale)^3)*Tween
	
	; Calculate actual movespeed
	MoveSpeed#=(cz*1.0/(scale)^3)*3600000
	
End Function

; Normalize a value
Function Normalize#(value#=128.0,value_min#=0.0,value_max#=255.0,norm_min#=0.0,norm_max#=1.0)
	
	Return ((value-value_min)/(value_max-value_min))*(norm_max-norm_min)+norm_min
	
End Function

Function CreateQuad()
	
	; Create mesh and surface
	Local mesh%=CreateMesh()
	Local surf%=CreateSurface(mesh)
	
	; Add vertices
	Local v0%=AddVertex(surf,  1.0,  1.0, 0.0, 0.0, 0.0 )	; upper left
	Local v1%=AddVertex(surf, -1.0,  1.0, 0.0, 1.0, 0.0 )	; upper right
	Local v2%=AddVertex(surf, -1.0, -1.0, 0.0, 1.0, 1.0 )	; lower right
	Local v3%=AddVertex(surf,  1.0, -1.0, 0.0, 0.0, 1.0 )	; lower left
	
	; Connect vertices
	AddTriangle surf,v0,v1,v2
	AddTriangle surf,v0,v2,v3
	
	Return mesh
	
End Function

; Create a simple sun texture
Function CreateSunTexture()
	
	Local tex%=CreateTexture(512,512,2)
	Local tb%=TextureBuffer(tex)
	
	Local i#,j%,col%,rgb%
	
	SetBuffer tb
	LockBuffer tb
	
	; Intensity steps
	For j=0 To 255
		
		; Exponential falloff
		col=Int((1.0/Exp(j*0.075))*100000000)
		If col>255 Then col=255
		rgb=col*$1000000+col*$10000+col*$100+col
		
		; Draw circles
		For i=0 To 360 Step 0.1
			WritePixelFast 256+(Sin(i)*j),256+(Cos(i)*j),rgb,tb
		Next
		
	Next
	
	UnlockBuffer tb
	SetBuffer BackBuffer()
	
	Return tex
	
End Function

Comments

CakeMonitor
(Posted 18 years ago)
This is pure gold! Only a little bit of tweaking and it is the exact control system I was developing. You have saved me soooo much work. The glow and lighting system for the sun and planets is also exceptional imo and has given me inspiration for overcoming various problems I will shortly be facing.

You will be fully credited ofc when my project hits the shelves.

Krischan
(Posted 18 years ago)
Yeah use it, it is public domain. My own engine will be even more advanced than this, but this demo shows the basic idea behind it. There is only a little bug in the very basic function to determine if you are between the planet and the sun or behind the planet: if you are exactly behind the planet and facing the sun, my triangle calculation fails. You need to limit the very small angle numbers or the glow will begin to flicker for a small angle range:

Failing Line:
If angle>1 Then angle=1
If angle<0 Then angle=0

Patch:
If angle Then lastangle#=angle#
Local s2#,s2d#,aa#,bb#,cc#

...

If Not angle And lastangle<0.5 Then angle=0.01
If Not angle And lastangle>0.5 Then angle=0.99
If angle<0.01 Then angle=0.01
If angle>0.99 Then angle=0.99

This is not a 100% solution but the numbers are so small that you wouldn't notice the difference.

To see the bug just change the player position line in the Function "Initplayer" to this and move backwards without moving the mouse, the ring will begin to flicker or even disappear:
PositionEntity Player,0,0,Scale*3


CakeMonitor
(Posted 18 years ago)
Hello again Christian,

I've been testing this quite a bit and have noticed one other tiny issue. When you get very close to the surface of the planet (about 10km), you can see what i think is one the glow surfaces from behind. It obscures the planet surface.

Hopefully this image I put on imageShack works...



[URL]http://img231.imageshack.us/my.php?image=atmosissuejl9.jpg[/URL]

Have you too had this issue and any ideas on a fix?


ps by the way, when you say that this is a small portion of your project, you make me drool and I cannot help but feel that my contribution to the space game genre will be strictly inferior to yours! Even so, I shall trudge on and wish you all the best with your project.

Krischan
(Posted 18 years ago)
Hi CakeMonitor. Yeah I know this issue, it's just the first value of the CameraRange which is not small enough anymore. You are so close to the planet that the camera can't project it, adjust it to a lower value to "fix" the issue.

In my project I plan to "auto-land" the ship when you reached about 10km height (and to fake the landing with some cloud and atmosphere entry fx), so I don't care. You could try to auto-update the camerarange depending on the distance to the surface, but it is not designed for that.

And thanks for the wishes - I am a biiiiiig Starflight 1 fan (Binary Systems, 1986) and I think I am able to warp this type of game with my own additions into 3D space with Blitz. At this moment I am working on a realtime planet generator using banks and blendings, you can take a look at it here:

http://www.blitzbasic.com/codearcs/codearcs.php?code=2416

Generate your unique Class M planet in 200ms! Still in an early stage but able to generate ten thousands of unique surfaces out of 10 images!

Code Archives Forum