import eu.mhsl.minecraft.pixelpics.assets.*; import eu.mhsl.minecraft.pixelpics.render.entity.*; import eu.mhsl.minecraft.pixelpics.render.render.*; import eu.mhsl.minecraft.pixelpics.render.sky.SkyContext; import eu.mhsl.minecraft.pixelpics.render.snapshot.WorldSnapshot; import eu.mhsl.minecraft.pixelpics.render.tint.BiomeTintProvider; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.bukkit.util.Vector; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.lang.reflect.Proxy; import java.util.*; import java.util.List; import java.util.logging.Logger; /** * Standalone (no server) render of {@link ObjectState}s — block/item/text displays, dropped items, * falling blocks — in one scene, to check the ObjectBaker geometry, UV orientation and transforms. * Output: ~/Dokumente/PixelPics-Entity-Renders/objects.png */ public class ObjectTestRender { static final String ROOT = "/home/elias/Dokumente/mcTestServer/plugins/PixelPics"; /** Server-free BlockData stand-in: material + blockstate string are all the model registry needs. */ static BlockData block(Material m, String state) { String s = "minecraft:" + m.getKey().getKey() + (state == null ? "" : "[" + state + "]"); return (BlockData) Proxy.newProxyInstance(ObjectTestRender.class.getClassLoader(), new Class[]{BlockData.class}, (p, meth, a) -> switch (meth.getName()) { case "getMaterial" -> m; case "getAsString" -> s; case "equals" -> p == a[0]; case "hashCode" -> System.identityHashCode(p); case "toString" -> s; default -> { Class rt = meth.getReturnType(); yield rt == boolean.class ? false : (rt.isPrimitive() ? 0 : null); } }); } static ObjectState obj(ObjectState.Kind k, Affine a, BlockData b, String item, List lines, int bg) { return switch (k) { case BLOCK -> ObjectState.block(a, b, null); case ITEM -> ObjectState.item(a, new ItemSpec(item, b, null, false), "none", false); case TEXT -> ObjectState.text(a, lines, 200, 0xFFFFFFFF, bg, true); case LEASH -> ObjectState.leash(new double[6]); }; } public static void main(String[] args) throws Exception { Logger log = Logger.getLogger("test"); ResourcePack pack = ResourcePackLoader.load(new File(ROOT, "resourcepack"), log).orElseThrow(); AssetReader reader = new AssetReader(pack); TextureCache textures = new TextureCache(pack); BlockModelRegistry registry = new BlockModelRegistry(reader, textures); BiomeTintProvider tint = new BiomeTintProvider(textures); var geo = new eu.mhsl.minecraft.pixelpics.render.entity.cem.CemModelLoader(); var font = eu.mhsl.minecraft.pixelpics.assets.font.FontLoader.load(pack, textures, log); SkinCache skins = new SkinCache(); var baker = new eu.mhsl.minecraft.pixelpics.render.entity.cem.CemBaker(geo, textures, skins); var beBaker = new eu.mhsl.minecraft.pixelpics.render.entity.cem.BlockEntityBaker(geo, textures, skins, font); 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/vanilla_entity_models.json"), log); DefaultScreenRenderer renderer = new DefaultScreenRenderer(registry, tint, textures, baker, beBaker, log); List objs = new ArrayList<>(); // Block displays: plain grass block (tinted), rotated + scaled stairs. objs.add(obj(ObjectState.Kind.BLOCK, Affine.translation(0, 0, 0), block(Material.GRASS_BLOCK, "snowy=false"), null, List.of(), 0)); objs.add(obj(ObjectState.Kind.BLOCK, Affine.translation(2, 0, 0).mul(Affine.rotY(Math.toRadians(30))).mul(Affine.scale(0.75)), block(Material.OAK_STAIRS, "facing=north,half=bottom,shape=straight,waterlogged=false"), null, List.of(), 0)); // Item displays: a flat item and a block item (centred). objs.add(obj(ObjectState.Kind.ITEM, Affine.translation(0.5, 2.0, 0.5), null, "diamond_sword", List.of(), 0)); objs.add(obj(ObjectState.Kind.ITEM, Affine.translation(2.5, 2.0, 0.5).mul(Affine.scale(0.6)), block(Material.OAK_LOG, "axis=y"), "oak_log", List.of(), 0)); // Text display (facing the camera side), opaque background. objs.add(obj(ObjectState.Kind.TEXT, Affine.translation(1.5, 3.0, 0).mul(Affine.rotY(Math.PI)).mul(Affine.scale(0.025)), null, null, List.of("Hallo PixelPics!", "Zeile zwei äöü"), 0xFF203040)); // Text seen from the BACK (front faces +Z, camera is on -Z) on the vanilla default 25% black // background (blended), and a shield item. objs.add(obj(ObjectState.Kind.TEXT, Affine.translation(-0.5, 3.0, 0).mul(Affine.scale(0.025)), null, null, List.of("Rueckseite"), 0x40000000)); objs.add(obj(ObjectState.Kind.ITEM, Affine.translation(4, 1.0, 0.5), null, "shield", List.of(), 0)); // Dropped item (flat) on the ground. objs.add(obj(ObjectState.Kind.ITEM, Affine.translation(-1, 0.3, 0.5).mul(Affine.scale(0.5)), null, "apple", List.of(), 0)); BlockData air = block(Material.AIR, null); WorldSnapshot empty = new WorldSnapshot(Map.of(), 0, 1, air); SkyContext sky = new SkyContext(6000, 0, 6000); int W = 480, H = 360; Vector cam = new Vector(1.5, 3.2, -6.5); Location loc = new Location(null, cam.getX(), cam.getY(), cam.getZ()); loc.setDirection(new Vector(1.2, 1.3, 0.5).subtract(cam)); List rayMap = EntityTestRender.buildRayMap(loc, W * 3, H * 3); RenderJob job = new RenderJob(empty, rayMap, cam, W, H, sky, List.of(), List.of(), List.of(), objs); BufferedImage img = renderer.execute(job); File out = new File("/home/elias/Dokumente/PixelPics-Entity-Renders/objects.png"); ImageIO.write(img, "png", out); System.out.println("WROTE " + out); } }