655 lines
37 KiB
Java
655 lines
37 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* it.unimi.dsi.fastutil.longs.Long2FloatLinkedOpenHashMap
|
|
* it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap
|
|
*/
|
|
package net.minecraft.client.renderer.block;
|
|
|
|
import com.mojang.blaze3d.vertex.PoseStack;
|
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
import it.unimi.dsi.fastutil.longs.Long2FloatLinkedOpenHashMap;
|
|
import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap;
|
|
import java.util.List;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.ReportedException;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.color.block.BlockColors;
|
|
import net.minecraft.client.renderer.LevelRenderer;
|
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
|
import net.minecraft.client.renderer.block.model.BlockModelPart;
|
|
import net.minecraft.client.renderer.block.model.BlockStateModel;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.util.ARGB;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.util.Util;
|
|
import net.minecraft.world.level.BlockAndTintGetter;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
|
|
public class ModelBlockRenderer {
|
|
private static final Direction[] DIRECTIONS = Direction.values();
|
|
private final BlockColors blockColors;
|
|
private static final int CACHE_SIZE = 100;
|
|
private static final ThreadLocal<Cache> CACHE = ThreadLocal.withInitial(Cache::new);
|
|
|
|
public ModelBlockRenderer(BlockColors blockColors) {
|
|
this.blockColors = blockColors;
|
|
}
|
|
|
|
public void tesselateBlock(BlockAndTintGetter level, List<BlockModelPart> parts, BlockState blockState, BlockPos pos, PoseStack poseStack, VertexConsumer builder, boolean cull, int overlayCoords) {
|
|
if (parts.isEmpty()) {
|
|
return;
|
|
}
|
|
boolean useAO = Minecraft.useAmbientOcclusion() && blockState.getLightEmission() == 0 && parts.getFirst().useAmbientOcclusion();
|
|
poseStack.translate(blockState.getOffset(pos));
|
|
try {
|
|
if (useAO) {
|
|
this.tesselateWithAO(level, parts, blockState, pos, poseStack, builder, cull, overlayCoords);
|
|
} else {
|
|
this.tesselateWithoutAO(level, parts, blockState, pos, poseStack, builder, cull, overlayCoords);
|
|
}
|
|
}
|
|
catch (Throwable t) {
|
|
CrashReport report = CrashReport.forThrowable(t, "Tesselating block model");
|
|
CrashReportCategory category = report.addCategory("Block model being tesselated");
|
|
CrashReportCategory.populateBlockDetails(category, level, pos, blockState);
|
|
category.setDetail("Using AO", useAO);
|
|
throw new ReportedException(report);
|
|
}
|
|
}
|
|
|
|
private static boolean shouldRenderFace(BlockAndTintGetter level, BlockState state, boolean cullEnabled, Direction direction, BlockPos neighborPos) {
|
|
if (!cullEnabled) {
|
|
return true;
|
|
}
|
|
BlockState neighborState = level.getBlockState(neighborPos);
|
|
return Block.shouldRenderFace(state, neighborState, direction);
|
|
}
|
|
|
|
public void tesselateWithAO(BlockAndTintGetter level, List<BlockModelPart> parts, BlockState state, BlockPos pos, PoseStack poseStack, VertexConsumer builder, boolean cull, int overlayCoords) {
|
|
AmbientOcclusionRenderStorage scratch = new AmbientOcclusionRenderStorage();
|
|
int cacheValid = 0;
|
|
int shouldRenderFaceCache = 0;
|
|
for (BlockModelPart part : parts) {
|
|
for (Direction direction : DIRECTIONS) {
|
|
List<BakedQuad> culledQuads;
|
|
boolean shouldRenderFace;
|
|
int cacheMask = 1 << direction.ordinal();
|
|
boolean validCacheForDirection = (cacheValid & cacheMask) == 1;
|
|
boolean bl = shouldRenderFace = (shouldRenderFaceCache & cacheMask) == 1;
|
|
if (validCacheForDirection && !shouldRenderFace || (culledQuads = part.getQuads(direction)).isEmpty()) continue;
|
|
if (!validCacheForDirection) {
|
|
shouldRenderFace = ModelBlockRenderer.shouldRenderFace(level, state, cull, direction, scratch.scratchPos.setWithOffset((Vec3i)pos, direction));
|
|
cacheValid |= cacheMask;
|
|
if (shouldRenderFace) {
|
|
shouldRenderFaceCache |= cacheMask;
|
|
}
|
|
}
|
|
if (!shouldRenderFace) continue;
|
|
this.renderModelFaceAO(level, state, pos, poseStack, builder, culledQuads, scratch, overlayCoords);
|
|
}
|
|
List<BakedQuad> unculledQuads = part.getQuads(null);
|
|
if (unculledQuads.isEmpty()) continue;
|
|
this.renderModelFaceAO(level, state, pos, poseStack, builder, unculledQuads, scratch, overlayCoords);
|
|
}
|
|
}
|
|
|
|
public void tesselateWithoutAO(BlockAndTintGetter level, List<BlockModelPart> parts, BlockState state, BlockPos pos, PoseStack poseStack, VertexConsumer builder, boolean cull, int overlayCoords) {
|
|
CommonRenderStorage scratch = new CommonRenderStorage();
|
|
int cacheValid = 0;
|
|
int shouldRenderFaceCache = 0;
|
|
for (BlockModelPart part : parts) {
|
|
for (Direction direction : DIRECTIONS) {
|
|
List<BakedQuad> culledQuads;
|
|
boolean shouldRenderFace;
|
|
int cacheMask = 1 << direction.ordinal();
|
|
boolean validCacheForDirection = (cacheValid & cacheMask) == 1;
|
|
boolean bl = shouldRenderFace = (shouldRenderFaceCache & cacheMask) == 1;
|
|
if (validCacheForDirection && !shouldRenderFace || (culledQuads = part.getQuads(direction)).isEmpty()) continue;
|
|
BlockPos.MutableBlockPos relativePos = scratch.scratchPos.setWithOffset((Vec3i)pos, direction);
|
|
if (!validCacheForDirection) {
|
|
shouldRenderFace = ModelBlockRenderer.shouldRenderFace(level, state, cull, direction, relativePos);
|
|
cacheValid |= cacheMask;
|
|
if (shouldRenderFace) {
|
|
shouldRenderFaceCache |= cacheMask;
|
|
}
|
|
}
|
|
if (!shouldRenderFace) continue;
|
|
int lightColor = scratch.cache.getLightColor(state, level, relativePos);
|
|
this.renderModelFaceFlat(level, state, pos, lightColor, overlayCoords, false, poseStack, builder, culledQuads, scratch);
|
|
}
|
|
List<BakedQuad> unculledQuads = part.getQuads(null);
|
|
if (unculledQuads.isEmpty()) continue;
|
|
this.renderModelFaceFlat(level, state, pos, -1, overlayCoords, true, poseStack, builder, unculledQuads, scratch);
|
|
}
|
|
}
|
|
|
|
private void renderModelFaceAO(BlockAndTintGetter level, BlockState state, BlockPos pos, PoseStack poseStack, VertexConsumer builder, List<BakedQuad> quads, AmbientOcclusionRenderStorage storage, int overlayCoords) {
|
|
for (BakedQuad quad : quads) {
|
|
ModelBlockRenderer.calculateShape(level, state, pos, quad.vertices(), quad.direction(), storage);
|
|
storage.calculate(level, state, pos, quad.direction(), quad.shade());
|
|
this.putQuadData(level, state, pos, builder, poseStack.last(), quad, storage, overlayCoords);
|
|
}
|
|
}
|
|
|
|
private void putQuadData(BlockAndTintGetter level, BlockState state, BlockPos pos, VertexConsumer builder, PoseStack.Pose pose, BakedQuad quad, CommonRenderStorage renderStorage, int overlayCoords) {
|
|
float b;
|
|
float g;
|
|
float r;
|
|
int tintIndex = quad.tintIndex();
|
|
if (tintIndex != -1) {
|
|
int tintColor;
|
|
if (renderStorage.tintCacheIndex == tintIndex) {
|
|
tintColor = renderStorage.tintCacheValue;
|
|
} else {
|
|
tintColor = this.blockColors.getColor(state, level, pos, tintIndex);
|
|
renderStorage.tintCacheIndex = tintIndex;
|
|
renderStorage.tintCacheValue = tintColor;
|
|
}
|
|
r = ARGB.redFloat(tintColor);
|
|
g = ARGB.greenFloat(tintColor);
|
|
b = ARGB.blueFloat(tintColor);
|
|
} else {
|
|
r = 1.0f;
|
|
g = 1.0f;
|
|
b = 1.0f;
|
|
}
|
|
builder.putBulkData(pose, quad, renderStorage.brightness, r, g, b, 1.0f, renderStorage.lightmap, overlayCoords, true);
|
|
}
|
|
|
|
private static void calculateShape(BlockAndTintGetter level, BlockState state, BlockPos pos, int[] vertices, Direction direction, CommonRenderStorage storage) {
|
|
float minX = 32.0f;
|
|
float minY = 32.0f;
|
|
float minZ = 32.0f;
|
|
float maxX = -32.0f;
|
|
float maxY = -32.0f;
|
|
float maxZ = -32.0f;
|
|
for (int i = 0; i < 4; ++i) {
|
|
float x = Float.intBitsToFloat(vertices[i * 8]);
|
|
float y = Float.intBitsToFloat(vertices[i * 8 + 1]);
|
|
float z = Float.intBitsToFloat(vertices[i * 8 + 2]);
|
|
minX = Math.min(minX, x);
|
|
minY = Math.min(minY, y);
|
|
minZ = Math.min(minZ, z);
|
|
maxX = Math.max(maxX, x);
|
|
maxY = Math.max(maxY, y);
|
|
maxZ = Math.max(maxZ, z);
|
|
}
|
|
if (storage instanceof AmbientOcclusionRenderStorage) {
|
|
AmbientOcclusionRenderStorage aoStorage = (AmbientOcclusionRenderStorage)storage;
|
|
aoStorage.faceShape[SizeInfo.WEST.index] = minX;
|
|
aoStorage.faceShape[SizeInfo.EAST.index] = maxX;
|
|
aoStorage.faceShape[SizeInfo.DOWN.index] = minY;
|
|
aoStorage.faceShape[SizeInfo.UP.index] = maxY;
|
|
aoStorage.faceShape[SizeInfo.NORTH.index] = minZ;
|
|
aoStorage.faceShape[SizeInfo.SOUTH.index] = maxZ;
|
|
aoStorage.faceShape[SizeInfo.FLIP_WEST.index] = 1.0f - minX;
|
|
aoStorage.faceShape[SizeInfo.FLIP_EAST.index] = 1.0f - maxX;
|
|
aoStorage.faceShape[SizeInfo.FLIP_DOWN.index] = 1.0f - minY;
|
|
aoStorage.faceShape[SizeInfo.FLIP_UP.index] = 1.0f - maxY;
|
|
aoStorage.faceShape[SizeInfo.FLIP_NORTH.index] = 1.0f - minZ;
|
|
aoStorage.faceShape[SizeInfo.FLIP_SOUTH.index] = 1.0f - maxZ;
|
|
}
|
|
float minEpsilon = 1.0E-4f;
|
|
float maxEpsilon = 0.9999f;
|
|
storage.facePartial = switch (direction) {
|
|
default -> throw new MatchException(null, null);
|
|
case Direction.DOWN, Direction.UP -> {
|
|
if (minX >= 1.0E-4f || minZ >= 1.0E-4f || maxX <= 0.9999f || maxZ <= 0.9999f) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.NORTH, Direction.SOUTH -> {
|
|
if (minX >= 1.0E-4f || minY >= 1.0E-4f || maxX <= 0.9999f || maxY <= 0.9999f) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.WEST, Direction.EAST -> minY >= 1.0E-4f || minZ >= 1.0E-4f || maxY <= 0.9999f || maxZ <= 0.9999f;
|
|
};
|
|
storage.faceCubic = switch (direction) {
|
|
default -> throw new MatchException(null, null);
|
|
case Direction.DOWN -> {
|
|
if (minY == maxY && (minY < 1.0E-4f || state.isCollisionShapeFullBlock(level, pos))) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.UP -> {
|
|
if (minY == maxY && (maxY > 0.9999f || state.isCollisionShapeFullBlock(level, pos))) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.NORTH -> {
|
|
if (minZ == maxZ && (minZ < 1.0E-4f || state.isCollisionShapeFullBlock(level, pos))) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.SOUTH -> {
|
|
if (minZ == maxZ && (maxZ > 0.9999f || state.isCollisionShapeFullBlock(level, pos))) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.WEST -> {
|
|
if (minX == maxX && (minX < 1.0E-4f || state.isCollisionShapeFullBlock(level, pos))) {
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case Direction.EAST -> minX == maxX && (maxX > 0.9999f || state.isCollisionShapeFullBlock(level, pos));
|
|
};
|
|
}
|
|
|
|
private void renderModelFaceFlat(BlockAndTintGetter level, BlockState state, BlockPos pos, int lightColor, int overlayCoords, boolean checkLight, PoseStack poseStack, VertexConsumer builder, List<BakedQuad> quads, CommonRenderStorage shapeState) {
|
|
for (BakedQuad quad : quads) {
|
|
float directionalBrightness;
|
|
if (checkLight) {
|
|
ModelBlockRenderer.calculateShape(level, state, pos, quad.vertices(), quad.direction(), shapeState);
|
|
BlockPos lightPos = shapeState.faceCubic ? shapeState.scratchPos.setWithOffset((Vec3i)pos, quad.direction()) : pos;
|
|
lightColor = shapeState.cache.getLightColor(state, level, lightPos);
|
|
}
|
|
shapeState.brightness[0] = directionalBrightness = level.getShade(quad.direction(), quad.shade());
|
|
shapeState.brightness[1] = directionalBrightness;
|
|
shapeState.brightness[2] = directionalBrightness;
|
|
shapeState.brightness[3] = directionalBrightness;
|
|
shapeState.lightmap[0] = lightColor;
|
|
shapeState.lightmap[1] = lightColor;
|
|
shapeState.lightmap[2] = lightColor;
|
|
shapeState.lightmap[3] = lightColor;
|
|
this.putQuadData(level, state, pos, builder, poseStack.last(), quad, shapeState, overlayCoords);
|
|
}
|
|
}
|
|
|
|
public static void renderModel(PoseStack.Pose pose, VertexConsumer builder, BlockStateModel model, float r, float g, float b, int lightCoords, int overlayCoords) {
|
|
for (BlockModelPart part : model.collectParts(RandomSource.create(42L))) {
|
|
for (Direction direction : DIRECTIONS) {
|
|
ModelBlockRenderer.renderQuadList(pose, builder, r, g, b, part.getQuads(direction), lightCoords, overlayCoords);
|
|
}
|
|
ModelBlockRenderer.renderQuadList(pose, builder, r, g, b, part.getQuads(null), lightCoords, overlayCoords);
|
|
}
|
|
}
|
|
|
|
private static void renderQuadList(PoseStack.Pose pose, VertexConsumer builder, float r, float g, float b, List<BakedQuad> quads, int lightCoords, int overlayCoords) {
|
|
for (BakedQuad quad : quads) {
|
|
float blue;
|
|
float green;
|
|
float red;
|
|
if (quad.isTinted()) {
|
|
red = Mth.clamp(r, 0.0f, 1.0f);
|
|
green = Mth.clamp(g, 0.0f, 1.0f);
|
|
blue = Mth.clamp(b, 0.0f, 1.0f);
|
|
} else {
|
|
red = 1.0f;
|
|
green = 1.0f;
|
|
blue = 1.0f;
|
|
}
|
|
builder.putBulkData(pose, quad, red, green, blue, 1.0f, lightCoords, overlayCoords);
|
|
}
|
|
}
|
|
|
|
public static void enableCaching() {
|
|
CACHE.get().enable();
|
|
}
|
|
|
|
public static void clearCache() {
|
|
CACHE.get().disable();
|
|
}
|
|
|
|
private static class AmbientOcclusionRenderStorage
|
|
extends CommonRenderStorage {
|
|
private final float[] faceShape = new float[SizeInfo.COUNT];
|
|
|
|
public void calculate(BlockAndTintGetter level, BlockState state, BlockPos centerPosition, Direction direction, boolean shade) {
|
|
int lightCorner13;
|
|
float shadeCorner13;
|
|
int lightCorner12;
|
|
float shadeCorner12;
|
|
int lightCorner03;
|
|
float shadeCorner03;
|
|
int lightCorner02;
|
|
float shadeCorner02;
|
|
boolean translucent3;
|
|
BlockPos basePosition = this.faceCubic ? centerPosition.relative(direction) : centerPosition;
|
|
AdjacencyInfo info = AdjacencyInfo.fromFacing(direction);
|
|
BlockPos.MutableBlockPos pos = this.scratchPos;
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[0]);
|
|
BlockState state0 = level.getBlockState(pos);
|
|
int light0 = this.cache.getLightColor(state0, level, pos);
|
|
float shade0 = this.cache.getShadeBrightness(state0, level, pos);
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[1]);
|
|
BlockState state1 = level.getBlockState(pos);
|
|
int light1 = this.cache.getLightColor(state1, level, pos);
|
|
float shade1 = this.cache.getShadeBrightness(state1, level, pos);
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[2]);
|
|
BlockState state2 = level.getBlockState(pos);
|
|
int light2 = this.cache.getLightColor(state2, level, pos);
|
|
float shade2 = this.cache.getShadeBrightness(state2, level, pos);
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[3]);
|
|
BlockState state3 = level.getBlockState(pos);
|
|
int light3 = this.cache.getLightColor(state3, level, pos);
|
|
float shade3 = this.cache.getShadeBrightness(state3, level, pos);
|
|
BlockState corner0 = level.getBlockState(pos.setWithOffset((Vec3i)basePosition, info.corners[0]).move(direction));
|
|
boolean translucent0 = !corner0.isViewBlocking(level, pos) || corner0.getLightBlock() == 0;
|
|
BlockState corner1 = level.getBlockState(pos.setWithOffset((Vec3i)basePosition, info.corners[1]).move(direction));
|
|
boolean translucent1 = !corner1.isViewBlocking(level, pos) || corner1.getLightBlock() == 0;
|
|
BlockState corner2 = level.getBlockState(pos.setWithOffset((Vec3i)basePosition, info.corners[2]).move(direction));
|
|
boolean translucent2 = !corner2.isViewBlocking(level, pos) || corner2.getLightBlock() == 0;
|
|
BlockState corner3 = level.getBlockState(pos.setWithOffset((Vec3i)basePosition, info.corners[3]).move(direction));
|
|
boolean bl = translucent3 = !corner3.isViewBlocking(level, pos) || corner3.getLightBlock() == 0;
|
|
if (translucent2 || translucent0) {
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[0]).move(info.corners[2]);
|
|
BlockState state02 = level.getBlockState(pos);
|
|
shadeCorner02 = this.cache.getShadeBrightness(state02, level, pos);
|
|
lightCorner02 = this.cache.getLightColor(state02, level, pos);
|
|
} else {
|
|
shadeCorner02 = shade0;
|
|
lightCorner02 = light0;
|
|
}
|
|
if (translucent3 || translucent0) {
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[0]).move(info.corners[3]);
|
|
BlockState state03 = level.getBlockState(pos);
|
|
shadeCorner03 = this.cache.getShadeBrightness(state03, level, pos);
|
|
lightCorner03 = this.cache.getLightColor(state03, level, pos);
|
|
} else {
|
|
shadeCorner03 = shade0;
|
|
lightCorner03 = light0;
|
|
}
|
|
if (translucent2 || translucent1) {
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[1]).move(info.corners[2]);
|
|
BlockState state12 = level.getBlockState(pos);
|
|
shadeCorner12 = this.cache.getShadeBrightness(state12, level, pos);
|
|
lightCorner12 = this.cache.getLightColor(state12, level, pos);
|
|
} else {
|
|
shadeCorner12 = shade0;
|
|
lightCorner12 = light0;
|
|
}
|
|
if (translucent3 || translucent1) {
|
|
pos.setWithOffset((Vec3i)basePosition, info.corners[1]).move(info.corners[3]);
|
|
BlockState state13 = level.getBlockState(pos);
|
|
shadeCorner13 = this.cache.getShadeBrightness(state13, level, pos);
|
|
lightCorner13 = this.cache.getLightColor(state13, level, pos);
|
|
} else {
|
|
shadeCorner13 = shade0;
|
|
lightCorner13 = light0;
|
|
}
|
|
int lightCenter = this.cache.getLightColor(state, level, centerPosition);
|
|
pos.setWithOffset((Vec3i)centerPosition, direction);
|
|
BlockState nextState = level.getBlockState(pos);
|
|
if (this.faceCubic || !nextState.isSolidRender()) {
|
|
lightCenter = this.cache.getLightColor(nextState, level, pos);
|
|
}
|
|
float shadeCenter = this.faceCubic ? this.cache.getShadeBrightness(level.getBlockState(basePosition), level, basePosition) : this.cache.getShadeBrightness(level.getBlockState(centerPosition), level, centerPosition);
|
|
AmbientVertexRemap remap = AmbientVertexRemap.fromFacing(direction);
|
|
if (!this.facePartial || !info.doNonCubicWeight) {
|
|
float lightLevel1 = (shade3 + shade0 + shadeCorner03 + shadeCenter) * 0.25f;
|
|
float lightLevel2 = (shade2 + shade0 + shadeCorner02 + shadeCenter) * 0.25f;
|
|
float lightLevel3 = (shade2 + shade1 + shadeCorner12 + shadeCenter) * 0.25f;
|
|
float lightLevel4 = (shade3 + shade1 + shadeCorner13 + shadeCenter) * 0.25f;
|
|
this.lightmap[remap.vert0] = AmbientOcclusionRenderStorage.blend(light3, light0, lightCorner03, lightCenter);
|
|
this.lightmap[remap.vert1] = AmbientOcclusionRenderStorage.blend(light2, light0, lightCorner02, lightCenter);
|
|
this.lightmap[remap.vert2] = AmbientOcclusionRenderStorage.blend(light2, light1, lightCorner12, lightCenter);
|
|
this.lightmap[remap.vert3] = AmbientOcclusionRenderStorage.blend(light3, light1, lightCorner13, lightCenter);
|
|
this.brightness[remap.vert0] = lightLevel1;
|
|
this.brightness[remap.vert1] = lightLevel2;
|
|
this.brightness[remap.vert2] = lightLevel3;
|
|
this.brightness[remap.vert3] = lightLevel4;
|
|
} else {
|
|
float tempShade1 = (shade3 + shade0 + shadeCorner03 + shadeCenter) * 0.25f;
|
|
float tempShade2 = (shade2 + shade0 + shadeCorner02 + shadeCenter) * 0.25f;
|
|
float tempShade3 = (shade2 + shade1 + shadeCorner12 + shadeCenter) * 0.25f;
|
|
float tempShade4 = (shade3 + shade1 + shadeCorner13 + shadeCenter) * 0.25f;
|
|
float vert0weight01 = this.faceShape[info.vert0Weights[0].index] * this.faceShape[info.vert0Weights[1].index];
|
|
float vert0weight23 = this.faceShape[info.vert0Weights[2].index] * this.faceShape[info.vert0Weights[3].index];
|
|
float vert0weight45 = this.faceShape[info.vert0Weights[4].index] * this.faceShape[info.vert0Weights[5].index];
|
|
float vert0weight67 = this.faceShape[info.vert0Weights[6].index] * this.faceShape[info.vert0Weights[7].index];
|
|
float vert1weight01 = this.faceShape[info.vert1Weights[0].index] * this.faceShape[info.vert1Weights[1].index];
|
|
float vert1weight23 = this.faceShape[info.vert1Weights[2].index] * this.faceShape[info.vert1Weights[3].index];
|
|
float vert1weight45 = this.faceShape[info.vert1Weights[4].index] * this.faceShape[info.vert1Weights[5].index];
|
|
float vert1weight67 = this.faceShape[info.vert1Weights[6].index] * this.faceShape[info.vert1Weights[7].index];
|
|
float vert2weight01 = this.faceShape[info.vert2Weights[0].index] * this.faceShape[info.vert2Weights[1].index];
|
|
float vert2weight23 = this.faceShape[info.vert2Weights[2].index] * this.faceShape[info.vert2Weights[3].index];
|
|
float vert2weight45 = this.faceShape[info.vert2Weights[4].index] * this.faceShape[info.vert2Weights[5].index];
|
|
float vert2weight67 = this.faceShape[info.vert2Weights[6].index] * this.faceShape[info.vert2Weights[7].index];
|
|
float vert3weight01 = this.faceShape[info.vert3Weights[0].index] * this.faceShape[info.vert3Weights[1].index];
|
|
float vert3weight23 = this.faceShape[info.vert3Weights[2].index] * this.faceShape[info.vert3Weights[3].index];
|
|
float vert3weight45 = this.faceShape[info.vert3Weights[4].index] * this.faceShape[info.vert3Weights[5].index];
|
|
float vert3weight67 = this.faceShape[info.vert3Weights[6].index] * this.faceShape[info.vert3Weights[7].index];
|
|
this.brightness[remap.vert0] = Math.clamp(tempShade1 * vert0weight01 + tempShade2 * vert0weight23 + tempShade3 * vert0weight45 + tempShade4 * vert0weight67, 0.0f, 1.0f);
|
|
this.brightness[remap.vert1] = Math.clamp(tempShade1 * vert1weight01 + tempShade2 * vert1weight23 + tempShade3 * vert1weight45 + tempShade4 * vert1weight67, 0.0f, 1.0f);
|
|
this.brightness[remap.vert2] = Math.clamp(tempShade1 * vert2weight01 + tempShade2 * vert2weight23 + tempShade3 * vert2weight45 + tempShade4 * vert2weight67, 0.0f, 1.0f);
|
|
this.brightness[remap.vert3] = Math.clamp(tempShade1 * vert3weight01 + tempShade2 * vert3weight23 + tempShade3 * vert3weight45 + tempShade4 * vert3weight67, 0.0f, 1.0f);
|
|
int _tc1 = AmbientOcclusionRenderStorage.blend(light3, light0, lightCorner03, lightCenter);
|
|
int _tc2 = AmbientOcclusionRenderStorage.blend(light2, light0, lightCorner02, lightCenter);
|
|
int _tc3 = AmbientOcclusionRenderStorage.blend(light2, light1, lightCorner12, lightCenter);
|
|
int _tc4 = AmbientOcclusionRenderStorage.blend(light3, light1, lightCorner13, lightCenter);
|
|
this.lightmap[remap.vert0] = AmbientOcclusionRenderStorage.blend(_tc1, _tc2, _tc3, _tc4, vert0weight01, vert0weight23, vert0weight45, vert0weight67);
|
|
this.lightmap[remap.vert1] = AmbientOcclusionRenderStorage.blend(_tc1, _tc2, _tc3, _tc4, vert1weight01, vert1weight23, vert1weight45, vert1weight67);
|
|
this.lightmap[remap.vert2] = AmbientOcclusionRenderStorage.blend(_tc1, _tc2, _tc3, _tc4, vert2weight01, vert2weight23, vert2weight45, vert2weight67);
|
|
this.lightmap[remap.vert3] = AmbientOcclusionRenderStorage.blend(_tc1, _tc2, _tc3, _tc4, vert3weight01, vert3weight23, vert3weight45, vert3weight67);
|
|
}
|
|
float directionalBrightness = level.getShade(direction, shade);
|
|
int i = 0;
|
|
while (i < this.brightness.length) {
|
|
int n = i++;
|
|
this.brightness[n] = this.brightness[n] * directionalBrightness;
|
|
}
|
|
}
|
|
|
|
private static int blend(int a, int b, int c, int def) {
|
|
if (a == 0) {
|
|
a = def;
|
|
}
|
|
if (b == 0) {
|
|
b = def;
|
|
}
|
|
if (c == 0) {
|
|
c = def;
|
|
}
|
|
return a + b + c + def >> 2 & 0xFF00FF;
|
|
}
|
|
|
|
private static int blend(int a, int b, int c, int d, float fa, float fb, float fc, float fd) {
|
|
int top = (int)((float)(a >> 16 & 0xFF) * fa + (float)(b >> 16 & 0xFF) * fb + (float)(c >> 16 & 0xFF) * fc + (float)(d >> 16 & 0xFF) * fd) & 0xFF;
|
|
int bottom = (int)((float)(a & 0xFF) * fa + (float)(b & 0xFF) * fb + (float)(c & 0xFF) * fc + (float)(d & 0xFF) * fd) & 0xFF;
|
|
return top << 16 | bottom;
|
|
}
|
|
}
|
|
|
|
private static class CommonRenderStorage {
|
|
public final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos();
|
|
public boolean faceCubic;
|
|
public boolean facePartial;
|
|
public final float[] brightness = new float[4];
|
|
public final int[] lightmap = new int[4];
|
|
public int tintCacheIndex = -1;
|
|
public int tintCacheValue;
|
|
public final Cache cache = CACHE.get();
|
|
|
|
private CommonRenderStorage() {
|
|
}
|
|
}
|
|
|
|
private static class Cache {
|
|
private boolean enabled;
|
|
private final Long2IntLinkedOpenHashMap colorCache = Util.make(() -> {
|
|
Long2IntLinkedOpenHashMap map = new Long2IntLinkedOpenHashMap(100, 0.25f){
|
|
|
|
protected void rehash(int newN) {
|
|
}
|
|
};
|
|
map.defaultReturnValue(Integer.MAX_VALUE);
|
|
return map;
|
|
});
|
|
private final Long2FloatLinkedOpenHashMap brightnessCache = Util.make(() -> {
|
|
Long2FloatLinkedOpenHashMap map = new Long2FloatLinkedOpenHashMap(100, 0.25f){
|
|
|
|
protected void rehash(int newN) {
|
|
}
|
|
};
|
|
map.defaultReturnValue(Float.NaN);
|
|
return map;
|
|
});
|
|
private final LevelRenderer.BrightnessGetter cachedBrightnessGetter = (level, pos) -> {
|
|
long key = pos.asLong();
|
|
int cached = this.colorCache.get(key);
|
|
if (cached != Integer.MAX_VALUE) {
|
|
return cached;
|
|
}
|
|
int value = LevelRenderer.BrightnessGetter.DEFAULT.packedBrightness(level, pos);
|
|
if (this.colorCache.size() == 100) {
|
|
this.colorCache.removeFirstInt();
|
|
}
|
|
this.colorCache.put(key, value);
|
|
return value;
|
|
};
|
|
|
|
private Cache() {
|
|
}
|
|
|
|
public void enable() {
|
|
this.enabled = true;
|
|
}
|
|
|
|
public void disable() {
|
|
this.enabled = false;
|
|
this.colorCache.clear();
|
|
this.brightnessCache.clear();
|
|
}
|
|
|
|
public int getLightColor(BlockState state, BlockAndTintGetter level, BlockPos pos) {
|
|
return LevelRenderer.getLightColor(this.enabled ? this.cachedBrightnessGetter : LevelRenderer.BrightnessGetter.DEFAULT, level, state, pos);
|
|
}
|
|
|
|
public float getShadeBrightness(BlockState state, BlockAndTintGetter level, BlockPos pos) {
|
|
float cached;
|
|
long key = pos.asLong();
|
|
if (this.enabled && !Float.isNaN(cached = this.brightnessCache.get(key))) {
|
|
return cached;
|
|
}
|
|
float brightness = state.getShadeBrightness(level, pos);
|
|
if (this.enabled) {
|
|
if (this.brightnessCache.size() == 100) {
|
|
this.brightnessCache.removeFirstFloat();
|
|
}
|
|
this.brightnessCache.put(key, brightness);
|
|
}
|
|
return brightness;
|
|
}
|
|
}
|
|
|
|
protected static enum SizeInfo {
|
|
DOWN(0),
|
|
UP(1),
|
|
NORTH(2),
|
|
SOUTH(3),
|
|
WEST(4),
|
|
EAST(5),
|
|
FLIP_DOWN(6),
|
|
FLIP_UP(7),
|
|
FLIP_NORTH(8),
|
|
FLIP_SOUTH(9),
|
|
FLIP_WEST(10),
|
|
FLIP_EAST(11);
|
|
|
|
public static final int COUNT;
|
|
private final int index;
|
|
|
|
private SizeInfo(int index) {
|
|
this.index = index;
|
|
}
|
|
|
|
static {
|
|
COUNT = SizeInfo.values().length;
|
|
}
|
|
}
|
|
|
|
protected static enum AdjacencyInfo {
|
|
DOWN(new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH}, 0.5f, true, new SizeInfo[]{SizeInfo.FLIP_WEST, SizeInfo.SOUTH, SizeInfo.FLIP_WEST, SizeInfo.FLIP_SOUTH, SizeInfo.WEST, SizeInfo.FLIP_SOUTH, SizeInfo.WEST, SizeInfo.SOUTH}, new SizeInfo[]{SizeInfo.FLIP_WEST, SizeInfo.NORTH, SizeInfo.FLIP_WEST, SizeInfo.FLIP_NORTH, SizeInfo.WEST, SizeInfo.FLIP_NORTH, SizeInfo.WEST, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.FLIP_EAST, SizeInfo.NORTH, SizeInfo.FLIP_EAST, SizeInfo.FLIP_NORTH, SizeInfo.EAST, SizeInfo.FLIP_NORTH, SizeInfo.EAST, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.FLIP_EAST, SizeInfo.SOUTH, SizeInfo.FLIP_EAST, SizeInfo.FLIP_SOUTH, SizeInfo.EAST, SizeInfo.FLIP_SOUTH, SizeInfo.EAST, SizeInfo.SOUTH}),
|
|
UP(new Direction[]{Direction.EAST, Direction.WEST, Direction.NORTH, Direction.SOUTH}, 1.0f, true, new SizeInfo[]{SizeInfo.EAST, SizeInfo.SOUTH, SizeInfo.EAST, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_EAST, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_EAST, SizeInfo.SOUTH}, new SizeInfo[]{SizeInfo.EAST, SizeInfo.NORTH, SizeInfo.EAST, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_EAST, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_EAST, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.WEST, SizeInfo.NORTH, SizeInfo.WEST, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_WEST, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_WEST, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.WEST, SizeInfo.SOUTH, SizeInfo.WEST, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_WEST, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_WEST, SizeInfo.SOUTH}),
|
|
NORTH(new Direction[]{Direction.UP, Direction.DOWN, Direction.EAST, Direction.WEST}, 0.8f, true, new SizeInfo[]{SizeInfo.UP, SizeInfo.FLIP_WEST, SizeInfo.UP, SizeInfo.WEST, SizeInfo.FLIP_UP, SizeInfo.WEST, SizeInfo.FLIP_UP, SizeInfo.FLIP_WEST}, new SizeInfo[]{SizeInfo.UP, SizeInfo.FLIP_EAST, SizeInfo.UP, SizeInfo.EAST, SizeInfo.FLIP_UP, SizeInfo.EAST, SizeInfo.FLIP_UP, SizeInfo.FLIP_EAST}, new SizeInfo[]{SizeInfo.DOWN, SizeInfo.FLIP_EAST, SizeInfo.DOWN, SizeInfo.EAST, SizeInfo.FLIP_DOWN, SizeInfo.EAST, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_EAST}, new SizeInfo[]{SizeInfo.DOWN, SizeInfo.FLIP_WEST, SizeInfo.DOWN, SizeInfo.WEST, SizeInfo.FLIP_DOWN, SizeInfo.WEST, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_WEST}),
|
|
SOUTH(new Direction[]{Direction.WEST, Direction.EAST, Direction.DOWN, Direction.UP}, 0.8f, true, new SizeInfo[]{SizeInfo.UP, SizeInfo.FLIP_WEST, SizeInfo.FLIP_UP, SizeInfo.FLIP_WEST, SizeInfo.FLIP_UP, SizeInfo.WEST, SizeInfo.UP, SizeInfo.WEST}, new SizeInfo[]{SizeInfo.DOWN, SizeInfo.FLIP_WEST, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_WEST, SizeInfo.FLIP_DOWN, SizeInfo.WEST, SizeInfo.DOWN, SizeInfo.WEST}, new SizeInfo[]{SizeInfo.DOWN, SizeInfo.FLIP_EAST, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_EAST, SizeInfo.FLIP_DOWN, SizeInfo.EAST, SizeInfo.DOWN, SizeInfo.EAST}, new SizeInfo[]{SizeInfo.UP, SizeInfo.FLIP_EAST, SizeInfo.FLIP_UP, SizeInfo.FLIP_EAST, SizeInfo.FLIP_UP, SizeInfo.EAST, SizeInfo.UP, SizeInfo.EAST}),
|
|
WEST(new Direction[]{Direction.UP, Direction.DOWN, Direction.NORTH, Direction.SOUTH}, 0.6f, true, new SizeInfo[]{SizeInfo.UP, SizeInfo.SOUTH, SizeInfo.UP, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_UP, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_UP, SizeInfo.SOUTH}, new SizeInfo[]{SizeInfo.UP, SizeInfo.NORTH, SizeInfo.UP, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_UP, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_UP, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.DOWN, SizeInfo.NORTH, SizeInfo.DOWN, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_NORTH, SizeInfo.FLIP_DOWN, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.DOWN, SizeInfo.SOUTH, SizeInfo.DOWN, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_SOUTH, SizeInfo.FLIP_DOWN, SizeInfo.SOUTH}),
|
|
EAST(new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH}, 0.6f, true, new SizeInfo[]{SizeInfo.FLIP_DOWN, SizeInfo.SOUTH, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_SOUTH, SizeInfo.DOWN, SizeInfo.FLIP_SOUTH, SizeInfo.DOWN, SizeInfo.SOUTH}, new SizeInfo[]{SizeInfo.FLIP_DOWN, SizeInfo.NORTH, SizeInfo.FLIP_DOWN, SizeInfo.FLIP_NORTH, SizeInfo.DOWN, SizeInfo.FLIP_NORTH, SizeInfo.DOWN, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.FLIP_UP, SizeInfo.NORTH, SizeInfo.FLIP_UP, SizeInfo.FLIP_NORTH, SizeInfo.UP, SizeInfo.FLIP_NORTH, SizeInfo.UP, SizeInfo.NORTH}, new SizeInfo[]{SizeInfo.FLIP_UP, SizeInfo.SOUTH, SizeInfo.FLIP_UP, SizeInfo.FLIP_SOUTH, SizeInfo.UP, SizeInfo.FLIP_SOUTH, SizeInfo.UP, SizeInfo.SOUTH});
|
|
|
|
private final Direction[] corners;
|
|
private final boolean doNonCubicWeight;
|
|
private final SizeInfo[] vert0Weights;
|
|
private final SizeInfo[] vert1Weights;
|
|
private final SizeInfo[] vert2Weights;
|
|
private final SizeInfo[] vert3Weights;
|
|
private static final AdjacencyInfo[] BY_FACING;
|
|
|
|
private AdjacencyInfo(Direction[] corners, float shadeWeight, boolean doNonCubicWeight, SizeInfo[] vert0Weights, SizeInfo[] vert1Weights, SizeInfo[] vert2Weights, SizeInfo[] vert3Weights) {
|
|
this.corners = corners;
|
|
this.doNonCubicWeight = doNonCubicWeight;
|
|
this.vert0Weights = vert0Weights;
|
|
this.vert1Weights = vert1Weights;
|
|
this.vert2Weights = vert2Weights;
|
|
this.vert3Weights = vert3Weights;
|
|
}
|
|
|
|
public static AdjacencyInfo fromFacing(Direction direction) {
|
|
return BY_FACING[direction.get3DDataValue()];
|
|
}
|
|
|
|
static {
|
|
BY_FACING = Util.make(new AdjacencyInfo[6], map -> {
|
|
map[Direction.DOWN.get3DDataValue()] = DOWN;
|
|
map[Direction.UP.get3DDataValue()] = UP;
|
|
map[Direction.NORTH.get3DDataValue()] = NORTH;
|
|
map[Direction.SOUTH.get3DDataValue()] = SOUTH;
|
|
map[Direction.WEST.get3DDataValue()] = WEST;
|
|
map[Direction.EAST.get3DDataValue()] = EAST;
|
|
});
|
|
}
|
|
}
|
|
|
|
private static enum AmbientVertexRemap {
|
|
DOWN(0, 1, 2, 3),
|
|
UP(2, 3, 0, 1),
|
|
NORTH(3, 0, 1, 2),
|
|
SOUTH(0, 1, 2, 3),
|
|
WEST(3, 0, 1, 2),
|
|
EAST(1, 2, 3, 0);
|
|
|
|
private final int vert0;
|
|
private final int vert1;
|
|
private final int vert2;
|
|
private final int vert3;
|
|
private static final AmbientVertexRemap[] BY_FACING;
|
|
|
|
private AmbientVertexRemap(int vert0, int vert1, int vert2, int vert3) {
|
|
this.vert0 = vert0;
|
|
this.vert1 = vert1;
|
|
this.vert2 = vert2;
|
|
this.vert3 = vert3;
|
|
}
|
|
|
|
public static AmbientVertexRemap fromFacing(Direction direction) {
|
|
return BY_FACING[direction.get3DDataValue()];
|
|
}
|
|
|
|
static {
|
|
BY_FACING = Util.make(new AmbientVertexRemap[6], map -> {
|
|
map[Direction.DOWN.get3DDataValue()] = DOWN;
|
|
map[Direction.UP.get3DDataValue()] = UP;
|
|
map[Direction.NORTH.get3DDataValue()] = NORTH;
|
|
map[Direction.SOUTH.get3DDataValue()] = SOUTH;
|
|
map[Direction.WEST.get3DDataValue()] = WEST;
|
|
map[Direction.EAST.get3DDataValue()] = EAST;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|