GLBasic forum

Codesnippets => Code Snippets => Topic started by: Wampus on 2010-Sep-09

Title: Classic 2D water ripple
Post by: Wampus on 2010-Sep-09
So, I did a quick routine to create a classic 2D water ripple effect. The code, exe and test image are all included in the attachment.

(http://i904.photobucket.com/albums/ac242/wheeethefibble/webpics/watereffect.jpg)

On my 32GB iPod Touch (3rd generation - quite powerful) it really struggles, reducing framerate to a crawl and is thus not usable for anything that requires 60 FPS.

Is anyone aware of a way to do this kind of ripple effect without having to load and then draw a sprite as a line-by-line animation? Although covering the screen line by line could be very fast, if that was all that was involved, having to use the DRAWSPRITE command to do it is considerably CPU expensive. This kind of ripple effect was possible without too much trouble even on 16-bit computers but calling DRAWSPRITE 480 times to fill the screen even slows the FPS on my netbook.

I don't need any alpha-blending, collision detection or anything like that so is there any way to do what I've done without DRAWSPRITE?

Code (glbasic) Select
LOADANIM "test.png", 1, 340, 1

GLOBAL waters[]
DIM waters[480]
GLOBAL f = 0

FOR n = 0 TO 3

FOR i = 0 TO 119

waters[i+(n*120)] = 5*SIN(f)

INC f
INC f
INC f

IF f >= 360 THEN f = f-360

NEXT

NEXT


f = 0


// MAIN LOOP
WHILE KEY(01) = FALSE
//MAIN LOOP


FOR i = 0 TO 479           

    DRAWANIM 1, i+5-waters[f], waters[f]-5, i            // Draw every single line as a sprite

INC f

IF f = 480

    f = f - 480

ENDIF

NEXT

DEC f

SHOWSCREEN

WEND

END


[attachment deleted by admin]
Title: Re: Classic 2D water ripple
Post by: matchy on 2010-Sep-09
That's nice although I would not use it for a game cycle. Maybe for a menu or title screen!!!!
Title: Re: Classic 2D water ripple
Post by: Cliff3D on 2010-Sep-09
Hmmm... sounds like you could do with a "cheaper, faster" sprite routine, with much of the cleverness ripped out for speed. Or inline C/C++ to achieve the same results...

but how about this. You're doing a fair bit of math there, and I suspect that the CPU isn't that great at math. If you move the math out to a function and the sprite drawing to another function you could use the profiler to see if there is significant time being spent on the math. If there IS significant time being spent on the math, you MIGHT be able to improve on the time taken per frame by pre-calculating values and storing them in an array? Maybe a 2-dimensional array of floats would be quicker to access than recalculating each time?

Other little optimisations all help. For example, change:

INC f
INC f
INC f

to

INC f,3
Title: Re: Classic 2D water ripple
Post by: Wampus on 2010-Sep-09
I think I'll mention a cheaper, cruder and faster image copying routine at the requests forum. It could be helpful in circumstances like these.

Good suggestions Re: the math. In this case I already use an array to hold pre-calculated Sine multiples for how far the screen should distort. However, there are always a few things that can be done to optimise.

What I've been avoiding and should now think about facing is learning some C++! Many years ago I used to compliment my STOS Gaming Basic programs with 68000 assembly routines where I needed a speed boost on something simple. These are oddly similar circumstances. I'm reasonably experienced with Java and OOP so how hard could C++ be?
Title: Re: Classic 2D water ripple
Post by: Ian Price on 2010-Sep-09
Nice. :)
Title: Re: Classic 2D water ripple
Post by: Scott_AW on 2010-Sep-09
Try using polyvector and newpolystrip.

You can load the image as a solid sprite and in your for-loop, change the texture position with the x position.

Surround the loop with the startpoly and endpoly, then put this in place of the sprite bit


newpolystrip
polyvector  i+5-waters[f], waters[f]-5, i-1,0, rgb(255,255,255)
polyvector  i+10-waters[f], waters[f]-5, i,0, rgb(50,50,155) 
polyvector  i+10-waters[f], waters[f]-5+480, i,480, rgb(50,50,155)
polyvector  i+5-waters[f], waters[f]-5+480, i-1,480, rgb(255,255,255)

