Added tiles. Not yet optimized.

This commit is contained in:
OLEGSHA 2020-08-27 00:24:11 +03:00
parent 21a30e8b97
commit 60fbfa9578
13 changed files with 568 additions and 56 deletions

View File

@ -30,6 +30,7 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.renders.BlockRenders;
import ru.windcorp.progressia.client.world.renders.TileRenders;
import ru.windcorp.progressia.common.resource.ResourceManager;
public class ClientProxy implements Proxy {
@ -47,6 +48,7 @@ public class ClientProxy implements Proxy {
}
BlockRenders.registerTest();
TileRenders.registerTest();
Atlases.loadAllAtlases();
GUI.addBottomLayer(new LayerWorld());

View File

@ -11,13 +11,13 @@ class BlockFaceVectors {
private static BlockFaceVectors createInner(BlockFaceVectors outer) {
ImmutableMap.Builder<BlockFace, Vec3> originBuilder =
ImmutableMap.builderWithExpectedSize(count());
ImmutableMap.builder();
ImmutableMap.Builder<BlockFace, Vec3> widthBuilder =
ImmutableMap.builderWithExpectedSize(count());
ImmutableMap.builder();
ImmutableMap.Builder<BlockFace, Vec3> heightBuilder =
ImmutableMap.builderWithExpectedSize(count());
ImmutableMap.builder();
for (BlockFace face : getFaces()) {
Vec3 width = outer.getWidth(face);
@ -43,7 +43,7 @@ class BlockFaceVectors {
static {
OUTER = new BlockFaceVectors(
ImmutableMap.<BlockFace, Vec3>builderWithExpectedSize(count())
ImmutableMap.<BlockFace, Vec3>builder()
.put(TOP, new Vec3(-0.5f, +0.5f, +0.5f))
.put(BOTTOM, new Vec3(-0.5f, -0.5f, -0.5f))
@ -54,7 +54,7 @@ class BlockFaceVectors {
.build(),
ImmutableMap.<BlockFace, Vec3>builderWithExpectedSize(count())
ImmutableMap.<BlockFace, Vec3>builder()
.put(TOP, new Vec3( 0, -1, 0))
.put(BOTTOM, new Vec3( 0, +1, 0))
@ -65,7 +65,7 @@ class BlockFaceVectors {
.build(),
ImmutableMap.<BlockFace, Vec3>builderWithExpectedSize(count())
ImmutableMap.<BlockFace, Vec3>builder()
.put(TOP, new Vec3(+1, 0, 0))
.put(BOTTOM, new Vec3(+1, 0, 0))

View File

@ -18,9 +18,11 @@
package ru.windcorp.progressia.client.world;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.model.Model;
import ru.windcorp.progressia.client.graphics.model.Shape;
@ -31,9 +33,14 @@ import ru.windcorp.progressia.client.graphics.model.StaticModel.Builder;
import ru.windcorp.progressia.client.world.renders.BlockRender;
import ru.windcorp.progressia.client.world.renders.BlockRenderNone;
import ru.windcorp.progressia.client.world.renders.BlockRenders;
import ru.windcorp.progressia.client.world.renders.TileRender;
import ru.windcorp.progressia.client.world.renders.TileRenders;
import ru.windcorp.progressia.client.world.renders.cro.ChunkRenderOptimizer;
import ru.windcorp.progressia.client.world.renders.cro.ChunkRenderOptimizerSupplier;
import ru.windcorp.progressia.client.world.renders.cro.ChunkRenderOptimizers;
import ru.windcorp.progressia.common.block.BlockFace;
import ru.windcorp.progressia.common.block.TileData;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.ChunkData;
public class ChunkRender {
@ -101,23 +108,8 @@ public class ChunkRender {
for (int z = 0; z < ChunkData.BLOCKS_PER_CHUNK; ++z) {
cursor.set(x, y, z);
BlockRender block = getBlock(cursor);
if (block instanceof BlockRenderNone) {
continue;
}
forwardToOptimizers(block, x, y, z, optimizers);
if (!block.needsOwnRenderable()) {
continue;
}
if (tryToCreateRenderable(block, x, y, z, builder)) {
continue;
}
addRenderAsRenderable(block, x, y, z, builder);
buildBlock(cursor, optimizers, builder);
buildBlockTiles(cursor, optimizers, builder);
}
}
}
@ -133,14 +125,41 @@ public class ChunkRender {
needsUpdate = false;
}
private void forwardToOptimizers(
private void buildBlock(
Vec3i cursor,
Collection<ChunkRenderOptimizer> optimizers,
Builder builder
) {
BlockRender block = getBlock(cursor);
int x = cursor.x;
int y = cursor.y;
int z = cursor.z;
if (block instanceof BlockRenderNone) {
return;
}
forwardBlockToOptimizers(block, x, y, z, optimizers);
if (!block.needsOwnRenderable()) {
return;
}
if (tryToCreateBlockRenderable(block, x, y, z, builder)) {
return;
}
addBlockRenderAsRenderable(block, x, y, z, builder);
}
private void forwardBlockToOptimizers(
BlockRender block, int x, int y, int z,
Collection<ChunkRenderOptimizer> optimizers
) {
optimizers.forEach(bro -> bro.processBlock(block, x, y, z));
}
private boolean tryToCreateRenderable(
private boolean tryToCreateBlockRenderable(
BlockRender block, int x, int y, int z,
Builder builder
) {
@ -154,7 +173,7 @@ public class ChunkRender {
return true;
}
private void addRenderAsRenderable(
private void addBlockRenderAsRenderable(
BlockRender block, int x, int y, int z,
Builder builder
) {
@ -164,4 +183,66 @@ public class ChunkRender {
);
}
private void buildBlockTiles(
Vec3i cursor,
Collection<ChunkRenderOptimizer> optimizers,
Builder builder
) {
for (BlockFace face : BlockFace.getFaces()) {
buildFaceTiles(cursor, face, optimizers, builder);
}
}
private void buildFaceTiles(
Vec3i cursor, BlockFace face,
Collection<ChunkRenderOptimizer> optimizers,
Builder builder
) {
List<TileData> tiles = getData().getTilesOrNull(cursor, face);
if (tiles == null) {
return;
}
for (int layer = 0; layer < tiles.size(); ++layer) {
if (tiles.get(layer) == null) {
System.out.println(tiles.get(layer).getId());
}
buildTile(
cursor, face,
TileRenders.get(tiles.get(layer).getId()),
layer,
optimizers, builder
);
}
}
private void buildTile(
Vec3i cursor, BlockFace face,
TileRender tile,
int layer,
Collection<ChunkRenderOptimizer> optimizers,
Builder builder
) {
// TODO implement
Vec3 pos = Vectors.grab3().set(cursor.x, cursor.y, cursor.z);
Vec3 offset = Vectors.grab3().set(
face.getVector().x, face.getVector().y, face.getVector().z
);
pos.add(offset.mul(1f / 64));
builder.addPart(
tile.createRenderable(face),
new Mat4().identity().translate(pos)
);
Vectors.release(pos);
Vectors.release(offset);
}
}

View File

@ -0,0 +1,33 @@
package ru.windcorp.progressia.client.world.renders;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.model.WorldRenderable;
import ru.windcorp.progressia.client.world.renders.cro.ChunkRenderOptimizer;
import ru.windcorp.progressia.common.block.BlockFace;
import ru.windcorp.progressia.common.util.Namespaced;
public class TileRender extends Namespaced {
public TileRender(String namespace, String name) {
super(namespace, name);
}
public void render(ShapeRenderHelper renderer, BlockFace face) {
throw new UnsupportedOperationException(
"TileRender.render() not implemented in " + this
);
}
public WorldRenderable createRenderable(BlockFace face) {
return null;
}
public boolean canBeOptimized(ChunkRenderOptimizer optimizer) {
return true;
}
public boolean needsOwnRenderable() {
return true;
}
}

View File

@ -0,0 +1,47 @@
package ru.windcorp.progressia.client.world.renders;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
import ru.windcorp.progressia.client.graphics.model.WorldRenderable;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.common.block.BlockFace;
import ru.windcorp.progressia.common.util.Vectors;
public class TileRenderSimple extends TileRender {
private final Texture texture;
public TileRenderSimple(String namespace, String name, Texture texture) {
super(namespace, name);
this.texture = texture;
}
public Texture getTexture() {
return texture;
}
@Override
public WorldRenderable createRenderable(BlockFace face) {
ShapeRenderProgram program = WorldRenderProgram.getDefault();
Vec3 color = Vectors.grab3().set(1, 1, 1);
Vec3 center = Vectors.grab3().set(0, 0, 0);
try {
return new Shape(
Usage.STATIC, WorldRenderProgram.getDefault(),
Faces.createBlockFace(
program, texture, color, center, face, false
)
);
} finally {
Vectors.release(color);
Vectors.release(center);
}
}
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Progressia
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.progressia.client.world.renders;
import java.util.HashMap;
import java.util.Map;
import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.resource.ResourceManager;
public class TileRenders {
private static final Map<String, TileRender> TILE_RENDERS =
new HashMap<>();
private static final AtlasGroup TILES_ATLAS_GROUP =
new AtlasGroup("Tiles", 1 << 12);
private TileRenders() {}
public static void registerTest() {
register(new TileRenderSimple("Test", "Stones", getTexture("stones")));
}
public static TileRender get(String name) {
return TILE_RENDERS.get(name);
}
public static void register(TileRender tileRender) {
TILE_RENDERS.put(tileRender.getId(), tileRender);
}
public static Texture getTexture(String name) {
return new SimpleTexture(
Atlases.getSprite(
ResourceManager.getTextureResource("tiles/" + name),
TILES_ATLAS_GROUP
)
);
}
}

View File

@ -19,6 +19,8 @@ package ru.windcorp.progressia.common.block;
import com.google.common.collect.ImmutableList;
import glm.vec._3.i.Vec3i;
public final class BlockFace extends BlockRelation {
public static final BlockFace
@ -35,6 +37,8 @@ public final class BlockFace extends BlockRelation {
private static final ImmutableList<BlockFace> PRIMARY_FACES =
ImmutableList.of(TOP, NORTH, WEST);
public static final int BLOCK_FACE_COUNT = ALL_FACES.size();
public static ImmutableList<BlockFace> getFaces() {
return ALL_FACES;
}
@ -43,10 +47,6 @@ public final class BlockFace extends BlockRelation {
return PRIMARY_FACES;
}
public static int count() {
return ALL_FACES.size();
}
static {
link(TOP, BOTTOM);
link(NORTH, SOUTH);
@ -58,11 +58,15 @@ public final class BlockFace extends BlockRelation {
b.counterFace = a;
}
private static int nextId = 0;
private final int id;
private BlockFace counterFace;
private final boolean isPrimary;
private BlockFace(int x, int y, int z, boolean isPrimary) {
super(x, y, z);
this.id = nextId++;
this.isPrimary = isPrimary;
}
@ -75,9 +79,42 @@ public final class BlockFace extends BlockRelation {
else return counterFace;
}
public BlockFace getPrimaryAndMoveCursor(Vec3i cursor) {
if (isPrimary) return this;
cursor.add(getVector());
return counterFace;
}
public BlockFace getSecondary() {
if (isPrimary) return counterFace;
else return this;
}
public BlockFace getSecondaryAndMoveCursor(Vec3i cursor) {
if (!isPrimary) return this;
cursor.add(getVector());
return counterFace;
}
public int getId() {
return id;
}
@Override
public float getEuclideanDistance() {
return 1.0f;
}
@Override
public int getChebyshevDistance() {
return 1;
}
@Override
public int getManhattanDistance() {
return 1;
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Progressia
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.progressia.common.block;
import ru.windcorp.progressia.common.util.Namespaced;
public class TileData extends Namespaced {
public TileData(String namespace, String name) {
super(namespace, name);
}
}

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Progressia
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.progressia.common.block;
import java.util.HashMap;
import java.util.Map;
public class TileDataRegistry {
private static final Map<String, TileData> REGISTRY = new HashMap<>();
static {
register(new TileData("Test", "Stones"));
}
public static TileData get(String name) {
return REGISTRY.get(name);
}
public static void register(TileData tileData) {
REGISTRY.put(tileData.getId(), tileData);
}
}

View File

@ -0,0 +1,71 @@
package ru.windcorp.progressia.common.util;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.RandomAccess;
import com.google.common.collect.ForwardingList;
public class SizeLimitedList<E> extends ForwardingList<E> {
private static final class RandomAccessSizeLimitedList<E>
extends SizeLimitedList<E>
implements RandomAccess {
protected RandomAccessSizeLimitedList(List<E> parent, int maxSize) {
super(parent, maxSize);
}
}
public static <E> List<E> wrap(List<E> list, int maxSize) {
if (list instanceof RandomAccess) {
return new RandomAccessSizeLimitedList<>(list, maxSize);
} else {
return new SizeLimitedList<>(list, maxSize);
}
}
private final List<E> delegate;
private final int maxSize;
protected SizeLimitedList(List<E> parent, int maxSize) {
this.delegate = Objects.requireNonNull(parent, "parent");
this.maxSize = maxSize;
}
@Override
public boolean addAll(Collection<? extends E> collection) {
return standardAddAll(collection);
}
@Override
public boolean addAll(int index, Collection<? extends E> elements) {
return standardAddAll(index, elements);
}
public boolean add(E e) {
checkMaxSize();
return delegate().add(e);
}
@Override
public void add(int index, final E e) {
checkMaxSize();
delegate().add(index, e);
}
private void checkMaxSize() {
if (size() >= maxSize) {
throw new UnsupportedOperationException(
"Maximum size " + maxSize + " reached"
);
}
}
@Override
protected List<E> delegate() {
return delegate;
}
}

View File

@ -17,9 +17,21 @@
*******************************************************************************/
package ru.windcorp.progressia.common.world;
import static ru.windcorp.progressia.common.block.BlockFace.*;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Lists;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.block.BlockData;
import ru.windcorp.progressia.common.block.BlockDataRegistry;
import ru.windcorp.progressia.common.block.BlockFace;
import ru.windcorp.progressia.common.block.TileData;
import ru.windcorp.progressia.common.block.TileDataRegistry;
import ru.windcorp.progressia.common.util.SizeLimitedList;
import ru.windcorp.progressia.common.util.Vectors;
public class ChunkData {
@ -27,23 +39,33 @@ public class ChunkData {
public static final int TILES_PER_FACE = 8;
private final Vec3i position = new Vec3i();
private final WorldData world;
private final BlockData[] blocks = new BlockData[
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK
];
private final BlockData grass = BlockDataRegistry.get("Test:Grass");
private final BlockData dirt = BlockDataRegistry.get("Test:Dirt");
private final BlockData stone = BlockDataRegistry.get("Test:Stone");
private final BlockData air = BlockDataRegistry.get("Test:Air");
@SuppressWarnings("unchecked")
private final List<TileData>[] tiles = (List<TileData>[]) new List<?>[
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK *
BLOCK_FACE_COUNT
];
public ChunkData(int x, int y, int z) {
public ChunkData(int x, int y, int z, WorldData world) {
this.position.set(x, y, z);
this.world = world;
tmp_generate();
}
private void tmp_generate() {
BlockData grass = BlockDataRegistry.get("Test:Grass");
BlockData dirt = BlockDataRegistry.get("Test:Dirt");
BlockData stone = BlockDataRegistry.get("Test:Stone");
BlockData air = BlockDataRegistry.get("Test:Air");
TileData stones = TileDataRegistry.get("Test:Stones");
Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2);
Vec3i pos = new Vec3i();
@ -74,46 +96,134 @@ public class ChunkData {
for (pos.z = BLOCKS_PER_CHUNK - 1; pos.z >= 0 && getBlock(pos) == air; --pos.z);
setBlock(pos, grass);
int hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129;
if (hash % 5 == 0) {
getTiles(pos, BlockFace.TOP).add(stones);
}
}
}
}
public BlockData getBlock(Vec3i posInChunk) {
if (!isInBounds(posInChunk)) {
throw new IllegalArgumentException(
"Coordinates " + str(posInChunk) + " "
+ "are not legal chunk coordinates"
);
}
return blocks[getBlockIndex(posInChunk)];
}
public void setBlock(Vec3i posInChunk, BlockData block) {
if (!isInBounds(posInChunk)) {
throw new IllegalArgumentException(
"Coordinates " + str(posInChunk) + " "
+ "are not legal chunk coordinates"
);
}
blocks[getBlockIndex(posInChunk)] = block;
}
private boolean isInBounds(Vec3i posInChunk) {
return
posInChunk.x >= 0 && posInChunk.x < BLOCKS_PER_CHUNK &&
posInChunk.y >= 0 && posInChunk.y < BLOCKS_PER_CHUNK &&
posInChunk.z >= 0 && posInChunk.z < BLOCKS_PER_CHUNK;
public List<TileData> getTilesOrNull(Vec3i blockInChunk, BlockFace face) {
return tiles[getTileIndex(blockInChunk, face)];
}
private int getBlockIndex(Vec3i posInChunk) {
/**
* Internal use only. Modify a list returned by
* {@link #getTiles(Vec3i, BlockFace)} or
* {@link #getTilesOrNull(Vec3i, BlockFace)}
* to change tiles.
*/
protected void setTiles(
Vec3i blockInChunk, BlockFace face,
List<TileData> tiles
) {
this.tiles[getTileIndex(blockInChunk, face)] = tiles;
}
public boolean hasTiles(Vec3i blockInChunk, BlockFace face) {
return getTilesOrNull(blockInChunk, face) != null;
}
public List<TileData> getTiles(Vec3i blockInChunk, BlockFace face) {
int index = getTileIndex(blockInChunk, face);
if (tiles[index] == null) {
createTileContainer(blockInChunk, face);
}
return tiles[index];
}
private void createTileContainer(Vec3i blockInChunk, BlockFace face) {
if (isBorder(blockInChunk, face)) {
createBorderTileContainer(blockInChunk, face);
} else {
createNormalTileContainer(blockInChunk, face);
}
}
private void createNormalTileContainer(Vec3i blockInChunk, BlockFace face) {
List<TileData> primaryList =
SizeLimitedList.wrap(
new ArrayList<>(TILES_PER_FACE), TILES_PER_FACE
);
List<TileData> secondaryList = Lists.reverse(primaryList);
Vec3i cursor = Vectors.grab3i()
.set(blockInChunk.x, blockInChunk.y, blockInChunk.z);
face = face.getPrimaryAndMoveCursor(cursor);
setTiles(cursor, face, primaryList);
face = face.getSecondaryAndMoveCursor(cursor);
setTiles(cursor, face, secondaryList);
}
private void createBorderTileContainer(Vec3i blockInChunk, BlockFace face) {
// TODO cooperate with neighbours
setTiles(
blockInChunk, face,
SizeLimitedList.wrap(
new ArrayList<>(TILES_PER_FACE), TILES_PER_FACE
)
);
}
private static int getBlockIndex(Vec3i posInChunk) {
checkLocalCoordinates(posInChunk);
return
posInChunk.z * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK +
posInChunk.y * BLOCKS_PER_CHUNK +
posInChunk.x;
}
private static int getTileIndex(Vec3i posInChunk, BlockFace face) {
return
getBlockIndex(posInChunk) * BLOCK_FACE_COUNT +
face.getId();
}
private static void checkLocalCoordinates(Vec3i posInChunk) {
if (!isInBounds(posInChunk)) {
throw new IllegalArgumentException(
"Coordinates " + str(posInChunk) + " "
+ "are not legal chunk coordinates"
);
}
}
private static boolean isInBounds(Vec3i posInChunk) {
return
posInChunk.x >= 0 && posInChunk.x < BLOCKS_PER_CHUNK &&
posInChunk.y >= 0 && posInChunk.y < BLOCKS_PER_CHUNK &&
posInChunk.z >= 0 && posInChunk.z < BLOCKS_PER_CHUNK;
}
private boolean isBorder(Vec3i blockInChunk, BlockFace face) {
final int min = 0, max = BLOCKS_PER_CHUNK - 1;
return
(blockInChunk.x == min && face == SOUTH ) ||
(blockInChunk.x == max && face == NORTH ) ||
(blockInChunk.y == min && face == WEST ) ||
(blockInChunk.y == max && face == EAST ) ||
(blockInChunk.z == min && face == BOTTOM) ||
(blockInChunk.z == max && face == TOP );
}
public int getX() {
return position.x;
}
@ -130,6 +240,10 @@ public class ChunkData {
return position;
}
public WorldData getWorld() {
return world;
}
private static String str(Vec3i v) {
return "(" + v.x + "; " + v.y + "; " + v.z + ")";
}

View File

@ -34,7 +34,7 @@ public class WorldData {
for (int x = -(size / 2); x <= (size / 2); ++x) {
for (int y = -(size / 2); y <= (size / 2); ++y) {
chunks.put(CoordinatePacker.pack3IntsIntoLong(x, y, 0), new ChunkData(x, y, 0));
chunks.put(CoordinatePacker.pack3IntsIntoLong(x, y, 0), new ChunkData(x, y, 0, this));
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B