Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 79 additions & 72 deletions src/main/java/com/daniking/backtools/BackToolFeatureRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,29 @@
import net.minecraft.client.render.entity.feature.PlayerHeldItemFeatureRenderer;
import net.minecraft.client.render.entity.model.PlayerEntityModel;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ModelTransformationMode;
import net.minecraft.item.ShieldItem;
import net.minecraft.item.*;
import net.minecraft.util.Arm;
import net.minecraft.util.math.RotationAxis;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

@Environment(EnvType.CLIENT)
public class BackToolFeatureRenderer <M extends PlayerEntityModel> extends PlayerHeldItemFeatureRenderer<PlayerEntityRenderState, M> {
public ItemStack mainStack = ItemStack.EMPTY;
public ItemStack offStack = ItemStack.EMPTY;
public Arm mainArm = Arm.RIGHT;

public BackToolFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, M> context) {
@Contract(pure = true)
public BackToolFeatureRenderer(final @NotNull FeatureRendererContext<PlayerEntityRenderState, M> context) {
super(context);
}

@Override
public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, PlayerEntityRenderState playerRenderState, float limbAngle, float limbDistance) {
if (!(playerRenderState.capeVisible && playerRenderState.skinTextures.capeTexture() != null && !ConfigHandler.isRenderWithCapesTrue()) &&
!playerRenderState.invisible && playerRenderState.sleepingDirection == null &&
public void render(final @NotNull MatrixStack matrixStack, final @NotNull VertexConsumerProvider vertexConsumerProvider, final int light,
final @NotNull PlayerEntityRenderState playerRenderState, final float limbAngle, final float limbDistance) {
if (!(playerRenderState.capeVisible && playerRenderState.skinTextures.capeTexture() != null && !ConfigHandler.shouldRenderWithCapes()) &&
!playerRenderState.invisible && playerRenderState.sleepingDirection == null && // todo render belt tools when sleeping and regardless of cape
ClientSetup.HELD_TOOLS.containsKey(playerRenderState.name)) {

final HeldItemContext ctx = ClientSetup.HELD_TOOLS.get(playerRenderState.name);
Expand All @@ -39,85 +40,91 @@ public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsume
return;
}
this.setRenders(ctx.previousMain, ctx.previousOff, playerRenderState.mainArm);
matrixStack.push();
this.getContextModel().body.rotate(matrixStack);
boolean isHelicopterMode = ConfigHandler.isHelicopterModeOn() && (playerRenderState.isSwimming || playerRenderState.isGliding);
this.renderItem(!playerRenderState.equippedChestStack.isEmpty() ? 1.0F : playerRenderState.jacketVisible ? 0.5F : 0F, matrixStack, vertexConsumerProvider, light, isHelicopterMode ? playerRenderState.age : 0);
matrixStack.pop();
final float age = ConfigHandler.isHelicopterModeOn() && (playerRenderState.isSwimming || playerRenderState.isGliding) ? playerRenderState.age : 0;
final float offset = !playerRenderState.equippedChestStack.isEmpty() ? 1.0F : playerRenderState.jacketVisible ? 0.5F : 0F;

renderItem(this.mainStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.RIGHT, age, light); // Mainhand stack
renderItem(this.offStack, matrixStack, vertexConsumerProvider, offset, this.mainArm == Arm.LEFT, age, light); // Offhand stack
}
}

private void renderItem(float offset, MatrixStack matrices, VertexConsumerProvider provider, int light, final float age) {
matrices.translate(0F, 4F/16F, 1.91F/16F + (offset / 16F));
matrices.translate(0F, 0F, 0.025F);
private void renderItem(final @NotNull ItemStack stack, final @NotNull MatrixStack matrices, final @NotNull VertexConsumerProvider provider, float offset, final boolean isInverted, final float age, int light) {
if (!stack.isEmpty()) {
final Item item = stack.getItem();
matrices.push();

if (!this.mainStack.isEmpty()) {
if (this.mainArm == Arm.RIGHT) {
matrices.scale(-1F, 1F, -1F);
}
boolean bl = this.mainStack.getItem() instanceof ShieldItem;
if (bl) {
float scale = 1.5F;
float orientationZ = ConfigHandler.getBeltOrientation(item);
if (orientationZ != Float.MIN_VALUE) {
// always do scaling and translations first before rotating, since the coordinate system rotates with the item
// and makes translations afterwards so much harder!
final float scale = 0.6F;
matrices.scale(scale, scale, scale);
if (this.mainArm == Arm.LEFT) {
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-25F));
matrices.translate(-2.5F/16F, 2F/16F, 1.25F/16F);
} else {
matrices.translate(-1F / 16F, 0.25F / 16F, 1.0F / 16F);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(25F));
}
}
if (!bl) {
final float i = ConfigHandler.getToolOrientation(this.mainStack.getItem());
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(i));
}
if (ConfigHandler.isBeltTool(this.mainStack.getItem())) {
float swordScale = 0.8F;
matrices.scale(swordScale, swordScale, swordScale);

if (this.mainArm == Arm.LEFT) {
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90F));
matrices.translate(0.19F, 0.6F, -0.33F);
if (isInverted) {
matrices.translate(-6 / 16F - 0.025F - offset / 16F, 1F, -0.5 / 16F);
} else {
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(270F));
matrices.translate(0.19F, 0.6F, 0.33F);
matrices.translate(6 / 16F + 0.025F + offset / 16F, 1F, -0.5 / 16F);
}
}
if (age > 0) {
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F));
}
MinecraftClient.getInstance().getItemRenderer().renderItem(this.mainStack, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, provider, null, 0);
}
if (!this.offStack.isEmpty()) {
if (this.mainArm == Arm.LEFT) {
matrices.scale(-1F, 1F, -1F);
}
boolean isShield = this.offStack.getItem() instanceof ShieldItem;
if (isShield) {
float scale = 1.5F;
matrices.scale(scale, scale, scale);
if (this.mainArm == Arm.RIGHT) {
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F));
matrices.translate(-2.5F/16F, 2F/16F, 1.25F/16F);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-25F));

matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90F));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(orientationZ));
} else {
orientationZ = ConfigHandler.getBackOrientation(item);

if (orientationZ != Float.MIN_VALUE) {
// default shield doesn't look good. So we scale it up and
if (item instanceof ShieldItem) {
float scale = 1.5F;
matrices.scale(scale, scale, scale);

if (isInverted) {
// tiny difference to avoid z-fighting if main and offhand item get rendered at the same time
matrices.translate(0F, 0, 0.001);

matrices.translate(1 / 16F, 3 / 16F, 0.025F + offset / 16F);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(orientationZ));
} else {
matrices.translate(-1 / 16F, 3 / 16F, 0.025F + offset / 16F);
matrices.multiply(RotationAxis.NEGATIVE_Z.rotationDegrees(90 + orientationZ));
}

matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90));
} else {
matrices.translate(0F, 4F / 16F, 1.91F / 16F + 0.025F + offset / 16F);

if (isInverted) {
// tiny difference to avoid z-fighting if main and offhand item get rendered at the same time
matrices.translate(0F, 0, 0.001);
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180F));
}

matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(orientationZ));

// again special case for fishing rod and alike since they look wierd with the fishing line defying gravity
if (item instanceof FishingRodItem || item instanceof OnAStickItem) {
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180F));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90F));
}

if (age > 0) {
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F));
}
}
} else {
matrices.translate(-1F / 16F, 0.25F / 16F, 1.0F / 16F);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(25F));
BackTools.LOGGER.info("Item {} was marked as enabled, but was neither a back nor a belt tool!", stack.getItem());
matrices.pop();
return; // Early return, without render, if neither back nor belt tool
}
}
if (!isShield) {
final float i = ConfigHandler.getToolOrientation(this.mainStack.getItem());
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(i));
}
if (age > 0) {
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(age * 40F));
}
MinecraftClient.getInstance().getItemRenderer().renderItem(this.offStack, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, provider, null, 0);

MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformationMode.FIXED, light, OverlayTexture.DEFAULT_UV, matrices, provider, null, 0);
matrices.pop();
}
}

