My goal here was to test the Billboards in PB (I've never used them before).
This code generates Strange Attractors with the Pickover equation, then display them in 3D using colored billboards. It's loosely based on this post: http://www.gamedev.net/community/forums ... _id=410954 (though my results are far less gorgeous...).
On my office PC, with a medium GPU (Radeon HD 3400), It can display up to 40000 billboards before going under 60 FPS.
Here's the controls:
- [Space]: generates a new random attractor.
- C : let you choose the billboards' colors
- Up arrow: add 5000 billboards
- Down arrow: remove 5000 billboards
- F1 : display frame rate
- Esc: quit
Also:
I've made a screensaver from this code, and I'd like you to test it; Please tell me if it works well, if you see any bug, and what's your config / frame rate. Thanks a lot!
You can download it here: http://keleb.free.fr/codecorner/screensavers03-en.htm (I've scanned it with 2 different antivirus; it's clean)
Now, here's the code:
Code: Select all
; Author: Kelebrindae
; Date: july, 17, 2010
; PB version: v4.41
; OS: Windows XP
; ---------------------------------------------------------------------------------------------------------------
; Description:
; ---------------------------------------------------------------------------------------------------------------
; This program is a very basic Strange Attractors generator.
; It computes a cloud of points using a variation of the Pickover formula. Then it displays the points using the distance from
; one point to the next to color them.
; Initially conceived to test the Billboards performances.
;
; Controls:
; - [Space] : compute a new cloud from random values
; - C : change colors
; - F1 : display FPS
; ---------------------------------------------------------------------------------------------------------------
#COLORTABLERANGE = 63 ; Number of steps between color 1 and color 2 (there's one billboard group for each step)
; These, you shouldn't change
#MAXIMUMXRANGE = 1023
#MAXIMUMYRANGE = 1023
#MAXIMUMZRANGE = 1023
;- Structures and globals
Structure coord3D
x.f
y.f
z.f
EndStructure
Structure cloudpoint_struct
x.f
y.f
z.f
distance.f
angle.f
EndStructure
Global NewList pickoverpoint.cloudpoint_struct()
Structure currentConfig_struct
nbPoints.i
param.f[6]
minValue.cloudpoint_struct ; used for "normalization"
maxValue.cloudpoint_struct ; used for "normalization"
color1.i
color2.i
EndStructure
Global currentConfig.currentConfig_struct
; Interesting start
currentConfig\nbPoints = 20000
currentConfig\color1 = $3F7FFF
currentConfig\color2 = $FF7F7F
currentConfig\param[1] = 1
currentConfig\param[2] = 1.8
currentConfig\param[3] = 0.71
currentConfig\param[4] = 1.51
currentConfig\param[5] = 1
Global width.i = 800, height.i = 600 ; Window dimension
EnableExplicit
;************************************************************************************
;- ---- Procedures ----
;************************************************************************************
Procedure createColorTable(numstep.i)
Protected i.i,redval.i,greenval.i,blueval.i
Protected redDiff.f,greenDiff.f,blueDiff.f
SetWindowTitle(0,"Generating new color table...")
; If it's not the first call, delete old materials and ask for new colors
If IsMaterial(1)
For i = 1 To numstep
FreeMaterial(i)
Next i
currentConfig\color1 = ColorRequester(currentConfig\color1)
currentConfig\color2 = ColorRequester(currentConfig\color2)
EndIf
SetWindowTitle(0,"Generating new color table... Please Wait...")
CreateImage(1,256,256)
redDiff = (Red(currentConfig\color2) - Red(currentConfig\color1))/numstep
greenDiff = (Green(currentConfig\color2) - Green(currentConfig\color1))/numstep
blueDiff = (Blue(currentConfig\color2) - Blue(currentConfig\color1))/numstep
For i = 1 To numstep
SetWindowTitle(0,"Generating new color table... Please Wait" + ReplaceString(Space(i % 8)," ",".") )
StartDrawing(ImageOutput(1))
DrawingMode(#PB_2DDrawing_Default)
Box(0,0,255,255,$000000)
redval = Red(currentConfig\color1) + redDiff*i
greenval = Green(currentConfig\color1) + greenDiff*i
blueval = Blue(currentConfig\color1) + blueDiff*i
If redval > 255
redval = 255
EndIf
If greenval > 255
greenval = 255
EndIf
If blueval > 255
blueval = 255
EndIf
DrawingMode(#PB_2DDrawing_Gradient)
BackColor( RGB(redval,greenval,blueval) )
FrontColor($000000)
CircularGradient(127, 127, 127)
Circle(127, 127, 127)
StopDrawing()
SaveImage(1,"temp"+Str(i)+".bmp")
LoadTexture(i,"temp"+Str(i)+".bmp")
DeleteFile("temp"+Str(i)+".bmp")
CreateMaterial(i, TextureID(i))
DisableMaterialLighting(i, #True)
MaterialBlendingMode (i, 2)
Next i
FreeImage(1)
SetWindowTitle(0,"Nebula")
EndProcedure
Procedure computeCloud(nbpoints.i,resetParams.b)
Protected i.i,j.i
Protected ok.b
Protected oldPos.Coord3D ; oldPos stores the coord of the previous point
; initialize min-max values (used For drawing only)
currentConfig\minValue\x = 999999999
currentConfig\minValue\y=999999999
currentConfig\minValue\z=999999999
currentConfig\minValue\distance=999999999
currentConfig\minValue\angle=999999999
currentConfig\maxValue\x=-999999999
currentConfig\maxValue\y=-999999999
currentConfig\maxValue\z=-999999999
currentConfig\maxValue\distance=-999999999
currentConfig\maxValue\angle=-999999999
Repeat
; If all params = 0, then randomize
If resetParams = #True
RandomSeed(ElapsedMilliseconds() + j) ; "+ j" to ensure we have a different seed when parameters are resetted
For i=1 To 5
currentConfig\param[i] = (Random(60000)-30000)/10000.0 ; random float between -3 and 3
Next i
EndIf
ok=#True
ClearList(pickoverpoint())
For i = 1 To nbpoints
; Store previous value
If ListIndex(pickoverpoint()) > -1
oldpos\x=pickoverpoint()\x
oldpos\y=pickoverpoint()\y
oldpos\z=pickoverpoint()\z
EndIf
; Pickover' formula
AddElement(pickoverpoint())
pickoverpoint()\x = Sin(currentConfig\param[1] * oldPos\y) - oldPos\z * Cos(currentConfig\param[2] * oldPos\x)
pickoverpoint()\y = oldPos\z * Sin(currentConfig\param[3] * oldPos\x) - Cos(currentConfig\param[4] * oldPos\y)
pickoverpoint()\z = currentConfig\param[5] * Sin(oldPos\x)
; Detect regularity
If oldpos\x=pickoverpoint()\x And oldpos\y=pickoverpoint()\y And oldpos\z=pickoverpoint()\z
ok = #False
resetParams = #True
j+1
Break
EndIf
; Stores min-max coords
If pickoverpoint()\x<currentConfig\minValue\x
currentConfig\minValue\x=pickoverpoint()\x
ElseIf pickoverpoint()\x>currentConfig\maxValue\x
currentConfig\maxValue\x=pickoverpoint()\x
EndIf
If pickoverpoint()\y<currentConfig\minValue\y
currentConfig\minValue\y=pickoverpoint()\y
ElseIf pickoverpoint()\y>currentConfig\maxValue\y
currentConfig\maxValue\y=pickoverpoint()\y
EndIf
If pickoverpoint()\z<currentConfig\minValue\z
currentConfig\minValue\z=pickoverpoint()\z
ElseIf pickoverpoint()\z>currentConfig\maxValue\z
currentConfig\maxValue\z=pickoverpoint()\z
EndIf
; Assign distance and angle (well, for now, angle isn't used; maybe later...)
; Distance from previous point (no SQR, to speed up the process)
pickoverpoint()\distance = (pickoverpoint()\x-oldPos\x)*(pickoverpoint()\x-oldPos\x) + (pickoverpoint()\y-oldPos\y)*(pickoverpoint()\y-oldPos\y) + (pickoverpoint()\z-oldPos\z)*(pickoverpoint()\z-oldPos\z)
If pickoverpoint()\distance<currentConfig\minValue\distance
currentConfig\minValue\distance=pickoverpoint()\distance
ElseIf pickoverpoint()\distance>currentConfig\maxValue\distance
currentConfig\maxValue\distance=pickoverpoint()\distance
EndIf
Next i
If currentConfig\maxValue\distance-currentConfig\minValue\distance = 0
ok = #False
resetParams = #True
EndIf
Until ok=#True
EndProcedure
Procedure normalizeCloud(nbPoints.i)
; "Normalize", so coords, distances and angle values are spread between 0 and N (makes drawing easier and faster)
; Normalizing distance and angle from 0 To COLORTABLERANGE can seem strange, but
; it's because they're drawn using color within a 0->COLORTABLERANGE range
ForEach pickoverpoint()
pickoverpoint()\x = (pickoverpoint()\x-currentConfig\minValue\x) * (#MAXIMUMXRANGE/(currentConfig\maxValue\x-currentConfig\minValue\x))
pickoverpoint()\y = (pickoverpoint()\y-currentConfig\minValue\y) * (#MAXIMUMYRANGE/(currentConfig\maxValue\y-currentConfig\minValue\y))
pickoverpoint()\z = (pickoverpoint()\z-currentConfig\minValue\z) * (#MAXIMUMZRANGE/(currentConfig\maxValue\z-currentConfig\minValue\z))
pickoverpoint()\distance = (pickoverpoint()\distance-currentConfig\minValue\distance) * (#COLORTABLERANGE/(currentConfig\maxValue\distance-currentConfig\minValue\distance))
; pickoverpoint()\angle = (pickoverpoint()\angle-currentConfig\minValue\angle) * (#COLORTABLERANGE/(currentConfig\maxValue\angle-currentConfig\minValue\angle))
Next pickoverpoint()
EndProcedure
Procedure drawCloud(nummat.i)
Protected i.i, numBillboard.i
; Create one billboard group for each color
For i= 1 To numMat
If IsBillboardGroup(i)
FreeBillboardGroup(i)
EndIf
CreateBillboardGroup(i,MaterialID(i),10,10, #MAXIMUMXRANGE/-2, #MAXIMUMYRANGE/-2, #MAXIMUMZRANGE/-2)
Next i
ForEach pickoverpoint()
; The point color is determined by its distance
numBillboard = Int(pickoverpoint()\distance) + 1
If numBillboard > nummat
numBillboard = nummat
ElseIf numBillboard < 1
numBillboard = 1
EndIf
; Add the point to the billboard
AddBillboard(i,numBillboard,pickoverpoint()\x,pickoverpoint()\y,pickoverpoint()\z)
Next pickoverpoint()
EndProcedure
DisableExplicit
;************************************************************************************
;- ---- Main program ----
;************************************************************************************
;- Init Engine 3D, keyboard, mouse...
If InitEngine3D() = 0
MessageRequester( "Error" , "Can't initialize 3D, check if engine3D.dll is available" , 0 )
End
ElseIf InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse() = 0
MessageRequester( "Error" , "Can't find DirectX 7.0 or above" , 0 )
End
EndIf
Add3DArchive(".", #PB_3DArchive_FileSystem)
;- Open a windowed screen
OpenWindow(0,0, 0, width, height ,"Nebula")
OpenWindowedScreen(WindowID(0),0,0, width , height,0,0,0,#PB_Screen_SmartSynchronization)
;- Create camera
CreateCamera(1,0,0,100,100)
RenderWorld()
anglecam.f = 0
;- Create the materials for the billboards
createColorTable(#COLORTABLERANGE+1)
;- Compute and draw cloud
computeCloud(currentConfig\nbPoints,#False)
normalizeCloud(currentConfig\nbPoints)
drawcloud(#COLORTABLERANGE+1)
; For linux users
KeyboardMode(#PB_Keyboard_International)
;- Main loop
Repeat
While WindowEvent() : Wend
Delay(1)
;- Keyboard management
If ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
quit = #True
EndIf
; Compute a new cloud
If KeyboardReleased(#PB_Key_Space)
computeCloud(currentConfig\nbPoints,#True)
normalizeCloud(currentConfig\nbPoints)
drawcloud(#COLORTABLERANGE+1)
EndIf
; Choose new colors
If KeyboardReleased(#PB_Key_C)
createColorTable(#COLORTABLERANGE+1)
drawcloud(#COLORTABLERANGE+1)
EndIf
; Add 5000 points
If KeyboardReleased(#PB_Key_Up)
currentConfig\nbPoints+5000
computeCloud(currentConfig\nbPoints,#False)
normalizeCloud(currentConfig\nbPoints)
drawcloud(#COLORTABLERANGE+1)
EndIf
; Remove 5000 points
If KeyboardReleased(#PB_Key_Down) And currentConfig\nbPoints > 5000
currentConfig\nbPoints-5000
computeCloud(currentConfig\nbPoints,#False)
normalizeCloud(currentConfig\nbPoints)
drawcloud(#COLORTABLERANGE+1)
EndIf
; Display performances info
If KeyboardReleased(#PB_Key_F1)
MessageRequester("Statistics",Str(CountRenderedTriangles()/2) + " points, " + Str(Engine3DFrameRate(#PB_Engine3D_Average)) + " Fps")
EndIf
EndIf
;- Rotate camera
anglecam+0.01
If anglecam>#PI
anglecam = -#PI
EndIf
CameraLocate(1,#MAXIMUMXRANGE * 1.5 * Cos(angleCam),0, #MAXIMUMZRANGE * 1.5 * Sin(angleCam))
CameraLookAt(1,0,0,0)
; Show it all
RenderWorld()
FlipBuffers()
Until quit = #True