mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-26 17:46:25 -05:00
Merge remote-tracking branch 'origin/1.19' into 1.19.1
This commit is contained in:
commit
437c2eec0b
16 changed files with 341 additions and 99 deletions
|
@ -18,7 +18,7 @@ package net.fabricmc.fabric.mixin.item;
|
|||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
@ -83,8 +83,8 @@ public abstract class ItemStackMixin {
|
|||
)
|
||||
public Multimap<EntityAttribute, EntityAttributeModifier> hookGetAttributeModifiers(Item item, EquipmentSlot slot) {
|
||||
ItemStack stack = (ItemStack) (Object) this;
|
||||
//we need to ensure it is modifiable for the callback
|
||||
Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = HashMultimap.create(item.getAttributeModifiers(stack, slot));
|
||||
//we need to ensure it is modifiable for the callback, use linked map to preserve ordering
|
||||
Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = LinkedHashMultimap.create(item.getAttributeModifiers(stack, slot));
|
||||
ModifyItemAttributeModifiersCallback.EVENT.invoker().modifyAttributeModifiers(stack, slot, attributeModifiers);
|
||||
return attributeModifiers;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.networking.client;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.font.MultilineText;
|
||||
import net.minecraft.client.gui.screen.DisconnectedScreen;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
/**
|
||||
* This mixin makes disconnect reason text scrollable.
|
||||
*/
|
||||
@Mixin(DisconnectedScreen.class)
|
||||
public abstract class DisconnectedScreenMixin extends Screen {
|
||||
@Shadow
|
||||
private int reasonHeight;
|
||||
|
||||
@Unique
|
||||
private int actualReasonHeight;
|
||||
|
||||
@Unique
|
||||
private int scroll;
|
||||
|
||||
@Unique
|
||||
private int maxScroll;
|
||||
|
||||
private DisconnectedScreenMixin() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
// Inject to right after reasonHeight is stored, to make sure the back button have correct position.
|
||||
@Inject(method = "init", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/DisconnectedScreen;reasonHeight:I", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER))
|
||||
private void init(CallbackInfo ci) {
|
||||
actualReasonHeight = reasonHeight;
|
||||
reasonHeight = Math.min(reasonHeight, height - 100);
|
||||
scroll = 0;
|
||||
maxScroll = actualReasonHeight - reasonHeight;
|
||||
}
|
||||
|
||||
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/MultilineText;drawCenterWithShadow(Lnet/minecraft/client/util/math/MatrixStack;II)I"))
|
||||
private int render(MultilineText instance, MatrixStack matrixStack, int x, int y) {
|
||||
double scale = client.getWindow().getScaleFactor();
|
||||
RenderSystem.enableScissor(0, (int) (y * scale), (int) (width * scale), (int) (reasonHeight * scale));
|
||||
instance.drawCenterWithShadow(matrixStack, x, y - scroll);
|
||||
RenderSystem.disableScissor();
|
||||
|
||||
// Draw gradient at the top/bottom to indicate that the text is scrollable.
|
||||
if (actualReasonHeight > reasonHeight) {
|
||||
int startX = (width - instance.getMaxWidth()) / 2;
|
||||
int endX = (width + instance.getMaxWidth()) / 2;
|
||||
|
||||
if (scroll > 0) {
|
||||
fillGradient(matrixStack, startX, y, endX, y + 10, 0xFF000000, 0);
|
||||
}
|
||||
|
||||
if (scroll < maxScroll) {
|
||||
fillGradient(matrixStack, startX, y + reasonHeight - 10, endX, y + reasonHeight, 0, 0xFF000000);
|
||||
}
|
||||
}
|
||||
|
||||
return y + reasonHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
|
||||
scroll = MathHelper.clamp(scroll - (MathHelper.sign(amount) * client.textRenderer.fontHeight * 10), 0, maxScroll);
|
||||
return super.mouseScrolled(mouseX, mouseY, amount);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,8 @@
|
|||
"accessor.ConnectScreenAccessor",
|
||||
"accessor.MinecraftClientAccessor",
|
||||
"client.ClientLoginNetworkHandlerMixin",
|
||||
"client.ClientPlayNetworkHandlerMixin"
|
||||
"client.ClientPlayNetworkHandlerMixin",
|
||||
"client.DisconnectedScreenMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -68,7 +68,7 @@ public final class GlobalReceiverRegistry<H> {
|
|||
try {
|
||||
final boolean replaced = this.handlers.putIfAbsent(channelName, handler) == null;
|
||||
|
||||
if (!replaced) {
|
||||
if (replaced) {
|
||||
this.handleRegistration(channelName, handler);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.networking.disconnectscreen;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
|
||||
public class DisconnectScreenTest implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) ->
|
||||
dispatcher.register(ClientCommandManager.literal("disconnect_screen_test").executes(context -> {
|
||||
StringBuilder builder = new StringBuilder("A very long disconnect reason:");
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
builder.append("\nLine ").append(i + 1);
|
||||
}
|
||||
|
||||
context.getSource().getPlayer().networkHandler.getConnection().disconnect(Text.of(builder.toString()));
|
||||
return 1;
|
||||
})));
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.networking.channeltest.NetworkingChannelClientTest",
|
||||
"net.fabricmc.fabric.test.networking.disconnectscreen.DisconnectScreenTest",
|
||||
"net.fabricmc.fabric.test.networking.keybindreciever.NetworkingKeybindClientPacketTest",
|
||||
"net.fabricmc.fabric.test.networking.login.NetworkingLoginQueryClientTest",
|
||||
"net.fabricmc.fabric.test.networking.play.NetworkingPlayPacketClientTest"
|
||||
|
|
|
@ -23,8 +23,8 @@ import java.io.IOException;
|
|||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
|
@ -89,7 +89,7 @@ public class Indigo implements ClientModInitializer {
|
|||
}
|
||||
|
||||
static {
|
||||
File configDir = new File(FabricLoader.getInstance().getConfigDirectory(), "fabric");
|
||||
File configDir = FabricLoader.getInstance().getConfigDir().resolve("fabric").toFile();
|
||||
|
||||
if (!configDir.exists()) {
|
||||
if (!configDir.mkdir()) {
|
||||
|
|
|
@ -28,12 +28,11 @@ import static net.minecraft.util.math.Direction.UP;
|
|||
import static net.minecraft.util.math.Direction.WEST;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.block.BlockModelRenderer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
@ -55,10 +54,15 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
|||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class AoCalculator {
|
||||
@FunctionalInterface
|
||||
public interface BrightnessFunc {
|
||||
int apply(BlockPos pos, BlockState state);
|
||||
}
|
||||
|
||||
/** Used to receive a method reference in constructor for ao value lookup. */
|
||||
@FunctionalInterface
|
||||
public interface AoFunc {
|
||||
float apply(BlockPos pos);
|
||||
float apply(BlockPos pos, BlockState state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,10 +85,10 @@ public class AoCalculator {
|
|||
private final BlockPos.Mutable lightPos = new BlockPos.Mutable();
|
||||
private final BlockPos.Mutable searchPos = new BlockPos.Mutable();
|
||||
private final BlockRenderInfo blockInfo;
|
||||
private final ToIntFunction<BlockPos> brightnessFunc;
|
||||
private final BrightnessFunc brightnessFunc;
|
||||
private final AoFunc aoFunc;
|
||||
|
||||
/** caches results of {@link #computeFace(Direction, boolean)} for the current block. */
|
||||
/** caches results of {@link #computeFace(Direction, boolean, boolean)} for the current block. */
|
||||
private final AoFaceData[] faceData = new AoFaceData[12];
|
||||
|
||||
/** indicates which elements of {@link #faceData} have been computed for the current block. */
|
||||
|
@ -97,7 +101,7 @@ public class AoCalculator {
|
|||
public final float[] ao = new float[4];
|
||||
public final int[] light = new int[4];
|
||||
|
||||
public AoCalculator(BlockRenderInfo blockInfo, ToIntFunction<BlockPos> brightnessFunc, AoFunc aoFunc) {
|
||||
public AoCalculator(BlockRenderInfo blockInfo, BrightnessFunc brightnessFunc, AoFunc aoFunc) {
|
||||
this.blockInfo = blockInfo;
|
||||
this.brightnessFunc = brightnessFunc;
|
||||
this.aoFunc = aoFunc;
|
||||
|
@ -193,7 +197,7 @@ public class AoCalculator {
|
|||
int flags = quad.geometryFlags();
|
||||
|
||||
// force to block face if shape is full cube - matches vanilla logic
|
||||
if ((flags & LIGHT_FACE_FLAG) == 0 && (flags & AXIS_ALIGNED_FLAG) == AXIS_ALIGNED_FLAG && Block.isShapeFullCube(blockInfo.blockState.getCollisionShape(blockInfo.blockView, blockInfo.blockPos))) {
|
||||
if ((flags & LIGHT_FACE_FLAG) == 0 && (flags & AXIS_ALIGNED_FLAG) != 0 && blockInfo.blockState.isFullCube(blockInfo.blockView, blockInfo.blockPos)) {
|
||||
flags |= LIGHT_FACE_FLAG;
|
||||
}
|
||||
|
||||
|
@ -355,7 +359,7 @@ public class AoCalculator {
|
|||
|
||||
/**
|
||||
* Computes smoothed brightness and Ao shading for four corners of a block face.
|
||||
* Outer block face is what you normally see and what you get get when second
|
||||
* Outer block face is what you normally see and what you get when the second
|
||||
* parameter is true. Inner is light *within* the block and usually darker.
|
||||
* It is blended with the outer face for inset surfaces, but is also used directly
|
||||
* in vanilla logic for some blocks that aren't full opaque cubes.
|
||||
|
@ -371,45 +375,76 @@ public class AoCalculator {
|
|||
|
||||
final BlockRenderView world = blockInfo.blockView;
|
||||
final BlockPos pos = blockInfo.blockPos;
|
||||
final BlockState blockState = blockInfo.blockState;
|
||||
final BlockPos.Mutable lightPos = this.lightPos;
|
||||
final BlockPos.Mutable searchPos = this.searchPos;
|
||||
BlockState searchState;
|
||||
|
||||
if (isOnBlockFace) {
|
||||
lightPos.set(pos, lightFace);
|
||||
} else {
|
||||
lightPos.set(pos);
|
||||
}
|
||||
|
||||
lightPos.set(isOnBlockFace ? pos.offset(lightFace) : pos);
|
||||
AoFace aoFace = AoFace.get(lightFace);
|
||||
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]);
|
||||
final int light0 = brightnessFunc.applyAsInt(searchPos);
|
||||
final float ao0 = aoFunc.apply(searchPos);
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]);
|
||||
final int light1 = brightnessFunc.applyAsInt(searchPos);
|
||||
final float ao1 = aoFunc.apply(searchPos);
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[2]);
|
||||
final int light2 = brightnessFunc.applyAsInt(searchPos);
|
||||
final float ao2 = aoFunc.apply(searchPos);
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[3]);
|
||||
final int light3 = brightnessFunc.applyAsInt(searchPos);
|
||||
final float ao3 = aoFunc.apply(searchPos);
|
||||
// Vanilla was further offsetting the positions for opaque block checks in the
|
||||
// direction of the light face, but it was actually mis-sampling and causing
|
||||
// visible artifacts in certain situations
|
||||
|
||||
// vanilla was further offsetting these in the direction of the light face
|
||||
// but it was actually mis-sampling and causing visible artifacts in certain situation
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]); //.setOffset(lightFace);
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace);
|
||||
final boolean isClear0 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0;
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]); //.setOffset(lightFace);
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace);
|
||||
final boolean isClear1 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0;
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[2]); //.setOffset(lightFace);
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace);
|
||||
final boolean isClear2 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0;
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[3]); //.setOffset(lightFace);
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.move(lightFace);
|
||||
final boolean isClear3 = world.getBlockState(searchPos).getOpacity(world, searchPos) == 0;
|
||||
searchPos.set(lightPos, aoFace.neighbors[0]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light0 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao0 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear0 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[1]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light1 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao1 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear1 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[2]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light2 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao2 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear2 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
searchPos.set(lightPos, aoFace.neighbors[3]);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
final int light3 = brightnessFunc.apply(searchPos, searchState);
|
||||
final float ao3 = aoFunc.apply(searchPos, searchState);
|
||||
|
||||
if (!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
searchPos.move(lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
}
|
||||
|
||||
final boolean isClear3 = !searchState.shouldBlockVision(world, searchPos) || searchState.getOpacity(world, searchPos) == 0;
|
||||
|
||||
// c = corner - values at corners of face
|
||||
int cLight0, cLight1, cLight2, cLight3;
|
||||
float cAo0, cAo1, cAo2, cAo3;
|
||||
|
||||
// If neighbors on both side of the corner are opaque, then apparently we use the light/shade
|
||||
// If neighbors on both sides of the corner are opaque, then apparently we use the light/shade
|
||||
// from one of the sides adjacent to the corner. If either neighbor is clear (no light subtraction)
|
||||
// then we use values from the outwardly diagonal corner. (outwardly = position is one more away from light face)
|
||||
if (!isClear2 && !isClear0) {
|
||||
|
@ -417,8 +452,9 @@ public class AoCalculator {
|
|||
cLight0 = light0;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[2]);
|
||||
cAo0 = aoFunc.apply(searchPos);
|
||||
cLight0 = brightnessFunc.applyAsInt(searchPos);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo0 = aoFunc.apply(searchPos, searchState);
|
||||
cLight0 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear3 && !isClear0) {
|
||||
|
@ -426,8 +462,9 @@ public class AoCalculator {
|
|||
cLight1 = light0;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[0]).move(aoFace.neighbors[3]);
|
||||
cAo1 = aoFunc.apply(searchPos);
|
||||
cLight1 = brightnessFunc.applyAsInt(searchPos);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo1 = aoFunc.apply(searchPos, searchState);
|
||||
cLight1 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear2 && !isClear1) {
|
||||
|
@ -435,8 +472,9 @@ public class AoCalculator {
|
|||
cLight2 = light1;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[2]);
|
||||
cAo2 = aoFunc.apply(searchPos);
|
||||
cLight2 = brightnessFunc.applyAsInt(searchPos);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo2 = aoFunc.apply(searchPos, searchState);
|
||||
cLight2 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
if (!isClear3 && !isClear1) {
|
||||
|
@ -444,22 +482,24 @@ public class AoCalculator {
|
|||
cLight3 = light1;
|
||||
} else {
|
||||
searchPos.set(lightPos).move(aoFace.neighbors[1]).move(aoFace.neighbors[3]);
|
||||
cAo3 = aoFunc.apply(searchPos);
|
||||
cLight3 = brightnessFunc.applyAsInt(searchPos);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
cAo3 = aoFunc.apply(searchPos, searchState);
|
||||
cLight3 = brightnessFunc.apply(searchPos, searchState);
|
||||
}
|
||||
|
||||
// If on block face or neighbor isn't occluding, "center" will be neighbor brightness
|
||||
// Doesn't use light pos because logic not based solely on this block's geometry
|
||||
int lightCenter;
|
||||
searchPos.set(pos).move(lightFace);
|
||||
searchPos.set(pos, lightFace);
|
||||
searchState = world.getBlockState(searchPos);
|
||||
|
||||
if (isOnBlockFace || !world.getBlockState(searchPos).isOpaqueFullCube(world, searchPos)) {
|
||||
lightCenter = brightnessFunc.applyAsInt(searchPos);
|
||||
if (isOnBlockFace || !searchState.isOpaqueFullCube(world, searchPos)) {
|
||||
lightCenter = brightnessFunc.apply(searchPos, searchState);
|
||||
} else {
|
||||
lightCenter = brightnessFunc.applyAsInt(pos);
|
||||
lightCenter = brightnessFunc.apply(pos, blockState);
|
||||
}
|
||||
|
||||
float aoCenter = aoFunc.apply(isOnBlockFace ? lightPos : pos);
|
||||
float aoCenter = aoFunc.apply(lightPos, world.getBlockState(lightPos));
|
||||
float worldBrightness = world.getBrightness(lightFace, shade);
|
||||
|
||||
result.a0 = ((ao3 + ao0 + cAo1 + aoCenter) * 0.25F) * worldBrightness;
|
||||
|
@ -495,12 +535,12 @@ public class AoCalculator {
|
|||
if (b == 0) b = d;
|
||||
if (c == 0) c = d;
|
||||
// bitwise divide by 4, clamp to expected (positive) range
|
||||
return a + b + c + d >> 2 & 16711935;
|
||||
return a + b + c + d >> 2 & 0xFF00FF;
|
||||
}
|
||||
|
||||
private static int meanInnerBrightness(int a, int b, int c, int d) {
|
||||
// bitwise divide by 4, clamp to expected (positive) range
|
||||
return a + b + c + d >> 2 & 16711935;
|
||||
return a + b + c + d >> 2 & 0xFF00FF;
|
||||
}
|
||||
|
||||
private static int nonZeroMin(int a, int b) {
|
||||
|
|
|
@ -28,16 +28,15 @@ import net.fabricmc.fabric.impl.client.indigo.Indigo;
|
|||
*/
|
||||
@FunctionalInterface
|
||||
public interface AoLuminanceFix {
|
||||
float apply(BlockView view, BlockPos pos);
|
||||
float apply(BlockView view, BlockPos pos, BlockState state);
|
||||
|
||||
AoLuminanceFix INSTANCE = Indigo.FIX_LUMINOUS_AO_SHADE ? AoLuminanceFix::fixed : AoLuminanceFix::vanilla;
|
||||
|
||||
static float vanilla(BlockView view, BlockPos pos) {
|
||||
return view.getBlockState(pos).getAmbientOcclusionLightLevel(view, pos);
|
||||
static float vanilla(BlockView view, BlockPos pos, BlockState state) {
|
||||
return state.getAmbientOcclusionLightLevel(view, pos);
|
||||
}
|
||||
|
||||
static float fixed(BlockView view, BlockPos pos) {
|
||||
final BlockState state = view.getBlockState(pos);
|
||||
static float fixed(BlockView view, BlockPos pos, BlockState state) {
|
||||
return state.getLuminance() == 0 ? state.getAmbientOcclusionLightLevel(view, pos) : 1f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
|
||||
|
@ -93,25 +92,22 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
|
|||
return;
|
||||
}
|
||||
|
||||
final RenderMaterialImpl.Value mat = quad.material();
|
||||
|
||||
if (!mat.disableAo(0) && MinecraftClient.isAmbientOcclusionEnabled()) {
|
||||
// needs to happen before offsets are applied
|
||||
aoCalc.compute(quad, false);
|
||||
}
|
||||
|
||||
tessellateQuad(quad, mat, 0);
|
||||
tessellateQuad(quad, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines color index and render layer, then routes to appropriate
|
||||
* tessellate routine based on material properties.
|
||||
*/
|
||||
private void tessellateQuad(MutableQuadViewImpl quad, RenderMaterialImpl.Value mat, int textureIndex) {
|
||||
private void tessellateQuad(MutableQuadViewImpl quad, int textureIndex) {
|
||||
final RenderMaterialImpl.Value mat = quad.material();
|
||||
final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
|
||||
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
|
||||
|
||||
if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
|
||||
// needs to happen before offsets are applied
|
||||
aoCalc.compute(quad, false);
|
||||
|
||||
if (mat.emissive(textureIndex)) {
|
||||
tessellateSmoothEmissive(quad, renderLayer, colorIndex);
|
||||
} else {
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
||||
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
|
@ -177,8 +177,12 @@ public abstract class AbstractQuadRenderer {
|
|||
// for reference.
|
||||
if (quad.cullFace() != null) {
|
||||
mpos.move(quad.cullFace());
|
||||
} else if ((quad.geometryFlags() & LIGHT_FACE_FLAG) != 0 || Block.isShapeFullCube(blockState.getCollisionShape(blockInfo.blockView, pos))) {
|
||||
mpos.move(quad.lightFace());
|
||||
} else {
|
||||
final int flags = quad.geometryFlags();
|
||||
|
||||
if ((flags & LIGHT_FACE_FLAG) != 0 || ((flags & AXIS_ALIGNED_FLAG) != 0 && blockState.isFullCube(blockInfo.blockView, pos))) {
|
||||
mpos.move(quad.lightFace());
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329
|
||||
|
|
|
@ -94,17 +94,17 @@ public class BlockRenderContext extends AbstractRenderContext {
|
|||
}
|
||||
};
|
||||
|
||||
private int brightness(BlockPos pos) {
|
||||
private int brightness(BlockPos pos, BlockState state) {
|
||||
if (blockInfo.blockView == null) {
|
||||
return LightmapTextureManager.MAX_LIGHT_COORDINATE;
|
||||
}
|
||||
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockView.getBlockState(pos), pos);
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
|
||||
}
|
||||
|
||||
private float aoLevel(BlockPos pos) {
|
||||
private float aoLevel(BlockPos pos, BlockState state) {
|
||||
final BlockRenderView blockView = blockInfo.blockView;
|
||||
return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos);
|
||||
return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos, state);
|
||||
}
|
||||
|
||||
private VertexConsumer outputBuffer(RenderLayer renderLayer) {
|
||||
|
|
|
@ -119,27 +119,27 @@ public class ChunkRenderInfo {
|
|||
}
|
||||
|
||||
/**
|
||||
* Cached values for {@link BlockState#getBlockBrightness(BlockRenderView, BlockPos)}.
|
||||
* Cached values for {@link WorldRenderer#getLightmapCoordinates(BlockRenderView, BlockState, BlockPos)}.
|
||||
* See also the comments for {@link #brightnessCache}.
|
||||
*/
|
||||
int cachedBrightness(BlockPos pos) {
|
||||
int cachedBrightness(BlockPos pos, BlockState state) {
|
||||
long key = pos.asLong();
|
||||
int result = brightnessCache.get(key);
|
||||
|
||||
if (result == Integer.MAX_VALUE) {
|
||||
result = WorldRenderer.getLightmapCoordinates(blockView, blockView.getBlockState(pos), pos);
|
||||
result = WorldRenderer.getLightmapCoordinates(blockView, state, pos);
|
||||
brightnessCache.put(key, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float cachedAoLevel(BlockPos pos) {
|
||||
float cachedAoLevel(BlockPos pos, BlockState state) {
|
||||
long key = pos.asLong();
|
||||
float result = aoLevelCache.get(key);
|
||||
|
||||
if (result == Float.MAX_VALUE) {
|
||||
result = AoLuminanceFix.INSTANCE.apply(blockView, pos);
|
||||
result = AoLuminanceFix.INSTANCE.apply(blockView, pos, state);
|
||||
aoLevelCache.put(key, result);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,14 @@
|
|||
package net.fabricmc.fabric.mixin.resource.loader.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
@ -31,25 +35,29 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.world.CreateWorldScreen;
|
||||
import net.minecraft.client.gui.screen.world.MoreOptionsDialog;
|
||||
import net.minecraft.client.world.GeneratorOptionsHolder;
|
||||
import net.minecraft.resource.DataPackSettings;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.server.SaveLoading;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.dynamic.RegistryOps;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.world.gen.GeneratorOptions;
|
||||
import net.minecraft.world.gen.WorldPresets;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
|
||||
import net.fabricmc.fabric.mixin.resource.loader.ResourcePackManagerAccessor;
|
||||
|
||||
@Mixin(CreateWorldScreen.class)
|
||||
public abstract class CreateWorldScreenMixin {
|
||||
public abstract class CreateWorldScreenMixin extends Screen {
|
||||
@Unique
|
||||
private static DataPackSettings defaultDataPackSettings;
|
||||
|
||||
|
@ -60,8 +68,25 @@ public abstract class CreateWorldScreenMixin {
|
|||
@Final
|
||||
private static Logger LOGGER;
|
||||
|
||||
@Unique
|
||||
private static RegistryOps<JsonElement> loadedOps;
|
||||
@Shadow
|
||||
@Final
|
||||
public MoreOptionsDialog moreOptionsDialog;
|
||||
|
||||
@Shadow
|
||||
protected DataPackSettings dataPackSettings;
|
||||
|
||||
@Shadow
|
||||
private static SaveLoading.ServerConfig createServerConfig(ResourcePackManager resourcePackManager, DataPackSettings dataPackSettings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Shadow
|
||||
@Nullable
|
||||
protected abstract Pair<File, ResourcePackManager> getScannedPack();
|
||||
|
||||
private CreateWorldScreenMixin() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "create(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/gui/screen/Screen;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/world/CreateWorldScreen;createServerConfig(Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/resource/DataPackSettings;)Lnet/minecraft/server/SaveLoading$ServerConfig;"))
|
||||
|
@ -84,22 +109,34 @@ public abstract class CreateWorldScreenMixin {
|
|||
@Redirect(method = "method_41854", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DynamicRegistryManager$Mutable;toImmutable()Lnet/minecraft/util/registry/DynamicRegistryManager$Immutable;"))
|
||||
private static DynamicRegistryManager.Immutable loadDynamicRegistry(DynamicRegistryManager.Mutable mutableRegistryManager, ResourceManager dataPackManager) {
|
||||
// This loads the dynamic registry from the data pack
|
||||
loadedOps = RegistryOps.ofLoaded(JsonOps.INSTANCE, mutableRegistryManager, dataPackManager);
|
||||
RegistryOps.ofLoaded(JsonOps.INSTANCE, mutableRegistryManager, dataPackManager);
|
||||
return mutableRegistryManager.toImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix GeneratorOptions not having custom dimensions.
|
||||
* Load the DynamicRegistryManager again to fix GeneratorOptions not having custom dimensions.
|
||||
* Taken from {@link CreateWorldScreen#applyDataPacks(ResourcePackManager)}.
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference")
|
||||
@Redirect(method = "method_41854", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/gen/WorldPresets;createDefaultOptions(Lnet/minecraft/util/registry/DynamicRegistryManager;)Lnet/minecraft/world/gen/GeneratorOptions;"))
|
||||
private static GeneratorOptions loadDatapackDimensions(DynamicRegistryManager dynamicRegistryManager) {
|
||||
GeneratorOptions defaultGen = WorldPresets.createDefaultOptions(dynamicRegistryManager);
|
||||
RegistryOps<JsonElement> registryOps = RegistryOps.of(JsonOps.INSTANCE, dynamicRegistryManager);
|
||||
return GeneratorOptions.CODEC.encodeStart(registryOps, defaultGen)
|
||||
.flatMap(json -> GeneratorOptions.CODEC.parse(loadedOps, json))
|
||||
.getOrThrow(false, Util.addPrefix("Error parsing worldgen settings after loading data packs: ", LOGGER::error));
|
||||
@Inject(method = "startServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/world/CreateWorldScreen;clearDataPackTempDir()V"))
|
||||
private void loadDatapackDimensions(CallbackInfo ci) {
|
||||
CompletableFuture<Void> future = SaveLoading.load(createServerConfig(getScannedPack().getSecond(), dataPackSettings), (resourceManager, dataPackSettings1) -> {
|
||||
GeneratorOptionsHolder holder = moreOptionsDialog.getGeneratorOptionsHolder();
|
||||
DynamicRegistryManager.Mutable newDrm = DynamicRegistryManager.createAndLoad();
|
||||
DynamicOps<JsonElement> heldOps = RegistryOps.of(JsonOps.INSTANCE, holder.dynamicRegistryManager());
|
||||
DynamicOps<JsonElement> newOps = RegistryOps.ofLoaded(JsonOps.INSTANCE, newDrm, resourceManager);
|
||||
DataResult<GeneratorOptions> result = GeneratorOptions.CODEC.encodeStart(heldOps, holder.generatorOptions())
|
||||
.flatMap(json -> GeneratorOptions.CODEC.parse(newOps, json));
|
||||
return Pair.of(result, newDrm.toImmutable());
|
||||
}, (resourceManager, dataPackContents, drm, result) -> {
|
||||
resourceManager.close();
|
||||
GeneratorOptions options = result.getOrThrow(false, Util.addPrefix("Error parsing worldgen settings after loading data packs: ", LOGGER::error));
|
||||
GeneratorOptionsHolder holder = new GeneratorOptionsHolder(options, result.lifecycle().add(drm.getRegistryLifecycle()), drm, dataPackContents);
|
||||
((MoreOptionsDialogAccessor) moreOptionsDialog).callSetGeneratorOptionsHolder(holder);
|
||||
return null;
|
||||
}, Util.getMainWorkerExecutor(), client);
|
||||
|
||||
client.runTasks(future::isDone);
|
||||
}
|
||||
|
||||
@Inject(method = "getScannedPack",
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader.client;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import net.minecraft.client.gui.screen.world.MoreOptionsDialog;
|
||||
import net.minecraft.client.world.GeneratorOptionsHolder;
|
||||
|
||||
@Mixin(MoreOptionsDialog.class)
|
||||
public interface MoreOptionsDialogAccessor {
|
||||
@Invoker
|
||||
void callSetGeneratorOptionsHolder(GeneratorOptionsHolder generatorOptionsHolder);
|
||||
}
|
|
@ -7,7 +7,8 @@
|
|||
"CreateWorldScreenMixin",
|
||||
"FontManagerResourceReloadListenerMixin",
|
||||
"GameOptionsMixin",
|
||||
"KeyedResourceReloadListenerClientMixin"
|
||||
"KeyedResourceReloadListenerClientMixin",
|
||||
"MoreOptionsDialogAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
Loading…
Reference in a new issue