ProjectZombie/src/projectzombie/display/DisplayLighting.java

360 lines
9.2 KiB
Java
Executable File

package projectzombie.display;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL33;
import bdf.types.BdfNamedList;
import bdf.types.BdfObject;
import gl_engine.MathHelpers;
import gl_engine.range.Range2i;
import gl_engine.vec.Vec2d;
import gl_engine.vec.Vec2i;
import projectzombie.Main;
import projectzombie.entity.Entity;
import projectzombie.entity.player.EntityPlayer;
import projectzombie.util.math.TileState;
import projectzombie.world.chunk.Chunk;
import projectzombie.world.chunk.ChunkEventHandler;
import projectzombie.world.layer.Layer;
public class DisplayLighting
{
private static class Lighting {
float[] p;
int w, h;
int x, y;
private int getID(int x, int y) {
return (x + y * w) * 4 + 1;
}
}
private static int lighting_last_x = 0;
private static int lighting_last_y = 0;
private static boolean lighting_dirty = false;
private static boolean lighting_new = false;
public static int lightmap;
private static Lighting lighting;
public static void setDirty() {
lighting_dirty = true;
}
private synchronized static Lighting getLighting() {
return lighting;
}
private synchronized static void setLighting(Lighting lighting) {
DisplayLighting.lighting = lighting;
lighting_new = true;
}
public DisplayLighting() {
lightmap = GL33.glGenTextures();
}
public static void update()
{
if(Camera.camera == null) return;
if(!ChunkEventHandler.loaded) return;
Layer layer = Main.world.getLayer();
EntityPlayer player = Main.player;
boolean dirty = lighting_dirty;
if(!dirty)
{
for(int cx=-Chunk.RENDER_DISTANCE;cx<=Chunk.RENDER_DISTANCE;cx++) {
for(int cy=-Chunk.RENDER_DISTANCE;cy<=Chunk.RENDER_DISTANCE;cy++) {
Vec2i cpos = new Vec2i(
cx + MathHelpers.floor(player.pos.x / 16),
cy + MathHelpers.floor(player.pos.y / 16));
Chunk chunk = layer.chunks.get(cpos);
if(chunk.isLightDirty()) {
chunk.resetLightDirty();
dirty = true;
}
}
}
}
if(!dirty) {
return;
}
lighting_dirty = false;
int size = (Chunk.RENDER_DISTANCE * 2 + 1) * 16;
float[] lights = new float[size * size * 3];
for(int cx=-Chunk.RENDER_DISTANCE;cx<=Chunk.RENDER_DISTANCE;cx++) {
for(int cy=-Chunk.RENDER_DISTANCE;cy<=Chunk.RENDER_DISTANCE;cy++)
{
Vec2i cpos = new Vec2i(
cx + MathHelpers.floor(player.pos.x / 16),
cy + MathHelpers.floor(player.pos.y / 16));
Chunk chunk = layer.chunks.get(cpos);
for(int x=0;x<16;x++) {
for(int y=0;y<16;y++)
{
Vec2i tpos = new Vec2i(x, y);
int tid = tpos.getId(Chunk.CHUNK_SIZE);
TileState ft = chunk.getFrontTile(tid);
TileState bt = chunk.getBackTile(tid);
float transparency = (float)MathHelpers.biggest(
ft.tile.getLightDissipation(ft),
bt.tile.getLightDissipation(bt));
float lightSrc = (float)MathHelpers.biggest(
ft.tile.getLightLevel(ft, tpos),
bt.tile.getLightLevel(bt, tpos));
float lightSun = ft.tile.passNaturalLight && bt.tile.passNaturalLight ? 1 : 0;
int id = ((cx * 16 + x + Chunk.RENDER_DISTANCE * 16) +
(cy * 16 + y + Chunk.RENDER_DISTANCE * 16) * size) * 3;
lights[id+0] = lightSun;
lights[id+1] = lightSrc;
lights[id+2] = transparency;
}
}
}
}
Main.worker.processLighting(lights, size, size,
MathHelpers.floor(player.pos.x / 16) - Chunk.RENDER_DISTANCE,
MathHelpers.floor(player.pos.y / 16) - Chunk.RENDER_DISTANCE);
}
private static void calculateLight(Layer layer, Lighting lighting, float[] pixels, int x, int y, float level)
{
if(x < 0 || y < 0 || x >= lighting.w || y >= lighting.h) {
return;
}
int id = lighting.getID(x, y);
float level_current = pixels[id];
Vec2i tpos = new Vec2i(x + lighting.x * 16, y + lighting.y * 16);
int tid = tpos.getId(Chunk.CHUNK_SIZE);
if(level_current >= level) {
return;
}
pixels[id] = level;
Chunk chunk = layer.getChunk(tpos);
TileState ft = chunk.getFrontTile(tid);
TileState bt = chunk.getBackTile(tid);
float dissipation = (float)Math.max(ft.tile.getLightDissipation(ft), bt.tile.getLightDissipation(bt));
float level_next = level - dissipation;
int[] adjacent = new int[] {
x+1, y+0,
x+0, y+1,
x-1, y-0,
x-0, y-1,
};
for(int i=0;i<8;i+=2) {
calculateLight(layer, lighting, pixels, adjacent[i], adjacent[i+1], level_next);
}
}
private static void calculateEntityLighting(Layer layer, Lighting lighting, Entity entity, float[] pixels)
{
if(!entity.emitsLight) {
return;
}
double level = entity.getLightLevel();
if(level <= 0) {
return;
}
if(level > 1) {
level = 1;
}
int[] positions = {
0, 0,
1, 0,
1, 1,
0, 1
};
for(int i=0;i<positions.length;i+=2)
{
int x = (int)Math.round(entity.pos.x) - positions[i+0];
int y = (int)Math.round(entity.pos.y) - positions[i+1];
Vec2i lpos = new Vec2i(x, y).subtract(new Vec2i(lighting.x * 16, lighting.y * 16));
if(lpos.x < 0 || lpos.y < 0 || lpos.x >= lighting.w || lpos.y >= lighting.h) {
continue;
}
int id = lighting.getID(lpos.x, lpos.y);
float level_current = pixels[id];
Vec2i tpos = new Vec2i(x, y);
int tid = tpos.getId(Chunk.CHUNK_SIZE);
if(level_current >= level) {
continue;
}
Chunk chunk = layer.getChunk(tpos);
TileState ft = chunk.getFrontTile(tid);
TileState bt = chunk.getBackTile(tid);
double dissipation = Math.max(ft.tile.getLightDissipation(ft), bt.tile.getLightDissipation(bt));
double level2 = level - dissipation * (Math.abs(x + 0.5 - entity.pos.x) + Math.abs(y + 0.5 - entity.pos.y));
//pixels[id] = (float)level2;
calculateLight(layer, lighting, pixels, lpos.x, lpos.y, (float)level2);
}
}
public static void updateLighting()
{
Lighting lighting = getLighting();
// Copy the pixels
float[] pixels = new float[lighting.p.length];
for(int i=0;i<pixels.length;i++) {
pixels[i] = lighting.p[i];
}
Layer layer = Main.world.getLayer();
calculateEntityLighting(layer, lighting, Main.player, pixels);
for(int cx=-Chunk.RENDER_DISTANCE;cx<=Chunk.RENDER_DISTANCE;cx++) {
for(int cy=-Chunk.RENDER_DISTANCE;cy<=Chunk.RENDER_DISTANCE;cy++)
{
Vec2i cpos = new Vec2i(
cx + MathHelpers.floor(Main.player.pos.x / 16),
cy + MathHelpers.floor(Main.player.pos.y / 16));
Chunk chunk = layer.chunks.get(cpos);
for(Entity entity : chunk.entities) {
calculateEntityLighting(layer, lighting, entity, pixels);
}
}
}
if(lighting_new)
{
for(int i=0;i<lighting.p.length/4;i++)
{
int x = i % lighting.w;
int y = i / lighting.w;
Vec2i tpos = new Vec2i(x + lighting.x * 16, y + lighting.y * 16);
// Store light level data from the image
layer.setDaylightLevel(lighting.p[i+0], tpos);
layer.setLightLevel(lighting.p[i+1], tpos);
}
lighting_new = false;
}
ByteBuffer pixels_b = BufferUtils.createByteBuffer(pixels.length);
for(int i=0;i<pixels.length;i++) {
pixels_b.put(i, (byte)(pixels[i]*255));
}
// Update the texture
GL33.glBindTexture(GL33.GL_TEXTURE_2D, lightmap);
if(lighting_last_x != lighting.w || lighting_last_y != lighting.h)
{
GL33.glTexImage2D(GL33.GL_TEXTURE_2D, 0, GL33.GL_RGBA,
lighting.w, lighting.h, 0, GL33.GL_RGBA, GL33.GL_UNSIGNED_BYTE, pixels_b);
lighting_last_x = lighting.w;
lighting_last_y = lighting.h;
}
else {
GL33.glTexSubImage2D(GL33.GL_TEXTURE_2D, 0, 0, 0,
lighting.w, lighting.h, GL33.GL_RGBA, GL33.GL_UNSIGNED_BYTE, pixels_b);
}
GL33.glGenerateMipmap(GL33.GL_TEXTURE_2D);
// Set the texture location data
GL33.glUniform2f(Main.window.glsl_lightmap_offset, lighting.x * 16 - 0.5f, lighting.y * 16 - 0.5f);
GL33.glUniform2f(Main.window.glsl_lightmap_size, lighting.w, lighting.h);
}
public static void clearLighting()
{
Lighting lighting = new Lighting();
lighting.p = new float[3];
lighting.w = 1;
lighting.h = 1;
lighting.x = 0;
lighting.y = 0;
setLighting(lighting);
}
public static void updateLighting(BdfObject bdf)
{
BdfNamedList nl = bdf.getNamedList();
float[] light = nl.get("light").getFloatArray();
int width = nl.get("w").getInteger();
int height = nl.get("h").getInteger();
int x = nl.get("x").getInteger();
int y = nl.get("y").getInteger();
float[] pixels = new float[width*height*4];
for(int i=0;i<width*height;i++) {
pixels[i*4+0] = light[i*2+0];
pixels[i*4+1] = light[i*2+1];
}
Lighting lighting = new Lighting();
lighting.p = pixels;
lighting.w = width;
lighting.h = height;
lighting.x = x;
lighting.y = y;
Layer layer = Main.world.getLayer();
for(int x2=0;x2<lighting.w;x2++) {
for(int y2=0;y2<lighting.h;y2++)
{
int i = (x2 + y2 * lighting.w) * 4;
// Send temperature and humidity data to the image
pixels[i+2] = (float)layer.layergen.getTemperatureStatic(
layer, new Vec2d(x2 + lighting.x * 16, y2 + lighting.y * 16));
pixels[i+3] = (float)layer.layergen.getHumidity(
layer, new Vec2d(x2 + lighting.x * 16, y2 + lighting.y * 16));
}
}
setLighting(lighting);
}
}