diff --git a/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java index 773652735..6a6cc5962 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java @@ -406,4 +406,356 @@ public class RenderDataContainer } + + private static final ThreadLocal tLocalHeightAndDepth = new ThreadLocal(); + private static final ThreadLocal tDataIndexCache = new ThreadLocal(); + private static final ThreadLocal tMaxVerticalData = new ThreadLocal(); + /** + * This method merge column of multiple data together + */ + // TODO: Make this operate on a out param array, to allow skipping copy array on use + public void mergeMultiData(int mergeInX, int mergeInZ, RenderDataContainer lowerDataContainer, int mergeFromX, int mergeToX, int mergeFromZ, int mergeToZ) + { + int outBaseIndex = mergeInX * SECTION_SIZE * verticalSize + mergeInZ*verticalSize; + int inputVerticalSize = lowerDataContainer.verticalSize; + int inputSectionSize = lowerDataContainer.SECTION_SIZE; + int mergeFromToX; + int xSize = (mergeFromX - mergeToX + 1); + int zSize = (mergeFromZ - mergeToZ + 1); + //size indicate how many position we are merging in one position + + // We initialize the arrays that are going to be used + int heightAndDepthLength = (DataPointUtil.MAX_WORLD_Y_SIZE / 2 + 16) * 2; + short[] heightAndDepth = tLocalHeightAndDepth.get(); + if (heightAndDepth==null || heightAndDepth.length != heightAndDepthLength) { + heightAndDepth = new short[heightAndDepthLength]; + tLocalHeightAndDepth.set(heightAndDepth); + } + int dataPointLength = verticalSize; + long[] dataPoint = tMaxVerticalData.get(); + if (dataPoint==null || dataPoint.length != dataPointLength) { + dataPoint = new long[dataPointLength]; + tMaxVerticalData.set(dataPoint); + } else Arrays.fill(dataPoint, 0); + + int firstIndex = mergeFromX*SECTION_SIZE*inputVerticalSize + mergeFromZ * inputVerticalSize; + byte genMode = DataPointUtil.getGenerationMode(lowerDataContainer.dataContainer[firstIndex]); + if (genMode == 0) genMode = 1; // FIXME: Hack to make the version 10 genMode never be 0. + boolean allEmpty = true; + boolean allVoid = true; + boolean limited = false; + boolean allDefault; + long singleData; + + + short depth; + short height; + int count = 0; + int i; + int ii; + + //We collect the indexes of the data, ordered by the depth + int dataIndex = 0; + int x; + int z; + int y; + for (x = mergeFromX; x <= mergeToX; x++) + { + for (z = mergeFromZ; z <= mergeToZ; z++) + { + if (x == mergeFromX && z == mergeFromZ) + { + for (y = 0; y < inputVerticalSize; y++) + { + dataIndex = x * inputSectionSize * inputVerticalSize + z * inputVerticalSize + y; + singleData = lowerDataContainer.dataContainer[dataIndex]; + if (DataPointUtil.doesItExist(singleData)) + { + //genMode = Math.min(genMode, getGenerationMode(singleData)); + allEmpty = false; + if (!DataPointUtil.isVoid(singleData)) + { + allVoid = false; + count++; + heightAndDepth[dataIndex * 2] = DataPointUtil.getHeight(singleData); + heightAndDepth[dataIndex * 2 + 1] = DataPointUtil.getDepth(singleData); + } + } + else + break; + } + } + else + { + for (y = 0; y < inputVerticalSize; y++) + { + dataIndex = x * inputSectionSize * inputVerticalSize + z * inputVerticalSize + y; + singleData = lowerDataContainer.dataContainer[dataIndex]; + if (DataPointUtil.doesItExist(singleData)) + { + //genMode = Math.min(genMode, getGenerationMode(singleData)); + allEmpty = false; + if (!DataPointUtil.isVoid(singleData)) + { + allVoid = false; + depth = DataPointUtil.getDepth(singleData); + height = DataPointUtil.getHeight(singleData); + + int botPos = -1; + int topPos = -1; + //values fall in between and possibly require extension of array + boolean botExtend = false; + boolean topExtend = false; + for (i = 0; i < count; i++) + { + if (depth < heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1]) + { + botPos = i; + break; + } + else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth >= heightAndDepth[(i + 1) * 2]) || i + 1 == count)) + { + botPos = i; + botExtend = true; + break; + } + } + for (i = 0; i < count; i++) + { + if (height <= heightAndDepth[i * 2] && height > heightAndDepth[i * 2 + 1]) + { + topPos = i; + break; + } + else if (height <= heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count)) + { + topPos = i; + topExtend = true; + break; + } + } + if (topPos == -1) + { + if (botPos == -1) + { + //whole block falls above + DataPointUtil.extendArray(heightAndDepth, 2, 0, 1, count); + heightAndDepth[0] = height; + heightAndDepth[1] = depth; + count++; + } + else if (!botExtend) + { + //only top falls above extending it there, while bottom is inside existing + DataPointUtil.shrinkArray(heightAndDepth, 2, 0, botPos, count); + heightAndDepth[0] = height; + count -= botPos; + } + else + { + //top falls between some blocks, extending those as well + DataPointUtil.shrinkArray(heightAndDepth, 2, 0, botPos, count); + heightAndDepth[0] = height; + heightAndDepth[1] = depth; + count -= botPos; + } + } + else if (!topExtend) + { + if (!botExtend) + //both top and bottom are within some exiting blocks, possibly merging them + heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1]; + else + //top falls between some blocks, extending it there + heightAndDepth[topPos * 2 + 1] = depth; + DataPointUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); + count -= botPos - topPos; + } + else + { + if (!botExtend) + { + //only top is within some exiting block, extending it + topPos++; //to make it easier + heightAndDepth[topPos * 2] = height; + heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1]; + DataPointUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); + count -= botPos - topPos; + } + else + { + //both top and bottom are outside existing blocks + DataPointUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); + count -= botPos - topPos; + DataPointUtil.extendArray(heightAndDepth, 2, topPos + 1, 1, count); + count++; + heightAndDepth[topPos * 2 + 2] = height; + heightAndDepth[topPos * 2 + 3] = depth; + } + } + } + } + else + break; + } + } + } + } + //We check if there is any data that's not empty or void + if (allEmpty) + return; + if (allVoid) + { + dataPoint[outBaseIndex] = DataPointUtil.createVoidDataPoint(genMode); + return; + } + + //we limit the vertical portion to maxVerticalData + int j = 0; + while (count > verticalSize) + { + limited = true; + ii = DataPointUtil.MAX_WORLD_Y_SIZE; + for (i = 0; i < count - 1; i++) + { + if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii) + { + ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2]; + j = i; + } + } + heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1]; + for (i = j + 1; i < count - 1; i++) + { + heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2]; + heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1]; + } + //System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1); + count--; + } + int yOut; + //As standard the vertical lods are ordered from top to bottom + if (!limited && xSize*zSize == 1) + { + for (yOut = 0; yOut < count; yOut++) + dataIndex = mergeFromX*inputSectionSize*inputVerticalSize + mergeFromZ*inputVerticalSize + yOut; + dataPoint[outBaseIndex + yOut] = lowerDataContainer.dataContainer[dataIndex]; + } + else + { + + //We want to efficiently memorize indexes + int[] dataIndexesCache = tDataIndexCache.get(); + if (dataIndexesCache==null || dataIndexesCache.length != xSize*zSize) { + dataIndexesCache = new int[xSize*zSize]; + tDataIndexCache.set(dataIndexesCache); + } + Arrays.fill(dataIndexesCache,0); + + //For each lod height-depth value we have found we now want to generate the rest of the data + //by merging all lods at lower level that are contained inside the new ones + for (yOut = 0; yOut < count; yOut++) + { + //We firstly collect height and depth data + //this will be added to each realtive long DataPoint + height = heightAndDepth[yOut * 2]; + depth = heightAndDepth[yOut * 2 + 1]; + + //if both height and depth are at 0 then we finished + if ((depth == 0 && height == 0) || yOut >= heightAndDepth.length / 2) + break; + + //We initialize data useful for the merge + int numberOfChildren = 0; + allEmpty = true; + allVoid = true; + + //We initialize all the new values that we are going to put in the dataPoint + int tempAlpha = 0; + int tempRed = 0; + int tempGreen = 0; + int tempBlue = 0; + int tempLightBlock = 0; + int tempLightSky = 0; + long data = 0; + + int index; + + //For each position that we want to merge + for(x = mergeFromX; x <= mergeToX; x++) + { + for (z = mergeFromZ; z <= mergeToZ; z++) + { + index = x * xSize + z; + //we scan the lods in the position from top to bottom + while (dataIndexesCache[index] < inputVerticalSize) + { + y = dataIndexesCache[index]; + dataIndex = x * inputSectionSize * inputVerticalSize + z * inputVerticalSize + y; + + singleData = lowerDataContainer.dataContainer[dataIndex]; + if (DataPointUtil.doesItExist(singleData) && !DataPointUtil.isVoid(singleData)) + { + dataIndexesCache[index]++; + if ((depth <= DataPointUtil.getDepth(singleData) && DataPointUtil.getDepth(singleData) < height) + || (depth < DataPointUtil.getHeight(singleData) && DataPointUtil.getHeight(singleData) <= height)) + { + data = singleData; + break; + } + } + else + break; + } + if (!DataPointUtil.doesItExist(data)) + { + data = DataPointUtil.createVoidDataPoint(genMode); + } + + if (DataPointUtil.doesItExist(data)) + { + allEmpty = false; + if (!DataPointUtil.isVoid(data)) + { + numberOfChildren++; + allVoid = false; + tempAlpha = Math.max(DataPointUtil.getAlpha(data), tempAlpha); + tempRed += DataPointUtil.getRed(data) * DataPointUtil.getRed(data); + tempGreen += DataPointUtil.getGreen(data) * DataPointUtil.getGreen(data); + tempBlue += DataPointUtil.getBlue(data) * DataPointUtil.getBlue(data); + tempLightBlock += DataPointUtil.getLightBlock(data); + tempLightSky += DataPointUtil.getLightSky(data); + } + } + } + } + + if (allEmpty) + //no child has been initialized + dataPoint[outBaseIndex + yOut] = DataPointUtil.EMPTY_DATA; + else if (allVoid) + //all the children are void + dataPoint[outBaseIndex + yOut] = DataPointUtil.createVoidDataPoint(genMode); + else + { + //we have at least 1 child + if (xSize*zSize != 1) + { + tempRed = tempRed / numberOfChildren; + tempGreen = tempGreen / numberOfChildren; + tempBlue = tempBlue / numberOfChildren; + tempLightBlock = tempLightBlock / numberOfChildren; + tempLightSky = tempLightSky / numberOfChildren; + } + //data = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault); + //if (j > 0 && getColor(data) == getColor(dataPoint[j])) + //{ + // add simplification at the end due to color + //} + dataPoint[outBaseIndex + yOut] = DataPointUtil.createDataPoint((int) Math.sqrt(tempAlpha), (int) Math.sqrt(tempRed), (int) Math.sqrt(tempGreen), (int) Math.sqrt(tempBlue), height, depth, tempLightSky, tempLightBlock, genMode); + } + } + } + } + }