Converting a 3D int array to an Octree in Java - java

I'm creating a voxel engine in Java using LWJGL just for practice, but I'm getting stuck on the chunk management system. More specifically, I'm trying to convert a Chunk, which is just a 3D array of integers for the block id, into an Octree for optimal rendering.
So far, I have the system working, but it's horribly inefficient.
Here's a screenshot of a 16*16*16 chunk with all positions below y=8 set to 1 (the red blocks)
https://raw.githubusercontent.com/ninthworld/Octree/master/screenshot0.png
I added a debugger to the OctNode generator code to find out how many times it needed to access the chunk array, and it came back with 8392704.
It accessed the chunk array over 8 million times just to generate 8 children nodes.
When I set the chunk array to only have blocks below y=4, the program has a blackscreen for almost 30 seconds, and the debugger returns 1623199744 array accesses.
Over 1.6 billion array calls just to generate 68 children nodes.
I obviously need to reduce the number of array calls, that much is certain, but I'm not sure how I would go about doing that. Here's the github page for the project if you'd like to see the entire source code.
Here are the important parts of my code:
Main.java
// Initialize Octree Object
// This is just an extended OctNode object
octree = new Octree(chunk);
octree.generate(lookup);
OctNode.java
public void generate(){
int value = -1;
// Loop through an area an area of width*width*width
for(int x=0; x<width; x++){
for(int y=0; y<width; y++){
for(int z=0; z<width; z++){
// Get the value from the master array based on the node's
// offset + the forloop'd area
int store = array[x+(int)offset.x][y+(int)offset.y][z+(int)offset.z];
// Basically sets the value variable to the value at
// 0, 0, 0 with respect to the offset
if(value < 0)
value = store;
// Then check if the current position's value is the
// same as the first one found (int value), if it's not,
// then this node needs to be subdivided
if(store != value){
// Create 8 children for this node
children = new OctNode[8];
// And for each of the children...
for(int i=0; i<children.length; i++){
// Set their offset equal to this node's offset +
// this node's width/2 all with respect to it's
// individual quadrant (which is related to i)
Vector3f offS = new Vector3f(offset.x + (width/2f)*(i%2), offset.y + (width/2f)*((int)Math.floor(i/2f)%2), offset.z + (width/2f)*((int)Math.floor(i/4f)%2));
// Initialize the new child node
children[i] = new OctNode(array, (int)(width/2f), offS);
// And now do the same thing (recursion), but
// for a smaller area
children[i].generate();
}
}
}
}
}
// This is only called if the node is completely made of one value
if(children == null){
data = value;
}
}
That's the best I can explain it unfortunately. If you could point out an easier, faster way to do the same thing that would be amazing.

You are partitioning the tree too frequently. If you have a 16x16x16 array with all different values, you are going to recurse at every cell except the first, so you will call generate (16x16x16-1) times at the top level, rather than just once; the value of the children array will be overwritten many times over; of course, you will repeat the doing of unnecessary work at the next level down etc.
You should move the decision to subdivide the current octree node outside the nested for loops. For example:
public void generate(){
// Assuming that width >= 1.
int minValue = Integer.MAX_VALUE;
int maxValue = Integer.MIN_VALUE;
// Loop through an area an area of width*width*width
// looking for min and max values in that area.
for(int x=0; x<width; x++){
for(int y=0; y<width; y++){
for(int z=0; z<width; z++){
int store = array[x+(int)offset.x][y+(int)offset.y][z+(int)offset.z];
minValue = Math.min(minValue, store);
maxValue = Math.max(maxValue, store);
}
}
}
if (minValue != maxValue) {
// Subdivide if the min and maxValues are different,
// as above.
children = new OctNode[8];
// etc.
} else {
data = minValue;
}
}

Related

Ordering ArrayList of points to follow closest point LibGDX Java

