aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com
diff options
context:
space:
mode:
authorAnthony Hilyard <anthony.hilyard@gmail.com>2023-03-29 23:12:14 -0700
committerAnthony Hilyard <anthony.hilyard@gmail.com>2023-03-29 23:12:14 -0700
commit81ee44c7b9297e24cf7b3e8be3452d06d4d7a7ac (patch)
treece0352c615677bd5b9bebfcdfbca1a72bd6ebc6d /src/main/java/com
parentb0d1aa6d5076738965f1f0fd9df98c6cd5bf946f (diff)
downloadIceberg-81ee44c7b9297e24cf7b3e8be3452d06d4d7a7ac.tar.gz
Iceberg-81ee44c7b9297e24cf7b3e8be3452d06d4d7a7ac.tar.bz2
Iceberg-81ee44c7b9297e24cf7b3e8be3452d06d4d7a7ac.zip
Fixed several 3d item preview issues.
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java48
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSourceSodium.java67
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java315
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollector.java53
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollectorSodium.java98
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/VertexConsumerSodium.java9
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java40
7 files changed, 553 insertions, 77 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java
index b8f5fde..e0ae33a 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java
@@ -1,20 +1,58 @@
package com.anthonyhilyard.iceberg.renderer;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+import com.anthonyhilyard.iceberg.Loader;
import com.mojang.blaze3d.vertex.VertexConsumer;
+import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.impl.util.version.VersionPredicateParser;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
-public final class CheckedBufferSource implements MultiBufferSource
+public class CheckedBufferSource implements MultiBufferSource
{
- private boolean hasRendered = false;
- private final MultiBufferSource bufferSource;
+ protected boolean hasRendered = false;
+ protected final MultiBufferSource bufferSource;
+
+ private static Boolean useSodiumVersion = null;
- public CheckedBufferSource(MultiBufferSource bufferSource)
+ protected CheckedBufferSource(MultiBufferSource bufferSource)
{
this.bufferSource = bufferSource;
}
+ public static CheckedBufferSource create(MultiBufferSource bufferSource)
+ {
+ if (useSodiumVersion == null)
+ {
+ try
+ {
+ // If Sodium 0.4.9 is installed, use the Sodium implementation.
+ useSodiumVersion = FabricLoader.getInstance().isModLoaded("sodium") && VersionPredicateParser.parse("0.4.9").test(FabricLoader.getInstance().getModContainer("sodium").get().getMetadata().getVersion());
+ }
+ catch (Exception e)
+ {
+ Loader.LOGGER.error(ExceptionUtils.getStackTrace(e));
+ }
+ }
+
+ if (useSodiumVersion)
+ {
+ // Instantiate the Sodium implementation using reflection.
+ try
+ {
+ return (CheckedBufferSource) Class.forName("com.anthonyhilyard.iceberg.renderer.CheckedBufferSourceSodium").getDeclaredConstructor(MultiBufferSource.class).newInstance(bufferSource);
+ }
+ catch (Exception e)
+ {
+ Loader.LOGGER.error(ExceptionUtils.getStackTrace(e));
+ }
+ }
+
+ return new CheckedBufferSource(bufferSource);
+ }
+
@Override
public VertexConsumer getBuffer(RenderType renderType)
{
@@ -65,4 +103,4 @@ public final class CheckedBufferSource implements MultiBufferSource
{
hasRendered = false;
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSourceSodium.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSourceSodium.java
new file mode 100644
index 0000000..2a37254
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSourceSodium.java
@@ -0,0 +1,67 @@
+package com.anthonyhilyard.iceberg.renderer;
+
+import org.lwjgl.system.MemoryStack;
+
+import com.mojang.blaze3d.vertex.VertexConsumer;
+
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.RenderType;
+
+import me.jellysquid.mods.sodium.client.render.vertex.VertexBufferWriter;
+import me.jellysquid.mods.sodium.client.render.vertex.VertexFormatDescription;
+
+public final class CheckedBufferSourceSodium extends CheckedBufferSource
+{
+ protected CheckedBufferSourceSodium(MultiBufferSource bufferSource)
+ {
+ super(bufferSource);
+ }
+
+ @Override
+ public VertexConsumer getBuffer(RenderType renderType)
+ {
+ final VertexConsumer vertexConsumer = bufferSource.getBuffer(renderType);
+ VertexConsumer vertexConsumerWrap = new VertexConsumerSodium()
+ {
+ @Override
+ public VertexConsumer vertex(double x, double y, double z)
+ {
+ hasRendered = true;
+ return vertexConsumer.vertex(x, y, z);
+ }
+
+ @Override
+ public VertexConsumer color(int r, int g, int b, int a) { return vertexConsumer.color(r, g, b, a); }
+
+ @Override
+ public VertexConsumer uv(float u, float v) { return vertexConsumer.uv(u, v); }
+
+ @Override
+ public VertexConsumer overlayCoords(int x, int y) { return vertexConsumer.overlayCoords(x, y); }
+
+ @Override
+ public VertexConsumer uv2(int u, int v) { return vertexConsumer.uv2(u, v); }
+
+ @Override
+ public VertexConsumer normal(float x, float y, float z) { return vertexConsumer.normal(x, y, z); }
+
+ @Override
+ public void endVertex() { vertexConsumer.endVertex(); }
+
+ @Override
+ public void defaultColor(int r, int g, int b, int a) { vertexConsumer.defaultColor(r, g, b, a); }
+
+ @Override
+ public void unsetDefaultColor() { vertexConsumer.unsetDefaultColor(); }
+
+ @Override
+ public void push(MemoryStack memoryStack, long pointer, int count, VertexFormatDescription format)
+ {
+ hasRendered = true;
+ ((VertexBufferWriter)vertexConsumer).push(memoryStack, pointer, count, format);
+ }
+ };
+
+ return vertexConsumerWrap;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java
index c3cc4b5..44c7a61 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java
@@ -1,13 +1,19 @@
package com.anthonyhilyard.iceberg.renderer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Predicate;
+import java.util.function.Supplier;
+import com.anthonyhilyard.iceberg.Loader;
import com.google.common.collect.Maps;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@@ -39,7 +45,9 @@ import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.MultiBufferSource.BufferSource;
+import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.block.model.ItemTransforms.TransformType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
@@ -69,6 +77,7 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.MinecartItem;
+import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.HalfTransparentBlock;
@@ -77,6 +86,9 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
+import net.minecraft.world.phys.BlockHitResult;
+import net.minecraft.world.phys.HitResult;
+import net.minecraft.world.phys.Vec3;
/**
@@ -94,6 +106,7 @@ public class CustomItemRenderer extends ItemRenderer
private static Boat boat = null;
private static Horse horse = null;
private static Pair<Item, CompoundTag> cachedArmorStandItem = null;
+ private static Item cachedBoatItem = null;
private static Pair<Item, CompoundTag> cachedHorseArmorItem = null;
private static Map<Item, ModelBounds> modelBoundsCache = Maps.newHashMap();
@@ -172,10 +185,10 @@ public class CustomItemRenderer extends ItemRenderer
entityRenderDispatcher.setRenderShadow(false);
RenderSystem.runAsFancy(() -> entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 0.0f, 1.0f, poseStack, bufferSource, packedLight));
-
- if (bufferSource instanceof BufferSource)
+
+ if (bufferSource instanceof BufferSource source)
{
- ((BufferSource)bufferSource).endBatch();
+ source.endBatch();
}
entityRenderDispatcher.setRenderShadow(true);
@@ -201,9 +214,9 @@ public class CustomItemRenderer extends ItemRenderer
if (!bakedModel.isCustomRenderer() && !itemStack.is(Items.TRIDENT))
{
boolean fabulous;
- if (transformType != ItemTransforms.TransformType.GUI && !transformType.firstPerson() && itemStack.getItem() instanceof BlockItem)
+ if (transformType != ItemTransforms.TransformType.GUI && !transformType.firstPerson() && itemStack.getItem() instanceof BlockItem blockItem)
{
- Block block = ((BlockItem)itemStack.getItem()).getBlock();
+ Block block = blockItem.getBlock();
fabulous = !(block instanceof HalfTransparentBlock) && !(block instanceof StainedGlassPaneBlock);
}
else
@@ -213,10 +226,12 @@ public class CustomItemRenderer extends ItemRenderer
if (bufferSourceReady.test(bufferSource) && itemStack.getItem() instanceof BlockItem blockItem)
{
+ Block block = blockItem.getBlock();
BakedModel blockModel = null;
+ BlockModelShaper blockModelShaper = minecraft.getBlockRenderer().getBlockModelShaper();
boolean isBlockEntity = false;
- blockModel = minecraft.getBlockRenderer().getBlockModelShaper().getBlockModel(blockItem.getBlock().defaultBlockState());
+ blockModel = blockModelShaper.getBlockModel(block.defaultBlockState());
if (blockModel != modelManager.getMissingModel())
{
// First try rendering via the BlockEntityWithoutLevelRenderer.
@@ -227,19 +242,19 @@ public class CustomItemRenderer extends ItemRenderer
blockModel = null;
}
- if (blockItem.getBlock().defaultBlockState().hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF))
+ if (block.defaultBlockState().hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF))
{
// This is a double block, so we'll need to render both halves.
// First render the bottom half.
- BlockState bottomState = blockItem.getBlock().defaultBlockState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER);
- BakedModel bottomModel = minecraft.getBlockRenderer().getBlockModelShaper().getBlockModel(bottomState);
+ BlockState bottomState = block.defaultBlockState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER);
+ BakedModel bottomModel = blockModelShaper.getBlockModel(bottomState);
renderBakedModel(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay, bottomModel, fabulous);
// Then render the top half.
poseStack.pushPose();
poseStack.translate(0.0f, 1.0f, 0.0f);
- BlockState topState = blockItem.getBlock().defaultBlockState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER);
- BakedModel topModel = minecraft.getBlockRenderer().getBlockModelShaper().getBlockModel(topState);
+ BlockState topState = block.defaultBlockState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER);
+ BakedModel topModel = blockModelShaper.getBlockModel(topState);
renderBakedModel(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay, topModel, fabulous);
poseStack.popPose();
}
@@ -273,9 +288,9 @@ public class CustomItemRenderer extends ItemRenderer
}
}
- if (bufferSourceReady.test(bufferSource) && itemStack.getItem() instanceof BoatItem boatItem)
+ if (bufferSourceReady.test(bufferSource) && itemCreatesBoat(itemStack.getItem()))
{
- if (updateBoat(boatItem))
+ if (updateBoat(itemStack.getItem()))
{
renderEntityModel(boat, poseStack, bufferSource, packedLight);
}
@@ -318,7 +333,7 @@ public class CustomItemRenderer extends ItemRenderer
{
isBlockItem = true;
}
- else if (itemStack.getItem() instanceof MinecartItem || itemStack.getItem() instanceof BoatItem)
+ else if (itemStack.getItem() instanceof MinecartItem || itemCreatesBoat(itemStack.getItem()))
{
spawnsEntity = true;
}
@@ -330,15 +345,18 @@ public class CustomItemRenderer extends ItemRenderer
poseStack.pushPose();
+ poseStack.translate(0.5f, 0.5f, 0.5f);
if (isBlockItem || spawnsEntity)
{
// Apply the standard block rotation so block entities match other blocks.
poseStack.mulPose(new Quaternionf().rotationXYZ((float)Math.toRadians(30.0f), (float)Math.toRadians(225.0f), 0.0f));
+
}
else
{
bakedModel.getTransforms().getTransform(previewTransform).apply(leftHanded, poseStack);
}
+ poseStack.translate(-0.5f, -0.5f, -0.5f);
// Get the model bounds.
ModelBounds modelBounds = getModelBounds(itemStack, previewTransform, leftHanded, poseStack, rotation, bufferSource, packedLight, packedOverlay, bakedModel);
@@ -351,8 +369,7 @@ public class CustomItemRenderer extends ItemRenderer
poseStack.mulPose(rotation);
// Scale the model to fit.
- float scale = 1.0f / Math.max(modelBounds.height, modelBounds.radius * 2.0f);
- scale *= isBlockItem ? 0.8f : 1.0f;
+ float scale = 0.8f / Math.max(modelBounds.height, modelBounds.radius * 2.0f);
// Adjust the scale based on the armor type.
if (isArmor)
@@ -363,17 +380,22 @@ public class CustomItemRenderer extends ItemRenderer
scale *= 0.75f;
break;
case LEGS:
- scale *= 1.1f;
+ scale *= 1.3f;
+ break;
+ case FEET:
+ scale *= 0.85f;
break;
default:
break;
}
}
+
poseStack.scale(scale, scale, scale);
// Translate the model to the center of the item.
poseStack.translate(-modelBounds.center.x(), -modelBounds.center.y(), -modelBounds.center.z());
+ poseStack.translate(0.5f, 0.5f, 0.5f);
// Reapply the camera transforms.
if (isBlockItem || spawnsEntity)
{
@@ -384,8 +406,10 @@ public class CustomItemRenderer extends ItemRenderer
{
bakedModel.getTransforms().getTransform(previewTransform).apply(leftHanded, poseStack);
}
+ poseStack.translate(-0.5f, -0.5f, -0.5f);
- CheckedBufferSource checkedBufferSource = new CheckedBufferSource(bufferSource);
+
+ CheckedBufferSource checkedBufferSource = CheckedBufferSource.create(bufferSource);
renderModelInternal(itemStack, previewTransform, leftHanded, poseStack, rotation, checkedBufferSource, packedLight, packedOverlay, bakedModel, b -> !b.hasRendered());
poseStack.popPose();
@@ -415,47 +439,42 @@ public class CustomItemRenderer extends ItemRenderer
private void renderBakedModel(ItemStack itemStack, ItemTransforms.TransformType transformType, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight, int packedOverlay, BakedModel bakedModel, boolean fabulous)
{
- for (var model : List.of(bakedModel))
+ RenderType renderType = ItemBlockRenderTypes.getRenderType(itemStack, fabulous);
+ VertexConsumer vertexConsumer;
+ if (itemStack.is(ItemTags.COMPASSES) && itemStack.hasFoil())
{
- for (var rendertype : List.of(ItemBlockRenderTypes.getRenderType(itemStack, fabulous)))
+ poseStack.pushPose();
+ PoseStack.Pose posestack$pose = poseStack.last();
+ if (transformType == ItemTransforms.TransformType.GUI)
{
- VertexConsumer vertexconsumer;
- if (itemStack.is(ItemTags.COMPASSES) && itemStack.hasFoil())
- {
- poseStack.pushPose();
- PoseStack.Pose posestack$pose = poseStack.last();
- if (transformType == ItemTransforms.TransformType.GUI)
- {
- MatrixUtil.mulComponentWise(posestack$pose.pose(), 0.5F);
- }
- else if (transformType.firstPerson())
- {
- MatrixUtil.mulComponentWise(posestack$pose.pose(), 0.75F);
- }
-
- if (fabulous)
- {
- vertexconsumer = getCompassFoilBufferDirect(bufferSource, rendertype, posestack$pose);
- }
- else
- {
- vertexconsumer = getCompassFoilBuffer(bufferSource, rendertype, posestack$pose);
- }
-
- poseStack.popPose();
- }
- else if (fabulous)
- {
- vertexconsumer = getFoilBufferDirect(bufferSource, rendertype, true, itemStack.hasFoil());
- }
- else
- {
- vertexconsumer = getFoilBuffer(bufferSource, rendertype, true, itemStack.hasFoil());
- }
+ MatrixUtil.mulComponentWise(posestack$pose.pose(), 0.5F);
+ }
+ else if (transformType.firstPerson())
+ {
+ MatrixUtil.mulComponentWise(posestack$pose.pose(), 0.75F);
+ }
- renderModelLists(model, itemStack, packedLight, packedOverlay, poseStack, vertexconsumer);
+ if (fabulous)
+ {
+ vertexConsumer = getCompassFoilBufferDirect(bufferSource, renderType, posestack$pose);
+ }
+ else
+ {
+ vertexConsumer = getCompassFoilBuffer(bufferSource, renderType, posestack$pose);
}
+
+ poseStack.popPose();
+ }
+ else if (fabulous)
+ {
+ vertexConsumer = getFoilBufferDirect(bufferSource, renderType, true, itemStack.hasFoil());
+ }
+ else
+ {
+ vertexConsumer = getFoilBuffer(bufferSource, renderType, true, itemStack.hasFoil());
}
+
+ renderModelLists(bakedModel, itemStack, packedLight, packedOverlay, poseStack, vertexConsumer);
}
private boolean updateArmorStand(ItemStack itemStack)
@@ -499,23 +518,185 @@ public class CustomItemRenderer extends ItemRenderer
private boolean updateMinecart(MinecartItem item)
{
- if (minecart == null || minecart.getMinecartType() != item.type)
+ // We don't support modded minecarts right now.
+ if (!item.getClass().equals(MinecartItem.class))
{
- Minecraft minecraft = Minecraft.getInstance();
- minecart = AbstractMinecart.createMinecart(minecraft.level, 0, 0, 0, item.type);
+ minecart = null;
+ }
+ else
+ {
+ if (minecart == null || minecart.getMinecartType() != item.type)
+ {
+ Minecraft minecraft = Minecraft.getInstance();
+ minecart = AbstractMinecart.createMinecart(minecraft.level, 0, 0, 0, item.type);
+ }
}
// If somehow the minecart is still null, then we can't render anything.
return minecart != null;
}
- private boolean updateBoat(BoatItem item)
+ private boolean itemCreatesBoat(Item item)
{
- if (boat == null || boat.getVariant() != item.type || (boat instanceof ChestBoat) != item.hasChest)
+ // Vanilla and modded subclass BoatItems obviously create boats.
+ if (item instanceof BoatItem)
+ {
+ return true;
+ }
+
+ // More complex modded items may create a boat subclass entity, so check for that next.
+ // To do so, we'll look for a method that returns a subclass of Boat.
+ try
+ {
+ Method[] methods = item.getClass().getDeclaredMethods();
+ for (Method method : methods)
+ {
+ if (Boat.class.isAssignableFrom(method.getReturnType()) && !method.getReturnType().equals(Boat.class))
+ {
+ return true;
+ }
+ }
+ }
+ catch (Exception e) {}
+
+ // This doesn't appear to be a boat-creating item.
+ return false;
+ }
+
+ /// Attempts to create a modded boat entity from a modded boat item using reflection.
+ /// Returns null if we failed to create the entity.
+ private Boat createModdedBoat(Item item)
+ {
+ Boat moddedBoat = null;
+
+ // Ensure this is actually a modded boat item before continuing.
+ //if (itemCreatesBoat(item))
{
Minecraft minecraft = Minecraft.getInstance();
- boat = item.hasChest ? new ChestBoat(minecraft.level, 0, 0, 0) : new Boat(minecraft.level, 0, 0, 0);
- boat.setVariant(item.type);
+
+ // Since this boat is modded, first we will look for a method in the BoatItem subclass that returns an instance of a Boat subclass.
+ // If we find it, we will use it to create the boat entity.
+ try
+ {
+ Method[] methods = item.getClass().getDeclaredMethods();
+ for (Method method : methods)
+ {
+ // If the signature matches, call it.
+ if (Boat.class.isAssignableFrom(method.getReturnType()) && !method.getReturnType().equals(Boat.class))
+ {
+ // First supported signature: createBoat(Level level, HitResult hit)
+ if (method.getParameterCount() == 2 &&
+ method.getParameterTypes()[0].equals(Level.class) &&
+ method.getParameterTypes()[1].equals(HitResult.class))
+
+ {
+ method.setAccessible(true);
+ moddedBoat = (Boat) method.invoke(item, minecraft.level, new BlockHitResult(Vec3.ZERO, Direction.DOWN, BlockPos.ZERO, false));
+ break;
+ }
+ // Second supported signature: createBoat(Level level, Vec3 pos, float yaw)
+ else if (method.getParameterCount() == 3 &&
+ method.getParameterTypes()[0].equals(Level.class) &&
+ method.getParameterTypes()[1].equals(Vec3.class) &&
+ method.getParameterTypes()[2].equals(float.class))
+ {
+ method.setAccessible(true);
+ moddedBoat = (Boat) method.invoke(item, minecraft.level, Vec3.ZERO, 30.0f);
+ break;
+ }
+ }
+ }
+
+ // Now, if we've created an entity we need to set the "wood type", so we will first look for an enum field on this item that matches
+ // the parameter type of a method on the boat subclass, then call that method.
+ if (moddedBoat != null)
+ {
+ Field[] fields = item.getClass().getDeclaredFields();
+ Method[] boatMethods = moddedBoat.getClass().getDeclaredMethods();
+ boolean enumFound = false;
+
+ for (Field field : fields)
+ {
+ if (field.getType().isEnum())
+ {
+ // Check if this enum field is the same type as the first parameter of a method on the boat subclass.
+ for (Method method : boatMethods)
+ {
+ if (method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(field.getType()))
+ {
+ method.setAccessible(true);
+ field.setAccessible(true);
+ method.invoke(moddedBoat, field.get(item));
+ enumFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // If we didn't find any enum fields to try, now try with suppliers.
+ if (!enumFound)
+ {
+ for (Field field : fields)
+ {
+ if (field.getType().isAssignableFrom(Supplier.class))
+ {
+ field.setAccessible(true);
+ Object fieldValue = ((Supplier<?>)field.get(item)).get();
+
+ // Check if this enum field is the same type as the first parameter of a method on the boat subclass.
+ for (Method method : boatMethods)
+ {
+ if (method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(fieldValue.getClass()))
+ {
+ method.setAccessible(true);
+ method.invoke(moddedBoat, fieldValue);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // If we fail to create the boat entity by looking for a "getBoat"-esque method, we can't render this modded boat.
+ // This will fallback to just rendering the item icon.
+ moddedBoat = null;
+ Loader.LOGGER.info(ExceptionUtils.getStackTrace(e));
+ }
+ }
+
+ return moddedBoat;
+ }
+
+ private boolean updateBoat(Item item)
+ {
+ boolean moddedBoat = false;
+
+ // Check if this is a modded boat item, that is if it is a subclass of BoatItem (or not a BoatItem at all).
+ if (!item.getClass().equals(BoatItem.class))
+ {
+ // This is a modded boat item, so we need to create the boat entity in a different way.
+ moddedBoat = true;
+ }
+
+ if (boat == null || cachedBoatItem != item)
+ {
+ Minecraft minecraft = Minecraft.getInstance();
+
+ if (moddedBoat)
+ {
+ boat = createModdedBoat(item);
+ }
+ else if (item instanceof BoatItem boatItem)
+ {
+ boat = boatItem.hasChest ? new ChestBoat(minecraft.level, 0, 0, 0) : new Boat(minecraft.level, 0, 0, 0);
+ boat.setVariant(boatItem.type);
+ }
+
+ cachedBoatItem = item;
}
// If somehow the boat is still null, then we can't render anything.
@@ -554,11 +735,11 @@ public class CustomItemRenderer extends ItemRenderer
return true;
}
- private ModelBounds boundsFromVertices(List<Vector3f> vertices)
+ private ModelBounds boundsFromVertices(Set<Vector3f> vertices)
{
Vector3f center = new Vector3f();
- float radius = 0.0F;
- float height = 0.0F;
+ float radius = 0.0f;
+ float height = 0.0f;
float minX = Float.MAX_VALUE;
float minY = Float.MAX_VALUE;
@@ -577,12 +758,12 @@ public class CustomItemRenderer extends ItemRenderer
maxZ = Math.max(maxZ, vertex.z);
}
- center = new Vector3f((minX + maxX) / 2.0F, (minY + maxY) / 2.0F, (minZ + maxZ) / 2.0F);
+ center = new Vector3f((minX + maxX) / 2.0f, (minY + maxY) / 2.0f, (minZ + maxZ) / 2.0f);
height = maxY - minY;
for (Vector3f vertex : vertices)
{
- radius = Math.max(radius, (float) Math.sqrt((vertex.x - center.x) * (vertex.x - center.x) + (vertex.y - center.y) * (vertex.y - center.y)));
+ radius = Math.max(radius, (float) Math.sqrt((vertex.x - center.x) * (vertex.x - center.x) + (vertex.z - center.z) * (vertex.z - center.z)));
}
return new ModelBounds(center, height, radius);
@@ -593,7 +774,7 @@ public class CustomItemRenderer extends ItemRenderer
{
if (!modelBoundsCache.containsKey(itemStack.getItem()))
{
- VertexCollector vertexCollector = new VertexCollector();
+ VertexCollector vertexCollector = VertexCollector.create();
renderModelInternal(itemStack, transformType, leftHanded, poseStack, rotation, vertexCollector, packedLight, packedOverlay, bakedModel, b -> b.getVertices().isEmpty());
// Now store the bounds in the cache.
diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollector.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollector.java
index 29ae995..c7960f8 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollector.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollector.java
@@ -1,21 +1,64 @@
package com.anthonyhilyard.iceberg.renderer;
import java.util.List;
+import java.util.Set;
import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import org.joml.Vector3f;
+import com.anthonyhilyard.iceberg.Loader;
+import com.google.common.collect.Sets;
import com.mojang.blaze3d.vertex.VertexConsumer;
+import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.impl.util.version.VersionPredicateParser;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
public class VertexCollector implements MultiBufferSource
{
- private final List<Vector3f> vertices = Lists.newArrayList();
- private final Vector3f currentVertex = new Vector3f();
- private int currentAlpha = 255;
- private int defaultAlpha = 255;
+ protected final Set<Vector3f> vertices = Sets.newHashSet();
+ protected final Vector3f currentVertex = new Vector3f();
+ protected int currentAlpha = 255;
+ protected int defaultAlpha = 255;
+
+ private static Boolean useSodiumVersion = null;
+
+ protected VertexCollector()
+ {
+ super();
+ }
+
+ public static VertexCollector create()
+ {
+ if (useSodiumVersion == null)
+ {
+ try
+ {
+ // If Sodium 0.4.9+ is installed, use the Sodium implementation.
+ useSodiumVersion = FabricLoader.getInstance().isModLoaded("sodium") && VersionPredicateParser.parse(">=0.4.9").test(FabricLoader.getInstance().getModContainer("sodium").get().getMetadata().getVersion());
+ }
+ catch (Exception e)
+ {
+ Loader.LOGGER.error(ExceptionUtils.getStackTrace(e));
+ }
+ }
+
+ if (useSodiumVersion)
+ {
+ // Instantiate the Sodium implementation using reflection.
+ try
+ {
+ return (VertexCollector) Class.forName("com.anthonyhilyard.iceberg.renderer.VertexCollectorSodium").getDeclaredConstructor().newInstance();
+ }
+ catch (Exception e)
+ {
+ Loader.LOGGER.error(ExceptionUtils.getStackTrace(e));
+ }
+ }
+ return new VertexCollector();
+ }
@Override
public VertexConsumer getBuffer(RenderType renderType)
@@ -72,7 +115,7 @@ public class VertexCollector implements MultiBufferSource
};
}
- public List<Vector3f> getVertices()
+ public Set<Vector3f> getVertices()
{
return vertices;
}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollectorSodium.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollectorSodium.java
new file mode 100644
index 0000000..ed0fb7c
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollectorSodium.java
@@ -0,0 +1,98 @@
+package com.anthonyhilyard.iceberg.renderer;
+
+import org.joml.Vector3f;
+import org.lwjgl.system.MemoryStack;
+
+import com.anthonyhilyard.iceberg.util.UnsafeUtil;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+
+import net.minecraft.client.renderer.RenderType;
+
+import me.jellysquid.mods.sodium.client.render.vertex.VertexFormatDescription;
+import me.jellysquid.mods.sodium.client.render.vertex.transform.CommonVertexElement;
+
+public class VertexCollectorSodium extends VertexCollector
+{
+ protected VertexCollectorSodium()
+ {
+ super();
+ }
+
+ @Override
+ public VertexConsumer getBuffer(RenderType renderType)
+ {
+ return new VertexConsumerSodium()
+ {
+ @Override
+ public VertexConsumer vertex(double x, double y, double z)
+ {
+ currentVertex.set((float) x, (float) y, (float) z);
+ currentAlpha = defaultAlpha;
+ return this;
+ }
+
+ @Override
+ public VertexConsumer color(int r, int g, int b, int a)
+ {
+ currentAlpha = a;
+ return this;
+ }
+
+ @Override
+ public VertexConsumer uv(float u, float v) { return this; }
+
+ @Override
+ public VertexConsumer overlayCoords(int x, int y) { return this; }
+
+ @Override
+ public VertexConsumer uv2(int u, int v) { return this; }
+
+ @Override
+ public VertexConsumer normal(float x, float y, float z) { return this; }
+
+ @Override
+ public void endVertex()
+ {
+ if (currentAlpha >= 25)
+ {
+ vertices.add(new Vector3f(currentVertex));
+ }
+ }
+
+ @Override
+ public void defaultColor(int r, int g, int b, int a)
+ {
+ defaultAlpha = a;
+ }
+
+ @Override
+ public void unsetDefaultColor()
+ {
+ defaultAlpha = 255;
+ }
+
+ @Override
+ public void push(MemoryStack memoryStack, long pointer, int count, VertexFormatDescription format)
+ {
+ // Loop over each vertex, and add it to the list if it's opaque.
+ // To determine this, we need to check the vertex format to find the vertex position and alpha.
+ for (int i = 0; i < count; i++)
+ {
+ // Get the vertex position.
+ float x = UnsafeUtil.readFloat(pointer + i * format.stride + format.getOffset(CommonVertexElement.POSITION));
+ float y = UnsafeUtil.readFloat(pointer + i * format.stride + format.getOffset(CommonVertexElement.POSITION) + 4);
+ float z = UnsafeUtil.readFloat(pointer + i * format.stride + format.getOffset(CommonVertexElement.POSITION) + 8);
+
+ // Get the vertex alpha.
+ int a = UnsafeUtil.readByte(pointer + i * format.stride + format.getOffset(CommonVertexElement.COLOR) + 3) & 0xFF;
+
+ // Add the vertex to the list if it's opaque.
+ if (a >= 25)
+ {
+ vertices.add(new Vector3f(x, y, z));
+ }
+ }
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexConsumerSodium.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexConsumerSodium.java
new file mode 100644
index 0000000..c02ef30
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/VertexConsumerSodium.java
@@ -0,0 +1,9 @@
+package com.anthonyhilyard.iceberg.renderer;
+
+import com.mojang.blaze3d.vertex.VertexConsumer;
+
+import me.jellysquid.mods.sodium.client.render.vertex.VertexBufferWriter;
+
+public interface VertexConsumerSodium extends VertexConsumer, VertexBufferWriter
+{
+}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java b/src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java
new file mode 100644
index 0000000..d8410ce
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java
@@ -0,0 +1,40 @@
+package com.anthonyhilyard.iceberg.util;
+
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+
+public class UnsafeUtil
+{
+ private static final Unsafe UNSAFE;
+
+ static
+ {
+ try
+ {
+ Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ field.setAccessible(true);
+
+ UNSAFE = (Unsafe) field.get(null);
+ }
+ catch (NoSuchFieldException | IllegalAccessException e)
+ {
+ throw new RuntimeException("Couldn't obtain reference to sun.misc.Unsafe", e);
+ }
+ }
+
+ public static float readFloat(long address)
+ {
+ return UNSAFE.getFloat(address);
+ }
+
+ public static int readInt(long address)
+ {
+ return UNSAFE.getInt(address);
+ }
+
+ public static byte readByte(long address)
+ {
+ return UNSAFE.getByte(address);
+ }
+} \ No newline at end of file