render all block entities from the dumped vanilla models with decompiled renderer poses, drop the cem block-entity path

This commit is contained in:
MineTec committed 2026-11-04 15:13:55 +01:00
1 parent c66ebaa942
commit 022b080fce
14 files changed
+659 -567

No files matched your search

+93 -51
View File
@@ -33,14 +33,20 @@ public class BlockEntityTestRender {
static BlockEntityBaker beBaker;
static DecorationBaker decoBaker;
static BlockEntityState be(Kind kind, int bx, int by, int bz, float yaw) {
return new BlockEntityState(kind, bx, by, bz, yaw, ChestKind.SINGLE, 0, null, "oak",
null, null, null, List.of(), List.of(), null, null, null);
/** A block-entity state at (bx, 0, bz); unused fields default. */
static BlockEntityState st(Kind kind, int bx, int bz, float yaw, String direction, boolean active, String color,
int baseColor, String head, List<BannerPattern> pats, List<String> sherds) {
return new BlockEntityState(kind, bx, 0, bz, yaw, direction, active, ChestKind.SINGLE, baseColor, color,
head, null, pats, sherds, null, null);
}
static BlockEntityState be(Kind kind, float yaw) {
return st(kind, 0, 0, yaw, null, false, null, 0, null, List.of(), List.of());
}
static BlockEntityState sign(Kind kind, float yaw, SignText front, SignText back) {
return new BlockEntityState(kind, 0, 0, 0, yaw, null, 0, null, "oak",
null, null, null, List.of(), List.of(), null, front, back);
return new BlockEntityState(kind, 0, 0, 0, yaw, null, false, null, 0, null, null, null,
List.of(), List.of(), front, back);
}
static SignText txt(org.bukkit.DyeColor dye, boolean glow, String... lines) {
@@ -57,7 +63,7 @@ public class BlockEntityTestRender {
BlockModelRegistry registry = new BlockModelRegistry(reader, textures);
BiomeTintProvider tint = new BiomeTintProvider(textures);
CemModelLoader geo = new CemModelLoader();
geo.load(new java.io.FileInputStream("src/main/resources/cem/cem_template_models.json"), log);
geo.load(new java.io.FileInputStream("src/main/resources/cem/cem_template_models.json"), log); // entity fallback only
geo.load(new java.io.FileInputStream("src/main/resources/cem/vanilla_entity_models.json"), log);
SkinCache skins = new SkinCache();
eu.mhsl.minecraft.pixelpics.assets.font.BitmapFont font =
@@ -113,49 +119,86 @@ public class BlockEntityTestRender {
chest(Kind.COPPER_CHEST, 0, 0, 0, ChestKind.LEFT, "oxidized"),
chest(Kind.COPPER_CHEST, -1, 0, 0, ChestKind.RIGHT, "oxidized")));
frontCam.put("copper_oxidized_double", 0f);
scenes.put("sign", List.of(be(Kind.SIGN, 0, 0, 0, 0)));
scenes.put("wall_sign", List.of(be(Kind.WALL_SIGN, 0, 0, 0, 0)));
scenes.put("hanging_sign", List.of(be(Kind.HANGING_SIGN, 0, 0, 0, 0)));
// --- sign text (calibration) --- facingDeg=180 → front (model −Z) faces the camera (north).
// --- sign text (boards are block models, not drawn here) --- facingDeg 180 → front faces north.
org.bukkit.DyeColor B = org.bukkit.DyeColor.BLACK;
scenes.put("sign_text", List.of(sign(Kind.SIGN, 180,
txt(B, false, "Hello World", "zweite Zeile", "Gruesse: AÖÜ", "ßäöü 12345"), null)));
scenes.put("wall_sign_text", List.of(sign(Kind.WALL_SIGN, 180,
txt(B, false, "Wall Sign", "Line 2", "Line 3", "Line 4"), null)));
scenes.put("hanging_sign_text", List.of(sign(Kind.HANGING_SIGN, 180,
txt(B, false, "Hanging", "sign text", "row 3", "row 4"), null)));
scenes.put("sign_text_red", List.of(sign(Kind.SIGN, 180,
txt(org.bukkit.DyeColor.RED, false, "RED INK", "colored", "sign text", ""), null)));
scenes.put("sign_text_glow", List.of(sign(Kind.SIGN, 180,
txt(org.bukkit.DyeColor.LIME, true, "GLOWING", "lime glow", "outline!", ""), null)));
scenes.put("sign_text_front", List.of(sign(Kind.SIGN, 180,
txt(B, false, "FRONT", "side", "", ""), txt(org.bukkit.DyeColor.BLUE, false, "BACK", "side", "", ""))));
// View the BACK side head-on (facingDeg=0 turns the back face toward the camera).
scenes.put("sign_view_back", List.of(sign(Kind.SIGN, 0,
txt(B, false, "FRONTXY", "f2", "", ""), txt(org.bukkit.DyeColor.BLUE, false, "BACKXY", "b2", "", ""))));
String[] D = {"S", "W", "N", "E"};
for (int i = 0; i < 4; i++) {
float yaw = i * 90;
scenes.put("sign_text_" + D[i], List.of(sign(Kind.SIGN, yaw, txt(B, false, "Hello World", "zweite Zeile", "Gruesse: AÖÜ", "ßäöü 12345"), null)));
frontCam.put("sign_text_" + D[i], yaw);
}
scenes.put("sign_text_diag", List.of(sign(Kind.SIGN, 22.5f * 3, txt(B, false, "rot 3", "67.5 deg", "", ""), null)));
frontCam.put("sign_text_diag", 22.5f * 3);
scenes.put("wall_sign_text", List.of(sign(Kind.WALL_SIGN, 180, txt(B, false, "Wall Sign", "Line 2", "Line 3", "Line 4"), null)));
frontCam.put("wall_sign_text", 180f);
scenes.put("wall_sign_text_E", List.of(sign(Kind.WALL_SIGN, 270, txt(B, false, "Wall East", "Line 2", "", ""), null)));
frontCam.put("wall_sign_text_E", 270f);
scenes.put("hanging_sign_text", List.of(sign(Kind.HANGING_SIGN, 180, txt(B, false, "Hanging", "sign text", "row 3", "row 4"), null)));
frontCam.put("hanging_sign_text", 180f);
scenes.put("sign_text_red", List.of(sign(Kind.SIGN, 180, txt(org.bukkit.DyeColor.RED, false, "RED INK", "colored", "sign text", ""), null)));
frontCam.put("sign_text_red", 180f);
scenes.put("sign_text_glow", List.of(sign(Kind.SIGN, 180, txt(org.bukkit.DyeColor.LIME, true, "GLOWING", "lime glow", "outline!", ""), null)));
frontCam.put("sign_text_glow", 180f);
// Back side viewed head-on: the camera looks at the side opposite the front.
scenes.put("sign_view_back", List.of(sign(Kind.SIGN, 180, txt(B, false, "FRONTXY", "f2", "", ""), txt(org.bukkit.DyeColor.BLUE, false, "BACKXY", "b2", "", ""))));
frontCam.put("sign_view_back", 0f);
scenes.put("sign_1line", List.of(sign(Kind.SIGN, 180, txt(B, false, "", "SHOP", "", ""), null)));
scenes.put("sign_2line", List.of(sign(Kind.SIGN, 180, txt(B, false, "", "Welcome", "home!", ""), null)));
frontCam.put("sign_1line", 180f);
scenes.put("sign_long", List.of(sign(Kind.SIGN, 180, txt(B, false, "a very long line here", "", "", ""), null)));
scenes.put("banner", List.of(new BlockEntityState(Kind.BANNER, 0, 0, 0, 0, null, 0xFFCC2020, null, null, null, null, null, List.of(), List.of(), null, null, null)));
scenes.put("banner_patterned", List.of(new BlockEntityState(Kind.BANNER, 0, 0, 0, 0, null, 0xFFFFFFFF, null, null, null, null, null,
List.of(new BannerPattern("stripe_bottom", 0xFFCC2020), new BannerPattern("cross", 0xFF2040CC), new BannerPattern("border", 0xFF20A020)), List.of(), null, null, null)));
scenes.put("wall_banner", List.of(new BlockEntityState(Kind.WALL_BANNER, 0, 0, 0, 0, null, 0xFF2040CC, null, null, null, null, null, List.of(), List.of(), null, null, null)));
scenes.put("bed", List.of(
new BlockEntityState(Kind.BED, 0, 0, 0, 0, null, 0, "red", null, BedPart.FOOT, null, null, List.of(), List.of(), null, null, null),
new BlockEntityState(Kind.BED, 0, 0, 1, 0, null, 0, "red", null, BedPart.HEAD, null, null, List.of(), List.of(), null, null, null)));
scenes.put("shulker_box", List.of(new BlockEntityState(Kind.SHULKER_BOX, 0, 0, 0, 0, null, 0, null, null, null, null, null, List.of(), List.of(), null, null, null)));
scenes.put("conduit", List.of(be(Kind.CONDUIT, 0, 0, 0, 0)));
scenes.put("decorated_pot", List.of(be(Kind.DECORATED_POT, 0, 0, 0, 0)));
scenes.put("decorated_pot_sherds", List.of(new BlockEntityState(Kind.DECORATED_POT, 0, 0, 0, 0, null, 0, null, null, null, null, null,
List.of(), List.of("angler_pottery_sherd", "heart_pottery_sherd", "skull_pottery_sherd", "brick"), null, null, null)));
scenes.put("bell", List.of(be(Kind.BELL, 0, 0, 0, 0)));
scenes.put("head_skeleton", List.of(head("skeleton")));
scenes.put("head_zombie", List.of(head("zombie")));
scenes.put("head_creeper", List.of(head("creeper")));
scenes.put("head_dragon", List.of(head("dragon")));
scenes.put("head_piglin", List.of(head("piglin")));
scenes.put("wither_skull", List.of(headKind(Kind.HEAD, "wither_skeleton")));
scenes.put("wall_head", List.of(headKind(Kind.WALL_HEAD, "skeleton")));
frontCam.put("sign_long", 180f);
// --- banners: standing (rotation 0/4/6), wall (4 facings), patterned ---
List<BannerPattern> pats = List.of(new BannerPattern("stripe_bottom", 0xFFCC2020), new BannerPattern("cross", 0xFF2040CC), new BannerPattern("border", 0xFF20A020));
for (float yaw : new float[] {0, 90, 135}) {
String k = "banner_" + (int) yaw;
scenes.put(k, List.of(st(Kind.BANNER, 0, 0, yaw, null, false, null, 0xFFCC2020, null, List.of(), List.of())));
frontCam.put(k, yaw);
}
scenes.put("banner_patterned", List.of(st(Kind.BANNER, 0, 0, 0, null, false, null, 0xFFFFFFFF, null, pats, List.of())));
frontCam.put("banner_patterned", 0f);
scenes.put("banner_side", scenes.get("banner_patterned"));
frontCam.put("banner_side", 90f);
for (int i = 0; i < 4; i++) {
scenes.put("wall_banner_" + D[i], List.of(st(Kind.WALL_BANNER, 0, 0, i * 90, null, false, null, 0xFF2040CC, null, pats, List.of())));
frontCam.put("wall_banner_" + D[i], i * 90f);
}
// --- shulker boxes: 6 facings ---
for (String d : new String[] {"up", "down", "north", "south", "east", "west"}) {
scenes.put("shulker_" + d, List.of(st(Kind.SHULKER_BOX, 0, 0, 0, d, false, d.equals("up") ? null : "red", 0, null, List.of(), List.of())));
frontCam.put("shulker_" + d, 0f);
}
scenes.put("conduit", List.of(be(Kind.CONDUIT, 0)));
scenes.put("conduit_active", List.of(st(Kind.CONDUIT, 0, 0, 0, null, true, null, 0, null, List.of(), List.of())));
List<String> sherds = List.of("heart_pottery_sherd", "skull_pottery_sherd", "angler_pottery_sherd", "brick");
for (int i = 0; i < 4; i++) {
scenes.put("pot_" + D[i], List.of(st(Kind.DECORATED_POT, 0, 0, i * 90, null, false, null, 0, null, List.of(), sherds)));
frontCam.put("pot_" + D[i], i * 90f);
}
scenes.put("pot_plain", List.of(be(Kind.DECORATED_POT, 0)));
frontCam.put("pot_plain", 0f);
scenes.put("bell", List.of(be(Kind.BELL, 0)));
for (int i = 0; i < 4; i++) {
scenes.put("lectern_book_" + D[i], List.of(st(Kind.LECTERN, 0, 0, i * 90, null, true, null, 0, null, List.of(), List.of())));
frontCam.put("lectern_book_" + D[i], i * 90f);
}
// --- heads: every type facing the camera (rotation 0), plus rotations and wall mounts ---
for (String t : new String[] {"skeleton", "wither_skeleton", "zombie", "creeper", "player", "piglin", "dragon"}) {
scenes.put("head_" + t, List.of(head(Kind.HEAD, t, 0)));
frontCam.put("head_" + t, 0f);
}
scenes.put("head_dragon_side", List.of(head(Kind.HEAD, "dragon", 0)));
frontCam.put("head_dragon_side", 90f);
for (float yaw : new float[] {90, 157.5f, 270}) {
scenes.put("head_creeper_" + yaw, List.of(head(Kind.HEAD, "creeper", yaw)));
frontCam.put("head_creeper_" + yaw, yaw);
}
for (int i = 0; i < 4; i++) {
scenes.put("wall_head_" + D[i], List.of(head(Kind.WALL_HEAD, "skeleton", i * 90)));
frontCam.put("wall_head_" + D[i], i * 90f);
}
scenes.put("wall_dragon_N", List.of(head(Kind.WALL_HEAD, "dragon", 180)));
frontCam.put("wall_dragon_N", 180f);
scenes.put("wall_piglin_E", List.of(head(Kind.WALL_HEAD, "piglin", 270)));
frontCam.put("wall_piglin_E", 270f);
// Decoration scenes (paintings + item frames), rendered via the same scene path.
LinkedHashMap<String, List<DecorationState>> decoScenes = new LinkedHashMap<>();
@@ -206,13 +249,12 @@ public class BlockEntityTestRender {
static final Map<String, Float> frontCam = new HashMap<>();
static BlockEntityState chest(Kind kind, int bx, int bz, float yaw, ChestKind ck, String variant) {
return new BlockEntityState(kind, bx, 0, bz, yaw, ck, 0, variant, null, null, null, null,
List.of(), List.of(), null, null, null);
return new BlockEntityState(kind, bx, 0, bz, yaw, null, false, ck, 0, variant, null, null,
List.of(), List.of(), null, null);
}
static BlockEntityState head(String type) { return headKind(Kind.HEAD, type); }
static BlockEntityState headKind(Kind kind, String type) {
return new BlockEntityState(kind, 0, 0, 0, 0, null, 0, null, null, null, type, null, List.of(), List.of(), null, null, null);
static BlockEntityState head(Kind kind, String type, float yaw) {
return st(kind, 0, 0, yaw, null, false, null, 0, type, List.of(), List.of());
}
static BufferedImage renderScene(DefaultScreenRenderer renderer, WorldSnapshot world, SkyContext sky,
+43
View File
@@ -156,6 +156,29 @@ public class VanillaModelDump {
failed.add(name + " (layer: " + t + ")");
}
}
// Block-entity models posed by their own setupAnim with the static state the vanilla
// BlockEntityRenderer uses ("posed/<layer>"): skulls at animation 0 (dragon jaw, piglin ears), banner
// flags at wave phase 0 (item/static pose), the lectern's open book (LecternRenderer.BOOK_STATE).
// Rest-pose-only models (chest, bell, shulker box, pot, conduit) use "layer/<layer>" directly.
posedBlockEntity(out, failed, set, net.minecraft.client.model.geom.ModelLayers.DRAGON_SKULL,
m -> new net.minecraft.client.model.object.skull.DragonHeadModel(m),
new net.minecraft.client.model.object.skull.SkullModelBase.State());
posedBlockEntity(out, failed, set, net.minecraft.client.model.geom.ModelLayers.PIGLIN_HEAD,
m -> new net.minecraft.client.model.object.skull.PiglinHeadModel(m),
new net.minecraft.client.model.object.skull.SkullModelBase.State());
posedBlockEntity(out, failed, set, net.minecraft.client.model.geom.ModelLayers.STANDING_BANNER_FLAG,
m -> new net.minecraft.client.model.object.banner.BannerFlagModel(m), 0.0F);
posedBlockEntity(out, failed, set, net.minecraft.client.model.geom.ModelLayers.WALL_BANNER_FLAG,
m -> new net.minecraft.client.model.object.banner.BannerFlagModel(m), 0.0F);
try {
Field bookState = Class.forName("net.minecraft.client.renderer.blockentity.LecternRenderer")
.getDeclaredField("BOOK_STATE");
bookState.setAccessible(true);
posedBlockEntity(out, failed, set, net.minecraft.client.model.geom.ModelLayers.BOOK,
m -> new net.minecraft.client.model.object.book.BookModel(m), bookState.get(null));
} catch (ReflectiveOperationException e) {
failed.add("posed/book (" + e + ")");
}
JsonObject root = new JsonObject();
root.addProperty("version", SharedConstants.getCurrentVersion().name());
JsonObject models = new JsonObject();
@@ -171,6 +194,26 @@ public class VanillaModelDump {
for (String f : failed) System.out.println("FAILED " + f);
}
/** One block-entity model posed with {@code state} (see main), stored as {@code posed/<model>#<layer>}. */
static void posedBlockEntity(
Map<String, JsonObject> out,
List<String> failed,
EntityModelSet set,
ModelLayerLocation loc,
java.util.function.Function<ModelPart, Model<?>> factory,
Object state) {
String name = "posed/" + loc.model().getPath() + "#" + loc.layer();
try {
JsonObject jem = dump(factory.apply(set.bakeLayer(loc)), state, name, failed);
if (jem == null) return;
JsonObject entry = new JsonObject();
entry.addProperty("model", new Gson().toJson(jem));
out.put(name, entry);
} catch (Throwable t) {
failed.add(name + " (" + t + ")");
}
}
/**
* Small, name-based tweaks so the default render state shows the typical idle look: illagers stand
* with crossed arms (their peaceful pose) instead of the empty-handed "neutral" pose.
+143
View File
@@ -91,6 +91,31 @@ public class PPTest extends JavaPlugin {
scenes.put("layers_top", new Scene("layers_top", this::layers, 5.5, 6, 2.0, 5.5, 0, 2.5));
scenes.put("layers_wither", new Scene("layers_wither", this::withers, 3.5, 4, -9, 3.5, 2.2, 1));
scenes.put("layers_wither_close", new Scene("layers_wither_close", this::withers, 6, 3.4, -3.5, 6, 2.6, 1));
// Block entities (vanilla BlockEntityRenderer poses): signs, banners, heads in all 16 rotations + wall
// mounts, and bells/pots/shulker boxes/lecterns/conduits, each layout seen from two sides.
scenes.put("be_signs_s", new Scene("be_signs_s", this::beSigns, 7, 3, 8, 7, 0.5, 1));
scenes.put("be_signs_n", new Scene("be_signs_n", this::beSigns, 7, 1.3, -8, 7, 0.6, 1));
scenes.put("be_signs_close", new Scene("be_signs_close", this::beSigns, 3, 1.8, 4.2, 3, 0.8, 2));
scenes.put("be_signs_wall", new Scene("be_signs_wall", this::beSigns, 4.6, 2.2, 10.2, 3.5, 0.9, 7));
scenes.put("be_signs_wall_n", new Scene("be_signs_wall_n", this::beSigns, 1.4, 2.2, 3.8, 3.5, 0.9, 7));
scenes.put("be_signs_wall2", new Scene("be_signs_wall2", this::beSigns, 12.5, 2.4, 10.2, 10, 1.2, 7));
scenes.put("be_signs_hang", new Scene("be_signs_hang", this::beSigns, 4, 1.3, 0.2, 4, 1.4, -3));
scenes.put("be_banners_s", new Scene("be_banners_s", this::beBanners, 7, 3, 10, 7, 1, 1.5));
scenes.put("be_banners_n", new Scene("be_banners_n", this::beBanners, 7, 3, -6, 7, 1, 1.5));
scenes.put("be_banners_wall", new Scene("be_banners_wall", this::beBanners, 13, 3.5, 9, 16, 1.5, 5));
scenes.put("be_banners_wall_n", new Scene("be_banners_wall_n", this::beBanners, 13, 3.5, 1, 16, 1.5, 5));
scenes.put("be_heads_s", new Scene("be_heads_s", this::beHeads, 5, 5, 11, 5, 0, 3));
scenes.put("be_heads_n", new Scene("be_heads_n", this::beHeads, 5, 5, -6, 5, 0, 3));
scenes.put("be_heads_close", new Scene("be_heads_close", this::beHeads, 4, 2.2, 9.5, 4, 0.3, 5));
scenes.put("be_heads_wall_w", new Scene("be_heads_wall_w", this::beHeads, 7, 3, 2.5, 12, 1, 2.5));
scenes.put("be_heads_wall_e", new Scene("be_heads_wall_e", this::beHeads, 17, 3, 2.5, 12, 1, 2.5));
scenes.put("be_misc_s", new Scene("be_misc_s", this::beMisc, 6, 5, 11, 6, 0.5, 3));
scenes.put("be_misc_n", new Scene("be_misc_n", this::beMisc, 6, 5, -6, 6, 0.5, 3));
scenes.put("be_misc_close", new Scene("be_misc_close", this::beMisc, 3, 2.5, 3, 3, 0.5, 0));
scenes.put("be_misc_lectern", new Scene("be_misc_lectern", this::beMisc, 3, 2.2, 9.5, 3, 0.6, 6));
scenes.put("be_misc_shulker", new Scene("be_misc_shulker", this::beMisc, 11, 2.8, 9.5, 11, 0.3, 4));
scenes.put("be_items", new Scene("be_items", this::beItems, 4.5, 2.6, 11, 4.5, 0.8, 3));
scenes.put("be_items_back", new Scene("be_items_back", this::beItems, 4.5, 2.6, -5, 4.5, 0.8, 3));
scenes.put("proj_stuck_sw", new Scene("proj_stuck_sw", this::projStuck, -3, 6, 10, 8, 1.5, 2));
}
@@ -741,4 +766,122 @@ public class PPTest extends JavaPlugin {
}
}
}
// --- block-entity scenes ------------------------------------------------------------------------
private void put(Location o, int x, int y, int z, String data) {
o.clone().add(x, y, z).getBlock().setBlockData(Bukkit.createBlockData("minecraft:" + data), false);
}
private void signText(Location o, int x, int y, int z, String front, String back) {
if (!(o.clone().add(x, y, z).getBlock().getState() instanceof org.bukkit.block.Sign sg)) return;
sg.getSide(org.bukkit.block.sign.Side.FRONT).setLine(1, front);
sg.getSide(org.bukkit.block.sign.Side.BACK).setLine(1, back);
sg.getSide(org.bukkit.block.sign.Side.BACK).setColor(DyeColor.BLUE);
sg.update(true, false);
}
private static final String[] DIRS = {"north", "east", "south", "west"};
private static final int[][] STEP = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
/** A stone pillar with one wall-mounted block on each of its four sides ({@code %s} = facing). */
private void pillar(Location o, int x, int z, int y, String fmt) {
put(o, x, y, z, "stone");
for (int i = 0; i < 4; i++) put(o, x + STEP[i][0], y, z + STEP[i][1], String.format(fmt, DIRS[i]));
}
/** Standing signs rot 0..15 (two rows), ceiling hanging signs rot 0..15, wall + wall-hanging signs. */
private void beSigns(Location o) {
for (int r = 0; r < 16; r++) {
int x = (r % 8) * 2, z = r < 8 ? 0 : 2;
put(o, x, 0, z, "oak_sign[rotation=" + r + "]");
signText(o, x, 0, z, "F" + r, "B" + r);
put(o, r, 2, -3, "stone");
put(o, r, 1, -3, "spruce_hanging_sign[rotation=" + r + ",attached=true]");
signText(o, r, 1, -3, "F" + r, "B" + r);
}
pillar(o, 3, 7, 0, "birch_wall_sign[facing=%s]");
pillar(o, 3, 7, 1, "cherry_wall_hanging_sign[facing=%s]");
for (int i = 0; i < 4; i++) {
signText(o, 3 + STEP[i][0], 0, 7 + STEP[i][1], DIRS[i], "back");
signText(o, 3 + STEP[i][0], 1, 7 + STEP[i][1], DIRS[i], "back");
}
pillar(o, 10, 7, 1, "dark_oak_wall_sign[facing=%s]");
for (int i = 0; i < 4; i++) signText(o, 10 + STEP[i][0], 1, 7 + STEP[i][1], DIRS[i].toUpperCase(), "");
}
/** Standing banners rot 0..15 with patterns, wall banners on a pillar. */
private void beBanners(Location o) {
DyeColor[] cols = DyeColor.values();
for (int r = 0; r < 16; r++) {
int x = (r % 8) * 2, z = r < 8 ? 0 : 3;
put(o, x, 0, z, cols[r].name().toLowerCase() + "_banner[rotation=" + r + "]");
if (o.clone().add(x, 0, z).getBlock().getState() instanceof org.bukkit.block.Banner b) {
b.addPattern(new org.bukkit.block.banner.Pattern(DyeColor.BLACK, org.bukkit.block.banner.PatternType.STRIPE_TOP));
b.addPattern(new org.bukkit.block.banner.Pattern(DyeColor.WHITE, org.bukkit.block.banner.PatternType.CREEPER));
b.update(true, false);
}
}
pillar(o, 16, 5, 2, "red_wall_banner[facing=%s]");
put(o, 16, 1, 5, "stone");
put(o, 16, 0, 5, "stone");
}
/** Every head type in five rotations (0, 2, 4, 8, 12) per row, wall heads on pillars. */
private void beHeads(Location o) {
String[] t = {"skeleton_skull", "wither_skeleton_skull", "zombie_head", "creeper_head", "player_head", "piglin_head", "dragon_head"};
int[] rots = {0, 2, 4, 8, 12};
for (int i = 0; i < t.length; i++)
for (int j = 0; j < rots.length; j++) put(o, j * 2, 0, i, t[i] + "[rotation=" + rots[j] + "]");
String[] wall = {"skeleton_wall_skull", "dragon_wall_head", "piglin_wall_head", "player_wall_head"};
for (int i = 0; i < wall.length; i++) pillar(o, 12, i * 3 - 2, 1, wall[i] + "[facing=%s]");
for (int i = 0; i < wall.length; i++) put(o, 12, 0, i * 3 - 2, "stone");
}
/** Bells (all attachments/facings), decorated pots with sherds, shulker boxes (6 facings), lecterns, conduit. */
private void beMisc(Location o) {
for (int i = 0; i < 4; i++) {
put(o, i * 2, 0, 0, "bell[attachment=floor,facing=" + DIRS[i] + "]");
put(o, i * 2, 2, 2, "stone");
put(o, i * 2, 1, 2, "bell[attachment=ceiling,facing=" + DIRS[i] + "]");
put(o, i * 2, 0, 4, "decorated_pot[facing=" + DIRS[i] + "]");
if (o.clone().add(i * 2, 0, 4).getBlock().getState() instanceof org.bukkit.block.DecoratedPot pot) {
pot.setSherd(org.bukkit.block.DecoratedPot.Side.FRONT, Material.HEART_POTTERY_SHERD);
pot.setSherd(org.bukkit.block.DecoratedPot.Side.LEFT, Material.SKULL_POTTERY_SHERD);
pot.setSherd(org.bukkit.block.DecoratedPot.Side.RIGHT, Material.ANGLER_POTTERY_SHERD);
pot.update(true, false);
}
put(o, i * 2, 0, 6, "lectern[facing=" + DIRS[i] + ",has_book=true]");
}
put(o, 9, 1, 0, "stone");
put(o, 10, 1, 0, "bell[attachment=single_wall,facing=east]");
put(o, 12, 1, 0, "stone");
put(o, 11, 1, 0, "bell[attachment=double_wall,facing=east]");
String[] f6 = {"up", "down", "north", "south", "east", "west"};
String[] c6 = {"", "red_", "blue_", "lime_", "black_", "white_"};
for (int i = 0; i < 6; i++) put(o, 9 + (i % 3) * 2, 0, 3 + (i / 3) * 2, c6[i] + "shulker_box[facing=" + f6[i] + "]");
put(o, 9, 0, 7, "conduit");
put(o, 8, 0, 6, "lectern[facing=south,has_book=false]");
}
/** Block-entity items: held by armor stands, worn on heads, as item displays and as block displays. */
private void beItems(Location o) {
Material[] m = {Material.CHEST, Material.RED_BANNER, Material.SHULKER_BOX, Material.DECORATED_POT,
Material.CONDUIT, Material.DRAGON_HEAD, Material.PIGLIN_HEAD, Material.CREEPER_HEAD, Material.PLAYER_HEAD};
for (int i = 0; i < m.length; i++) {
ArmorStand as = spawn(o, i + 0.5, 0, 6.5, 0, ArmorStand.class);
as.setArms(true);
as.getEquipment().setItemInMainHand(it(m[i]));
if (m[i].name().endsWith("_HEAD")) as.getEquipment().setHelmet(it(m[i]));
ItemDisplay d = spawn(o, i + 0.5, 0.6, 3, 0, ItemDisplay.class);
d.setItemStack(it(m[i]));
d.setTransformation(new Transformation(new Vector3f(), new Quaternionf(), new Vector3f(0.6f), new Quaternionf()));
}
String[] blocks = {"chest[facing=south]", "red_banner[rotation=0]", "shulker_box[facing=up]", "decorated_pot[facing=south]",
"skeleton_skull[rotation=8]", "lectern[facing=south,has_book=true]"};
for (int i = 0; i < blocks.length; i++) {
BlockDisplay b = spawn(o, i * 1.5, 0, 0, 0, BlockDisplay.class);
b.setBlock(Bukkit.createBlockData("minecraft:" + blocks[i]));
}
}
}
+3 -3
View File
@@ -4,9 +4,9 @@
# Source: the CEM Template Loader data (Ewan Howell / wynem) — vanilla Java entity models as JSON,
# already correctly posed. These ultimately mirror Mojang's hardcoded Java EntityModel classes.
#
# NOTE: this set is now only the FALLBACK (rest-pose geometry) + the block-entity models (chests, signs,
# heads, banners, armor_layer_*). Mob models come from the vanilla client dump — regenerate that with
# tools/update-vanilla-models.sh <version> on every Minecraft update.
# NOTE: this set is now only the FALLBACK (rest-pose geometry) for entity types missing from the vanilla
# client dump (+ the elytra fallback). Mob AND block-entity models come from that dump — regenerate it
# with tools/update-vanilla-models.sh <version> on every Minecraft update.
#
# Usage: tools/update-cem-models.sh # update if newer
# tools/update-cem-models.sh --force # always overwrite