Also using the color you create a interesting fadeoff.


Okay I was wrong about the fadeoff and used one additional poly vector more than  I should of, but here's the same code again with polyvector and a gradient routine simply applied.

Code (glbasic) Select
// --------------------------------- //
// Project: watereffect2d
// Author: Scott A. Williams
// Based off code provided by 'Ragaril' from GLBasic forums
// Start: Wednesday, December 31, 1969
// IDE Version: 8.078

LOADSPRITE "test.png", 1

SETSCREEN 340, 480, 0

GLOBAL water[]

DIM water[480]

GLOBAL f = 0, c1 = 255, c2 = 0

FOR n = 0 TO 3
  FOR i = 0 TO 119
    water[i+(n*120)] = 5*SIN(f)
    INC f, 3
   
    IF f >= 360 THEN DEC f, 360
   
  NEXT
NEXT

f = 0

WHILE KEY(1) = FALSE
  STARTPOLY 1, 2
  FOR i = 0 TO 479
    c1 = 255 - (255 * (i/480))
    c2 = 0 + (255 * (i/480))
    IF i > 0 THEN POLYNEWSTRIP
      POLYVECTOR water[f]-5-340, i+1, -340, i+5-water[f]+1, RGB(c1,c1,c2)
      POLYVECTOR water[f]-5+340, i, 340, i+5-water[f],  RGB(c1,c1,c2)
      POLYVECTOR water[f]-5+340, i+1, 340, i+5-water[f]+1,  RGB(c1,c1,c2)
   
    INC f, 1
   
    IF f = 480 THEN DEC f, 480
   
  NEXT
  ENDPOLY
  DEC f, 1
  SHOWSCREEN
WEND

END
[/cude]
Title: Re: Classic 2D water ripple
Post by: Schranz0r on 2010-Sep-09
Love it!
Title: Re: Classic 2D water ripple
Post by: Wampus on 2010-Sep-10
Scott_AW your changes work great!  8)

Not only is the wavy pattern now totally smooth in appearance but its significantly faster than calling drawsprite 480 times. Using a wavy background for my PowFish game is now netbook friendly! My iPod Touch is still dropping to half frame rate but heck, I was starting to overload onscreen detail anyway.

Thanks!  =D
Title: Re: Classic 2D water ripple
Post by: Scott_AW on 2010-Sep-10
Thanks for the great example.
Title: Re: Classic 2D water ripple
Post by: Wampus on 2010-Dec-03
Thought I could update this thread with an improved routine. Using the code below still looks as nice but is far more optimised since it draws polys in steps of 16. Its even fast enough that I use a variation of it to display the backdrop for my PowFish game on the iPhone.

Code (glbasic) Select
// --------------------------------- //
// Project: Water Effect
// Start: Wednesday, September 08, 2010
// IDE Version: 7.341



SETCURRENTDIR("Media") // seperate media and binaries?
SETSCREEN 320,480,0 // iPhone size 320x480 Window
LIMITFPS 60 // refresh at 60 frames per second

LOADANIM "test.png", 1, 340, 1  // should be 340x by 500y

GLOBAL waters[]
DIM waters[640]
GLOBAL f = 0

FOR n = 0 TO 4

FOR i = 0 TO 119

waters[i+(n*120)] = 5*SIN(f)

INC f
INC f
INC f

IF f >= 360 THEN f = f-360

NEXT

NEXT


f = 0


// MAIN LOOP
WHILE KEY(01) = FALSE
//MAIN LOOP

STARTPOLY 1,1

FOR i = 0 TO 479 STEP 16

POLYVECTOR 0, i, waters[f]+5, i+5-waters[f], RGB(255,255,255)
POLYVECTOR 0, i+16, waters[f+16]+5, i+21-waters[f+16], RGB(255,255,255)
POLYVECTOR 320, i+16, waters[f+16]+325, i+21-waters[f+16], RGB(255,255,255)

POLYVECTOR 0, i, waters[f]+5, i+5-waters[f], RGB(255,255,255)
POLYVECTOR 320, i, waters[f]+325, i+5-waters[f], RGB(255,255,255)
POLYVECTOR 320, i+16, waters[f+16]+325, i+21-waters[f+16], RGB(255,255,255)

