Compare commits

..

43 Commits

Author SHA1 Message Date
cola98765 2834a73551 fix fog for fixed render distance 2021-12-10 20:06:22 +01:00
cola98765 5a7af89f5c fix calculation of view range. if it was k*32+x where k is int and 0<=x<16. To compare with old versions add 32 2021-12-10 20:06:14 +01:00
cola98765 eff491127b changed debug key to F8 and added chat messages when using debug keys. 2021-12-10 20:06:04 +01:00
Morippi 61502c32a6 fixewd generation not working 2021-12-10 19:18:22 +01:00
Morippi b7911ec4c8 Removed most of BlockPos and ChunkPos use 2021-12-10 19:05:27 +01:00
cola98765 1115c5f6c5 moved merge works now 19min on my PC 2021-12-10 09:28:10 +01:00
Morippi 5d001bf7ea changed some parameters 2021-12-09 20:26:19 +01:00
Morippi 4e732f7916 added a parameter 2021-12-09 20:25:55 +01:00
Morippi 30d392f861 Added field name and methods name for the merge 2021-12-09 20:25:01 +01:00
cola98765 f8910b0c3b move the merge 2021-12-09 19:59:50 +01:00
Morippi da4f423d10 Added maps to cache the biome blocks couples 2021-12-09 17:30:43 +01:00
Morippi 5a4c04b5a3 added getters and creators in the new format. 2021-12-09 17:30:18 +01:00
cola98765 cd50be6531 fix FAR_FIRST and AUTO options 2021-12-09 15:20:31 +01:00
cola98765 5d4698621c buffer rebuild due to time is no longer needed 2021-12-09 14:36:24 +01:00
Morippi aba392ace4 Fixed another small bug 2021-12-09 14:31:25 +01:00
Morippi d1c0ea123a Merge remote-tracking branch 'origin/main' into main 2021-12-09 14:30:12 +01:00
Morippi 816efb2837 Fixed a small bug 2021-12-09 14:30:06 +01:00
cola98765 0221a39819 wrong "-" sign 2021-12-09 14:20:30 +01:00
Morippi cc08707f32 Fixed some part of the new format 2021-12-09 13:50:59 +01:00
Morippi 8db253f886 Merge remote-tracking branch 'origin/main' into main 2021-12-09 13:50:48 +01:00
Morippi f59bdf15d7 Created the BlockBiomeCouple 2021-12-09 13:50:37 +01:00
cola98765 ac0d439b3f Merge remote-tracking branch 'origin/main' into main 2021-12-09 13:32:41 +01:00
cola98765 4e652c7573 just switch B and R and should be fine 2021-12-09 13:31:56 +01:00
Morippi 3686bfb4e0 switched two formats 2021-12-09 12:58:52 +01:00
Morippi fe1b2c2683 Added new format classes 2021-12-09 12:57:35 +01:00
cola98765 d5ed9a22fa more consistent skylight-blocklight order 2021-12-09 11:59:37 +01:00
cola98765 e3f9c974f8 partially fix shader based light 2021-12-09 11:57:05 +01:00
cola98765 daead98102 move where in region pos is calculated 2021-12-09 09:49:57 +01:00
James Seibel 5c31927d54 Add shaders files for shader based lighting (ops) 2021-12-08 23:07:10 -06:00
James Seibel f9fa1a5260 merge GLProxy 2021-12-08 23:05:14 -06:00
James Seibel d0472ee56d Add (buggy, unfinished) shader based lighting 2021-12-08 22:59:37 -06:00
Ran e4e21d2dc8 Merge branch 'main' into 'main'
Fix for WindowOS Render null Error