I have an ArrayList of Vector2 (Vector2 is an object that contains an x and y coordinate) that contains a bunch of vertices that make a shape when drawn. However, the way they were put into the ArrayList are out of order. For example, the starting Vector2 point is index 0 but the closest next point is of index 4. My goal is to have another ArrayList of Vector2 that orders the Vector2's in a way so that the starting point is index 0, the next closest point is index 1, next is index 2, etc. Here is an image of what is happening.
The image shows what is happening but the green arrows show what I want to happen. The green point is the starting point.
So what I need is once the new ArrayList is created and ordered, it should look like this:
So here is the code in which I thought would do it:
public Array<Vector2> orderedVertices;
private void cleanVertices(Array<Vector2> originalVertices){
//This will be the Array containing ordered vertices
orderedVertices = new Array<Vector2>();
//Copy vertices of unsorted Array in a new Array
Array<Vector2> verticesOfImage = originalVertices;
//Add a random vertice to begin with (starting point)
orderedVertices.add(verticesOfImage.get(1));
//For breaking loop
int originalArraySize = originalVertices.size;
for(int i = 0; i < orderedVertices.size; i++){
//Index to be compared, meaning to find closest vertice around this vertice
int indexOfCompare = i;
//Get a distance for reference //Random point to check
double currentDistance = MathFactory.distance(orderedVertices.get(indexOfCompare), verticesOfImage.get(0));
//If we found a closest vertice, add to ordered pair and remove from original
// So we don't go backwards
if(getClosest(verticesOfImage, indexOfCompare, currentDistance) != -1){
orderedVertices.add(verticesOfImage.removeIndex(getClosest(verticesOfImage, indexOfCompare, currentDistance)));
}
//If not, then the original 'currentDistance' check was the closest vertice
else
orderedVertices.add(verticesOfImage.removeIndex(0));
//Stop checks, ordered pair fully populated
if(orderedVertices.size == originalArraySize)
break;
}
}
public int getClosest(Array<Vector2> vertices, int checkedIndex, double currentDistance){
//This is used to find the closest vertice, defaulted to -1 for condition statements
int correctIndex = -1;
for(int k = 0; k < vertices.size; k++){
//Make sure to not check same Vector2
if(vertices.get(k).x == orderedVertices.get(checkedIndex).x && vertices.get(k).y == orderedVertices.get(checkedIndex).y){
continue;
}
if(MathFactory.distance(orderedVertices.get(checkedIndex), vertices.get(k)) < currentDistance){
correctIndex = k;
currentDistance = MathFactory.distance(orderedVertices.get(checkedIndex), vertices.get(k));
}
}
return correctIndex;
}
This code comes very close to working, here is an image explaining what is going wrong.
I understand the one that isn't working doesn't look as smooth, but the only real problem is the fact that the lines freak out at the end and go through the body. I can't seem to figure out why this is happening or how to fix it. Any help at all with this is hugely appreciated! Thank you!

Dynamic nested loops with dynamic bounds

I have a LinkedList< Point > points ,with random values:
10,20
15,30
13,43
.
.
I want to perform this kind of loop:
for (int i= points.get(0).x; i< points.get(0).y; i++){
for (int j= points.get(1).x; j< points.get(1).y; j++){
for (int k= points.get(2).x; k< points.get(2).y; k++){
...
...
}
}
}
How can I do that if I don't know the size of the list?
There's probably a better way to solve equations like that with less cpu and memory consumption but a brute-force approach like your's could be implemented via recursion or some helper structure to keep track of the state.
With recursion you could do it like this:
void permutate( List<Point> points, int pointIndex, int[] values ) {
Point p = points.get(pointIndex);
for( int x = p.x; x < p.y; x++ ) {
values[pointIndex] = x;
//this assumes pointIndex to be between 0 and points.size() - 1
if( pointIndex < points.size() - 1 ) {
permutate( points, pointIndex + 1; values );
}
else { //pointIndex is assumed to be equal to points.size() - 1 here
//you have collected all intermediate values so solve the equation
//this is simplified since you'd probably want to collect all values where the result is correct
//as well as pass the equation somehow
int result = solveEquation( values );
}
}
}
//initial call
List<Point> points = ...;
int[] values = new int[points.size()];
permutate( points, 0, values );
This would first iterate over the points list using recursive calls and advancing the point index by one until you reach the end of the list. Each recursive call would iterate over the point values and add the current one to an array at the respective position. This array is then used to calculate the equation result.
Note that this might result in a stack overflow for huge equations (the meaning of "huge" depends on the environment but is normally at several 1000 points). Performance might be really low if you check all permutations in any non-trivial case.

How to determine if position Z is within regions with start X and end Y

