360 lines
9.2 KiB
Java
Executable File
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);
|
|
}
|
|
}
|