See merge request jeseibel/distant-horizons-core!3
2021-12-08 14:18:05 +00:00
TomTheFurry 79e4dce569 Fix for WindowOS Render null Error 2021-12-08 14:18:04 +00:00
cola98765 408f09c0f4 added and commented out time testing code 2021-12-07 13:05:34 +01:00
cola98765 65bfedc942 Merge remote-tracking branch 'origin/main' into main 2021-12-06 10:21:33 +01:00
cola98765 fab99cd4ec fix LOWEST HorizontalQuality 2021-12-06 10:21:15 +01:00
cola98765 019f4b7c1e fix LOWEST HorizontalQuality 2021-12-06 10:16:51 +01:00
cola98765 f3b6b15bcb reverted DrawResolutionOffset 2021-12-05 20:03:17 +01:00
cola98765 7c086cdc40 HorizontalScale is now a number 2-32 2021-12-05 17:03:38 +01:00
cola98765 5617e1312c updated DYNAMIC VanillaOverdraw setting. 2021-12-05 16:43:21 +01:00
cola98765 9dd51bfdde added DrawResolutionOffset to work with DrawResolution 2021-12-05 16:37:32 +01:00
James Seibel c9dc998eef Fix a few buffer building issues 2021-12-04 22:22:00 -06:00
Morippi 068df9d5e0 Change Box to VertexOptimizer and added the DataFormat folder with empty classes 2021-12-02 23:07:08 +01:00
41 changed files with 2019 additions and 1050 deletions
@@ -40,7 +40,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
* Specifically for the client.
*
* @author James Seibel
* @version 11-12-2021
* @version 12-8-2021
*/
public class ClientApi
{
@@ -141,6 +141,9 @@ public class ClientApi
{
MC.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION);
MC.sendChatMessage("You are running a unsupported version of the mod!");
MC.sendChatMessage("==========================================");
MC.sendChatMessage("SEIZURE WARNING: Flashing lights expected!"); // remove this line when the lighting shaders are fixed
MC.sendChatMessage("==========================================");
MC.sendChatMessage("Here be dragons!");
configOverrideReminderPrinted = true;
@@ -184,14 +184,16 @@ public class EventApi
{
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled())
{
if (key == GLFW.GLFW_KEY_F4 && keyAction == GLFW.GLFW_PRESS)
if (key == GLFW.GLFW_KEY_F8 && keyAction == GLFW.GLFW_PRESS)
{
CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
MC.sendChatMessage("F8: Set debug mode " + CONFIG.client().advanced().debugging().getDebugMode());
}
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS)
{
CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
MC.sendChatMessage("F6: Set rendering " + CONFIG.client().advanced().debugging().getDrawLods());
}
}
}
@@ -222,7 +224,7 @@ public class EventApi
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
// make sure we have an odd number of regions
newWidth += (newWidth & 1) == 0 ? 1 : 2;
newWidth += (newWidth & 1) == 0 ? 1 : 0;
// do the dimensions need to change in size?
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
@@ -30,7 +30,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.lod.core.objects.VertexOptimizer;
import com.seibel.lod.core.enums.config.LodTemplate;
import net.minecraft.util.math.BlockPos;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
@@ -69,7 +70,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
* rendered by the LodRenderer.
*
* @author James Seibel
* @version 11-29-2021
* @version 12-8-2021
*/
public class LodBufferBuilderFactory
{
@@ -145,14 +146,17 @@ public class LodBufferBuilderFactory
private volatile VertexOptimizer[][] vertexOptimizerCache;
private volatile PosToRenderContainer[][] setsToRender;
private volatile RegionPos center;
private volatile int centerRegionX = 0;
private volatile int centerRegionZ = 0;
/**
* This is the ChunkPosWrapper the player was at the last time the buffers were built.
* IE the center of the buffers last time they were built
*/
private volatile AbstractChunkPosWrapper drawableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
private volatile AbstractChunkPosWrapper buildableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
private volatile int drawableCenterChunkPosX = 0;
private volatile int drawableCenterChunkPosZ = 0;
private volatile int buildableCenterChunkPosX = 0;
private volatile int buildableCenterChunkPosZ = 0;
@@ -174,7 +178,7 @@ public class LodBufferBuilderFactory
* swapped with the drawable buffers in the LodRenderer to be drawn.
*/
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
AbstractBlockPosWrapper playerBlockPos, boolean fullRegen)
int playerX, int playerY, int playerZ, boolean fullRegen)
{
// only allow one generation process to happen at a time
@@ -192,7 +196,7 @@ public class LodBufferBuilderFactory
generatingBuffers = true;
Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen));
Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerX, playerY, playerZ, fullRegen));
mainGenThread.execute(thread);
}
@@ -201,15 +205,17 @@ public class LodBufferBuilderFactory
// more easily edited by hot swapping. Because, As far as James is aware
// you can't hot swap lambda expressions.
private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim,
AbstractBlockPosWrapper playerBlockPos, boolean fullRegen)
int playerX, int playerY, int playerZ, boolean fullRegen)
{
bufferLock.lock();
try
{
// round the player's block position down to the nearest chunk BlockPos
AbstractChunkPosWrapper playerChunkPos = WRAPPER_FACTORY.createChunkPos(playerBlockPos);
AbstractBlockPosWrapper playerBlockPosRounded = playerChunkPos.getWorldPosition();
int playerChunkX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerX,LodUtil.CHUNK_DETAIL_LEVEL);
int playerChunkZ = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerZ,LodUtil.CHUNK_DETAIL_LEVEL);
int playerRegionX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerX,LodUtil.REGION_DETAIL_LEVEL);
int playerRegionZ = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL,playerZ,LodUtil.REGION_DETAIL_LEVEL);
//long startTime = System.currentTimeMillis();
@@ -218,11 +224,6 @@ public class LodBufferBuilderFactory
startBuffers(fullRegen, lodDim);
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
if (center == null)
center = playerRegionPos;
if (setsToRender == null)
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
@@ -236,14 +237,15 @@ public class LodBufferBuilderFactory
vertexOptimizerCache = new VertexOptimizer[lodDim.getWidth()][lodDim.getWidth()];
// this will be the center of the VBOs once they have been built
buildableCenterChunkPos = playerChunkPos;
buildableCenterChunkPosX = playerChunkX;
buildableCenterChunkPosZ = playerChunkZ;
//================================//
// create the nodeToRenderThreads //
//================================//
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerBlockPos);
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerX, playerY, playerZ);
for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++)
{
@@ -295,9 +297,7 @@ public class LodBufferBuilderFactory
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte) 0);
//we get or create the map that will contain the adj data
Map<LodDirection, int[]> adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData);
Map<LodDirection, byte[]> adjFlags = ThreadMapUtil.getAdjFlagsArray(maxVerticalData);
Map<LodDirection, long[]> adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData);
//previous setToRender cache
if (setsToRender[xR][zR] == null)
@@ -311,8 +311,8 @@ public class LodBufferBuilderFactory
lodDim.getPosToRender(
posToRender,
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
playerX,
playerZ);
@@ -330,14 +330,14 @@ public class LodBufferBuilderFactory
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkX;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkZ;
//We don't want to render this fake block if
//The block is inside the render distance with, is not bigger than a chunk and is positioned in a chunk set as vanilla rendered
//
//The block is in the player chunk or in a chunk adjacent to the player
if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance))
if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkX, playerChunkZ, vanillaRenderedChunks, gameChunkRenderDistance))
{
continue;
}
@@ -357,11 +357,9 @@ public class LodBufferBuilderFactory
xAdj = posX + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x;
zAdj = posZ + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z;
int color;
int data;
byte flags;
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.getX();
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.getZ();
long data;
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkX;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkZ;
adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0);
//If the adj block is rendered in the same region and with same detail
@@ -370,29 +368,25 @@ public class LodBufferBuilderFactory
// We avoid cases where the adjPosition is in player chunk while the position is not
// to always have a wall underwater
if(posToRender.contains(detailLevel, xAdj, zAdj)
&& !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance)
&& !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkX, playerChunkZ, vanillaRenderedChunks, gameChunkRenderDistance)
&& !(posNotInPlayerChunk && adjPosInPlayerChunk))
{
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++)
{
data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex);
flags = lodDim.getFlags(detailLevel, xAdj, zAdj, verticalIndex);
adjShadeDisabled[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = false;
adjData.get(lodDirection)[verticalIndex] = data;
adjFlags.get(lodDirection)[verticalIndex] = flags;
}
}
else
{
//Otherwise, we check if this position is
data = lodDim.getData(detailLevel, xAdj, zAdj, 0);
flags = lodDim.getFlags(detailLevel, xAdj, zAdj, 0);
data = lodDim.getSingleData(detailLevel, xAdj, zAdj);
adjData.get(lodDirection)[0] = 0;
adjFlags.get(lodDirection)[0] = 0;
adjData.get(lodDirection)[0] = DataPointUtil.EMPTY_DATA;
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
&& DataPointUtil.doesItExist(flags) && !DataPointUtil.isVoid(flags))
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkX, playerChunkZ, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
&& !DataPointUtil.isVoid(data))
{
adjShadeDisabled[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = DataPointUtil.getAlpha(data) < 255;
}
@@ -402,47 +396,32 @@ public class LodBufferBuilderFactory
// We render every vertical lod present in this position
// We only stop when we find a block that is void or non-existing block
int color;
int data;
byte flags;
long data;
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
{
//we get the above block as adj UP
if (verticalIndex > 0)
{
adjData.get(LodDirection.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1);
adjFlags.get(LodDirection.UP)[0] = lodDim.getFlags(detailLevel, posX, posZ, verticalIndex - 1);
}
else
{
adjData.get(LodDirection.UP)[0] = 0;
adjFlags.get(LodDirection.UP)[0] = 0;
}
adjData.get(LodDirection.UP)[0] = DataPointUtil.EMPTY_DATA;
//we get the below block as adj DOWN
if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1)
{
adjData.get(LodDirection.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1);
adjFlags.get(LodDirection.DOWN)[0] = lodDim.getFlags(detailLevel, posX, posZ, verticalIndex + 1);
}
else
{
adjData.get(LodDirection.DOWN)[0] = 0;
adjFlags.get(LodDirection.DOWN)[0] = 0;
}
adjData.get(LodDirection.DOWN)[0] = DataPointUtil.EMPTY_DATA;
//We extract the data to render
color = lodDim.getColor(detailLevel, posX, posZ, verticalIndex);
data = lodDim.getData(detailLevel, posX, posZ, verticalIndex);
flags = lodDim.getFlags(detailLevel, posX, posZ, verticalIndex);
//If the data is not renderable (Void or non-existing) we stop since there is no data left in this position
if (!DataPointUtil.doesItExist(flags) || DataPointUtil.isVoid(flags))
if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data))
break;
//We send the call to create the vertices
CONFIG.client().graphics().advancedGraphics().getLodTemplate().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, color, data, flags, adjData, adjFlags,
LodTemplate.CUBIC.template.addLodToBuffer(currentBuffers[bufferIndex], playerX, playerY, playerZ, data, adjData,
detailLevel, posX, posZ, vertexOptimizer, renderer.previousDebugMode, adjShadeDisabled);
}
@@ -450,7 +429,9 @@ public class LodBufferBuilderFactory
// the thread executed successfully
return true;
};
nodeToRenderThreads.add(dataToRenderThread);
}
} // region z
} // region z
@@ -510,12 +491,12 @@ public class LodBufferBuilderFactory
}
}
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, AbstractChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, int chunkPosX, int chunkPosZ, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
// skip any chunks that Minecraft is going to render
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - chunkPosX;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - chunkPosZ;
// check if the chunk is on the border
boolean isItBorderPos;
@@ -585,7 +566,7 @@ public class LodBufferBuilderFactory
{
regionMemoryRequired = DEFAULT_MEMORY_ALLOCATION;
// if the memory required is greater than the max buffer
// if the memory required is greater than the max buffer
// capacity, divide the memory across multiple buffers
if (regionMemoryRequired > LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY)
{
@@ -650,7 +631,7 @@ public class LodBufferBuilderFactory
drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
}
@@ -901,7 +882,7 @@ public class LodBufferBuilderFactory
if (vbo.id != -1 && GLProxy.getInstance().getGlContext() == GLProxyContext.LOD_BUILDER)
{
// this is how many points will be rendered
vbo.vertexCount = (uploadBuffer.capacity() / ((Float.BYTES * 3) + (Byte.BYTES * 4))); // TODO make this change with the LodTemplate
vbo.vertexCount = (uploadBuffer.capacity() / ((Float.BYTES * 3) + (Byte.BYTES * 4) + Byte.BYTES + Byte.BYTES)); // TODO make this change with the LodTemplate
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
try
@@ -1038,7 +1019,8 @@ public class LodBufferBuilderFactory
drawableStorageBufferIds = buildableStorageBufferIds;
buildableStorageBufferIds = tmpStorage;
drawableCenterChunkPos = buildableCenterChunkPos;
drawableCenterChunkPosX = buildableCenterChunkPosX;
drawableCenterChunkPosZ = buildableCenterChunkPosZ;
// the vbos have been swapped
switchVbos = false;
@@ -1054,7 +1036,7 @@ public class LodBufferBuilderFactory
}
}
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPos);
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPosX, drawableCenterChunkPosZ);
}
/** A simple container to pass multiple objects back in the getVertexBuffers method. */
@@ -1062,13 +1044,15 @@ public class LodBufferBuilderFactory
{
public final LodVertexBuffer[][][] vbos;
public final int[][][] storageBufferIds;
public final AbstractChunkPosWrapper drawableCenterChunkPos;
public int drawableCenterChunkPosX;
public int drawableCenterChunkPosZ;
public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, AbstractChunkPosWrapper newDrawableCenterChunkPos)
public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, int newDrawableCenterChunkPosX, int newDrawableCenterChunkPosZ)
{
vbos = newVbos;
storageBufferIds = newStorageBufferIds;
drawableCenterChunkPos = newDrawableCenterChunkPos;
drawableCenterChunkPosX = newDrawableCenterChunkPosX;
drawableCenterChunkPosZ = newDrawableCenterChunkPosZ;
}
}
@@ -32,21 +32,24 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
* This is the abstract class used to create different
* BufferBuilders.
* @author James Seibel
* @version 11-13-2021
* @version 12-8-2021
*/
public abstract class AbstractLodTemplate
{
/** Uploads the given LOD to the buffer. */
public abstract void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, int color, int data, byte flags, Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
public abstract void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled);
/** add the given position and color to the buffer */
protected void addPosAndColor(LodBufferBuilder buffer,
float x, float y, float z,
int color)
int color, byte skyLightValue, byte blockLightValue)
{
// TODO re-add transparency by replacing the 255 with "ColorUtil.getAlpha(color)"
buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255).endVertex();
// TODO re-add transparency by replacing the color 255 with "ColorUtil.getAlpha(color)"
buffer.position(x, y, z)
.color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255)
.minecraftLightValue(skyLightValue).minecraftLightValue(blockLightValue)
.endVertex();
}
}
@@ -25,15 +25,13 @@ import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.objects.VertexOptimizer;
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
/**
* Builds LODs as rectangular prisms.
* @author James Seibel
* @version 11-8-2021
* @version 12-8-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
@@ -44,9 +42,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
}
@Override
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos,
int color, int data, byte flags,
Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
public void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
if (vertexOptimizer == null)
@@ -55,8 +51,12 @@ public class CubicLodTemplate extends AbstractLodTemplate
// equivalent to 2^detailLevel
int blockWidth = 1 << detailLevel;
int color;
if (debugging != DebugMode.OFF)
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
else
color = DataPointUtil.getColor(data);
generateBoundingBox(
vertexOptimizer,
@@ -64,11 +64,12 @@ public class CubicLodTemplate extends AbstractLodTemplate
DataPointUtil.getDepth(data),
blockWidth,
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
bufferCenterBlockPos,
playerX,
playerY,
playerZ,
adjData,
adjFlags,
color,
DataPointUtil.getLightSkyAlt(data, flags),
DataPointUtil.getLightSkyAlt(data),
DataPointUtil.getLightBlock(data),
adjShadeDisabled);
@@ -78,12 +79,9 @@ public class CubicLodTemplate extends AbstractLodTemplate
private void generateBoundingBox(VertexOptimizer vertexOptimizer,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
AbstractBlockPosWrapper bufferCenterBlockPos,
Map<LodDirection, int[]> adjData,
Map<LodDirection, byte[]> adjFlags,
int color,
int skyLight,
int blockLight,
int playerX, int playerY, int playerZ,
Map<LodDirection, long[]> adjData,
int color, byte skyLight, byte blockLight,
boolean[] adjShadeDisabled)
{
// don't add an LOD if it is empty
@@ -98,41 +96,36 @@ public class CubicLodTemplate extends AbstractLodTemplate
// offset the AABB by its x/z position in the world since
// it uses doubles to specify its location, unlike the model view matrix
// which only uses floats
double x = -bufferCenterBlockPos.getX();
double z = -bufferCenterBlockPos.getZ();
double x = -playerX;
double z = -playerZ;
vertexOptimizer.reset();
vertexOptimizer.setColor(color, adjShadeDisabled);
vertexOptimizer.setLights(skyLight, blockLight);
vertexOptimizer.setWidth(width, height - depth, width);
vertexOptimizer.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
vertexOptimizer.setUpCulling(32, bufferCenterBlockPos);
vertexOptimizer.setAdjData(adjData, adjFlags);
vertexOptimizer.setAdjData(adjData);
}
private void addBoundingBoxToBuffer(LodBufferBuilder buffer, VertexOptimizer vertexOptimizer)
{
int color;
int skyLight;
int blockLight;
byte skyLight;
byte blockLight;
for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS)
{
if(vertexOptimizer.isCulled(lodDirection))
continue;
int verticalFaceIndex = 0;
while (vertexOptimizer.shouldRenderFace(lodDirection, verticalFaceIndex))
{
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
{
color = vertexOptimizer.getColor(lodDirection);
skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex);
blockLight = vertexOptimizer.getBlockLight();
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
blockLight = (byte) vertexOptimizer.getBlockLight();
color = vertexOptimizer.getColor(lodDirection);
addPosAndColor(buffer,
vertexOptimizer.getX(lodDirection, vertexIndex),
vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + DataPointUtil.VERTICAL_OFFSET,
vertexOptimizer.getZ(lodDirection, vertexIndex),
color);
color, skyLight, blockLight );
}
verticalFaceIndex++;
}
@@ -39,8 +39,7 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, int color, int data, byte flags,
Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
public void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
@@ -37,8 +37,7 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, int color, int data, byte flags,
Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags,
public void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerY, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
@@ -162,6 +162,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
public void generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config)
throws IllegalArgumentException
{
//long executeTime = System.currentTimeMillis();
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
@@ -169,7 +170,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
int startZ;
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
LodRegion region = lodDim.getRegion(chunk.getRegionPosX(), chunk.getRegionPosZ());
if (region == null)
return;
@@ -195,119 +196,104 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
startX = detail.startX[i];
startZ = detail.startZ[i];
// creates a vertical DataPoint
// equivalent to 2^detailLevel
int size = 1 << detail.detailLevel;
long[] data;
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
posX = LevelPosUtil.convert((byte) 0, chunk.getChunkPosX() * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getChunkPosZ() * 16 + startZ, detail.detailLevel);
lodDim.mergeMultiData(detailLevel, posX, posZ, false, dataToMergeVertical, DataPointUtil.WORLD_HEIGHT / 2 + 1);
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getChunkPosX(), chunk.getChunkPosZ());
//executeTime = System.currentTimeMillis() - executeTime;
//if (executeTime > 0) ClientApi.LOGGER.info("generateLodNodeFromChunk level: " + detailLevel + " time ms: " + executeTime);
}
/** creates a vertical DataPoint */
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ)
{
// equivalent to 2^detailLevel
int size = 1 << detail.detailLevel;
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
int verticalData = DataPointUtil.WORLD_HEIGHT / 2 + 1;
int height;
int depth;
int color;
int light;
int lightSky;
int lightBlock;
int generation = config.distanceGenerationMode.complexity;
int xRel;
int zRel;
int xAbs;
int yAbs;
int zAbs;
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
boolean isDefault;
int index;
for (index = 0; index < size * size; index++)
{
xRel = startX + index % size;
zRel = startZ + index / size;
xAbs = chunk.getMinX() + xRel;
zAbs = chunk.getMinZ() + zRel;
int[] dataToMergeColor = ThreadMapUtil.getBuilderVerticalArrayColor(detail.detailLevel);
int[] dataToMergeData = ThreadMapUtil.getBuilderVerticalArrayData(detail.detailLevel);
byte[] dataToMergeFlags = ThreadMapUtil.getBuilderVerticalArrayFlags(detail.detailLevel);
int verticalData = DataPointUtil.WORLD_HEIGHT / 2 + 1;
AbstractChunkPosWrapper chunkPos = chunk.getPos();
int height;
int depth;
int color;
int light;
int lightSky;
int lightBlock;
byte generation = config.distanceGenerationMode.complexity;
int xRel;
int zRel;
int xAbs;
int yAbs;
int zAbs;
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
boolean isDefault;
AbstractBlockPosWrapper blockPos = FACTORY.createBlockPos();
int index;
for (index = 0; index < size * size; index++)
//Calculate the height of the lod
yAbs = chunk.getMaxY(xRel, zRel); //DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET + 1;
int count = 0;
boolean topBlock = true;
while (yAbs > 0)
{
xRel = startX + index % size;
zRel = startZ + index / size;
xAbs = chunkPos.getMinBlockX() + xRel;
zAbs = chunkPos.getMinBlockZ() + zRel;
height = determineHeightPointFrom(chunk, config, xAbs, yAbs, zAbs);
//Calculate the height of the lod
yAbs = DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET + 1;
int count = 0;
boolean topBlock = true;
while (yAbs > 0)
// If the lod is at the default height, it must be void data
if (height == DEFAULT_HEIGHT)
{
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
// If the lod is at the default height, it must be void data
if (height == DEFAULT_HEIGHT)
{
if (topBlock)
dataToMergeFlags[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
break;
}
yAbs = height - 1;
// We search light on above air block
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
if (hasCeiling && topBlock)
{
yAbs = depth;
blockPos.set(xAbs, yAbs, zAbs);
light = getLightValue(chunk, blockPos, true, hasSkyLight, true);
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, blockPos);
blockPos.set(xAbs, yAbs - 1, zAbs);
}
else
{
blockPos.set(xAbs, yAbs, zAbs);
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
blockPos.set(xAbs, yAbs + 1, zAbs);
}
lightBlock = light & 0b1111;
lightSky = (light >> 4) & 0b1111;
isDefault = ((light >> 8)) == 1;
DataPointUtil.createDataPoint(height - DataPointUtil.VERTICAL_OFFSET, depth - DataPointUtil.VERTICAL_OFFSET, color, lightSky, lightBlock, generation, isDefault);
dataToMergeColor[index * verticalData + count] = ThreadMapUtil.dataPointColor;
dataToMergeData[index * verticalData + count] = ThreadMapUtil.dataPointData;
dataToMergeFlags[index * verticalData + count] = ThreadMapUtil.dataPointFlags;
topBlock = false;
yAbs = depth - 1;
count++;
if (topBlock)
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
break;
}
}
DataPointUtil.mergeMultiData(dataToMergeColor, dataToMergeData, dataToMergeFlags, DataPointUtil.WORLD_HEIGHT / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
int[] mergedColor = ThreadMapUtil.getRawVerticalDataArrayColor();
int[] mergedData = ThreadMapUtil.getRawVerticalDataArrayData();
byte[] mergedFlags = ThreadMapUtil.getRawVerticalDataArrayFlags();
//lodDim.clear(detailLevel, posX, posZ);
if (mergedFlags.length != 0)
{
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().getX() * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().getZ() * 16 + startZ, detail.detailLevel);
lodDim.addVerticalData(detailLevel, posX, posZ, mergedColor, mergedData, mergedFlags, false);
yAbs = height - 1;
// We search light on above air block
depth = determineBottomPointFrom(chunk, config, xAbs, yAbs, zAbs);
if (hasCeiling && topBlock)
{
yAbs = depth;
light = getLightValue(chunk, xAbs,yAbs,zAbs, true, hasSkyLight, true);
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs);
}
else
{
light = getLightValue(chunk, xAbs, yAbs, zAbs, hasCeiling, hasSkyLight, topBlock);
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs);
}
lightBlock = light & 0b1111;
lightSky = (light >> 4) & 0b1111;
isDefault = ((light >> 8)) == 1;
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height - DataPointUtil.VERTICAL_OFFSET, depth - DataPointUtil.VERTICAL_OFFSET, color, lightSky, lightBlock, generation, isDefault);
topBlock = false;
yAbs = depth - 1;
count++;
}
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
return dataToMerge;
}
/**
* Find the lowest valid point from the bottom.
* Used when creating a vertical LOD.
*/
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
{
short depth = DEFAULT_DEPTH;
for (int y = yAbs; y >= 0; y--)
{
blockPos.set(xAbs, y, zAbs);
if (!isLayerValidLodPoint(chunk, blockPos))
if (!isLayerValidLodPoint(chunk, xAbs, y, zAbs))
{
depth = (short) (y + 1);
break;
@@ -317,8 +303,9 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
}
/** Find the highest valid point from the Top */
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
{
short height = DEFAULT_HEIGHT;
if (config.useHeightmap)
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
@@ -326,8 +313,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
{
for (int y = yAbs; y >= 0; y--)
{
blockPos.set(xAbs, y, zAbs);
if (isLayerValidLodPoint(chunk, blockPos))
if (isLayerValidLodPoint(chunk, xAbs, y, zAbs))
{
height = (short) (y + 1);
break;
@@ -347,19 +333,18 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*/
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xRel, int yAbs, int zRel, AbstractBlockPosWrapper blockPos)
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int x, int y, int z)
{
int colorInt;
if (builderConfig.useBiomeColors)
{
// I have no idea why I need to bit shift to the right, but
// if I don't the biomes don't show up correctly.
colorInt = chunk.getBiome(xRel, yAbs, zRel).getColorForBiome(xRel, zRel);
colorInt = chunk.getBiome(x, y, z).getColorForBiome(x, z);
}
else
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, chunk.getPos().getMinBlockZ() + zRel);
colorInt = getColorForBlock(chunk, blockPos);
colorInt = getColorForBlock(chunk, x, y, z);
// if we are skipping non-full and non-solid blocks that means we ignore
// snow, flowers, etc. Get the above block so we can still get the color
@@ -367,8 +352,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
int aboveColorInt = 0;
if (config.client().worldGenerator().getBlocksToAvoid().nonFull || config.client().worldGenerator().getBlocksToAvoid().noCollision)
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel);
aboveColorInt = getColorForBlock(chunk, blockPos);
aboveColorInt = getColorForBlock(chunk, x, y, z);
}
//if (colorInt == 0 && yAbs > 0)
@@ -385,7 +369,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
}
/** Gets the light value for the given block position */
private int getLightValue(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
private int getLightValue(IChunkWrapper chunk, int x, int y, int z, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
{
int skyLight = 0;
int blockLight;
@@ -394,25 +378,25 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
IWorldWrapper world = MC.getWrappedServerWorld();
int blockBrightness = chunk.getEmittedBrightness(blockPos);
int blockBrightness = chunk.getEmittedBrightness(x, y, z);
// get the air block above or below this block
if (hasCeiling && topBlock)
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
y--;
else
blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ());
y++;
if (world != null)
{
// server world sky light (always accurate)
blockLight = world.getBlockLight(blockPos);
blockLight = world.getBlockLight(x,y,z);
if (topBlock && !hasCeiling && hasSkyLight)
skyLight = DEFAULT_MAX_LIGHT;
else
{
if (hasSkyLight)
skyLight = world.getSkyLight(blockPos);
skyLight = world.getSkyLight(x,y,z);
//else
// skyLight = 0;
}
@@ -420,7 +404,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
{
// we are on predicted terrain, and we don't know what the light here is,
// lets just take a guess
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
if (y >= MC.getWrappedClientWorld().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
@@ -441,7 +425,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
else
{
// client world sky light (almost never accurate)
blockLight = world.getBlockLight(blockPos);
blockLight = world.getBlockLight(x,y,z);
// estimate what the lighting should be
if (hasSkyLight || !hasCeiling)
{
@@ -450,14 +434,14 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
else
{
if (hasSkyLight)
skyLight = world.getSkyLight(blockPos);
skyLight = world.getSkyLight(x,y,z);
//else
// skyLight = 0;
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
{
// we don't know what the light here is,
// lets just take a guess
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
if (y >= MC.getWrappedClientWorld().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
@@ -476,24 +460,19 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
}
/** Returns a color int for the given block. */
private int getColorForBlock(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
private int getColorForBlock(IChunkWrapper chunk, int x, int y, int z)
{
int colorOfBlock;
int colorInt;
int xRel = blockPos.getX() - chunk.getPos().getMinBlockX();
int zRel = blockPos.getZ() - chunk.getPos().getMinBlockZ();
//int x = blockPos.getX();
int y = blockPos.getY();
//int z = blockPos.getZ();
IBlockColorWrapper blockColorWrapper;
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(x, y, z);
if (chunk.isWaterLogged(blockPos))
if (chunk.isWaterLogged(x, y, z))
blockColorWrapper = BLOCK_COLOR.getWaterColor();
else
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
blockColorWrapper = chunk.getBlockColorWrapper(x, y, z);
if (blockShapeWrapper.isToAvoid())
return 0;
@@ -503,7 +482,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
if (blockColorWrapper.hasTint())
{
IBiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
IBiomeWrapper biome = chunk.getBiome(x, y, z);
int tintValue;
if (blockColorWrapper.hasGrassTint())
// grass and green plants
@@ -523,15 +502,15 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
/** Is the block at the given blockPos a valid LOD point? */
private boolean isLayerValidLodPoint(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
private boolean isLayerValidLodPoint(IChunkWrapper chunk, int x, int y, int z)
{
if (chunk.isWaterLogged(blockPos))
if (chunk.isWaterLogged(x, y, z))
return true;
boolean nonFullAvoidance = config.client().worldGenerator().getBlocksToAvoid().nonFull;
boolean noCollisionAvoidance = config.client().worldGenerator().getBlocksToAvoid().noCollision;
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos);
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(x, y, z);
return !block.isToAvoid()
&& !(nonFullAvoidance && block.isNonFull())
&& !(noCollisionAvoidance && block.hasNoCollision());
@@ -126,7 +126,7 @@ public class LodGenWorker
@Override
public void run()
{
try
//try
{
// only generate LodChunks if they can
// be added to the current LodDimension
@@ -172,19 +172,19 @@ public class LodGenWorker
}// if in range
}
catch (Exception e)
{
ClientApi.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
e.printStackTrace();
}
finally
{
//catch (Exception e)
//{
// ClientApi.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
// e.printStackTrace();
//}
//finally
//{
// decrement how many threads are running
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
// this position is no longer being generated
LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos);
}
//}
}// run
@@ -1,5 +1,502 @@
package com.seibel.lod.core.dataFormat;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.ThreadMapUtil;
import static com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.skyLightPlayer;
public class BlockDataFormat
{
/*
|a |a |a |a |r |r |r |r |
|r |r |r |r |g |g |g |g |
|g |g |g |g |b |b |b |b |
|b |b |b |b |h |h |h |h |
|h |h |h |h |h |h |d |d |
|d |d |d |d |d |d |d |d |
|bl |bl |bl |bl |sl |sl |sl |sl |
|l |l |f |g |g |g |v |e |
*/
// Reminder: bytes have range of [-128, 127].
// When converting to or from an int a 128 should be added or removed.
// If there is a bug with color then it's probably caused by this.
//To be used in the future for negative value
//public final static int MIN_DEPTH = -64;
//public final static int MIN_HEIGHT = -64;
public final static int EMPTY_DATA = 0;
public static final short VERTICAL_OFFSET = -64;
public static int WORLD_HEIGHT = 1024;
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
//public final static int BLUE_COLOR_SHIFT = 0;
//public final static int GREEN_COLOR_SHIFT = 8;
//public final static int RED_COLOR_SHIFT = 16;
//public final static int ALPHA_COLOR_SHIFT = 24;
public final static int BLUE_SHIFT = 36;
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
public final static int RED_SHIFT = BLUE_SHIFT + 16;
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
public final static int COLOR_SHIFT = 36;
public final static int HEIGHT_SHIFT = 26;
public final static int DEPTH_SHIFT = 16;
public final static int BLOCK_LIGHT_SHIFT = 12;
public final static int SKY_LIGHT_SHIFT = 8;
//public final static int LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
//public final static int VERTICAL_INDEX_SHIFT = 6;
public final static int FLAG_SHIFT = 5;
public final static int GEN_TYPE_SHIFT = 2;
public final static int VOID_SHIFT = 1;
public final static int EXISTENCE_SHIFT = 0;
public final static long ALPHA_MASK = 0b1111;
public final static long RED_MASK = 0b1111_1111;
public final static long GREEN_MASK = 0b1111_1111;
public final static long BLUE_MASK = 0b1111_1111;
public final static long COLOR_MASK = 0b11111111_11111111_11111111;
public final static long HEIGHT_MASK = 0b11_1111_1111;
public final static long DEPTH_MASK = 0b11_1111_1111;
//public final static long LIGHTS_MASK = 0b1111_1111;
public final static long BLOCK_LIGHT_MASK = 0b1111;
public final static long SKY_LIGHT_MASK = 0b1111;
//public final static long VERTICAL_INDEX_MASK = 0b11;
public final static long FLAG_MASK = 0b1;
public final static long GEN_TYPE_MASK = 0b111;
public final static long VOID_MASK = 1;
public final static long EXISTENCE_MASK = 1;
public static long createVoidDataPoint(int generationMode)
{
long dataPoint = 0;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
dataPoint += VOID_MASK << VOID_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
return dataPoint;
}
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag)
{
return createDataPoint(
ColorUtil.getAlpha(color),
ColorUtil.getRed(color),
ColorUtil.getGreen(color),
ColorUtil.getBlue(color),
height, depth, lightSky, lightBlock, generationMode, flag);
}
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
{
long dataPoint = 0;
dataPoint += (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT;
dataPoint += (red & RED_MASK) << RED_SHIFT;
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
dataPoint += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
dataPoint += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
if (flag)
dataPoint += FLAG_MASK << FLAG_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
return dataPoint;
}
public static short getHeight(long dataPoint)
{
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
}
public static short getDepth(long dataPoint)
{
return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK);
}
public static short getAlpha(long dataPoint)
{
return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111);
}
public static short getRed(long dataPoint)
{
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
}
public static short getGreen(long dataPoint)
{
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
}
public static short getBlue(long dataPoint)
{
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
}
public static byte getLightSky(long dataPoint)
{
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightSkyAlt(long dataPoint)
{
if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1)
return 0;
else
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightBlock(long dataPoint)
{
return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
public static boolean getFlag(long dataPoint)
{
return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1;
}
public static byte getGenerationMode(long dataPoint)
{
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
}
public static boolean isVoid(long dataPoint)
{
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
}
public static boolean doesItExist(long dataPoint)
{
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
}
public static int getColor(long dataPoint)
{
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
}
/** This is used to convert a dataPoint to string (useful for the print function) */
@SuppressWarnings("unused")
public static String toString(long dataPoint)
{
return getHeight(dataPoint) + " " +
getDepth(dataPoint) + " " +
getAlpha(dataPoint) + " " +
getRed(dataPoint) + " " +
getBlue(dataPoint) + " " +
getGreen(dataPoint) + " " +
getLightBlock(dataPoint) + " " +
getLightSky(dataPoint) + " " +
getGenerationMode(dataPoint) + " " +
isVoid(dataPoint) + " " +
doesItExist(dataPoint) + '\n';
}
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = 0; i < arraySize - start; i++)
{
array[start + i] = array[start + length + i];
//remove comment to not leave garbage at the end
//array[start + packetSize + i] = 0;
}
}
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = arraySize - start - 1; i >= 0; i--)
{
array[start + length + i] = array[start + i];
array[start + i] = 0;
}
}
/**
* This method merge column of multiple data together
* @param dataToMerge one or more columns of data
* @param inputVerticalData vertical size of an input data
* @param maxVerticalData max vertical size of the merged data
* @return one column of correctly parsed data
*/
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
{
int size = dataToMerge.length / inputVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((WORLD_HEIGHT / 2 + 1) * 2);
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
int genMode = DistanceGenerationMode.FULL.complexity;
boolean allEmpty = true;
boolean allVoid = true;
boolean allDefault;
long singleData;
short depth;
short height;
int count = 0;
int i;
int ii;
int dataIndex;
//We collect the indexes of the data, ordered by the depth
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData))
{
genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleData))
{
allVoid = false;
depth = getDepth(singleData);
height = 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
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
shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
count -= botPos;
}
else
{
//top falls between some blocks, extending those as well
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;
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];
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
//both top and bottom are outside existing blocks
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
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 dataPoint;
if (allVoid)
{
dataPoint[0] = createVoidDataPoint(genMode);
return dataPoint;
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > maxVerticalData)
{
ii = WORLD_HEIGHT - VERTICAL_OFFSET;
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--;
}
//As standard the vertical lods are ordered from top to bottom
for (j = count - 1; j >= 0; j--)
{
height = heightAndDepth[j * 2];
depth = heightAndDepth[j * 2 + 1];
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
break;
int numberOfChildren = 0;
int tempAlpha = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempLightBlock = 0;
int tempLightSky = 0;
byte tempGenMode = DistanceGenerationMode.FULL.complexity;
allEmpty = true;
allVoid = true;
allDefault = true;
long data = 0;
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData) && !isVoid(singleData))
{
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
{
if (getHeight(singleData) > getHeight(data))
data = singleData;
}
}
else
break;
}
if (!doesItExist(data))
{
singleData = dataToMerge[index * inputVerticalData];
data = createVoidDataPoint(getGenerationMode(singleData));
}
if (doesItExist(data))
{
allEmpty = false;
if (!isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha += getAlpha(data);
tempRed += getRed(data);
tempGreen += getGreen(data);
tempBlue += getBlue(data);
tempLightBlock += getLightBlock(data);
tempLightSky += getLightSky(data);
if (!getFlag(data))
allDefault = false;
}
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
}
else
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
}
if (allEmpty)
//no child has been initialized
dataPoint[j] = EMPTY_DATA;
else if (allVoid)
//all the children are void
dataPoint[j] = createVoidDataPoint(tempGenMode);
else
{
//we have at least 1 child
tempAlpha = tempAlpha / numberOfChildren;
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
}
}
return dataPoint;
}
}
@@ -2,4 +2,13 @@ package com.seibel.lod.core.dataFormat;
public class ColorFormat
{
public final static int BLUE_SHIFT = 0;
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
public final static int RED_SHIFT = BLUE_SHIFT + 16;
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
public final static long ALPHA_MASK = 0b1111;
public final static long RED_MASK = 0b1111_1111;
public final static long GREEN_MASK = 0b1111_1111;
public final static long BLUE_MASK = 0b1111_1111;
}
@@ -1,5 +0,0 @@
package com.seibel.lod.core.dataFormat;
public class DepthHeightFormat
{
}
@@ -2,4 +2,38 @@ package com.seibel.lod.core.dataFormat;
public class LightFormat
{
public final static byte INT_BLOCK_LIGHT_SHIFT = 16;
public final static byte INT_SKY_LIGHT_SHIFT = 0;
public final static byte BYTE_BLOCK_LIGHT_SHIFT = 4;
public final static byte BYTE_SKY_LIGHT_SHIFT = 0;
public final static byte BLOCK_LIGHT_MASK = 0b1111;
public final static byte SKY_LIGHT_MASK = 0b1111;
public byte formatLightAsByte(byte skyLight, byte blockLight)
{
return (byte) (((skyLight & SKY_LIGHT_MASK) << BYTE_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << BYTE_BLOCK_LIGHT_SHIFT));
}
public int formatLightAsInt(byte skyLight, byte blockLight)
{
return ((skyLight & SKY_LIGHT_MASK) << INT_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << INT_BLOCK_LIGHT_SHIFT);
}
public int convertByteToIntFormat(byte lights)
{
return 0;
}
public byte getSkyLight(byte lights)
{
return (byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public byte getBlockLight(byte lights)
{
return (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
}
@@ -2,4 +2,77 @@ package com.seibel.lod.core.dataFormat;
public class PositionDataFormat
{
public final static byte LOD_COUNT_SHIFT = 6;
public final static byte CORRECT_LIGHT_SHIFT = 5;
public final static byte GEN_TYPE_SHIFT = 2;
public final static byte VOID_SHIFT = 1;
public final static byte EXISTENCE_SHIFT = 0;
//We are able to count up to 64 different lods in a column
public final static short LOD_COUNT_MASK = 0b11_1111;
public final static short CORRECT_LIGHT_MASK = 0b1;
public final static short GEN_TYPE_MASK = 0b111;
public final static short VOID_MASK = 0b1;
public final static short EXISTENCE_MASK = 0b1;
public final static int EMPTY_DATA = 0;
public final static int VOID_DATA = VOID_MASK<<VOID_SHIFT + EXISTENCE_MASK<<EXISTENCE_SHIFT;
public static short createVoidPositionData(byte generationMode)
{
short positionData = 0;
positionData |= (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
positionData |= VOID_MASK << VOID_SHIFT;
positionData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
return positionData;
}
public static short createPositionData(int lodCount, boolean correctLight, byte generationMode)
{
short positionData = 0;
positionData |= (lodCount & LOD_COUNT_MASK) << LOD_COUNT_SHIFT;
positionData |= (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
if (correctLight)
positionData |= CORRECT_LIGHT_MASK << CORRECT_LIGHT_SHIFT;
positionData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
return positionData;
}
public static String toString(short verticalData, short positionData)
{
return getLodCount(verticalData) + " " +
getLightFlag(verticalData) + " " +
getGenerationMode(verticalData) + " " +
isVoid(verticalData) + " " +
doesItExist(verticalData) + " " +'\n';
}
public static byte getLodCount(short dataPoint)
{
return (byte) ((dataPoint >>> LOD_COUNT_SHIFT) & LOD_COUNT_MASK);
}
public static boolean getLightFlag(short dataPoint)
{
return ((dataPoint >>> CORRECT_LIGHT_SHIFT) & CORRECT_LIGHT_MASK) == 1;
}
public static byte getGenerationMode(short dataPoint)
{
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
}
public static boolean isVoid(short dataPoint)
{
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
}
public static boolean doesItExist(short dataPoint)
{
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
}
}
@@ -0,0 +1,81 @@
package com.seibel.lod.core.dataFormat;
public class VerticalDataFormat
{
public final static short MIN_WORLD_HEIGHT = -2048;
public final static short MAX_WORLD_HEIGHT = 2047;
public final static short WORLD_HEIGHT = MAX_WORLD_HEIGHT - MIN_WORLD_HEIGHT;
public final static byte HEIGHT_SHIFT = 20;
public final static byte DEPTH_SHIFT = 8;
public final static byte LEVEL_SHIFT = 3;
public final static byte BOTTOM_TYPE_SHIFT = 2;
public final static byte TRANSPARENCY_SHIFT = 1;
public final static byte EMPTY_LOD_SHIFT = 0;
public final static int HEIGHT_MASK = 0b1111_1111_1111;
public final static int DEPTH_MASK = 0b1111_1111_1111;
public final static int LEVEL_MASK = 0b111;
public final static int TRANSPARENCY_MASK = 0b1;
public final static int BOTTOM_TYPE_MASK = 0b1;
public final static int EMPTY_LOD_MASK = 0b1;
public final static int EMPTY_LOD = 0;
public static int createVerticalData(int height, int depth, int level, boolean transparent, boolean bottom)
{
int verticalData = 0;
verticalData |= ((height - MIN_WORLD_HEIGHT) & HEIGHT_MASK) << HEIGHT_SHIFT;
verticalData |= ((depth - MIN_WORLD_HEIGHT) & DEPTH_MASK) << DEPTH_SHIFT;
verticalData |= (level & LEVEL_MASK) << LEVEL_SHIFT;
if (bottom)
verticalData |= BOTTOM_TYPE_MASK << BOTTOM_TYPE_SHIFT;
if (transparent)
verticalData |= TRANSPARENCY_MASK << TRANSPARENCY_SHIFT;
verticalData |= EMPTY_LOD_MASK << EMPTY_LOD_SHIFT;
return verticalData;
}
public static String toString(int verticalData, short positionData)
{
return getHeight(verticalData) + " " +
getDepth(verticalData) + " " +
getLevel(verticalData) + " " +
isTransparent(verticalData) + " " +
isBottom(verticalData) + " " +
doesItExist(verticalData) + " " + '\n';
}
public static short getHeight(int verticalData)
{
return (short) (((verticalData >>> HEIGHT_SHIFT) & HEIGHT_MASK) + MIN_WORLD_HEIGHT);
}
public static short getDepth(int verticalData)
{
return (short) (((verticalData >>> DEPTH_SHIFT) & DEPTH_MASK) + MIN_WORLD_HEIGHT);
}
public static byte getLevel(int verticalData)
{
return (byte) ((verticalData >>> LEVEL_SHIFT) & LEVEL_MASK);
}
public static boolean isTransparent(int verticalData)
{
return ((verticalData >>> TRANSPARENCY_SHIFT) & TRANSPARENCY_MASK) == 1;
}
public static boolean isBottom(int verticalData)
{
return ((verticalData >>> BOTTOM_TYPE_SHIFT) & BOTTOM_TYPE_MASK) == 1;
}
public static boolean doesItExist(int verticalData)
{
return (((verticalData >>> EMPTY_LOD_SHIFT) & EMPTY_LOD_MASK) == 1);
}
}
@@ -21,6 +21,7 @@ package com.seibel.lod.core.handlers;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
@@ -77,7 +78,7 @@ public class LodDimensionFileHandler
* file handler, older versions (smaller numbers) will be deleted and overwritten,
* newer versions (larger numbers) will be ignored and won't be read.
*/
public static final int LOD_SAVE_FILE_VERSION = 8;
public static final int LOD_SAVE_FILE_VERSION = 7;
/**
* Allow saving asynchronously, but never try to save multiple regions
@@ -207,14 +208,14 @@ public class LodDimensionFileHandler
break;
}
else if (fileVersion < LOD_SAVE_FILE_VERSION)
else if (fileVersion == 6)
{
//this is old, but readable version
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
inputStream.read(data);
inputStream.close();
// add the data to our region
region.addLevelContainer(new VerticalLevelContainer(data, fileVersion));
region.addLevelContainer(new VerticalLevelContainer(data, 6));
} else
{
// this file is a readable version,
@@ -0,0 +1,104 @@
package com.seibel.lod.core.objects;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class BlockBiomeCouple
{
public static ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple> noBiomeIstanceCache = new ConcurrentHashMap<>();
public static ConcurrentMap<IBiomeWrapper, ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple>> withBiomeIstanceCache = new ConcurrentHashMap<>();
String blockName;
String biomeName;
String coupleName;
IBiomeWrapper biomeColor;
IBlockColorWrapper blockColor;
public static void addBlockBiomeToCache(IBlockColorWrapper blockColor){
}
public static BlockBiomeCouple getBlockBiomeCouple(IBlockColorWrapper blockColor){
if(noBiomeIstanceCache.containsKey(blockColor))
{
return noBiomeIstanceCache.get(blockColor);
}
else
{
BlockBiomeCouple couple = new BlockBiomeCouple(blockColor);
noBiomeIstanceCache.put(blockColor,couple);
return couple;
}
}
public static BlockBiomeCouple getBlockBiomeCouple(IBiomeWrapper biomeColor, IBlockColorWrapper blockColor){
if(biomeColor == null)
{
return getBlockBiomeCouple(blockColor);
}
else
{
if(withBiomeIstanceCache.containsKey(biomeColor))
{
withBiomeIstanceCache.put(biomeColor, new ConcurrentHashMap<>());
}
ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple> blockToCoupleMap = withBiomeIstanceCache.get(biomeColor);
if(blockToCoupleMap.containsKey(blockColor))
{
return blockToCoupleMap.get(blockColor);
}
else
{
BlockBiomeCouple couple = new BlockBiomeCouple(blockColor,biomeColor);
blockToCoupleMap.put(blockColor,couple);
return couple;
}
}
}
public BlockBiomeCouple(IBlockColorWrapper blockColor)
{
this.biomeColor = null;
this.blockColor = blockColor;
biomeName = "";
blockName = blockColor.getName();
coupleName = blockName;
}
public BlockBiomeCouple(IBlockColorWrapper blockColor, IBiomeWrapper biomeColor)
{
this.biomeColor = biomeColor;
this.blockColor = blockColor;
if(biomeColor == null)
biomeName = biomeColor.getName();
else
biomeName = "";
blockName = blockColor.getName();
coupleName = blockName + biomeName;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockBiomeCouple))
return false;
BlockBiomeCouple that = (BlockBiomeCouple) o;
return Objects.equals(blockName, that.blockName) && Objects.equals(biomeName, that.biomeName);
}
@Override public int hashCode()
{
return Objects.hash(blockName, biomeName);
}
}
@@ -200,8 +200,6 @@ public class VertexOptimizer
public final Map<LodDirection, byte[]> skyLights;
public byte blockLight;
/** Holds if the given direction should be culled or not */
public final boolean[] culling;
/** creates an empty box */
@@ -235,8 +233,6 @@ public class VertexOptimizer
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
culling = new boolean[6];
}
/** Set the light of the columns */
@@ -309,68 +305,37 @@ public class VertexOptimizer
}
}
/** determine which faces should be culled */
public void setUpCulling(int cullingDistance, AbstractBlockPosWrapper playerPos)
{
for (LodDirection lodDirection : DIRECTIONS)
{
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.WEST || lodDirection == LodDirection.NORTH)
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) > getFacePos(lodDirection) + cullingDistance;
else if (lodDirection == LodDirection.UP || lodDirection == LodDirection.EAST || lodDirection == LodDirection.SOUTH)
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) < getFacePos(lodDirection) - cullingDistance;
culling[DIRECTION_INDEX.get(lodDirection)] = false;
}
}
/**
* @param lodDirection direction that we want to check if it's culled
* @return true if and only if the face of the direction is culled
*/
public boolean isCulled(LodDirection lodDirection)
{
return culling[DIRECTION_INDEX.get(lodDirection)];
}
/**
* This method create all the shared face culling based on the adjacent data
* @param adjData data adjacent to the column we are going to render
*/
public void setAdjData(Map<LodDirection, int[]> adjData, Map<LodDirection, byte[]> adjFlags)
public void setAdjData(Map<LodDirection, long[]> adjData)
{
int height;
int depth;
int minY = getMinY();
int maxY = getMaxY();
int singleAdjData;
byte singleAdjFlags;
long singleAdjDataPoint;
/* TODO implement attached vertical face culling
//Up direction case
if(DataPointUtil.doesItExist(adjData.get(Direction.UP)))
{
height = DataPointUtil.getHeight(singleAdjData);
depth = DataPointUtil.getDepth(singleAdjData);
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
}*/
//Down direction case
singleAdjData = adjData.get(LodDirection.DOWN)[0];
singleAdjFlags = adjFlags.get(LodDirection.DOWN)[0];
if(DataPointUtil.doesItExist(singleAdjFlags))
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
singleAdjDataPoint = adjData.get(LodDirection.DOWN)[0];
if(DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
else
skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0];
//other sided
//TODO clean some similar cases
for (LodDirection lodDirection : ADJ_DIRECTIONS)
{
if (isCulled(lodDirection))
continue;
int[] data = adjData.get(lodDirection);
byte[] flags = adjFlags.get(lodDirection);
if (DataPointUtil.isVoid(flags[0]))
long[] dataPoint = adjData.get(lodDirection);
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
{
adjHeight.get(lodDirection)[0] = maxY;
adjDepth.get(lodDirection)[0] = minY;
@@ -386,16 +351,15 @@ public class VertexOptimizer
boolean toFinish = false;
int toFinishIndex = 0;
boolean allAbove = true;
for (i = 0; i < flags.length; i++)
for (i = 0; i < dataPoint.length; i++)
{
singleAdjData = data[i];
singleAdjFlags = flags[i];
singleAdjDataPoint = dataPoint[i];
if (DataPointUtil.isVoid(singleAdjFlags) || !DataPointUtil.doesItExist(singleAdjFlags))
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
break;
height = DataPointUtil.getHeight(singleAdjData);
depth = DataPointUtil.getDepth(singleAdjData);
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
if (depth <= maxY)
{
@@ -408,12 +372,12 @@ public class VertexOptimizer
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = getMinY();
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags); //skyLights.get(Direction.UP)[0];
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(lodDirection)[faceToDraw] = getMinY();
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
}
faceToDraw++;
toFinish = false;
@@ -439,12 +403,12 @@ public class VertexOptimizer
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = height;
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags); //skyLights.get(Direction.UP)[0];
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(lodDirection)[faceToDraw] = height;
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
}
toFinish = false;
faceToDraw++;
@@ -456,7 +420,7 @@ public class VertexOptimizer
// the adj data intersects the higher part of the current data
// we start the creation of a new face
adjHeight.get(lodDirection)[faceToDraw] = depth;
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjData);
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
firstFace = false;
toFinish = true;
toFinishIndex = i + 1;
@@ -472,7 +436,7 @@ public class VertexOptimizer
}
adjDepth.get(lodDirection)[faceToDraw] = height;
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
faceToDraw++;
adjHeight.get(lodDirection)[faceToDraw] = depth;
firstFace = false;
@@ -492,12 +456,11 @@ public class VertexOptimizer
else if (toFinish)
{
adjDepth.get(lodDirection)[faceToDraw] = minY;
if(toFinishIndex < flags.length)
if(toFinishIndex < dataPoint.length)
{
singleAdjData = data[toFinishIndex];
singleAdjFlags = flags[toFinishIndex];
if (DataPointUtil.doesItExist(singleAdjFlags))
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjData, singleAdjFlags);
singleAdjDataPoint = dataPoint[toFinishIndex];
if (DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
else
skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0];
}
@@ -525,17 +488,6 @@ public class VertexOptimizer
boxOffset[Z] = zOffset;
}
/**
* This method return the position of a face in the axis of the face
* This is useful for the face culling
* @param lodDirection that we want to check
* @return position in the axis of the face
*/
public int getFacePos(LodDirection lodDirection)
{
return boxOffset[FACE_DIRECTION.get(lodDirection)[0]] + boxWidth[FACE_DIRECTION.get(lodDirection)[0]] * FACE_DIRECTION.get(lodDirection)[1];
}
/**
* returns true if the given direction should be rendered.
*/
@@ -32,7 +32,7 @@ public interface LevelContainer
* @param index z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addData(int color, int data, byte flags, int posX, int posZ, int index);
boolean addData(long data, int posX, int posZ, int index);
/**
* With this you can add data to the level container
@@ -41,7 +41,7 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addVerticalData(int[] color, int[] data, byte[] flags, int posX, int posZ);
boolean addVerticalData(long[] data, int posX, int posZ);
/**
* With this you can add data to the level container
@@ -50,18 +50,25 @@ public interface LevelContainer
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addSingleData(int color, int data, byte flags, int posX, int posZ);
int getColor(int posX, int posZ, int verticalIndex);
int getData(int posX, int posZ, int index);
byte getFlags(int posX, int posZ, int index);
byte getSingleFlags(int posX, int posZ);
boolean addSingleData(long data, int posX, int posZ);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long getData(int posX, int posZ, int index);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long getSingleData(int posX, int posZ);
/**
* data is returned to ThreadMapUtil variables
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return true only if the data exist
@@ -105,4 +112,6 @@ public interface LevelContainer
* @return data as a String
*/
int getMaxNumberOfLods();
void mergeMultiData(int posX, int posZ, long[] dataToMergeVertical, int inputVerticalData);
}
@@ -30,7 +30,12 @@ import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.handlers.LodDimensionFileHandler;
import com.seibel.lod.core.objects.PosToGenerateContainer;
import com.seibel.lod.core.objects.PosToRenderContainer;
import com.seibel.lod.core.util.*;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodThreadFactory;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
@@ -440,7 +445,7 @@ public class LodDimension
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, int color, int data, byte flags, boolean dontSave)
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
@@ -450,7 +455,7 @@ public class LodDimension
if (region == null)
return false;
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, color, data, flags);
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
@@ -482,7 +487,7 @@ public class LodDimension
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, int[] color, int[] data, byte[] flags, boolean dontSave)
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
@@ -492,7 +497,7 @@ public class LodDimension
if (region == null)
return false;
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, color, data, flags);
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
@@ -546,7 +551,6 @@ public class LodDimension
// We can use two type of generation scheduling
switch (CONFIG.client().worldGenerator().getGenerationPriority())
{
default:
case NEAR_FIRST:
//in the NEAR_FIRST generation scheduling we prioritize the nearest un-generated position to the player
//the chunk position to generate will be stored in a posToGenerate object
@@ -561,9 +565,7 @@ public class LodDimension
byte detailLevel;
int posX;
int posZ;
int color;
int data;
byte flags;
long data;
int numbChunksWide = (width) * 32;
int circleLimit = Integer.MAX_VALUE;
@@ -611,11 +613,11 @@ public class LodDimension
posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel);
posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel);
flags = getSingleFlags(detailLevel, posX, posZ);
data = getSingleData(detailLevel, posX, posZ);
//we will generate the position only if the current generation complexity is lower than the target one.
//an un-generated area will always have 0 generation
if (DataPointUtil.getGenerationMode(flags) < complexity)
if (DataPointUtil.getGenerationMode(data) < complexity)
{
posToGenerate.addPosToGenerate(detailLevel, posX, posZ);
if (maxDataToGenerate >= 0)
@@ -634,7 +636,7 @@ public class LodDimension
}
break;
default:
case FAR_FIRST:
//in the FAR_FIRST generation we dedicate part of the generation process to the far region with really
//low detail quality.
@@ -702,52 +704,43 @@ public class LodDimension
return region.getMaxVerticalData(detailLevel);
}
public int getColor(byte detailLevel, int posX, int posZ, int verticalIndex)
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return 0;
else
return region.getColor(detailLevel, posX, posZ, verticalIndex);
return DataPointUtil.EMPTY_DATA;
return region.getData(detailLevel, posX, posZ, verticalIndex);
}
public int getData(byte detailLevel, int posX, int posZ, int verticalIndex)
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public long getSingleData(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return 0;
else
return region.getData(detailLevel, posX, posZ, verticalIndex);
}
public byte getFlags(byte detailLevel, int posX, int posZ, int verticalIndex)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
return DataPointUtil.EMPTY_DATA;
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return 0;
else
return region.getFlags(detailLevel, posX, posZ, verticalIndex);
}
public byte getSingleFlags(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return (byte) 0;
else
return region.getSingleFlags(detailLevel, posX, posZ);
return region.getSingleData(detailLevel, posX, posZ);
}
/** Clears the given region */
@@ -916,4 +909,39 @@ public class LodDimension
{
isRegionDirty[i][j] = val;
}
public void mergeMultiData(byte detailLevel, int posX, int posZ, boolean dontSave, long[] dataToMergeVertical, int inputVerticalData)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
// don't continue if the region can't be saved
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return;
region.mergeMultiData(detailLevel, posX, posZ, dataToMergeVertical, inputVerticalData);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
try
{
// mark the region as dirty, so it will be saved to disk
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
regenRegionBuffer[xIndex][zIndex] = true;
regenDimensionBuffers = true;
}
catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// If this happens, the method was probably
// called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
}
}
}
@@ -154,7 +154,7 @@ public class LodRegion
* TODO this will always return true unless it has
* @return true if the data was added successfully
*/
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, int color, int data, byte flags)
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
@@ -162,11 +162,9 @@ public class LodRegion
// The dataContainer could have null entries if the
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
{
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
}
this.dataContainer[detailLevel].addData(color, data, flags, posX, posZ, verticalIndex);
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
return true;
}
@@ -177,51 +175,50 @@ public class LodRegion
* TODO this will always return true unless it has
* @return true if the data was added successfully
*/
public boolean addVerticalData(byte detailLevel, int posX, int posZ, int[] color, int[] data, byte[] flags)
public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
{
//position is already relative
//posX = LevelPosUtil.getRegionModule(detailLevel, posX);
//posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
// The dataContainer could have null entries if the
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
return this.dataContainer[detailLevel].addVerticalData(color, data, flags, posX, posZ);
}
public int getColor(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getColor(posX, posZ, verticalIndex);
}
public int getData(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
}
public byte getFlags(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getFlags(posX, posZ, verticalIndex);
return this.dataContainer[detailLevel].addVerticalData(data, posX, posZ);
}
/**
* Get the flags at the given relative position.
* Get the dataPoint at the given relative position.
* @return the data at the relative pos and detail level,
* 0 if the data doesn't exist.
*/
public byte getSingleFlags(byte detailLevel, int posX, int posZ)
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getSingleFlags(posX, posZ);
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
}
/**
* Get the dataPoint at the given relative position.
* @return the data at the relative pos and detail level,
* 0 if the data doesn't exist.
*/
public long getSingleData(byte detailLevel, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[detailLevel].getSingleData(posX, posZ);
}
/**
* Clears the datapoint at the given relative position
*/
public void clear(byte detailLevel, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
dataContainer[detailLevel].clear(posX, posZ);
}
@@ -364,7 +361,6 @@ public class LodRegion
posZ + regionPosZ * size);
}
else
{
//if (desiredLevel > detailLevel)
//{
// we have gone beyond the target Detail level
@@ -389,7 +385,7 @@ public class LodRegion
{
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
{
if (!requireCorrectDetailLevel)
if (requireCorrectDetailLevel)
childrenCount++;
else
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
@@ -398,7 +394,7 @@ public class LodRegion
}
if (!requireCorrectDetailLevel)
if (requireCorrectDetailLevel)
{
// If all the four children exist go deeper
if (childrenCount == 4)
@@ -415,7 +411,6 @@ public class LodRegion
}
}
}
}
}
@@ -458,6 +453,8 @@ public class LodRegion
*/
private void update(byte detailLevel, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
}
@@ -467,15 +464,12 @@ public class LodRegion
*/
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
if (detailLevel < minDetailLevel)
if (detailLevel < minDetailLevel || dataContainer[detailLevel] == null)
return false;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
if (dataContainer[detailLevel] == null)
return false;
return dataContainer[detailLevel].doesItExist(posX, posZ);
}
@@ -487,7 +481,7 @@ public class LodRegion
if (dataContainer[detailLevel].doesItExist(posX, posZ))
// We take the bottom information always
// TODO what does that mean? bottom of what?
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleFlags(posX, posZ));
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ));
else
return DistanceGenerationMode.NONE.complexity;
}
@@ -616,4 +610,16 @@ public class LodRegion
{
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
}
public void mergeMultiData(byte detailLevel, int posX, int posZ, long[] dataToMergeVertical, int inputVerticalData)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
// The dataContainer could have null entries if the
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
this.dataContainer[detailLevel].mergeMultiData(posX, posZ, dataToMergeVertical, inputVerticalData);
}
}
@@ -19,6 +19,7 @@
package com.seibel.lod.core.objects.lod;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
@@ -35,21 +36,28 @@ public class VerticalLevelContainer implements LevelContainer
public final byte detailLevel;
public final int size;
public final int chunkCount = 0;
public final int chunkSize = 0;
public final int maxVerticalData;
public final int[] dataContainerColor;
public final int[] dataContainerData;
public final byte[] dataContainerFlags;
public final long[] dataContainer;
//length should be chunkCount
public final byte[] verticalSize = null;
//length should be chunkCount
public final short[] positionData = null;
public final int[][] verticalData = null;
public final int[][] colorData = null;
public final byte[][] lightData = null;
public VerticalLevelContainer(byte detailLevel)
{
this.detailLevel = detailLevel;
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
final int i = size * size * maxVerticalData;
dataContainerColor = new int[i];
dataContainerData = new int[i];
dataContainerFlags = new byte[i];
dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)];
}
@Override
@@ -61,81 +69,41 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public void clear(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
{
final int i = (posX * size + posZ) * maxVerticalData + verticalIndex;
dataContainerColor[i] = 0;
dataContainerData[i] = 0;
dataContainerFlags[i] = 0;
}
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA;
}
@Override
public boolean addData(int color, int data, byte flags, int posX, int posZ, int verticalIndex)
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
final int i = (posX * size + posZ) * maxVerticalData + verticalIndex;
dataContainerColor[i] = color;
dataContainerData[i] = data;
dataContainerFlags[i] = flags;
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data;
return true;
}
@Override
public boolean addVerticalData(int[] color, int[] data, byte[] flags, int posX, int posZ)
public boolean addVerticalData(long[] data, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
{
final int i = (posX * size + posZ) * maxVerticalData + verticalIndex;
dataContainerColor[i] = color[verticalIndex];
dataContainerData[i] = data[verticalIndex];
dataContainerFlags[i] = flags[verticalIndex];
}
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data[verticalIndex];
return true;
}
@Override
public boolean addSingleData(int color, int data, byte flags, int posX, int posZ)
public boolean addSingleData(long data, int posX, int posZ)
{
return addData(color, data, flags, posX, posZ, 0);
return addData(data, posX, posZ, 0);
}
@Override
public int getColor(int posX, int posZ, int verticalIndex)
public long getData(int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerColor[(posX * size + posZ) * maxVerticalData + verticalIndex];
}
@Override
public int getData(int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerData[(posX * size + posZ) * maxVerticalData + verticalIndex];
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex];
}
@Override
public byte getFlags(int posX, int posZ, int verticalIndex)
public long getSingleData(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerFlags[(posX * size + posZ) * maxVerticalData + verticalIndex];
}
@Override
public byte getSingleFlags(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainerFlags[(posX * size + posZ) * maxVerticalData];
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData];
}
@Override
@@ -152,9 +120,7 @@ public class VerticalLevelContainer implements LevelContainer
@Override
public boolean doesItExist(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return DataPointUtil.doesItExist(dataContainerFlags[(posX * size + posZ) * maxVerticalData]);
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
}
public VerticalLevelContainer(byte[] inputData, int version)
@@ -162,131 +128,69 @@ public class VerticalLevelContainer implements LevelContainer
int tempMaxVerticalData;
int tempIndex;
int index = 0;
long newData;
detailLevel = inputData[index];
index++;
tempMaxVerticalData = inputData[index] & 0b01111111;
index++;
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int x = size * size * tempMaxVerticalData;
int[] tempDataContainerColor = new int[x];
int[] tempDataContainerData = new int[x];
byte[] tempDataContainerFlags = new byte[x];
long[] tempDataContainer = new long[x];
if (version == 6)
{
long oldData;
for (int i = 0; i < x; i++)
{
oldData = 0;
newData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
oldData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
/*
|a |a |a |a |r |r |r |r |
|r |r |r |r |g |g |g |g |
|g |g |g |g |b |b |b |b |
|b |b |b |b |h |h |h |h |
|h |h |h |h |h |h |d |d |
|d |d |d |d |d |d |d |d |
|bl |bl |bl |bl |sl |sl |sl |sl |
|l |l |f |g |g |g |v |e |
*/
DataPointUtil.createDataPoint(
(int)((oldData >> 60) << 4) + 15,
(int)(oldData >> 52) & 0xFF,
(int)(oldData >> 44) & 0xFF,
(int)(oldData >> 36) & 0xFF,
(int)(oldData >> 26) & 0x3FF - DataPointUtil.VERTICAL_OFFSET,
(int)(oldData >> 16) & 0x3FF - DataPointUtil.VERTICAL_OFFSET,
(int)(oldData >> 8) & 0xF,
(int)(oldData >> 12) & 0xF,
(int)(oldData >> 5) & 0x1,
((oldData >> 5) & 0x1) == 1
);
tempDataContainerColor[i] = ThreadMapUtil.dataPointColor;
tempDataContainerData[i] = ThreadMapUtil.dataPointData;
tempDataContainerFlags[i] = ThreadMapUtil.dataPointFlags;
}
}
else if (version == 7)
{
long oldData;
for (int i = 0; i < x; i++)
{
oldData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
oldData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
DataPointUtil.createDataPoint(
(int)((oldData >> 60) << 4) + 15,
(int)(oldData >> 52) & 0xFF,
(int)(oldData >> 44) & 0xFF,
(int)(oldData >> 36) & 0xFF,
(int)(oldData >> 26) & 0x3FF - DataPointUtil.VERTICAL_OFFSET - 64,
(int)(oldData >> 16) & 0x3FF - DataPointUtil.VERTICAL_OFFSET - 64,
(int)(oldData >> 8) & 0xF,
(int)(oldData >> 12) & 0xF,
(int)(oldData >> 5) & 0x1,
((oldData >> 5) & 0x1) == 1
);
tempDataContainerColor[i] = ThreadMapUtil.dataPointColor;
tempDataContainerData[i] = ThreadMapUtil.dataPointData;
tempDataContainerFlags[i] = ThreadMapUtil.dataPointFlags;
}
}
else //if (version == 8)
{
int color;
int data;
for (int i = 0; i < x; i++)
{
byte flags = inputData[index];
index++;
data = 0;
color = 0;
for (tempIndex = 0; tempIndex < 4; tempIndex++)
{
data += (((int) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
color += (((int) inputData[index + tempIndex + 4]) & 0xff) << (8 * tempIndex);
}
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
tempDataContainerColor[i] = color;
tempDataContainerData[i] = data;
tempDataContainerFlags[i] = flags;
newData = DataPointUtil.createDataPoint(
DataPointUtil.getAlpha(newData),
DataPointUtil.getRed(newData),
DataPointUtil.getGreen(newData),
DataPointUtil.getBlue(newData),
DataPointUtil.getHeight(newData) - DataPointUtil.VERTICAL_OFFSET,
DataPointUtil.getDepth(newData) - DataPointUtil.VERTICAL_OFFSET,
DataPointUtil.getLightSky(newData),
DataPointUtil.getLightBlock(newData),
DataPointUtil.getGenerationMode(newData),
DataPointUtil.getFlag(newData)
);
tempDataContainer[i] = newData;
}
}
else //if (version == 7)
{
for (int i = 0; i < x; i++)
{
newData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
tempDataContainer[i] = newData;
}
}
if (tempMaxVerticalData > DetailDistanceUtil.getMaxVerticalData(detailLevel))
{
int tempMaxVerticalData2 = DetailDistanceUtil.getMaxVerticalData(detailLevel);
int[] dataToMergeColor = new int[tempMaxVerticalData];
int[] dataToMergeData = new int[tempMaxVerticalData];
byte[] dataToMergeFlags = new byte[tempMaxVerticalData];
int[] tempDataContainer2Color = new int[size * size * tempMaxVerticalData2];
int[] tempDataContainer2Data = new int[size * size * tempMaxVerticalData2];
byte[] tempDataContainer2Flags = new byte[size * size * tempMaxVerticalData2];
long[] dataToMerge = new long[tempMaxVerticalData];
long[] tempDataContainer2 = new long[size * size * tempMaxVerticalData2];
for (int i = 0; i < size * size; i++)
{
System.arraycopy(tempDataContainerColor, i * tempMaxVerticalData, dataToMergeColor, 0, tempMaxVerticalData);
System.arraycopy(tempDataContainerData, i * tempMaxVerticalData, dataToMergeData, 0, tempMaxVerticalData);
System.arraycopy(tempDataContainerFlags, i * tempMaxVerticalData, dataToMergeFlags, 0, tempMaxVerticalData);
DataPointUtil.mergeMultiData(dataToMergeColor, dataToMergeData, dataToMergeFlags, tempMaxVerticalData, tempMaxVerticalData2);
System.arraycopy(ThreadMapUtil.getRawVerticalDataArrayColor(), 0, tempDataContainer2Color, i * tempMaxVerticalData2, tempMaxVerticalData2);
System.arraycopy(ThreadMapUtil.getRawVerticalDataArrayData(), 0, tempDataContainer2Data, i * tempMaxVerticalData2, tempMaxVerticalData2);
System.arraycopy(ThreadMapUtil.getRawVerticalDataArrayFlags(), 0, tempDataContainer2Flags, i * tempMaxVerticalData2, tempMaxVerticalData2);
System.arraycopy(tempDataContainer, i * tempMaxVerticalData, dataToMerge, 0, tempMaxVerticalData);
dataToMerge = DataPointUtil.mergeMultiData(dataToMerge, tempMaxVerticalData, tempMaxVerticalData2);
System.arraycopy(dataToMerge, 0, tempDataContainer2, i * tempMaxVerticalData2, tempMaxVerticalData2);
}
maxVerticalData = tempMaxVerticalData2;
this.dataContainerColor = tempDataContainer2Color;
this.dataContainerData = tempDataContainer2Data;
this.dataContainerFlags = tempDataContainer2Flags;
this.dataContainer = tempDataContainer2;
}
else
{
maxVerticalData = tempMaxVerticalData;
this.dataContainerColor = tempDataContainerColor;
this.dataContainerData = tempDataContainerData;
this.dataContainerFlags = tempDataContainerFlags;
this.dataContainer = tempDataContainer;
}
}
@@ -300,15 +204,11 @@ public class VerticalLevelContainer implements LevelContainer
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
{
//We reset the array
int[] dataToMergeColor = ThreadMapUtil.getVerticalUpdateArrayColor(detailLevel);
int[] dataToMergeData = ThreadMapUtil.getVerticalUpdateArrayData(detailLevel);
byte[] dataToMergeFlags = ThreadMapUtil.getVerticalUpdateArrayFlags(detailLevel);
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel);
int lowerMaxVertical = dataToMergeFlags.length / 4;
int lowerMaxVertical = dataToMerge.length / 4;
int childPosX;
int childPosZ;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
@@ -316,17 +216,10 @@ public class VerticalLevelContainer implements LevelContainer
childPosX = 2 * posX + x;
childPosZ = 2 * posZ + z;
for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
{
final int i = (z * 2 + x) * lowerMaxVertical + verticalIndex;
dataToMergeColor[i] = lowerLevelContainer.getColor(childPosX, childPosZ, verticalIndex);
dataToMergeData[i] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
dataToMergeFlags[i] = lowerLevelContainer.getFlags(childPosX, childPosZ, verticalIndex);
}
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
}
}
DataPointUtil.mergeMultiData(dataToMergeColor, dataToMergeData, dataToMergeFlags, lowerMaxVertical, getMaxVerticalData());
addVerticalData(ThreadMapUtil.getRawVerticalDataArrayColor(), ThreadMapUtil.getRawVerticalDataArrayData(), ThreadMapUtil.getRawVerticalDataArrayFlags(), posX, posZ);
mergeMultiData(posX, posZ, dataToMerge, lowerMaxVertical);
}
@Override
@@ -335,8 +228,7 @@ public class VerticalLevelContainer implements LevelContainer
int index = 0;
int x = size * size;
int tempIndex;
int currentColor;
int currentData;
long current;
boolean allGenerated = true;
byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel);
@@ -349,18 +241,12 @@ public class VerticalLevelContainer implements LevelContainer
{
for (j = 0; j < maxVerticalData; j++)
{
currentColor = dataContainerColor[i * maxVerticalData + j];
currentData = dataContainerData[i * maxVerticalData + j];
tempData[index] = dataContainerFlags[i * maxVerticalData + j];
index++;
for (tempIndex = 0; tempIndex < 4; tempIndex++)
{
tempData[index + tempIndex] = (byte) (currentData >>> (8 * tempIndex));
tempData[index + tempIndex + 4] = (byte) (currentColor >>> (8 * tempIndex));
}
current = dataContainer[i * maxVerticalData + j];
for (tempIndex = 0; tempIndex < 8; tempIndex++)
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
index += 8;
}
if(!DataPointUtil.doesItExist(dataContainerFlags[i]))
if(!DataPointUtil.doesItExist(dataContainer[i]))
allGenerated = false;
}
if (allGenerated)
@@ -368,6 +254,325 @@ public class VerticalLevelContainer implements LevelContainer
return tempData;
}
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = 0; i < arraySize - start; i++)
{
array[start + i] = array[start + length + i];
//remove comment to not leave garbage at the end
//array[start + packetSize + i] = 0;
}
}
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = arraySize - start - 1; i >= 0; i--)
{
array[start + length + i] = array[start + i];
array[start + i] = 0;
}
}
/**
*
* @param positionDataToMerge
* @effect
*/
public void computePositionData(short[] positionDataToMerge)
{
}
/**
*
* @param verticalDataToMerge
* @param maxVerticalData max vertical size of the merged data
* @effect save in
*/
public void computeHeightAndDepthData(int[] verticalDataToMerge, int maxVerticalData)
{
}
/**
*
* @param colorDataToMerge
* @param lightDataToMerge
* @effect
*/
public void computeColorLightVerticalData(int[] colorDataToMerge, byte[] lightDataToMerge, int[] verticalDataToMerge)
{
}
/**
* This method merge column of multiple data together
* @param dataToMerge one or more columns of data
* @param inputVerticalData vertical size of an input data
*/
public void mergeMultiData(int posX, int posZ, long[] dataToMerge, int inputVerticalData)
{
int size = dataToMerge.length / inputVerticalData;
final int arrayPos = (posX * this.size + posZ) * maxVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((DataPointUtil.WORLD_HEIGHT / 2 + 1) * 2);
int genMode = DistanceGenerationMode.FULL.complexity;
boolean allEmpty = true;
boolean allVoid = true;
boolean allDefault;
long singleData;
short depth;
short height;
int count = 0;
int i;
int ii;
int dataIndex;
//We collect the indexes of the data, ordered by the depth
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (DataPointUtil.doesItExist(singleData))
{
genMode = Math.min(genMode, DataPointUtil.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
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
shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
count -= botPos;
}
else
{
//top falls between some blocks, extending those as well
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;
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];
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
//both top and bottom are outside existing blocks
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
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)
{
dataContainer[arrayPos] = DataPointUtil.createVoidDataPoint(genMode);
return;
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > maxVerticalData)
{
ii = DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET;
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--;
}
//As standard the vertical lods are ordered from top to bottom
for (j = count - 1; j >= 0; j--)
{
if (j >= heightAndDepth.length / 2)
break;
height = heightAndDepth[j * 2];
depth = heightAndDepth[j * 2 + 1];
if ((depth == 0 && height == 0))
break;
int numberOfChildren = 0;
int tempAlpha = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempLightBlock = 0;
int tempLightSky = 0;
byte tempGenMode = DistanceGenerationMode.FULL.complexity;
allEmpty = true;
allVoid = true;
allDefault = true;
long data = 0;
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (DataPointUtil.doesItExist(singleData) && !DataPointUtil.isVoid(singleData))
{
if ((depth <= DataPointUtil.getDepth(singleData) && DataPointUtil.getDepth(singleData) <= height)
|| (depth <= DataPointUtil.getHeight(singleData) && DataPointUtil.getHeight(singleData) <= height))
{
if (DataPointUtil.getHeight(singleData) > DataPointUtil.getHeight(data))
data = singleData;
}
}
else
break;
}
if (!DataPointUtil.doesItExist(data))
{
singleData = dataToMerge[index * inputVerticalData];
data = DataPointUtil.createVoidDataPoint(DataPointUtil.getGenerationMode(singleData));
}
if (DataPointUtil.doesItExist(data))
{
allEmpty = false;
if (!DataPointUtil.isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha += DataPointUtil.getAlpha(data);
tempRed += DataPointUtil.getRed(data);
tempGreen += DataPointUtil.getGreen(data);
tempBlue += DataPointUtil.getBlue(data);
tempLightBlock += DataPointUtil.getLightBlock(data);
tempLightSky += DataPointUtil.getLightSky(data);
if (!DataPointUtil.getFlag(data)) allDefault = false;
}
tempGenMode = (byte) Math.min(tempGenMode, DataPointUtil.getGenerationMode(data));
}
else
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
}
if (allEmpty)
//no child has been initialized
dataContainer[arrayPos + j] = DataPointUtil.EMPTY_DATA;
else if (allVoid)
//all the children are void
dataContainer[arrayPos + j] = DataPointUtil.createVoidDataPoint(tempGenMode);
else
{
//we have at least 1 child
tempAlpha = tempAlpha / numberOfChildren;
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
dataContainer[arrayPos + j] = DataPointUtil.createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
}
}
}
@Override
@SuppressWarnings("unused")
public String toString()
@@ -26,7 +26,7 @@ import com.google.common.collect.ImmutableList;
* DefaultVertexFormats class.
*
* @author James Seibel
* @version 11-13-2021
* @version 12-8-2021
*/
public class DefaultLodVertexFormats
{
@@ -37,6 +37,8 @@ public class DefaultLodVertexFormats
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3);
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1);
public static final LodVertexFormatElement ELEMENT_BLOCK_LIGHT = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 1);
public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).build());
public static final LodVertexFormat POSITION_COLOR = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).build());
@@ -45,4 +47,5 @@ public class DefaultLodVertexFormats
public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build());
public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build());
public static final LodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_BLOCK_LIGHT).add(ELEMENT_BLOCK_LIGHT).build());
}
@@ -19,7 +19,6 @@
package com.seibel.lod.core.objects.opengl;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
@@ -38,7 +37,7 @@ import com.google.common.collect.Lists;
* OpenGL buffers.
*
* @author James Seibel
* @version 11-13-2021
* @version 12-8-2021
*/
public class LodBufferBuilder
{
@@ -354,7 +353,22 @@ public class LodBufferBuilder
}
}
public LodBufferBuilder vertex(float x, float y, float z)
public LodBufferBuilder minecraftLightValue(byte lightValue)
{
LodVertexFormatElement LodVertexFormatelement = this.currentElement();
if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE)
{
throw new IllegalStateException("Light Color must be stored as a UBYTE");
}
else
{
this.putByte(0, lightValue);
this.nextElement();
return this;
}
}
public LodBufferBuilder position(float x, float y, float z)
{
if (this.currentElement().getType() != LodVertexFormatElement.DataType.FLOAT)
{
@@ -19,7 +19,7 @@
package com.seibel.lod.core.render;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -57,7 +57,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
* https://stackoverflow.com/questions/63509735/massive-performance-loss-with-glmapbuffer <br><br>
*
* @author James Seibel
* @version 12-1-2021
* @version 12-8-2021
*/
public class GLProxy
{
@@ -90,6 +90,8 @@ public class GLProxy
public LodShaderProgram lodShaderProgram;
/** This is the VAO that is used when rendering */
public final int vertexArrayObjectId;
/** This is the 2D texture that holds MC's lightmap */
public final int lightMapTextureId;
/** Requires OpenGL 4.5, and offers the best buffer uploading */
@@ -100,18 +102,20 @@ public class GLProxy
/**
* @throws IllegalStateException
* @throws RuntimeException
* @throws FileNotFoundException
*/
private GLProxy()
{
ClientApi.LOGGER.error("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
ClientApi.LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
// getting Minecraft's context has to be done on the render thread,
// where the GL context is
if (GLFW.glfwGetCurrentContext() == 0L)
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
//============================//
// create the builder context //
//============================//
@@ -174,7 +178,7 @@ public class GLProxy
if (!bufferStorageSupported)
{
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
ClientApi.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
ClientApi.LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
}
@@ -230,6 +234,7 @@ public class GLProxy
// this must be created on minecraft's render context to work correctly
vertexArrayObjectId = GL30.glGenVertexArrays();
lightMapTextureId = GL30.glGenTextures();
@@ -243,10 +248,14 @@ public class GLProxy
// GLProxy creation success
ClientApi.LOGGER.error(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
ClientApi.LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
}
/** Creates all required shaders */
/**
* Creates all required shaders
* @throws RuntimeException
* @throws FileNotFoundException
*/
public void createShaderProgram()
{
LodShader vertexShader = null;
@@ -255,8 +264,8 @@ public class GLProxy
try
{
// get the shaders from the resource folder
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders" + File.separator + "standard.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders" + File.separator + "flat_shaded.frag", false);
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/standard.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/flat_shaded.frag", false);
// this can be used when testing shaders,
// since we can't hot swap the files in the resource folder
@@ -287,6 +296,35 @@ public class GLProxy
{
ClientApi.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage());
}
// get the shaders from the resource folder
// Use File.separator ONLY if the file is external (not in the resourse), otherwise use "/"
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/standard.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/flat_shaded.frag", false);
// this can be used when testing shaders,
// since we can't hot swap the files in the resource folder
// vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "C:/Users/James Seibel/Desktop/shaders/standard.vert", true);
// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/flat_shaded.frag", true);
// create the shaders
lodShaderProgram = new LodShaderProgram();
// Attach the compiled shaders to the program, throws RuntimeException on link error
lodShaderProgram.attachShader(vertexShader);
lodShaderProgram.attachShader(fragmentShader);
// activate the fragment shader output
GL30.glBindFragDataLocation(lodShaderProgram.id, 0, "fragColor");
// attach the shader program to the OpenGL context
lodShaderProgram.link();
// after the shaders have been attached to the program
// we don't need their OpenGL references anymore
GL20.glDeleteShader(vertexShader.id);
GL20.glDeleteShader(fragmentShader.id);
}
@@ -22,11 +22,13 @@ package com.seibel.lod.core.render;
import java.awt.Color;
import java.util.HashSet;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.VertexBuffersAndOffset;
import com.seibel.lod.core.enums.config.GpuUploadMethod;
@@ -48,8 +50,6 @@ import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
@@ -60,7 +60,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 11-27-2021
* @version 12-8-2021
*/
public class LodRenderer
{
@@ -93,7 +93,8 @@ public class LodRenderer
@SuppressWarnings("unused")
private int[][][] storageBufferIds;
private AbstractChunkPosWrapper vbosCenter = FACTORY.createChunkPos();
private int vbosCenterX = 0;
private int vbosCenterZ = 0;
/** This is used to determine if the LODs should be regenerated */
@@ -192,7 +193,7 @@ public class LodRenderer
if ((partialRegen || fullRegen) && !lodBufferBuilderFactory.generatingBuffers && !lodBufferBuilderFactory.newBuffersAvailable())
{
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos(), true);
lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getY(), MC.getPlayerBlockPos().getZ(), true);
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvailable()
@@ -278,6 +279,13 @@ public class LodRenderer
shaderProgram.enableVertexAttribute(posAttrib);
int colAttrib = shaderProgram.getAttributeLocation("color");
shaderProgram.enableVertexAttribute(colAttrib);
int blockSkyLightAttrib = shaderProgram.getAttributeLocation("blockSkyLight");
// TODO the block sky light is being passed in correctly but the data
// we were given appears to be incorrect, so we won't use it for now
//shaderProgram.enableVertexAttribute(blockSkyLightAttrib);
int blockLightAttrib = shaderProgram.getAttributeLocation("blockLight");
shaderProgram.enableVertexAttribute(blockLightAttrib);
// global uniforms
@@ -291,6 +299,58 @@ public class LodRenderer
shaderProgram.setUniform(fogColorUniform, getFogColor());
int skyLightUniform = shaderProgram.getUniformLocation("worldSkyLight");
shaderProgram.setUniform(skyLightUniform, (int) (MC.getSkyDarken(partialTicks) * 15));
int lightMapUniform = shaderProgram.getUniformLocation("lightMap");
GL20.glBindTexture(GL20.GL_TEXTURE_2D, glProxy.lightMapTextureId);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_BORDER);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_BORDER);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_NEAREST);
GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_NEAREST);
// get the latest lightmap from MC
try
{
int lightMapHeight = MC_RENDER.getLightmapTextureHeight();
int lightMapWidth = MC_RENDER.getLightmapTextureWidth();
int[] pixels = MC_RENDER.getLightmapPixels();
// comment me out to see when the lightmap is changing
// boolean same = true;
// int badIndex = 0;
// if (testArray != null && pixels != null)
// for (int i = 0; i < pixels.length; i++)
// {
// if(pixels[i] != testArray[i])
// {
// same = false;
// badIndex = i;
// break;
// }
// }
// testArray = pixels;
// MC.sendChatMessage(same + " " + badIndex);
// comment this line out to prevent uploading the new lightmap
GL20.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_RGBA, lightMapWidth,
lightMapHeight, 0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixels);
// TODO is this needed/correct?
shaderProgram.setUniform(lightMapUniform, 0);
}
catch (Exception e)
{
ClientApi.LOGGER.info(e.getMessage(), e);
}
// region dependent uniforms
int fogEnabledUniform = shaderProgram.getUniformLocation("fogEnabled");
int nearFogEnabledUniform = shaderProgram.getUniformLocation("nearFogEnabled");
@@ -316,8 +376,10 @@ public class LodRenderer
boolean renderBufferStorage = CONFIG.client().advanced().buffers().getGpuUploadMethod() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported;
// where the center of the buffers is (needed when culling regions)
RegionPos vboCenterRegionPos = new RegionPos(vbosCenter);
RegionPos vboPos = new RegionPos();
int vboCenterRegionPosX = vbosCenterX;
int vboCenterRegionPosZ = vbosCenterZ;
int vboPosX;
int vboPosZ;
// render each of the buffers
@@ -325,10 +387,13 @@ public class LodRenderer
{
for (int z = 0; z < vbos.length; z++)
{
vboPos.x = x + vboCenterRegionPos.x - (lodDim.getWidth() / 2);
vboPos.z = z + vboCenterRegionPos.z - (lodDim.getWidth() / 2);
vboPosX = x + vboCenterRegionPosX - (lodDim.getWidth() / 2);
vboPosZ = z + vboCenterRegionPosZ - (lodDim.getWidth() / 2);
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(), MC_RENDER.getLookAtVector(), vboPos.blockPos()))
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(),
MC_RENDER.getLookAtVector(),
LodUtil.convertLevelPos(LodUtil.REGION_DETAIL_LEVEL, vboPosX, LodUtil.BLOCK_DETAIL_LEVEL),
LodUtil.convertLevelPos(LodUtil.REGION_DETAIL_LEVEL, vboPosZ, LodUtil.BLOCK_DETAIL_LEVEL)))
{
// fog may be different from region to region
applyFog(shaderProgram,
@@ -341,7 +406,7 @@ public class LodRenderer
for (int i = 0; i < vbos[x][z].length; i++)
{
bufferId = (storageBufferIds != null && renderBufferStorage) ? storageBufferIds[x][z][i] : vbos[x][z][i].id;
drawArrays(bufferId, vbos[x][z][i].vertexCount, posAttrib, colAttrib);
drawArrays(bufferId, vbos[x][z][i].vertexCount, posAttrib, colAttrib, blockLightAttrib, blockSkyLightAttrib);
}
}
@@ -349,18 +414,21 @@ public class LodRenderer
}
GL20.glBindTexture(GL20.GL_TEXTURE_2D, 0);
//================//
// render cleanup //
//================//
// if this cleanup isn't done MC may crash
// if this cleanup isn't done MC will crash
// when trying to render its own terrain
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
GL20.glDisableVertexAttribArray(posAttrib);
GL20.glDisableVertexAttribArray(colAttrib);
// GL20.glDisableVertexAttribArray(blockSkyLightAttrib);
GL20.glDisableVertexAttribArray(blockLightAttrib);
}
@@ -387,8 +455,10 @@ public class LodRenderer
// end of internal LOD profiling
profiler.pop();
}
// Temporary variables James was using while working with the shader lightmap
int[] testArray = null;
int testInt = 0;
@@ -396,14 +466,14 @@ public class LodRenderer
/** This is where the actual drawing happens. */
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib)
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib, int blockLightAttrib, int blockSkyLightAttrib)
{
if (glBufferId == 0)
return;
// can be used to check for OpenGL errors
// int error = GL15.glGetError();
// ClientProxy.LOGGER.info(Integer.toHexString(error));
// ClientApi.LOGGER.info(Integer.toHexString(error));
// bind the buffer we are going to draw
@@ -411,11 +481,15 @@ public class LodRenderer
GL30.glBindVertexArray(GLProxy.getInstance().vertexArrayObjectId);
// let OpenGL know how our buffer is set up
int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4);
int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4) + Byte.BYTES + Byte.BYTES; // TODO move this into the template
GL20.glEnableVertexAttribArray(posAttrib);
GL20.glVertexAttribPointer(posAttrib, 3, GL15.GL_FLOAT, false, vertexByteCount, 0);
GL20.glEnableVertexAttribArray(colAttrib);
GL20.glVertexAttribPointer(colAttrib, 4, GL15.GL_UNSIGNED_BYTE, true, vertexByteCount, Float.BYTES * 3);
GL20.glEnableVertexAttribArray(blockLightAttrib);
GL20.glVertexAttribPointer(blockLightAttrib, 1, GL15.GL_UNSIGNED_BYTE, false, vertexByteCount, Float.BYTES * (3 + 1));
// GL20.glEnableVertexAttribArray(blockSkyLightAttrib);
// GL20.glVertexAttribPointer(blockSkyLightAttrib, 1, GL15.GL_UNSIGNED_BYTE, false, vertexByteCount, Float.BYTES * (3 + 1 + 1));
// draw the LODs
@@ -462,12 +536,12 @@ public class LodRenderer
// far fog //
if (CONFIG.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR)
fogConfig.farFogStart = farPlaneBlockDistance * 1.6f * 0.9f;
fogConfig.farFogStart = farPlaneBlockDistance * 0.9f;
else
// for more realistic fog when using FAR
fogConfig.farFogStart = Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f);
fogConfig.farFogStart = Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f);
fogConfig.farFogEnd = farPlaneBlockDistance * 1.6f;
fogConfig.farFogEnd = farPlaneBlockDistance;
// near fog //
@@ -510,9 +584,10 @@ public class LodRenderer
// translate the camera relative to the regions' center
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
AbstractBlockPosWrapper bufferPos = vbosCenter.getWorldPosition();
double xDiff = projectedView.x - bufferPos.getX();
double zDiff = projectedView.z - bufferPos.getZ();
int bufferPosX = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterX, LodUtil.CHUNK_DETAIL_LEVEL);
int bufferPosZ = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterZ, LodUtil.CHUNK_DETAIL_LEVEL);
double xDiff = projectedView.x - bufferPosX;
double zDiff = projectedView.z - bufferPosZ;
mcModelViewMatrix.multiplyTranslationMatrix(-xDiff, -projectedView.y, -zDiff);
return mcModelViewMatrix;
@@ -524,9 +599,10 @@ public class LodRenderer
*/
private Vec3f getTranslatedCameraPos()
{
AbstractBlockPosWrapper worldCenter = vbosCenter.getWorldPosition();
int worldCenterX = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterX, LodUtil.CHUNK_DETAIL_LEVEL);
int worldCenterZ = LevelPosUtil.convert(LodUtil.REGION_DETAIL_LEVEL, vbosCenterZ, LodUtil.CHUNK_DETAIL_LEVEL);
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
return new Vec3f((float)cameraPos.x - worldCenter.getX(), (float)cameraPos.y, (float)cameraPos.z - worldCenter.getZ());
return new Vec3f((float)cameraPos.x - worldCenterX, (float)cameraPos.y, (float)cameraPos.z - worldCenterZ);
}
/**
@@ -602,7 +678,8 @@ public class LodRenderer
VertexBuffersAndOffset result = lodBufferBuilderFactory.getVertexBuffers();
vbos = result.vbos;
storageBufferIds = result.storageBufferIds;
vbosCenter = result.drawableCenterChunkPos;
vbosCenterX = result.drawableCenterChunkPosX;
vbosCenterZ = result.drawableCenterChunkPosZ;
}
/** Calls the BufferBuilder's destroyBuffers method. */
@@ -662,7 +739,7 @@ public class LodRenderer
}
/*
// determine how far the lighting has to
// change in order to rebuild the buffers
@@ -694,12 +771,6 @@ public class LodRenderer
fullRegen = true;
prevBrightness = MC_RENDER.getGamma();
prevSkyBrightness = skyBrightness;
}
/*if (lightMap != lastLightMap)
{
fullRegen = true;
lastLightMap = lightMap;
}*/
//================//
@@ -85,11 +85,11 @@ public class RenderUtil
* Returns true if one of the region's 4 corners is in front
* of the camera.
*/
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, AbstractBlockPosWrapper vboCenterPos)
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, int vboCenterPosX, int vboCenterPosZ)
{
// convert the vbo position into a direction vector
// starting from the player's position
Vec3f vboVec = new Vec3f(vboCenterPos.getX(), 0, vboCenterPos.getZ());
Vec3f vboVec = new Vec3f(vboCenterPosX, 0, vboCenterPosZ);
Vec3f playerVec = new Vec3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
vboVec.subtract(playerVec);
@@ -21,14 +21,13 @@ package com.seibel.lod.core.render.shader;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.lwjgl.opengl.GL20;
import com.seibel.lod.core.api.ClientApi;
/**
* This object holds a OpenGL reference to a shader
* and allows for reading in and compiling a shader file.
@@ -59,14 +58,23 @@ public class LodShader
* @param absoluteFilePath If false the file path is relative to the resource jar folder.
* @throws Exception if the shader fails to compile
*/
public static LodShader loadShader(int type, String path, boolean absoluteFilePath) throws Exception
public static LodShader loadShader(int type, String path, boolean absoluteFilePath)
{
StringBuilder stringBuilder = new StringBuilder();
try
{
// open the file
InputStream in = absoluteFilePath ? new FileInputStream(path) : LodShader.class.getClassLoader().getResourceAsStream(path);
InputStream in;
if (absoluteFilePath) {
// Throws FileNotFoundException
in = new FileInputStream(path); // Note: this should use OS path seperator
} else {
in = LodShader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/'
if (in == null) {
throw new FileNotFoundException("Shader file not found in resource: "+path);
}
}
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// read in the file
@@ -76,7 +84,7 @@ public class LodShader
}
catch (IOException e)
{
ClientApi.LOGGER.error("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
}
CharSequence shaderFileSource = stringBuilder.toString();
@@ -90,7 +98,7 @@ public class LodShader
* @param source Source of the shader
* @throws Exception if the shader fails to compile
*/
public static LodShader createShader(int type, CharSequence source) throws Exception
public static LodShader createShader(int type, CharSequence source)
{
LodShader shader = new LodShader(type);
GL20.glShaderSource(shader.id, source);
@@ -103,14 +111,14 @@ public class LodShader
* Compiles the shader and checks its status afterwards.
* @throws Exception if the shader fails to compile
*/
public void compile() throws Exception
public void compile()
{
GL20.glCompileShader(id);
// check if the shader compiled
int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS);
if (status != GL20.GL_TRUE)
throw new Exception(GL20.glGetShaderInfoLog(id));
throw new RuntimeException("Shader compiler error. Details: "+GL20.glGetShaderInfoLog(id));
}
}
@@ -73,7 +73,7 @@ public class LodShaderProgram
* Links the shader program to the current OpenGL context.
* @throws Exception Exception if the program failed to link
*/
public void link() throws Exception
public void link()
{
GL20.glLinkProgram(this.id);
checkLinkStatus();
@@ -83,11 +83,11 @@ public class LodShaderProgram
* Checks if the program was linked successfully.
* @throws Exception if the program failed to link
*/
public void checkLinkStatus() throws Exception
public void checkLinkStatus()
{
int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS);
if (status != GL20.GL_TRUE)
throw new Exception(GL20.glGetProgramInfoLog(this.id));
throw new RuntimeException("Shader Link Error. Details: "+GL20.glGetProgramInfoLog(this.id));
}
@@ -57,9 +57,9 @@ public class DataPointUtil
//To be used in the future for negative value
//public final static int MIN_DEPTH = -64;
//public final static int MIN_HEIGHT = -64;
public final static byte EMPTY_DATA = 0;
public static final short VERTICAL_OFFSET = -2048;
public static int WORLD_HEIGHT = 4096;
public final static int EMPTY_DATA = 0;
public static final short VERTICAL_OFFSET = -64;
public static int WORLD_HEIGHT = 1024;
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
@@ -68,159 +68,167 @@ public class DataPointUtil
//public final static int RED_COLOR_SHIFT = 16;
//public final static int ALPHA_COLOR_SHIFT = 24;
public final static byte BLUE_SHIFT = 0;
public final static byte GREEN_SHIFT = 8;
public final static byte RED_SHIFT = 16;
public final static byte ALPHA_SHIFT = 24;
public final static int BLUE_SHIFT = 36;
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
public final static int RED_SHIFT = BLUE_SHIFT + 16;
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
//public final static byte COLOR_SHIFT = 36;
public final static int COLOR_SHIFT = 36;
public final static byte HEIGHT_SHIFT = 20;
public final static byte DEPTH_SHIFT = 8;
public final static byte BLOCK_LIGHT_SHIFT = 4;
public final static byte SKY_LIGHT_SHIFT = 0;
//public final static byte LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
//public final static byte VERTICAL_INDEX_SHIFT = 6;
public final static byte FLAG_SHIFT = 5;
public final static byte GEN_TYPE_SHIFT = 2;
public final static byte VOID_SHIFT = 1;
public final static byte EXISTENCE_SHIFT = 0;
public final static int HEIGHT_SHIFT = 26;
public final static int DEPTH_SHIFT = 16;
public final static int BLOCK_LIGHT_SHIFT = 12;
public final static int SKY_LIGHT_SHIFT = 8;
//public final static int LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
//public final static int VERTICAL_INDEX_SHIFT = 6;
public final static int FLAG_SHIFT = 5;
public final static int GEN_TYPE_SHIFT = 2;
public final static int VOID_SHIFT = 1;
public final static int EXISTENCE_SHIFT = 0;
public final static int ALPHA_MASK = 0xFF;
public final static int RED_MASK = 0xFF;
public final static int GREEN_MASK = 0xFF;
public final static int BLUE_MASK = 0xFF;
public final static int COLOR_MASK = 0xFFFFFFFF;
public final static int HEIGHT_MASK = 0xFFF;
public final static int DEPTH_MASK = 0xFFF;
public final static int LIGHTS_MASK = 0xFF;
public final static int BLOCK_LIGHT_MASK = 0xF;
public final static int SKY_LIGHT_MASK = 0xF;
public final static int VERTICAL_INDEX_MASK = 0x3;
public final static byte FLAG_MASK = 0x1;
public final static byte GEN_TYPE_MASK = 0x7;
public final static byte VOID_MASK = 1;
public final static byte EXISTENCE_MASK = 1;
public final static long ALPHA_MASK = 0b1111;
public final static long RED_MASK = 0b1111_1111;
public final static long GREEN_MASK = 0b1111_1111;
public final static long BLUE_MASK = 0b1111_1111;
public final static long COLOR_MASK = 0b11111111_11111111_11111111;
public final static long HEIGHT_MASK = 0b11_1111_1111;
public final static long DEPTH_MASK = 0b11_1111_1111;
//public final static long LIGHTS_MASK = 0b1111_1111;
public final static long BLOCK_LIGHT_MASK = 0b1111;
public final static long SKY_LIGHT_MASK = 0b1111;
//public final static long VERTICAL_INDEX_MASK = 0b11;
public final static long FLAG_MASK = 0b1;
public final static long GEN_TYPE_MASK = 0b111;
public final static long VOID_MASK = 1;
public final static long EXISTENCE_MASK = 1;
/** Returns the Flags byte */
public static byte createVoidDataPoint(byte generationMode)
public static long createVoidDataPoint(int generationMode)
{
generationMode = (byte) ((generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT);
generationMode |= VOID_MASK << VOID_SHIFT;
generationMode |= EXISTENCE_MASK << EXISTENCE_SHIFT;
return generationMode;
long dataPoint = 0;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
dataPoint += VOID_MASK << VOID_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
return dataPoint;
}
/** Returned datapoint is in ThreadMapUtil */
public static void createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag)
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag)
{
int data = (height & HEIGHT_MASK) << HEIGHT_SHIFT;
data += (depth & DEPTH_MASK) << DEPTH_SHIFT;
data += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
data += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
byte flags = (byte) ((generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT);
if (flag) flags += FLAG_MASK << FLAG_SHIFT;
flags += EXISTENCE_MASK << EXISTENCE_SHIFT;
ThreadMapUtil.saveDataPoint(color, data, flags);
return createDataPoint(
ColorUtil.getAlpha(color),
ColorUtil.getRed(color),
ColorUtil.getGreen(color),
ColorUtil.getBlue(color),
height, depth, lightSky, lightBlock, generationMode, flag);
}
public static void createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
{
createDataPoint(
height, depth,
(alpha << ALPHA_SHIFT) | (red << RED_SHIFT) | (green << GREEN_SHIFT) | blue,
lightSky, lightBlock, generationMode, flag);
long dataPoint = 0;
dataPoint += (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT;
dataPoint += (red & RED_MASK) << RED_SHIFT;
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
dataPoint += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
dataPoint += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
if (flag) dataPoint += FLAG_MASK << FLAG_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
return dataPoint;
}
public static short getHeight(int data)
public static short getHeight(long dataPoint)
{
return (short) ((data >>> HEIGHT_SHIFT) & HEIGHT_MASK);
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
}
public static short getDepth(int data)
public static short getDepth(long dataPoint)
{
return (short) ((data >>> DEPTH_SHIFT) & DEPTH_MASK);
return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK);
}
public static short getAlpha(int color)
public static short getAlpha(long dataPoint)
{
return (short) ((color >>> ALPHA_SHIFT) & ALPHA_MASK);
return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111);
}
public static short getRed(int color)
public static short getRed(long dataPoint)
{
return (short) ((color >>> RED_SHIFT) & RED_MASK);
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
}
public static short getGreen(int color)
public static short getGreen(long dataPoint)
{
return (short) ((color >>> GREEN_SHIFT) & GREEN_MASK);
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
}
public static short getBlue(int color)
public static short getBlue(long dataPoint)
{
return (short) ((color >>> BLUE_SHIFT) & BLUE_MASK);
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
}
public static byte getLightSky(int data)
public static byte getLightSky(long dataPoint)
{
return (byte) ((data >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightSkyAlt(int data, byte flags)
public static byte getLightSkyAlt(long dataPoint)
{
if (skyLightPlayer == 0 && ((flags >>> FLAG_SHIFT) & FLAG_MASK) == 1)
if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1)
return 0;
else
return (byte) ((data >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightBlock(int data)
public static byte getLightBlock(long dataPoint)
{
return (byte) ((data >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
public static boolean getFlag(byte flags)
public static boolean getFlag(long dataPoint)
{
return ((flags >>> FLAG_SHIFT) & FLAG_MASK) == 1;
return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1;
}
public static byte getGenerationMode(byte flags)
public static byte getGenerationMode(long dataPoint)
{
return (byte) ((flags >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
}
public static boolean isVoid(byte flags)
public static boolean isVoid(long dataPoint)
{
return (((flags >>> VOID_SHIFT) & VOID_MASK) == 1);
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
}
public static boolean doesItExist(byte flags)
public static boolean doesItExist(long dataPoint)
{
return ((flags >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1;
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
}
@Deprecated
public static int getColor(int color)
public static int getColor(long dataPoint)
{
return color;
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
}
/** This is used to convert a dataPoint to string (useful for the print function) */
@SuppressWarnings("unused")
public static String toString(int color, int data, byte flags)
public static String toString(long dataPoint)
{
return getHeight(data) + " " +
getDepth(data) + " " +
getAlpha(color) + " " +
getRed(color) + " " +
getBlue(color) + " " +
getGreen(color) + " " +
getLightBlock(data) + " " +
getLightSky(data) + " " +
getGenerationMode(flags) + " " +
isVoid(flags) + " " +
doesItExist(flags) + '\n';
return getHeight(dataPoint) + " " +
getDepth(dataPoint) + " " +
getAlpha(dataPoint) + " " +
getRed(dataPoint) + " " +
getBlue(dataPoint) + " " +
getGreen(dataPoint) + " " +
getLightBlock(dataPoint) + " " +
getLightSky(dataPoint) + " " +
getGenerationMode(dataPoint) + " " +
isVoid(dataPoint) + " " +
doesItExist(dataPoint) + '\n';
}
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
@@ -250,30 +258,26 @@ public class DataPointUtil
/**
* This method merge column of multiple data together
* Returned datapoint is in ThreadMapUtil
* @param dataToMergeColor colors of one or more columns of data
* @param dataToMergeData data of one or more columns of data
* @param dataToMergeFlags flags of one or more columns of data
* @param dataToMerge one or more columns of data
* @param inputVerticalData vertical size of an input data
* @param maxVerticalData max vertical size of the merged data
* @return one column of correctly parsed data
*/
public static void mergeMultiData(int[] dataToMergeColor, int[] dataToMergeData, byte[] dataToMergeFlags, int inputVerticalData, int maxVerticalData)
@Deprecated
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
{
int size = dataToMergeData.length / inputVerticalData;
int size = dataToMerge.length / inputVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((WORLD_HEIGHT / 2 + 1) * 2);
int[] dataPointColor = ThreadMapUtil.getVerticalDataArrayColor(DetailDistanceUtil.getMaxVerticalData(0));
int[] dataPointData = ThreadMapUtil.getVerticalDataArrayData(DetailDistanceUtil.getMaxVerticalData(0));
byte[] dataPointFlags = ThreadMapUtil.getVerticalDataArrayFlags(DetailDistanceUtil.getMaxVerticalData(0));
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
byte genMode = DistanceGenerationMode.FULL.complexity;
int genMode = DistanceGenerationMode.FULL.complexity;
boolean allEmpty = true;
boolean allVoid = true;
boolean allDefault;
int singleDataData;
byte singleDataFlags;
long singleData;
short depth;
@@ -287,17 +291,16 @@ public class DataPointUtil
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleDataData = dataToMergeData[index * inputVerticalData + dataIndex];
singleDataFlags = dataToMergeFlags[index * inputVerticalData + dataIndex];
if (doesItExist(singleDataFlags))
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData))
{
genMode = (byte) Math.min(genMode, getGenerationMode(singleDataFlags));
genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleDataFlags))
if (!isVoid(singleData))
{
allVoid = false;
depth = getDepth(singleDataData);
height = getHeight(singleDataData);
depth = getDepth(singleData);
height = getHeight(singleData);
int botPos = -1;
int topPos = -1;
@@ -400,11 +403,11 @@ public class DataPointUtil
//We check if there is any data that's not empty or void
if (allEmpty)
return;
return dataPoint;
if (allVoid)
{
dataPointFlags[0] = createVoidDataPoint(genMode);
return;
dataPoint[0] = createVoidDataPoint(genMode);
return dataPoint;
}
//we limit the vertical portion to maxVerticalData
@@ -449,90 +452,71 @@ public class DataPointUtil
allEmpty = true;
allVoid = true;
allDefault = true;
int singleDataColor;
int data = EMPTY_DATA;
int color = EMPTY_DATA;
byte flags = EMPTY_DATA;
long data = 0;
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleDataColor = dataToMergeColor[index * inputVerticalData + dataIndex];
singleDataData = dataToMergeData[index * inputVerticalData + dataIndex];
singleDataFlags = dataToMergeFlags[index * inputVerticalData + dataIndex];
if (doesItExist(singleDataFlags) && !isVoid(singleDataFlags))
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData) && !isVoid(singleData))
{
if ((depth <= getDepth(singleDataData) && getDepth(singleDataData) <= height)
|| (depth <= getHeight(singleDataData) && getHeight(singleDataData) <= height))
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
{
if (getHeight(singleDataData) > getHeight(data))
{
color = singleDataColor;
data = singleDataData;
flags = singleDataFlags;
}
if (getHeight(singleData) > getHeight(data))
data = singleData;
}
}
else
break;
}
if (!doesItExist(flags))
if (!doesItExist(data))
{
singleDataFlags = dataToMergeFlags[index * inputVerticalData];
if (doesItExist(singleDataFlags))
flags = createVoidDataPoint(getGenerationMode(singleDataFlags));
else
flags = createVoidDataPoint((byte) 0);
data = EMPTY_DATA;
color = EMPTY_DATA;
singleData = dataToMerge[index * inputVerticalData];
data = createVoidDataPoint(getGenerationMode(singleData));
}
if (doesItExist(flags))
if (doesItExist(data))
{
allEmpty = false;
if (!isVoid(flags))
if (!isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha += getAlpha(color);
tempRed += getRed(color);
tempGreen += getGreen(color);
tempBlue += getBlue(color);
tempAlpha += getAlpha(data);
tempRed += getRed(data);
tempGreen += getGreen(data);
tempBlue += getBlue(data);
tempLightBlock += getLightBlock(data);
tempLightSky += getLightSky(data);
if (!getFlag(flags))
allDefault = false;
if (!getFlag(data)) allDefault = false;
}
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(flags));
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
}
else
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
}
if (!allEmpty)
if (allEmpty)
//no child has been initialized
dataPoint[j] = EMPTY_DATA;
else if (allVoid)
//all the children are void
dataPoint[j] = createVoidDataPoint(tempGenMode);
else
{
//child has been initialized
if (allVoid)
{
//all the children are void
dataPointFlags[j] = createVoidDataPoint(tempGenMode);
}
else
{
//we have at least 1 child
tempAlpha = tempAlpha / numberOfChildren;
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
dataPointColor[j] = ThreadMapUtil.dataPointColor;
dataPointData[j] = ThreadMapUtil.dataPointData;
dataPointFlags[j] = ThreadMapUtil.dataPointFlags;
}
//we have at least 1 child
tempAlpha = tempAlpha / numberOfChildren;
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
}
}
return dataPoint;
}
}
@@ -44,7 +44,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
* This class holds methods and constants that may be used in multiple places.
*
* @author James Seibel
* @version 11-13-2021
* @version 12-8-2021
*/
public class LodUtil
{
@@ -140,7 +140,7 @@ public class LodUtil
public static final int MAX_ALLOCATABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
/** the format of data stored in the GPU buffers */
public static final LodVertexFormat LOD_VERTEX_FORMAT = DefaultLodVertexFormats.POSITION_COLOR;
public static final LodVertexFormat LOD_VERTEX_FORMAT = DefaultLodVertexFormats.POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT;
@@ -38,18 +38,15 @@ import com.seibel.lod.core.objects.VertexOptimizer;
*/
public class ThreadMapUtil
{
public static final ConcurrentMap<String, int[][]> threadBuilderVerticalArrayMapColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> threadBuilderVerticalArrayMapData = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[][]> threadBuilderVerticalArrayMapFlags = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[]> threadVerticalAddDataMapColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[]> threadVerticalAddDataMapData = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[]> threadVerticalAddDataMapFlags = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[]> threadSingleUpdateMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> threadBuilderArrayMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> threadBuilderVerticalArrayMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[]> threadVerticalAddDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[][]> saveContainer = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, short[]> projectionArrayMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, short[]> heightAndDepthMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> verticalUpdateColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, int[][]> verticalUpdateData = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, byte[][]> verticalUpdateFlags = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[]> singleDataToMergeMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, long[][]> verticalUpdate = new ConcurrentHashMap<>();
//________________________//
@@ -57,14 +54,9 @@ public class ThreadMapUtil
//________________________//
public static final ConcurrentMap<String, boolean[]> adjShadeDisabled = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Map<LodDirection, int[]>> adjDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Map<LodDirection, byte[]>> adjFlagsMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, Map<LodDirection, long[]>> adjDataMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<String, VertexOptimizer> boxMap = new ConcurrentHashMap<>();
public static int dataPointColor = 0;
public static int dataPointData = 0;
public static byte dataPointFlags = 0;
/** returns the array NOT cleared every time */
@@ -80,7 +72,7 @@ public class ThreadMapUtil
}
/** returns the array NOT cleared every time */
public static Map<LodDirection, int[]> getAdjDataArray(int verticalData)
public static Map<LodDirection, long[]> getAdjDataArray(int verticalData)
{
if (!adjDataMap.containsKey(Thread.currentThread().getName())
|| (adjDataMap.get(Thread.currentThread().getName()) == null)
@@ -88,41 +80,20 @@ public class ThreadMapUtil
|| (adjDataMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH).length != verticalData))
{
adjDataMap.put(Thread.currentThread().getName(), new HashMap<>());
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new int[1]);
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new int[1]);
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new long[1]);
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new long[1]);
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new int[verticalData]);
adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new long[verticalData]);
}
else
{
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), 0);
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), DataPointUtil.EMPTY_DATA);
}
return adjDataMap.get(Thread.currentThread().getName());
}
/** returns the array NOT cleared every time */
public static Map<LodDirection, byte[]> getAdjFlagsArray(int verticalData)
{
if (!adjFlagsMap.containsKey(Thread.currentThread().getName())
|| (adjFlagsMap.get(Thread.currentThread().getName()) == null)
|| (adjFlagsMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH) == null)
|| (adjFlagsMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH).length != verticalData))
{
adjFlagsMap.put(Thread.currentThread().getName(), new HashMap<>());
adjFlagsMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new byte[1]);
adjFlagsMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new byte[1]);
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
adjFlagsMap.get(Thread.currentThread().getName()).put(lodDirection, new byte[verticalData]);
}
else
{
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
Arrays.fill(adjFlagsMap.get(Thread.currentThread().getName()).get(lodDirection), (byte) 0);
}
return adjFlagsMap.get(Thread.currentThread().getName());
}
public static VertexOptimizer getBox()
{
if (!boxMap.containsKey(Thread.currentThread().getName())
@@ -148,57 +119,21 @@ public class ThreadMapUtil
/** returns the array filled with 0's */
public static int[] getBuilderVerticalArrayColor(int detailLevel)
public static long[] getBuilderVerticalArray(int detailLevel)
{
if (!threadBuilderVerticalArrayMapColor.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMapColor.get(Thread.currentThread().getName()) == null))
if (!threadBuilderVerticalArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMap.get(Thread.currentThread().getName()) == null))
{
int[][] array = new int[5][];
long[][] array = new long[5][];
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new int[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
array[i] = new long[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
}
threadBuilderVerticalArrayMapColor.put(Thread.currentThread().getName(), array);
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
}
Arrays.fill(threadBuilderVerticalArrayMapColor.get(Thread.currentThread().getName())[detailLevel], 0);
return threadBuilderVerticalArrayMapColor.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static int[] getBuilderVerticalArrayData(int detailLevel)
{
if (!threadBuilderVerticalArrayMapData.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMapData.get(Thread.currentThread().getName()) == null))
{
int[][] array = new int[5][];
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new int[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
}
threadBuilderVerticalArrayMapData.put(Thread.currentThread().getName(), array);
}
Arrays.fill(threadBuilderVerticalArrayMapData.get(Thread.currentThread().getName())[detailLevel], 0);
return threadBuilderVerticalArrayMapData.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static byte[] getBuilderVerticalArrayFlags(int detailLevel)
{
if (!threadBuilderVerticalArrayMapFlags.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMapFlags.get(Thread.currentThread().getName()) == null))
{
byte[][] array = new byte[5][];
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new byte[size * size * (DataPointUtil.WORLD_HEIGHT / 2 + 1)];
}
threadBuilderVerticalArrayMapFlags.put(Thread.currentThread().getName(), array);
}
Arrays.fill(threadBuilderVerticalArrayMapFlags.get(Thread.currentThread().getName())[detailLevel], (byte) 0);
return threadBuilderVerticalArrayMapFlags.get(Thread.currentThread().getName())[detailLevel];
Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0);
return threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array NOT cleared every time */
@@ -210,7 +145,7 @@ public class ThreadMapUtil
int size = 1;
for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--)
{
array[i] = new byte[2 + 9 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
size = size << 1;
}
saveContainer.put(Thread.currentThread().getName(), array);
@@ -221,46 +156,19 @@ public class ThreadMapUtil
/** returns the array filled with 0's */
public static int[] getVerticalDataArrayColor(int arrayLength)
public static long[] getVerticalDataArray(int arrayLength)
{
if (!threadVerticalAddDataMapColor.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMapColor.get(Thread.currentThread().getName()) == null))
threadVerticalAddDataMapColor.put(Thread.currentThread().getName(), new int[arrayLength]);
if (!threadVerticalAddDataMap.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMap.get(Thread.currentThread().getName()) == null))
{
threadVerticalAddDataMap.put(Thread.currentThread().getName(), new long[arrayLength]);
}
else
Arrays.fill(threadVerticalAddDataMapColor.get(Thread.currentThread().getName()), 0);
return threadVerticalAddDataMapColor.get(Thread.currentThread().getName());
}
public static int[] getRawVerticalDataArrayColor()
{
return threadVerticalAddDataMapColor.get(Thread.currentThread().getName());
{
Arrays.fill(threadVerticalAddDataMap.get(Thread.currentThread().getName()), 0);
}
return threadVerticalAddDataMap.get(Thread.currentThread().getName());
}
/** returns the array filled with 0's */
public static int[] getVerticalDataArrayData(int arrayLength)
{
if (!threadVerticalAddDataMapData.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMapData.get(Thread.currentThread().getName()) == null))
threadVerticalAddDataMapData.put(Thread.currentThread().getName(), new int[arrayLength]);
else
Arrays.fill(threadVerticalAddDataMapData.get(Thread.currentThread().getName()), 0);
return threadVerticalAddDataMapData.get(Thread.currentThread().getName());
}
public static int[] getRawVerticalDataArrayData()
{
return threadVerticalAddDataMapData.get(Thread.currentThread().getName());
}
/** returns the array filled with 0's */
public static byte[] getVerticalDataArrayFlags(int arrayLength)
{
if (!threadVerticalAddDataMapFlags.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMapFlags.get(Thread.currentThread().getName()) == null))
threadVerticalAddDataMapFlags.put(Thread.currentThread().getName(), new byte[arrayLength]);
else
Arrays.fill(threadVerticalAddDataMapFlags.get(Thread.currentThread().getName()), (byte) 0);
return threadVerticalAddDataMapFlags.get(Thread.currentThread().getName());
}
public static byte[] getRawVerticalDataArrayFlags()
{
return threadVerticalAddDataMapFlags.get(Thread.currentThread().getName());
}
/** returns the array NOT cleared every time */
@@ -273,49 +181,22 @@ public class ThreadMapUtil
return heightAndDepthMap.get(Thread.currentThread().getName());
}
/** returns the array filled with 0's */
public static int[] getVerticalUpdateArrayColor(int detailLevel)
{
if (!verticalUpdateColor.containsKey(Thread.currentThread().getName()) || (verticalUpdateColor.get(Thread.currentThread().getName()) == null))
{
int[][] array = new int[LodUtil.DETAIL_OPTIONS][];
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
array[i] = new int[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdateColor.put(Thread.currentThread().getName(), array);
}
else
Arrays.fill(verticalUpdateColor.get(Thread.currentThread().getName())[detailLevel], 0);
return verticalUpdateColor.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static int[] getVerticalUpdateArrayData(int detailLevel)
public static long[] getVerticalUpdateArray(int detailLevel)
{
if (!verticalUpdateData.containsKey(Thread.currentThread().getName()) || (verticalUpdateData.get(Thread.currentThread().getName()) == null))
if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null))
{
int[][] array = new int[LodUtil.DETAIL_OPTIONS][];
long[][] array = new long[LodUtil.DETAIL_OPTIONS][];
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
array[i] = new int[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdateData.put(Thread.currentThread().getName(), array);
array[i] = new long[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdate.put(Thread.currentThread().getName(), array);
}
else
Arrays.fill(verticalUpdateData.get(Thread.currentThread().getName())[detailLevel], 0);
return verticalUpdateData.get(Thread.currentThread().getName())[detailLevel];
}
/** returns the array filled with 0's */
public static byte[] getVerticalUpdateArrayFlags(int detailLevel)
{
if (!verticalUpdateFlags.containsKey(Thread.currentThread().getName()) || (verticalUpdateFlags.get(Thread.currentThread().getName()) == null))
{
byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][];
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
array[i] = new byte[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
verticalUpdateFlags.put(Thread.currentThread().getName(), array);
Arrays.fill(verticalUpdate.get(Thread.currentThread().getName())[detailLevel], 0);
}
else
Arrays.fill(verticalUpdateFlags.get(Thread.currentThread().getName())[detailLevel], (byte) 0);
return verticalUpdateFlags.get(Thread.currentThread().getName())[detailLevel];
return verticalUpdate.get(Thread.currentThread().getName())[detailLevel];
}
/** clears all arrays so they will have to be rebuilt */
@@ -324,24 +205,14 @@ public class ThreadMapUtil
adjShadeDisabled.clear();
adjDataMap.clear();
boxMap.clear();
threadBuilderVerticalArrayMapColor.clear();
threadBuilderVerticalArrayMapData.clear();
threadBuilderVerticalArrayMapFlags.clear();
threadVerticalAddDataMapColor.clear();
threadVerticalAddDataMapData.clear();
threadVerticalAddDataMapFlags.clear();
threadSingleUpdateMap.clear();
threadBuilderArrayMap.clear();
threadBuilderVerticalArrayMap.clear();
threadVerticalAddDataMap.clear();
saveContainer.clear();
projectionArrayMap.clear();
heightAndDepthMap.clear();
verticalUpdateColor.clear();
verticalUpdateData.clear();
verticalUpdateFlags.clear();
}
public static void saveDataPoint(int color, int data, byte flags)
{
dataPointColor = color;
dataPointData = data;
dataPointFlags = flags;
singleDataToMergeMap.clear();
verticalUpdate.clear();
}
}
@@ -31,6 +31,8 @@ public interface IBlockColorWrapper
boolean hasColor();
String getName();
int getColor();
@@ -23,6 +23,8 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.forge.wrappers.block.BlockShapeWrapper;
import net.minecraft.block.Block;
/**
* @author James Seibel
@@ -32,21 +34,27 @@ public interface IChunkWrapper
{
int getHeight();
boolean isPositionInWater(AbstractBlockPosWrapper blockPos);
boolean isPositionInWater(int x, int y, int z);
int getHeightMapValue(int xRel, int zRel);
IBiomeWrapper getBiome(int xRel, int yAbs, int zRel);
IBiomeWrapper getBiome(int x, int y, int z);
IBlockColorWrapper getBlockColorWrapper(int x, int y, int z);
IBlockShapeWrapper getBlockShapeWrapper(int x, int y, int z);
IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos);
IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos);
AbstractChunkPosWrapper getPos();
int getChunkPosX();
int getChunkPosZ();
int getRegionPosX();
int getRegionPosZ();
int getMaxY(int x, int z);
int getMaxX();
int getMaxZ();
int getMinX();
int getMinZ();
boolean isLightCorrect();
boolean isWaterLogged(AbstractBlockPosWrapper blockPos);
boolean isWaterLogged(int x, int y, int z);
int getEmittedBrightness(AbstractBlockPosWrapper blockPos);
int getEmittedBrightness(int x, int y, int z);
}
@@ -184,17 +184,6 @@ public interface ILodConfigWrapperSingleton
{
String DESC = "Graphics options that are a bit more technical.";
LodTemplate LOD_TEMPLATE_DEFAULT = LodTemplate.CUBIC;
String LOD_TEMPLATE_DESC = ""
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between each other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n";
LodTemplate getLodTemplate();
void setLodTemplate(LodTemplate newLodTemplate);
boolean DISABLE_DIRECTIONAL_CULLING_DEFAULT = false;
String DISABLE_DIRECTIONAL_CULLING_DESC = ""
@@ -451,7 +440,7 @@ public interface ILodConfigWrapperSingleton
boolean DEBUG_KEYBINDINGS_ENABLED_DEFAULT = true;
String DEBUG_KEYBINDINGS_ENABLED_DESC = ""
+ " If true the F4 key can be used to cycle through the different debug modes. \n"
+ " If true the F8 key can be used to cycle through the different debug modes. \n"
+ " and the F6 key can be used to enable and disable LOD rendering.";
boolean getDebugKeybindingsEnabled();
void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings);
@@ -33,7 +33,7 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
* rendering in Minecraft.
*
* @author James Seibel
* @version 11-26-2021
* @version 12-8-2021
*/
public interface IMinecraftRenderWrapper
{
@@ -66,4 +66,14 @@ public interface IMinecraftRenderWrapper
* is going to render this frame.
*/
HashSet<AbstractChunkPosWrapper> getRenderedChunks();
/** @returns null if there was a issue getting the lightmap */
int[] getLightmapPixels();
/** @returns -1 if there was an issue getting the lightmap */
int getLightmapTextureHeight();
/** @returns -1 if there was an issue getting the lightmap */
int getLightmapTextureWidth();
/** @returns -1 if there was an issue getting the lightmap */
public int getLightmapGLFormat();
}
@@ -34,7 +34,7 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
* Contains everything related to the Minecraft object.
*
* @author James Seibel
* @version 9-16-2021
* @version 12-8-2021
*/
public interface IMinecraftWrapper
{
@@ -77,18 +77,18 @@ public interface IMinecraftWrapper
/**
* Returns the color int at the given pixel coordinates
* from the current lightmap.
* @param u x location in texture space
* @param v z location in texture space
* @param blockLight x location in texture space
* @param skyLight z location in texture space
*/
int getColorIntFromLightMap(int u, int v);
int getColorIntFromLightMap(int blockLight, int skyLight);
/**
* Returns the Color at the given pixel coordinates
* from the current lightmap.
* @param u x location in texture space
* @param v z location in texture space
* @param blockLight x location in texture space
* @param skyLight z location in texture space
*/
Color getColorFromLightMap(int u, int v);
Color getColorFromLightMap(int blockLight, int skyLight);
@@ -28,6 +28,8 @@ public interface IBiomeWrapper
/** Returns a color int for the given biome. */
int getColorForBiome(int x, int z);
String getName();
int getGrassTint(int x, int z);
int getFolliageTint();
@@ -36,11 +36,9 @@ public interface IWorldWrapper
WorldType getWorldType();
int getBlockLight(AbstractBlockPosWrapper blockPos);
int getBlockLight(int x, int y, int z);
int getSkyLight(AbstractBlockPosWrapper blockPos);
IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos);
int getSkyLight(int x, int y, int z);
boolean hasCeiling();
+1 -2
View File
@@ -2,13 +2,12 @@
in vec4 vertexColor;
in vec4 vertexWorldPos;
//in vec2 textureCoord;
out vec4 fragColor;
//uniform sampler2D texImage;
uniform vec3 cameraPos;
uniform bool fogEnabled;
+20 -4
View File
@@ -2,30 +2,46 @@
in vec3 vPosition;
in vec4 color;
in float blockSkyLight;
in float blockLight;
out vec4 vertexColor;
out vec4 vertexWorldPos;
//out vec2 textureCoord;
out float depth;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform int worldSkyLight;
uniform sampler2D lightMap;
/**
* Vertex Shader
*
* author: James Seibel
* version: 11-26-2021
* version: 12-8-2021
*/
void main()
{
// just skylight
// good for sanity checks; but will cause OpenGL errors since we are binding unused data
// vertexColor = vec4(color.xyz * worldSkyLight / 16.0, color.w);
float blockLightTex = blockLight / 16.0;
float skyLightTex = blockSkyLight / 16.0;
// we don't really need alpha in the lightmap
// vertexColor = color * vec4(texture(lightMap, vec2(blockLightTex, skyLightTex)).xyz, 1);
vertexColor = color * texture(lightMap, vec2(blockLightTex, skyLightTex));
// TODO: add a simple white texture to support Optifine shaders
//textureCoord = textureCoord;
vertexColor = color;
vertexWorldPos = vec4(vPosition, 1);
// the vPosition needs to be converted to a vec4 so it can be multiplied