I have a JTextArea where the user can create regions using special syntax. I am looking for some assistance in the best way (most efficient using a linear time algorithm) to determine if the current position is within one of the non-overlapping regions.
Let's assume I have the following to determine the user defined regions (I scan the document at the start using regex to determine the regions):
REGION START = 0, END = 20
REGION START = 21, END = 24
REGION START = 34, END = 40
I don't care what region the user is in, I just need to determine if they are in or out of a region, given position X. I could store the regions as an array and loop through the entries until I find one that matches, but this isn't linear time and would take longer if it didn't match a region.
Is there an easier way to do this using an algorithm or storing the data in a certain way?
Actually the algorithm you are proposing is indeed linear. Here is another one, a bit more complicated, but faster:
You need to use a Cumulative Table data structure, like Binary Indexed Tree (BIT). A BIT allows you to implement the following operations with logarithmic complexity:
Update lo, hi, val: add at the indices [lo, hi] the value val
Query x: return the sum at index x
For each region [lo, hi], you call Update(lo, hi, 1), adding 1 to the appropriate positions in the BIT
For each query just check if Query(x) is zero. If yes, then x, does not overlap with a region
About Binary Indexed Trees: http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees
And some code:
public class BIT {
// AddAtPosition: adds at binary indexed tree [bit] the value [v]
// exactly at position [i]. The binary indexed tree has size [size]
public static void AddAtPosition(int [] bit, int size, int i, int v) {
while(i < size) {
bit[i] += v;
i += (i & -i);
}
}
// AddAtInterval: adds at binary indexed tree [bit] the value [v]
// to all position from [lo] to [hi]. The binary indexed tree has size [size]
public static void AddAtInterval(int [] bit, int size, int lo, int hi, int v) {
AddAtPosition(bit, size, lo+1, v);
AddAtPosition(bit, size, hi+2, -v);
}
// QueryAtPosition: returns the value of index [i] at binary indexed tree [bit]
public static int QueryAtPosition(int [] bit, int i) {
int ans = 0;
i++;
while(i > 0) {
ans += bit[i];
i -= (i & -i);
}
return ans;
}
public static void main(String [] args) {
int [] bit = new int[10+1]; // for values from 0-9
AddAtInterval(bit, 11, 0, 5, 1);
AddAtInterval(bit, 11, 4, 7, 1);
for(int i=0; i<=9; ++i) {
System.out.print("Query At position " + i + ": ");
System.out.println(QueryAtPosition(bit, i));
}
}
}
I could store the regions as an array and loop through the entries
until I find one that matches, but this isn't linear time
It is linear.
Assuming that regions are sorted, you could use Binary Search.
Although I like the BIT example, I think I might use a simpler solution which I am hoping doesn't have a huge performance impact compared to a BIT - is there, considering I need the same length of array to start with? I have defined an array in the same way based on the length of my JText Area:
int[] pC = new int[myArea.getText().length()];
I then search for my regions and whenever I find a region I set the appropriate position to 1 in my array:
for (int i = m.start(); i < m.end(); i++) {
pC[i] = 1;
}
I can then do a simple check against position Z using the following syntax:
if (pC[Z] == 0) {
// OUTSIDE REGION
}
else {
// INSIDE REGION
}

Unexpected Java performance

