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