TGC Codebase Backup



Ye olde water algorithm Mk. 2 by Tinkerer

10th May 2005 23:48
Summary

An old and well-known water effect, plus quick and dirty normals calculation.



Description

Quick and dirty normals calculation used to illuminate the matrix from three light sources. Spacebar selects matrix behaviour, mouse2 selects interference type, and mouse1 affects interference amount.

The basic principle of the algorithm is surprisingly simple, with the main part being only four lines long. Two two-dimensional arrays are set up with zero values. The algorithm runs over every element in the first array and does this:

For each element in the first array...
Get the value of the four horizontal and vertical elements adjoining the current element in the second two-dimensional array space, divided by two.
Remove from this value, the value of the current element in the first array.
Apply the result to the current element in the first array.
Multiply the value of the current element in the first array by a damping value of less than one.
Repeat for all elements.

The data in the two arrays is then swapped, and the cycle repeats. If a non-zero value is placed into an array, waves of non-zero values will propagate outwards from the point of interference, reflecting from the edge elements of the arrays. If an array is then used as the height information for a matrix object, that matrix will display the patterns as water-like waves. Because of the damping value, the interference will gradually appear to dissipate, eventually leaving a flat landscape of zero values.

<update 1.1>
Code generally tidied up and optimised. The memory foam effect is no longer a broken water effect. Even more remarks added. That's about it for this script until I embed it in a function and use it in something bigger.

Known bugs: This algorithm only approximates a wave-like effect on a 2D map. It isn't precise, but it's precise enough for computer games. If excessive interference is introduced into the arrays, the result can be chaotic at best. High-frequency waves will also not be rendered well.
In this particular script's case, flicking rapidly between water and memory foam matrix behaviours can generate increasingly large values in the arrays. The workaraound is to be patient and let the values decrease.



