Skip to main content
GameDev.net gamedev.net
🔒 Locked

[java] Function Optimizsation

Started by Wrathnut Apr 22, 2003 at 11:09 AM 12 replies 1.6k views
Original Post
Wrathnut
Wrathnut
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:
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;
		}
		
	}
	
}
  
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."
CaptainJester
CaptainJester
There are some other optimizations you can do as well.

1. When you compile use the '-g:none' switch. This turns off debugging information.

2. Another thing you can do is make all your methods final. This will cause the compiler to use static binding, which is faster.

3. Do not use fixed point math. On today's machines, floating point operations are just as fast as integer operations. What you should be carefull not to do is cast from float to int or int to float. This is a huge it.

4. Here is one of your biggest performance killers:

//Dampen the waveMap
n = n - (n/15);

This is almost always guarunteed to cast a float to an int and it is inside a double loop, so it is a real killer.

If you could change the 15 to a 16, you could do this instead:

//Dampen the waveMap
n = n - (n>>4);

This is way faster, as there are no intermediate operations.

6.Check out http://www-2.cs.cmu.edu/~jch/java/optimization.html. I just took a quick look, but it doesn't look to bad.



First make it work,
then make it fast.

--Brian Kernighan

"I’m happy to share what I can, because I’m in it for the love of programming. The Ferraris are just gravy, honest!" --John Carmack: Forward to Graphics Programming Black Book

[edited by - CaptainJester on April 22, 2003 10:20:36 PM]
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][
CaptainJester
CaptainJester
quote:
Original post by Anonymous Poster
That''s simply not true, even if a lot of people are claiming it. Fixed point IS still faster (but more ugly). The problem with his code are the trigonometrix function. He has 4 calls to tan/atan per pixel, which (at 100*100) makes 40000 calls just for that. And that is slow and the first thing to get rid of. To use look-up tables and to avoid the cast every time (you are right on this one), you somehow have to use fixed-point math instead. How do you want to get your table-index otherwise?



Talk to many of the elite on this board, ie Oluseyi, Kylotan, Magmai Kai Holmlor. I have heard the same from many of these people. Modern processors have been so optimized that floating point operations are done with the same speed as fixed point integer operations.

Like you say, however, calling tan/atan that much is a real killer as well.



First make it work,
then make it fast.

--Brian Kernighan

"I’m happy to share what I can, because I’m in it for the love of programming. The Ferraris are just gravy, honest!" --John Carmack: Forward to Graphics Programming Black Book
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][
NuffSaid
NuffSaid
Adding to CaptainJester''s list:

7. Force all your users to use Java 1.4.2 and above. The JVM now has support for SSE/SSE2 instructions, and it automagically generates them from your floating point code. Download the beta and see. Of course, you''ll need a processor that supports SSE2.

quote:
To use look-up tables and to avoid the cast every time (you are right on this one), you somehow have to use fixed-point math instead. How do you want to get your table-index otherwise?


I have to disagree on look up tables. Floating point operations of today''s processors is fast enough that using look up tables might actually decrease performance. Especially if the data is not already in the cache. Most processors (IIRC, P6 and above) have instructions for functions like cos, sin, atan, tan, and sqrt. A good compiler/VM will use these instructions, so you''ll get very good speed. A bad one....that''s another story altogether.

So my opinion is this. Try both methods out, and see which one is faster.
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
NuffSaid
NuffSaid
I have no idea.......

I concur with what you''ve said though. I tried compiling it on my machine, and it was dog slow. He needs another algorithm....
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
CaptainJester
CaptainJester
I made some optimizations and got a significat increase in speed. The big problem was how slow you were making the frame rate with your delay variable.

Here is the changed source: look for //******* for changes

  
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 final 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


//**********
//go for 100 frames per second 40 is way to slow
delay = 10;
//**********

//Connect the mouse listener
addMouseListener(this);
}

public final void start() {
isRunning = true;
runner = new Thread(this);
runner.start();
}

