From 179f5a6006301e1e4c2556a316e47b0390640a93 Mon Sep 17 00:00:00 2001 From: ch-yx Date: Mon, 12 Sep 2022 06:38:52 +0800 Subject: [PATCH] adding a shape that can display a item or a block (#1365) added 'polygon', 'item' and 'block' to scarpet rendered shapes --- docs/scarpet/api/Auxiliary.md | 26 + .../carpet/mixins/ShulkerBoxAccessMixin.java | 11 + .../carpet/script/utils/ShapeDispatcher.java | 473 +++++++++++++++++- .../carpet/script/utils/ShapesRenderer.java | 363 +++++++++++++- src/main/resources/carpet.mixins.json | 1 + 5 files changed, 868 insertions(+), 6 deletions(-) create mode 100644 src/main/java/carpet/mixins/ShulkerBoxAccessMixin.java diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index bd1729c4..752271c6 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -91,6 +91,7 @@ Optional shared shape attributes: value is `'xyz'`, meaning the shape will be drawn relatively to the entity in all three directions. Using `xz` for instance makes so that the shape follows the entity, but stays at the same, absolute Y coordinate. Preceeding an axis with `d`, like `dxdydz` would make so that entity position is treated discretely (rounded down). + * `toggleable` - if True, can toggle visibility by using F3+B. just like entities' hitbox. Available shapes: * `'line'` - draws a straight line between two points. @@ -142,6 +143,31 @@ Available shapes: * `height` - height of the cyllinder, defaults to `0`, so flat disk. Can be negative. * `level` - level of details, see `'sphere'`. + * `'polygon'`: + * Required attributes: + * `points` - list of points defining vertices of the polygon + * Optional attributes: + * `relative` - list of bools. vertices of the polygon that affected by 'follow'. Could be a single bools to affact allpoints too. Default means that every point is affacted. + * `mode` - how those points are connected. may be "polygon"(default),"strip" or "triangles". "polygon" means that it will be viewed as vertices of a polygon center on the first one. "strip" means that it will be viewed as a triangles strip. "triangles" means that it will be viewed as some triangles that are not related to each other (therefor length of `points` in this mode have to be a multiple of 3). + * `inner` - if `true` it will make the inner edges be drawn as well. + * `doublesided` - if `true` it will make the shapes visible from the back as well. Default is `true`. + + * `'block'` \ `'item'`: + * Required attributes: + * `pos` - position of the object. + * `item` - the object to show. could have NBT. is a item tuple or a string-id (exclusive to `'item'`) + * `block` - the object to show. could have NBT. is a block value or a string-id (exclusive to `'block'`) + * Optional attributes: + * `facing` - object's direction, where its facing. Possible options are: `player` (it + always rotates to face the player), `north`(default), `south`, `east`, `west`, `up`, `down` + * `tilt`, `lean`, `turn` - additional rotations along all three axis. for `block`, it use its block center as the origin. + * `scale` - scale of it in 3 axis-direction. should be a number or a list of 3 numbers (x,y,z). + * `skylight`, `blocklight` - light level. omit it to use local light level. should between 0~15. + * `variant` - one of "none","third_person_left_hand","third_person_right_hand","first_person_left_hand", + "first_person_right_hand","head","gui","ground","fixed". In addition to the literal meaning, it can also be used to use special models of tridents and telescopes. (exclusive to `'item'`) + + + ### `create_marker(text, pos, rotation?, block?, interactive?)` Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations. diff --git a/src/main/java/carpet/mixins/ShulkerBoxAccessMixin.java b/src/main/java/carpet/mixins/ShulkerBoxAccessMixin.java new file mode 100644 index 00000000..a0d59a4e --- /dev/null +++ b/src/main/java/carpet/mixins/ShulkerBoxAccessMixin.java @@ -0,0 +1,11 @@ +package carpet.mixins; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.model.ShulkerModel; + +@Mixin(net.minecraft.client.renderer.blockentity.ShulkerBoxRenderer.class) +public interface ShulkerBoxAccessMixin { + @Accessor("model") + ShulkerModel getModel(); +} \ No newline at end of file diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 6bd18b8f..205ff16c 100644 --- a/src/main/java/carpet/script/utils/ShapeDispatcher.java +++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java @@ -6,18 +6,26 @@ import carpet.script.exception.InternalExpressionException; import carpet.script.exception.ThrowStatement; import carpet.script.exception.Throwables; import carpet.script.language.Sys; +import carpet.script.value.AbstractListValue; import carpet.script.value.BlockValue; import carpet.script.value.BooleanValue; import carpet.script.value.EntityValue; import carpet.script.value.FormattedTextValue; import carpet.script.value.ListValue; import carpet.script.value.MapValue; +import carpet.script.value.NBTSerializableValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; +import carpet.script.value.ValueConversions; + import com.google.common.collect.Sets; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat.Mode; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.client.renderer.block.model.ItemTransforms.TransformType; import net.minecraft.commands.arguments.ParticleArgument; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -29,8 +37,11 @@ import net.minecraft.nbt.DoubleTag; import net.minecraft.nbt.FloatTag; import net.minecraft.nbt.IntTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; +import net.minecraft.nbt.NumericTag; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -40,12 +51,16 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -249,6 +264,9 @@ public class ShapeDispatcher put("sphere", creator(Sphere::new)); put("cylinder", creator(Cylinder::new)); put("label", creator(DisplayedText::new)); + put("polygon",creator(Polyface::new)); + put("block",creator(()->new DisplayedSprite(false))); + put("item",creator(()->new DisplayedSprite(true))); }}; private static Function,ExpiringShape> creator(Supplier shapeFactory) { @@ -271,6 +289,7 @@ public class ShapeDispatcher protected boolean snapX, snapY, snapZ; protected boolean discreteX, discreteY, discreteZ; protected ResourceKey shapeDimension; + protected boolean needf3b; protected ExpiringShape() { } @@ -322,6 +341,11 @@ public class ShapeDispatcher this.b = (float)(color >> 8 & 0xFF) / 255.0F; this.a = (float)(color & 0xFF) / 255.0F; + needf3b = false; + if (options.containsKey("toggleable")) { + needf3b = options.get("toggleable").getBoolean(); + } + key = 0; followEntity = -1; shapeDimension = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(options.get("dim").getString())); @@ -386,6 +410,7 @@ public class ShapeDispatcher hash ^= shapeDimension.hashCode(); hash *= 1099511628211L; hash ^= color; hash *= 1099511628211L; hash ^= followEntity; hash *= 1099511628211L; + hash ^= Boolean.hashCode(needf3b); hash *= 1099511628211L; if (followEntity >= 0) { hash ^= snapTo.hashCode(); hash *= 1099511628211L; @@ -408,6 +433,7 @@ public class ShapeDispatcher "color", new NumericValue(-1), "follow", new NumericValue(-1), "line", new NumericValue(2.0), + "toggleable", Value.FALSE, "fill", new NumericValue(0xffffff00), "snap", new StringValue("xyz") ); @@ -546,6 +572,140 @@ public class ShapeDispatcher } } + public static class DisplayedSprite extends ExpiringShape { + private final Set required = Set.of("pos"); + private final Map optional = Map.ofEntries( + entry("facing", new StringValue("north")), + entry("tilt", new NumericValue(0)), + entry("lean", new NumericValue(0)), + entry("turn", new NumericValue(0)), + entry("scale", ListValue.fromTriple(1,1,1)), + entry("blocklight", new NumericValue(-1)), + entry("skylight", new NumericValue(-1))); + private boolean isitem; + + @Override + protected Set requiredParams() { + return Sets.union(Sets.union(super.requiredParams(), required), Set.of(isitem ? "item" : "block")); + } + + @Override + protected Set optionalParams() { + return Sets.union(Sets.union(super.optionalParams(), optional.keySet()),isitem ? Set.of("variant") : Set.of()); + } + + public DisplayedSprite(boolean i) { + isitem = i; + } + + Vec3 pos; + + Direction facing; + + float tilt; + float lean; + float turn; + float scale_z = 1.0f; + int light_fromblock; + int light_fromsky; + + float scale_y = 1.0f; + float scale_x = 1.0f; + CompoundTag blockEntity; + BlockState blockState; + ItemStack item = null; + TransformType item_transform_type; + + @Override + protected void init(Map options) { + super.init(options); + pos = vecFromValue(options.get("pos")); + if(!this.isitem){ + blockState = ((BlockValue) options.get("block")).getBlockState(); + blockEntity = ((BlockValue) options.get("block")).getData(); + }else{ + this.item = ItemStack.of(((NBTSerializableValue) options.get("item")).getCompoundTag()); + } + light_fromblock = NumericValue + .asNumber(options.getOrDefault("blocklight", optional.get("blocklight"))).getInt(); + if (light_fromblock > 15) + light_fromblock = 15; + light_fromsky = NumericValue.asNumber(options.getOrDefault("skylight", optional.get("skylight"))) + .getInt(); + if (light_fromsky > 15) + light_fromsky = 15; + + item_transform_type = TransformType.NONE; + if(options.containsKey("variant")){ + item_transform_type = TransformType.valueOf(options.get("variant").getString()); + } + + String dir = options.getOrDefault("facing", optional.get("facing")).getString(); + facing = null; + switch (dir) + { + case "north": facing = Direction.NORTH; break; + case "south": facing = Direction.SOUTH; break; + case "east": facing = Direction.EAST; break; + case "west": facing = Direction.WEST; break; + case "up": facing = Direction.UP; break; + case "down": facing = Direction.DOWN; break; + } + + + tilt = NumericValue.asNumber(options.getOrDefault("tilt", optional.get("tilt"))).getFloat(); + lean = NumericValue.asNumber(options.getOrDefault("lean", optional.get("lean"))).getFloat(); + turn = NumericValue.asNumber(options.getOrDefault("turn", optional.get("turn"))).getFloat(); + List scale = ((ListValue) options.getOrDefault("scale", optional.get("scale"))).unpack(); + scale_y = NumericValue.asNumber(scale.get(1)).getFloat(); + scale_x = NumericValue.asNumber(scale.get(0)).getFloat(); + scale_z = NumericValue.asNumber(scale.get(2)).getFloat(); + } + + @Override + public Consumer alternative() { + ParticleOptions locparticledata; + if(this.isitem){ + if(Block.byItem(this.item.getItem()).defaultBlockState().isAir()) + return p->{}; + locparticledata = getParticleData("block_marker "+Registry.BLOCK.getKey(Block.byItem(this.item.getItem()))); + }else{ + locparticledata = getParticleData("block_marker "+Registry.BLOCK.getKey(this.blockState.getBlock())); + } + + + return p -> { + Vec3 v=relativiseRender(p.level, this.pos, 0); + p.getLevel().sendParticles(p,locparticledata , true, + v.x, v.y, v.z, 1, + 0.0, 0.0, 0.0, 0.0); + }; + } + + @Override + public long calcKey() { + long hash = super.calcKey(); + hash ^= 7; hash *= 1099511628211L; + hash ^= vec3dhash(pos); hash *= 1099511628211L; + if (facing!= null) hash ^= facing.hashCode(); hash *= 1099511628211L; + hash ^= Float.hashCode(tilt); hash *= 1099511628211L; + hash ^= Float.hashCode(lean); hash *= 1099511628211L; + hash ^= Float.hashCode(turn); hash *= 1099511628211L; + hash ^= Float.hashCode(scale_y); hash *= 1099511628211L; + hash ^= Float.hashCode(scale_z); hash *= 1099511628211L; + hash ^= Float.hashCode(scale_x); hash *= 1099511628211L; + hash ^= Float.hashCode(light_fromsky); hash *= 1099511628211L; + hash ^= Float.hashCode(light_fromblock); hash *= 1099511628211L; + if (blockEntity!= null) hash ^= blockEntity.toString().hashCode(); hash *= 1099511628211L; + if (blockState!= null) hash ^= blockState.hashCode(); hash *= 1099511628211L; + hash ^= ValueConversions.of(item).getString().hashCode(); hash *= 1099511628211L; + //hash ^= Boolean.hashCode(needf3b); hash *= 1099511628211L; + hash ^= item_transform_type.hashCode(); hash *= 1099511628211L; + + return hash; + } + } + public static class Box extends ExpiringShape { @@ -625,6 +785,192 @@ public class ShapeDispatcher } } + public static class Polyface extends ExpiringShape + { + @Override + public long calcKey(){ + long hash = super.calcKey(); + hash ^= 6; hash *= 1099511628211L; + hash ^= mode.hashCode(); hash *= 1099511628211L; + hash ^= relative.hashCode(); hash *= 1099511628211L; + for (Vec3 i :vertex_list){ + hash ^= vec3dhash(i); + hash *= 1099511628211L; + } + hash ^= Boolean.hashCode(doublesided); hash *= 1099511628211L; + hash ^= Integer.hashCode(vertex_list.size()); hash *= 1099511628211L; + hash ^= Boolean.hashCode(inneredges); hash *= 1099511628211L; + //hash ^= Boolean.hashCode(needf3b); hash *= 1099511628211L; + return hash; + } + ArrayList alter_point=null; + final Random random=new Random(); + boolean doublesided; + + ArrayList alter_point(ServerPlayer p){ + if (alter_point!=null){ + return alter_point; + } + alter_point=new ArrayList<>(); + switch (mode) { + case TRIANGLES: + for (int i=0;i last = new ArrayList(); + //last.add(vecA);last.add(vecB); + for(int i=2;i alternative() { + final ParticleOptions locparticledata = getParticleData(String.format(Locale.ROOT ,"dust %.1f %.1f %.1f %.1f", fr, fg, fb, fa)); + return p->{ + if(p.level.dimension() != this.shapeDimension){return;} + if(!(fa>0.0f)){return;} + for(Vec3 v : alter_point(p)){ + p.getLevel().sendParticles(p,locparticledata , true, + v.x, v.y, v.z, 1, + 0.0, 0.0, 0.0, 0.0); + } + }; + } + private final Set required = Set.of("points"); + private final Map optional = Map.ofEntries( + entry("relative",Value.NULL), + entry("mode", new StringValue("polygon")), + entry("inner", Value.TRUE), + entry("doublesided", Value.TRUE) + ); + @Override + protected Set requiredParams() { return Sets.union(super.requiredParams(), required); } + @Override + protected Set optionalParams() { return Sets.union(super.optionalParams(), optional.keySet()); } + ArrayList vertex_list=new ArrayList<>(); + Mode mode; + ArrayList relative=new ArrayList<>(); + boolean inneredges; + @Override + protected void init(Map options) + { + super.init(options); + + + + doublesided=options.getOrDefault("doublesided",optional.get("doublesided")).getBoolean(); + + if (options.get("points") instanceof AbstractListValue abl){ + abl.forEach(x->vertex_list.add(vecFromValue(x))); + } + String _mode = options.getOrDefault("mode",optional.get("mode")).getString(); + inneredges = options.getOrDefault("inner",optional.get("inner")).getBoolean(); + if(vertex_list.size()<3){ + throw new IllegalArgumentException("Unexpected vertex list size: " + vertex_list.size()); + }else if(vertex_list.size()<4){ + inneredges=false; + } + if("polygon".equals(_mode)){ + mode=VertexFormat.Mode.TRIANGLE_FAN; + }else if("strip".equals(_mode)){ + mode=VertexFormat.Mode.TRIANGLE_STRIP; + }else if("triangles".equals(_mode)){ + mode=VertexFormat.Mode.TRIANGLES; + if(vertex_list.size()%3!=0){ + throw new IllegalArgumentException("Unexpected vertex list size: " + vertex_list.size()); + } + } + if (options.getOrDefault("relative",optional.get("relative")) instanceof AbstractListValue abl){ + Iterator it = abl.iterator(); + for(long i=0L;i required = Set.of("from", "to"); @@ -858,11 +1204,30 @@ public class ShapeDispatcher public static abstract class Param { public static Map of = new HashMap(){{ + put("mode",new StringChoiceParam("mode","polygon","strip","triangles")); + put("relative",new OptionalBoolListParam("relative")); + put("inner", new BoolParam("inner")); put("shape", new ShapeParam()); put("dim", new DimensionParam()); put("duration", new NonNegativeIntParam("duration")); put("color", new ColorParam("color")); put("follow", new EntityParam("follow")); + put("variant",new StringChoiceParam("variant", + "NONE", + "THIRD_PERSON_LEFT_HAND", + "THIRD_PERSON_RIGHT_HAND", + "FIRST_PERSON_LEFT_HAND", + "FIRST_PERSON_RIGHT_HAND", + "HEAD", + "GUI", + "GROUND", + "FIXED") + { + public Value validate(Map o, MinecraftServer s, Value v) + { + return super.validate(o, s ,new StringValue(v.getString().toUpperCase(Locale.ROOT))); + } + }); put("snap", new StringChoiceParam("snap", "xyz", "xz", "yz", "xy", "x", "y", "z", "dxdydz", "dxdz", "dydz", "dxdy", "dx", "dy", "dz", @@ -879,6 +1244,15 @@ public class ShapeDispatcher put("radius", new PositiveFloatParam("radius")); put("level", new PositiveIntParam("level")); put("height", new FloatParam("height")); + put("width", new FloatParam("width")); + put("scale", new Vec3Param("scale", false){ + public Value validate(java.util.Map options, MinecraftServer server, Value value) { + if (value instanceof NumericValue vn){ + value = ListValue.of(vn,vn,vn); + } + return super.validate(options, server, value); + }; + }); put("axis", new StringChoiceParam("axis", "x", "y", "z")); put("points", new PointsParam("points")); put("text", new FormattedTextParam("text")); @@ -886,11 +1260,18 @@ public class ShapeDispatcher put("size", new PositiveIntParam("size")); put("align", new StringChoiceParam("align", "center", "left", "right")); + put("block", new BlockParam("block")); + put("item", new ItemParam("item")); + put("blocklight", new NonNegativeIntParam("blocklight")); + put("skylight", new NonNegativeIntParam("skylight")); put("indent", new FloatParam("indent")); put("raise", new FloatParam("raise")); put("tilt", new FloatParam("tilt")); + put("lean", new FloatParam("lean")); + put("turn", new FloatParam("turn")); put("facing", new StringChoiceParam("axis", "player", "north", "south", "east", "west", "up", "down")); put("doublesided", new BoolParam("doublesided")); + put("toggleable", new BoolParam("toggleable")); }}; protected String id; @@ -903,7 +1284,34 @@ public class ShapeDispatcher public abstract Value validate(Map options, MinecraftServer server, Value value); // makes sure the value is proper public abstract Value decode(Tag tag); } + public static class OptionalBoolListParam extends Param + { + public OptionalBoolListParam(String id) { super(id); } + @Override + public Tag toTag(Value value) { return value.toTag(true);} + @Override + public Value decode(Tag tag) { + if(tag instanceof ListTag){ + return ListValue.wrap(((ListTag)tag).stream().map(x->BooleanValue.of(((NumericTag)x).getAsNumber().doubleValue()!=0))); + } + if(tag instanceof ByteTag booltag){ + return BooleanValue.of(booltag.getAsByte()!=0); + } + return Value.NULL; + } + + @Override + public Value validate(Map options, MinecraftServer server, Value value) { + if(value instanceof AbstractListValue lv){ + return ListValue.wrap(lv.unpack().stream().map(Value::getBoolean).map(BooleanValue::of)); + } + if(value instanceof BooleanValue || value.isNull()){ + return value; + } + return BooleanValue.of(value.getBoolean()); + } + } public abstract static class StringParam extends Param { protected StringParam(String id) { super(id); } @@ -913,6 +1321,65 @@ public class ShapeDispatcher @Override public Value decode(Tag tag) { return new StringValue(tag.getAsString()); } } + public static class BlockParam extends Param + { + + protected BlockParam(String id) { + super(id); + } + @Override + public Value validate(Map options, MinecraftServer server, Value value) + { + if(value instanceof BlockValue blv){ + return value; + } + return BlockValue.fromString(value.getString()); + } + @Override + public Tag toTag(Value value) { + if(value instanceof BlockValue blv){ + CompoundTag com = NbtUtils.writeBlockState(blv.getBlockState()); + CompoundTag dataTag = blv.getData(); + if (dataTag != null) + { + com.put("TileEntityData", dataTag); + } + return com; + } + return null; + } + + @Override + public Value decode(Tag tag) { + BlockState bs = NbtUtils.readBlockState((CompoundTag) tag); + CompoundTag compoundTag2 = null; + if (((CompoundTag) tag).contains("TileEntityData", 10)) { + compoundTag2 = ((CompoundTag) tag).getCompound("TileEntityData"); + } + return new BlockValue(bs, null, null, compoundTag2); + } + } + public static class ItemParam extends Param + { + + protected ItemParam(String id) { + super(id); + } + @Override + public Value validate(Map options, MinecraftServer server, Value value) + { + ItemStack item=ValueConversions.getItemStackFromValue(value, true); + return new NBTSerializableValue(item.save(new CompoundTag())); + } + @Override + public Tag toTag(Value value) { + return ((NBTSerializableValue)value).getTag(); + } + @Override + public Value decode(Tag tag) { + return new NBTSerializableValue(tag); + } + } public static class TextParam extends StringParam { protected TextParam(String id) @@ -1211,9 +1678,9 @@ public class ShapeDispatcher { List coords = ((ListValue)value).getItems(); ListTag tag = new ListTag(); - tag.add(DoubleTag.valueOf(NumericValue.asNumber(lv.get(0), "x").getDouble())); - tag.add(DoubleTag.valueOf(NumericValue.asNumber(lv.get(1), "y").getDouble())); - tag.add(DoubleTag.valueOf(NumericValue.asNumber(lv.get(2), "z").getDouble())); + tag.add(DoubleTag.valueOf(NumericValue.asNumber(coords.get(0), "x").getDouble())); + tag.add(DoubleTag.valueOf(NumericValue.asNumber(coords.get(1), "y").getDouble())); + tag.add(DoubleTag.valueOf(NumericValue.asNumber(coords.get(2), "z").getDouble())); ltag.add(tag); } return ltag; diff --git a/src/main/java/carpet/script/utils/ShapesRenderer.java b/src/main/java/carpet/script/utils/ShapesRenderer.java index 202bd059..deb9e066 100644 --- a/src/main/java/carpet/script/utils/ShapesRenderer.java +++ b/src/main/java/carpet/script/utils/ShapesRenderer.java @@ -2,30 +2,81 @@ package carpet.script.utils; import carpet.CarpetSettings; import carpet.utils.CarpetProfiler; + +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat.Mode; +import com.mojang.datafixers.util.Either; import com.mojang.math.Vector3f; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Random; import java.util.function.BiFunction; +import java.util.function.Predicate; +import java.util.stream.Stream; + import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; +import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.GameRenderer; +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.Sheets; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.Material; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.TagKey; import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.ShulkerBoxBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.ticks.LevelTickAccess; public class ShapesRenderer { @@ -41,6 +92,9 @@ public class ShapesRenderer put("sphere", RenderedSphere::new); put("cylinder", RenderedCylinder::new); put("label", RenderedText::new); + put("polygon",RenderedPolyface::new); + put("block",(c,s)->new RenderedSprite(c,s).set_isitem(false)); + put("item",(c,s)->new RenderedSprite(c,s).set_isitem(true)); }}; public ShapesRenderer(Minecraft minecraftClient) @@ -82,6 +136,7 @@ public class ShapesRenderer double cameraX = camera.getPosition().x; double cameraY = camera.getPosition().y; double cameraZ = camera.getPosition().z; + boolean hasf3b = client.getEntityRenderDispatcher().shouldRenderHitBoxes(); if (shapes.size() != 0) { synchronized (shapes) { shapes.get(dimensionType).long2ObjectEntrySet().removeIf( @@ -95,12 +150,12 @@ public class ShapesRenderer // lines RenderSystem.lineWidth(0.5F); shapes.get(dimensionType).values().forEach( s -> { - if (s.shouldRender(dimensionType)) s.renderLines(matrices, tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); + if ( (!s.shape.needf3b ||hasf3b ) && s.shouldRender(dimensionType)) s.renderLines(matrices, tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); }); // faces RenderSystem.lineWidth(0.1F); shapes.get(dimensionType).values().forEach(s -> { - if (s.shouldRender(dimensionType)) s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); + if ( (!s.shape.needf3b ||hasf3b ) && s.shouldRender(dimensionType)) s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); }); RenderSystem.lineWidth(1.0F); matrixStack.popPose(); @@ -113,7 +168,7 @@ public class ShapesRenderer entry -> entry.getValue().isExpired(currentTime) ); labels.get(dimensionType).values().forEach(s -> { - if (s.shouldRender(dimensionType)) s.renderLines(matrices, tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); + if ( (!s.shape.needf3b || hasf3b) && s.shouldRender(dimensionType)) s.renderLines(matrices, tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick); }); }} RenderSystem.enableCull(); @@ -233,6 +288,175 @@ public class ShapesRenderer } } + public static class RenderedSprite extends RenderedShape { + + private boolean isitem; + + public RenderedSprite set_isitem(boolean isitem) { + this.isitem = isitem; + return this; + } + + private BlockPos blockPos; + private BlockState blockState; + private BlockEntity BlockEntity = null; + + protected RenderedSprite(Minecraft client, ShapeDispatcher.ExpiringShape shape) { + super(client, (ShapeDispatcher.DisplayedSprite) shape); + } + + @Override + public void renderLines(PoseStack matrices, Tesselator tessellator, BufferBuilder builder, double cx, double cy, + double cz, float partialTick) { + if (shape.a == 0.0) + return; + + Vec3 v1 = shape.relativiseRender(client.level, shape.pos, partialTick); + Camera camera1 = client.gameRenderer.getMainCamera(); + + matrices.pushPose(); + if (!isitem)// blocks should use its center as the origin + matrices.translate(0.5, 0.5, 0.5); + matrices.translate(v1.x - cx, v1.y - cy, v1.z - cz); + if (shape.facing == null) { + matrices.mulPose(camera1.rotation()); + matrices.mulPose(Vector3f.YP.rotationDegrees(180)); + } else { + switch (shape.facing) { + case NORTH: + break; + case SOUTH: + matrices.mulPose(Vector3f.YP.rotationDegrees(180)); + break; + case EAST: + matrices.mulPose(Vector3f.YP.rotationDegrees(270)); + break; + case WEST: + matrices.mulPose(Vector3f.YP.rotationDegrees(90)); + break; + case UP: + matrices.mulPose(Vector3f.XP.rotationDegrees(90)); + break; + case DOWN: + matrices.mulPose(Vector3f.XP.rotationDegrees(-90)); + break; + } + } + + if (shape.tilt != 0.0f) + matrices.mulPose(Vector3f.ZP.rotationDegrees(shape.tilt)); + if (shape.lean != 0.0f) + matrices.mulPose(Vector3f.XP.rotationDegrees(shape.lean)); + if (shape.turn != 0.0f) + matrices.mulPose(Vector3f.YP.rotationDegrees(shape.turn)); + matrices.scale(shape.scale_x, shape.scale_y, shape.scale_z); + + if (!isitem)// blocks should use its center as the origin + matrices.translate(-0.5, -0.5, -0.5); + + RenderSystem.depthMask(true); + RenderSystem.enableCull(); + RenderSystem.enableDepthTest(); + + + + blockPos = new BlockPos(v1); + int light; + light = LightTexture.pack( + shape.light_fromblock < 0 ? client.level.getBrightness(LightLayer.BLOCK, blockPos) + : shape.light_fromblock, + shape.light_fromsky < 0 ? client.level.getBrightness(LightLayer.SKY, blockPos) + : shape.light_fromsky); + + blockState = shape.blockState; + + MultiBufferSource.BufferSource immediate = client.renderBuffers().bufferSource(); + if (!isitem) { + // draw the block itself + if (blockState.getRenderShape() == RenderShape.MODEL) { + + var bakedModel = client.getBlockRenderer().getBlockModel(blockState); + int color = client.getBlockColors().getColor(blockState,client.level,blockPos,0); + //dont know why there is a 0. + //see https://github.com/senseiwells/EssentialClient/blob/4db1f291936f502304791ee323f369c206b3021d/src/main/java/me/senseiwells/essentialclient/utils/render/RenderHelper.java#L464 + float red = (color >> 16 & 0xFF) / 255.0F; + float green = (color >> 8 & 0xFF) / 255.0F; + float blue = (color & 0xFF) / 255.0F; + client.getBlockRenderer().getModelRenderer().renderModel(matrices.last(), immediate.getBuffer(ItemBlockRenderTypes.getRenderType(blockState, false)), blockState, bakedModel, red, green, blue, light, OverlayTexture.NO_OVERLAY); + } + + // draw the block`s entity part + if (BlockEntity == null) { + if (blockState.getBlock() instanceof EntityBlock eb) { + BlockEntity = eb.newBlockEntity(blockPos, blockState); + if (BlockEntity != null) { + BlockEntity.setLevel(client.level); + if (shape.blockEntity != null) + BlockEntity.load(shape.blockEntity); + } + } + } + if (BlockEntity instanceof ShulkerBoxBlockEntity sbBlockEntity){ + sbrender(sbBlockEntity, partialTick, + matrices, immediate, light, OverlayTexture.NO_OVERLAY); + }else + if (BlockEntity != null && client.getBlockEntityRenderDispatcher().getRenderer(BlockEntity) != null) { + client.getBlockEntityRenderDispatcher().getRenderer(BlockEntity).render(BlockEntity, partialTick, + matrices, immediate, light, OverlayTexture.NO_OVERLAY); + } + } else { + // draw item + if (shape.item != null) + client.getItemRenderer().renderStatic(shape.item, shape.item_transform_type, light, + OverlayTexture.NO_OVERLAY, matrices, immediate, (int) shape.key()); + } + matrices.popPose(); + immediate.endBatch(); + RenderSystem.disableCull(); + RenderSystem.disableDepthTest(); + RenderSystem.depthMask(false); + + } + + @Override + public boolean stageDeux() { + return true; + } + + // copy and modifiy a bit from net.minecraft.client.renderer.blockentity.ShulkerBoxRenderer.render + public void sbrender(ShulkerBoxBlockEntity shulkerBoxBlockEntity, float f, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j) { + Direction direction = Direction.UP; + if (shulkerBoxBlockEntity.hasLevel()) { + BlockState blockState = shulkerBoxBlockEntity.getBlockState(); + if (blockState.getBlock() instanceof ShulkerBoxBlock) { + direction = blockState.getValue(ShulkerBoxBlock.FACING); + } + } + DyeColor dyeColor = shulkerBoxBlockEntity.getColor(); + Material material; + if (dyeColor == null) { + material = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION; + } else { + material = (Material)Sheets.SHULKER_TEXTURE_LOCATION.get(dyeColor.getId()); + } + + poseStack.pushPose(); + poseStack.translate(0.5, 0.5, 0.5); + float g = 0.9995F; + poseStack.scale(0.9995F, 0.9995F, 0.9995F); + poseStack.mulPose(direction.getRotation()); + poseStack.scale(1.0F, -1.0F, -1.0F); + poseStack.translate(0.0, -1.0, 0.0); + ModelPart modelPart = ((carpet.mixins.ShulkerBoxAccessMixin)(Object)(client.getBlockEntityRenderDispatcher().getRenderer(BlockEntity))).getModel().getLid(); + modelPart.setPos(0.0F, 24.0F - shulkerBoxBlockEntity.getProgress(f) * 0.5F * 16.0F, 0.0F); + modelPart.yRot = 270.0F * shulkerBoxBlockEntity.getProgress(f) * (float) (Math.PI / 180.0); + VertexConsumer vertexConsumer = material.buffer(multiBufferSource, RenderType::entityCutoutNoCull); + ((carpet.mixins.ShulkerBoxAccessMixin)(Object)(client.getBlockEntityRenderDispatcher().getRenderer(BlockEntity))).getModel().renderToBuffer(poseStack, vertexConsumer, i, j, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + } + } + + public static class RenderedText extends RenderedShape { @@ -386,7 +610,140 @@ public class ShapesRenderer ); } } + public static class RenderedPolyface extends RenderedShape + { + public RenderedPolyface(Minecraft client, ShapeDispatcher.ExpiringShape shape) + { + super(client, (ShapeDispatcher.Polyface)shape); + } + @Override + public void renderFaces(Tesselator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick) + { + if(shape.fa==0){return;} + if (shape.doublesided) + RenderSystem.disableCull(); + else + RenderSystem.enableCull(); + + bufferBuilder.begin(shape.mode, DefaultVertexFormat.POSITION_COLOR); + for(int i=0;i0;i-=2){ + vec=shape.vertex_list.get(i); + if(shape.relative.get(i)){ + vec=shape.relativiseRender(client.level, vec, partialTick); + } + builder.vertex(vec.x()-cx, vec.y()-cy, vec.z()-cz).color(shape.r, shape.g, shape.b, shape.a).endVertex(); + } + if(shape.inneredges){ + for(i=2;i { public RenderedSphere(Minecraft client, ShapeDispatcher.ExpiringShape shape) diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 502c2e1b..3fd7fb06 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -189,6 +189,7 @@ "SlimeBlock_customStickyMixin" ], "client": [ + "ShulkerBoxAccessMixin", "Timer_tickSpeedMixin", "MinecraftMixin", "Minecraft_pausedShakeMixin",