package it.marteEngine.game.starcleaner;
import it.marteEngine.ME;
import it.marteEngine.entity.Entity;
import java.util.ArrayList;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.ImageBuffer;
import org.newdawn.slick.SlickException;
public class LightMap extends Entity {
public static final int DAWN = 0;
public static final int DAY = 4;
public static final int EVENING = 16;
public static final int NIGHT = 20;
public static final int ENDOFDAY = 29;
// dawn is between 0 and 4 (5 seconds), day is between 5 and 14, evening
// between 15 and 19 and night between 20 and 29
private int dayTime = 12; // we'll start with late morning
private int incrDayTime = 1000; // increment daytime every second
private int counterDayTime = 0;
private int dawnCounter = 0;
private int dawnSpan = 0;
private boolean colouredLights = true;
private Image whiteSquare;
private ArrayList<Light> lights = new ArrayList<Light>();
/**
* 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[][][] roughLightValues;
private float[][][] smoothLightValues;
private int mapWidth, mapHeight, mapTileSize;
public LightMap(float x, float y, int tilesize) {
super(x, y);
mapTileSize = tilesize;
mapWidth = StarCleaner.WIDTH / tilesize;
mapHeight = StarCleaner.HEIGHT / tilesize;
roughLightValues = new float[mapWidth + 1][mapHeight + 1][4];
smoothLightValues = new float[mapWidth + 1][mapHeight + 1][4];
depth = 250; // lightmap is on top of all of them, but below the blender
whiteSquare = createImage(tilesize);
}
private Image createImage(int tilesize) {
ImageBuffer buf = new ImageBuffer(tilesize, tilesize);
for (int x = 0; x < tilesize; x++)
for (int y = 0; y < tilesize; y++)
buf.setRGBA(x, y, 255, 255, 255, 255);
return buf.getImage();
}
public void update(GameContainer container, int delta)
throws SlickException {
counterDayTime += delta;
if (counterDayTime >= incrDayTime) {
counterDayTime -= incrDayTime;
dayTime++;
if (dayTime > ENDOFDAY)
dayTime = 0;
}
updateLightMap(delta);
}
// do nothing during day time
public void updateLightMap(int delta) {
boolean dawn = false;
// Log.debug("updateLightMap() is called, dayTime is " + dayTime);
if (dayTime >= DAWN && dayTime < DAY) {
dawn = fillLightMap(delta, -1, DAY - DAWN);
if (!dawn)
return;
} else if (dayTime >= EVENING && dayTime < NIGHT) {
dawn = fillLightMap(delta, 1, NIGHT - EVENING);
}
// for every vertex on the map (notice the +1 again accounting for the
// trailing vertex)
for (int y = 0; y < mapHeight + 1; y++) {
for (int x = 0; x < mapWidth + 1; x++) {
// first reset the lighting value for each component (red,
// green, blue, alpha)
if (!dawn) {
for (int component = 0; component < 4; component++) {
roughLightValues[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
* mapTileSize, y * mapTileSize, colouredLights);
// if (effect[0] != 0 || effect[1] != 0 || effect[2] != 0)
// Log.debug("Light " + i + ": x = " + x + ", y = " + y +
// ", effect[0]-[3]=" + effect[0] + ", " + effect[1] + ", "
// + effect[2] + ", " + effect[3]);
for (int component = 0; component < 3; component++) {
roughLightValues[x][y][component] += effect[component];
}
// alpha value is treated different, not added but the min
// value wins
if (effect[3] > 0)
if (roughLightValues[x][y][3] > 0)
roughLightValues[x][y][3] = Math.min(effect[3],
roughLightValues[x][y][3]);
else
roughLightValues[x][y][3] = effect[3];
}
// finally clamp the components to 1, since we don't want to
// blow up over the colour values
for (int component = 0; component < 4; component++) {
if (roughLightValues[x][y][component] > 1) {
roughLightValues[x][y][component] = 1;
}
}
}
}
// smooth the light map
float smooth = 1 / 9f;
for (int x = 0; x < mapWidth + 1; x++) {
for (int y = 0; y < mapHeight + 1; y++) {
for (int i = 0; i < 3; i++) {
smoothLightValues[x][y][i] = 0;
for (int a = -1; a < 2; a++) {
for (int b = -1; b < 2; b++) {
if ((x + a) >= 0 && (y + b) >= 0
&& (x + a) < mapWidth + 1
&& (y + b) < mapHeight + 1)
smoothLightValues[x][y][i] += (roughLightValues[x
+ a][y + b][i] * smooth);
}
}
}
smoothLightValues[x][y][3] = roughLightValues[x][y][3];
}
}
}
private boolean fillLightMap(int delta, int multiplier, int rangeInSeconds) {
// Log.debug("fillLightMap() is called, dayTime is " + dayTime +
// ", value is " + value + ", range is " + range);
if (this.dawnSpan == 0) {
this.dawnSpan = rangeInSeconds * 1000;
if (multiplier == 1)
this.dawnCounter = 0;
else
this.dawnCounter = this.dawnSpan;
}
this.dawnCounter += (delta * multiplier);
float greyVal = 0.0f;
if (this.dawnCounter <= 0) {
this.dawnSpan = 0;
this.dawnCounter = 0;
return false;
// greyVal = 1.0f;
} else if (this.dawnCounter >= this.dawnSpan) {
this.dawnSpan = 0;
this.dawnCounter = 0;
return false;
// greyVal = 0.0f;
} else
greyVal = ((float) this.dawnCounter) / ((float) this.dawnSpan);
for (int y = 0; y < mapHeight + 1; y++) {
for (int x = 0; x < mapWidth + 1; x++) {
// first reset the lighting value for each component (red,
// green, blue, alpha)
for (int component = 0; component < 3; component++) {
// lightValues[x][y][component] = 0;
roughLightValues[x][y][component] = 0;
}
// lightValues[x][y][3] = greyVal;
roughLightValues[x][y][3] = greyVal;
}
}
return true;
}
public void render(GameContainer container, Graphics g)
throws SlickException {
super.render(container, g);
if (dayTime >= EVENING || dayTime < DAY) {
// only during dawn, evening and night do we have some work to do.
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
g.setColor(Color.white);
// 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.
whiteSquare.setColor(Image.TOP_LEFT,
smoothLightValues[x][y][0],
smoothLightValues[x][y][1],
smoothLightValues[x][y][2],
smoothLightValues[x][y][3]);
whiteSquare.setColor(Image.TOP_RIGHT,
smoothLightValues[x + 1][y][0],
smoothLightValues[x + 1][y][1],
smoothLightValues[x + 1][y][2],
smoothLightValues[x + 1][y][3]);
whiteSquare.setColor(Image.BOTTOM_RIGHT,
smoothLightValues[x + 1][y + 1][0],
smoothLightValues[x + 1][y + 1][1],
smoothLightValues[x + 1][y + 1][2],
smoothLightValues[x + 1][y + 1][3]);
whiteSquare.setColor(Image.BOTTOM_LEFT,
smoothLightValues[x][y + 1][0],
smoothLightValues[x][y + 1][1],
smoothLightValues[x][y + 1][2],
smoothLightValues[x][y + 1][3]);
// draw the image with it's newly declared vertex colours
// to the display
whiteSquare.draw(x * mapTileSize, y * mapTileSize,
mapTileSize, mapTileSize);
}
}
}
if (ME.debugEnabled) {
g.setColor(Color.white);
g.drawString("Lights: " + lights.size(),
container.getWidth() - 110, 100);
g.drawString("dayTime: " + dayTime, container.getWidth() - 110, 120);
}
}
public int getDayTime() {
return dayTime;
}
public void setDayTime(int newDayTime) {
dayTime = newDayTime;
}
public boolean isDay() {
if (dayTime >= DAWN && dayTime < NIGHT)
return true;
return false;
}
public boolean isNight() {
if (dayTime >= NIGHT || dayTime < DAWN)
return true;
return false;
}
public void addLight(Light light) {
light.setLightMap(this);
lights.add(light);
}
public void removeLight(Light light) {
lights.remove(light);
}
}