Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5

This commit is contained in:
James Seibel
2021-10-27 21:25:09 -05:00
22 changed files with 558 additions and 625 deletions
@@ -415,7 +415,7 @@ public class LodBufferBuilder
//We send the call to create the vertices
LodConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.get().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData,
detailLevel, posX, posZ, box, renderer.previousDebugMode, renderer.lightMap, adjShadeDisabled);
detailLevel, posX, posZ, box, renderer.previousDebugMode, adjShadeDisabled);
}
@@ -25,7 +25,6 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
@@ -40,7 +39,7 @@ public abstract class AbstractLodTemplate
/** Uploads the given LOD to the buffer. */
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled);
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled);
/** add the given position and color to the buffer */
protected void addPosAndColor(BufferBuilder buffer,
@@ -25,10 +25,8 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
@@ -47,7 +45,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
{
if (box == null)
return;
@@ -128,7 +126,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
color = box.getColor(direction);
skyLight = box.getSkyLight(direction, verticalFaceIndex);
blockLight = box.getBlockLight();
color = ColorUtil.applyLightValue(color, skyLight, blockLight, MinecraftWrapper.INSTANCE.getCurrentLightMap());
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
addPosAndColor(buffer,
box.getX(direction, vertexIndex),
box.getY(direction, vertexIndex, verticalFaceIndex),
@@ -25,7 +25,6 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
@@ -41,7 +40,7 @@ public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
@@ -25,7 +25,6 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
@@ -39,7 +38,7 @@ public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
@@ -19,22 +19,15 @@
package com.seibel.lod.builders.lodBuilding;
import java.awt.Color;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
@@ -42,47 +35,24 @@ import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Block.BlockColorWrapper;
import com.seibel.lod.wrappers.Block.BlockShapeWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.AbstractTopPlantBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.BushBlock;
import net.minecraft.block.FlowerBlock;
import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.SixWayBlock;
import net.minecraft.block.TallGrassBlock;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.data.BlockModelProvider;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.shapes.VoxelShape;
import com.seibel.lod.wrappers.World.BiomeColorWrapper;
import com.seibel.lod.wrappers.World.BiomeWrapper;
import com.seibel.lod.wrappers.World.WorldWrapper;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
import net.minecraft.world.LightType;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeColors;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.client.model.data.ModelDataMap;
/**
* This object is in charge of creating Lod related objects.
*
*
* @author Cola
* @author Leonardo Amato
* @author James Seibel
@@ -94,18 +64,6 @@ public class LodBuilder
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final ConcurrentMap<Block, Integer> colorMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Integer> tintColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> toTint = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> notFullBlock = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> smallBlock = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
/** If no blocks are found in the area in determineBottomPointForArea return this */
public static final short DEFAULT_DEPTH = 0;
@@ -131,12 +89,12 @@ public class LodBuilder
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
public void generateLodNodeAsync(ChunkWrapper chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
public void generateLodNodeAsync(ChunkWrapper chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || lodWorld.getIsWorldNotLoaded())
return;
@@ -191,7 +149,7 @@ public class LodBuilder
* Creates a LodNode for a chunk in the given world.
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException
public void generateLodNodeFromChunk(LodDimension lodDim, ChunkWrapper chunk) throws IllegalArgumentException
{
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
}
@@ -200,7 +158,7 @@ public class LodBuilder
* Creates a LodNode for a chunk in the given world.
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config)
public void generateLodNodeFromChunk(LodDimension lodDim, ChunkWrapper chunk, LodBuilderConfig config)
throws IllegalArgumentException
{
if (chunk == null)
@@ -233,36 +191,33 @@ public class LodBuilder
{
startX = detail.startX[i];
startZ = detail.startZ[i];
endX = detail.endX[i];
endZ = detail.endZ[i];
long[] data;
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ, endX, endZ);
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
//lodDim.clear(detailLevel, posX, posZ);
if (data != null && data.length != 0)
{
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().x * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().z * 16 + startZ, detail.detailLevel);
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, data, false);
}
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
}
/** creates a vertical DataPoint */
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ)
private long[] createVerticalDataToMerge(HorizontalResolution detail, ChunkWrapper 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.worldHeight / 2 + 1;
ChunkPos chunkPos = chunk.getPos();
ChunkPosWrapper chunkPos = chunk.getPos();
int height;
int depth;
int color;
@@ -279,7 +234,7 @@ public class LodBuilder
boolean hasCeiling = mc.getClientWorld().dimensionType().hasCeiling();
boolean hasSkyLight = mc.getClientWorld().dimensionType().hasSkyLight();
boolean isDefault;
BlockPos.Mutable blockPos = new BlockPos.Mutable(0, 0, 0);
BlockPosWrapper blockPos = new BlockPosWrapper();
int index;
for (index = 0; index < size * size; index++)
@@ -295,7 +250,7 @@ public class LodBuilder
boolean topBlock = true;
while (yAbs > 0)
{
height = determineHeightPointFrom(chunk, config, xRel, zRel, yAbs, blockPos);
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)
@@ -307,13 +262,13 @@ public class LodBuilder
yAbs = height - 1;
// We search light on above air block
depth = determineBottomPointFrom(chunk, config, xRel, zRel, yAbs, blockPos);
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
if (hasCeiling && topBlock)
{
yAbs = depth;
blockPos.set(xAbs, yAbs, zAbs);
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, blockPos);
blockPos.set(xAbs, yAbs - 1, zAbs);
}
else
@@ -340,67 +295,38 @@ public class LodBuilder
* Find the lowest valid point from the bottom.
* Used when creating a vertical LOD.
*/
private short determineBottomPointFrom(IChunk chunk, LodBuilderConfig config, int xRel, int zRel, int yAbs, BlockPos.Mutable blockPos)
private short determineBottomPointFrom(ChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, BlockPosWrapper blockPos)
{
short depth = DEFAULT_DEPTH;
/*if (config.useHeightmap)
for (int y = yAbs; y >= 0; y--)
{
// when using the generated heightmap there is no data about the lowest point
depth = 0; //DEFAULT_DEPTH == 0
}
else
{*/
boolean voidData = true;
ChunkSection[] chunkSections = chunk.getSections();
for (int sectionIndex = chunkSections.length - 1; sectionIndex >= 0; sectionIndex--)
{
for (int yRel = CHUNK_DATA_WIDTH - 1; yRel >= 0; yRel--)
blockPos.set(xAbs, y, zAbs);
if (!isLayerValidLodPoint(chunk, blockPos))
{
if (sectionIndex * CHUNK_DATA_WIDTH + yRel > yAbs)
continue;
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (!isLayerValidLodPoint(chunk, blockPos))
{
depth = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel + 1);
voidData = false;
break;
}
}
if (!voidData)
depth = (short) (y + 1);
break;
}
}
//}
return depth;
}
/** Find the highest valid point from the Top */
private short determineHeightPointFrom(IChunk chunk, LodBuilderConfig config, int xRel, int zRel, int yAbs, BlockPos.Mutable blockPos)
private short determineHeightPointFrom(ChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, BlockPosWrapper blockPos)
{
short height = DEFAULT_HEIGHT;
if (config.useHeightmap)
height = (short) chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
else
{
boolean voidData = true;
ChunkSection[] chunkSections = chunk.getSections();
for (int sectionIndex = chunkSections.length - 1; sectionIndex >= 0; sectionIndex--)
for (int y = yAbs; y >= 0; y--)
{
for (int yRel = CHUNK_DATA_WIDTH - 1; yRel >= 0; yRel--)
blockPos.set(xAbs, y, zAbs);
if (isLayerValidLodPoint(chunk, blockPos))
{
if (sectionIndex * CHUNK_DATA_WIDTH + yRel > yAbs)
continue;
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (isLayerValidLodPoint(chunk, blockPos))
{
height = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel + 1);
voidData = false;
break;
}
}
if (!voidData)
height = (short) (y + 1);
break;
}
}
}
return height;
@@ -416,26 +342,19 @@ public class LodBuilder
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*/
private int generateLodColor(IChunk chunk, LodBuilderConfig config, int xRel, int yAbs, int zRel, BlockPos.Mutable blockPos)
private int generateLodColor(ChunkWrapper chunk, LodBuilderConfig config, int xRel, int yAbs, int zRel, BlockPosWrapper blockPos)
{
ChunkSection[] chunkSections = chunk.getSections();
int colorInt = 0;
if (config.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.
Biome biome = chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2);
colorInt = getColorForBiome(xRel, zRel, biome);
colorInt = chunk.getBiome(xRel, yAbs, zRel).getColorForBiome(xRel, zRel);
}
else
{
int sectionIndex = Math.floorDiv(yAbs, CHUNK_SECTION_HEIGHT);
int yRel = Math.floorMod(yAbs, CHUNK_SECTION_HEIGHT);
if (chunkSections[sectionIndex] != null)
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
colorInt = getColorForBlock(chunk, blockPos);
}
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, chunk.getPos().getMinBlockZ() + zRel);
colorInt = getColorForBlock(chunk, blockPos);
// 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
@@ -443,13 +362,13 @@ public class LodBuilder
int aboveColorInt = 0;
if (LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull || LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision)
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel + 1, chunk.getPos().getMinBlockZ() + zRel);
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel);
aboveColorInt = getColorForBlock(chunk, blockPos);
}
if (colorInt == 0 && yAbs > 0)
// if this block is invisible, check the block below it
colorInt = generateLodColor(chunk, config, xRel, yAbs - 1, zRel, blockPos);
//if (colorInt == 0 && yAbs > 0)
// if this block is invisible, check the block below it
// colorInt = generateLodColor(chunk, config, xRel, yAbs - 1, zRel, blockPos);
// override this block's color if there was a block above this
// and we were avoiding non-full/non-solid blocks
@@ -461,21 +380,16 @@ public class LodBuilder
}
/** Gets the light value for the given block position */
private int getLightValue(IChunk chunk, BlockPos.Mutable blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
private int getLightValue(ChunkWrapper chunk, BlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
{
int skyLight = 0;
int blockLight;
// 1 means the lighting is a guess
int isDefault = 0;
WorldWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerWorld();
ClientWorld clientWorld = mc.getClientWorld();
if (clientWorld == null)
return 0;
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType());
int blockBrightness = chunk.getLightEmission(blockPos);
int blockBrightness = chunk.getEmittedBrightness(blockPos);
// get the air block above or below this block
if (hasCeiling && topBlock)
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
@@ -484,15 +398,19 @@ public class LodBuilder
if (serverWorld != null)
if (!world.isEmpty())
{
// server world sky light (always accurate)
blockLight = serverWorld.getBrightness(LightType.BLOCK, blockPos);
if(topBlock && !hasCeiling && hasSkyLight)
blockLight = world.getBlockLight(blockPos);
if (topBlock && !hasCeiling && hasSkyLight)
skyLight = DEFAULT_MAX_LIGHT;
else
skyLight = serverWorld.getBrightness(LightType.SKY, blockPos);
{
if (hasSkyLight)
skyLight = world.getSkyLight(blockPos);
else
skyLight = 0;
}
if (!topBlock && skyLight == 15)
{
// we are on predicted terrain, and we don't know what the light here is,
@@ -508,8 +426,11 @@ public class LodBuilder
}
else
{
world = MinecraftWrapper.INSTANCE.getWrappedClientWorld();
if (world.isEmpty())
return 0;
// client world sky light (almost never accurate)
blockLight = clientWorld.getBrightness(LightType.BLOCK, blockPos);
blockLight = world.getBlockLight(blockPos);
// estimate what the lighting should be
if (hasSkyLight || !hasCeiling)
{
@@ -517,7 +438,12 @@ public class LodBuilder
skyLight = DEFAULT_MAX_LIGHT;
else
{
skyLight = clientWorld.getBrightness(LightType.SKY, blockPos);
if (hasSkyLight)
skyLight = world.getSkyLight(blockPos);
else
skyLight = 0;
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
{
// we don't know what the light here is,
@@ -531,6 +457,8 @@ public class LodBuilder
skyLight = 0;
}
}
if (hasSkyLight)
skyLight = 0;
}
}
@@ -539,151 +467,12 @@ public class LodBuilder
return blockLight + (skyLight << 4) + (isDefault << 8);
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private int getColorTextureForBlock(BlockState blockState, BlockPos blockPos, boolean useTopTexture)
{
// use the pre-generated color if we can
Block block = blockState.getBlock();
if (colorMap.containsKey(block) && toTint.containsKey(block))
return colorMap.get(block);
World world = mc.getClientWorld();
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
int tintIndex = Integer.MIN_VALUE;
boolean isTinted = false;
int listSize = 0;
// get the first quad we can for this block
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(blockState).getQuads(blockState, direction, new Random(0), dataMap);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
isTinted |= bakedQuad.isTinted();
tintIndex = Math.max(tintIndex, bakedQuad.getTintIndex());
}
}
toTint.put(block, isTinted);
tintColor.put(block, tintIndex);
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(blockState).getQuads(blockState, direction, new Random(0), dataMap);
if (!quads.isEmpty())
break;
}
if (useTopTexture && !quads.isEmpty())
texture = quads.get(0).getSprite();
else
texture = mc.getModelManager().getBlockModelShaper().getTexture(blockState, world, blockPos);
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int color;
int colorMultiplier;
// generate the block's color
for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getHeight(); u++)
{
for (int v = 0; v < texture.getWidth(); v++)
{
if (texture.isTransparent(frameIndex, u, v))
continue;
color = texture.getPixelRGBA(frameIndex, u, v);
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(color), ColorUtil.getGreen(color)), ColorUtil.getRed(color));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(color), ColorUtil.getGreen(color)), ColorUtil.getRed(color));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(color) > (ColorUtil.getBlue(color) + 30)) || !(ColorUtil.getGreen(color) > (ColorUtil.getRed(color) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(color) * colorMultiplier;
red += ColorUtil.getBlue(color) * colorMultiplier;
green += ColorUtil.getGreen(color) * colorMultiplier;
blue += ColorUtil.getRed(color) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
color = 0;
else
{
// determine the average color
alpha /= count;
red /= count;
green /= count;
blue /= count;
color = ColorUtil.rgbToInt(alpha, red, green, blue);
}
// determine if this block should use the biome color tint
if ((useGrassTint(block) || useLeafTint(block) || useWaterTint(block)) && (float) numberOfGreyPixel / count > 0.75f)
toTint.replace(block, true);
// add the newly generated block color to the map for later use
colorMap.put(block, color);
return color;
}
/** determine if the given block should use the biome's grass color */
private boolean useGrassTint(Block block)
{
return block instanceof GrassBlock
|| block instanceof BushBlock
|| block instanceof IGrowable
|| block instanceof AbstractPlantBlock
|| block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean useLeafTint(Block block)
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's water color */
private boolean useWaterTint(Block block)
{
return block == Blocks.WATER;
}
/** Returns a color int for the given block. */
private int getColorForBlock(IChunk chunk, BlockPos blockPos)
private int getColorForBlock(ChunkWrapper chunk, BlockPosWrapper blockPos)
{
int blockColor;
int colorOfBlock;
int colorInt;
int xRel = blockPos.getX() - chunk.getPos().getMinBlockX();
@@ -692,251 +481,75 @@ public class LodBuilder
int y = blockPos.getY();
int z = blockPos.getZ();
//Biome biome = chunk.getBiomes().getNoiseBiome(xRel >> 2, y >> 2, zRel >> 2);
BlockState blockState = chunk.getBlockState(blockPos);
BlockColorWrapper blockColorWrapper;
BlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
if(isInWater(blockState))
blockState = Blocks.WATER.defaultBlockState();
if (chunk.isWaterLogged(blockPos))
{
BiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
return biome.getWaterTint();
}
else
{
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
}
// block special cases
// TODO: this needs to be replaced by a config file of some sort
if (blockState == Blocks.AIR.defaultBlockState()
|| blockState == Blocks.CAVE_AIR.defaultBlockState()
|| blockState == Blocks.BARRIER.defaultBlockState())
if (blockShapeWrapper.isToAvoid())
{
return 0;
}
blockColor = getColorTextureForBlock(blockState, blockPos, true);
colorOfBlock = blockColorWrapper.getColor();
//if the blockColor is 0 we reset it and don't use the faceColor
if (blockColor == 0)
{
tintColor.remove(blockState.getBlock());
toTint.remove(blockState.getBlock());
colorMap.remove(blockState.getBlock());
blockColor = getColorTextureForBlock(blockState, blockPos, false);
}
//if the blockColor is still 0 we use the default material color
if (blockColor == 0)
if (blockColorWrapper.hasTint())
{
tintColor.replace(blockState.getBlock(), 0);
toTint.replace(blockState.getBlock(), false);
colorMap.replace(blockState.getBlock(), blockState.getBlock().defaultMaterialColor().col);
}
if (toTint.get(blockState.getBlock()))
{
Biome biome = chunk.getBiomes().getNoiseBiome(xRel >> 2, y >> 2, zRel >> 2);
ClientWorld clientWorld = mc.getClientWorld();
if (clientWorld == null)
return 0;
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType());
WorldWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerWorld();
if (world.isEmpty())
{
world = MinecraftWrapper.INSTANCE.getWrappedClientWorld();
}
int tintValue;
if (useGrassTint(blockState.getBlock()))
if (blockColorWrapper.hasGrassTint())
{
// grass and green plants
try
{
tintValue = BiomeColors.getAverageGrassColor(serverWorld, blockPos);
}
catch(NullPointerException e)
{
tintValue = biome.getGrassColor(x, z);
}
tintValue = BiomeColorWrapper.getGrassColor(world, blockPos);
}
else if (useWaterTint(blockState.getBlock()))
else if (blockColorWrapper.hasFolliageTint())
{
// water
try
{
tintValue = BiomeColors.getAverageWaterColor(serverWorld, blockPos);
}
catch(NullPointerException e)
{
tintValue = biome.getWaterColor();
}
tintValue = BiomeColorWrapper.getFoliageColor(world, blockPos);
}
else
{
// leaves
try
{
tintValue = BiomeColors.getAverageFoliageColor(serverWorld, blockPos);
}
catch(NullPointerException e)
{
tintValue = biome.getFoliageColor();
}
//we can reintroduce this with the wrappers
tintValue = BiomeColorWrapper.getWaterColor(world, blockPos);
}
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, blockColor);
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock);
}
else
colorInt = blockColor;
colorInt = colorOfBlock;
return colorInt;
}
/** Returns a color int for the given biome. */
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.NETHERRACK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
private boolean isInWater(BlockState blockState)
{
//This type of block is always in water
if((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
return true;
//This type of block could be in water
if(blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get())
return true;
return false;
}
/** Is the block at the given blockPos a valid LOD point? */
private boolean isLayerValidLodPoint(IChunk chunk, BlockPos.Mutable blockPos)
private boolean isLayerValidLodPoint(ChunkWrapper chunk, BlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(blockPos);
if (isInWater(blockState))
if (chunk.isWaterLogged(blockPos))
return true;
boolean nonFullAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull;
boolean noCollisionAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision;
if (blockState != null)
{
if (nonFullAvoidance)
{
if(!blockState.getFluidState().isEmpty() || blockState.getBlock() instanceof SixWayBlock)
{
notFullBlock.put(blockState.getBlock(), false);
}
if (!notFullBlock.containsKey(blockState.getBlock()) || notFullBlock.get(blockState.getBlock()) == null)
{
VoxelShape voxelShape = blockState.getBlock().defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AxisAlignedBB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
if (xWidth < 1 && zWidth < 1 && yWidth < 1)
notFullBlock.put(blockState.getBlock(), true);
else
notFullBlock.put(blockState.getBlock(), false);
}
else
{
notFullBlock.put(blockState.getBlock(), false);
}
}
if (notFullBlock.get(blockState.getBlock()))
{
return false;
}
}
if (noCollisionAvoidance)
{
if(!blockState.getFluidState().isEmpty() || blockState.getBlock() instanceof SixWayBlock)
smallBlock.put(blockState.getBlock(), false);
if (!smallBlock.containsKey(blockState.getBlock()) || smallBlock.get(blockState.getBlock()) == null)
{
VoxelShape voxelShape = blockState.getCollisionShape(chunk, blockPos);
if (!blockState.getFluidState().isEmpty())
{
smallBlock.put(blockState.getBlock(), false);
}
else
{
if (voxelShape.isEmpty())
{
smallBlock.put(blockState.getBlock(), true);
}
else
{
smallBlock.put(blockState.getBlock(), false);
}
}
}
if (smallBlock.get(blockState.getBlock()))
{
return false;
}
}
return blockState.getBlock() != Blocks.AIR
&& blockState.getBlock() != Blocks.CAVE_AIR
&& blockState.getBlock() != Blocks.BARRIER;
}
return false;
boolean nonFullAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull;
boolean noCollisionAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision;
BlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos);
return !block.isToAvoid()
&& !(nonFullAvoidance && block.isNonFull())
&& !(noCollisionAvoidance && block.hasNoCollision());
}
}
@@ -37,6 +37,7 @@ import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.ChunkPos;
@@ -384,7 +385,7 @@ public class LodGenWorker implements IWorker
if (!inTheEnd)
{
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
else
{
@@ -392,7 +393,7 @@ public class LodGenWorker implements IWorker
// Since we don't know where the islands are, everything
// generates the same, and it looks awful.
//TODO it appears that 'if' can be collapsed, but comment says that it should not be a case
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
@@ -432,7 +433,7 @@ public class LodGenWorker implements IWorker
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.SURFACE));
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE));
/*TODO if we want to use Biome utils and terrain utils for overworld
* lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/
@@ -558,7 +559,7 @@ public class LodGenWorker implements IWorker
}
// generate a Lod like normal
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.FEATURES));
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES));
}
@@ -573,7 +574,7 @@ public class LodGenWorker implements IWorker
*/
private void generateWithServer()
{
lodBuilder.generateLodNodeFromChunk(lodDim, serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), new LodBuilderConfig(DistanceGenerationMode.SERVER));
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.SERVER));
}
@@ -19,6 +19,7 @@
package com.seibel.lod.proxy;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
@@ -213,7 +214,7 @@ public class ClientProxy
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
lodBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
lodBuilder.generateLodNodeAsync(new ChunkWrapper(event.getChunk()), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
}
@SubscribeEvent
@@ -283,7 +284,7 @@ public class ClientProxy
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
// recreate the LOD where the blocks were changed
lodBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
lodBuilder.generateLodNodeAsync(new ChunkWrapper(event.getWorld().getChunk(event.getPos())), lodWorld, event.getWorld());
}
}
@@ -49,7 +49,6 @@ import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.potion.Effects;
import net.minecraft.profiler.IProfiler;
@@ -104,9 +103,6 @@ public class LodRenderer
/** This is used to determine if the LODs should be regenerated */
private int[] previousPos = new int[] { 0, 0, 0 };
public NativeImage lightMap = null;
public NativeImage lastLightMap = null;
// these variables are used to determine if the buffers should be rebuilt
private float prevSkyBrightness = 0;
private double prevBrightness = 0;
@@ -866,10 +862,9 @@ public class LodRenderer
// (just in case the minLightingDifference is too large to notice the change)
|| (skyBrightness == 1.0f && prevSkyBrightness != 1.0f) // noon
|| (skyBrightness == 0.2f && prevSkyBrightness != 0.2f) // midnight
|| mc.getOptions().gamma != prevBrightness || lightMap == null)
|| mc.getOptions().gamma != prevBrightness)
{
fullRegen = true;
lightMap = mc.getCurrentLightMap();
prevBrightness = mc.getOptions().gamma;
prevSkyBrightness = skyBrightness;
}
@@ -21,7 +21,7 @@ package com.seibel.lod.util;
import java.awt.Color;
import net.minecraft.client.renderer.texture.NativeImage;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
*
@@ -78,9 +78,9 @@ public class ColorUtil
}
/** This method apply the lightmap to the color to use */
public static int applyLightValue(int color, int skyLight, int blockLight, NativeImage lightMap)
public static int applyLightValue(int color, int skyLight, int blockLight)
{
int lightColor = lightMap.getPixelRGBA(blockLight, skyLight);
int lightColor = MinecraftWrapper.INSTANCE.getColorIntFromLightMap(blockLight, skyLight);
int red = ColorUtil.getBlue(lightColor);
int green = ColorUtil.getGreen(lightColor);
int blue = ColorUtil.getRed(lightColor);
@@ -125,11 +125,11 @@ public class ThreadMapUtil
if (!threadBuilderVerticalArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMap.get(Thread.currentThread().getName()) == null))
{
long[][] array = new long[5][];
int size = 1;
int size;
for (int i = 0; i < 5; i++)
{
size = 1 << i;
array[i] = new long[size * size * DataPointUtil.worldHeight / 2 + 1];
size = size << 1;
}
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
}
@@ -6,72 +6,74 @@ import net.minecraft.block.*;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.*;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
//This class wraps the minecraft Block class
public class BlockWrapper
public class BlockColorWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockWrapper> blockWrapperMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final BlockPos blockPos = new BlockPos(0,0,0);
public static Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
private Block block;
private boolean nonFull;
private boolean noCollision;
private int color;
private boolean isColored;
private boolean toTint;
private boolean leavesTint;
private boolean folliageTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockWrapper(Block block)
public BlockColorWrapper(BlockState blockState, BlockPosWrapper blockPosWrapper)
{
this.nonFull = true;
this.noCollision = true;
this.block = blockState.getBlock();
this.color = 0;
this.isColored = true;
this.toTint = false;
this.block = block;
setupColorAndTint();
setupShapes();
this.folliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint(blockState,blockPosWrapper);
System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
* @param blockState of the block to wrap
*/
static public BlockWrapper getBlockWrapper(Block block)
static public BlockColorWrapper getBlockColorWrapper(BlockState blockState, BlockPosWrapper blockPosWrapper)
{
//first we check if the block has already been wrapped
if(blockWrapperMap.containsKey(block) && blockWrapperMap.get(block) != null)
return blockWrapperMap.get(block);
if (blockColorWrapperMap.containsKey(blockState.getBlock()) && blockColorWrapperMap.get(blockState.getBlock()) != null)
return blockColorWrapperMap.get(blockState.getBlock());
//if it hasn't been created yet, we create it and save it in the map
BlockWrapper blockWrapper = new BlockWrapper(block);
blockWrapperMap.put(block, blockWrapper);
BlockColorWrapper blockWrapper = new BlockColorWrapper(blockState, blockPosWrapper);
blockColorWrapperMap.put(blockState.getBlock(), blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
private void setupColorAndTint(BlockState blockState, BlockPosWrapper blockPosWrapper)
{
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
@@ -81,9 +83,9 @@ public class BlockWrapper
int listSize = 0;
// first step is to check if this block has a tinted face
for (Direction direction : Direction.values())
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(block.defaultBlockState(), direction, random, dataMap);
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
@@ -92,23 +94,27 @@ public class BlockWrapper
}
//if it contains a tinted face then we store this block in the toTint set
if(isTinted)
if (isTinted)
this.toTint = true;
//now we get the first non empty face
for (Direction direction : Direction.values())
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(block.defaultBlockState(), direction, random, dataMap);
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
if (!quads.isEmpty())
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
{
isColored = true;
texture = quads.get(0).getSprite();
}
else
{
return;
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientWorld(), blockPosWrapper.getBlockPos());
}
int count = 0;
@@ -128,11 +134,12 @@ public class BlockWrapper
{
for (int v = 0; v < texture.getWidth(); v++)
{
if (texture.isTransparent(frameIndex, u, v))
continue;
tempColor = texture.getPixelRGBA(frameIndex, u, v);
if (ColorUtil.getAlpha(texture.getPixelRGBA(frameIndex, u, v)) == 0)
continue;
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
@@ -177,14 +184,11 @@ public class BlockWrapper
this.toTint = true;
// we check which kind of tint we need to apply
if (grassInstance() && this.toTint)
this.grassTint = true;
this.grassTint = grassInstance() && toTint;
if (leavesInstance() && this.toTint)
this.leavesTint = true;
this.folliageTint = leavesInstance() && toTint;
if (waterIstance() && this.toTint)
this.waterTint = true;
this.waterTint = waterIstance() && toTint;
color = tempColor;
}
@@ -213,14 +217,10 @@ public class BlockWrapper
{
return block == Blocks.WATER;
}
private void setupShapes(){
}
//--------------//
//Colors getters//
//--------------//
//--------------//
//Colors getters//
//--------------//
public boolean hasColor()
{
@@ -229,12 +229,12 @@ public class BlockWrapper
public int getColor()
{
return color;
return color;
}
//------------//
//Tint getters//
//------------//
//------------//
//Tint getters//
//------------//
public boolean hasTint()
@@ -247,9 +247,9 @@ public class BlockWrapper
return grassTint;
}
public boolean hasLeavesTint()
public boolean hasFolliageTint()
{
return leavesTint;
return folliageTint;
}
public boolean hasWaterTint()
@@ -258,13 +258,15 @@ public class BlockWrapper
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockWrapper))
if (!(o instanceof BlockColorWrapper))
return false;
BlockWrapper that = (BlockWrapper) o;
BlockColorWrapper that = (BlockColorWrapper) o;
return Objects.equals(block, that.block);
}
@@ -274,3 +276,4 @@ public class BlockWrapper
}
}
@@ -23,7 +23,7 @@ public class BlockPosWrapper
public BlockPosWrapper()
{
this.blockPos = new BlockPos.Mutable();
this.blockPos = new BlockPos.Mutable(0,0,0);
}
public void set(int x, int y, int z)
@@ -0,0 +1,153 @@
package com.seibel.lod.wrappers.Block;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.block.*;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
//This class wraps the minecraft Block class
public class BlockShapeWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockShapeWrapper> blockShapeWrapperMap = new ConcurrentHashMap<>();
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private Block block;
private boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
/**Constructor only require for the block instance we are wrapping**/
public BlockShapeWrapper(Block block, ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper)
{
this.block = block;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = ofBlockToAvoid();
setupShapes(chunkWrapper, blockPosWrapper);
System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid);
}
private BlockShapeWrapper()
{
this.block = Blocks.WATER;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = false;
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockShapeWrapper getBlockShapeWrapper(Block block, ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper)
{
//first we check if the block has already been wrapped
if (blockShapeWrapperMap.containsKey(block) && blockShapeWrapperMap.get(block) != null)
return blockShapeWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockShapeWrapper blockWrapper = new BlockShapeWrapper(block, chunkWrapper, blockPosWrapper);
blockShapeWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
private void setupShapes(ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper)
{
IBlockReader chunk = chunkWrapper.getChunk();
BlockPos blockPos = blockPosWrapper.getBlockPos();
boolean noCollisionSetted = false;
boolean nonFullSetted = false;
if (!block.defaultBlockState().getFluidState().isEmpty() || block instanceof SixWayBlock)
{
noCollisionSetted = true;
nonFullSetted = true;
noCollision = false;
nonFull = false;
}
if (!nonFullSetted)
{
VoxelShape voxelShape = block.defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AxisAlignedBB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
nonFull = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
else
{
nonFull = false;
}
}
if (!noCollisionSetted)
{
VoxelShape collisionShape = block.defaultBlockState().getCollisionShape(chunk, blockPos);
noCollision = collisionShape.isEmpty();
}
}
public boolean ofBlockToAvoid()
{
return block.equals(Blocks.AIR)
|| block.equals(Blocks.CAVE_AIR)
|| block.equals(Blocks.BARRIER);
}
//-----------------//
//Avoidance getters//
//-----------------//
public boolean isNonFull()
{
return nonFull;
}
public boolean hasNoCollision()
{
return noCollision;
}
public boolean isToAvoid()
{
return toAvoid;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockShapeWrapper))
return false;
BlockShapeWrapper that = (BlockShapeWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -26,6 +26,26 @@ public class ChunkPosWrapper
return chunkPos.z;
}
public int getMinBlockX()
{
return chunkPos.getMinBlockX();
}
public int getMinBlockZ()
{
return chunkPos.getMinBlockZ();
}
public int getRegionX()
{
return chunkPos.getRegionX();
}
public int getRegionZ()
{
return chunkPos.getRegionZ();
}
public ChunkPos getChunkPos()
{
return chunkPos;
@@ -1,7 +1,14 @@
package com.seibel.lod.wrappers.Chunk;
import com.seibel.lod.wrappers.Block.BlockWrapper;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockColorWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Block.BlockShapeWrapper;
import com.seibel.lod.wrappers.World.BiomeWrapper;
import net.minecraft.block.BlockState;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.world.chunk.IChunk;
public class ChunkWrapper
@@ -10,20 +17,77 @@ public class ChunkWrapper
private IChunk chunk;
private ChunkPosWrapper chunkPos;
public int getHeight(){
return chunk.getMaxBuildHeight();
}
public boolean isPositionInWater(BlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(blockPos.getBlockPos());
//This type of block is always in water
if((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
return true;
//This type of block could be in water
if(blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get())
return true;
return false;
}
public int getHeightMapValue(int xRel, int zRel){
return chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
public BiomeWrapper getBiome(int xRel, int yAbs, int zRel)
{
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2));
}
public BlockColorWrapper getBlockColorWrapper(BlockPosWrapper blockPos)
{
return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(blockPos.getBlockPos()),blockPos);
}
public BlockShapeWrapper getBlockShapeWrapper(BlockPosWrapper blockPos)
{
return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock(), this, blockPos);
}
public ChunkWrapper(IChunk chunk)
{
this.chunk = chunk;
this.chunkPos = new ChunkPosWrapper(chunk.getPos());
}
public BlockWrapper getBlock(BlockPosWrapper blockPos)
{
return BlockWrapper.getBlockWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock());
public IChunk getChunk(){
return chunk;
}
public ChunkPosWrapper getChunkPos(){
public ChunkPosWrapper getPos(){
return chunkPos;
}
public boolean isLightCorrect(){
return chunk.isLightCorrect();
}
public boolean
isWaterLogged(BlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(blockPos.getBlockPos());
//This type of block is always in water
if((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
return true;
//This type of block could be in water
if(blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get())
return true;
return false;
}
public int getEmittedBrightness(BlockPosWrapper blockPos)
{
return chunk.getLightEmission(blockPos.getBlockPos());
@@ -9,7 +9,7 @@ public class LigthMapWrapper
public static void setLightMap(NativeImage lightMap)
{
lightMap = null;
lightMap = lightMap;
}
public static int getLightValue(int skyLight, int blockLight)
@@ -26,6 +26,7 @@ import com.seibel.lod.ModInfo;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.World.WorldWrapper;
import net.minecraft.client.GameSettings;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
@@ -44,6 +45,7 @@ import net.minecraft.profiler.IProfiler;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
/**
* A singleton that wraps the Minecraft class
@@ -183,6 +185,36 @@ public class MinecraftWrapper
return mc.level;
}
public WorldWrapper getWrappedClientWorld()
{
return WorldWrapper.getWorldWrapper(mc.level);
}
public WorldWrapper getWrappedServerWorld()
{
if (mc.level == null)
return null;
DimensionType dimension = mc.level.dimensionType();
IntegratedServer server = mc.getSingleplayerServer();
if (server == null)
return null;
Iterable<ServerWorld> worlds = server.getAllLevels();
ServerWorld returnWorld = null;
for (ServerWorld world : worlds)
{
if (world.dimensionType() == dimension)
{
returnWorld = world;
break;
}
}
return WorldWrapper.getWorldWrapper(returnWorld);
}
/** Measured in chunks */
public int getRenderDistance()
{
@@ -0,0 +1,33 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeColors;
import java.awt.*;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class BiomeColorWrapper
{
public static int getGrassColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper)
{
return BiomeColors.getAverageGrassColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos());
}
public static int getWaterColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper)
{
return BiomeColors.getAverageWaterColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos());
}
public static int getFoliageColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper)
{
return BiomeColors.getAverageFoliageColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos());
}
}
@@ -1,10 +1,16 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.wrappers.Block.BlockWrapper;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockColorWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeColors;
import java.awt.*;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -42,72 +48,55 @@ public class BiomeWrapper
public int getColorForBiome(int x, int z)
{
int colorInt;
int color;
int tint;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = BlockWrapper.getBlockWrapper(Blocks.NETHERRACK).getColor();
colorInt = Blocks.NETHERRACK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = BlockWrapper.getBlockWrapper(Blocks.END_STONE).getColor();
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = BlockWrapper.getBlockWrapper(Blocks.SAND).getColor();
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = BlockWrapper.getBlockWrapper(Blocks.STONE).getColor();
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = BlockWrapper.getBlockWrapper(Blocks.MYCELIUM).getColor();
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = BlockWrapper.getBlockWrapper(Blocks.SNOW).getColor();
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = BlockWrapper.getBlockWrapper(Blocks.RED_SAND).getColor();
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case SWAMP:
case FOREST:
color = BlockWrapper.getBlockWrapper(Blocks.OAK_LEAVES).getColor();
tint = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
case TAIGA:
color = BlockWrapper.getBlockWrapper(Blocks.SPRUCE_LEAVES).getColor();
tint = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
case JUNGLE:
color = BlockWrapper.getBlockWrapper(Blocks.JUNGLE_LEAVES).getColor();
tint = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
break;
default:
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
color = BlockWrapper.getBlockWrapper(Blocks.GRASS_BLOCK).getColor();
tint = biome.getGrassColor(x,z);
colorInt = ColorUtil.multiplyRGBcolors(color, tint);
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
@@ -115,6 +104,22 @@ public class BiomeWrapper
return colorInt;
}
public int getGrassTint(int x, int z)
{
return biome.getGrassColor(x, z);
}
public int getFolliageTint()
{
return biome.getFoliageColor();
}
public int getWaterTint()
{
return biome.getWaterColor();
}
@Override public boolean equals(Object o)
{
if (this == o)
@@ -0,0 +1,7 @@
package com.seibel.lod.wrappers.World;
//We will use this class to get all the light information from the game like skylight, blocklight and light emission;
public class WorldLightWrapper
{
}
@@ -16,7 +16,8 @@ public class WorldWrapper
this.world = world;
}
public WorldWrapper getWorldWrapper(IWorld world)
public static WorldWrapper getWorldWrapper(IWorld world)
{
//first we check if the biome has already been wrapped
if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null)
@@ -43,12 +44,12 @@ public class WorldWrapper
public int getBlockLight(BlockPosWrapper blockPos)
{
return world.getLightEngine().skyEngine.getLightValue(blockPos.getBlockPos());
return world.getLightEngine().blockEngine.getLightValue(blockPos.getBlockPos());
}
public int getSkyLight(BlockPosWrapper blockPos)
{
return world.getLightEngine().blockEngine.getLightValue(blockPos.getBlockPos());
return world.getLightEngine().skyEngine.getLightValue(blockPos.getBlockPos());
}
public BiomeWrapper getBiome(BlockPosWrapper blockPos)
@@ -56,6 +57,11 @@ public class WorldWrapper
return BiomeWrapper.getBiomeWrapper(world.getBiome(blockPos.getBlockPos()));
}
public IWorld getWorld()
{
return world;
}
public boolean hasCeiling()
{
return world.dimensionType().hasCeiling();
@@ -65,4 +71,9 @@ public class WorldWrapper
{
return world.dimensionType().hasSkyLight();
}
public boolean isEmpty()
{
return world == null;
}
}