diff --git a/src/main/java/com/emamaker/voxeltest/intervaltrees/data/Interval.java b/src/main/java/com/emamaker/voxeltest/intervaltrees/data/Interval.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/emamaker/voxeltest/intervaltrees/data/IntervalMap.java b/src/main/java/com/emamaker/voxeltest/intervaltrees/data/IntervalMap.java new file mode 100644 index 0000000..2dd0eda --- /dev/null +++ b/src/main/java/com/emamaker/voxeltest/intervaltrees/data/IntervalMap.java @@ -0,0 +1,74 @@ +package com.emamaker.voxeltest.intervaltrees.data; + +import java.util.ArrayList; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +public class IntervalMap { + public TreeMap treemap = new TreeMap<>(); + int terminator = Integer.MAX_VALUE; + ArrayList toDelete = new ArrayList<>(); + public void insert(int start, int end, V value){ + // if(c.compare(start, end) > 0 || c.compare(start, end) == 0)return; + if(start >= end) return; + + // take the last key before end + Map.Entry upper_bound = treemap.lowerEntry(end); //'higherEntry' makes sure that there's no equal case, otherwise the node could be removed as soon as it is inserted + + Map.Entry upper_entry = treemap.higherEntry(end); + Map.Entry lower_entry = treemap.lowerEntry(start); + + // insert the start key. Replaces whatever value is already there. Do not place if the lowerEntry is of the same value + if(lower_entry == null || ( lower_entry.getValue() != null && !lower_entry.getValue().equals(value))) treemap.put(start, value); + + if(upper_entry == null){ + if(upper_bound != null ) treemap.put(end+1, upper_bound.getKey() > start ? upper_bound.getValue():null); + else treemap.put(end+1, null); + }else{ + if(upper_entry.getValue() != null && !upper_entry.getValue().equals(value) && upper_entry.getKey() > end + 1) treemap.put(end+1, upper_bound.getValue()); + else treemap.put(end+1, upper_bound.getValue()); + } + for(Integer k : treemap.subMap(start, lower_entry != null && value == lower_entry.getValue(), end, true).keySet()){ + treemap.remove(k); + } + } + + public void insertTerminator(int start){ + treemap.put(start, null); + terminator=start; + } + + public Integer begin(){ + return treemap.firstKey(); + } + + public Integer end(){ + return treemap.lastKey(); + } + Map.Entry e; + public V valueAtKey(Integer key){ + return treemap.floorEntry(key).getValue(); + } + + public SortedMap valuesBetween(Integer start, boolean startInclusive, Integer end, boolean endInclusive){ + return treemap.subMap(start, startInclusive, end, endInclusive); + } + public SortedMap valuesBetween(Integer start , Integer end){ + return treemap.subMap(start, true, end, false); + } + + V oldValue = null; + Integer oldK = null; + public void print() { + System.out.println("-------"); + for (Integer k : treemap.keySet()) { + if (oldK != null) { + System.out.println("[" + oldK + ", " + k + ") --> " + oldValue); + } + oldK = k; + oldValue = treemap.get(k); + } + oldK = null; + } +} diff --git a/src/main/java/com/emamaker/voxeltest/intervaltrees/data/IntervalTree.java b/src/main/java/com/emamaker/voxeltest/intervaltrees/data/IntervalTree.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/emamaker/voxeltest/intervaltrees/world/Chunk.java b/src/main/java/com/emamaker/voxeltest/intervaltrees/world/Chunk.java index e2d1a83..ae6ef11 100644 --- a/src/main/java/com/emamaker/voxeltest/intervaltrees/world/Chunk.java +++ b/src/main/java/com/emamaker/voxeltest/intervaltrees/world/Chunk.java @@ -1,33 +1,37 @@ package com.emamaker.voxeltest.intervaltrees.world; import java.util.ArrayDeque; -import java.util.Arrays; import java.util.Queue; +import java.util.TreeMap; import com.emamaker.voxeltest.intervaltrees.data.Interval; +import com.emamaker.voxeltest.intervaltrees.data.IntervalMap; import com.emamaker.voxeltest.intervaltrees.data.IntervalTree; import com.emamaker.voxeltest.intervaltrees.utils.Config; import com.emamaker.voxeltest.intervaltrees.utils.Utils; import com.emamaker.voxeltest.intervaltrees.world.blocks.Blocks; -import com.jme3.material.Material; import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; import com.jme3.scene.Node; public class Chunk { - Vector3f pos; - public IntervalTree blocks = new IntervalTree(); + public Vector3f pos; + public IntervalMap blocks = new IntervalMap<>(); public Node chunkNode = new Node(); + public boolean loaded = false; public Chunk() { this(Vector3f.ZERO); } public Chunk(Vector3f pos_) { + this.pos = pos_; + chunkNode.setLocalTranslation(pos.mult(Config.CHUNK_SIZE)); + // I still have to decided if this is necessary. With an empty tree this // takes O(1) - blocks.insertValue(Blocks.AIR, new Interval(0, Config.CHUNK_3DCOORD_MAX_INDEX)); + //blocks.insertTerminator(Config.CHUNK_3DCOORD_MAX_INDEX); + blocks.insert(0, Config.CHUNK_3DCOORD_MAX_INDEX, Blocks.AIR); } public static int coord3DTo1D(int x, int y, int z) { @@ -39,34 +43,44 @@ public class Chunk { } public Blocks getBlock(int x, int y, int z) { - return blocks.getSingleValue(Chunk.coord3DTo1D(x, y, z)); + return blocks.valueAtKey(Chunk.coord3DTo1D(x, y, z)); } public void generatePlane() { // blocks.print(); - // this.setBlocks(Blocks.STONE, 0, - // Config.CHUNK_3DCOORD_MAX_INDEX)); - for (int i = 0; i < Config.CHUNK_SIZE; i++) this.setBlocks(Blocks.STONE, coord3DTo1D(0, 0, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 0, i)); - for (int i = 0; i < Config.CHUNK_SIZE; i++) this.setBlocks(Blocks.DIRT, coord3DTo1D(0, 1, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 1, i)); - for (int i = 0; i < Config.CHUNK_SIZE; i++) this.setBlocks(Blocks.GRASS, coord3DTo1D(0, 2, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 2, i)); + for (int i = 0; i < Config.CHUNK_SIZE; i++) + this.setBlocks(Blocks.STONE, coord3DTo1D(0, 0, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 0, i)); + for (int i = 0; i < Config.CHUNK_SIZE; i++) + this.setBlocks(Blocks.DIRT, coord3DTo1D(0, 1, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 1, i)); + for (int i = 0; i < Config.CHUNK_SIZE; i++) + this.setBlocks(Blocks.GRASS, coord3DTo1D(0, 2, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 2, i)); - for (int i = 0; i < Config.CHUNK_SIZE; i++) this.setBlocks(Blocks.STONE, coord3DTo1D(0, 3, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 3, i)); - for (int i = 0; i < Config.CHUNK_SIZE; i++) this.setBlocks(Blocks.DIRT, coord3DTo1D(0, 4, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 4, i)); - for (int i = 0; i < Config.CHUNK_SIZE; i++) this.setBlocks(Blocks.GRASS, coord3DTo1D(0, 5, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 5, i)); + for (int i = 0; i < Config.CHUNK_SIZE; i++) + this.setBlocks(Blocks.STONE, coord3DTo1D(0, 3, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 3, i)); + for (int i = 0; i < Config.CHUNK_SIZE; i++) + this.setBlocks(Blocks.DIRT, coord3DTo1D(0, 4, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 4, i)); + for (int i = 0; i < Config.CHUNK_SIZE; i++) + this.setBlocks(Blocks.GRASS, coord3DTo1D(0, 5, i), coord3DTo1D(Config.CHUNK_SIZE - 1, 5, i)); } - - public void generateStair() { - for (int i = 0; i < Config.CHUNK_SIZE; i++) { - for (int j = 0; j < Config.CHUNK_SIZE; j++) { - this.setBlocks(j < Config.CHUNK_SIZE - i - 1 ? Blocks.STONE : Blocks.GRASS, coord3DTo1D(0, i, j), coord3DTo1D(Config.CHUNK_SIZE - 1, i, j)); - } + + public void generateCube() { + this.setBlocks(Blocks.STONE, 0, + Config.CHUNK_3DCOORD_MAX_INDEX); + } + + public void generateStair() { + for (int i = 0; i < Config.CHUNK_SIZE; i++) { + for (int j = 0; j < Config.CHUNK_SIZE; j++) { + this.setBlocks(j < Config.CHUNK_SIZE - i - 1 ? Blocks.STONE : Blocks.GRASS, coord3DTo1D(0, i, j), + coord3DTo1D(Config.CHUNK_SIZE - 1, i, j)); } - - this.setBlocks(Blocks.DIRT, Chunk.coord3DTo1D(0, 0, 0), Chunk.coord3DTo1D(2, 0, 0)); - this.setBlocks(Blocks.DIRT, Chunk.coord3DTo1D(1, 0, 2), Chunk.coord3DTo1D(2, 0, 2)); } + this.setBlocks(Blocks.DIRT, Chunk.coord3DTo1D(0, 0, 0), Chunk.coord3DTo1D(2, 0, 0)); + this.setBlocks(Blocks.DIRT, Chunk.coord3DTo1D(1, 0, 2), Chunk.coord3DTo1D(2, 0, 2)); + } + public void arrayGenerateCorner() { Blocks[] array = new Blocks[Config.CHUNK_3DCOORD_MAX_INDEX + 1]; @@ -78,11 +92,14 @@ public class Chunk { // Distance from 0,0,0 dist = (int) Math.sqrt((i * i) + (j * j) + (k * k)); - if (dist <= (int) (Config.CHUNK_SIZE / 3)) array[Chunk.coord3DTo1D(i, j, k)] = Blocks.STONE; - else if (dist > (int) (Config.CHUNK_SIZE / 3) && dist <= (int) (2 * Config.CHUNK_SIZE / 3)) array[Chunk.coord3DTo1D(i, j, k)] = Blocks.DIRT; - else if (dist > (int) (2 * Config.CHUNK_SIZE / 3) && dist <= (int) (Config.CHUNK_SIZE)) array[Chunk.coord3DTo1D(i, j, k)] = Blocks.GRASS; - else array[Chunk.coord3DTo1D(i, j, k)] = Blocks.AIR; - + if (dist <= (int) (Config.CHUNK_SIZE / 3)) + array[Chunk.coord3DTo1D(i, j, k)] = Blocks.STONE; + else if (dist > (int) (Config.CHUNK_SIZE / 3) && dist <= (int) (2 * Config.CHUNK_SIZE / 3)) + array[Chunk.coord3DTo1D(i, j, k)] = Blocks.DIRT; + else if (dist > (int) (2 * Config.CHUNK_SIZE / 3) && dist <= (int) (Config.CHUNK_SIZE)) + array[Chunk.coord3DTo1D(i, j, k)] = Blocks.GRASS; + else + array[Chunk.coord3DTo1D(i, j, k)] = Blocks.AIR; } } @@ -91,52 +108,48 @@ public class Chunk { this.treeFrom1DArray(array); } - /* Set blocks. Interval to be intended in the same way as in interval tree (flatten 1d index of 3d coords) */ - public void setBlocks(Blocks block, Interval interval){ - this.blocks.insertValue(block, new Interval(Math.max(0, interval.getLow()), Math.min(Config.CHUNK_3DCOORD_MAX_INDEX, interval.getHigh()))); - } - - public void setBlocks(Blocks block, int intLow, int intHigh){ - this.blocks.insertValue(block, new Interval(Math.max(0, intLow), Math.min(Config.CHUNK_3DCOORD_MAX_INDEX, intHigh))); + /* + * Set blocks. Interval to be intended in the same way as in interval tree + * (flatten 1d index of 3d coords) + */ + public void setBlocks(Blocks block, int intLow, int intHigh) { + this.blocks.insert( Math.max(0,intLow), Math.min(Config.CHUNK_3DCOORD_MAX_INDEX+1, intHigh),block); } public Blocks[] treeTo1DArray() { // System.out.println(Config.CHUNK_3DCOORD_MAX_INDEX); Blocks[] result = new Blocks[Config.CHUNK_3DCOORD_MAX_INDEX + 1]; - - Queue.TreeNode> queue = new ArrayDeque(); - if(blocks.getRoot() != null) queue.add(blocks.getRoot()); - - IntervalTree.TreeNode t; - - while (!queue.isEmpty()) { - t = queue.poll(); - if (t.getLeft() != null) queue.add(t.getLeft()); - if (t.getRight() != null) queue.add(t.getRight()); - - for (int i = t.getInterval().getLow(); i <= t.getInterval().getHigh(); i++) result[i] = t.getValue(); + Blocks v; + Integer oldK = null; + for(Integer k : blocks.treemap.keySet()){ + if(oldK != null){ + v = blocks.valueAtKey(oldK); + //System.out.println("From " +oldK+ " to " + k + ": " + v); + for(int i = oldK; i < k; i++) result[i] = v; + } + oldK = k; } + v = blocks.valueAtKey(oldK); + for(int i = oldK; i < result.length; i++) result[i] = v; return result; } public void treeFrom1DArray(Blocks[] array) { - this.blocks = new IntervalTree<>(); + this.blocks = new IntervalMap<>(); int start = 0; Blocks prev = array[0]; for (int i = 0; i < array.length; i++) { if (array[i] != prev) { - // System.out.println("From " + start + " to " + i + ": " + prev); this.setBlocks(prev == null ? Blocks.AIR : prev, start, i); start = i; } prev = array[i]; } - - // System.out.println("From " + start + " to end :" + prev); - this.setBlocks(prev, start, array.length - 1); + + this.setBlocks(prev, start, array.length-1); } }