diff options
Diffstat (limited to 'src/main/java/com/anthonyhilyard')
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 |