private void setRenders(final ItemStack mainStack, final ItemStack offStack, final Arm side) {
private void setRenders(final @NotNull ItemStack mainStack, final @NotNull ItemStack offStack, final @NotNull Arm side) {
this.mainStack = mainStack;
this.offStack = offStack;
this.mainArm = side;
Expand Down
67 changes: 51 additions & 16 deletions src/main/java/com/daniking/backtools/BackToolsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,64 @@
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Environment(EnvType.CLIENT)
@Config(name = "BackTools")
public class BackToolsConfig implements ConfigData {
@Comment(value = "\nThese options affect only the client that loads the mod.\nIt is not possible to override the environment of the mod.")
@Comment(value = """
These options affect only the client that loads the mod.
It is not possible to override the environment of the mod.""")
public final String environment = EnvType.CLIENT.name();
@Comment(value = "What items should render on your belt.")
public List<String> beltTools = new ArrayList<>();
@Comment(value = "Enabled tools, by their resource name. Eg: minecraft:diamond_hoe. Putting any entry in here converts BackTools to a whitelist-only mod. Disabled Tools will be ignored.")
public List<String> enabledTools = new ArrayList<>();
@Comment(value = "Disabled tools, by their resource name. Eg: minecraft:diamond_hoe")
public List<String> disabledTools = new ArrayList<>();
@Comment(value = "Tool orientation, by class file and degrees. Separate with \":\" . See defaults for examples.")
public List<String> toolOrientation = Arrays.asList(
"net.minecraft.item.MiningToolItem" + ":0",
"net.minecraft.item.HoeItem" + ":0",
"net.minecraft.item.FishingRodItem" + ":0",
"net.minecraft.item.TridentItem" + ":0",
"net.minecraft.item.MaceItem" + ":-22.5",
"net.minecraft.item.RangedWeaponItem" + ":90");
@Comment(value =
"""
Back tool orientation, by item id, or tag and degrees for rotation, separated by a colon ":".
What items should render on your back

- Entries starting with a hash "#" are tags (https://minecraft.wiki/w/Tag)
- Entries without a leading hash "#" are item identifiers
- Entries starting with a minus "-" will remove them from the list.
E.g.
> "#minecraft:hoes", -iron_hoe" will do all hoes but the iron one.
> "minecraft:iron_hoe, -#hoes" will do no hoes at all.
> "-iron_hoe #minecraft:hoes" will do all hoes,
since the iron one is removed without being in the list in the list in the first place,
and then added with all the other hoes in the tag.
- Later occurrences of the same item will override all the previous ones
- Leading namespace (e.g. minecraft:) is optional.
- See defaults for examples""")
public List<String> backTools = Arrays.asList(
"#minecraft:pickaxes:0",
"#minecraft:axes:0",
"#minecraft:shovels:0",
"#minecraft:hoes:0",
"minecraft:fishing_rod:0",
"minecraft:carrot_on_a_stick:0",
"minecraft:warped_fungus_on_a_stick:0",
"minecraft:shears:0",
"#minecraft:swords:0",
"minecraft:mace:-22.5",
"minecraft:trident:0",
"minecraft:bow:90",
"minecraft:crossbow:90",
"minecraft:shield:65");
@Comment(value = """
Belt tool orientation, by item id, or tag and degrees for rotation, separated by a colon ":".
What items should render on your belt, overwriting a possible occurrence in the backTools list,
with one exception: a negation here will not remove it from the backtools.
So, if for example "iron_hoe" is added to backTools, and "-iron_hoe" is added to beltTools,
it will stay in the backTools list.
However, if both have a "iron_hoe" entry, the hoe will render on the belt.

Else wise same rules as above.""")
public List<String> beltTools = Arrays.asList(
"#minecraft:bundles:180",
"minecraft:potion:180",
"minecraft:splash_potion:180",
"minecraft:lingering_potion:180",
"minecraft:lead:180"
);
@Comment(value = "Get in swimming position and your tools go \"Weeee\"")
public boolean helicopterMode = false;
@Comment(value = "If true, tools render with capes")
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/daniking/backtools/ClientSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;

import java.util.Map;
import java.util.WeakHashMap;
Expand All @@ -18,6 +19,8 @@ public class ClientSetup implements ClientModInitializer {
public void onInitializeClient() {
AutoConfig.register(BackToolsConfig.class, JanksonConfigSerializer::new);
config = AutoConfig.getConfigHolder(BackToolsConfig.class).getConfig();
ConfigHandler.init();

// since we depend on item tags, our config can't load until the tags are loaded first. (creating / joining worlds)
CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> ConfigHandler.reload());
}
}
Loading