public final void stop() {
isRunning = false;
}

public final void run() {
long time=0;
while(isRunning){
time=System.currentTimeMillis();
//Apply our filter
filter_waveMap();
//Render waves
renderWaves();
//Call our paint routines
//repaint();

//**********
//don''t call repaint() it is much slower
paint(getGraphics());
//**********

//swap the maps
//Dampen wave Height maps even further
//fadeMap();
//Pseudo random rain drops
//addRainDrop();
swapMaps();
//Sign this thing

//**********
//don''t be vain until it works
//this.showStatus("Written by Andrew Mentges");
//**********

//**********
//don''t use sleep in performance routines
while(System.currentTimeMillis()-time<delay);
/*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 final void update(Graphics g) {
paint(g);
}

public final 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 final void filter_waveMap() {
int n;
//Loop through all parts of the waveMap
int tempx=xborder-1;
int tempy=yborder-1;
for(int x=1; x < tempx-1; x++) {
for(int y=1; y< tempy-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

//**********
//changed the n/15 to n/16(n>>4) so you can use the faster shift operator
n = n - (n>>4);
//**********
//Update the new wave map
waveMap[NW][x][y] = n;
}
}
}

public final void swapMaps() {
int tmp;
tmp = CT;
CT = NW;
NW = tmp;
}

public final 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 final void renderWaves() {
int xDiff, yDiff, xDisplace, yDisplace;
int tmpPos, pos, n;
double xAngle, yAngle, xRefraction, yRefraction;
//Loop through all parts of the waveMap
int tempx=xborder-1;
int tempy=yborder-1;
for(int x=1; x < tempx; x++) {
for(int y=1; y< tempy; y++) {
xDiff = waveMap[CT][x+1][y] - waveMap[CT][x][y];
yDiff = waveMap[CT][x][y+1] - waveMap[CT][x][y];

//**********
// check to see if it is necessaray to make a change at all
if(!(xDiff==0 && yDiff==0)) {
//**********

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;

//**********
//the visuals are so small that I did not notice a difference when I
//commented out all the various diffs
//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;
}

//**********

//if the cahnge was not necessary, just copy the pixel directly

}
else {
pos = y*xborder + x;
targetImg[pos] = bgImgSrc[pos];
}
//**********


}
}
}

//Handle Mouse Events

public final void mouseClicked(MouseEvent e) {
//Add an ripple

waveMap[NW][e.getX()][e.getY()] = -700;
}
public final void mousePressed(MouseEvent e){
//Do nothing

}
public final void mouseReleased(MouseEvent e){
//Do nothing

}
public final void mouseEntered(MouseEvent e){
//Do nothing

}
public final void mouseExited(MouseEvent e){
//Do nothing

}
public final 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;
}
}
}


There are still more optimizations that could be done, like getting rid of the double loop for the wavemap and making it a single loop. Set up a lookup table for tan and atan as you are constantly casting inside the loop and that is a real killer.



First make it work,
then make it fast.

--Brian Kernighan

"I’m happy to share what I can, because I’m in it for the love of programming. The Ferraris are just gravy, honest!" --John Carmack: Forward to Graphics Programming Black Book
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][
NuffSaid
NuffSaid
Its noticably faster, but its still rather slow.
==========================================In a team, you either lead, follow or GET OUT OF THE WAY.
Wrathnut
Wrathnut
Hi all!

Sorry it took me so long to get back to the thread. I did try some of the suggestions from yesterday to get the code running faster and it helped alot. I used bit shifting instead of division everywhere and I also found the domain sets for the algo and made some lookup tables.

Just doing that made it work about 1000x faster. I think if I try the changes that CaptainJester pointed out I can make this thing blazing fast for even large pics.
Although with the lookup tables I did notice that the waves aren''t as pretty anymore, but the do give the illusion of ripples which is the point of it. Also the height maps dampen much quicker(I personally like that better.)

Anyway here is the code that I hae optimised so far. If anyone is interested I will post the code tomorrow with te hoptimisations suggested today. Thanks million for the great response everyone!!

  
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, CT, NW;
Thread runner;
Graphics offscreenG;
Image offscreenImg, bgImg, fgImg;
MemoryImageSource memSrc;
int[] bgImgSrc;
int[] targetImg;
int[][][] waveMap;
int[] arcTangent;
int[] arcTangentNeg;
int[] Tangent;
int[] TangentNeg;

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];
arcTangent = new int[1001];
arcTangentNeg = new int [1001];
//Precalculate tangent array

Tangent = new int []{(int)(Math.tan(0)),(int)(Math.tan(1))};
//Precalculate angent array for negative numbers

TangentNeg = new int []{(int)(Math.tan(0)),(int)(Math.tan(-1))};

//Precalculate arc tangent array

for(int i=0; i <= 1000; i++){
arcTangent[i] = (int)(Math.atan(i));
}
//Precalculate arc angent array for negative numbers

for(int i=-1000; i <= 0; i++){
arcTangentNeg[(i*-1)] = (int)(Math.atan(i));
}

//Crap

CT = 0;
NW = 1;

// Create Image & Graphic type variables for buffering animation.

offscreenImg = createImage(xborder, yborder);
offscreenG = offscreenImg.getGraphics();

//Load the background image

bgImg = getImage(getDocumentBase(),getParameter("imgName"));

//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/35;

//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();

//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){

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>>4);
//Update the new wave map

waveMap[NW][x][y] = n;

}
}
}



public void swapMaps(){
int tmp;
tmp = CT;
CT = NW;
NW = tmp;
}

public void renderWaves(){
int xDiff, yDiff, xDisplace, yDisplace;
int tmpPos, pos, n, xRefraction, yRefraction, xAngle, yAngle;
//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];

if(xDiff >= 0){
xAngle = arcTangent[xDiff];
}
else{
xAngle = arcTangentNeg[(xDiff*-1)];
}

xRefraction = xAngle>>2;

if(xRefraction >=0){
xDisplace = Tangent[xRefraction]* xDiff;
}
else{
xDisplace = TangentNeg[(xRefraction*-1)]* xDiff;
}

if(yDiff >= 0){
yAngle = arcTangent[yDiff];
}
else{
yAngle = arcTangentNeg[(yDiff*-1)];
}

yRefraction = yAngle>>2;
if(yRefraction >=0){
yDisplace = Tangent[yRefraction]* yDiff;
}
else{
yDisplace = TangentNeg[(yRefraction*-1)]* 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()] = 800;
}

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-2)) + 1;
tY = (int)(Math.random() * (yborder-2)) + 1;
vel = (int)(Math.random() * -700) -100;
//Add an ripple

waveMap[NW][tX][tY] = vel;
}

}

}


Applet code:

<applet code="waterFX.class" name="WaterFX" width="280" height="102" >
<param name="imgName" value="beb_titb.gif">



PS Nuffsaid, You''re right I am using 1.1(hoping to rectify this as all the job listings in DC seem to be for J2EE coders.) Is there something wrong with these forums cause it seems like one of the posts I missed out on.


"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."
CaptainJester
CaptainJester
I did one more thing. I converted your 3 dimensional array to 2 dimensions.


  
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 final 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][length];
//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


//**********
//go for 100 frames per second 40 is way to slow
delay = 10;
//**********

//Connect the mouse listener
addMouseListener(this);
}

public final void start() {
isRunning = true;
runner = new Thread(this);
runner.start();
}

public final void stop() {
isRunning = false;
}

public final void run() {
long time=0;
while(isRunning){
time=System.currentTimeMillis();
//Apply our filter
filter_waveMap();
//Render waves
renderWaves();
//Call our paint routines
//repaint();

//**********
//don''t call repaint() it is much slower
paint(getGraphics());
//**********

//swap the maps
//Dampen wave Height maps even further
//fadeMap();
//Pseudo random rain drops
addRainDrop();
swapMaps();
//Sign this thing

//**********
//don''t be vain until it works
//this.showStatus("Written by Andrew Mentges");
//**********

//**********
//don''t use sleep in performance routines
while(System.currentTimeMillis()-time<delay);
/*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 final void update(Graphics g) {
paint(g);
}

public final 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 final void filter_waveMap() {
int n;
int remainder=0;
//Loop through all parts of the waveMap
int tempx=((length-1)-xborder)-1;
for(int x=xborder; x < tempx; x++) {
remainder=x%xborder;
if(!(remainder==0 || remainder==(xborder-1))) {
n = 0;
//Filter surround pixels of wave map
//Apply filter kernel and shift towards new map
n = ((waveMap[CT][x+1] + waveMap[CT][x-1] + waveMap[CT][x+xborder] + waveMap[CT][x-xborder])/2) - waveMap[NW][x];

//Dampen the waveMap

//**********
//changed the n/15 to n/16(n>>4) so you can use the faster shift operator
n = n - (n>>4);
//**********
//Update the new wave map
waveMap[NW][x] = n;
}
}
}

public final void swapMaps() {
int tmp;
tmp = CT;
CT = NW;
NW = tmp;
}

public final void fadeMap() {
for(int x=0; x < length; x++) {
if(waveMap[CT][x] != 0) {
if(waveMap[CT][x] > 0){waveMap[CT][x] -= 1;}
if(waveMap[CT][x] < 0){waveMap[CT][x] += 1;}
}
}
}

public final void renderWaves() {
int xDiff, yDiff, xDisplace, yDisplace;
int tmpPos, pos, n;
int remainder=0;
double xAngle, yAngle, xRefraction, yRefraction;
//Loop through all parts of the waveMap
int tempx=((length-1)-xborder)-1;
for(int x=1; x < tempx; x++) {
remainder=x%xborder;
if(!(remainder==0 || remainder==(xborder-1))) {
xDiff = waveMap[CT][x+1] - waveMap[CT][x];
yDiff = waveMap[CT][x+xborder] - waveMap[CT][x];

//**********
// check to see if it is necessaray to make a change at all
if(!(xDiff==0 && yDiff==0)) {
//**********

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;

//**********
//the visuals are so small that I did not notice a difference when I
//commented out all the various diffs
//if(xDiff < 0) {
//Current position is higher - Clockwise rotation
//if(yDiff < 0) {
tmpPos = x-xDisplace + (yDisplace*xborder);//(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];
}
}
}*/

//**********

targetImg[x] = n;

//**********

//if the cahnge was not necessary, just copy the pixel directly

}
else {
targetImg[x] = bgImgSrc[x];
}
//**********


}
}
}

//Handle Mouse Events

public final void mouseClicked(MouseEvent e) {
//Add an ripple

waveMap[NW][e.getX()+(e.getY()*xborder)] = -700;
}
public final void mousePressed(MouseEvent e){
//Do nothing

}
public final void mouseReleased(MouseEvent e){
//Do nothing

}
public final void mouseEntered(MouseEvent e){
//Do nothing

}
public final void mouseExited(MouseEvent e){
//Do nothing

}
public final void addRainDrop() {
double chance;
chance = Math.random();
if(chance > .95) {
int tX, tY, vel;
tX = (int)(Math.random() * length);
//tX = (int)(Math.random() * xborder);

//tY = (int)(Math.random() * yborder);

vel = (int)(Math.random() * -500) -100;
//Add an ripple

waveMap[NW][tX] = vel;
}
}
}


I don''t know what effect this will have on slower machines. I am running this on a 2.3GHz, so I did not see a difference.



First make it work,
then make it fast.

--Brian Kernighan

"I’m happy to share what I can, because I’m in it for the love of programming. The Ferraris are just gravy, honest!" --John Carmack: Forward to Graphics Programming Black Book
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.