diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java index 3a70d82db..c5b8bdd30 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java +++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModeller.java @@ -69,8 +69,8 @@ default Collection getDependencies() { } /** - * A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem() - * crafting item}. + * A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(CompoundTag)} + * upgrade item}. *

* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated} * model type. It will not appear correct for 3D models with additional depth, such as blocks. @@ -80,7 +80,7 @@ default Collection getDependencies() { */ @SuppressWarnings("unchecked") static TurtleUpgradeModeller flatItem() { - return (TurtleUpgradeModeller) TurtleUpgradeModellers.FLAT_ITEM; + return (TurtleUpgradeModeller) TurtleUpgradeModellers.UPGRADE_ITEM; } /** diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java index a4ecacaed..76f47bfb9 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java +++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/TurtleUpgradeModellers.java @@ -6,13 +6,20 @@ import com.mojang.math.Transformation; import dan200.computercraft.api.client.TransformedModel; +import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.impl.client.ClientPlatformHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; import org.joml.Matrix4f; +import javax.annotation.Nullable; + class TurtleUpgradeModellers { - private static final Transformation leftTransform = getMatrixFor(-0.40625f); - private static final Transformation rightTransform = getMatrixFor(0.40625f); + private static final Transformation leftTransform = getMatrixFor(-0.4065f); + private static final Transformation rightTransform = getMatrixFor(0.4065f); private static Transformation getMatrixFor(float offset) { var matrix = new Matrix4f(); @@ -26,6 +33,23 @@ private static Transformation getMatrixFor(float offset) { return new Transformation(matrix); } - static final TurtleUpgradeModeller FLAT_ITEM = (upgrade, turtle, side) -> - TransformedModel.of(upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform); + static final TurtleUpgradeModeller UPGRADE_ITEM = new UpgradeItemModeller(); + + private static class UpgradeItemModeller implements TurtleUpgradeModeller { + @Override + public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { + return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side); + } + + @Override + public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) { + return getModel(upgrade.getUpgradeItem(data), side); + } + + private TransformedModel getModel(ItemStack stack, TurtleSide side) { + var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack); + if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model); + return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform); + } + } } diff --git a/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java b/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java index 581d3fb01..2f79ed05d 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java +++ b/projects/common-api/src/client/java/dan200/computercraft/impl/client/ClientPlatformHelper.java @@ -5,6 +5,7 @@ package dan200.computercraft.impl.client; import dan200.computercraft.impl.Services; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.client.resources.model.ModelResourceLocation; @@ -24,6 +25,15 @@ public interface ClientPlatformHelper { */ BakedModel getModel(ModelManager manager, ResourceLocation location); + /** + * Wrap this model in a version which renders a foil/enchantment glint. + * + * @param model The model to wrap. + * @return The wrapped model. + * @see RenderType#glint() + */ + BakedModel createdFoiledModel(BakedModel model); + static ClientPlatformHelper get() { var instance = Instance.INSTANCE; return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance; diff --git a/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java b/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java index cb159a1e2..c512f0c0d 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java @@ -27,10 +27,9 @@ * This is typically used with a {@link BakedModel} subclass - see the loader-specific projects. */ public class ModelTransformer { - @SuppressWarnings("MutablePublicArray") // It's not nice, but is efficient. - public static final int[] INVERSE_ORDER = new int[]{ 3, 2, 1, 0 }; + private static final int[] INVERSE_ORDER = new int[]{ 3, 2, 1, 0 }; - public static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize(); + private static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize(); private static final int POS_OFFSET = findOffset(DefaultVertexFormat.BLOCK, DefaultVertexFormat.ELEMENT_POSITION); protected final Matrix4f transformation; @@ -62,7 +61,7 @@ private BakedQuad transformQuad(BakedQuad quad) { for (var i = 0; i < 4; i++) { var inStart = STRIDE * i; // Reverse the order of the quads if we're inverting - var outStart = STRIDE * (invert ? INVERSE_ORDER[i] : i); + var outStart = getVertexOffset(i, invert); System.arraycopy(inputData, inStart, outputData, outStart, STRIDE); // Apply the matrix to our position @@ -86,6 +85,10 @@ private BakedQuad transformQuad(BakedQuad quad) { return new BakedQuad(outputData, quad.getTintIndex(), direction, quad.getSprite(), quad.isShade()); } + public static int getVertexOffset(int vertex, boolean invert) { + return (invert ? ModelTransformer.INVERSE_ORDER[vertex] : vertex) * ModelTransformer.STRIDE; + } + private record TransformedQuads(List original, List transformed) { } diff --git a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java index bac1422cc..70d5dc327 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java +++ b/projects/common/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelper.java @@ -4,8 +4,13 @@ package dan200.computercraft.client.platform; +import com.mojang.blaze3d.vertex.PoseStack; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.resources.model.BakedModel; + +import javax.annotation.Nullable; public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper { static ClientPlatformHelper get() { @@ -18,4 +23,16 @@ static ClientPlatformHelper get() { * @param message The message to send. */ void sendToServer(NetworkMessage message); + + /** + * Render a {@link BakedModel}, using any loader-specific hooks. + * + * @param transform The current matrix transformation to apply. + * @param buffers The current pool of render buffers. + * @param model The model to draw. + * @param lightmapCoord The current packed lightmap coordinate. + * @param overlayLight The current overlay light. + * @param tints Block colour tints to apply to the model. + */ + void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints); } diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/ModelRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/ModelRenderer.java new file mode 100644 index 000000000..60110a8c8 --- /dev/null +++ b/projects/common/src/client/java/dan200/computercraft/client/render/ModelRenderer.java @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.client.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import dan200.computercraft.client.model.turtle.ModelTransformer; +import dan200.computercraft.client.platform.ClientPlatformHelper; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.world.item.ItemStack; +import org.joml.Vector4f; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Utilities for rendering {@link BakedModel}s and {@link BakedQuad}s. + */ +public final class ModelRenderer { + private ModelRenderer() { + } + + /** + * Render a list of {@linkplain BakedQuad quads} to a buffer. + *

+ * This is not intended to be used directly, but instead by {@link ClientPlatformHelper#renderBakedModel(PoseStack, MultiBufferSource, BakedModel, int, int, int[])}. The + * implementation here is pretty similar to {@link ItemRenderer#renderQuadList(PoseStack, VertexConsumer, List, ItemStack, int, int)}, + * but supports inverted quads (i.e. those with a negative scale). + * + * @param transform The current matrix transformation to apply. + * @param buffer The buffer to draw to. + * @param quads The quads to draw. + * @param lightmapCoord The current packed lightmap coordinate. + * @param overlayLight The current overlay light. + * @param tints Block colour tints to apply to the model. + */ + public static void renderQuads(PoseStack transform, VertexConsumer buffer, List quads, int lightmapCoord, int overlayLight, @Nullable int[] tints) { + var matrix = transform.last(); + var inverted = matrix.pose().determinant() < 0; + + for (var bakedquad : quads) { + var tint = -1; + if (tints != null && bakedquad.isTinted()) { + var idx = bakedquad.getTintIndex(); + if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()]; + } + + var r = (float) (tint >> 16 & 255) / 255.0F; + var g = (float) (tint >> 8 & 255) / 255.0F; + var b = (float) (tint & 255) / 255.0F; + putBulkQuad(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight, inverted); + } + } + + /** + * A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} which + * will reverse vertex order when the matrix is inverted. + * + * @param buffer The buffer to draw to. + * @param pose The current matrix stack. + * @param quad The quad to draw. + * @param red The red tint of this quad. + * @param green The green tint of this quad. + * @param blue The blue tint of this quad. + * @param lightmapCoord The lightmap coordinate + * @param overlayLight The overlay light. + */ + private static void putBulkQuad(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight, boolean invert) { + var matrix = pose.pose(); + // It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in + // Direction.rotate (so not out of nowhere!), but is a little suspicious. + var dirNormal = quad.getDirection().getNormal(); + var vector = new Vector4f(); + + matrix.transform(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f, vector).normalize(); + float normalX = vector.x(), normalY = vector.y(), normalZ = vector.z(); + + var vertices = quad.getVertices(); + for (var vertex = 0; vertex < 4; vertex++) { + var i = ModelTransformer.getVertexOffset(vertex, invert); + + var x = Float.intBitsToFloat(vertices[i]); + var y = Float.intBitsToFloat(vertices[i + 1]); + var z = Float.intBitsToFloat(vertices[i + 2]); + + matrix.transform(x, y, z, 1, vector); + + var u = Float.intBitsToFloat(vertices[i + 4]); + var v = Float.intBitsToFloat(vertices[i + 5]); + buffer.vertex( + vector.x(), vector.y(), vector.z(), + red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord, + normalX, normalY, normalZ + ); + } + } +} diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java index d063c2796..92fce737f 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java @@ -5,36 +5,28 @@ package dan200.computercraft.client.render; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import com.mojang.math.Transformation; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.client.model.turtle.ModelTransformer; import dan200.computercraft.client.platform.ClientPlatformHelper; import dan200.computercraft.client.turtle.TurtleUpgradeModellers; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; -import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.Holiday; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.Sheets; -import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; -import org.joml.Vector4f; import javax.annotation.Nullable; -import java.util.List; public class TurtleBlockEntityRenderer implements BlockEntityRenderer { private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory"); @@ -42,8 +34,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer quads, @Nullable int[] tints) { - var matrix = transform.last(); - var inverted = matrix.pose().determinant() < 0; - - for (var bakedquad : quads) { - var tint = -1; - if (tints != null && bakedquad.isTinted()) { - var idx = bakedquad.getTintIndex(); - if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()]; - } - - var r = (float) (tint >> 16 & 255) / 255.0F; - var g = (float) (tint >> 8 & 255) / 255.0F; - var b = (float) (tint & 255) / 255.0F; - if (inverted) { - putBulkQuadInvert(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); - } else { - buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); - } - } - } - - /** - * A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} for - * when the matrix is inverted. - * - * @param buffer The buffer to draw to. - * @param pose The current matrix stack. - * @param quad The quad to draw. - * @param red The red tint of this quad. - * @param green The green tint of this quad. - * @param blue The blue tint of this quad. - * @param lightmapCoord The lightmap coordinate - * @param overlayLight The overlay light. - */ - private static void putBulkQuadInvert(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight) { - var matrix = pose.pose(); - // It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in - // Direction.rotate (so not out of nowhere!), but is a little suspicious. - var dirNormal = quad.getDirection().getNormal(); - var normal = matrix.transform(new Vector4f(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f)).normalize(); - - var vertices = quad.getVertices(); - for (var vertex : ModelTransformer.INVERSE_ORDER) { - var i = vertex * ModelTransformer.STRIDE; - - var x = Float.intBitsToFloat(vertices[i]); - var y = Float.intBitsToFloat(vertices[i + 1]); - var z = Float.intBitsToFloat(vertices[i + 2]); - var transformed = matrix.transform(new Vector4f(x, y, z, 1)); - - var u = Float.intBitsToFloat(vertices[i + 4]); - var v = Float.intBitsToFloat(vertices[i + 5]); - buffer.vertex( - transformed.x(), transformed.y(), transformed.z(), - red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord, - normal.x(), normal.y(), normal.z() - ); - } - } - - private static void pushPoseFromTransformation(PoseStack stack, Transformation transformation) { - stack.pushPose(); - + private static void applyTransformation(PoseStack stack, Transformation transformation) { var trans = transformation.getTranslation(); stack.translate(trans.x(), trans.y(), trans.z()); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java index b34ec8180..6341b1f70 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java @@ -100,12 +100,12 @@ public CompoundTag getUpgradeData(ItemStack stack) { public ItemStack getUpgradeItem(CompoundTag upgradeData) { // Copy upgrade data back to the item. var item = super.getUpgradeItem(upgradeData).copy(); - item.setTag(upgradeData.contains(TAG_ITEM_TAG, TAG_COMPOUND) ? upgradeData.getCompound(TAG_ITEM_TAG).copy() : null); + item.setTag(upgradeData.contains(TAG_ITEM_TAG, TAG_COMPOUND) ? upgradeData.getCompound(TAG_ITEM_TAG) : null); return item; } private ItemStack getToolStack(ITurtleAccess turtle, TurtleSide side) { - return getUpgradeItem(turtle.getUpgradeNBTData(side)); + return getUpgradeItem(turtle.getUpgradeNBTData(side)).copy(); } private void setToolStack(ITurtleAccess turtle, TurtleSide side, ItemStack stack) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/ConsList.java b/projects/common/src/main/java/dan200/computercraft/shared/util/ConsList.java new file mode 100644 index 000000000..909959642 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/ConsList.java @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.util; + +import org.jetbrains.annotations.Nullable; + +import java.util.AbstractList; +import java.util.Iterator; +import java.util.List; + +/** + * A list which prepends a single value to another list. + * + * @param The type of item in the list. + */ +public final class ConsList extends AbstractList { + private final T head; + private final List tail; + + public ConsList(T head, List tail) { + this.head = head; + this.tail = tail; + } + + @Override + public T get(int index) { + return index == 0 ? head : tail.get(index - 1); + } + + @Override + public int size() { + return 1 + tail.size(); + } + + @Override + public Iterator iterator() { + return new Iterator<>() { + private @Nullable Iterator tailIterator; + + @Override + public boolean hasNext() { + return tailIterator == null || tailIterator.hasNext(); + } + + @Override + public T next() { + if (tailIterator != null) return tailIterator.next(); + + tailIterator = tail.iterator(); + return head; + } + }; + } +} diff --git a/projects/common/src/test/java/dan200/computercraft/shared/util/ConsListTest.java b/projects/common/src/test/java/dan200/computercraft/shared/util/ConsListTest.java new file mode 100644 index 000000000..7c65b836f --- /dev/null +++ b/projects/common/src/test/java/dan200/computercraft/shared/util/ConsListTest.java @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.util; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * A couple of trivial tests for {@link ConsList}, mostly as a quick safety check. + */ +public class ConsListTest { + @Test + public void testGet() { + var list = new ConsList<>(1, List.of(2, 3, 4)); + assertEquals(1, list.get(0)); + assertEquals(2, list.get(1)); + assertEquals(4, list.get(3)); + } + + @Test + public void testSize() { + var list = new ConsList<>(1, List.of(2, 3, 4)); + assertEquals(4, list.size()); + } + + @Test + public void testIterator() { + var list = new ConsList<>(1, List.of(2, 3, 4)); + assertArrayEquals(new Integer[]{ 1, 2, 3, 4 }, list.toArray(Integer[]::new)); + } +} diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt index 4d2d67e48..15dcd9afa 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt @@ -623,6 +623,24 @@ fun Peripheral_change(helper: GameTestHelper) = helper.sequence { } // TODO: Turtle sucking from items + + /** + * Render turtles as an item. + */ + @ClientGameTest + fun Render_turtle_items(helper: GameTestHelper) = helper.sequence { + thenExecute { helper.positionAtArmorStand() } + thenScreenshot() + } + + /** + * Render turtles as a block entity. + */ + @ClientGameTest + fun Render_turtle_blocks(helper: GameTestHelper) = helper.sequence { + thenExecute { helper.positionAtArmorStand() } + thenScreenshot() + } } private val LuaTaskContext.turtle get() = getApi() diff --git a/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_blocks.snbt b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_blocks.snbt new file mode 100644 index 000000000..fd929d435 --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_blocks.snbt @@ -0,0 +1,140 @@ +{ + DataVersion: 3337, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "computercraft:turtle_normal{facing:east,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}, On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:air"}, + {pos: [2, 1, 2], state: "minecraft:air"}, + {pos: [2, 1, 3], state: "minecraft:air"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], Label: "Dinnerbone", On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "computercraft:turtle_normal{facing:east,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], Label: "Dinnerbone", LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}, On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, Owner: {LowerId: -7931330074873442406L, Name: "Player848", UpperId: 7430876841693101418L}, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}, Slot: 0, id: "computercraft:turtle_normal"}}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [ + {blockPos: [2, 1, 0], pos: [2.52408694606693d, 1.0d, 0.6487863808268131d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"turtle_test.render_turtle_blocks"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [124.52408694606693d, -58.0d, 50.64878638082681d], Pose: {}, Rotation: [0.14965993f, 4.066999f], ShowArms: 0b, Small: 0b, UUID: [I; 755114464, 1289439453, -2088751844, 985194758], id: "minecraft:armor_stand"}} + ], + palette: [ + "minecraft:polished_andesite", + "minecraft:air", + "computercraft:turtle_normal{facing:east,waterlogged:false}", + "computercraft:turtle_normal{facing:west,waterlogged:false}" + ] +} diff --git a/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_items.snbt b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_items.snbt new file mode 100644 index 000000000..d772a0196 --- /dev/null +++ b/projects/common/src/testMod/resources/data/cctest/structures/turtle_test.render_turtle_items.snbt @@ -0,0 +1,142 @@ +{ + DataVersion: 3337, + size: [5, 5, 5], + data: [ + {pos: [0, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [0, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [1, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [2, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [3, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 0], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 1], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 2], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 3], state: "minecraft:polished_andesite"}, + {pos: [4, 0, 4], state: "minecraft:polished_andesite"}, + {pos: [0, 1, 0], state: "minecraft:air"}, + {pos: [0, 1, 1], state: "minecraft:air"}, + {pos: [0, 1, 2], state: "minecraft:air"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [0, 1, 4], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "minecraft:air"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "minecraft:air"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 4], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "minecraft:air"}, + {pos: [2, 1, 1], state: "minecraft:air"}, + {pos: [2, 1, 2], state: "minecraft:air"}, + {pos: [2, 1, 3], state: "minecraft:air"}, + {pos: [2, 1, 4], state: "minecraft:air"}, + {pos: [3, 1, 0], state: "minecraft:air"}, + {pos: [3, 1, 1], state: "minecraft:air"}, + {pos: [3, 1, 2], state: "minecraft:air"}, + {pos: [3, 1, 3], state: "minecraft:air"}, + {pos: [3, 1, 4], state: "minecraft:air"}, + {pos: [4, 1, 0], state: "minecraft:air"}, + {pos: [4, 1, 1], state: "minecraft:air"}, + {pos: [4, 1, 2], state: "minecraft:air"}, + {pos: [4, 1, 3], state: "minecraft:air"}, + {pos: [4, 1, 4], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "minecraft:air"}, + {pos: [0, 2, 1], state: "minecraft:air"}, + {pos: [0, 2, 2], state: "minecraft:air"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [0, 2, 4], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "minecraft:air"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "minecraft:air"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 4], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "minecraft:air"}, + {pos: [2, 2, 1], state: "minecraft:air"}, + {pos: [2, 2, 2], state: "minecraft:air"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 4], state: "minecraft:air"}, + {pos: [3, 2, 0], state: "minecraft:air"}, + {pos: [3, 2, 1], state: "minecraft:air"}, + {pos: [3, 2, 2], state: "minecraft:air"}, + {pos: [3, 2, 3], state: "minecraft:air"}, + {pos: [3, 2, 4], state: "minecraft:air"}, + {pos: [4, 2, 0], state: "minecraft:air"}, + {pos: [4, 2, 1], state: "minecraft:air"}, + {pos: [4, 2, 2], state: "minecraft:air"}, + {pos: [4, 2, 3], state: "minecraft:air"}, + {pos: [4, 2, 4], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "minecraft:air"}, + {pos: [0, 3, 1], state: "minecraft:air"}, + {pos: [0, 3, 2], state: "minecraft:air"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [0, 3, 4], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "minecraft:air"}, + {pos: [1, 3, 1], state: "minecraft:air"}, + {pos: [1, 3, 2], state: "minecraft:air"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 4], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "minecraft:air"}, + {pos: [2, 3, 1], state: "minecraft:air"}, + {pos: [2, 3, 2], state: "minecraft:air"}, + {pos: [2, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 4], state: "minecraft:air"}, + {pos: [3, 3, 0], state: "minecraft:air"}, + {pos: [3, 3, 1], state: "minecraft:air"}, + {pos: [3, 3, 2], state: "minecraft:air"}, + {pos: [3, 3, 3], state: "minecraft:air"}, + {pos: [3, 3, 4], state: "minecraft:air"}, + {pos: [4, 3, 0], state: "minecraft:air"}, + {pos: [4, 3, 1], state: "minecraft:air"}, + {pos: [4, 3, 2], state: "minecraft:air"}, + {pos: [4, 3, 3], state: "minecraft:air"}, + {pos: [4, 3, 4], state: "minecraft:air"}, + {pos: [0, 4, 0], state: "minecraft:air"}, + {pos: [0, 4, 1], state: "minecraft:air"}, + {pos: [0, 4, 2], state: "minecraft:air"}, + {pos: [0, 4, 3], state: "minecraft:air"}, + {pos: [0, 4, 4], state: "minecraft:air"}, + {pos: [1, 4, 0], state: "minecraft:air"}, + {pos: [1, 4, 1], state: "minecraft:air"}, + {pos: [1, 4, 2], state: "minecraft:air"}, + {pos: [1, 4, 3], state: "minecraft:air"}, + {pos: [1, 4, 4], state: "minecraft:air"}, + {pos: [2, 4, 0], state: "minecraft:air"}, + {pos: [2, 4, 1], state: "minecraft:air"}, + {pos: [2, 4, 2], state: "minecraft:air"}, + {pos: [2, 4, 3], state: "minecraft:air"}, + {pos: [2, 4, 4], state: "minecraft:air"}, + {pos: [3, 4, 0], state: "minecraft:air"}, + {pos: [3, 4, 1], state: "minecraft:air"}, + {pos: [3, 4, 2], state: "minecraft:air"}, + {pos: [3, 4, 3], state: "minecraft:air"}, + {pos: [3, 4, 4], state: "minecraft:air"}, + {pos: [4, 4, 0], state: "minecraft:air"}, + {pos: [4, 4, 1], state: "minecraft:air"}, + {pos: [4, 4, 2], state: "minecraft:air"}, + {pos: [4, 4, 3], state: "minecraft:air"}, + {pos: [4, 4, 4], state: "minecraft:air"} + ], + entities: [ + {blockPos: [2, 1, 0], pos: [2.5d, 1.0d, 0.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"turtle_test.render_turtle_items"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [125.5d, -58.0d, 53.6501934495752d], Pose: {}, Rotation: [0.14965993f, 4.066999f], ShowArms: 0b, Small: 0b, UUID: [I; -1678989666, 1780632657, -1267321893, 665166246], id: "minecraft:armor_stand"}}, + {blockPos: [3, 1, 3], pos: [3.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334450, -268547745, -1360971514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}, + {blockPos: [3, 3, 3], pos: [3.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334422, -268542345, -1362491514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}, + {blockPos: [1, 1, 3], pos: [1.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 625334450, -268647745, -1360971514, -649724242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}, + {blockPos: [1, 3, 3], pos: [1.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 675334422, -268542245, -1362491514, -649755242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, LeftUpgrade: "cctest:netherite_pickaxe", LeftUpgradeNbt: {Tag: {Damage: 0, Enchantments: [{id: "minecraft:efficiency", lvl: 5s}], RepairCost: 1}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}} + ], + palette: [ + "minecraft:polished_andesite", + "minecraft:air" + ] +} diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/model/FoiledModel.java b/projects/fabric/src/client/java/dan200/computercraft/client/model/FoiledModel.java new file mode 100644 index 000000000..a0995211a --- /dev/null +++ b/projects/fabric/src/client/java/dan200/computercraft/client/model/FoiledModel.java @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.client.model; + +import dan200.computercraft.core.util.Nullability; +import net.fabricmc.fabric.api.renderer.v1.Renderer; +import net.fabricmc.fabric.api.renderer.v1.RendererAccess; +import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; +import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; +import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.fabricmc.fabric.api.util.TriState; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.block.state.BlockState; + +import javax.annotation.Nullable; +import java.util.function.Supplier; + +/** + * A model wrapper which applies a {@link RenderMaterial#glint() glint}/foil to the original model. + */ +public final class FoiledModel extends ForwardingBakedModel implements RenderContext.QuadTransform { + private final @Nullable Renderer renderer = RendererAccess.INSTANCE.getRenderer(); + private @Nullable RenderMaterial lastMaterial, lastFoiledMaterial; + + public FoiledModel(BakedModel model) { + wrapped = model; + } + + @Override + public boolean isVanillaAdapter() { + return false; + } + + @Override + public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + context.pushTransform(this); + super.emitBlockQuads(blockView, state, pos, randomSupplier, context); + context.popTransform(); + } + + @Override + public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + context.pushTransform(this); + super.emitItemQuads(stack, randomSupplier, context); + context.popTransform(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof FoiledModel other && wrapped.equals(other.wrapped)); + } + + @Override + public int hashCode() { + return wrapped.hashCode() ^ 1; + } + + @Override + public boolean transform(MutableQuadView quad) { + if (renderer == null) return true; + + var material = quad.material(); + if (material == lastMaterial) { + quad.material(Nullability.assertNonNull(lastFoiledMaterial)); + } else { + lastMaterial = material; + quad.material(lastFoiledMaterial = renderer.materialFinder().copyFrom(material).glint(TriState.TRUE).find()); + } + + return true; + } +} diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java index 686ab6f49..f71256f72 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java @@ -5,16 +5,28 @@ package dan200.computercraft.client.platform; import com.google.auto.service.AutoService; +import com.mojang.blaze3d.vertex.PoseStack; +import dan200.computercraft.client.model.FoiledModel; +import dan200.computercraft.client.render.ModelRenderer; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.platform.NetworkHandler; +import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.Sheets; +import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; + +import javax.annotation.Nullable; @AutoService(dan200.computercraft.impl.client.ClientPlatformHelper.class) public class ClientPlatformHelperImpl implements ClientPlatformHelper { + private static final RandomSource random = RandomSource.create(0); + @Override public void sendToServer(NetworkMessage message) { Minecraft.getInstance().player.connection.send(NetworkHandler.encodeServer(message)); @@ -25,4 +37,22 @@ public BakedModel getModel(ModelManager manager, ResourceLocation location) { var model = manager.getModel(location); return model == null ? manager.getMissingModel() : model; } + + @Override + public BakedModel createdFoiledModel(BakedModel model) { + return new FoiledModel(model); + } + + @Override + public void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints) { + // Unfortunately we can't call Fabric's emitItemQuads here, as there's no way to obtain a RenderContext via the + // API. Instead, we special case our FoiledModel, and just render everything else normally. + var buffer = ItemRenderer.getFoilBuffer(buffers, Sheets.translucentCullBlockSheet(), true, model instanceof FoiledModel); + + for (var faceIdx = 0; faceIdx <= ModelHelper.NULL_FACE_ID; faceIdx++) { + var face = ModelHelper.faceFromIndex(faceIdx); + random.setSeed(42); + ModelRenderer.renderQuads(transform, buffer, model.getQuads(null, face, random), lightmapCoord, overlayLight, tints); + } + } } diff --git a/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java b/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java new file mode 100644 index 000000000..c351b79ee --- /dev/null +++ b/projects/forge/src/client/java/dan200/computercraft/client/model/FoiledModel.java @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.client.model; + +import dan200.computercraft.shared.util.ConsList; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.BakedModelWrapper; +import net.minecraftforge.client.model.data.ModelData; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * A model wrapper which applies a glint/foil to the original model. + */ +public final class FoiledModel extends BakedModelWrapper { + public FoiledModel(BakedModel model) { + super(model); + } + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData extraData, @Nullable RenderType renderType) { + return renderType == RenderType.glint() + ? super.getQuads(state, side, rand, extraData, null) + : super.getQuads(state, side, rand, extraData, renderType); + } + + @Override + public List getRenderTypes(ItemStack itemStack, boolean fabulous) { + return new ConsList<>(fabulous ? RenderType.glintDirect() : RenderType.glint(), super.getRenderTypes(itemStack, fabulous)); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof FoiledModel other && originalModel.equals(other.originalModel)); + } + + @Override + public int hashCode() { + return originalModel.hashCode() ^ 1; + } + +} diff --git a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java index 568b6ea9d..aaa41c4a4 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/platform/ClientPlatformHelperImpl.java @@ -5,22 +5,53 @@ package dan200.computercraft.client.platform; import com.google.auto.service.AutoService; +import com.mojang.blaze3d.vertex.PoseStack; +import dan200.computercraft.client.model.FoiledModel; +import dan200.computercraft.client.render.ModelRenderer; import dan200.computercraft.shared.network.NetworkMessage; import dan200.computercraft.shared.network.server.ServerNetworkContext; import dan200.computercraft.shared.platform.NetworkHandler; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.client.model.data.ModelData; + +import javax.annotation.Nullable; +import java.util.Arrays; @AutoService(dan200.computercraft.impl.client.ClientPlatformHelper.class) public class ClientPlatformHelperImpl implements ClientPlatformHelper { + private static final RandomSource random = RandomSource.create(0); + private static final Direction[] directions = Arrays.copyOf(Direction.values(), 7); + @Override public BakedModel getModel(ModelManager manager, ResourceLocation location) { return manager.getModel(location); } + @Override + public BakedModel createdFoiledModel(BakedModel model) { + return new FoiledModel(model); + } + @Override public void sendToServer(NetworkMessage message) { NetworkHandler.sendToServer(message); } + + @Override + public void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints) { + for (var renderType : model.getRenderTypes(ItemStack.EMPTY, true)) { + var buffer = buffers.getBuffer(renderType); + for (var face : directions) { + random.setSeed(42); + var quads = model.getQuads(null, face, random, ModelData.EMPTY, renderType); + ModelRenderer.renderQuads(transform, buffer, quads, lightmapCoord, overlayLight, tints); + } + } + } }