Update core + Fix biome blending
This commit is contained in:
@@ -22,9 +22,12 @@ package com.seibel.lod.common.wrappers;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import com.mojang.math.Matrix4f;
|
||||
import com.seibel.lod.common.wrappers.block.BlockPosWrapper;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
|
||||
/**
|
||||
@@ -48,17 +51,29 @@ public class McObjectConverter
|
||||
|
||||
|
||||
static final Direction[] directions;
|
||||
static final LodDirection[] lodDirections;
|
||||
static {
|
||||
LodDirection[] lodDirs = LodDirection.values();
|
||||
directions = new Direction[lodDirs.length];
|
||||
lodDirections = new LodDirection[lodDirs.length];
|
||||
for (LodDirection lodDir : lodDirs) {
|
||||
directions[lodDir.ordinal()] = Direction.byName(lodDir.name());
|
||||
Direction dir = Direction.byName(lodDir.name());
|
||||
directions[lodDir.ordinal()] = dir;
|
||||
lodDirections[dir.ordinal()] = lodDir;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockPos Convert(AbstractBlockPosWrapper wrappedPos) {
|
||||
return new BlockPos(wrappedPos.getX(),wrappedPos.getY(), wrappedPos.getZ());
|
||||
}
|
||||
|
||||
|
||||
public static Direction Convert(LodDirection lodDirection)
|
||||
{
|
||||
return directions[lodDirection.ordinal()];
|
||||
}
|
||||
public static LodDirection Convert(Direction direction)
|
||||
{
|
||||
return lodDirections[direction.ordinal()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,286 +1,27 @@
|
||||
package com.seibel.lod.common.wrappers.block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.BlockDetail;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.FlowerBlock;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.LiquidBlock;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
public class BlockDetailMap
|
||||
{
|
||||
public static final int FLOWER_COLOR_SCALE = 5;
|
||||
|
||||
public static final Random random = new Random(0);
|
||||
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
//FIXME: Stuff is wrong.
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, boolean useFlowerScaling, boolean useFastLeaf) {
|
||||
|
||||
int count = 0;
|
||||
double alpha = 0;
|
||||
double red = 0;
|
||||
double green = 0;
|
||||
double blue = 0;
|
||||
int tempColor;
|
||||
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
for (int u = 0; u < texture.getWidth(); u++)
|
||||
{
|
||||
for (int v = 0; v < texture.getHeight(); v++)
|
||||
{
|
||||
//note: Minecraft color format is: 0xAA BB GG RR
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
||||
|
||||
double r = ((tempColor & 0x000000FF) )/255.;
|
||||
double g = ((tempColor & 0x0000FF00) >>> 8)/255.;
|
||||
double b = ((tempColor & 0x00FF0000) >>> 16)/255.;
|
||||
double a = ((tempColor & 0xFF000000) >>> 24)/255.;
|
||||
int scale = 1;
|
||||
|
||||
if (useFastLeaf) {
|
||||
r *= a;
|
||||
g *= a;
|
||||
b *= a;
|
||||
a = 1.;
|
||||
} else if (a==0.) {
|
||||
continue;
|
||||
} else if (useFlowerScaling && (g+0.1<b || g+0.1<r)) {
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
|
||||
count += scale;
|
||||
alpha += a*a*scale;
|
||||
red += r*r*scale;
|
||||
green += g*g*scale;
|
||||
blue += b*b*scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.rgbToInt(255,255,0,255);
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
(int) (Math.sqrt(alpha/count)*255.),
|
||||
(int) (Math.sqrt(red / count)*255.),
|
||||
(int) (Math.sqrt(green / count)*255.),
|
||||
(int) (Math.sqrt(blue / count)*255.));
|
||||
}
|
||||
// TODO: Remove this when transparency is added!
|
||||
double colorAlpha = ColorUtil.getAlpha(tempColor)/255.;
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
ColorUtil.getAlpha(tempColor),
|
||||
(int)(ColorUtil.getRed(tempColor) * colorAlpha),
|
||||
(int)(ColorUtil.getGreen(tempColor) * colorAlpha),
|
||||
(int)(ColorUtil.getBlue(tempColor) * colorAlpha)
|
||||
);
|
||||
|
||||
return tempColor;
|
||||
}
|
||||
|
||||
|
||||
static class BlockDetailCache {
|
||||
static BlockDetailCache NULL_BLOCK_DETAIL = new BlockDetailCache();
|
||||
|
||||
private static final Block[] BLOCK_TO_AVOID = {Blocks.AIR, Blocks.CAVE_AIR, Blocks.BARRIER};
|
||||
|
||||
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
|
||||
|
||||
ConcurrentHashMap<Biome, BlockDetail> biomeDetailMap = null;
|
||||
BlockDetail blockDetail;
|
||||
BlockDetail defaultTintedDetail = null;
|
||||
boolean requireResolving;
|
||||
@SuppressWarnings("unused")
|
||||
boolean requireShade; //TODO: Add back using this in renderer
|
||||
@SuppressWarnings("unused")
|
||||
boolean scaleFlowerColor; //FIXME: Do I need to scale the tint color???
|
||||
int tintIndex;
|
||||
|
||||
static boolean isBlockToBeAvoid(Block b) {
|
||||
for (Block bta : BLOCK_TO_AVOID)
|
||||
if (bta==b) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean hasNoCollision(BlockState bs, BlockPos pos, LevelReader getter) {
|
||||
if (!bs.getFluidState().isEmpty() || bs.getBlock() instanceof LiquidBlock) // Is blockState a fluid?
|
||||
return false;
|
||||
if (bs.getCollisionShape(getter, pos).isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
static boolean hasOnlyNonFullFace(BlockState bs, BlockPos pos, LevelReader getter) {
|
||||
if (!bs.getFluidState().isEmpty() || bs.getBlock() instanceof LiquidBlock) // Is blockState a fluid?
|
||||
return false;
|
||||
VoxelShape voxelShape = bs.getShape(getter, pos);
|
||||
if (voxelShape.isEmpty()) return true;
|
||||
AABB bbox = voxelShape.bounds();
|
||||
double xWidth = (bbox.maxX - bbox.minX);
|
||||
double yWidth = (bbox.maxY - bbox.minY);
|
||||
double zWidth = (bbox.maxZ - bbox.minZ);
|
||||
return xWidth < 1 && zWidth < 1 && yWidth < 1;
|
||||
}
|
||||
|
||||
static BlockDetailCache make(BlockState bs, BlockPos pos, LevelReader getter) {
|
||||
boolean noCol, nonFull, canOcclude;
|
||||
if(!bs.getFluidState().isEmpty()) {
|
||||
bs = bs.getFluidState().createLegacyBlock();
|
||||
FluidState fs = bs.getFluidState();
|
||||
fs.getType();
|
||||
noCol = false;
|
||||
nonFull = false;
|
||||
canOcclude = false;
|
||||
BlockDetailCache result = new BlockDetailCache(fs);
|
||||
//ApiShared.LOGGER.info(fs.toString()+" = ["+result+"]");
|
||||
return result;
|
||||
} else {
|
||||
if (bs.getRenderShape() != RenderShape.MODEL) return NULL_BLOCK_DETAIL;
|
||||
if (isBlockToBeAvoid(bs.getBlock())) return NULL_BLOCK_DETAIL;
|
||||
//BlocksToAvoid toAvoid = CONFIG.client().worldGenerator().getBlocksToAvoid();
|
||||
noCol = hasNoCollision(bs, pos, getter);
|
||||
nonFull = hasOnlyNonFullFace(bs, pos, getter);
|
||||
canOcclude = bs.canOcclude();
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : DIRECTION_ORDER)
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(bs).getQuads(bs, direction, random);
|
||||
if (!quads.isEmpty() && !(bs.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
|
||||
break;
|
||||
};
|
||||
if (quads == null || quads.isEmpty()) return NULL_BLOCK_DETAIL;
|
||||
BlockDetailCache result = new BlockDetailCache(canOcclude, noCol, nonFull, quads.get(0),
|
||||
bs.getBlock() instanceof FlowerBlock, bs.getBlock() instanceof LeavesBlock);
|
||||
// ApiShared.LOGGER.info(bs.toString()+" = ["+result+"]");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
BlockDetailCache(boolean isFullBlock, boolean noCol, boolean nonFull, BakedQuad quad, boolean useFlowerScaling, boolean useFastLeaf) {
|
||||
requireResolving = quad.isTinted();
|
||||
requireShade = quad.isShade();
|
||||
tintIndex = quad.getTintIndex();
|
||||
scaleFlowerColor = useFlowerScaling;
|
||||
blockDetail = new BlockDetail(calculateColorFromTexture(quad.getSprite(), useFlowerScaling, useFastLeaf), isFullBlock, noCol, nonFull);
|
||||
if (quad.isTinted()) biomeDetailMap = new ConcurrentHashMap<Biome, BlockDetail>();
|
||||
}
|
||||
|
||||
BlockDetailCache(FluidState fluid) {
|
||||
requireResolving = true; // TODO: Maybe in the future recheck that there really is no way to see if a fluid needs tinting
|
||||
requireShade = false;
|
||||
tintIndex = 0; // Vanilla doesn't use this index currently. (Checked at 1.18.X, See BlockColors.class)
|
||||
scaleFlowerColor = false;
|
||||
TextureAtlasSprite text = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(fluid.createLegacyBlock()).getParticleIcon();
|
||||
blockDetail = new BlockDetail(calculateColorFromTexture(text, false, false), true, false, false);
|
||||
biomeDetailMap = new ConcurrentHashMap<Biome, BlockDetail>();
|
||||
}
|
||||
|
||||
private BlockDetailCache()
|
||||
{
|
||||
//DUMMY CREATOR
|
||||
}
|
||||
|
||||
BlockDetail getResolvedBlockDetail(BlockState bs, int x, int y, int z, LevelReader getter) {
|
||||
if (!requireResolving) return blockDetail;
|
||||
BlockPos pos = new BlockPos(x,y,z);
|
||||
Biome biome = getter.getBiome(pos).value();
|
||||
BlockDetail tintDetail = biomeDetailMap.get(biome);
|
||||
if (tintDetail == null) {
|
||||
if (!bs.getFluidState().isEmpty()) bs = bs.getFluidState().createLegacyBlock();
|
||||
BlockColors bc = Minecraft.getInstance().getBlockColors();
|
||||
int tintColor = bc.getColor(bs, getter, pos, tintIndex);
|
||||
tintColor = ColorUtil.multiplyARGBwithRGB(blockDetail.color, tintColor);
|
||||
tintDetail = new BlockDetail(tintColor, blockDetail.isFullBlock,
|
||||
blockDetail.hasNoCollision, blockDetail.hasOnlyNonFullFace);
|
||||
BlockDetail tintDetailCAS = biomeDetailMap.putIfAbsent(biome, tintDetail);
|
||||
if (tintDetailCAS != null) tintDetail = tintDetailCAS;
|
||||
}
|
||||
return tintDetail;
|
||||
}
|
||||
|
||||
// Note: this one won't resolve biome based or pos based colors. (Kinda like GUI block icons)
|
||||
BlockDetail getResolvedBlockDetail(BlockState bs) {
|
||||
if (!requireResolving) return blockDetail;
|
||||
if (defaultTintedDetail != null) return defaultTintedDetail;
|
||||
|
||||
BlockColors bc = Minecraft.getInstance().getBlockColors();
|
||||
if (!bs.getFluidState().isEmpty()) bs = bs.getFluidState().createLegacyBlock();
|
||||
int tintColor = bc.getColor(bs, null, null, tintIndex);
|
||||
if (tintColor == -1) {
|
||||
defaultTintedDetail = blockDetail;
|
||||
}
|
||||
else {
|
||||
defaultTintedDetail = new BlockDetail(ColorUtil.multiplyARGBwithRGB(blockDetail.color, tintColor),
|
||||
blockDetail.isFullBlock, blockDetail.hasNoCollision, blockDetail.hasOnlyNonFullFace);
|
||||
}
|
||||
return defaultTintedDetail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[BlockDetail: "+blockDetail+", RequireResolving: "+requireResolving+", requireShade: "+requireShade+", scaleFlowerColor: "+scaleFlowerColor+"]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static ConcurrentHashMap<BlockState, BlockDetailCache> map = new ConcurrentHashMap<BlockState, BlockDetailCache>();
|
||||
private static ConcurrentHashMap<BlockState, BlockDetailWrapper> map = new ConcurrentHashMap<BlockState, BlockDetailWrapper>();
|
||||
|
||||
private BlockDetailMap() {}
|
||||
|
||||
private static BlockDetailCache getOrMakeBlockDetailCache(BlockState bs, BlockPos pos, LevelReader getter) {
|
||||
BlockDetailCache cache = map.get(bs);
|
||||
public static BlockDetailWrapper getOrMakeBlockDetailCache(BlockState bs, BlockPos pos, LevelReader getter) {
|
||||
BlockDetailWrapper cache = map.get(bs);
|
||||
if (cache != null) return cache;
|
||||
if (bs.getFluidState().isEmpty()) {
|
||||
cache = BlockDetailCache.make(bs, pos, getter);
|
||||
cache = BlockDetailWrapper.make(bs, pos, getter);
|
||||
} else {
|
||||
cache = BlockDetailCache.make(bs.getFluidState().createLegacyBlock(), pos, getter);
|
||||
cache = BlockDetailWrapper.make(bs.getFluidState().createLegacyBlock(), pos, getter);
|
||||
}
|
||||
BlockDetailCache cacheCAS = map.putIfAbsent(bs, cache);
|
||||
BlockDetailWrapper cacheCAS = map.putIfAbsent(bs, cache);
|
||||
return cacheCAS==null ? cache : cacheCAS;
|
||||
}
|
||||
|
||||
|
||||
// Return null means skip the block
|
||||
public static BlockDetail getBlockDetail(BlockState bs) {
|
||||
BlockDetailCache cache = getOrMakeBlockDetailCache(bs, new BlockPos(0, 0, 0), null);
|
||||
if (cache == BlockDetailCache.NULL_BLOCK_DETAIL) return null;
|
||||
return cache.getResolvedBlockDetail(bs);
|
||||
}
|
||||
|
||||
// Return null means skip the block
|
||||
public static BlockDetail getBlockDetailWithCompleteTint(BlockState bs, int x, int y, int z, LevelReader tintGetter) {
|
||||
BlockDetailCache cache = getOrMakeBlockDetailCache(bs, new BlockPos(x,y,z), tintGetter);
|
||||
if (cache == BlockDetailCache.NULL_BLOCK_DETAIL) return null;
|
||||
return cache.getResolvedBlockDetail(bs, x, y, z, tintGetter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
package com.seibel.lod.common.wrappers.block;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import com.seibel.lod.common.wrappers.McObjectConverter;
|
||||
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.FlowerBlock;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
public class BlockDetailWrapper extends IBlockDetailWrapper
|
||||
{
|
||||
public static final int FLOWER_COLOR_SCALE = 5;
|
||||
|
||||
public static final Random random = new Random(0);
|
||||
|
||||
enum ColorMode {
|
||||
Default,
|
||||
Flower,
|
||||
Leaves;
|
||||
static ColorMode getColorMode(Block b) {
|
||||
if (b instanceof LeavesBlock) return Leaves;
|
||||
if (b instanceof FlowerBlock) return Flower;
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) {
|
||||
|
||||
int count = 0;
|
||||
double alpha = 0;
|
||||
double red = 0;
|
||||
double green = 0;
|
||||
double blue = 0;
|
||||
int tempColor;
|
||||
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
for (int u = 0; u < texture.getWidth(); u++)
|
||||
{
|
||||
for (int v = 0; v < texture.getHeight(); v++)
|
||||
{
|
||||
//note: Minecraft color format is: 0xAA BB GG RR
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
||||
|
||||
double r = ((tempColor & 0x000000FF) )/255.;
|
||||
double g = ((tempColor & 0x0000FF00) >>> 8)/255.;
|
||||
double b = ((tempColor & 0x00FF0000) >>> 16)/255.;
|
||||
double a = ((tempColor & 0xFF000000) >>> 24)/255.;
|
||||
int scale = 1;
|
||||
|
||||
if (colorMode == ColorMode.Leaves) {
|
||||
r *= a;
|
||||
g *= a;
|
||||
b *= a;
|
||||
a = 1.;
|
||||
} else if (a==0.) {
|
||||
continue;
|
||||
} else if (colorMode == ColorMode.Flower && (g+0.1<b || g+0.1<r)) {
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
|
||||
count += scale;
|
||||
alpha += a*a*scale;
|
||||
red += r*r*scale;
|
||||
green += g*g*scale;
|
||||
blue += b*b*scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.rgbToInt(255,255,0,255);
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
(int) (Math.sqrt(alpha/count)*255.),
|
||||
(int) (Math.sqrt(red / count)*255.),
|
||||
(int) (Math.sqrt(green / count)*255.),
|
||||
(int) (Math.sqrt(blue / count)*255.));
|
||||
}
|
||||
// TODO: Remove this when transparency is added!
|
||||
double colorAlpha = ColorUtil.getAlpha(tempColor)/255.;
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
ColorUtil.getAlpha(tempColor),
|
||||
(int)(ColorUtil.getRed(tempColor) * colorAlpha),
|
||||
(int)(ColorUtil.getGreen(tempColor) * colorAlpha),
|
||||
(int)(ColorUtil.getBlue(tempColor) * colorAlpha)
|
||||
);
|
||||
return tempColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static final Block[] BLOCK_TO_AVOID = {Blocks.AIR, Blocks.CAVE_AIR, Blocks.BARRIER};
|
||||
|
||||
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
|
||||
|
||||
private static boolean isBlockToBeAvoid(Block b) {
|
||||
for (Block bta : BLOCK_TO_AVOID)
|
||||
if (bta==b) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
final BlockState state;
|
||||
final BlockPos samplePos;
|
||||
final LevelReader sampleGetter;
|
||||
|
||||
boolean isShapeResolved = false;
|
||||
boolean[] dontOccludeFaces = null;
|
||||
boolean noCollision = false;
|
||||
boolean noFullFace = false;
|
||||
|
||||
boolean isColorResolved = false;
|
||||
int baseColor = 0; //TODO: Impl per-face color
|
||||
boolean needShade = true;
|
||||
boolean needPostTinting = false;
|
||||
int tintIndex = 0;
|
||||
|
||||
public static BlockDetailWrapper NULL_BLOCK_DETAIL = new BlockDetailWrapper();
|
||||
|
||||
public BlockDetailWrapper(BlockState state, BlockPos pos, LevelReader getter) {
|
||||
this.state = state;
|
||||
this.samplePos = pos;
|
||||
this.sampleGetter = getter;
|
||||
}
|
||||
|
||||
private BlockDetailWrapper() {
|
||||
this.state = null;
|
||||
this.samplePos = null;
|
||||
this.sampleGetter = null;
|
||||
}
|
||||
|
||||
static BlockDetailWrapper make(BlockState bs, BlockPos pos, LevelReader getter) {
|
||||
if(!bs.getFluidState().isEmpty()) { // Is a fluidBlock
|
||||
if (isBlockToBeAvoid(bs.getBlock())) return NULL_BLOCK_DETAIL;
|
||||
if (bs.isAir()) return NULL_BLOCK_DETAIL;
|
||||
return new BlockDetailWrapper(bs, pos, getter);
|
||||
} else {
|
||||
if (bs.getRenderShape() != RenderShape.MODEL) return NULL_BLOCK_DETAIL;
|
||||
if (isBlockToBeAvoid(bs.getBlock())) return NULL_BLOCK_DETAIL;
|
||||
return new BlockDetailWrapper(bs, pos, getter);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveShapes() {
|
||||
if (isShapeResolved) return;
|
||||
if (state.getFluidState().isEmpty()) {
|
||||
noCollision = state.getCollisionShape(sampleGetter, samplePos).isEmpty();
|
||||
dontOccludeFaces = new boolean[6];
|
||||
if (state.canOcclude()) {
|
||||
/* FIXME: Figure out how or if needed to impl per-face culling?
|
||||
for (Direction dir : Direction.values()) {
|
||||
dontOccludeFaces[McObjectConverter.Convert(dir).ordinal()]
|
||||
= state.getFaceOcclusionShape(sampleGetter, samplePos, dir).isEmpty();
|
||||
}*/
|
||||
} else {
|
||||
Arrays.fill(dontOccludeFaces, true);
|
||||
}
|
||||
|
||||
VoxelShape voxelShape = state.getShape(sampleGetter, samplePos);
|
||||
if (voxelShape.isEmpty()) {
|
||||
noFullFace = true;
|
||||
} else {
|
||||
AABB bbox = voxelShape.bounds();
|
||||
double xWidth = (bbox.maxX - bbox.minX);
|
||||
double yWidth = (bbox.maxY - bbox.minY);
|
||||
double zWidth = (bbox.maxZ - bbox.minZ);
|
||||
noFullFace = xWidth < 1 && zWidth < 1 && yWidth < 1;
|
||||
}
|
||||
} else { // Liquad Block
|
||||
dontOccludeFaces = new boolean[6];
|
||||
}
|
||||
isShapeResolved = true;
|
||||
}
|
||||
|
||||
private void resolveColors() {
|
||||
if (isColorResolved) return;
|
||||
if (state.getFluidState().isEmpty()) {
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : DIRECTION_ORDER)
|
||||
{
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(state).getQuads(state, direction, random);
|
||||
if (!quads.isEmpty() &&
|
||||
!(state.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
|
||||
break;
|
||||
};
|
||||
if (quads != null && !quads.isEmpty()) {
|
||||
needPostTinting = quads.get(0).isTinted();
|
||||
needShade = quads.get(0).isShade();
|
||||
tintIndex = quads.get(0).getTintIndex();
|
||||
baseColor = calculateColorFromTexture(quads.get(0).getSprite(),
|
||||
ColorMode.getColorMode(state.getBlock()));
|
||||
}
|
||||
} else { // Liquid Block
|
||||
|
||||
needPostTinting = true;
|
||||
needShade = false;
|
||||
tintIndex = 0;
|
||||
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state),
|
||||
ColorMode.getColorMode(state.getBlock()));
|
||||
|
||||
}
|
||||
isColorResolved = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getAndResolveFaceColor(LodDirection dir, IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
{
|
||||
// FIXME: impl per-face colors
|
||||
resolveColors();
|
||||
if (!needPostTinting) return baseColor;
|
||||
int tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(state, ((ChunkWrapper)chunk).getColorResolver(),
|
||||
McObjectConverter.Convert(blockPos), tintIndex);
|
||||
if (tintColor == -1) return baseColor;
|
||||
return ColorUtil.multiplyARGBwithRGB(baseColor, tintColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFaceCullingFor(LodDirection dir)
|
||||
{
|
||||
resolveShapes();
|
||||
return !dontOccludeFaces[dir.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNoCollision()
|
||||
{
|
||||
resolveShapes();
|
||||
return noCollision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noFaceIsFullFace()
|
||||
{
|
||||
resolveShapes();
|
||||
return noFullFace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize()
|
||||
{
|
||||
// FIXME: Impl this for the blockState Storage stuff
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSame(IBlockDetailWrapper iBlockDetail)
|
||||
{
|
||||
return ((BlockDetailWrapper)iBlockDetail).state.getBlock().equals(state.getBlock());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,18 +2,17 @@ package com.seibel.lod.common.wrappers.chunk;
|
||||
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.BlockDetail;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
import com.seibel.lod.common.wrappers.WrapperUtil;
|
||||
import com.seibel.lod.common.wrappers.block.BlockDetailMap;
|
||||
import com.seibel.lod.common.wrappers.block.BlockDetailWrapper;
|
||||
import com.seibel.lod.common.wrappers.world.BiomeWrapper;
|
||||
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.QuartPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.block.LiquidBlockContainer;
|
||||
@@ -72,9 +71,11 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockDetail getBlockDetail(int x, int y, int z) {
|
||||
BlockState blockState = chunk.getBlockState(new BlockPos(x,y,z));
|
||||
return BlockDetailMap.getBlockDetailWithCompleteTint(blockState, x, y, z, lightSource);
|
||||
public BlockDetailWrapper getBlockDetail(int x, int y, int z) {
|
||||
BlockPos pos = new BlockPos(x,y,z);
|
||||
BlockState blockState = chunk.getBlockState(pos);
|
||||
BlockDetailWrapper blockDetail = BlockDetailMap.getOrMakeBlockDetailCache(blockState, pos, lightSource);
|
||||
return blockDetail == BlockDetailWrapper.NULL_BLOCK_DETAIL ? null : blockDetail;
|
||||
}
|
||||
|
||||
public ChunkAccess getChunk() {
|
||||
@@ -176,4 +177,9 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
return true;
|
||||
}
|
||||
|
||||
public LevelReader getColorResolver()
|
||||
{
|
||||
return lightSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -429,7 +429,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
||||
}
|
||||
e.pEvent.emptyNano = System.nanoTime();
|
||||
e.refreshTimeout();
|
||||
region = new LightedWorldGenRegion(params.level, lightEngine, e.tParam.structFeat, chunks, ChunkStatus.STRUCTURE_STARTS, rangeEmpty, e.lightMode, generator);
|
||||
region = new LightedWorldGenRegion(params.level, lightEngine, chunks, ChunkStatus.STRUCTURE_STARTS, rangeEmpty, e.lightMode, generator);
|
||||
adaptor.setRegion(region);
|
||||
e.tParam.makeStructFeat(region);
|
||||
referencedChunks = chunks.subGrid(e.range);
|
||||
|
||||
+47
-3
@@ -10,16 +10,24 @@ import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.LightGenerationMode;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockTintCache;
|
||||
import net.minecraft.client.renderer.BiomeColors;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Cursor3D;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
import net.minecraft.world.level.StructureFeatureManager;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -40,21 +48,25 @@ public class LightedWorldGenRegion extends WorldGenRegion {
|
||||
public final int size;
|
||||
private final ChunkPos firstPos;
|
||||
private final List<ChunkAccess> cache;
|
||||
private final StructureFeatureManager structFeat;
|
||||
Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
|
||||
|
||||
public LightedWorldGenRegion(ServerLevel serverLevel, WorldGenLevelLightEngine lightEngine,
|
||||
StructureFeatureManager structFeat, List<ChunkAccess> list, ChunkStatus chunkStatus, int i,
|
||||
List<ChunkAccess> list, ChunkStatus chunkStatus, int i,
|
||||
LightGenerationMode lightMode, EmptyChunkGenerator generator) {
|
||||
super(serverLevel, list, chunkStatus, i);
|
||||
this.lightMode = lightMode;
|
||||
this.firstPos = list.get(0).getPos();
|
||||
this.generator = generator;
|
||||
this.structFeat = structFeat;
|
||||
light = lightEngine;
|
||||
writeRadius = i;
|
||||
cache = list;
|
||||
size = Mth.floor(Math.sqrt(list.size()));
|
||||
|
||||
this.tintCaches = Util.make(new Object2ObjectArrayMap(3), object2ObjectArrayMap -> {
|
||||
object2ObjectArrayMap.put(BiomeColors.GRASS_COLOR_RESOLVER, new BlockTintCache((pos) -> {return calculateBlockTint(pos, BiomeColors.GRASS_COLOR_RESOLVER);}));
|
||||
object2ObjectArrayMap.put(BiomeColors.FOLIAGE_COLOR_RESOLVER, new BlockTintCache((pos) -> {return calculateBlockTint(pos, BiomeColors.FOLIAGE_COLOR_RESOLVER);}));
|
||||
object2ObjectArrayMap.put(BiomeColors.WATER_COLOR_RESOLVER, new BlockTintCache((pos) -> {return calculateBlockTint(pos, BiomeColors.WATER_COLOR_RESOLVER);}));
|
||||
});
|
||||
}
|
||||
|
||||
// Bypass BCLib mixin overrides.
|
||||
@@ -226,4 +238,36 @@ public class LightedWorldGenRegion extends WorldGenRegion {
|
||||
return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final Object2ObjectArrayMap<ColorResolver, BlockTintCache> tintCaches;
|
||||
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
BlockTintCache blockTintCache = (BlockTintCache) this.tintCaches.get(colorResolver);
|
||||
return blockTintCache.getColor(blockPos);
|
||||
}
|
||||
|
||||
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
|
||||
if (i == 0)
|
||||
return colorResolver.getColor((Biome) getBiome(blockPos).value(), blockPos.getX(), blockPos.getZ());
|
||||
int j = (i * 2 + 1) * (i * 2 + 1);
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
int m = 0;
|
||||
Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
|
||||
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
while (cursor3D.advance())
|
||||
{
|
||||
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
|
||||
int n = colorResolver.getColor((Biome) getBiome((BlockPos) mutableBlockPos).value(), mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
k += (n & 0xFF0000) >> 16;
|
||||
l += (n & 0xFF00) >> 8;
|
||||
m += n & 0xFF;
|
||||
}
|
||||
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
Submodule core updated: d731424e93...c9cabd8a32
Reference in New Issue
Block a user