add sign text rendering: support font loading, text rasterization, and color adjustments for signs

This commit is contained in:
2026-06-21 16:15:57 +02:00
parent 5330948dbd
commit fed94f97d1
11 changed files with 564 additions and 22 deletions
+46 -12
View File
@@ -35,7 +35,18 @@ public class BlockEntityTestRender {
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, null, List.of(), List.of(), null, null, null);
}
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);
}
static SignText txt(org.bukkit.DyeColor dye, boolean glow, String... lines) {
return new SignText(List.of(lines),
eu.mhsl.minecraft.pixelpics.render.util.ColorUtil.signFillArgb(dye, glow),
eu.mhsl.minecraft.pixelpics.render.util.ColorUtil.signOutlineArgb(dye), glow);
}
public static void main(String[] args) throws Exception {
@@ -48,8 +59,11 @@ public class BlockEntityTestRender {
CemModelLoader geo = new CemModelLoader();
geo.load(new java.io.FileInputStream("/tmp/cem_models.json"), log);
SkinCache skins = new SkinCache();
eu.mhsl.minecraft.pixelpics.assets.font.BitmapFont font =
eu.mhsl.minecraft.pixelpics.assets.font.FontLoader.load(pack, textures, log);
log.info("font glyphs loaded: " + (font.isEmpty() ? "NONE" : "ok"));
CemBaker baker = new CemBaker(geo, textures, skins);
beBaker = new BlockEntityBaker(geo, textures, skins);
beBaker = new BlockEntityBaker(geo, textures, skins, font);
decoBaker = new DecorationBaker(textures);
DefaultScreenRenderer renderer = new DefaultScreenRenderer(registry, tint, textures, baker, beBaker, log);
@@ -76,23 +90,43 @@ public class BlockEntityTestRender {
scenes.put("chest_E", List.of(be(Kind.CHEST, 0, 0, 0, 270)));
scenes.put("chest_W", List.of(be(Kind.CHEST, 0, 0, 0, 90)));
scenes.put("double_chest", List.of(
new BlockEntityState(Kind.CHEST, 0, 0, 0, 0, ChestKind.LEFT, 0, null, null, null, null, null, List.of(), List.of(), null),
new BlockEntityState(Kind.CHEST, 1, 0, 0, 0, ChestKind.RIGHT, 0, null, null, null, null, null, List.of(), List.of(), null)));
new BlockEntityState(Kind.CHEST, 0, 0, 0, 0, ChestKind.LEFT, 0, null, null, null, null, null, List.of(), List.of(), null, null, null),
new BlockEntityState(Kind.CHEST, 1, 0, 0, 0, ChestKind.RIGHT, 0, null, null, null, null, null, List.of(), List.of(), null, null, null)));
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)));
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)));
// --- sign text (calibration) --- facingDeg=180 → front (model Z) faces the camera (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", "", ""))));
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)));
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)));
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)));
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),
new BlockEntityState(Kind.BED, 0, 0, 1, 0, null, 0, "red", null, BedPart.HEAD, null, null, List.of(), List.of(), 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)));
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)));
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")));
@@ -149,7 +183,7 @@ public class BlockEntityTestRender {
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);
return new BlockEntityState(kind, 0, 0, 0, 0, null, 0, null, null, null, type, null, List.of(), List.of(), null, null, null);
}
static BufferedImage renderScene(DefaultScreenRenderer renderer, WorldSnapshot world, SkyContext sky,