Original Post
Hey I have a question about optimization. I was following the tutorial The Water Effect Explained here on GameDev. Anyway I got the applet done but it runs VERY slowly on even moderate sized images.
After reviewing the article and my code I am not sure how much more I can do to make it run faster. The author mentioned that this block of pseudo code could be optimised a bit:
I commented out some of te functions that are unnecessary and I was just playing with.
Any help would be greatly appreciated.
"And then 2 men appeared... Men in dark suits.. with dark soulless eyes. Men like this could have come from only one place..
The bank."
for y := 1 to MAXY-1 do begin
for x := 1 to MAXX-1 do begin
xDiff := Trunc(WaveMap[x+1, y] - WaveMap[x, y]);
yDiff := Trunc(WaveMap[x, y+1] - WaveMap[x, y]);
xAngle := arctan( xDiff );
xRefraction := arcsin( sin( xAngle ) / rIndex );
xDisplace := Trunc( tan( xRefraction ) * xDiff );
yAngle := arctan( yDiff );
yRefraction := arcsin( sin( yAngle ) / rIndex );
yDisplace := Trunc( tan( yRefraction ) * yDiff );
if xDiff < 0 then begin
{ Current position is higher - Clockwise rotation }
if yDiff < 0 then
newcolor := BackgroundImage[x-xDisplace, y-yDisplace]
else
newcolor := BackgroundImage[x-xDisplace, y+yDisplace]
end else begin
{ Current position is lower - Counterclockwise rotation }
if yDiff < 0 then
newcolor := BackgroundImage[x+xDisplace, y-yDisplace]
else
newcolor := BackgroundImage[x+xDisplace, y+yDisplace]
end;
TargetImage[x, y] := newcolor;
end;
end;
The only thing I found that I could change a bit were the calculations for xRefraction, and yRefraction. I changed it to:
xRefraction := xAngle/ rIndex;
I am sure this is not a perfect reduction but it doesn''t seem to change much of anything when the code is run. Anyway I was wondering if there was anything else I could do to speed up the codes execution. I think that most of the codes time is spent traversing the array, and I am not so sure the is anything I can do about it.
Here is the source code I am using for t applet:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
public class waterFX extends Applet implements Runnable, MouseListener{
boolean isRunning;
int xborder, yborder, delay, length, rIndex, CT, NW;
Thread runner;
Graphics offscreenG;
Image offscreenImg, bgImg, fgImg;
MemoryImageSource memSrc;
int[] bgImgSrc;
int[] targetImg;
int[][][] waveMap;
public void init(){
//Find the applet border
xborder = this.getSize().width;
yborder = this.getSize().height;
//The length for all of our arrays
length = xborder * yborder;
//init arrays
bgImgSrc = new int[length];
targetImg = new int[length];
waveMap = new int[2][xborder][yborder];
//Crap
CT = 0;
NW = 1;
//Set the refraction index of water
rIndex = 2;
// Create Image & Graphic type variables for buffering animation.
offscreenImg = createImage(xborder, yborder);
offscreenG = offscreenImg.getGraphics();
//Load the background image
bgImg = getImage(getDocumentBase(),"cam411-471.jpg");
//Get the pixel data for the background image
PixelGrabber pg = new PixelGrabber(bgImg,0 ,0 ,xborder ,yborder, bgImgSrc, 0 ,xborder);
try{pg.grabPixels();}
catch(InterruptedException e){System.out.println("Interrupted Exception caught while Pixel Grabbing. Error: " + e);}
//Create the memory image source for our pixel data
memSrc = new MemoryImageSource(xborder, yborder, targetImg, 0, xborder);
memSrc.setAnimated(true);
fgImg = createImage(memSrc);
//Shoot for 40 frames per second
delay = 1000/10;
//Connect the mouse listener
addMouseListener(this);
}
public void start(){
isRunning = true;
runner = new Thread(this);
runner.start();
}
public void stop(){
isRunning = false;
}
public void run(){
while(isRunning){
//Apply our filter
filter_waveMap();
//Render waves
renderWaves();
//Call our paint routines
repaint();
//swap the maps
swapMaps();
//Dampen wave Height maps even further
//fadeMap();
//Pseudo random rain drops
//addRainDrop();
//Sign this thing
this.showStatus("Written by Andrew Mentges");
try{runner.sleep(delay);}
catch(InterruptedException e){
//Throw an error to the console window
System.out.println("Exception caught when trying to sleep.");
}
}
}
// Override the update in the Graphics class to reduce flicker even more.
public void update(Graphics g){paint(g);}
public void paint(Graphics g){
//Paint background
offscreenG.setColor(Color.black);
offscreenG.fillRect(0, 0, xborder, yborder);
offscreenG.drawImage(fgImg, 0, 0, this);
// Actually paint image onscreen.
g.drawImage(offscreenImg, 0, 0, this);
//Flush the memory image source
memSrc.newPixels();
}
public void filter_waveMap(){
int n;
//Loop through all parts of the waveMap
for(int x=1; x < xborder-1; x++){
for(int y=1; y< yborder-1; y++){
n = 0;
//Filter surround pixels of wave map
//Apply filter kernel and shift towards new map
n = ((waveMap[CT][x+1][y] + waveMap[CT][x-1][y] + waveMap[CT][x][y+1] + waveMap[CT][x][y-1])/2) - waveMap[NW][x][y];
//Dampen the waveMap
n = n - (n/15);
//Update the new wave map
waveMap[NW][x][y] = n;
}
}
}
public void swapMaps(){
int tmp;
tmp = CT;
CT = NW;
NW = tmp;
}
public void fadeMap(){
for(int x=0; x < xborder; x++){
for(int y=0; y < yborder; y++){
//if(waveMap[NW][x][y] != 0){
// if(waveMap[NW][x][y] < 0){waveMap[NW][x][y] += 1;}
// if(waveMap[NW][x][y] > 0){waveMap[NW][x][y] -= 1;}
//}
if(waveMap[CT][x][y] != 0){
if(waveMap[CT][x][y] > 0){waveMap[CT][x][y] -= 1;}
if(waveMap[CT][x][y] < 0){waveMap[CT][x][y] += 1;}
}
}
}
}
public void renderWaves(){
int xDiff, yDiff, xDisplace, yDisplace;
int tmpPos, pos, n;
double xAngle, yAngle, xRefraction, yRefraction;
//Loop through all parts of the waveMap
for(int x=1; x < xborder-1; x++){
for(int y=1; y< yborder-1; y++){
xDiff = waveMap[CT][x+1][y] - waveMap[CT][x][y];
yDiff = waveMap[CT][x][y+1] - waveMap[CT][x][y];
xAngle = Math.atan(xDiff);
//xRefraction = Math.asin( Math.sin(xAngle)/ rIndex );
xRefraction = xAngle/ rIndex ;
xDisplace = (int)(Math.tan(xRefraction)* xDiff);
yAngle = Math.atan(yDiff);
//yRefraction = Math.asin( Math.sin(yAngle)/ rIndex );
yRefraction = yAngle/ rIndex ;
yDisplace = (int)(Math.tan(yRefraction)* yDiff);
n = 0x00;
if(xDiff < 0){
//Current position is higher - Clockwise rotation
if(yDiff < 0){
tmpPos = (y-yDisplace)*xborder + (x-xDisplace);
if(tmpPos > 0 && tmpPos < length){
n = bgImgSrc[tmpPos];
}
}
else{
tmpPos = (y+yDisplace)*xborder + (x-xDisplace);
if(tmpPos > 0 && tmpPos < length){
n = bgImgSrc[tmpPos];
}
}
}
else{
//Current position is lower - Counterclockwise rotation
if(yDiff < 0){
tmpPos = (y-yDisplace)*xborder + (x+xDisplace);
if(tmpPos > 0 && tmpPos < length){
n = bgImgSrc[tmpPos];
}
}
else{
tmpPos = (y+yDisplace)*xborder + (x+xDisplace);
if(tmpPos > 0 && tmpPos < length){
n = bgImgSrc[tmpPos];
}
}
}
pos = y*xborder + x;
if(pos > 0 && pos < length){
targetImg[pos] = n;
}
}
}
}
//Handle Mouse Events
public void mouseClicked(MouseEvent e){
//Add an ripple
waveMap[NW][e.getX()][e.getY()] = -700;
}
public void mousePressed(MouseEvent e){
//Do nothing
}
public void mouseReleased(MouseEvent e){
//Do nothing
}
public void mouseEntered(MouseEvent e){
//Do nothing
}
public void mouseExited(MouseEvent e){
//Do nothing
}
public void addRainDrop(){
double chance;
chance = Math.random();
if(chance > .95){
int tX, tY, vel;
tX = (int)(Math.random() * xborder);
tY = (int)(Math.random() * yborder);
vel = (int)(Math.random() * -500) -100;
//Add an ripple
waveMap[NW][tX][tY] = vel;
}
}
}
The bank."