/* * 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 = ThreadLocal.withInitial(Cache::new); public ModelBlockRenderer(BlockColors blockColors) { this.blockColors = blockColors; } public void tesselateBlock(BlockAndTintGetter level, List 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 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 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 unculledQuads = part.getQuads(null); if (unculledQuads.isEmpty()) continue; this.renderModelFaceAO(level, state, pos, poseStack, builder, unculledQuads, scratch, overlayCoords); } } public void tesselateWithoutAO(BlockAndTintGetter level, List 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 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 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 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 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 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; }); } } }