aboutsummaryrefslogtreecommitdiffstats
path: root/src/ru
diff options
context:
space:
mode:
Diffstat (limited to 'src/ru')
-rw-r--r--src/ru/olamedia/Options.java2
-rw-r--r--src/ru/olamedia/camera/MatrixCamera.java20
-rw-r--r--src/ru/olamedia/game/GameManager.java51
-rw-r--r--src/ru/olamedia/geom/Mesh.java59
-rw-r--r--src/ru/olamedia/geom/SimpleQuadMesh.java2
-rw-r--r--src/ru/olamedia/math/OpenBitSet.java1389
-rw-r--r--src/ru/olamedia/olacraft/network/GameClient.java2
-rw-r--r--src/ru/olamedia/olacraft/network/GameServer.java10
-rw-r--r--src/ru/olamedia/olacraft/network/Network.java20
-rw-r--r--src/ru/olamedia/olacraft/network/packet/RegionDataPacket.java7
-rw-r--r--src/ru/olamedia/olacraft/render/jogl/ChunkRenderer.java213
-rw-r--r--src/ru/olamedia/olacraft/render/jogl/PlaneRenderer.java3
-rw-r--r--src/ru/olamedia/olacraft/scene/GameScene.java24
-rw-r--r--src/ru/olamedia/olacraft/world/chunk/BlockSlice.java61
-rw-r--r--src/ru/olamedia/olacraft/world/chunk/Chunk.java55
-rw-r--r--src/ru/olamedia/olacraft/world/chunk/ChunkSlice.java34
-rw-r--r--src/ru/olamedia/olacraft/world/data/ChunkData.java59
-rw-r--r--src/ru/olamedia/olacraft/world/data/HeightMap.java4
-rw-r--r--src/ru/olamedia/olacraft/world/data/RegionData.java4
-rw-r--r--src/ru/olamedia/olacraft/world/data/SectorData.java10
-rw-r--r--src/ru/olamedia/olacraft/world/dataProvider/CachedChunkDataProvider.java17
-rw-r--r--src/ru/olamedia/olacraft/world/dataProvider/LocalChunkDataProvider.java109
-rw-r--r--src/ru/olamedia/olacraft/world/dataProvider/RemoteChunkDataProvider.java5
-rw-r--r--src/ru/olamedia/olacraft/world/generator/HeightMapGenerator.java15
-rw-r--r--src/ru/olamedia/olacraft/world/generator/RegionGenerator.java36
-rw-r--r--src/ru/olamedia/olacraft/world/location/ChunkLocation.java5
-rw-r--r--src/ru/olamedia/olacraft/world/location/RegionLocation.java12
-rw-r--r--src/ru/olamedia/olacraft/world/location/SectorLocation.java8
-rw-r--r--src/ru/olamedia/olacraft/world/provider/WorldProvider.java14
29 files changed, 1886 insertions, 364 deletions
diff --git a/src/ru/olamedia/Options.java b/src/ru/olamedia/Options.java
index e1c4d41..9adf252 100644
--- a/src/ru/olamedia/Options.java
+++ b/src/ru/olamedia/Options.java
@@ -1,5 +1,5 @@
package ru.olamedia;
public class Options {
- public static int renderDistance = 256;
+ public static int renderDistance = 128;
}
diff --git a/src/ru/olamedia/camera/MatrixCamera.java b/src/ru/olamedia/camera/MatrixCamera.java
index de37fc8..2515daf 100644
--- a/src/ru/olamedia/camera/MatrixCamera.java
+++ b/src/ru/olamedia/camera/MatrixCamera.java
@@ -13,8 +13,6 @@ import static org.openmali.FastMath.*;
import ru.olamedia.olacraft.game.Game;
import ru.olamedia.input.Keyboard;
import ru.olamedia.math.Frustum;
-import ru.olamedia.olacraft.render.jogl.PlaneRenderer;
-import ru.olamedia.olacraft.render.jogl.VectorRenderer;
import com.jogamp.newt.event.KeyEvent;
@@ -161,16 +159,16 @@ public class MatrixCamera {
loadProjectionMatrix(drawable);
loadViewMatrix(drawable);
- gl.glColor3f(1, 0, 0);
- PlaneRenderer.render(frustum.leftPlane, drawable);
- gl.glColor3f(1, 1, 0);
- PlaneRenderer.render(frustum.rightPlane, drawable);
- gl.glColor3f(1, 0, 1);
- PlaneRenderer.render(frustum.topPlane, drawable);
- gl.glColor3f(1, 1, 1);
- PlaneRenderer.render(frustum.bottomPlane, drawable);
+ // gl.glColor3f(1, 0, 0);
+ // PlaneRenderer.render(frustum.leftPlane, drawable);
+ // gl.glColor3f(1, 1, 0);
+ // PlaneRenderer.render(frustum.rightPlane, drawable);
+ // gl.glColor3f(1, 0, 1);
+ // PlaneRenderer.render(frustum.topPlane, drawable);
+ // gl.glColor3f(1, 1, 1);
+ // PlaneRenderer.render(frustum.bottomPlane, drawable);
- VectorRenderer.render(nearc, frustum.leftPlane.n, drawable);
+ // VectorRenderer.render(nearc, frustum.leftPlane.n, drawable);
}
private void loadViewMatrix(GLAutoDrawable drawable) {
diff --git a/src/ru/olamedia/game/GameManager.java b/src/ru/olamedia/game/GameManager.java
index f8c05d9..d9efdfb 100644
--- a/src/ru/olamedia/game/GameManager.java
+++ b/src/ru/olamedia/game/GameManager.java
@@ -2,8 +2,6 @@ package ru.olamedia.game;
import java.util.Set;
-import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode;
-import javax.media.opengl.DebugGL2ES2;
import javax.media.opengl.GL;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLAutoDrawable;
@@ -13,9 +11,10 @@ import ru.olamedia.olacraft.game.Game;
import ru.olamedia.olacraft.network.discovery.DiscoveryThread;
import ru.olamedia.olacraft.render.jogl.DefaultRenderer;
import ru.olamedia.olacraft.render.jogl.IRenderer;
+import ru.olamedia.olacraft.world.chunk.ChunkMeshBulder;
+import ru.olamedia.olacraft.world.location.BlockLocation;
import ru.olamedia.tasks.TaskManager;
-
import com.jogamp.opengl.JoglVersion;
public class GameManager implements GLEventListener {
@@ -28,6 +27,37 @@ public class GameManager implements GLEventListener {
public GameManager() {
instance = this;
+ tests();
+ }
+
+ private void tests() {
+ BlockLocation b;
+ b = new BlockLocation(0, 0, 0);
+ assert b.getChunkLocation().x == 0;
+ assert b.getChunkLocation().getBlockLocation().x == 0;
+ b = new BlockLocation(14, 0, 0);
+ assert b.getChunkLocation().x == 0;
+ assert b.getChunkLocation().getBlockLocation().x == 0;
+ b = new BlockLocation(15, 0, 0);
+ assert b.getChunkLocation().x == 0;
+ assert b.getChunkLocation().getBlockLocation().x == 0;
+ b = new BlockLocation(16, 0, 0);
+ assert b.getChunkLocation().x == 1;
+ assert b.getChunkLocation().getBlockLocation().x == 0;
+ b = new BlockLocation(-1, 0, 0);
+ assert b.getChunkLocation().x == -1;
+ assert b.getChunkLocation().getBlockLocation().x == -1;
+ b = new BlockLocation(-14, 0, 0);
+ assert b.getChunkLocation().x == -1;
+ b = new BlockLocation(-15, 0, 0);
+ assert b.getChunkLocation().x == -1;
+ assert b.getChunkLocation().getBlockLocation().x == -1;
+ b = new BlockLocation(-16, 0, 0);
+ assert b.getChunkLocation().x == -1;
+ assert b.getChunkLocation().getBlockLocation().x == -1;
+ b = new BlockLocation(-17, 0, 0);
+ assert b.getChunkLocation().x == -2;
+ assert b.getChunkLocation().getBlockLocation().x == -17;
}
private void createServerGame() {
@@ -107,12 +137,18 @@ public class GameManager implements GLEventListener {
}
}
frame.dispose();
+ if (ChunkMeshBulder.instance.isAlive()) {
+ ChunkMeshBulder.instance.interrupt();
+ }
// Get all threads
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (Thread t : threadSet) {
if (t instanceof DiscoveryThread) {
t.interrupt();
}
+ if (t instanceof ChunkMeshBulder) {
+ t.interrupt();
+ }
}
}
@@ -120,10 +156,9 @@ public class GameManager implements GLEventListener {
public void init(GLAutoDrawable drawable) {
// GLContext.getContext().getGL()
GL2ES2 gl = drawable.getGL().getGL2ES2();
- //drawable.setGL(new DebugGL2ES2(gl));
+ // drawable.setGL(new DebugGL2ES2(gl));
System.err.println(JoglVersion.getGLInfo(drawable.getGL(), null));
- System.err.println(Thread.currentThread() + " Chosen GLCapabilities: "
- + drawable.getChosenGLCapabilities());
+ System.err.println(Thread.currentThread() + " Chosen GLCapabilities: " + drawable.getChosenGLCapabilities());
System.err.println(Thread.currentThread() + " INIT GL IS: " + gl.getClass().getName());
System.err.println(Thread.currentThread() + " GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR));
System.err.println(Thread.currentThread() + " GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER));
@@ -144,7 +179,7 @@ public class GameManager implements GLEventListener {
public void display(GLAutoDrawable drawable) {
GL2ES2 gl = drawable.getGL().getGL2ES2();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
- //gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
+ // gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
renderer.render(drawable);
}
@@ -153,7 +188,7 @@ public class GameManager implements GLEventListener {
GL gl = drawable.getGL().getGL2ES2();
gl.glViewport(0, 0, width, height);
}
-
+
public void showMainMenu() {
menu.setVisible(true);
}
diff --git a/src/ru/olamedia/geom/Mesh.java b/src/ru/olamedia/geom/Mesh.java
index 140cc4d..842291f 100644
--- a/src/ru/olamedia/geom/Mesh.java
+++ b/src/ru/olamedia/geom/Mesh.java
@@ -7,8 +7,10 @@ import java.util.HashMap;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
+import javax.media.opengl.GL2ES1;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLContext;
import javax.media.opengl.fixedfunc.GLPointerFunc;
import com.jogamp.opengl.util.GLArrayDataServer;
@@ -240,50 +242,39 @@ public class Mesh {
}
- public void joglRender(GL glx) {
+ public void joglRender() {
if (vertexCount < 1) {
return;
}
- GL2 gl;
- GL2ES2 es2;
- gl = glx.getGL2();
- if (useDisplayList) {
- gl.glEnable(GL2.GL_CULL_FACE);
- gl.glEnable(GL2.GL_DEPTH_TEST);
- }
- if (wireframe) {
- // Set wireframe mode
- gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE);
- } else {
- gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
- }
-
+ GL glx = GLContext.getCurrentGL();
if (useVbo) {
- // es2 = glx.getGL2ES2();
- // gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, data);
-
- // gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
- // gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
+ //GL2ES2 gl = glx.getGL2ES2();
+ GL2 gl = glx.getGL2();
for (Integer m : materials.keySet()) {
- //final GLArrayDataServer interleaved = arrays.get(m);
- arrays.get(m).enableBuffer(gl, true);
gl.glEnable(GL.GL_TEXTURE_2D);
- gl.glEnable(GL2.GL_CULL_FACE);
- gl.glEnable(GL2.GL_DEPTH_TEST);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, (int) m);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST_MIPMAP_NEAREST);
- gl.glBindTexture(GL.GL_TEXTURE_2D, (int) m);
+ arrays.get(m).enableBuffer(gl, true);
+ gl.glEnable(GL.GL_CULL_FACE);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ //gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
gl.glDrawArrays(GL2.GL_QUADS, 0, arrays.get(m).getElementCount());
arrays.get(m).enableBuffer(gl, false);
+ //System.exit(0);
}
- /*
- * gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
- * gl.glVertexPointer(3, GL2.GL_FLOAT, 10, buffer);
- * gl.glDrawArrays(GL2.GL_QUADS, 0, vertexCount);
- * gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
- */
-
} else {
+ GL2 gl = glx.getGL2();
+ if (useDisplayList) {
+ gl.glEnable(GL2.GL_CULL_FACE);
+ gl.glEnable(GL2.GL_DEPTH_TEST);
+ }
+ if (wireframe) {
+ // Set wireframe mode
+ gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE);
+ } else {
+ gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
+ }
if (useDisplayList) {
if (DL == null) {
DL = new DisplayList(glx);
@@ -343,4 +334,8 @@ public class Mesh {
}
}
}
+
+ public int getVertexCount() {
+ return vertexCount;
+ }
}
diff --git a/src/ru/olamedia/geom/SimpleQuadMesh.java b/src/ru/olamedia/geom/SimpleQuadMesh.java
index 42cf439..e832789 100644
--- a/src/ru/olamedia/geom/SimpleQuadMesh.java
+++ b/src/ru/olamedia/geom/SimpleQuadMesh.java
@@ -110,4 +110,6 @@ public class SimpleQuadMesh extends Mesh {
addBottomRightFrontVertex();
}
+
+
}
diff --git a/src/ru/olamedia/math/OpenBitSet.java b/src/ru/olamedia/math/OpenBitSet.java
new file mode 100644
index 0000000..93c26b0
--- /dev/null
+++ b/src/ru/olamedia/math/OpenBitSet.java
@@ -0,0 +1,1389 @@
+package ru.olamedia.math;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * http://www.apache.org/licenses/LICENSE-2.0 (Lucene)
+ *
+ * An "open" BitSet implementation that allows direct access to the
+ * array of words
+ * storing the bits.
+ * <p/>
+ * Unlike java.util.bitet, the fact that bits are packed into an array of longs
+ * is part of the interface. This allows efficient implementation of other
+ * algorithms by someone other than the author. It also allows one to
+ * efficiently implement alternate serialization or interchange formats.
+ * <p/>
+ * <code>OpenBitSet</code> is faster than <code>java.util.BitSet</code> in most
+ * operations and *much* faster at calculating cardinality of sets and results
+ * of set operations. It can also handle sets of larger cardinality (up to 64 *
+ * 2**32-1)
+ * <p/>
+ * The goals of <code>OpenBitSet</code> are the fastest implementation possible,
+ * and maximum code reuse. Extra safety and encapsulation may always be built on
+ * top, but if that's built in, the cost can never be removed (and hence people
+ * re-implement their own version in order to get better performance). If you
+ * want a "safe", totally encapsulated (and slower and limited) BitSet class,
+ * use <code>java.util.BitSet</code>.
+ * <p/>
+ * <h3>Performance Results</h3>
+ *
+ * Test system: Pentium 4, Sun Java 1.5_06 -server -Xbatch -Xmx64M <br/>
+ * BitSet size = 1,000,000 <br/>
+ * Results are java.util.BitSet time divided by OpenBitSet time.
+ * <table border="1">
+ * <tr>
+ * <th></th>
+ * <th>cardinality</th>
+ * <th>intersect_count</th>
+ * <th>union</th>
+ * <th>nextSetBit</th>
+ * <th>get</th>
+ * <th>iterator</th>
+ * </tr>
+ * <tr>
+ * <th>50% full</th>
+ * <td>3.36</td>
+ * <td>3.96</td>
+ * <td>1.44</td>
+ * <td>1.46</td>
+ * <td>1.99</td>
+ * <td>1.58</td>
+ * </tr>
+ * <tr>
+ * <th>1% full</th>
+ * <td>3.31</td>
+ * <td>3.90</td>
+ * <td>&nbsp;</td>
+ * <td>1.04</td>
+ * <td>&nbsp;</td>
+ * <td>0.99</td>
+ * </tr>
+ * </table>
+ *
+ * @author olamedia (modified version)
+ *
+ * @author yonik
+ * @version $Id$
+ */
+
+public class OpenBitSet implements Cloneable, Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 7041505168827686846L;
+ public long[] bits;
+ public int wlen; // number of words (elements) used in the array
+
+ public OpenBitSet() {
+
+ }
+
+ public OpenBitSet(long numBits) {
+ bits = new long[(int) (((numBits - 1) >>> 6) + 1)];
+ wlen = bits.length;
+ }
+
+ public OpenBitSet(long[] bits, int numWords) {
+ this.bits = bits;
+ this.wlen = numWords;
+ }
+
+ public long[] getBits() {
+ return bits;
+ }
+
+ public void setBits(long[] bits) {
+ this.bits = bits;
+ }
+
+ public int getNumWords() {
+ return wlen;
+ }
+
+ public void setNumWords(int nWords) {
+ this.wlen = nWords;
+ }
+
+ public static int wordNum(int index) {
+ // java doesn't currently accept 64 bit array indicies, so returning
+ // a long isn't needed.
+ return index >> 6; // div 64
+ }
+
+ public static int wordNum(long index) {
+ // java doesn't currently accept 64 bit array indicies, so returning
+ // a long isn't needed.
+ return (int) (index >> 6); // div 64
+ }
+
+ public static int bitNum(int index) {
+ // should and or cast come first for best efficiency?
+ return index & 0x0000003f; // mod 64
+ }
+
+ public static int bitNum(long index) {
+ // should and or cast come first for best efficiency?
+ return (int) index & 0x0000003f; // mod 64
+ }
+
+ public static long bitMask(int index) {
+ return 1L << (index & 0x0000003f);
+ }
+
+ /** Returns true or false for the specified bit index */
+ public boolean get(int index) {
+ int i = index >> 6; // div 64
+ // signed shift will keep a negative index and force an exception,
+ // removing the need for an explicit check.
+ int bit = index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ return (bits[i] & bitmask) != 0;
+ }
+
+ /** Returns true or false for the specified bit index */
+ public boolean get(long index) {
+ int i = (int) (index >> 6); // div 64
+ // if (i>=bits.length) return false;
+ int bit = (int) index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ return (bits[i] & bitmask) != 0;
+ }
+
+ // alternate implementation of get()
+ public boolean get1(int index) {
+ int i = index >> 6; // div 64
+ int bit = index & 0x3f; // mod 64
+ return ((bits[i] >>> bit) & 0x01) != 0;
+ // this does a long shift and a bittest (on x86) vs
+ // a long shift, and a long AND, (the test for zero is prob a no-op)
+ // testing on a P4 indicates this is slower than (bits[i] & bitmask) !=
+ // 0;
+ }
+
+ /** returns 1 if the bit is set, 0 if not */
+ public int getBit(int index) {
+ int i = index >> 6; // div 64
+ int bit = index & 0x3f; // mod 64
+ return ((int) (bits[i] >>> bit)) & 0x01;
+ }
+
+ /***
+ * public boolean get2(int index) {
+ * int word = index >> 6; // div 64
+ * int bit = index & 0x0000003f; // mod 64
+ * return (bits[word] << bit) < 0; // hmmm, this would work if bit
+ * order were reversed
+ * // we could right shift and check for parity bit, if it was available to
+ * us.
+ * }
+ ***/
+
+ public void set(int index) {
+ int wordNum = index >> 6; // div 64
+ int bit = index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] |= bitmask;
+ }
+
+ public void set(long index) {
+ int wordNum = (int) (index >> 6); // div 64
+ int bit = (int) index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] |= bitmask;
+ }
+
+ public void clear(int index) {
+ int wordNum = index >> 6; // div 64
+ int bit = index & 0x0000003f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] &= ~bitmask;
+ // hmmm, it takes one more instruction to clear than it does to set...
+ // any
+ // way to work around this? If there were only 63 bits per word, we
+ // could
+ // use a right shift of 10111111...111 in binary to position the 0 in
+ // the
+ // correct place (using sign extension).
+ // Could also use Long.rotateRight() or rotateLeft() *if* they were
+ // converted
+ // by the JVM into a native instruction.
+ }
+
+ public void clear(long index) {
+ int wordNum = (int) (index >> 6); // div 64
+ int bit = (int) index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] &= ~bitmask;
+ }
+
+ /***
+ * public void clear2(int index) {
+ * int word = index >> 6; // div 64
+ * int bit = index & 0x0000003f; // mod 64
+ * bits[word] &= Long.rotateLeft(0xfffffffe,bit);
+ * }
+ ***/
+
+ public boolean getAndSet(int index) {
+ int wordNum = index >> 6; // div 64
+ int bit = index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ boolean val = (bits[wordNum] & bitmask) != 0;
+ bits[wordNum] |= bitmask;
+ return val;
+ }
+
+ public boolean getAndSet(long index) {
+ int wordNum = (int) (index >> 6); // div 64
+ int bit = (int) index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ boolean val = (bits[wordNum] & bitmask) != 0;
+ bits[wordNum] |= bitmask;
+ return val;
+ }
+
+ /** flips a bit */
+ public void flip(int index) {
+ int wordNum = index >> 6; // div 64
+ int bit = index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] ^= bitmask;
+ }
+
+ /** flips a bit */
+ public void flip(long index) {
+ int wordNum = (int) (index >> 6); // div 64
+ int bit = (int) index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] ^= bitmask;
+ }
+
+ /** flips a bit and returns the resulting bit value */
+ public boolean flipAndGet(int index) {
+ int wordNum = index >> 6; // div 64
+ int bit = index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] ^= bitmask;
+ return (bits[wordNum] & bitmask) != 0;
+ }
+
+ /** flips a bit and returns the resulting bit value */
+ public boolean flipAndGet(long index) {
+ int wordNum = (int) (index >> 6); // div 64
+ int bit = (int) index & 0x3f; // mod 64
+ long bitmask = 1L << bit;
+ bits[wordNum] ^= bitmask;
+ return (bits[wordNum] & bitmask) != 0;
+ }
+
+ static long pop_array5(long A[], int wordOffset, int numWords) {
+ int n = numWords;
+ long tot = 0;
+ long ones = 0, twos = 0, twosA, twosB, fours = 0, foursA, foursB, eights;
+
+ int i;
+ for (i = wordOffset; i <= n - 8; i += 8) {
+ /***
+ * #define CSA(h,l, a,b,c) \
+ * {unsigned u = a ^ b; unsigned v = c; \
+ * h = (a & b) | (u & v); l = u ^ v;}
+ ***/
+
+ // CSA(twosA, ones, ones, A[i], A[i+1])
+ {
+ long a = ones, b = A[i], c = A[i + 1];
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ twosA = h;
+ ones = l;
+ }
+
+ // CSA(twosB, ones, ones, A[i+2], A[i+3])
+ {
+ long a = ones, b = A[i + 2], c = A[i + 3];
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ twosB = h;
+ ones = l;
+ }
+
+ // CSA(foursA, twos, twos, twosA, twosB)
+ {
+ long a = twos, b = twosA, c = twosB;
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ foursA = h;
+ twos = l;
+ }
+
+ // CSA(twosA, ones, ones, A[i+4], A[i+5])
+ {
+ long a = ones, b = A[i + 4], c = A[i + 5];
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ twosA = h;
+ ones = l;
+ }
+
+ // CSA(twosB, ones, ones, A[i+6], A[i+7])
+ {
+ long a = ones, b = A[i + 6], c = A[i + 7];
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ twosB = h;
+ ones = l;
+ }
+
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long a = twos, b = twosA, c = twosB;
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ foursB = h;
+ twos = l;
+ }
+
+ // CSA(eights, fours, fours, foursA, foursB)
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long a = fours, b = foursA, c = foursB;
+ long u = a ^ b, v = c;
+ long h = (a & b) | (u & v), l = u ^ v;
+ eights = h;
+ fours = l;
+ }
+
+ tot += pop(eights);
+ }
+
+ tot = (pop(fours) << 2) + (pop(twos) << 1) + pop(ones) + (tot << 3);
+
+ for (i = i; i < n; i++)
+ // Add in the last elements
+ tot = tot + pop(A[i]);
+
+ return tot;
+ }
+
+ /**
+ * Returns the number of bits set in the long
+ */
+ public static int pop(long x) {
+ /***
+ * Hacker's Delight 32 bit pop function:
+ * http://www.hackersdelight.org/HDcode/newCode/pop_arrayHS.cc
+ *
+ * int pop(unsigned x) {
+ * x = x - ((x >> 1) & 0x55555555);
+ * x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ * x = (x + (x >> 4)) & 0x0F0F0F0F;
+ * x = x + (x >> 8);
+ * x = x + (x >> 16);
+ * return x & 0x0000003F;
+ * }
+ ***/
+
+ // 64 bit extension of the C function from above
+ x = x - ((x >>> 1) & 0x5555555555555555L);
+ x = (x & 0x3333333333333333L) + ((x >>> 2) & 0x3333333333333333L);
+ x = (x + (x >>> 4)) & 0x0F0F0F0F0F0F0F0FL;
+ x = x + (x >>> 8);
+ x = x + (x >>> 16);
+ x = x + (x >>> 32);
+ return ((int) x) & 0x7F;
+ }
+
+ /***
+ * http://supertech.lcs.mit.edu/~heinz/dt/node7.html Ernst A. Heinz
+ * DARKTHOUGHT prefers the following non-iterative formulation that
+ * stems from the well-known ``Hacker's Memory'' collection of
+ * programming tricks. It performs better than intuitive methods with
+ * lookup tables because the tables get either too large or need too many
+ * lookups.1.3
+ *
+ * #define m1 ((unsigned_64) 0x5555555555555555)
+ * #define m2 ((unsigned_64) 0x3333333333333333)
+ *
+ * unsigned int non_iterative_pop(const unsigned_64 b) {
+ * unsigned_32 n;
+ * const unsigned_64 a = b - ((b >> 1) & m1);
+ * const unsigned_64 c = (a & m2) + ((a >> 2) & m2);
+ * n = ((unsigned_32) c) + ((unsigned_32) (c >> 32));
+ * n = (n & 0x0F0F0F0F) + ((n >> 4) & 0x0F0F0F0F);
+ * n = (n & 0xFFFF) + (n >> 16);
+ * n = (n & 0xFF) + (n >> 8);
+ * return n;
+ * }
+ *
+ * // Looks like 19 simple arithmetic operations -YCS
+ ***/
+
+ public static int pop(long v0, long v1, long v2, long v3) {
+ // derived from pop_array by setting last four elems to 0.
+ // exchanges one pop() call for 10 elementary operations
+ // saving about 7 instructions... is there a better way?
+ long twosA = v0 & v1;
+ long ones = v0 ^ v1;
+
+ long u2 = ones ^ v2;
+ long twosB = (ones & v2) | (u2 & v3);
+ ones = u2 ^ v3;
+
+ long fours = (twosA & twosB);
+ long twos = twosA ^ twosB;
+
+ return (pop(fours) << 2) + (pop(twos) << 1) + pop(ones);
+
+ }
+
+ /***
+ * Counts the number of set bits in an array of longs
+ */
+ public static long pop_array(long A[], int wordOffset, int numWords) {
+ /*
+ * Robert Harley and David Seal's bit counting algorithm, as documented
+ * in the revisions of Hacker's Delight
+ * http://www.hackersdelight.org/revisions.pdf
+ * http://www.hackersdelight.org/HDcode/newCode/pop_arrayHS.cc
+ *
+ * This function was adapted to Java, and extended to use 64 bit words.
+ * if only we had access to wider registers like SSE from java...
+ *
+ * This function can be transformed to compute the popcoun of other
+ * functions
+ * on bitsets via something like this:
+ * sed 's/A\[\([^]]*\)\]/\(A[\1] \& B[\1]\)/g'
+ */
+ int n = wordOffset + numWords;
+ long tot = 0, tot8 = 0;
+ long ones = 0, twos = 0, fours = 0;
+
+ int i;
+ for (i = wordOffset; i <= n - 8; i += 8) {
+ /***
+ * C macro from Hacker's Delight
+ * #define CSA(h,l, a,b,c) \
+ * {unsigned u = a ^ b; unsigned v = c; \
+ * h = (a & b) | (u & v); l = u ^ v;}
+ ***/
+
+ long twosA, twosB, foursA, foursB, eights;
+
+ // CSA(twosA, ones, ones, A[i], A[i+1])
+ {
+ long b = A[i], c = A[i + 1];
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, A[i+2], A[i+3])
+ {
+ long b = A[i + 2], c = A[i + 3];
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursA, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ // CSA(twosA, ones, ones, A[i+4], A[i+5])
+ {
+ long b = A[i + 4], c = A[i + 5];
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, A[i+6], A[i+7])
+ {
+ long b = A[i + 6], c = A[i + 7];
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursB = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+
+ // CSA(eights, fours, fours, foursA, foursB)
+ {
+ long u = fours ^ foursA;
+ eights = (fours & foursA) | (u & foursB);
+ fours = u ^ foursB;
+ }
+ tot8 += pop(eights);
+ }
+
+ if (i <= n - 4) {
+ long twosA, twosB, foursA, eights;
+ {
+ long b = A[i], c = A[i + 1];
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long b = A[i + 2], c = A[i + 3];
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 4;
+ }
+
+ if (i <= n - 2) {
+ long b = A[i], c = A[i + 1];
+ long u = ones ^ b;
+ long twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+
+ long foursA = twos & twosA;
+ twos = twos ^ twosA;
+
+ long eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 2;
+ }
+
+ if (i < n) {
+ tot += pop(A[i]);
+ }
+
+ tot += (pop(fours) << 2) + (pop(twos) << 1) + pop(ones) + (tot8 << 3);
+
+ return tot;
+ }
+
+ /**
+ * Returns the popcount or cardinality of the two sets after an
+ * intersection.
+ * Neither array is modified.
+ */
+ public static long pop_intersect(long A[], long B[], int wordOffset, int numWords) {
+ // generated from pop_array via sed 's/A\[\([^]]*\)\]/\(A[\1] \&
+ // B[\1]\)/g'
+ int n = wordOffset + numWords;
+ long tot = 0, tot8 = 0;
+ long ones = 0, twos = 0, fours = 0;
+
+ int i;
+ for (i = wordOffset; i <= n - 8; i += 8) {
+ long twosA, twosB, foursA, foursB, eights;
+
+ // CSA(twosA, ones, ones, (A[i] & B[i]), (A[i+1] & B[i+1]))
+ {
+ long b = (A[i] & B[i]), c = (A[i + 1] & B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+2] & B[i+2]), (A[i+3] & B[i+3]))
+ {
+ long b = (A[i + 2] & B[i + 2]), c = (A[i + 3] & B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursA, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ // CSA(twosA, ones, ones, (A[i+4] & B[i+4]), (A[i+5] & B[i+5]))
+ {
+ long b = (A[i + 4] & B[i + 4]), c = (A[i + 5] & B[i + 5]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+6] & B[i+6]), (A[i+7] & B[i+7]))
+ {
+ long b = (A[i + 6] & B[i + 6]), c = (A[i + 7] & B[i + 7]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursB = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+
+ // CSA(eights, fours, fours, foursA, foursB)
+ {
+ long u = fours ^ foursA;
+ eights = (fours & foursA) | (u & foursB);
+ fours = u ^ foursB;
+ }
+ tot8 += pop(eights);
+ }
+
+ if (i <= n - 4) {
+ long twosA, twosB, foursA, eights;
+ {
+ long b = (A[i] & B[i]), c = (A[i + 1] & B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long b = (A[i + 2] & B[i + 2]), c = (A[i + 3] & B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 4;
+ }
+
+ if (i <= n - 2) {
+ long b = (A[i] & B[i]), c = (A[i + 1] & B[i + 1]);
+ long u = ones ^ b;
+ long twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+
+ long foursA = twos & twosA;
+ twos = twos ^ twosA;
+
+ long eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 2;
+ }
+
+ if (i < n) {
+ tot += pop((A[i] & B[i]));
+ }
+
+ tot += (pop(fours) << 2) + (pop(twos) << 1) + pop(ones) + (tot8 << 3);
+
+ return tot;
+ }
+
+ /**
+ * Returns the popcount or cardinality of the union of two sets.
+ * Neither array is modified.
+ */
+ public static long pop_union(long A[], long B[], int wordOffset, int numWords) {
+ // generated from pop_array via sed 's/A\[\([^]]*\)\]/\(A[\1] \|
+ // B[\1]\)/g'
+ int n = wordOffset + numWords;
+ long tot = 0, tot8 = 0;
+ long ones = 0, twos = 0, fours = 0;
+
+ int i;
+ for (i = wordOffset; i <= n - 8; i += 8) {
+ /***
+ * C macro from Hacker's Delight
+ * #define CSA(h,l, a,b,c) \
+ * {unsigned u = a ^ b; unsigned v = c; \
+ * h = (a & b) | (u & v); l = u ^ v;}
+ ***/
+
+ long twosA, twosB, foursA, foursB, eights;
+
+ // CSA(twosA, ones, ones, (A[i] | B[i]), (A[i+1] | B[i+1]))
+ {
+ long b = (A[i] | B[i]), c = (A[i + 1] | B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+2] | B[i+2]), (A[i+3] | B[i+3]))
+ {
+ long b = (A[i + 2] | B[i + 2]), c = (A[i + 3] | B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursA, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ // CSA(twosA, ones, ones, (A[i+4] | B[i+4]), (A[i+5] | B[i+5]))
+ {
+ long b = (A[i + 4] | B[i + 4]), c = (A[i + 5] | B[i + 5]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+6] | B[i+6]), (A[i+7] | B[i+7]))
+ {
+ long b = (A[i + 6] | B[i + 6]), c = (A[i + 7] | B[i + 7]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursB = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+
+ // CSA(eights, fours, fours, foursA, foursB)
+ {
+ long u = fours ^ foursA;
+ eights = (fours & foursA) | (u & foursB);
+ fours = u ^ foursB;
+ }
+ tot8 += pop(eights);
+ }
+
+ if (i <= n - 4) {
+ long twosA, twosB, foursA, eights;
+ {
+ long b = (A[i] | B[i]), c = (A[i + 1] | B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long b = (A[i + 2] | B[i + 2]), c = (A[i + 3] | B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 4;
+ }
+
+ if (i <= n - 2) {
+ long b = (A[i] | B[i]), c = (A[i + 1] | B[i + 1]);
+ long u = ones ^ b;
+ long twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+
+ long foursA = twos & twosA;
+ twos = twos ^ twosA;
+
+ long eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 2;
+ }
+
+ if (i < n) {
+ tot += pop((A[i] | B[i]));
+ }
+
+ tot += (pop(fours) << 2) + (pop(twos) << 1) + pop(ones) + (tot8 << 3);
+
+ return tot;
+ }
+
+ /**
+ * Returns the popcount or cardinality of A & ~B
+ * Neither array is modified.
+ */
+ public static long pop_andnot(long A[], long B[], int wordOffset, int numWords) {
+ // generated from pop_array via sed 's/A\[\([^]]*\)\]/\(A[\1] \&
+ // ~B[\1]\)/g'
+ int n = wordOffset + numWords;
+ long tot = 0, tot8 = 0;
+ long ones = 0, twos = 0, fours = 0;
+
+ int i;
+ for (i = wordOffset; i <= n - 8; i += 8) {
+ /***
+ * C macro from Hacker's Delight
+ * #define CSA(h,l, a,b,c) \
+ * {unsigned u = a ^ b; unsigned v = c; \
+ * h = (a & b) | (u & v); l = u ^ v;}
+ ***/
+
+ long twosA, twosB, foursA, foursB, eights;
+
+ // CSA(twosA, ones, ones, (A[i] & ~B[i]), (A[i+1] & ~B[i+1]))
+ {
+ long b = (A[i] & ~B[i]), c = (A[i + 1] & ~B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+2] & ~B[i+2]), (A[i+3] & ~B[i+3]))
+ {
+ long b = (A[i + 2] & ~B[i + 2]), c = (A[i + 3] & ~B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursA, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ // CSA(twosA, ones, ones, (A[i+4] & ~B[i+4]), (A[i+5] & ~B[i+5]))
+ {
+ long b = (A[i + 4] & ~B[i + 4]), c = (A[i + 5] & ~B[i + 5]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+6] & ~B[i+6]), (A[i+7] & ~B[i+7]))
+ {
+ long b = (A[i + 6] & ~B[i + 6]), c = (A[i + 7] & ~B[i + 7]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursB = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+
+ // CSA(eights, fours, fours, foursA, foursB)
+ {
+ long u = fours ^ foursA;
+ eights = (fours & foursA) | (u & foursB);
+ fours = u ^ foursB;
+ }
+ tot8 += pop(eights);
+ }
+
+ if (i <= n - 4) {
+ long twosA, twosB, foursA, eights;
+ {
+ long b = (A[i] & ~B[i]), c = (A[i + 1] & ~B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long b = (A[i + 2] & ~B[i + 2]), c = (A[i + 3] & ~B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 4;
+ }
+
+ if (i <= n - 2) {
+ long b = (A[i] & ~B[i]), c = (A[i + 1] & ~B[i + 1]);
+ long u = ones ^ b;
+ long twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+
+ long foursA = twos & twosA;
+ twos = twos ^ twosA;
+
+ long eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 2;
+ }
+
+ if (i < n) {
+ tot += pop((A[i] & ~B[i]));
+ }
+
+ tot += (pop(fours) << 2) + (pop(twos) << 1) + pop(ones) + (tot8 << 3);
+
+ return tot;
+ }
+
+ public static long pop_xor(long A[], long B[], int wordOffset, int numWords) {
+ int n = wordOffset + numWords;
+ long tot = 0, tot8 = 0;
+ long ones = 0, twos = 0, fours = 0;
+
+ int i;
+ for (i = wordOffset; i <= n - 8; i += 8) {
+ /***
+ * C macro from Hacker's Delight
+ * #define CSA(h,l, a,b,c) \
+ * {unsigned u = a ^ b; unsigned v = c; \
+ * h = (a & b) | (u & v); l = u ^ v;}
+ ***/
+
+ long twosA, twosB, foursA, foursB, eights;
+
+ // CSA(twosA, ones, ones, (A[i] ^ B[i]), (A[i+1] ^ B[i+1]))
+ {
+ long b = (A[i] ^ B[i]), c = (A[i + 1] ^ B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+2] ^ B[i+2]), (A[i+3] ^ B[i+3]))
+ {
+ long b = (A[i + 2] ^ B[i + 2]), c = (A[i + 3] ^ B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursA, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ // CSA(twosA, ones, ones, (A[i+4] ^ B[i+4]), (A[i+5] ^ B[i+5]))
+ {
+ long b = (A[i + 4] ^ B[i + 4]), c = (A[i + 5] ^ B[i + 5]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(twosB, ones, ones, (A[i+6] ^ B[i+6]), (A[i+7] ^ B[i+7]))
+ {
+ long b = (A[i + 6] ^ B[i + 6]), c = (A[i + 7] ^ B[i + 7]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ // CSA(foursB, twos, twos, twosA, twosB)
+ {
+ long u = twos ^ twosA;
+ foursB = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+
+ // CSA(eights, fours, fours, foursA, foursB)
+ {
+ long u = fours ^ foursA;
+ eights = (fours & foursA) | (u & foursB);
+ fours = u ^ foursB;
+ }
+ tot8 += pop(eights);
+ }
+
+ if (i <= n - 4) {
+ long twosA, twosB, foursA, eights;
+ {
+ long b = (A[i] ^ B[i]), c = (A[i + 1] ^ B[i + 1]);
+ long u = ones ^ b;
+ twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long b = (A[i + 2] ^ B[i + 2]), c = (A[i + 3] ^ B[i + 3]);
+ long u = ones ^ b;
+ twosB = (ones & b) | (u & c);
+ ones = u ^ c;
+ }
+ {
+ long u = twos ^ twosA;
+ foursA = (twos & twosA) | (u & twosB);
+ twos = u ^ twosB;
+ }
+ eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 4;
+ }
+
+ if (i <= n - 2) {
+ long b = (A[i] ^ B[i]), c = (A[i + 1] ^ B[i + 1]);
+ long u = ones ^ b;
+ long twosA = (ones & b) | (u & c);
+ ones = u ^ c;
+
+ long foursA = twos & twosA;
+ twos = twos ^ twosA;
+
+ long eights = fours & foursA;
+ fours = fours ^ foursA;
+
+ tot8 += pop(eights);
+ i += 2;
+ }
+
+ if (i < n) {
+ tot += pop((A[i] ^ B[i]));
+ }
+
+ tot += (pop(fours) << 2) + (pop(twos) << 1) + pop(ones) + (tot8 << 3);
+
+ return tot;
+ }
+
+ public long cardinality() {
+ return pop_array(bits, 0, wlen);
+ }
+
+ /**
+ * Returns the popcount or cardinality of the intersection of the two sets.
+ * Neither set is modified.
+ */
+ public static long intersectionCount(OpenBitSet a, OpenBitSet b) {
+ return pop_intersect(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen));
+ }
+
+ /**
+ * Returns the popcount or cardinality of the union of the two sets.
+ * Neither set is modified.
+ */
+ public static long unionCount(OpenBitSet a, OpenBitSet b) {
+ long tot = pop_union(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen));
+ if (a.wlen < b.wlen) {
+ tot += pop_array(b.bits, a.wlen, b.wlen - a.wlen);
+ } else if (a.wlen > b.wlen) {
+ tot += pop_array(a.bits, b.wlen, a.wlen - b.wlen);
+ }
+ return tot;
+ }
+
+ /**
+ * Returns the popcount or cardinality of "a and not b"
+ * or "intersection(a, not(b))"
+ * Neither set is modified.
+ */
+ public static long andNotCount(OpenBitSet a, OpenBitSet b) {
+ long tot = pop_andnot(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen));
+ if (a.wlen > b.wlen) {
+ tot += pop_array(a.bits, b.wlen, a.wlen - b.wlen);
+ }
+ return tot;
+ }
+
+ /**
+ * Returns the popcount or cardinality of the exclusive-or of the two sets.
+ * Neither set is modified.
+ */
+ public static long xorCount(OpenBitSet a, OpenBitSet b) {
+ long tot = pop_xor(a.bits, b.bits, 0, Math.min(a.wlen, b.wlen));
+ if (a.wlen < b.wlen) {
+ tot += pop_array(b.bits, a.wlen, b.wlen - a.wlen);
+ } else if (a.wlen > b.wlen) {
+ tot += pop_array(a.bits, b.wlen, a.wlen - b.wlen);
+ }
+ return tot;
+ }
+
+ /**
+ * Returns the index of the first set bit starting at the index specified.
+ * -1 is returned if there are no more set bits.
+ */
+ public int nextSetBit(int index) {
+ int i = index >> 6;
+ if (i >= wlen)
+ return -1;
+ int subIndex = index & 0x3f; // index within the word
+ long word = bits[i] >> subIndex; // skip all the bits to the right of
+ // index
+
+ if (word != 0) {
+ return (i << 6) + subIndex + ntz(word);
+ }
+
+ while (++i < wlen) {
+ word = bits[i];
+ if (word != 0)
+ return (i << 6) + ntz(word);
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns the index of the first set bit starting at the index specified.
+ * -1 is returned if there are no more set bits.
+ */
+ public long nextSetBit(long index) {
+ int i = (int) (index >>> 6);
+ if (i >= wlen)
+ return -1;
+ int subIndex = (int) index & 0x3f; // index within the word
+ long word = bits[i] >>> subIndex; // skip all the bits to the right of
+ // index
+
+ if (word != 0) {
+ return (((long) i) << 6) + (subIndex + ntz(word));
+ }
+
+ while (++i < wlen) {
+ word = bits[i];
+ if (word != 0)
+ return (((long) i) << 6) + ntz(word);
+ }
+
+ return -1;
+ }
+
+ /***
+ * python code to generate ntzTable
+ * def ntz(val):
+ * if val==0: return 8
+ * i=0
+ * while (val&0x01)==0:
+ * i = i+1
+ * val >>= 1
+ * return i
+ * print ','.join([ str(ntz(i)) for i in range(256) ])
+ ***/
+ public static final byte ntzTable[] = { 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,
+ 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,
+ 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2,
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,
+ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,
+ 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 };
+
+ /** Returns number of trailing zeros in the 64 bit long value */
+ public static int ntz(long val) {
+ // A full binary search to determine the low byte was slower than
+ // a linear search for nextSetBit(). This is most likely because
+ // the implementation of nextSetBit() shifts bits to the right,
+ // increasing
+ // the probability that the first non-zero byte is in the rhs.
+ //
+ // This implementation does a single binary search at the top level only
+ // so that all other bit shifting can be done on ints instead of longs
+ // to
+ // remain friendly to 32 bit architectures. In addition, the case of a
+ // non-zero first byte is checked for first because it is the most
+ // common
+ // in dense bit arrays.
+
+ int lower = (int) val;
+ int lowByte = lower & 0xff;
+ if (lowByte != 0)
+ return ntzTable[lowByte];
+
+ if (lower != 0) {
+ lowByte = (lower >>> 8) & 0xff;
+ if (lowByte != 0)
+ return ntzTable[lowByte] + 8;
+ lowByte = (lower >>> 16) & 0xff;
+ if (lowByte != 0)
+ return ntzTable[lowByte] + 16;
+ // no need to mask off low byte for the last byte in the 32 bit word
+ // no need to check for zero on the last byte either.
+ return ntzTable[lower >>> 24] + 24;
+ } else {
+ // grab upper 32 bits
+ int upper = (int) (val >> 32);
+ lowByte = upper & 0xff;
+ if (lowByte != 0)
+ return ntzTable[lowByte] + 32;
+ lowByte = (upper >>> 8) & 0xff;
+ if (lowByte != 0)
+ return ntzTable[lowByte] + 40;
+ lowByte = (upper >>> 16) & 0xff;
+ if (lowByte != 0)
+ return ntzTable[lowByte] + 48;
+ // no need to mask off low byte for the last byte in the 32 bit word
+ // no need to check for zero on the last byte either.
+ return ntzTable[upper >>> 24] + 56;
+ }
+ }
+
+ /**
+ * returns 0 based index of first set bit
+ * (only works for x!=0)
+ */
+ public static int ntz2(long x) {
+ int n = 0;
+ int y = (int) x;
+ if (y == 0) {
+ n += 32;
+ y = (int) (x >>> 32);
+ } // the only 64 bit shift necessary
+ if ((y & 0x0000FFFF) == 0) {
+ n += 16;
+ y >>>= 16;
+ }
+ if ((y & 0x000000FF) == 0) {
+ n += 8;
+ y >>>= 8;
+ }
+ return (ntzTable[y & 0xff]) + n;
+ }
+
+ int ntz3(long x) {
+ int n = 1;
+
+ // do the first step as a long, all others as ints.
+ int y = (int) x;
+ if (y == 0) {
+ n += 32;
+ y = (int) (x >>> 32);
+ }
+ if ((y & 0x0000FFFF) == 0) {
+ n += 16;
+ y >>>= 16;
+ }
+ if ((y & 0x000000FF) == 0) {
+ n += 8;
+ y >>>= 8;
+ }
+ if ((y & 0x0000000F) == 0) {
+ n += 4;
+ y >>>= 4;
+ }
+ if ((y & 0x00000003) == 0) {
+ n += 2;
+ y >>>= 2;
+ }
+ return n - (y & 1);
+ }
+
+ /***
+ * Many 32 bit ntz algorithms at http://www.hackersdelight.org/HDcode/ntz.cc
+ */
+
+ public Object clone() {
+ try {
+ OpenBitSet obs = (OpenBitSet) super.clone();
+ obs.bits = obs.bits.clone(); // hopefully an array clone is as
+ // fast(er) than arraycopy
+ return obs;
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** this = this AND other */
+ public void intersect(OpenBitSet other) {
+ int newLen = Math.min(this.wlen, other.wlen);
+ long[] thisArr = this.bits;
+ long[] otherArr = other.bits;
+ // testing against zero can be more efficient
+ int pos = newLen;
+ while (--pos >= 0) {
+ thisArr[pos] &= otherArr[pos];
+ }
+ if (this.wlen > newLen) {
+ // fill zeros from the new shorter length to the old length
+ Arrays.fill(bits, newLen, this.wlen, 0);
+ }
+ this.wlen = newLen;
+ }
+
+ /** this = this OR other */
+ public void union(OpenBitSet other) {
+ int newLen = Math.max(wlen, other.wlen);
+ ensureCapacityWords(newLen);
+
+ long[] thisArr = this.bits;
+ long[] otherArr = other.bits;
+ int pos = this.wlen;
+ while (--pos >= 0) {
+ thisArr[pos] |= otherArr[pos];
+ }
+ if (this.wlen < newLen) {
+ System.arraycopy(otherArr, this.wlen, thisArr, this.wlen, newLen - this.wlen);
+ }
+ this.wlen = newLen;
+ }
+
+ /** Remove all elements set in other. this = this AND_NOT other */
+ public void remove(OpenBitSet other) {
+ int idx = Math.min(wlen, other.wlen);
+ long[] thisArr = this.bits;
+ long[] otherArr = other.bits;
+ while (--idx >= 0) {
+ thisArr[idx] &= ~otherArr[idx];
+ }
+ }
+
+ /** this = this XOR other */
+ public void xor(OpenBitSet other) {
+ int newLen = Math.max(wlen, other.wlen);
+ ensureCapacityWords(newLen);
+
+ long[] thisArr = this.bits;
+ long[] otherArr = other.bits;
+ int pos = this.wlen;
+ while (--pos >= 0) {
+ thisArr[pos] ^= otherArr[pos];
+ }
+ if (this.wlen < newLen) {
+ System.arraycopy(otherArr, this.wlen, thisArr, this.wlen, newLen - this.wlen);
+ }
+ this.wlen = newLen;
+ }
+
+ // some BitSet compatability methods
+
+ // ** see {@link intersect} */
+ public void and(OpenBitSet other) {
+ intersect(other);
+ }
+
+ // ** see {@link union} */
+ public void or(OpenBitSet other) {
+ union(other);
+ }
+
+ // ** see {@link andNot} */
+ public void andNot(OpenBitSet other) {
+ remove(other);
+ }
+
+ /**
+ * Resize the bitset with the size given as a number of words (64
+ * bit longs)
+ */
+ public void ensureCapacityWords(int numWords) {
+ if (bits.length < numWords) {
+ long[] newBits = new long[numWords];
+ System.arraycopy(bits, 0, newBits, 0, wlen);
+ bits = newBits;
+ }
+ }
+
+ public void trimTrailingZeros() {
+ int idx = wlen - 1;
+ while (idx >= 0 && bits[idx] == 0)
+ idx--;
+ wlen = idx + 1;
+ }
+
+}
diff --git a/src/ru/olamedia/olacraft/network/GameClient.java b/src/ru/olamedia/olacraft/network/GameClient.java
index acd50fd..2312c3d 100644
--- a/src/ru/olamedia/olacraft/network/GameClient.java
+++ b/src/ru/olamedia/olacraft/network/GameClient.java
@@ -25,6 +25,7 @@ import ru.olamedia.olacraft.network.packet.WorldInfoPacket;
import ru.olamedia.olacraft.scene.GameScene;
import ru.olamedia.olacraft.world.WorldInfo;
import ru.olamedia.olacraft.world.dataProvider.CachedChunkDataProvider;
+import ru.olamedia.olacraft.world.dataProvider.LocalChunkDataProvider;
import ru.olamedia.olacraft.world.dataProvider.RemoteChunkDataProvider;
import ru.olamedia.olacraft.world.provider.WorldProvider;
@@ -64,6 +65,7 @@ public class GameClient extends ConnectionStateListener implements IPacketListen
// INIT WORLD
worldProvider = new WorldProvider();
worldProvider.setChunkDataProvider(new CachedChunkDataProvider(new RemoteChunkDataProvider(this)));
+ //worldProvider.setChunkDataProvider(new CachedChunkDataProvider(new LocalChunkDataProvider(worldProvider.getInfo().name)));
// CREATE SCENE
scene = new GameScene(worldProvider);
Kryo kryo = client.getKryo();
diff --git a/src/ru/olamedia/olacraft/network/GameServer.java b/src/ru/olamedia/olacraft/network/GameServer.java
index 3d3832d..e1e7723 100644
--- a/src/ru/olamedia/olacraft/network/GameServer.java
+++ b/src/ru/olamedia/olacraft/network/GameServer.java
@@ -31,7 +31,7 @@ import com.esotericsoftware.kryonet.Server;
public class GameServer {
private WorldProvider worldProvider;
-
+
private ExecutorService threadPool = Executors.newFixedThreadPool(1);
public static Server server = new Server(10 * 1024 * 1024, 1024 * 1024) {
@Override
@@ -56,7 +56,10 @@ public class GameServer {
public GameServer() {
// INIT WORLD
worldProvider = new WorldProvider();
- worldProvider.setChunkDataProvider(new CachedChunkDataProvider(new LocalChunkDataProvider(worldProvider.getInfo().name)));
+ // worldProvider.setChunkDataProvider(new
+ // LocalChunkDataProvider(worldProvider.getInfo().name));
+ worldProvider.setChunkDataProvider(new CachedChunkDataProvider(new LocalChunkDataProvider(worldProvider
+ .getInfo().name)));
// CREATE SCENE
scene = new GameScene(worldProvider);
// worldProvider.getInfo().name = "world";
@@ -78,8 +81,7 @@ public class GameServer {
if (object instanceof GetRegionPacket) {
GetRegionPacket p = (GetRegionPacket) object;
RegionData data = worldProvider.getRegion(p.location);
- RegionDataPacket response = new RegionDataPacket();
- response.data = data;
+ RegionDataPacket response = new RegionDataPacket(data);
server.sendToTCP(connection.getID(), response);
}
if (object instanceof SpawnRequestPacket) {
diff --git a/src/ru/olamedia/olacraft/network/Network.java b/src/ru/olamedia/olacraft/network/Network.java
index 2ba8eab..22434f1 100644
--- a/src/ru/olamedia/olacraft/network/Network.java
+++ b/src/ru/olamedia/olacraft/network/Network.java
@@ -2,6 +2,9 @@ package ru.olamedia.olacraft.network;
import java.util.BitSet;
+import org.objenesis.strategy.SerializingInstantiatorStrategy;
+
+import ru.olamedia.math.OpenBitSet;
import ru.olamedia.olacraft.network.packet.ChunkDataPacket;
import ru.olamedia.olacraft.network.packet.ConnectionPacket;
import ru.olamedia.olacraft.network.packet.ConnectionRequestPacket;
@@ -26,7 +29,14 @@ import ru.olamedia.olacraft.world.location.SectorLocation;
import com.esotericsoftware.kryo.Kryo;
public class Network {
+ private static boolean isRegistered = false;
+
public static void registerPackets(Kryo kryo) {
+ if (isRegistered) {
+ // return;
+ }
+ isRegistered = true;
+ //kryo.setInstantiatorStrategy(new SerializingInstantiatorStrategy());
// types
kryo.register(boolean.class);
kryo.register(boolean[].class);
@@ -39,7 +49,7 @@ public class Network {
kryo.register(float[].class);
kryo.register(long.class);
kryo.register(long[].class);
- kryo.register(BitSet.class);
+ kryo.register(OpenBitSet.class);
kryo.register(HeightMap.class);
kryo.register(WorldInfo.class);
kryo.register(WorldInfoPacket.class);
@@ -55,17 +65,11 @@ public class Network {
kryo.register(RegionData.class);
kryo.register(GetRegionPacket.class);
kryo.register(RegionDataPacket.class);
-
-
- kryo.register(ChunkLightData.class);
- kryo.register(ChunkData.class);
- // packets
kryo.register(ConnectionRequestPacket.class);
kryo.register(ConnectionPacket.class);
kryo.register(SpawnRequestPacket.class);
kryo.register(SpawnPacket.class);
- kryo.register(GetChunkDataPacket.class);
- kryo.register(ChunkDataPacket.class);
kryo.register(LiveEntityLocationUpdatePacket.class);
+ // packets
}
}
diff --git a/src/ru/olamedia/olacraft/network/packet/RegionDataPacket.java b/src/ru/olamedia/olacraft/network/packet/RegionDataPacket.java
index 3a60230..72ddba3 100644
--- a/src/ru/olamedia/olacraft/network/packet/RegionDataPacket.java
+++ b/src/ru/olamedia/olacraft/network/packet/RegionDataPacket.java
@@ -3,5 +3,12 @@ package ru.olamedia.olacraft.network.packet;
import ru.olamedia.olacraft.world.data.RegionData;
public class RegionDataPacket implements IPacket {
+ public RegionDataPacket() {
+ }
+
+ public RegionDataPacket(RegionData data) {
+ this.data = data;
+ }
+
public RegionData data;
}
diff --git a/src/ru/olamedia/olacraft/render/jogl/ChunkRenderer.java b/src/ru/olamedia/olacraft/render/jogl/ChunkRenderer.java
index e24e8e9..22a88ab 100644
--- a/src/ru/olamedia/olacraft/render/jogl/ChunkRenderer.java
+++ b/src/ru/olamedia/olacraft/render/jogl/ChunkRenderer.java
@@ -4,6 +4,8 @@ import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLContext;
+import ru.olamedia.Options;
+import ru.olamedia.geom.SimpleQuadMesh;
import ru.olamedia.math.Box;
import ru.olamedia.math.Classifier;
import ru.olamedia.olacraft.game.Game;
@@ -11,6 +13,8 @@ import ru.olamedia.olacraft.world.chunk.BlockSlice;
import ru.olamedia.olacraft.world.chunk.Chunk;
import ru.olamedia.olacraft.world.chunk.ChunkMeshBulder;
import ru.olamedia.olacraft.world.chunk.ChunkSlice;
+import ru.olamedia.olacraft.world.location.BlockLocation;
+import ru.olamedia.olacraft.world.location.ChunkLocation;
public class ChunkRenderer {
private BlockSlice slice;
@@ -30,22 +34,21 @@ public class ChunkRenderer {
public int frustumIntersectChunks = 0;
public boolean renderChunk(Chunk chunk, boolean skipnew) {
- GL gl = GLContext.getCurrentGL();
+
if (!chunk.isAvailable()) {
// System.out.println("not available");
chunk.request();
return skipnew;
}
- /*
- * if (!chunk.isNeighborsAvailable()) {
- * System.out.println("no neighbors");
- * chunk.requestNeighbors();
- * return;
- * }
- */
+
+ if (!chunk.isNeighborsAvailable()) {
+ chunk.requestNeighbors();
+ return skipnew;
+ }
+
// System.out.println("available");
- Box box = new Box(chunk.getX(), chunk.getY(), chunk.getZ(), chunk.getX() + chunk.getWidth(), chunk.getY()
- + chunk.getHeight(), chunk.getZ() + chunk.getDepth());
+ Box box = new Box(chunk.getOffset().x, chunk.getOffset().y, chunk.getOffset().z, chunk.getOffset().x
+ + chunk.getWidth(), chunk.getOffset().y + chunk.getHeight(), chunk.getOffset().z + chunk.getDepth());
if (Game.instance.camera.frustum.quickClassify(box) == Classifier.OUTSIDE) {
frustumCulledChunks++;
return skipnew;
@@ -74,20 +77,27 @@ public class ChunkRenderer {
if (!chunk.isMeshCostructed) {
ChunkMeshBulder.instance.add(chunk);
if (ChunkMeshBulder.instance.isFull()) {
+ // System.out.println("queue is full, skipping");
skipnew = true;
}
+ // System.out.println("not constructed");
return skipnew;
}
if (null == chunk.getMesh()) {
+ // System.out.println("mesh is null");
+ // skipnew = true;
} else {
- chunk.getMesh().joglRender(gl);
+ SimpleQuadMesh mesh = chunk.getMesh();
+ // System.out.println("render " + chunk + " " +
+ // mesh.getVertexCount());
+ mesh.joglRender();
}
return skipnew;
}
public void render(GLAutoDrawable drawable) {
- if (!ChunkMeshBulder.instance.isAlive()) {
+ if (!ChunkMeshBulder.instance.isAlive() && !ChunkMeshBulder.instance.isInterrupted()) {
ChunkMeshBulder.instance.start();
}
@@ -100,47 +110,162 @@ public class ChunkRenderer {
frustumCulledChunks = 0;
boolean skipnew = false;
ChunkSlice cs = slice.getChunkSlice();
+ for (int x = cs.getX(); x < cs.getX() + cs.getWidth(); x++) {
+ for (int z = cs.getZ(); z < cs.getZ() + cs.getDepth(); z++) {
+ for (int y = cs.getY(); y < cs.getY() + cs.getHeight(); y++) {
+ skipnew = renderChunk(cs.getChunk(new ChunkLocation(x, y, z)), skipnew);
+ }
+ }
+ }
+ if (true) {
+ return;
+ }
// rendering from center
int x, y, z;
- int dw = cs.getWidth() / 2;
- int dd = cs.getDepth() / 2;
- int dh = cs.getHeight() / 2;
- for (int dx = 0; dx < dw; dx++) {
- x = cs.getX() + dw + dx;
- for (int dz = 0; dz < dd; dz++) {
- z = cs.getZ() + dd + dz;
- for (int dy = 0; dy < dh; dy++) {
- y = cs.getY() + dh + dy;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
- y = cs.getY() + dh - dy - 1;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ int half = (Options.renderDistance / 16) / 2 + 1;
+ BlockLocation camera = new BlockLocation();
+ camera.x = (int) Game.client.getScene().getPlayer().getCameraX();
+ camera.y = (int) Game.client.getScene().getPlayer().getCameraY();
+ camera.z = (int) Game.client.getScene().getPlayer().getCameraZ();
+ ChunkLocation cameraChunk = camera.getChunkLocation();
+ int cx = cameraChunk.x;
+ int cy = cameraChunk.y;
+ int cz = cameraChunk.z;
+ ChunkLocation cLoc = new ChunkLocation(cx, cy, cz);
+ for (int r = 0; r <= half; r++) {
+ // +x
+ x = cx + r;
+ for (z = cz - r - 1; z <= cz + r; z++) {
+ for (y = cy - r - 1; y <= cy + r; y++) {
+ cLoc = new ChunkLocation(x, y, z);
+ skipnew = renderChunk(cs.getChunk(cLoc), skipnew);
}
- z = cs.getZ() + dd - dz - 1;
- for (int dy = 0; dy < dh; dy++) {
- y = cs.getY() + dh + dy;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
- y = cs.getY() + dh - dy - 1;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ }
+ // -x
+ x = cx - r - 1;
+ for (z = cz - r - 1; z <= cz + r; z++) {
+ for (y = cy - r - 1; y <= cy + r; y++) {
+ cLoc = new ChunkLocation(x, y, z);
+ skipnew = renderChunk(cs.getChunk(cLoc), skipnew);
+ }
+ }
+
+ // +z
+ z = cz + r;
+ for (x = cx - r - 1; x <= cz + r; x++) {
+ for (y = cy - r - 1; y <= cy + r; y++) {
+ cLoc = new ChunkLocation(x, y, z);
+ skipnew = renderChunk(cs.getChunk(cLoc), skipnew);
}
}
- x = cs.getX() + dw - dx - 1;
- for (int dz = 0; dz < dd; dz++) {
- z = cs.getZ() + dd + dz;
- for (int dy = 0; dy < dh; dy++) {
- y = cs.getY() + dh + dy;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
- y = cs.getY() + dh - dy - 1;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // -z
+ z = cz - r - 1;
+ for (x = cx - r - 1; x <= cz + r; x++) {
+ for (y = cy - r - 1; y <= cy + r; y++) {
+ cLoc = new ChunkLocation(x, y, z);
+ skipnew = renderChunk(cs.getChunk(cLoc), skipnew);
}
- z = cs.getZ() + dd - dz - 1;
- for (int dy = 0; dy < dh; dy++) {
- y = cs.getY() + dh + dy;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
- y = cs.getY() + dh - dy - 1;
- skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ }
+ // +y
+ y = cy + r;
+ for (x = cx - r - 1; x <= cz + r; x++) {
+ for (z = cz - r - 1; z <= cz + r; z++) {
+ cLoc = new ChunkLocation(x, y, z);
+ skipnew = renderChunk(cs.getChunk(cLoc), skipnew);
+ }
+ }
+ // -y
+ y = cy - r - 1;
+ for (x = cx - r - 1; x <= cz + r; x++) {
+ for (z = cz - r - 1; z <= cz + r; z++) {
+ cLoc = new ChunkLocation(x, y, z);
+ skipnew = renderChunk(cs.getChunk(cLoc), skipnew);
}
}
+ if (skipnew) {
+ // break;
+ }
+ // break;
}
+
+ // int dw = cs.getWidth() / 2;
+ // int dd = cs.getDepth() / 2;
+ // int dh = cs.getHeight() / 2;
+ // for (int dx = 0; dx < dw; dx++) {
+ // x = cs.getX() + dw + dx;
+ // for (int dz = 0; dz < dd; dz++) {
+ // z = cs.getZ() + dd + dz;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // z = cs.getZ() + dd - dz - 1;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // }
+ // x = cs.getX() + dw - dx - 1;
+ // for (int dz = 0; dz < dd; dz++) {
+ // z = cs.getZ() + dd + dz;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // int dd = cs.getDepth() / 2;
+ // int dh = cs.getHeight() / 2;
+ // for (int dx = 0; dx < dw; dx++) {
+ // x = cs.getX() + dw + dx;
+ // for (int dz = 0; dz < dd; dz++) {
+ // z = cs.getZ() + dd + dz;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // z = cs.getZ() + dd - dz - 1;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // }
+ // x = cs.getX() + dw - dx - 1;
+ // for (int dz = 0; dz < dd; dz++) {
+ // z = cs.getZ() + dd + dz;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // z = cs.getZ() + dd - dz - 1;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // }
+ // }
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // z = cs.getZ() + dd - dz - 1;
+ // for (int dy = 0; dy < dh; dy++) {
+ // y = cs.getY() + dh + dy;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // y = cs.getY() + dh - dy - 1;
+ // skipnew = renderChunk(cs.getChunk(x, y, z), skipnew);
+ // }
+ // }
+ // }
+
// System.out.println("visible top " + visibleTop);
}
}
diff --git a/src/ru/olamedia/olacraft/render/jogl/PlaneRenderer.java b/src/ru/olamedia/olacraft/render/jogl/PlaneRenderer.java
index 754a093..a6e796c 100644
--- a/src/ru/olamedia/olacraft/render/jogl/PlaneRenderer.java
+++ b/src/ru/olamedia/olacraft/render/jogl/PlaneRenderer.java
@@ -19,6 +19,9 @@ public class PlaneRenderer {
}
public static void render(Plane p, GLAutoDrawable drawable) {
+ if (true){
+ return;
+ }
GL2 gl = drawable.getGL().getGL2();
float size = 100;
float step = size / 5;
diff --git a/src/ru/olamedia/olacraft/scene/GameScene.java b/src/ru/olamedia/olacraft/scene/GameScene.java
index 618e270..7f2e1fa 100644
--- a/src/ru/olamedia/olacraft/scene/GameScene.java
+++ b/src/ru/olamedia/olacraft/scene/GameScene.java
@@ -25,6 +25,7 @@ import ru.olamedia.olacraft.weapon.BulletScene;
import ru.olamedia.olacraft.world.blockTypes.AbstractBlockType;
import ru.olamedia.olacraft.world.blockTypes.GrassBlockType;
import ru.olamedia.olacraft.world.chunk.BlockSlice;
+import ru.olamedia.olacraft.world.chunk.ChunkSlice;
import ru.olamedia.olacraft.world.provider.WorldProvider;
import ru.olamedia.player.Player;
import ru.olamedia.vbo.VBO;
@@ -97,7 +98,7 @@ public class GameScene {
*/
public void setRenderDistance(int renderDistance) {
this.renderDistance = renderDistance;
- viewSlice = new BlockSlice(provider, renderDistance, renderDistance * 2, renderDistance);
+ viewSlice = new BlockSlice(provider, renderDistance, renderDistance, renderDistance);
blockRenderer = new ChunkRenderer(viewSlice);
}
@@ -152,10 +153,12 @@ public class GameScene {
gl.glClearColor(49f / 255f, 119f / 255f, 243f / 255f, 1);
// GOING 3D
gl.glPushMatrix();
+ gl.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS);
Game.instance.camera.setUp(drawable);
viewSlice.setCenter((int) Game.instance.camera.getX(), (int) Game.instance.camera.getY(),
(int) Game.instance.camera.getZ());
// RENDER BLOCKS
+ gl.glPopAttrib();
gl.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS);
gl.glColor4f(0f, 1f, 0, 1);
gl.glEnable(GL2.GL_DEPTH_TEST);
@@ -180,6 +183,7 @@ public class GameScene {
for (LiveEntity entity : liveEntities.values()) {
gl.glPushMatrix();
gl.glTranslatef(entity.getX(), entity.getCameraY(), entity.getZ());
+ gl.glPolygonMode(GL2.GL_FRONT_AND_BACK, GL2.GL_LINE);
glu.gluSphere(qobj0, 0.5f, 10, 10);
gl.glPopMatrix();
}
@@ -187,7 +191,7 @@ public class GameScene {
// bullets.render(drawable);
gl.glPopMatrix();
- testObject.render();
+ // testObject.render();
// GOIND 2D
gl.glMatrixMode(GL2.GL_PROJECTION);
@@ -227,7 +231,7 @@ public class GameScene {
// inventoryprivate PMVMatrix matrix;
if (null != inventoryRenderer) {
- inventoryRenderer.render(drawable);
+ // inventoryRenderer.render(drawable);
}
viewport.drawText("avg fps: " + (int) Game.timer.getAvgFps(), 10, height - 20);
@@ -244,9 +248,21 @@ public class GameScene {
viewport.drawText("inAir: " + Game.instance.player.inAir(), width - msz - 10, height - msz - 110);
viewport.drawText("rdistance: " + renderDistance, width - msz - 10, height - msz - 155);
- viewport.drawText("cam x: " + Game.instance.camera.getX(), width - msz - 10, height - msz - 170);
+ ChunkSlice cs = viewSlice.getChunkSlice();
+ viewport.drawText("slice x: " + cs.getX() + ".." + (cs.getX() + cs.getWidth() - 1) + " y: " + cs.getY() + ".."
+ + (cs.getY() + cs.getHeight() - 1) + " z: " + cs.getZ() + ".." + (cs.getZ() + cs.getDepth() - 1), width
+ - msz * 2 - 10, height - msz - 170);
+ // viewport.drawText("slice x: " + (cs.getX() + cs.getWidth() - 1) +
+ // " y: " + (cs.getY() + cs.getHeight() - 1)
+ // + " z: " + (cs.getY() + cs.getDepth() - 1), width - msz * 2 - 10,
+ // height - msz - 185);
+
gl.glPopAttrib();
gl.glPopMatrix();
gl.glFlush();
}
+
+ public Player getPlayer() {
+ return player;
+ }
}
diff --git a/src/ru/olamedia/olacraft/world/chunk/BlockSlice.java b/src/ru/olamedia/olacraft/world/chunk/BlockSlice.java
index ca4c92a..bc744c4 100644
--- a/src/ru/olamedia/olacraft/world/chunk/BlockSlice.java
+++ b/src/ru/olamedia/olacraft/world/chunk/BlockSlice.java
@@ -1,37 +1,20 @@
package ru.olamedia.olacraft.world.chunk;
+import ru.olamedia.olacraft.world.location.BlockLocation;
+import ru.olamedia.olacraft.world.location.ChunkLocation;
import ru.olamedia.olacraft.world.provider.WorldProvider;
public class BlockSlice {
protected WorldProvider provider;
- protected int leftX;
- protected int bottomY;
- protected int backZ;
+ protected BlockLocation offset;
protected int width;
protected int height;
protected int depth;
protected ChunkSlice chunkSlice;
- // Memory leak:
- //protected int[][] highest = new int[256][256];
-
- public void invalidateCache(){
- //highest = new int[256][256];
+ public void invalidateCache() {
}
-
-/* public int getHighest(int blockX, int blockZ) {
- if (highest[blockX - leftX][blockZ - backZ] > 0){
- return highest[blockX - leftX][blockZ - backZ];
- }
- for (int y = 0; y < 128; y++) {
- if (provider.isEmptyBlock(blockX, y, blockZ)){
- highest[blockX - leftX][blockZ - backZ] = y;
- return y;
- }
- }
- return 0;
- }*/
/**
*
@@ -44,6 +27,7 @@ public class BlockSlice {
* (blocks)
*/
public BlockSlice(WorldProvider provider, int width, int height, int depth) {
+ offset = new BlockLocation();
this.provider = provider;
this.width = width;
this.height = height;
@@ -54,10 +38,8 @@ public class BlockSlice {
if (null == chunkSlice) {
chunkSlice = new ChunkSlice(provider, width / 16, height / 16, depth / 16);
}
- int x = Chunk.v(leftX);
- int y = Chunk.v(bottomY);
- int z = Chunk.v(backZ);
- chunkSlice.setLocation(x, y, z);
+ ChunkLocation chunkOffset = offset.getChunkLocation();
+ chunkSlice.setLocation(chunkOffset);
return chunkSlice;
}
@@ -120,12 +102,12 @@ public class BlockSlice {
* (blocks)
*/
public void setLocation(int x, int y, int z) {
- if (x != leftX || y != bottomY || z != backZ){
+ if (x != offset.x || y != offset.y || z != offset.z) {
invalidateCache();
}
- leftX = x;
- bottomY = y;
- backZ = z;
+ offset.x = x;
+ offset.y = y;
+ offset.z = z;
}
/**
@@ -142,23 +124,14 @@ public class BlockSlice {
}
/**
- * @return the left x (blocks)
- */
- public int getX() {
- return leftX;
- }
-
- /**
- * @return the bottom y (blocks)
+ * @return offset
*/
- public int getY() {
- return bottomY;
+ public BlockLocation getOffset() {
+ return offset;
}
- /**
- * @return the back z (blocks)
- */
- public int getZ() {
- return backZ;
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "[" + offset + ";" + width + "x" + height + "x" + depth + "]";
}
}
diff --git a/src/ru/olamedia/olacraft/world/chunk/Chunk.java b/src/ru/olamedia/olacraft/world/chunk/Chunk.java
index ca2d4bf..0eaad3c 100644
--- a/src/ru/olamedia/olacraft/world/chunk/Chunk.java
+++ b/src/ru/olamedia/olacraft/world/chunk/Chunk.java
@@ -37,6 +37,24 @@ public class Chunk extends BlockSlice {
}
/**
+ * Convert chunk coordinate into block coordinate (back left bottom)
+ *
+ * @param v
+ * block coordinate along one axis
+ * @return
+ */
+ public static int rev(int v) {
+ return v * 16; // -32..-17 -16..-1 0..15 16..31 32..
+ /*
+ * if (v >= 0) {
+ * return v * 16;
+ * } else {
+ * return (v + 1) * 16 - 1;
+ * }
+ */
+ }
+
+ /**
* Convert block coordinate into block position inside of chunk
*
* @param v
@@ -78,11 +96,11 @@ public class Chunk extends BlockSlice {
if (isMeshCostructed) {
return mesh;
}
- if (getY() > provider.getInfo().maxHeight) {
+ if (offset.y > provider.getInfo().maxHeight) {
isMeshCostructed = true;
return null;
}
- if (getY() < provider.getInfo().minHeight) {
+ if (offset.y < provider.getInfo().minHeight) {
isMeshCostructed = true;
return null;
}
@@ -95,9 +113,9 @@ public class Chunk extends BlockSlice {
// gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_FASTEST);
// gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
GrassBlockType grass = new GrassBlockType();
- for (int x = getX(); x < getX() + getWidth(); x++) {
- for (int y = getY(); y < getY() + getHeight(); y++) {
- for (int z = getZ(); z < getZ() + getDepth(); z++) {
+ for (int x = offset.x; x < offset.x + getWidth(); x++) {
+ for (int y = offset.y; y < offset.y + getHeight(); y++) {
+ for (int z = offset.z; z < offset.z + getDepth(); z++) {
//
if (!isEmptyBlock(x, y, z)) {
@@ -168,6 +186,9 @@ public class Chunk extends BlockSlice {
mesh.addFrontQuad();
visibleFront++;
}
+ // System.out.println("mesh not empty");
+ } else {
+ // System.out.println("mesh empty");
}
}
}
@@ -193,7 +214,7 @@ public class Chunk extends BlockSlice {
}
private BlockLocation getBlockLocation() {
- return new BlockLocation(getX(), getY(), getZ());
+ return offset;
}
public boolean isAvailable() {
@@ -201,9 +222,9 @@ public class Chunk extends BlockSlice {
}
public boolean isNeighborsAvailable() {
- int x = Chunk.v(getX());
- int y = Chunk.v(getY());
- int z = Chunk.v(getZ());
+ int x = offset.getChunkLocation().x;
+ int y = offset.getChunkLocation().y;
+ int z = offset.getChunkLocation().z;
return provider.isChunkAvailable(new ChunkLocation(x - 1, y, z))
&& provider.isChunkAvailable(new ChunkLocation(x + 1, y, z))
&& provider.isChunkAvailable(new ChunkLocation(x, y - 1, z))
@@ -213,9 +234,9 @@ public class Chunk extends BlockSlice {
}
public void requestNeighbors() {
- int x = Chunk.v(getX());
- int y = Chunk.v(getY());
- int z = Chunk.v(getZ());
+ int x = offset.getChunkLocation().x;
+ int y = offset.getChunkLocation().y;
+ int z = offset.getChunkLocation().z;
if (!provider.isChunkAvailable(new ChunkLocation(x - 1, y, z))) {
provider.loadChunk(new ChunkLocation(x - 1, y, z));
}
@@ -237,11 +258,7 @@ public class Chunk extends BlockSlice {
}
public void request() {
- BlockLocation blockLocation = new BlockLocation(getX(), getY(), getZ());
- // System.out.println("provider.requestChunk(" +
- // blockLocation.getRegionLocation() + blockLocation.getChunkLocation()
- // + ")");
- provider.loadChunk(blockLocation.getChunkLocation());
+ provider.loadChunk(offset.getChunkLocation());
}
// public BlockType getBlockType(int x, int y, int z) {
@@ -287,4 +304,8 @@ public class Chunk extends BlockSlice {
public WorldProvider getProvider() {
return provider;
}
+
+ public void setLocation(ChunkLocation location) {
+ setLocation(location.getBlockLocation().x, location.getBlockLocation().y, location.getBlockLocation().z);
+ }
}
diff --git a/src/ru/olamedia/olacraft/world/chunk/ChunkSlice.java b/src/ru/olamedia/olacraft/world/chunk/ChunkSlice.java
index e440740..d0307bb 100644
--- a/src/ru/olamedia/olacraft/world/chunk/ChunkSlice.java
+++ b/src/ru/olamedia/olacraft/world/chunk/ChunkSlice.java
@@ -2,13 +2,12 @@ package ru.olamedia.olacraft.world.chunk;
import java.util.HashMap;
+import ru.olamedia.olacraft.world.location.ChunkLocation;
import ru.olamedia.olacraft.world.provider.WorldProvider;
public class ChunkSlice {
private WorldProvider provider;
- private int leftX;
- private int bottomY;
- private int backZ;
+ private ChunkLocation offset;
private int width;
private int height;
private int depth;
@@ -18,17 +17,21 @@ public class ChunkSlice {
this.width = width;
this.height = height;
this.depth = depth;
+ offset = new ChunkLocation();
}
protected HashMap<String, Chunk> chunks = new HashMap<String, Chunk>();
- public Chunk getChunk(int x, int y, int z) {
+ public Chunk getChunk(ChunkLocation location) {
+ int x = location.x;
+ int y = location.y;
+ int z = location.z;
String key = x + ";" + y + ";" + z;
if (chunks.containsKey(key)) {
return chunks.get(key);
} else {
Chunk chunk = new Chunk(provider);
- chunk.setLocation(x * 16, y * 16, z * 16);
+ chunk.setLocation(location);
chunks.put(key, chunk);
return chunk;
}
@@ -79,36 +82,33 @@ public class ChunkSlice {
this.depth = depth;
}
- public void setLocation(int x, int y, int z) {
- leftX = x;
- bottomY = y;
- backZ = z;
- }
-
public void setCenter(int x, int y, int z) {
- leftX = x - width / 2;
- bottomY = y - height / 2;
- backZ = z - depth / 2;
+ offset.x = x - width / 2;
+ offset.y = y - height / 2;
+ offset.z = z - depth / 2;
}
/**
* @return the leftX
*/
public int getX() {
- return leftX;
+ return offset.x;
}
/**
* @return the bottomY
*/
public int getY() {
- return bottomY;
+ return offset.y;
}
/**
* @return the backZ
*/
public int getZ() {
- return backZ;
+ return offset.z;
+ }
+ public void setLocation(ChunkLocation chunkOffset) {
+ offset = chunkOffset;
}
}
diff --git a/src/ru/olamedia/olacraft/world/data/ChunkData.java b/src/ru/olamedia/olacraft/world/data/ChunkData.java
index a1abc9e..a6b9f34 100644
--- a/src/ru/olamedia/olacraft/world/data/ChunkData.java
+++ b/src/ru/olamedia/olacraft/world/data/ChunkData.java
@@ -1,8 +1,8 @@
package ru.olamedia.olacraft.world.data;
import java.io.Serializable;
-import java.util.BitSet;
+import ru.olamedia.math.OpenBitSet;
import ru.olamedia.olacraft.world.chunk.Chunk;
import ru.olamedia.olacraft.world.location.BlockLocation;
import ru.olamedia.olacraft.world.location.ChunkLocation;
@@ -12,58 +12,30 @@ public class ChunkData implements Serializable {
public ChunkLocation location;
public static transient int SIZE = 4096;
// private boolean[] notEmpty = new boolean[SIZE];
- private BitSet emptyBlocks = new BitSet(4096);
+ public OpenBitSet emptyBlocks = new OpenBitSet(4096);
public int notEmptyCount = 0;
// public transient int[] type = new int[SIZE];
- // /public transient ChunkLightData light;
public ChunkData() {
- // light = new ChunkLightData();
}
public void compact() {
- if (notEmptyCount == 0) {
+ if (emptyBlocks.cardinality() == 0) {
emptyBlocks = null;
}
}
- public static int normalize(int v) {
- int n = v;
- if (n > 15) {
- n = n % 16;
- }
- if (n < 0) {
- n = 16 + n % 16 - 1;
- // v = 15 - v;
- }
- // System.out.println("normalize(" + v + ") = " + n);
- return n;
- }
-
- public static int getId(int xInsideChunk, int yInsideChunk, int zInsideChunk) {
- xInsideChunk = normalize(xInsideChunk);
- yInsideChunk = normalize(yInsideChunk);
- zInsideChunk = normalize(zInsideChunk);
- int id = xInsideChunk * 16 * 16 + yInsideChunk * 16 + zInsideChunk;
- if (id > SIZE) {
- System.err.println("Exception while getID(" + xInsideChunk + "," + yInsideChunk + "," + zInsideChunk + ")");
- throw new ArrayIndexOutOfBoundsException(id);
- }
- return id;
- }
-
public boolean isEmpty(BlockLocation blockLocation) {
- if (notEmptyCount == 0) {
+ if (emptyBlocks == null) {
return true;
}
- int id = getId(Chunk.in(blockLocation.x), Chunk.in(blockLocation.y), Chunk.in(blockLocation.z));
+ int id = Chunk.in(blockLocation.x) * 16 * 16 + Chunk.in(blockLocation.y) * 16 + Chunk.in(blockLocation.z);
return isEmpty(id);
- // return !notEmpty[id];
}
public boolean isEmpty(int id) {
- if (notEmptyCount == 0) {
+ if (emptyBlocks == null) {
return true;
}
return emptyBlocks.get(id);
@@ -77,10 +49,25 @@ public class ChunkData implements Serializable {
notEmptyCount--;
}
}
- emptyBlocks.set(id, isEmpty);
+ if (isEmpty) {
+ emptyBlocks.set(id);
+ } else {
+ emptyBlocks.clear(id);
+ }
}
public boolean isEmpty() {
- return notEmptyCount == 0;
+ return emptyBlocks == null || emptyBlocks.cardinality() == 0;
+ }
+
+ public void setEmpty(int inChunkX, int inChunkY, int inChunkZ, boolean isEmpty) {
+ int id = inChunkX * 16 * 16 + inChunkY * 16 + inChunkZ;
+ setEmpty(id, isEmpty);
}
+
+ public void setEmpty(BlockLocation blockLocation, boolean isEmpty) {
+ int id = Chunk.in(blockLocation.x) * 16 * 16 + Chunk.in(blockLocation.y) * 16 + Chunk.in(blockLocation.z);
+ setEmpty(id, isEmpty);
+ }
+
}
diff --git a/src/ru/olamedia/olacraft/world/data/HeightMap.java b/src/ru/olamedia/olacraft/world/data/HeightMap.java
index b2c0a63..4d5d458 100644
--- a/src/ru/olamedia/olacraft/world/data/HeightMap.java
+++ b/src/ru/olamedia/olacraft/world/data/HeightMap.java
@@ -12,10 +12,14 @@ import java.io.Serializable;
public class HeightMap implements Serializable {
private static final long serialVersionUID = -6777972159522169977L;
public byte[][] map; // -128..127
+ public int width;
+ public int height;
public HeightMap(){
}
public HeightMap(int width, int height) {
+ this.width = width;
+ this.height = height;
map = new byte[width][height];
}
diff --git a/src/ru/olamedia/olacraft/world/data/RegionData.java b/src/ru/olamedia/olacraft/world/data/RegionData.java
index fa292d2..f58a424 100644
--- a/src/ru/olamedia/olacraft/world/data/RegionData.java
+++ b/src/ru/olamedia/olacraft/world/data/RegionData.java
@@ -24,6 +24,10 @@ public class RegionData implements Serializable {
public HeightMap heightMap = new HeightMap(256, 256);
public SectorData[][] sectorData = new SectorData[16][16];
+ public RegionData(){
+
+ }
+
public void writeTo(OutputStream stream) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(stream);
out.writeObject(this);
diff --git a/src/ru/olamedia/olacraft/world/data/SectorData.java b/src/ru/olamedia/olacraft/world/data/SectorData.java
index 12a97cd..31526a7 100644
--- a/src/ru/olamedia/olacraft/world/data/SectorData.java
+++ b/src/ru/olamedia/olacraft/world/data/SectorData.java
@@ -12,10 +12,14 @@ import ru.olamedia.olacraft.world.location.SectorLocation;
*/
public class SectorData implements Serializable{
private static final long serialVersionUID = 5304471397211814748L;
- public HeightMap heightMap; // locations of highest nonempty blocks
- public ChunkData[] chunkData; // 256/16 = 16
+ public HeightMap heightMap = new HeightMap(16, 16); // locations of highest nonempty blocks
+ public ChunkData[] chunkData = new ChunkData[16]; // 256/16 = 16
public SectorLocation location;
+ public SectorData(){
+
+ }
+
public static int yIndex(int y) {
return (y + 128) / 16;
// 1: (-128 + 128) / 16 = 0
@@ -26,8 +30,6 @@ public class SectorData implements Serializable{
}
public static SectorData generate(){
SectorData data = new SectorData();
- data.heightMap = new HeightMap(16, 16);
- data.chunkData = new ChunkData[16];
return data;
}
}
diff --git a/src/ru/olamedia/olacraft/world/dataProvider/CachedChunkDataProvider.java b/src/ru/olamedia/olacraft/world/dataProvider/CachedChunkDataProvider.java
index 23270a7..444cff6 100644
--- a/src/ru/olamedia/olacraft/world/dataProvider/CachedChunkDataProvider.java
+++ b/src/ru/olamedia/olacraft/world/dataProvider/CachedChunkDataProvider.java
@@ -27,29 +27,19 @@ public class CachedChunkDataProvider extends AbstractChunkDataProvider {
@Override
public boolean isRegionAvailable(RegionLocation regionLocation) {
String key = regionLocation.toString();// regionLocation.x + "-" +
- // regionLocation.z;
if (regionMap.containsKey(key)) {
return true;
}
- if (loading.contains(key)) {
- // return false;
- }
return provider.isRegionAvailable(regionLocation);
}
@Override
public void loadRegion(RegionLocation regionLocation) {
String key = regionLocation.toString();
- //debug("loadRegion(" + regionLocation + ")");
+ // debug("loadRegion(" + regionLocation + ")");
if (!regionMap.containsKey(key)) {
- if (!loading.contains(key)) {
- debug("load()");
- loading.add(key);
- provider.loadRegion(regionLocation);
- }else{
- //debug("loadRegion(" + regionLocation + ") already in loading");
- }
- }else{
+ provider.loadRegion(regionLocation);
+ } else {
debug("error: loadRegion(" + regionLocation + ") already in regionMap");
}
}
@@ -62,7 +52,6 @@ public class CachedChunkDataProvider extends AbstractChunkDataProvider {
} else {
RegionData data = provider.getRegion(regionLocation);
regionMap.put(key, data);
- loading.remove(key);
return data;
}
}
diff --git a/src/ru/olamedia/olacraft/world/dataProvider/LocalChunkDataProvider.java b/src/ru/olamedia/olacraft/world/dataProvider/LocalChunkDataProvider.java
index 514081f..887421f 100644
--- a/src/ru/olamedia/olacraft/world/dataProvider/LocalChunkDataProvider.java
+++ b/src/ru/olamedia/olacraft/world/dataProvider/LocalChunkDataProvider.java
@@ -9,10 +9,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
-import ru.olamedia.olacraft.world.data.ChunkData;
import ru.olamedia.olacraft.world.data.RegionData;
-import ru.olamedia.olacraft.world.generator.HeightMapGenerator;
import ru.olamedia.olacraft.world.generator.RegionGenerator;
import ru.olamedia.olacraft.world.location.RegionLocation;
@@ -80,41 +80,23 @@ public class LocalChunkDataProvider extends AbstractChunkDataProvider {
debug("loadRegion(" + regionLocation + ")");
}
- private ChunkData createChunk(int chunkX, int chunkY, int chunkZ) {
- debug("createChunk " + chunkX + " " + chunkY + " " + chunkZ);
- ChunkData data = new ChunkData();
- try {
- getSeed();
- } catch (IOException e) {
- e.printStackTrace();
- }
- HeightMapGenerator.minValue = 1;
- HeightMapGenerator.maxValue = 64;
- HeightMapGenerator.init();
- HeightMapGenerator.seed = seed[0];
- int[][] heightMap = HeightMapGenerator.getChunkHeightMap(chunkX, chunkZ);
- for (int y = 0; y < 16; y++) {
- for (int x = 0; x < 16; x++) {
- for (int z = 0; z < 16; z++) {
- data.setEmpty(ChunkData.getId(x, y, z), (heightMap[x][z] < chunkY * 16 + y));
- }
- }
- }
- return data;
- }
-
+ @SuppressWarnings("unused")
@Override
public RegionData getRegion(RegionLocation regionLocation) {
String filename = path + File.separator + regionLocation.getFilename();
RegionData data = null;
- // TODO READ/WRITE FILE
+ if (true) {
+ return generateRegion(regionLocation);
+ }
File chunkFile = new File(filename);
- if (chunkFile.exists()) {
+ if (false && chunkFile.exists()) {
InputStream in;
try {
- in = new FileInputStream(chunkFile);
+ FileInputStream fIn = new FileInputStream(chunkFile);
+ in = new GZIPInputStream(fIn);
data = RegionData.loadFrom(in);
in.close();
+ fIn.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
@@ -124,11 +106,14 @@ public class LocalChunkDataProvider extends AbstractChunkDataProvider {
}
} else {
data = generateRegion(regionLocation);
+ OutputStream out;
try {
chunkFile.createNewFile();
- FileOutputStream out = new FileOutputStream(chunkFile);
+ FileOutputStream fOut = new FileOutputStream(chunkFile);
+ out = new GZIPOutputStream(fOut);
data.writeTo(out);
out.close();
+ fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
@@ -139,7 +124,6 @@ public class LocalChunkDataProvider extends AbstractChunkDataProvider {
public RegionData generateRegion(RegionLocation regionLocation) {
RegionData data = new RegionData();
data.location = regionLocation;
- // TODO FILL HERE
RegionGenerator generator = new RegionGenerator();
try {
generator.setSeed(getSeed());
@@ -150,69 +134,4 @@ public class LocalChunkDataProvider extends AbstractChunkDataProvider {
return data;
}
- public ChunkData get(int chunkX, int chunkY, int chunkZ) {
- debug("get " + chunkX + " " + chunkY + " " + chunkZ);
- ChunkData data = null;
- String filename = path + File.separator + chunkX + "_" + chunkY + "_" + chunkZ + ".chunk";
- /*
- * File chunkFile = new File(filename);
- * if (chunkFile.exists()) {
- * try {
- * InputStream in = new FileInputStream(chunkFile);
- * DataInputStream din = new DataInputStream(in);
- * data = new ChunkData();
- * data.readFrom(din);
- * din.close();
- * in.close();
- * } catch (FileNotFoundException e) {
- * e.printStackTrace();
- * } catch (IOException e) {
- * e.printStackTrace();
- * }
- * } else {
- */
- data = createChunk(chunkX, chunkY, chunkZ);
- /*
- * OutputStream out = null;
- * ByteArrayOutputStream bout = null;
- * DataOutputStream dout = null;
- * try {
- * chunkFile.createNewFile();
- * out = new FileOutputStream(chunkFile);
- * // bout = new ByteArrayOutputStream(4096);
- * dout = new DataOutputStream(out);
- * data.writeTo(dout);
- * // dout.flush();
- * // out.write(bout.toByteArray());
- * out.flush();
- * } catch (IOException e) {
- * e.printStackTrace();
- * } finally {
- * if (null != dout) {
- * try {
- * dout.close();
- * } catch (IOException e) {
- * e.printStackTrace();
- * }
- * }
- * if (null != bout) {
- * try {
- * bout.close();
- * } catch (IOException e) {
- * e.printStackTrace();
- * }
- * }
- * if (null != out) {
- * try {
- * out.close();
- * } catch (IOException e) {
- * e.printStackTrace();
- * }
- * }
- * }
- * }
- */
- return data;
- }
-
}
diff --git a/src/ru/olamedia/olacraft/world/dataProvider/RemoteChunkDataProvider.java b/src/ru/olamedia/olacraft/world/dataProvider/RemoteChunkDataProvider.java
index 8284ff9..cd48071 100644
--- a/src/ru/olamedia/olacraft/world/dataProvider/RemoteChunkDataProvider.java
+++ b/src/ru/olamedia/olacraft/world/dataProvider/RemoteChunkDataProvider.java
@@ -36,8 +36,8 @@ public class RemoteChunkDataProvider extends AbstractChunkDataProvider implement
@Override
public void loadRegion(RegionLocation regionLocation) {
String key = regionLocation.toString();
- debug("loadRegion(" + key + ")");
if (!loading.contains(key)) {
+ debug("loadRegion(" + key + ")");
loading.add(key);
client.send(new GetRegionPacket(regionLocation));
debug("sent packet: GetRegionPacket");
@@ -64,8 +64,9 @@ public class RemoteChunkDataProvider extends AbstractChunkDataProvider implement
@Override
public void onPacket(Connection connection, IPacket p) {
if (p instanceof RegionDataPacket) {
- debug("received packet [conn " + connection.getID() + "]: ChunkDataPacket");
+ debug("received packet [conn " + connection.getID() + "]: RegionDataPacket");
RegionData data = ((RegionDataPacket) p).data;
+ System.out.println(data.sectorData[0][0].chunkData[15].isEmpty(0) + "");
String key = data.location.toString();
map.put(key, data);
loading.remove(key);
diff --git a/src/ru/olamedia/olacraft/world/generator/HeightMapGenerator.java b/src/ru/olamedia/olacraft/world/generator/HeightMapGenerator.java
index c3e32b5..649961f 100644
--- a/src/ru/olamedia/olacraft/world/generator/HeightMapGenerator.java
+++ b/src/ru/olamedia/olacraft/world/generator/HeightMapGenerator.java
@@ -77,6 +77,7 @@ public class HeightMapGenerator {
maxTerrain = new Max(plains, turbulence);
finalTerrain = new ScaleBias(maxTerrain);
finalTerrain.setBias(2);
+ finalTerrain.setScale(0.4);
setSeed(seed);
} catch (ExceptionInvalidParam e) {
e.printStackTrace();
@@ -117,7 +118,7 @@ public class HeightMapGenerator {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
// System.out.print(((float) heights[x][z]) + ";");
- ints[x][z] = (int) (minValue + (maxValue - minValue) * (heights[x][z] + 1) / 2);
+ ints[x][z] = (int) (50 + minValue + (maxValue - minValue) * (heights[x][z] + 1) / 2);
}
}
// System.out.println("");
@@ -137,15 +138,17 @@ public class HeightMapGenerator {
builder.setSourceModule(finalTerrain);
builder.setDestNoiseMap(heightMap);
builder.setDestSize(256, 256);
- float bx = location.x;
- float bz = location.z;
- builder.setBounds(bx, bx + 1, bz, bz + 1);
+ float bx = location.getBlockLocation().x;
+ float bz = location.getBlockLocation().z;
+ float cx = location.getChunkLocation().x;
+ float cz = location.getChunkLocation().z;
+ builder.setBounds(cx, cx + 16, cz, cz + 16);
builder.build();
double[][] heights = heightMap.getNoiseMap();
for (int x = 0; x < 256; x++) {
for (int z = 0; z < 256; z++) {
- map.setHeight(x, z, 0);
- //(int) (minValue + (maxValue - minValue) * (heights[x][z] + 1) / 2)
+ // map.setHeight(x, z, 0);
+ map.setHeight(x, z, (int) (minValue + ((1 + heights[x][z]) / 2) * (maxValue - minValue)));
}
}
return map;
diff --git a/src/ru/olamedia/olacraft/world/generator/RegionGenerator.java b/src/ru/olamedia/olacraft/world/generator/RegionGenerator.java
index 9657e32..05fc067 100644
--- a/src/ru/olamedia/olacraft/world/generator/RegionGenerator.java
+++ b/src/ru/olamedia/olacraft/world/generator/RegionGenerator.java
@@ -21,42 +21,54 @@ public class RegionGenerator {
public void generate(RegionData data) {
HeightMapGenerator.minValue = -5;
- HeightMapGenerator.maxValue = 100;
+ HeightMapGenerator.maxValue = 60;
HeightMapGenerator.init();
HeightMapGenerator.seed = seed[0];
- BlockLocation offset = data.location.getBlockLocation();
+ // BlockLocation blockOffset = data.location.getBlockLocation();
+ SectorLocation sectorOffset = data.location.getSectorLocation();
// int[][] heightMap =
// HeightMapGenerator.getHeightMap(data.location.getBlockLocation().x,
// data.location.getBlockLocation().z, 256, 256);
debug(data.location.toString());
data.heightMap = HeightMapGenerator.getHeightMap(data.location);
- //debug(data.heightMap.toString());
+ // debug(data.heightMap.toString());
data.sectorData = new SectorData[16][16];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
// CREATE SECTOR
SectorData sector = new SectorData();
- sector.location = new SectorLocation(offset.x + x * 16, offset.z + z * 16);
+ sector.location = new SectorLocation(sectorOffset.x + x, sectorOffset.z + z);
sector.heightMap = new HeightMap(16, 16);
sector.chunkData = new ChunkData[16];
for (int y = 0; y < 16; y++) {
// CREATE CHUNK
ChunkData chunk = new ChunkData();
chunk.location = new ChunkLocation(sector.location.x, y, sector.location.z);
- int chunkOffsetY = y * 16 - 128;
+ BlockLocation chunkOffset = chunk.location.getBlockLocation();
for (int inChunkX = 0; inChunkX < 16; inChunkX++) {
for (int inChunkZ = 0; inChunkZ < 16; inChunkZ++) {
int height = data.heightMap.getHeight(x * 16 + inChunkX, z * 16 + inChunkZ);
- //System.out.println("height: " + height);
+ // System.out.println("height: " + height);
sector.heightMap.setHeight(inChunkX, inChunkZ, height);
+ BlockLocation blockOffset = new BlockLocation();
+ blockOffset.x = chunkOffset.x + inChunkX;
+ blockOffset.z = chunkOffset.z + inChunkZ;
for (int inChunkY = 0; inChunkY < 16; inChunkY++) {
- //height = sector.heightMap.getHeight(inChunkX, inChunkZ);
- int id = ChunkData.getId(inChunkX, inChunkY, inChunkZ);
- if (chunkOffsetY + inChunkY > height) {
- chunk.setEmpty(id, true);
+ blockOffset.y = chunkOffset.y + inChunkY;
+ // height = sector.heightMap.getHeight(inChunkX,
+ // inChunkZ);
+ if (blockOffset.y > height) {
+ // System.out.println("--- height: " +
+ // height + " block:" + blockOffset);
+ chunk.setEmpty(blockOffset, true);
} else {
- //System.out.println("not empty, height: " + height);
- chunk.setEmpty(id, false);
+ if (blockOffset.y > 0) {
+ // System.out.println("+++ height: " +
+ // height + " block:" + blockOffset);
+ // System.out.println("not empty, height: "
+ // + height);
+ }
+ chunk.setEmpty(blockOffset, false);
}
}
}
diff --git a/src/ru/olamedia/olacraft/world/location/ChunkLocation.java b/src/ru/olamedia/olacraft/world/location/ChunkLocation.java
index 57acc52..a772c6d 100644
--- a/src/ru/olamedia/olacraft/world/location/ChunkLocation.java
+++ b/src/ru/olamedia/olacraft/world/location/ChunkLocation.java
@@ -32,9 +32,14 @@ public class ChunkLocation implements Serializable {
public String toString() {
return "chunkLocation[" + x + "," + y + "," + z + "]";
}
+
/*
* public BlockSlice getSlice(){
*
* }
*/
+
+ public BlockLocation getBlockLocation() {
+ return new BlockLocation(Chunk.rev(x), Chunk.rev(y) - 128, Chunk.rev(z));
+ }
}
diff --git a/src/ru/olamedia/olacraft/world/location/RegionLocation.java b/src/ru/olamedia/olacraft/world/location/RegionLocation.java
index 59a57ec..a1100c0 100644
--- a/src/ru/olamedia/olacraft/world/location/RegionLocation.java
+++ b/src/ru/olamedia/olacraft/world/location/RegionLocation.java
@@ -2,6 +2,8 @@ package ru.olamedia.olacraft.world.location;
import java.io.Serializable;
+import ru.olamedia.olacraft.world.chunk.Chunk;
+
public class RegionLocation implements Serializable {
private static final long serialVersionUID = -141619138379029773L;
public int x;
@@ -23,7 +25,15 @@ public class RegionLocation implements Serializable {
return "" + x + "_" + z + ".region";
}
+ public SectorLocation getSectorLocation() {
+ return new SectorLocation(Chunk.rev(x), Chunk.rev(z));
+ }
+
+ public ChunkLocation getChunkLocation() {
+ return new ChunkLocation(Chunk.rev(x), 0, Chunk.rev(z));
+ }
+
public BlockLocation getBlockLocation() {
- return new BlockLocation(x * 256, 0, z * 256);
+ return new BlockLocation(Chunk.rev(Chunk.rev(x)), 0, Chunk.rev(Chunk.rev(z)));
}
}
diff --git a/src/ru/olamedia/olacraft/world/location/SectorLocation.java b/src/ru/olamedia/olacraft/world/location/SectorLocation.java
index 59390b8..0c3af25 100644
--- a/src/ru/olamedia/olacraft/world/location/SectorLocation.java
+++ b/src/ru/olamedia/olacraft/world/location/SectorLocation.java
@@ -23,6 +23,14 @@ public class SectorLocation implements Serializable {
return new RegionLocation(Chunk.v(x), Chunk.v(z));
}
+ public ChunkLocation getChunkLocation() {
+ return new ChunkLocation(Chunk.rev(x), 0, Chunk.rev(z));
+ }
+
+ public BlockLocation getBlockLocation() {
+ return new BlockLocation(Chunk.rev(Chunk.rev(x)), 0, Chunk.rev(Chunk.rev(z)));
+ }
+
public String toString() {
return "sectorLocation[" + x + "," + z + "]";
}
diff --git a/src/ru/olamedia/olacraft/world/provider/WorldProvider.java b/src/ru/olamedia/olacraft/world/provider/WorldProvider.java
index 8dacce9..b7984bc 100644
--- a/src/ru/olamedia/olacraft/world/provider/WorldProvider.java
+++ b/src/ru/olamedia/olacraft/world/provider/WorldProvider.java
@@ -45,9 +45,7 @@ public class WorldProvider {
spawnLocation.y = y;
System.out.print(y + ". ");
ChunkData chunk = dataProvider.getChunk(spawnLocation.getChunkLocation());
- boolean notEmpty = !chunk.isEmpty(ChunkData.getId(Chunk.in(spawnLocation.x), Chunk.in(spawnLocation.y),
- Chunk.in(spawnLocation.z)));
- if (notEmpty) {
+ if (!chunk.isEmpty(spawnLocation)) {
// found
l.y = y + 1;
System.out.println("found: " + y);
@@ -65,6 +63,9 @@ public class WorldProvider {
}
public boolean renderTop(int x, int y, int z) {
+ // System.out.println("Check render top " + y + "[" + x + " " + y + " "
+ // + z + "]" + !isEmptyBlock(x, y, z)
+ // + " && " + isEmptyBlock(x, y + 1, z));
return (!isEmptyBlock(x, y, z)) && (isEmptyBlock(x, y + 1, z));
}
@@ -91,9 +92,14 @@ public class WorldProvider {
ChunkData data = dataProvider.getChunk(blockLocation.getChunkLocation());
if (null != data) {
return data.isEmpty(blockLocation);
+ } else {
+ // System.out.println("chunk null " + x + " " + y + " " + z);
}
+ } else {
+ // System.out.println("chunk not available " + x + " " + y + " " +
+ // z);
}
- return false;
+ return true;
}
public void requestChunk(int chunkX, int chunkY, int chunkZ) {