在Java中将3D int数组转换为八叉树

时间:2015-03-31 05:21:41

标签: java arrays optimization lwjgl octree

我正在使用LWJGL在Java中创建一个体素引擎,仅仅是为了练习,但我却陷入了块管理系统的困境。更具体地说,我正在尝试将块(仅仅是块ID的整数3D数组)转换为八叉树以实现最佳渲染。

到目前为止,我的系统正在运行,但效率非常低。

这是一个16 * 16 * 16块的屏幕截图,所有位置都低于y = 8设置为1(红色块)

https://raw.githubusercontent.com/ninthworld/Octree/master/screenshot0.png

我在OctNode生成器代码中添加了一个调试器,以找出访问块数组所需的次数,然后返回 8392704

它访问块数组超过800万次,只是为了生成8个子节点。

当我将块数组设置为只有y = 4以下的块时,程序有一个黑屏几乎30秒,调试器返回 1623199744 数组访问。

超过16亿个数组调用只是为了生成68个子节点。

我显然需要减少数组调用的数量,这是肯定的,但我不确定如何去做。 Here's the github page for the project if you'd like to see the entire source 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;
    }
}

不幸的是,这是我能解释的最好的。如果你能指出一种更容易,更快速的方法来做同样令人惊奇的事情。

1 个答案:

答案 0 :(得分:1)

您过于频繁地对树进行分区。如果你有一个16x16x16数组,其中包含所有不同的值,那么除了第一个单元格之外,你将在每个单元格中进行递归,因此你将在顶层调用generate(16x16x16-1)次,而不只是一次; children数组的值将被覆盖多次;当然,你会在下一级重复做不必要的工作等。

您应该决定将当前八叉树节点细分为嵌套for循环。例如:

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;
    }
}