ProjectZombie/src/projectzombie/world/chunk/Chunk.java

717 lines
17 KiB
Java
Executable File

package projectzombie.world.chunk;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Random;
import bdf.classes.IBdfClassManager;
import bdf.types.BdfArray;
import bdf.types.BdfNamedList;
import bdf.types.BdfObject;
import gl_engine.MathHelpers;
import gl_engine.matrix.Matrix4;
import gl_engine.range.Range2i;
import gl_engine.texture.TextureRef3D;
import gl_engine.vec.Vec2d;
import gl_engine.vec.Vec2i;
import gl_engine.vec.Vec3d;
import projectzombie.Main;
import projectzombie.display.Camera;
import projectzombie.entity.Entity;
import projectzombie.entity.EntityAlive;
import projectzombie.entity.EntityHoldsEntities;
import projectzombie.entity.EntityKillWithParticles;
import projectzombie.entity.EntityParticle;
import projectzombie.entity.EntityParticlePart;
import projectzombie.entity.particle.ParticleBreak;
import projectzombie.entity.tileentity.TileEntity;
import projectzombie.init.Tiles;
import projectzombie.model.IModel;
import projectzombie.model.Model;
import projectzombie.model.ModelChunk;
import projectzombie.tiles.Tile;
import projectzombie.util.math.TileState;
import projectzombie.util.math.random.RandomHelpers;
import projectzombie.world.layer.Layer;
public class Chunk implements IBdfClassManager
{
public static final Range2i CHUNK_SIZE = new Range2i(16, 16);
public static final Chunk CHUNK_EMPTY = new ChunkEmpty();
public static final int CHUNK_INDEX = CHUNK_SIZE.mx * CHUNK_SIZE.my;
public static final Random rand = new Random();
public static int SIMULATION_DISTANCE = 10;
public static int RENDER_DISTANCE = 2;
public static boolean SHOW_CHUNKS = false;
private Tile tiles_back[] = new Tile[CHUNK_INDEX];
private Tile tiles_front[] = new Tile[CHUNK_INDEX];
private byte tiles_front_meta[] = new byte[CHUNK_INDEX];
private byte tiles_back_meta[] = new byte[CHUNK_INDEX];
private byte tiles_lighting[] = new byte[CHUNK_INDEX];
public ArrayList<Entity> entities = new ArrayList<Entity>();
private Layer layer;
public Vec2i c_pos;
private boolean dirty;
private boolean light_dirty;
private boolean render_dirty;
private ModelChunk model;
public boolean isDirty()
{
if(entities.size() > 0) {
return true;
}
return dirty;
}
public void clearDirty() {
dirty = false;
}
private void setDirty() {
this.light_dirty = true;
this.render_dirty = true;
this.dirty = true;
}
public boolean isLightDirty() {
return light_dirty;
}
public void resetLightDirty() {
this.light_dirty = false;
}
@Override
public void BdfClassLoad(BdfObject bdf)
{
BdfNamedList nl = bdf.getNamedList();
// Load all the tiles and meta
short[] tb = nl.get("tilesBack").getShortArray();
short[] tf = nl.get("tilesFront").getShortArray();
byte[] mb = nl.get("metaBack").getByteArray();
byte[] mf = nl.get("metaFront").getByteArray();
for(int i=0;i<CHUNK_INDEX;i++)
{
tiles_back[i] = Tiles.tiles.get(tb[i]);
tiles_front[i] = Tiles.tiles.get(tf[i]);
tiles_back_meta[i] = mb[i];
tiles_front_meta[i] = mf[i];
tiles_lighting[i] = 0;
}
// Load all the entities
entities.clear();
BdfArray bdf_entities = nl.get("entities").getArray();
for(BdfObject bdf_entity : bdf_entities) {
Entity entity = Entity.loadEntity(bdf_entity);
if(entity != null) {
entity.chunk = this;
entities.add(entity);
}
}
}
@Override
public void BdfClassSave(BdfObject bdf)
{
BdfNamedList nl = bdf.getNamedList();
short[] tb = new short[CHUNK_INDEX];
short[] tf = new short[CHUNK_INDEX];
// Save all the tiles and tile meta
for(int i=0;i<CHUNK_INDEX;i++) {
tb[i] = tiles_back[i].id;
tf[i] = tiles_front[i].id;
}
nl.set("tilesBack", bdf.newObject().setShortArray(tb));
nl.set("tilesFront", bdf.newObject().setShortArray(tf));
nl.set("metaBack", bdf.newObject().setByteArray(tiles_back_meta));
nl.set("metaFront", bdf.newObject().setByteArray(tiles_front_meta));
// Save all the saveable entity data
BdfArray bdf_entities = bdf.newArray();
nl.set("entities", bdf.newObject().setArray(bdf_entities));
for(Entity e : entities) {
if(e.getID() == -1)
continue;
BdfObject bdf_e = bdf.newObject();
e.BdfClassSave(bdf_e);
bdf_entities.add(bdf_e);
}
}
public Chunk(Layer layer, Vec2i c_pos, BdfObject bdf)
{
this.layer = layer;
this.c_pos = c_pos;
setDirty();
BdfClassLoad(bdf);
}
public Chunk(Layer layer, Vec2i c_pos, Random rand)
{
// Set some specified values
this.layer = layer;
this.c_pos = c_pos;
this.dirty = false;
this.light_dirty = true;
// Loop over all the tiles in the chunk
for(int i=0;i<CHUNK_INDEX;i++)
{
// Make all these tiles void
tiles_back[i] = Tiles.VOID;
tiles_front[i] = Tiles.VOID;
tiles_back_meta[i] = 0;
tiles_front_meta[i] = 0;
// Set the light level to 0
tiles_lighting[i] = 0;
}
}
public int getParticlePoolSize() {
return getParticlePoolSize(entities.toArray());
}
public int getParticlePoolSize(Object[] entities)
{
int particle_pool_size = 0;
for(Object o : entities)
{
Entity e = (Entity)o;
if(e == null || e.isDead()) {
continue;
}
if(e instanceof EntityHoldsEntities) {
particle_pool_size += getParticlePoolSize(((EntityHoldsEntities) e).getEntities());
}
if(e instanceof EntityParticle)
{
EntityParticle ep = (EntityParticle) e;
for(int i=0;i<ep.getParticleCount();i++)
{
EntityParticlePart p = ep.getParticleAt(i);
if(p == null) {
continue;
}
Vec3d pos = Matrix4.multiply(
Camera.camera.projection,
p.pos.subtract(new Vec3d(0.5 + Camera.camera.x, 0, 0.5 + Camera.camera.y)));
if(Math.abs(pos.x) <= 1.125 && Math.abs(pos.y) <= 1.125 && Math.abs(pos.z) <= 1.125) {
particle_pool_size += 1;
}
}
}
}
return particle_pool_size;
}
private int renderEntities(Object[] entities, FloatBuffer particle_pool, int upto)
{
// Render each entity
for(Object o : entities)
{
Entity e = (Entity)o;
if(e == null || e.isDead()) {
continue;
}
if(e instanceof EntityHoldsEntities) {
upto = renderEntities(((EntityHoldsEntities) e).getEntities(), particle_pool, upto);
}
if(e instanceof EntityParticle && Main.world.isPoolDirty())
{
EntityParticle ep = (EntityParticle)e;
for(int i=0;i<ep.getParticleCount();i++)
{
EntityParticlePart p = ep.getParticleAt(i);
if(p == null) {
continue;
}
Vec3d pos = Matrix4.multiply(
Camera.camera.projection,
p.pos.subtract(new Vec3d(0.5 + Camera.camera.x, 0, 0.5 + Camera.camera.y)));
if(Math.abs(pos.x) > 1.125 || Math.abs(pos.y) > 1.125 || Math.abs(pos.z) > 1.125) {
continue;
}
float px = (float)(p.pos.x - 0.5f - Camera.camera.x);
float py = (float)(p.pos.y);
float pz = (float)(p.pos.z - 0.5f - Camera.camera.y);
float k = Model.OFFSET;
float tsx = p.tex.sx+k;
float tex = p.tex.ex-k;
float tsy = p.tex.sy+k;
float tey = p.tex.ey-k;
float tz = p.tex.z;
float g = (float)p.getFade();
float s = (float)p.size / 2;
float f = p.flags;
float a_si = p.animationSize;
float a_sp = p.animationSpeed;
float a = -s, b = s, c = 0, d = 0;
if(p.isFlat()) {
c = a;
d = b;
a = 0;
b = 0;
}
/*
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aTex;
layout (location = 2) in vec2 aTexY;
layout (location = 3) in vec3 aOffset;
layout (location = 4) in vec2 aAnimate;
layout (location = 5) in vec3 aFlags;
*/
float[] verticies = {
-s, a, c, tex, tsy, tz, tsy, tey, px, py, pz, a_si, a_sp, g, 0, f,
-s, b, d, tex, tey, tz, tsy, tey, px, py, pz, a_si, a_sp, g, 0, f,
+s, b, d, tsx, tey, tz, tsy, tey, px, py, pz, a_si, a_sp, g, 0, f,
+s, a, c, tsx, tsy, tz, tsy, tey, px, py, pz, a_si, a_sp, g, 0, f,
};
for(int j=0;j<verticies.length;j++) {
particle_pool.put(upto+j, verticies[j]);
}
upto += verticies.length;
}
}
IModel model = e.getModel();
if(model == null) {
continue;
}
Vec3d pos = e.getPos();
// Don't try to render anything if the model is empty
if(model.getSize() == 0) {
continue;
}
// Render the model
model.setModel(Matrix4.translate(
pos.x - Camera.camera.x - 0.5, pos.y,
pos.z - Camera.camera.y - 0.5));
model.render();
}
return upto;
}
public int render(Camera camera, FloatBuffer particle_pool, int upto)
{
if(model == null || this.render_dirty)
{
this.render_dirty = false;
int verticies_size = 0;
int indicies_size = 0;
int index_offset = 0;
for(int i=0;i<CHUNK_INDEX;i++) {
TileState bt = getBackTile(i);
TileState ft = getFrontTile(i);
verticies_size += bt.tile.getModel(bt.meta).getSize();
verticies_size += ft.tile.getModel(ft.meta).getSize();
indicies_size += bt.tile.getModel(bt.meta).getIndexSize();
indicies_size += ft.tile.getModel(ft.meta).getIndexSize();
}
int[] indicies = new int[indicies_size];
float[] verticies = new float[verticies_size * Model.SIZE];
TextureRef3D[] textures = new TextureRef3D[verticies_size];
int upto_v = 0;
int upto_i = 0;
for(int i=0;i<CHUNK_INDEX;i++)
{
Vec2i pos = Vec2i.fromId(CHUNK_SIZE, i);
for(TileState state : new TileState[] {getFrontTile(pos), getBackTile(pos)})
{
Model model = state.tile.getModel(state.meta);
TextureRef3D[] textures2 = model.getTextures();
float[] verticies2 = model.getVerticies();
int[] indicies2 = model.getIndicies();
for(int j=0;j<model.getSize();j++)
{
textures[upto_v] = textures2[j];
for(int k=0;k<Model.SIZE;k++)
{
switch(k)
{
case 8:
verticies[upto_v * Model.SIZE + k ] = pos.x;
continue;
case 10:
verticies[upto_v * Model.SIZE + k ] = pos.y;
continue;
default:
verticies[upto_v * Model.SIZE + k ] = verticies2[j * Model.SIZE + k ];
continue;
}
}
upto_v += 1;
}
for(int j=0;j<indicies2.length;j++) {
indicies[j + upto_i] = indicies2[j] + index_offset;
}
upto_i += indicies2.length;
index_offset += model.getSize();
}
}
if(model != null) {
model.free();
}
model = new ModelChunk(verticies, indicies, textures, verticies_size);
}
// Render all the tiles in the chunk as a block
model.setModel(Matrix4.translate(c_pos.x * 16 - Camera.camera.x, 0, c_pos.y * 16 - Camera.camera.y));
model.render();
return renderEntities(entities.toArray(), particle_pool, upto);
}
public void spawnEntity(Entity e) {
e.chunk = this;
entities.add(e);
}
public void tickEntities()
{
// Loop over every entitiy
for(int i=0;i<entities.size();i++)
{
// Get the entity and tick it
Entity e = entities.get(i);
e.tick(this, layer);
}
}
public void checkEntities()
{
// Loop over every entity
for(int i=0;i<entities.size();i++)
{
// Get the entitiy
Entity e = entities.get(i);
// is this entity alive
if(e instanceof EntityAlive)
{
// Get the alive entity
EntityAlive ea = (EntityAlive)e;
// Kill the entity if it should be dead and continue to the next loop iteration
if(ea.getHealth() < 0) {
ea.onDeath(layer);
killEntity(e);
continue;
}
}
// Has the entity left the chunk
int cx = c_pos.x * CHUNK_SIZE.mx;
int cy = c_pos.y * CHUNK_SIZE.my;
double px = e.getPos().x;
double py = e.getPos().z;
if(px > cx + CHUNK_SIZE.mx || px < cx || py > cy + CHUNK_SIZE.my || py < cy)
{
// Process the entity by layer and remove the entity from the array
layer.spawnEntity(e);
entities.remove(i);
i -= 1;
}
}
}
public void createTileEntity(Vec2i pos, TileEntity te)
{
TileEntity te_old = getTileEntity(pos);
// Kill the old tile entity if it exists
if(te_old != null) {
te_old.kill();
}
te.setPos(new Vec3d(pos.x, 0, pos.y));
spawnEntity(te);
}
public void destroyTileEntity(Vec2i pos)
{
TileEntity te = getTileEntity(pos);
if(te != null) {
te.kill();
}
}
public TileEntity getTileEntity(Vec2i pos)
{
for(Entity e : entities)
{
if(e instanceof TileEntity && e.getPos().xz().toInt().equal(pos)) {
return (TileEntity)e;
}
}
return null;
}
public void setBackTile(TileState tile, Vec2i pos)
{
// Get the id
Vec2i cpos = new Vec2i(0, 0);
cpos.x = MathHelpers.mod(pos.x, CHUNK_SIZE.mx);
cpos.y = MathHelpers.mod(pos.y, CHUNK_SIZE.my);
int id = cpos.getId(CHUNK_SIZE);
setBackTile(tile, id);
}
public void setBackTile(TileState tile, int id)
{
Vec2i pos = Vec2i.fromId(CHUNK_SIZE, id).add(c_pos.multiply(16));
tiles_back[id].onDestroy(layer, this, new TileState(tiles_back[id], tiles_back_meta[id]), tile, pos);
// Set the back tile
tiles_back[id] = tile.tile;
tiles_back_meta[id] = tile.meta;
setDirty();
tile.tile.onGenerate(layer, this, tile, pos);
}
public void setFrontTile(TileState tile, Vec2i pos)
{
// Get the id
Vec2i cpos = new Vec2i(0, 0);
cpos.x = MathHelpers.mod(pos.x, CHUNK_SIZE.mx);
cpos.y = MathHelpers.mod(pos.y, CHUNK_SIZE.my);
int id = cpos.getId(CHUNK_SIZE);
setFrontTile(tile, id);
}
public void setFrontTile(TileState tile, int id)
{
Vec2i pos = Vec2i.fromId(CHUNK_SIZE, id).add(c_pos.multiply(16));
tiles_front[id].onDestroy(layer, this, new TileState(tiles_front[id], tiles_front_meta[id]), tile, pos);
// Set the front tile
this.tiles_front[id] = tile.tile;
this.tiles_front_meta[id] = tile.meta;
setDirty();
tile.tile.onGenerate(layer, this, tile, pos);
}
public TileState getBackTile(Vec2i pos)
{
// Get the id
Vec2i cpos = new Vec2i(0, 0);
cpos.x = MathHelpers.mod(pos.x, CHUNK_SIZE.mx);
cpos.y = MathHelpers.mod(pos.y, CHUNK_SIZE.my);
int id = cpos.getId(CHUNK_SIZE);
return getBackTile(id);
}
public TileState getBackTile(int id)
{
// Send back the back tile
TileState ts = new TileState(this.tiles_back[id], this.tiles_back_meta[id]);
ts.light = getLightLevel(id);
return ts;
}
public TileState getFrontTile(Vec2i pos)
{
// Get the id
Vec2i cpos = new Vec2i(0, 0);
cpos.x = MathHelpers.mod(pos.x, CHUNK_SIZE.mx);
cpos.y = MathHelpers.mod(pos.y, CHUNK_SIZE.my);
int id = cpos.getId(CHUNK_SIZE);
return getFrontTile(id);
}
public TileState getFrontTile(int id)
{
// Send back the front tile
TileState ts = new TileState(this.tiles_front[id], this.tiles_front_meta[id]);
ts.light = getLightLevel(id);
return ts;
}
public void breakBackTile(Vec2i pos)
{
TileState ts = getBackTile(pos);
setDirty();
if(!ts.tile.unbreakable) {
setBackTile(layer.layergen.getTileDestroyed(layer, pos), pos);
spawnEntity(new ParticleBreak(new Vec3d(pos.x + 0.5, 0, pos.y + 0.5), new Vec3d(0, 0, 0), ts.tile.getModel(ts.meta)));
}
}
public void breakFrontTile(Vec2i pos)
{
TileState ts = getFrontTile(pos);
setDirty();
if(!ts.tile.unbreakable) {
setFrontTile(Tiles.VOID.getDefaultState(), pos);
spawnEntity(new ParticleBreak(new Vec3d(pos.x + 0.5, 0, pos.y + 0.5),
new Vec3d(0, 0, 0), ts.tile.getModel(ts.meta)));
}
}
public double getLightLevel(int id) {
return tiles_lighting[id] / (double)Byte.MAX_VALUE;
}
public double getLightLevel(Vec2i pos)
{
// Get the id
Vec2i cpos = new Vec2i(0, 0);
cpos.x = MathHelpers.mod(pos.x, CHUNK_SIZE.mx);
cpos.y = MathHelpers.mod(pos.y, CHUNK_SIZE.my);
int id = cpos.getId(CHUNK_SIZE);
return getLightLevel(id);
}
public void setLightLevel(double light, int id) {
this.tiles_lighting[id] = (byte)(light * Byte.MAX_VALUE);
}
public void setLightLevel(double light, Vec2i pos)
{
// Get the id
Vec2i cpos = new Vec2i(0, 0);
cpos.x = MathHelpers.mod(pos.x, CHUNK_SIZE.mx);
cpos.y = MathHelpers.mod(pos.y, CHUNK_SIZE.my);
int id = cpos.getId(CHUNK_SIZE);
setLightLevel(light, id);
}
public void killEntity(Entity e)
{
if(e instanceof EntityKillWithParticles) {
((EntityKillWithParticles)e).killWithParticles();
}
entities.remove(e);
}
public ArrayList<Entity> getNearbyEntities(Vec2d pos, double distance)
{
// Get the list of entities to send back
ArrayList<Entity> nearby_entities = new ArrayList<Entity>();
// Loop over the entities
for(Entity e : entities)
{
Vec2d epos = e.getPos().xz();
if(
epos.x + distance > pos.x &&
epos.x - distance < pos.x &&
epos.y + distance > pos.y &&
epos.y - distance < pos.y
) {
if(pos.squareDistance(epos) < distance) {
nearby_entities.add(e);
}
}
}
// Send back the entities
return nearby_entities;
}
public void tickRandomly()
{
// Get a random tile
int id = RandomHelpers.randrange(rand, CHUNK_INDEX);
Vec2i pos = Vec2i.fromId(CHUNK_SIZE, id);
pos.x += c_pos.x * 16;
pos.y += c_pos.y * 16;
// Randomly select the front and back tiles
TileState ts;
if(rand.nextBoolean()) {
ts = getBackTile(id);
}
else {
ts = getFrontTile(id);
}
// Tick the tile
ts.tile.tickRandomly(layer, this, ts, pos);
}
public void free()
{
if(this.model != null) {
this.model.free();
this.model = null;
}
}
}