INC f, 16

IF f >= 480 THEN DEC f, 480

NEXT

DEC f

ENDPOLY

SHOWSCREEN

WEND

END
Title: Re: Classic 2D water ripple
Post by: Albert on 2011-Apr-06
I've modified it a bit. Added some shade and optimized a bit (using triangle strip)

Note that you really want to use an image with 340x500!
Quote// --------------------------------- //
// Project: Water Effect
// Start: Wednesday, September 08, 2010
// IDE Version: 7.341



SETCURRENTDIR("Media") // seperate media and binaries?
SETSCREEN 320,480,0 // iPhone size 320x480 Window
LIMITFPS 60 // refresh at 60 frames per second

LOADANIM "test3.png", 1, 340, 1  // should be 340x by 500y

GLOBAL waters1[]
DIM waters1[640]
GLOBAL waters2[]
DIM waters2[640]
GLOBAL f = 0

FOR n = 0 TO 4
   FOR i = 0 TO 119
      waters1[i+(n*120)] = 5*SIN(f)
      waters2[i+(n*120)] = 5*COS(360-f)      
      INC f
      INC f
      INC f
      IF f >= 360 THEN f = f-360
   NEXT
NEXT

f = 0

// MAIN LOOP
WHILE KEY(01) = FALSE
   //MAIN LOOP
   STARTPOLY 1,2
   LOCAL c=waters1[f]*20+(255-100)
   LOCAL color1a=RGB(c,c,c)
   c=waters1[f+16]*20+(255-100)
   LOCAL color1b=RGB(c,c,c)
   c=waters2[f/2]*20+(255-100)
   LOCAL color2a=RGB(c,c,c)
   c=waters2[f/2+16]*20+(255-100)
   LOCAL color2b=RGB(c,c,c)   
   POLYVECTOR 320,          0,          waters2[f]+325,       5-waters2[f],         color2a
   POLYVECTOR 0,             0,          waters1[f]+5,          5-waters1[f],         color1a
   FOR i = 0 TO 479 STEP 16
      c=waters1[f]*20+(255-100)
      color1a=RGB(c,c,c)
      c=waters1[f+16]*20+(255-100)
      color1b=RGB(c,c,c)
      c=waters2[f/2]*20+(255-100)
      color2a=RGB(c,c,c)
      c=waters2[f/2+16]*20+(255-100)
      color2b=RGB(c,c,c)            
      POLYVECTOR 320,          i+16,       waters2[f+16]+325,       i+21-waters2[f+16],      color2b
      POLYVECTOR 0,            i+16,       waters1[f+16]+5,       i+21-waters1[f+16],      color1b
      INC f, 16
      IF f >= 480 THEN DEC f, 480
   NEXT
   DEC f
   ENDPOLY
   
   SHOWSCREEN
WEND
END
Title: Re: Classic 2D water ripple
Post by: Wampus on 2011-Apr-07
Lovely effect Albert.  :)

For some reason there were some jagged edges occurring. Look at the light beams going down into to water on the different versions for comparison. I wasn't sure why that was occuring but instead of working out why I just de-optimised the routine (sorry - I'm lazy) by modifying the older code with your new shading changes so that the POLY section looks like this: -

Code (glbasic) Select
STARTPOLY 1,1
FOR i = 0 TO 479 STEP 16

   LOCAL c=waters[f]*6+(255-30)
   LOCAL color1a=RGB(c,c,c)
   c=waters[f+16]*6+(255-30)
   LOCAL color1b=RGB(c,c,c)

POLYVECTOR 0, i, waters[f]+5, i+5-waters[f], color1a
POLYVECTOR 0, i+16, waters[f+16]+5, i+21-waters[f+16], color1b
POLYVECTOR 320, i+16, waters[f+16]+325, i+21-waters[f+16], color1b

POLYVECTOR 0, i, waters[f]+5, i+5-waters[f], color1a
POLYVECTOR 320, i, waters[f]+325, i+5-waters[f], color1a
POLYVECTOR 320, i+16, waters[f+16]+325, i+21-waters[f+16], color1b

INC f, 16
IF f >= 480 THEN DEC f, 480
NEXT
DEC f
ENDPOLY