render item frame contents with the item geometry like vanilla ItemFrameRenderer (fixed context, 8 rotations, all facings), frame block models, map contents incl. pixelpics photos, invisible frames, head skins/banner patterns from item meta

This commit is contained in:
MineTec committed 2026-11-04 16:03:34 +01:00
1 parent a743dc32c7
commit 2d56a1910c
13 files changed
+408 -96

No files matched your search

+138 -1
View File
@@ -13,6 +13,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Bed;
import org.bukkit.command.Command;
@@ -128,6 +129,13 @@ public class PPTest extends JavaPlugin {
// Needs a datapack cat variant "pptest:ocelotcat" (asset_id entity/cat/ocelot) in the world's datapacks.
scenes.put("rf_dpcat", new Scene("rf_dpcat", this::rfDatapackCat, 1.2, 1.4, -2.2, 1.2, 0.2, 0.5));
scenes.put("rf_armor", new Scene("rf_armor", this::rfArmor, 7, 3.2, -6.5, 7, 1.0, 1.5));
// Item frames (vanilla ItemFrameRenderer): north wall with rotations 0-7, block/flat/tool/block-entity
// items, photo + synthetic + vanilla-data maps, invisible and glow frames; frames on all 6 block sides.
scenes.put("frames", new Scene("frames", this::frames, 4.5, 2.8, -3.5, 4.5, 2.6, 4));
scenes.put("frames_close", new Scene("frames_close", this::frames, 2.6, 2.4, 0.8, 2.6, 2.3, 4));
scenes.put("frames_side", new Scene("frames_side", this::frames, -1.5, 3.2, -1.0, 4.5, 2.6, 3.6));
scenes.put("frames_dirs_ne", new Scene("frames_dirs_ne", this::framesDirs, 16, 7.5, -4, 8, 4, 3.5));
scenes.put("frames_dirs_sw", new Scene("frames_dirs_sw", this::framesDirs, 0, 1.2, 10, 8, 4, 3.5));
}
@Override
@@ -165,7 +173,7 @@ public class PPTest extends JavaPlugin {
Location eye = o.clone().add(sc.camX, sc.camY, sc.camZ);
Vector look = o.clone().add(sc.lookX, sc.lookY, sc.lookZ).toVector().subtract(eye.toVector());
eye.setDirection(look);
RenderJob job = r.prepare(eye, sc.name.startsWith("proj_") || sc.name.startsWith("rf_") ? new Resolution(960, 640) : new Resolution(480, 320), null);
RenderJob job = r.prepare(eye, sc.name.startsWith("proj_") || sc.name.startsWith("rf_") || sc.name.startsWith("frames") ? new Resolution(960, 640) : new Resolution(480, 320), null);
if (sc.name.equals("dbg")) {
try {
java.lang.reflect.Field f = DefaultScreenRenderer.class.getDeclaredField("objectBaker");
@@ -1098,4 +1106,133 @@ public class PPTest extends JavaPlugin {
b.setBlock(Bukkit.createBlockData("minecraft:" + blocks[i]));
}
}
// --- item frames -------------------------------------------------------------------------------
private void frames(Location o) {
for (int x = -2; x <= 11; x++)
for (int y = 0; y <= 5; y++) w.getBlockAt(o.getBlockX() + x, o.getBlockY() + y, o.getBlockZ() + 4).setType(Material.STONE, false);
BlockFace f = BlockFace.NORTH;
for (int r = 0; r < 8; r++) frame(o, r, 4, 3, f, false, it(Material.DIAMOND_SWORD), r, true);
ItemStack banner = it(Material.RED_BANNER);
org.bukkit.inventory.meta.BannerMeta bm = (org.bukkit.inventory.meta.BannerMeta) banner.getItemMeta();
bm.addPattern(new org.bukkit.block.banner.Pattern(DyeColor.YELLOW, org.bukkit.block.banner.PatternType.STRIPE_TOP));
banner.setItemMeta(bm);
ItemStack[] row3 = {it(Material.STONE), it(Material.OAK_STAIRS), it(Material.GRASS_BLOCK), it(Material.APPLE),
it(Material.TORCH), it(Material.CHEST), banner, eu.mhsl.minecraft.pixelpics.survival.CameraItems.createCamera(3),
it(Material.CREEPER_HEAD), it(Material.SHIELD)};
for (int i = 0; i < row3.length; i++) frame(o, i - 1, 3, 3, f, false, row3[i], 0, true);
ItemStack photo = photoMap(), synth = synthMap(), vmap = vanillaMap();
int[] photoRot = {0, 1, 2, 3, 5};
for (int i = 0; i < photoRot.length; i++) frame(o, i - 1, 2, 3, f, false, photo, photoRot[i], true);
frame(o, 4, 2, 3, f, false, synth, 0, true);
frame(o, 5, 2, 3, f, false, synth, 1, true);
frame(o, 6, 2, 3, f, false, vmap, 0, true);
frame(o, 7, 2, 3, f, false, it(Material.MAP), 0, true);
frame(o, 8, 2, 3, f, false, it(Material.FILLED_MAP), 0, true);
ItemStack[] inv = {it(Material.DIAMOND_SWORD), it(Material.STONE), synth, it(Material.CHEST)};
for (int i = 0; i < inv.length; i++) frame(o, i - 1, 1, 3, f, false, inv[i], i == 0 ? 1 : 0, false);
ItemStack[] glow = {it(Material.APPLE), it(Material.STONE), synth, null};
for (int i = 0; i < glow.length; i++) frame(o, i + 3, 1, 3, f, true, glow[i], 0, true);
frame(o, 7, 1, 3, f, false, null, 0, true);
frame(o, 8, 1, 3, f, true, null, 0, true);
}
/** Three stone blocks with a frame on every side: block item, tool, synthetic map (all at rotation 1). */
private void framesDirs(Location o) {
ItemStack[] items = {it(Material.OAK_STAIRS), it(Material.DIAMOND_SWORD), synthMap()};
BlockFace[] faces = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN};
for (int i = 0; i < items.length; i++) {
int bx = 3 + i * 5, by = 4, bz = 3;
w.getBlockAt(o.getBlockX() + bx, o.getBlockY() + by, o.getBlockZ() + bz).setType(Material.STONE, false);
for (BlockFace face : faces)
frame(o, bx + face.getModX(), by + face.getModY(), bz + face.getModZ(), face, i == 2, items[i], 1, true);
}
}
private ItemFrame frame(Location o, int x, int y, int z, BlockFace face, boolean glow, ItemStack item, int rot, boolean visible) {
Location l = o.clone().add(x + 0.5, y + 0.5, z + 0.5);
Class<? extends ItemFrame> c = glow ? GlowItemFrame.class : ItemFrame.class;
ItemFrame fr = w.spawn(l, c, e -> {
e.setFacingDirection(face, true);
e.setPersistent(false);
});
if (item != null) fr.setItem(item, false);
fr.setRotation(Rotation.values()[rot]);
fr.setVisible(visible);
fr.setFixed(true);
return fr;
}
private static ItemStack mapItem(org.bukkit.map.MapView view) {
ItemStack s = it(Material.FILLED_MAP);
org.bukkit.inventory.meta.MapMeta mm = (org.bukkit.inventory.meta.MapMeta) s.getItemMeta();
mm.setMapView(view);
s.setItemMeta(mm);
return s;
}
/** A real stored PixelPics photo (lowest managed map id on disk), or the synthetic map if none exists. */
private ItemStack photoMap() {
File dir = new File(Main.getInstance().getDataFolder(), "images");
int best = Integer.MAX_VALUE;
File[] files = dir.listFiles();
if (files != null)
for (File f : files) {
java.util.regex.Matcher m = java.util.regex.Pattern.compile("mapId_(\\d+)\\.mcmap").matcher(f.getName());
if (m.matches()) best = Math.min(best, Integer.parseInt(m.group(1)));
}
int id = best == Integer.MAX_VALUE ? -1 : best;
@SuppressWarnings("deprecation") org.bukkit.map.MapView v = id < 0 ? null : Bukkit.getMap(id);
if (v == null) return synthMap();
if (v.getRenderers().stream().noneMatch(r -> r instanceof eu.mhsl.minecraft.pixelpics.utils.ImageMapRenderer)) {
byte[] idx = eu.mhsl.minecraft.pixelpics.utils.MapManager.readIndices(id);
if (idx != null) eu.mhsl.minecraft.pixelpics.utils.MapManager.attachView(v, new eu.mhsl.minecraft.pixelpics.utils.ImageMapRenderer(idx));
}
getLogger().info("frames: photo map id " + id);
return mapItem(v);
}
private ItemStack synthMap;
/** Asymmetric test picture as a PixelPics map: red top-left, blue top-right, green bottom-left, black "F". */
private ItemStack synthMap() {
if (synthMap != null) return synthMap;
BufferedImage img = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB);
java.awt.Graphics2D g = img.createGraphics();
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, 128, 128);
g.setColor(java.awt.Color.RED);
g.fillRect(0, 0, 32, 32);
g.setColor(java.awt.Color.BLUE);
g.fillRect(96, 0, 32, 32);
g.setColor(new java.awt.Color(0, 160, 0));
g.fillRect(0, 96, 32, 32);
g.setColor(java.awt.Color.BLACK);
g.fillRect(48, 24, 14, 80);
g.fillRect(48, 24, 44, 14);
g.fillRect(48, 56, 32, 12);
g.dispose();
org.bukkit.map.MapView v = Bukkit.createMap(w);
eu.mhsl.minecraft.pixelpics.utils.MapManager.attachView(v, new eu.mhsl.minecraft.pixelpics.utils.ImageMapRenderer(img));
return synthMap = mapItem(v);
}
/** A plain vanilla map whose server-side colours are written directly (diagonal colour bands + transparent corner). */
private ItemStack vanillaMap() {
org.bukkit.map.MapView v = Bukkit.createMap(w);
try {
java.lang.reflect.Field wm = v.getClass().getDeclaredField("worldMap");
wm.setAccessible(true);
Object data = wm.get(v);
byte[] colors = (byte[]) data.getClass().getField("colors").get(data);
byte[] palette = {(byte) (18 * 4 + 2), (byte) (7 * 4 + 2), (byte) (12 * 4 + 1), (byte) (29 * 4 + 2)};
for (int y = 0; y < 128; y++)
for (int x = 0; x < 128; x++)
colors[y * 128 + x] = x < 24 && y < 24 ? 0 : palette[((x + y) / 32) % palette.length];
} catch (ReflectiveOperationException e) {
getLogger().warning("vanillaMap: " + e);
}
return mapItem(v);
}
}