Strange attractors

Everything else that doesn't fall into one of the other PB categories.
Kelebrindae
Enthusiast
Enthusiast
Posts: 151
Joined: Tue Apr 01, 2008 3:23 pm

Strange attractors

Post by Kelebrindae »

Hi!

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
Last edited by Kelebrindae on Wed Sep 01, 2010 4:16 pm, edited 1 time in total.
User avatar
idle
Always Here
Always Here
Posts: 5989
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Strange attractors

Post by idle »

nice looks good too
tastyraspberry
User
User
Posts: 39
Joined: Fri Sep 28, 2012 10:22 am

Re: Strange attractors

Post by tastyraspberry »

Hi, I got here after trying to diagnose what is wrong with my program! This looks great but it needs updating as the AddBillboard() command has changed. I've also tried downloading your screensaver but it didn't work on Windows10 - I can see the dll has been downloaded but some how I can't get the screensaver to register with the system etc...

Any chance of an update? This looks good.

Michael
tastyraspberry
User
User
Posts: 39
Joined: Fri Sep 28, 2012 10:22 am

Re: Strange attractors

Post by tastyraspberry »

I really liked the idea of this program so I have updated it for PB 5.50 (as far as I see it works here). I just had to change the Billboard creation as I've found it won't create a billboard with an integer number - it has to be PB_Any

Anyway, enjoy the pretty patterns..

Code: Select all

; Author: Kelebrindae
; Date: july, 17, 2010
; PB version: v4.41
; OS: Windows XP
; Updated for PB 5.50 Michael Hutton, 3rd November 2016
;  		line 254	- If IsBillboardGroup(i)
;						    - If IsBillboardGroup(aBillboardGroup(i))
;  		line 255	- FreeBillboardGroup(i)
;								- FreeBillboardGroup(aBillboardGroup(i))
;  		line 261  - aBillboardGroup(i) = CreateBillboardGroup(i, MaterialID(i),10,10, #MAXIMUMXRANGE/-2, #MAXIMUMYRANGE/-2, #MAXIMUMZRANGE/-2)
;								- aBillboardGroup(i) = CreateBillboardGroup(#PB_Any, MaterialID(i),10,10, #MAXIMUMXRANGE/-2, #MAXIMUMYRANGE/-2, #MAXIMUMZRANGE/-2)								
; Added a timer to display framerate in Window Titlebar 								
								
; ---------------------------------------------------------------------------------------------------------------
; 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
Global Dim aBillboardGroup(#COLORTABLERANGE + 1)

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, #PB_Material_Add       ) 
  
 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(aBillboardGroup(i))
   FreeBillboardGroup(aBillboardGroup(i))
  EndIf
 
  aBillboardGroup(i) = CreateBillboardGroup(#PB_Any, 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(aBillboardGroup(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)
AddWindowTimer(0, 0, 250)

;- 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
	Repeat
		Event = WindowEvent()
		If Event = #PB_Event_Timer And EventTimer() = 0
			t$ = "Billboard count = " + Str(Engine3DStatus(#PB_Engine3D_NbRenderedTriangles)/2) + " at "
			t$ + Str(Engine3DStatus(#PB_Engine3D_CurrentFPS)) + " fps"
			SetWindowTitle(0, t$)		
		EndIf
	Until Event = 0
 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
 MoveCamera(1,#MAXIMUMXRANGE * 1.5 * Cos(angleCam),0, #MAXIMUMZRANGE * 1.5 * Sin(angleCam), #PB_Absolute)
 CameraLookAt(1,0,0,0)
 
 ; Show it all
 RenderWorld()
 FlipBuffers()

Until quit = #True
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Re: Strange attractors

Post by djes »

Really nice, thank you :)
Post Reply