Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
kevin
committed
Nov 2, 2010
1 parent
c2f894d
commit 42eda61
Showing
2 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package org.newdawn.slick.examples.lights; | ||
|
||
import org.newdawn.slick.Color; | ||
|
||
/** | ||
* A single light in the example. It's capable of determining how much effect | ||
* it will have in any given point on the tile map. Note that all coordinates | ||
* are given in tile coordinates rather than pixel coordinates. | ||
* | ||
* @author kevin | ||
*/ | ||
class Light { | ||
/** The x coordinate of the position the light has in the world */ | ||
private float xpos; | ||
/** The y coordinate of the position the light has in the world */ | ||
private float ypos; | ||
/** The strength of the light, this specifies in tiles how far the light will shine */ | ||
private float strength; | ||
/** The colour the light should apply */ | ||
private Color col; | ||
|
||
/** | ||
* Create a new light in the world | ||
* | ||
* @param x The x coordinate of the position the light has in the world | ||
* @param y The y coordinate of the position the light has in the world | ||
* @param str The strength of the light, this specifies in tiles how far the light will shine | ||
* @param col The colour the light should apply | ||
*/ | ||
public Light(float x, float y, float str, Color col) { | ||
xpos = x; | ||
ypos = y; | ||
strength = str; | ||
this.col = col; | ||
} | ||
|
||
/** | ||
* Set the location of the light in the world | ||
* | ||
* @param x The x coordinate of the position the light has in the world | ||
* @param y The y coordinate of the position the light has in the world | ||
*/ | ||
public void setLocation(float x, float y) { | ||
xpos = x; | ||
ypos = y; | ||
} | ||
|
||
/** | ||
* Get the effect the light should apply to a given location | ||
* | ||
* @param x The x coordinate of the location being considered for lighting | ||
* @param y The y coordinate of the location being considered for lighting | ||
* @param colouredLights True if we're supporting coloured lights | ||
* | ||
* @return The effect on a given location of the light in terms of colour components (all | ||
* the same if we don't support coloured lights) | ||
*/ | ||
public float[] getEffectAt(float x, float y, boolean colouredLights) { | ||
// first work out what propotion of the strength distance the light | ||
// is from the point. This is a value from 0-1 where 1 is the centre of the | ||
// light (i.e. full brightness) and 0 is the very edge (or outside) the lights | ||
// range | ||
float dx = (x - xpos); | ||
float dy = (y - ypos); | ||
float distance2 = (dx*dx)+(dy*dy); | ||
float effect = 1 - (distance2 / (strength*strength)); | ||
|
||
if (effect < 0) { | ||
effect = 0; | ||
} | ||
|
||
// if we doing coloured lights then multiple the colour of the light | ||
// by the effect. Otherwise just use the effect for all components to | ||
// give white light | ||
if (colouredLights) { | ||
return new float[] {col.r * effect, col.g * effect, col.b * effect}; | ||
} else { | ||
return new float[] {effect,effect,effect}; | ||
} | ||
} | ||
} |
244 changes: 244 additions & 0 deletions
244
examples/org/newdawn/slick/examples/lights/LightTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
package org.newdawn.slick.examples.lights; | ||
|
||
import java.util.ArrayList; | ||
|
||
import org.newdawn.slick.BasicGame; | ||
import org.newdawn.slick.Color; | ||
import org.newdawn.slick.GameContainer; | ||
import org.newdawn.slick.Graphics; | ||
import org.newdawn.slick.Image; | ||
import org.newdawn.slick.Input; | ||
import org.newdawn.slick.SlickException; | ||
import org.newdawn.slick.SpriteSheet; | ||
import org.newdawn.slick.util.Bootstrap; | ||
|
||
/** | ||
* This example shows using vertex colours on a tile map to producing a lighting | ||
* effect. The approach is very efficient and pretty flexible. It can be extended | ||
* in many ways. | ||
* | ||
* The down side is that the resolution of your lighting map is the same as that of your | ||
* tile map. So the small area you'll see a change in lighting across is 32x32 pixels in | ||
* this case (the size of a single tile). This can be worked round by rendering black tiles | ||
* over an existing map at a different resolution. For instance, you may use 16x16 black tiles | ||
* over a 32x32 pixels tiled map to get a better resolution of lighting. | ||
* | ||
* This example essentially generates a random map of tiles, creates a set of lights and calculates | ||
* their effect on each of vertexs in the tiled map. When rendering we apply those calculated values | ||
* to the tile image vertex colours before rendering it. This gives us the effect of lighting | ||
* the tiles. | ||
* | ||
* @author kevin | ||
*/ | ||
public class LightTest extends BasicGame { | ||
/** The width of the tile map in tiles */ | ||
private static final int WIDTH = 15; | ||
/** The height of the tile map in tiles */ | ||
private static final int HEIGHT = 15; | ||
|
||
/** True if we're going to render with lighting */ | ||
private boolean lightingOn = true; | ||
/** True if we're going to render coloured lighting .... oooooh disco inferno! */ | ||
private boolean colouredLights = false; | ||
|
||
/** The sprite sheet we're using for our tiles */ | ||
private SpriteSheet tiles; | ||
/** The tile map we'll randomly generate and render */ | ||
private int[][] map = new int[WIDTH][HEIGHT]; | ||
/** | ||
* The values calculated for each vertex of the tile map, | ||
* note how it's one more to account for the bottom corner of the map. | ||
* The 3 dimension is for colour components (red, green, blue) used for coloured lighting | ||
*/ | ||
private float[][][] lightValue = new float[WIDTH+1][HEIGHT+1][3]; | ||
/** The lights we've defined */ | ||
private ArrayList lights = new ArrayList(); | ||
/** The main light that we'll move around with the mouse, held seperately so we can update it */ | ||
private Light mainLight; | ||
|
||
/** | ||
* Create the example game | ||
*/ | ||
public LightTest() { | ||
super("Light Test"); | ||
} | ||
|
||
/** | ||
* Initialise our resources for the example | ||
* | ||
* @param container The game container the game is running in | ||
*/ | ||
public void init(GameContainer container) throws SlickException { | ||
tiles = new SpriteSheet("testdata/tiles.png", 32,32); | ||
generateMap(); | ||
} | ||
|
||
/** | ||
* Randomly generate a tile map | ||
*/ | ||
private void generateMap() { | ||
// cycle through the map placing a random tile in each location | ||
for (int y=0;y<HEIGHT;y++) { | ||
for (int x=0;x<WIDTH;x++) { | ||
map[x][y] = 0; | ||
|
||
// 20% of tiles have features | ||
if (Math.random() > 0.8) { | ||
map[x][y] = 1 + (int) (Math.random() * 7); | ||
} | ||
} | ||
} | ||
|
||
// create and add our lights | ||
lights.clear(); | ||
|
||
mainLight = new Light(8f,7f,4f,Color.white); | ||
lights.add(mainLight); | ||
lights.add(new Light(2,2,2f,Color.red)); | ||
lights.add(new Light(2,11,1.5f,Color.yellow)); | ||
lights.add(new Light(12,2,3f,Color.green)); | ||
|
||
// finally update the lighting map for the first time | ||
updateLightMap(); | ||
} | ||
|
||
/** | ||
* Update the vertex values for lighting based on the current | ||
* light configuration. | ||
*/ | ||
private void updateLightMap() { | ||
// for every vertex on the map (notice the +1 again accounting for the trailing vertex) | ||
for (int y=0;y<HEIGHT+1;y++) { | ||
for (int x=0;x<WIDTH+1;x++) { | ||
// first reset the lighting value for each component (red, green, blue) | ||
for (int component=0;component<3;component++) { | ||
lightValue[x][y][component] = 0; | ||
} | ||
|
||
// next cycle through all the lights. Ask each light how much effect | ||
// it'll have on the current vertex. Combine this value with the currently | ||
// existing value for the vertex. This lets us blend coloured lighting and | ||
// brightness | ||
for (int i=0;i<lights.size();i++) { | ||
float[] effect = ((Light) lights.get(i)).getEffectAt(x, y, colouredLights); | ||
for (int component=0;component<3;component++) { | ||
lightValue[x][y][component] += effect[component]; | ||
} | ||
} | ||
|
||
// finally clamp the components to 1, since we don't want to | ||
// blow up over the colour values | ||
for (int component=0;component<3;component++) { | ||
if (lightValue[x][y][component] > 1) { | ||
lightValue[x][y][component] = 1; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Update the game | ||
* | ||
* @param container The container the game is running in | ||
* @param delta The amount of time that passed since last update (in seconds) | ||
*/ | ||
public void update(GameContainer container, int delta) | ||
throws SlickException { | ||
// toggle the lighting on/off | ||
if (container.getInput().isKeyPressed(Input.KEY_L)){ | ||
lightingOn = !lightingOn; | ||
} | ||
// toggle the use of coloured lighting on/off | ||
if (container.getInput().isKeyPressed(Input.KEY_C)){ | ||
colouredLights = !colouredLights; | ||
// we need to recaculate the lighting values because | ||
// colours may now be involved | ||
updateLightMap(); | ||
} | ||
} | ||
|
||
/** | ||
* Notification that the mouse was dragged | ||
* | ||
* @param oldx The old x coordinate of the mouse | ||
* @param oldy The old y coordinate of the mouse | ||
* @param newx The new x coordinate of the mouse | ||
* @param newy The new y coordinate of the mouse | ||
*/ | ||
public void mouseDragged(int oldx, int oldy, int newx, int newy) { | ||
mousePressed(0, newx, newy); | ||
} | ||
|
||
/** | ||
* Notification that mouse was pressed | ||
* | ||
* @param button The button that was pressed | ||
* @param x The x coordinate the mouse was pressed at | ||
* @param y The y coordinate the mouse was pressed at | ||
*/ | ||
public void mousePressed(int button, int x, int y) { | ||
mainLight.setLocation((x-64)/32.0f,(y-50)/32.0f); | ||
updateLightMap(); | ||
} | ||
|
||
/** | ||
* Render the tile map and lighting to the game window | ||
* | ||
* @param container The container the game is running in | ||
* @param g The graphics context to which we can render | ||
*/ | ||
public void render(GameContainer container, Graphics g) | ||
throws SlickException { | ||
// display some instructions on how to use the example | ||
g.setColor(Color.white); | ||
g.drawString("Lighting Example", 440, 5); | ||
g.drawString("Press L to toggle light", 80, 560); | ||
g.drawString("Press C to toggle coloured lights", 80, 575); | ||
g.drawString("Click or Drag to move the main light", 80, 545); | ||
|
||
// move the display to nicely position the tilemap | ||
g.translate(64,50); | ||
|
||
// cycle round every tile in the map | ||
for (int y=0;y<HEIGHT;y++) { | ||
for (int x=0;x<WIDTH;x++) { | ||
// get the appropriate image to draw for the current tile | ||
int tile = map[x][y]; | ||
Image image = tiles.getSubImage(tile % 4, tile / 4); | ||
|
||
if (lightingOn) { | ||
// if lighting is on apply the lighting values we've | ||
// calculated for each vertex to the image. We can apply | ||
// colour components here as well as just a single value. | ||
image.setColor(Image.TOP_LEFT, lightValue[x][y][0], lightValue[x][y][1], lightValue[x][y][2], 1); | ||
image.setColor(Image.TOP_RIGHT, lightValue[x+1][y][0], lightValue[x+1][y][1], lightValue[x+1][y][2], 1); | ||
image.setColor(Image.BOTTOM_RIGHT, lightValue[x+1][y+1][0], lightValue[x+1][y+1][1], lightValue[x+1][y+1][2], 1); | ||
image.setColor(Image.BOTTOM_LEFT, lightValue[x][y+1][0], lightValue[x][y+1][1], lightValue[x][y+1][2], 1); | ||
} else { | ||
// if lighting is turned off then use "1" for every value | ||
// so we just have full colour everywhere. | ||
float light = 1; | ||
image.setColor(Image.TOP_LEFT, light, light, light, 1); | ||
image.setColor(Image.TOP_RIGHT, light, light, light, 1); | ||
image.setColor(Image.BOTTOM_RIGHT, light, light, light, 1); | ||
image.setColor(Image.BOTTOM_LEFT, light, light, light, 1); | ||
} | ||
|
||
// draw the image with it's newly declared vertex colours | ||
// to the display | ||
image.draw(x*32,y*32); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Entry point to the example game | ||
* | ||
* @param argv The arguments provided at the command line | ||
*/ | ||
public static void main(String[] argv) { | ||
Bootstrap.runAsApplication(new LightTest(), 600, 600, false); | ||
} | ||
} | ||
|