Code archives/3D Graphics - Misc/Spacegame tech demo
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
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
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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! |
| ||
Well, here is an update with a whole solar system. I had some problems with the correct distances but I think I solved it - could be better but it is working and sufficient as an example. Have fun. Steering: Mouse= Target direction Arrows = move forward/backward and roll LMB = 10x Zoom RMB = move 10x faster SPACE = Wireframe Demo screenshot: ![]() Pimped screenshot, uses 99% of the code below: ![]() |
| ||
It's looking absolutley beautiful, and very smooth/fast too! I dunnoif it's important for your use, though your p\radius=(4+3*2^(i-1))*1000is very similar. Unless you want astronomically accurate mean distances for your planets, a quick method (especially if static) is to use the Titus-Bode approximation: Function Titus_Bode_Distance#(Planet_Number%) Return 0.4+(0.3*(2^(Planet_Number-2))) End Function You also need to assume a value for dwarf-planets etc. such as the asteroid field between Mars and Jupiter to maintain consistency, so the 'Planet_Number' list should be: 1 - Mercury 2 - Venus 3 - Earth 4 - Mars 5 - (Asteroids) 6 - Jupiter 7 - Saturn 8 - Uranus 9 - Pluto Note the exception of Neptune, due to its greatly obliquatwed orbit and interference from Pluto and other TNO gravitational effects. The approximation still gives excellent results quickly. |
Code Archives Forum