I have just thrown everything I know about Java optimisation out the window. I have the following task:
Given a 2D array representing a playing field and a position on the field, fill another array with the number of steps a player can make to get to every other position in the field. The player can move up, down, left and right. For example, the first neighbours will be all 1's, with the diagonals being all 2's.
For the first attempt, I tried a simple 4-way floodfill algorithm. It wad dreadfully slow.
Secondly, I decided to get rid of the recursion and use a simple Queue. It worked lovely and gave a huge speed-up (very roughly 20x). Here is the code:
private void fillCounterArray(int[] counters, int position) {
Queue<Integer> queue = new ArrayDeque<Integer>(900);
// Obtain the possible destinations from position, check the valid ones
// and add it the stack.
int[] destination = board.getPossibleDestinations(position);
for (int i = 0; i < destination.length; i++) {
if (board.getBoard()[destination[i]] == Board.CLEAR) {
counters[destination[i]] = 1;
queue.add(destination[i]);
}
}
// Now fill up the space.
while (!queue.isEmpty()) {
int pos = queue.remove();
int steps = counters[pos];
destination = board.getPossibleDestinations(pos);
for (int i = 0; i < destination.length; i++) {
int dest = destination[i];
if (board.getBoard()[dest] == Board.CLEAR && (counters[dest] > steps + 1 || counters[dest] == 0)) {
counters[dest] = steps + 1;
queue.add(dest);
}
}
}
}
Now, "common-sense" told me that the queue operations with a static array and an int-pointer would be faster. So I removed the Queue and use a standard int[] array. The code is identical, except for the queue-like operations. It now looks like this (As you can see, I use to live by the C-side :)):
private void fillCounterArray(int[] counters, int position) {
// Array and its pointer.
int[] queue = new int[900]; // max size of field
int head = 0;
// Obtain the possible destinations from position, check the valid ones
// and add it the stack.
int[] destination = board.getPossibleDestinations(position);
for (int i = 0; i < destination.length; i++) {
if (board.getBoard()[destination[i]] == Board.CLEAR) {
counters[destination[i]] = 1;
queue[head++] = dest[i];
}
}
// Now fill up the space.
while (head > 0) {
int pos = queue[--head];
int steps = counters[pos];
destination = board.getPossibleDestinations(pos);
for (int i = 0; i < destination.length; i++) {
int dest = destination[i];
if (board.getBoard()[dest] == Board.CLEAR && (counters[dest] > steps + 1 || counters[dest] == 0)) {
counters[dest] = steps + 1;
queue[head++] = dest;
}
}
}
}
When I ran this "optimised code" it was significantly slower than using the Queue and only about twice as fast as the recursive technique. There is also hardly any difference when I declare the array as an instance variable. How is this possible?
Your reversed the order while optimising I think;
The queue is fifo, first in first out
The array is lifo, last in first out, as you walk it downwards
That will usually give you different performance ;-)
Insert two Counters in each Loop one in the for loop and one in the while loop in both versions, compare the numbers you get at the end, how many rounds do you make in each version, if you have another Loop in getPossibleDestination then log the pos variable too.
I guess that would be a good starting point to figure it out.
Another way would be to print the time difference on different lines in your program, let say before the 1st loop, between both and at the end, once you compare results and know where it takes a long time in the second Version you can print timestamps in the loop on different lines.

Formula to draw pyramid of circles

I'm trying to create a pyramid of circles to my game, looking similar to this :
alt text http://img266.imageshack.us/img266/3094/lab1213c.jpg
But I can't make it print properly. Constantly I'm getting really strange spirals but nothing close to this. Can anyone give me some tip on proper formula ? My window is 600x600, base of pyramid is 8 .
fields = new Field[BASE*(BASE/2)+4];
int line_count = BASE;
int line_tmp = line_count;
for(int i=0; i< fields.length; i++){
for( int j=line_tmp; j <= line_count; j++){
fields[i] = new Field(0, (150+(line_tmp*5)),(600+line_tmp*5));
}
line_count--;
line_tmp = line_count;
}
The mistakes I see are:
Incorrect array size formula.
Including line_tmp (which seems to be your column counter) in your y expression.
Having two variables, line_count and line_temp that are always equal.
Having your outer loop count by node rather than counting by row.
Having generally meaningless variable names and magic numbers strewn about.
// I use java.util.ArrayList because using its add(..) method is convenient here.
// The proper forumula for anticipated number of nodes is: base×(base+1)÷2
final List<Field> fields = new ArrayList<Field>(BASE*(BASE+1)/2);
// I use a java.awt.Point to store the (x,y) value of the first node of the row.
// This clarifies the meaning, rather than using ints or long inline expressions.
final Point rowStart = new Point(PANEL_WIDTH/2, DIAMETER);
// The number of rows equals the number of nodes on the final row.
for (int row = 1; row <= BASE; row++) {
// The nth row has n nodes.
for (int circle = 0; circle < row; circle++) {
// Each row starts at rowStart and each subsequent circle is offset to
// the right by two times the circle diameter.
fields.add(new Field(0, rowStart.x + circle*DIAMETER*2, rowStart.y));
}
// Each subsequent row starts a little down and to the left of the previous.
rowStart.x -= DIAMETER;
rowStart.y += DIAMETER;
}
Remember to only use this as reference for fixing your own code if this is homework.

Categories

Resources