SuperStrict
' Copyright (c) 2009 David De Candia
' All rights reserved.
'
' Redistribution and use in source and binary forms, with or without
' modification, are permitted provided that the following conditions are met:
' * Redistributions of source code must retain the above copyright
' notice, this list of conditions and the following disclaimer.
' * Redistributions in binary form must reproduce the above copyright
' notice, this list of conditions and the following disclaimer in the
' documentation and/or other materials provided with the distribution.
' * Neither the auther nor the names of its contributors may be used to
' endorse or promote products derived from this software without specific
' prior written permission.
'
' THIS SOFTWARE IS PROVIDED BY David De Candia ``AS IS'' AND ANY
' EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
' DISCLAIMED. IN NO EVENT SHALL David De Candia BE LIABLE FOR ANY
' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'
Rem
bbdoc: Plasma
about: Pixmap-based real-time plasma algorithm
End Rem
Module gibbongames.plasma
ModuleInfo "Version: 1.0 29th October 2008"
ModuleInfo "Author: David De Candia"
ModuleInfo "Contact: david@gibbongames.com"
ModuleInfo "URL: www.gibbongames.com"
ModuleInfo "About: Real-time plasma algorithm for PF_RGBA8888 based pixmaps"
ModuleInfo "Credit: Plasma source: Michael Bevin, Jan Miller and Erik Hansen. See FX library at http://www.geocities.com/SiliconValley/Vista/9582/ for original C source"
Import BRL.GLMax2D
Import BRL.Random
' ----------------------------------------------------------------
Rem
bbdoc:
about:
End Rem
Type TPlasmaStructure
Field Table1:Byte[]
Field Table2:Byte[]
Field Width:Int
Field Height:Int
Field PlasmaType:Int
Const MAX_CIRCLES:Int = 8
Field Circle:Float[MAX_CIRCLES]
Field Roll:Int
End Type
' ----------------------------------------------------------------
Rem
bbdoc:
about:
credit:
End Rem
Type TPlasma
Const BLUE_PLASMA:Int = 0,..
GREEN_PLASMA:Int = 1,..
RED_PLASMA:Int = 2,..
MAX_PLASMAS:Int = 3
Field NumPlasmas:Int = 3
Field f_PlasmaSpeed:Float
Field f_PlasmaRipples:Float = 6
Field PlasmaComplexity:Int
Field f_PlasmaInterference:Float
Field PlasmaContrast:Int
Field PixelStep:Int ' 1 to 8, even numbers after 1. The higher the pixel step the faster the algorithm, and the lower the plasma resolution
Field PlasmaBrighten:Int ' 0 or 1
Const F_RADS:Float = 57.2957795
' All plasmas have the same width and height - must be power of 2
Field Width:Int
Field Height:Int
Field Plasma:TPlasmaStructure[MAX_PLASMAS]
' Plasma is drawn to here
Field Pixmap:TPixmap
' ----------------------------------------------------------------
Rem
bbdoc:
about:
End Rem
Method Create:TPlasma(_width:Int, _contrast:Int = 4, _f_speed:Float = 0.07, _complexity:Int = 100, _f_interference:Float = 9.5, _brighten:Int = 1, _pixel_step:Int = 1, _num_plasmas:Int = 3 )
SeedRnd MilliSecs()
PlasmaContrast = _contrast
PlasmaComplexity = _complexity
f_PlasmaInterference = _f_interference
PlasmaBrighten = _brighten
f_PlasmaSpeed = _f_speed
PixelStep = _pixel_step
NumPlasmas = Max(1, Min(MAX_PLASMAS,_num_plasmas))
' Create 3 plasmas, one for each colour channel and overlay them during render
If NumPlasmas > RED_PLASMA Then Init(Plasma[RED_PLASMA], _width, _width)
If NumPlasmas > GREEN_PLASMA Then Init(Plasma[GREEN_PLASMA], _width,_width)
If NumPlasmas > BLUE_PLASMA Then Init(Plasma[BLUE_PLASMA], _width, _width)
' Plasma is rendered on this
Pixmap = CreatePixmap(_width, _width,PF_RGBA8888)
Return Self
End Method
' ----------------------------------------------------------------
Rem
bbdoc: _f_frame_time currently unused in favour of vsync
about:
End Rem
Method Draw(_f_frame_time:Float = 1.0/60.0)
Local double_width:Int = Width *2
Local xx1:Int[MAX_PLASMAS],xx2:Int[MAX_PLASMAS],xx3:Int[MAX_PLASMAS],xx4:Int[MAX_PLASMAS]
Local x1:Int[MAX_PLASMAS],y1:Int[MAX_PLASMAS],x2:Int[MAX_PLASMAS],y2:Int[MAX_PLASMAS],x3:Int[MAX_PLASMAS],y3:Int[MAX_PLASMAS],x4:Int[MAX_PLASMAS],y4:Int[MAX_PLASMAS]
Local ubyte:Byte[MAX_PLASMAS]
' If you were frame limiting the code via f_frame_time, you'd prob do it here
Local f_sway:Float = F_RADS * f_PlasmaSpeed
Local wo4:Int = Width/4
Local wo2:Int = Width/2
Local ho2:Int = Height/2
Local f_sways:Float[] = [ 0.0425 * f_sway , -0.05* f_sway, 0.15* f_sway, -0.10* f_sway, 0.20* f_sway, -0.075* f_sway, 0.175* f_sway, -0.025* f_sway ]
For Local plasma_id:Int = 0 Until NumPlasmas
For Local circle_id:Int = 0 Until TPlasmaStructure.MAX_CIRCLES
Plasma[plasma_id].Circle[circle_id] :+ f_sways[circle_id]
Next
x2[plasma_id] = wo2 + Int( (Float(wo2) * Sin(Plasma[plasma_id].Circle[0])) )
y2[plasma_id] = ho2 + Int( (Float(ho2) * Cos(Plasma[plasma_id].Circle[1])) )
x1[plasma_id] = wo2 + Int( (Float(wo2) * Cos(Plasma[plasma_id].Circle[2])))
y1[plasma_id] = ho2 + Int( (Float(ho2) * Sin(Plasma[plasma_id].Circle[3])))
x3[plasma_id] = wo2 + Int( (Float(wo2) * Cos(Plasma[plasma_id].Circle[4])))
y3[plasma_id] = ho2 + Int( (Float(ho2) * Sin(Plasma[plasma_id].Circle[5])))
x4[plasma_id] = wo2 + Int( (Float(wo2) * Cos(Plasma[plasma_id].Circle[6])))
y4[plasma_id] = ho2 + Int( (Float(ho2) * Sin(Plasma[plasma_id].Circle[7])))
xx1[plasma_id] = double_width * y1[plasma_id] + x1[plasma_id]
xx2[plasma_id] = double_width * y2[plasma_id] + x2[plasma_id]
xx3[plasma_id] = double_width * y3[plasma_id] + x3[plasma_id]
xx4[plasma_id] = double_width * y4[plasma_id] + x4[plasma_id]
Next
Local colour_value_a:Int, colour_value_b:Int, colour_value_c:Int
Local colour_to_plot:Int
Local y_offset:Int
Local alpha:Int = $ff000000'255 Shl 24
Local table_index:Int
Local plasma_id:Int
Local pixel_id:Int
Local x:Int
Local pixel:Byte Ptr
' For each y pixel in the pixmap
For Local y:Int = 0 Until Height
y_offset = double_width * y;
table_index = 0
x = 0
' Write 1 line of pixels
While x < Width
For plasma_id:Int = 0 Until NumPlasmas
ubyte[plasma_id] = Plasma[plasma_id].Table1[table_index + y_offset + xx1[plasma_id]] +..
Plasma[plasma_id].Table2[table_index + y_offset + xx3[plasma_id]] +..
Plasma[plasma_id].Table2[table_index + y_offset + xx4[plasma_id]] +..
Plasma[plasma_id].Table2[table_index + y_offset + xx2[plasma_id]]
' Beautify - essential!
If ubyte[plasma_id] & 128 Then ubyte[plasma_id] = ~ubyte[plasma_id]
'
Next
colour_value_a = ubyte[BLUE_PLASMA] ' BLUE
colour_value_b = ubyte[GREEN_PLASMA] Shl 8 ' GREEN
colour_value_c = ubyte[RED_PLASMA] Shl 16 ' RED
' Shl 1 brightens everything
colour_to_plot = alpha | ((colour_value_a|colour_value_b|colour_value_c) Shl PlasmaBrighten)
' Grab the memory address of the next pixel to write to
' This will only work for 4 byte pixmaps like RGBA - else use the slower PixelPtr call
pixel = Pixmap.pixels + y * Pixmap.pitch + x * 4
'pixel=Pixmap.PixelPtr(x,y)
' Writing a bunch of the same colour pixels at once saves FPS but decreases resolution
For pixel_id = 0 Until PixelStep
' This is more generic and safer - in that it detects the dest surface type before writing, but it is also slower
'WritePixel Pixmap,x+pixel_id,y,colour_to_plot
pixel[0]=colour_to_plot Shr 16 ; pixel[1]=colour_to_plot Shr 8 ; pixel[2]=colour_to_plot ; pixel[3]=colour_to_plot Shr 24
pixel :+ 4
Next
table_index :+ 1
x :+ PixelStep
Wend'x
Next 'y
End Method
' ----------------------------------------------------------------
Rem
bbdoc:
about:
End Rem
Method Init(_plasma:TPlasmaStructure, _width:Int, _height:Int)
' Safety
If Not _plasma Then Return
Local height_index:Int
Local width_index:Int
Local k:Int
Local double_width:Int
Local double_height:Int
Local contrast:Int = PlasmaContrast
Local constant_a:Int
Local f_angle_a:Float
Local f_angle_b:Float
Local f_temp:Float
If _plasma
_plasma.Width = _width
_plasma.Height = _height
Width = _width
Height = _height
double_height = _height*2
double_width = _width * 2
f_angle_a = f_PlasmaRipples
f_angle_b = f_PlasmaInterference
constant_a = PlasmaComplexity
For Local circle_id:Int = 0 Until TPlasmaStructure.MAX_CIRCLES
_plasma.Circle[circle_id] = F_RADS * Float(Rand(0, 65535)) / 16384.0
If circle_id & 1 Then _plasma.Circle[circle_id] :* -1
Next
_plasma.PlasmaType = contrast
' Each table is double width, double height
_plasma.Table1 = New Byte[_width*_height*4]
_plasma.Table2 = New Byte[_width*_height*4]
Local table_index:Int
While height_index < _height
width_index = 0
k = (_height-height_index) * (_height-height_index)
While width_index < _width
f_temp = Sqr(16+k+(_width-width_index)*(_width-width_index)-4)
_plasma.Table1[table_index] = Byte( f_temp * f_angle_a / contrast)
_plasma.Table2[table_index] = Byte( (Sin(F_RADS*f_temp/f_angle_b) + 1) * constant_a / contrast)
width_index :+ 1
table_index :+ 1
Wend 'width_index < _width
Local symmetry_index:Int = 0
Local middle:Int = table_index - 1
' Mirror Symmetry on X - copy the current line backwards
While width_index < double_width
_plasma.Table1[table_index] = _plasma.Table1[middle - symmetry_index]
_plasma.Table2[table_index] = _plasma.Table2[middle - symmetry_index]
table_index :+1
symmetry_index :+ 1
width_index :+ 1
Wend
height_index :+ 1
Wend 'height_index < height
' Mirror symmetry on Y
While height_index < double_height
MemCopy (Varptr(_plasma.Table1[height_index*double_width]), Varptr(_plasma.Table1[(double_height-height_index-1) * double_width]), double_width)
MemCopy (Varptr(_plasma.Table2[height_index*double_width]), Varptr(_plasma.Table2[(double_height-height_index-1) * double_width]), double_width)
height_index :+ 1
Wend
EndIf ' if _plasma
End Method
' ----------------------------------------------------------------
Rem
bbdoc:
about:
End Rem
Method New()
For Local plasma_id:Int = 0 Until MAX_PLASMAS
Plasma[plasma_id] = New TPlasmaStructure
Next
End Method
End Type |