Code
                                    ` This code was downloaded from The Game Creators
                                    ` It is reproduced here with full permission
                                    ` http://www.thegamecreators.com
                                    
                                    rem Set sync up.
sync on : sync rate 30
rem Hide the mouse pointer.
hide mouse
rem Declare the two arrays that will be used to hold height information.
dim imgbuffer1(1024, 1024) AS FLOAT
dim imgbuffer2(1024, 1024) AS FLOAT
rem Make this as big as your system can handle!
gridsize = 128
rem This value determines how large the matrix will be rendered.
gridmultiplier = 8
rem Fill the arrays with zeros.
for z = 0 to gridsize + 1
   for x = 0 to gridsize + 1
      imgbuffer1(x, z) = 0.0
      imgbuffer2(x, z) = 0.0
   next x
next z

rem Create a set of vectors that will be used to calculate the normal of
rem each matrix tile.
for vc = 1 to 6
   rtmp = make vector3(vc)
   set vector3 vc, 0.0, 0.0, 0.0
next vc
rem Determine where the middle of the matrix will be
middle = gridsize / 2

rem Set this to a value BELOW ZERO. Or don't and enjoy the fireworks.
rem 0.90 to 0.99 are "sensible" values.
damping AS FLOAT
damping = 0.96
rem Making this higher will slow the wave frequency of the mouse+sine effect.
wavefreq AS FLOAT
wavefreq = 2

rem Adjusting these two variables will determine how much mouse button 1 affects
rem the interference generators.
lowheight = 10
highheight = 100
rem You can customize your own water behaviour using the above three variables together

rem The current pattern value is stored here
pattern AS INTEGER
pattern = 0
rem The current algorithm (water or mousepad foam)
mtype = 0
rem The next two variables are used together to stop the pattern changing in a loop when
rem mouse 2 is held down.
clik AS INTEGER
clik = 0
lastclik AS INTEGER
lastclik = 0
rem The next two variables are used together to stop the matrix behaviour changing in a
rem loop when the spacebar is held down.
lastspace = 0
space = 0
rem A temporary variable. Used later on.
tmp AS FLOAT

rem Current mouse position and mouse+sine wave generator height go here.
mouseposx AS INTEGER
mouseposz AS INTEGER
waveheight AS INTEGER
waveheight = lowheight

rem Load matrix texture here.
MImg = 1 : load image "wtex2.jpg", MImg
rem Calculate matrix size and place in these two variables.
matrixsize AS FLOAT
matrixsize = gridsize * gridmultiplier
matrixmiddle AS FLOAT
matrixmiddle = matrixsize / 2.0
rem Declare and set the matrix here.
MObj = 1 : make matrix MObj, matrixsize, matrixsize, gridsize, gridsize
prepare matrix texture MObj, MImg, 1,1
rem Set the initial position of the camera here.
position camera 0,(sin(timer() / 100.0) * 100.0) + matrixsize,300.0,(cos(timer() / 110.0) * 100.0) + matrixsize

rem Define a light to follow the camera.
CLight = 1 : make light CLight
set point light CLight, 0, 0, 0
color light CLight, 0, 255, 0
set light range CLight, 1500

rem Define two lights to roam around.
RLighta = 2 : make light RLighta
set point light RLighta, 0, 0, 0
color light RLighta, 255, 0, 0
set light range RLighta, 1500

RLightb = 3 : make light RLightb
set point light RLightb, 0, 0, 0
color light RLightb, 0, 0, 255
set light range RLightb, 1500

rem Hide the default light.
hide light 0

rem Main loop.
do
   rem Get the new mouse position.
   mouseposx = mouseposx + (mousemovex() / 5)
   mouseposz = mouseposz + -(mousemovey() / 5)
   rem Limit the mouse position to valid array indices.
   if mouseposx > gridsize then mouseposx = gridsize
   if mouseposz > gridsize then mouseposz = gridsize
   if mouseposx < 1 then mouseposx = 1
   if mouseposz < 1 then mouseposz = 1

   rem Move the camera around slowly.
   position camera 0,matrixmiddle - (sin(timer() / 100.0) * 200.0),300.0,(cos(timer() / 110.0) * 100.0)
   point camera matrixmiddle,-300,matrixmiddle

   rem Reposition any moving lights here.
   position light CLight, camera position x(), camera position y(), camera position z()
   position light RLighta, matrixmiddle - (cos(timer() / 40.0) * 200.0),100.0, matrixmiddle + (sin(timer() / 110.0) * 100.0)
   position light RLightb, matrixmiddle - (sin(timer() / 100.0) * 200.0),100.0, matrixmiddle + (cos(timer() / 40.0) * 100.0)

   rem Calculate current wave generator values.
   sinfreq = timer() / wavefreq
   sincalc = sin(sinfreq) * waveheight
   coscalc = cos(sinfreq) * waveheight

   rem Capture mouse values and change pattern and waveheight values accordingly.
   lastclik = clik
   clik = mouseclick()
   if clik <> lastclik
      select clik
         case 0
            waveheight = lowheight
         endcase
         case 1
            waveheight = highheight
         endcase
         case 2
            waveheight = lowheight
            inc pattern
         endcase
         case 3
            waveheight = highheight
            inc pattern
         endcase
      endselect
   endif
   if pattern > 2 then pattern = 0
   select pattern
      case 0
         print "Interference type: mouse + sine"
         imgbuffer1(mouseposx, mouseposz) = imgbuffer1(mouseposx, mouseposz) + sincalc
      endcase
      case 1
         print "Inteference type: rain"
         randomize timer()
         tx = rnd(gridsize - 1) + 1
         ty = rnd(gridsize - 1) + 1
         imgbuffer1(tx, ty) = imgbuffer1(tx, ty) - waveheight
      endcase
      case 2
         print "Interference type: none"
      endcase
   endselect
   rem Get the spacebar state.
   lastspace = space
   space = spacekey()
   rem Ignore if state is same as last state.
   if space <> lastspace
      if space = 1
         inc mtype
         if mtype > 1 then mtype = 0
      endif
   endif
   select mtype
      case 0
         print "Matrix behaviour: water"
      endcase
      case 1
         print "Matrix behaviour: memory foam"
      endcase
   endselect
   remstart
   This is the place where algorithms are calculated and applied.
   The first set of embedded loops are for water, the second for a low rebound
   foam-like behaviour. The third set of loops is used by both behaviours to
   swap the contents of the two buffers and calculate a simple tile normal
   detection algorithm.
   Accuracy of the normals map is traded for execution speed in this
   script. With a few extra lines of code, an averaged normals map can be
   created, however this strains an Athlon XP 2000 when rendering a map with a
   grid size of 128.

   Remarks left out for speed.
   remend
   select mtype
      case 0
         for z = 1 to gridsize
            for x = 1 to gridsize
               imgbuffer2(x, z) = (imgbuffer1(x - 1, z) + imgbuffer1(x + 1, z) + imgbuffer1(x, z + 1) + imgbuffer1(x, z - 1)) / 2 - imgbuffer2(x, z)
               imgbuffer2(x, z) = imgbuffer2(x, z) * damping
            next x
         next z
      endcase
      case 1
         for z = 1 to gridsize
            for x = 1 to gridsize
               imgbuffer2(x, z) = imgbuffer1(x, z) * damping
            next x
         next z
      endcase
   endselect
   for z = 1 to gridsize
      for x = 1 to gridsize
         tmp = imgbuffer1(x, z)
         imgbuffer1(x, z) = imgbuffer2(x, z)
         imgbuffer2(x, z) = tmp
         set matrix height MObj, x, z, imgbuffer2(x, z)
         set vector3 1,0.0,imgbuffer2(x, z),0.0
         set vector3 2,-1.0,imgbuffer2(x - 1, z),0.0
         set vector3 3,0.0,imgbuffer2(x, z + 1),1.0
         subtract vector3 4, 1, 2
         subtract vector3 5, 2, 3
         cross product vector3 6, 4, 5
         normalize vector3 6, 6
         set matrix normal MObj, x, z, x vector3(6), y vector3(6), z vector3(6)
      next x
   next z
   rem done with algorithms. Update matrix with new values.
   update matrix MObj


   rem print out a status display.
   sfps = screen fps()
   rem mouse pointer values and frames per second.
   set cursor 0,1 : print "mpx: "; mouseposx; " mpz: "; mouseposz; " fps: "; sfps
   rem quick polygons per second calculation.
   pps = statistic(1) * sfps
   print pps;" polygons per second."
   rem flip the buffers.
   sync
   rem end of main loop.
loop

rem That's it, folks!
end