final comment sweep: section separators, signature-only javadocs and history notes removed, stale comments corrected (sign/contents model lookup, inverted entity cubes, item-bounds use, datapack folder search, biome tint argb, water-only overlay faces, beacon first section, mushroom-cow instanceof example, legacy skin arm copy, elytra and pack-detection notes in the scripts, pptest scene descriptions), comments only

This commit is contained in:
MineTec committed 2026-11-05 03:35:48 +01:00
1 parent 02e052787c
commit a3fbe122e3
29 files changed
+51 -125

No files matched your search

@@ -39,7 +39,5 @@ public final class DirectoryResourcePack implements ResourcePack {
}
@Override
public void close() {
// nothing to release
}
public void close() {}
}
@@ -47,15 +47,15 @@ public final class SkinCache extends ImageCache<String> {
int[][] src = pixels(img, w, h);
for (int y = 0; y < h; y++) System.arraycopy(src[y], 0, out[y], 0, w);
if (h <= 32) {
// Legacy skin: copy the right arm/leg regions into the modern left arm/leg slots (mirrored).
copyMirrored(img, out, 44, 16, 36, 48); // right leg -> left leg (top/quads handled by mirror)
// Legacy skin: mirrored copies of the right-arm region (40,16) into the modern left-arm slot (32,48).
copyMirrored(img, out, 44, 16, 36, 48);
copyMirrored(img, out, 44, 16, 36, 52);
copyMirrored(img, out, 40, 16, 32, 48); // right arm -> left arm
copyMirrored(img, out, 40, 16, 32, 48);
}
return out;
}
/** Mirror-copies a 16x12 limb block (legacy) into a modern slot; coarse but visually adequate. */
/** Mirror-copies a 16x12 block (whole block flipped horizontally, not per face); coarse approximation. */
private void copyMirrored(BufferedImage img, int[][] out, int srcX, int srcY, int dstX, int dstY) {
for (int y = 0; y < 12 && srcY + y < img.getHeight(); y++) {
for (int x = 0; x < 16 && srcX + x < img.getWidth(); x++) {
@@ -26,8 +26,7 @@ import org.bukkit.plugin.Plugin;
* <li><b>Globally:</b> at most {@code maxConcurrent} photos render at once, with up to
* {@code queueSize} more waiting; further requests are rejected.</li>
* <li><b>CPU:</b> the ray tracing runs on a dedicated {@link ForkJoinPool} of {@code threads}
* low-priority worker threads ({@link Thread#MIN_PRIORITY}), so it tends to use spare CPU and
* never fans out across every core like the default common pool would.</li>
* {@link Thread#MIN_PRIORITY} workers instead of fanning out over the common pool.</li>
* </ul>
*
* <p>Reserve a slot with {@link #tryReserve(UUID)}; if accepted, run the heavy work via
@@ -87,7 +86,7 @@ public final class RenderManager {
return Outcome.ACCEPTED;
}
/** Releases a reservation made by {@link #tryReserve}. Safe to call once per accepted reserve. */
/** Releases a reservation made by {@link #tryReserve}; a no-op when {@code user} holds none. */
public void release(UUID user) {
if (activeUsers.remove(user)) {
inFlight.decrementAndGet();
@@ -48,7 +48,7 @@ public final class BlockEntityModels {
*/
public record VanillaModel(Affine pose, List<Part> parts) {}
/** The vanilla model + pose of a block-entity, or null when nothing model-based is drawn (signs). */
/** The vanilla model + pose of a block-entity, or null without an entity model (signs, contents only). */
public static VanillaModel vanillaModel(BlockEntityState s) {
double deg = s.facingDeg();
return switch (s.kind()) {
@@ -7,8 +7,8 @@ import eu.mhsl.minecraft.pixelpics.assets.model.Face;
* null = not drawn), the inverse map and the world-space AABB.
*/
public final class EntityCube {
public final double[] from; // local min (inflated)
public final double[] to; // local max
public final double[] from; // local corners; from > to on some axis = inverted (below)
public final double[] to;
public final Face[] faces;
public final Affine toWorld;
public final Affine toLocal;
@@ -269,8 +269,6 @@ public final class EntityScene {
return tmin;
}
// ---- BVH construction (binned SAH over the cube AABB centroids) ----
private double[] centroid; // 3 per original cube
private double[] srcBox; // 6 per original cube
private int[] perm; // original cube indices, partitioned in place
@@ -37,8 +37,7 @@ public record EntityState(
int collar, // ARGB dye of a tamed cat's/wolf's collar; 0 = no collar (untamed)
boolean powered, // energy-swirl layer: charged creeper, wither below half health
ItemSpec mouthItem, // item carried in the mouth (fox/dolphin main hand); null = none
String pose, // "+"-joined state pose/render flags (the dump's STATE_POSES names, e.g. "lying+head_up"); null =
// idle
String pose, // "+"-joined STATE_POSES flags of the dump (e.g. "lying+head_up"); null = idle
Map<String, float[]> partPoses, // armor-stand part rotations by render-state field ("headPose", …), x/y/z deg
String shoulderLeft, // parrot variant on a player's left shoulder (e.g. "red"); null = none
String shoulderRight // parrot variant on the right shoulder; null = none
@@ -55,8 +55,6 @@ public final class ObjectBaker implements EntityBaker<ObjectState> {
return cubes.isEmpty() ? null : RenderedEntity.of(cubes);
}
// --- leash ---------------------------------------------------------------------------------------
/**
* Vanilla LeashRenderer curve: 24 segments from the leashed entity to the holder, horizontal position
* linear, height {@code dy·(t² + t)/2} (sags below the straight line), alternating light/dark brown.
@@ -105,8 +103,6 @@ public final class ObjectBaker implements EntityBaker<ObjectState> {
if (l > 0) for (int i = 0; i < 3; i++) v[i] /= l;
}
// --- text ------------------------------------------------------------------------------------
private void addText(ObjectState s, List<EntityCube> cubes) {
if (font == null || font.isEmpty()) return;
List<String> lines = wrap(s.lines(), Math.max(1, s.lineWidth()));
@@ -2,7 +2,7 @@ package eu.mhsl.minecraft.pixelpics.render.entity;
import java.util.List;
/** A baked entity: its world-space cubes and their union AABB (framing in the test harnesses). */
/** A baked entity: its world-space cubes and their union AABB (item model bounds, harness framing). */
public final class RenderedEntity {
public final List<EntityCube> cubes;
public final double[] aabbMin;
@@ -23,7 +23,6 @@ public final class TextureOps {
}
}
/** A tinted copy of {@code src}. */
public static int[][] tinted(int[][] src, int argb) {
int[][] out = deepCopy(src);
tint(out, argb);
@@ -99,8 +99,6 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
}
}
// --- contents: items, spawner mob, beacon beam, end portal ---
private void addContents(BlockEntityState s, Affine cell, List<EntityCube> cubes) {
ItemGeometry ig = items;
switch (s.kind()) {
@@ -385,8 +383,6 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
return tex[y][x];
}
// --- textures ---
private int[][] texture(String path) {
return textures.get(ResourceLocation.parse(path)).orElse(null);
}
@@ -435,8 +431,6 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
TextureOps.overlay(out, TextureOps.tinted(mask, argb));
}
// --- sign text ---
/**
* Front and back text as quads centred in the sign renderer's text frame ({@link BlockEntityModels#signTextFrame})
* facing +Z, the reader; the +Z (SOUTH) face reads left to right ({@code s = fx}).
@@ -188,8 +188,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
return tex;
}
// --- poses & items -------------------------------------------------------------------------------
/** Pre-space transform and pose overrides shared by every vanilla model of one entity. */
private record Pose(Affine pre, Map<String, CemModelLoader.PartPose> parts) {
List<CemGeometry.Baked> bake(CemModelLoader.CemModel m, int[][] tex) {
@@ -490,8 +488,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
return info != null && info.poses().keySet().stream().anyMatch(k -> k.startsWith("arms_"));
}
// --- model choice --------------------------------------------------------------------------------
/**
* The body model: baby model for babies, variant model matched by name token (temperature variants, salmon
* size, puff state, …), else the main model; else the legacy CEM template.
@@ -576,8 +572,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
});
}
// --- vanilla render layers -----------------------------------------------------------------------
private void addVanillaLayers(
EntityState s, Choice ch, CemModelLoader.EntityInfo info, Pose pose, int[][] bodyTex, Out out) {
String base = ch.base;
@@ -657,7 +651,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
}
}
/** The layer model's own texture, or null. */
private int[][] layerTexture(CemModelLoader.CemModel lm) {
return lm.texture() == null ? null : texture(lm.texture());
}
@@ -703,8 +696,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
return out;
}
// --- energy swirl & collars ----------------------------------------------------------------------
// A photo has no time: the swirl scroll is frozen at this age (ticks), chosen so the sparse wither_armor pattern
// covers every wither part (at e.g. 42 the side heads land on empty rows).
private static final float SWIRL_AGE = 59f;
@@ -757,8 +748,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
return derived.get(DerivedTextureCache.key("tint", src, argb), () -> TextureOps.tinted(src, argb));
}
// --- emissive layers -----------------------------------------------------------------------------
/**
* The dump's emissive layers (eyes, warden bioluminescence, …): texture per variant, processed by the layer's
* render type, times its static alpha. Skipped: animated layers (a photo has no age), layers whose state flag is
@@ -798,8 +787,6 @@ public final class CemBaker implements EntityBaker<EntityState> {
});
}
// --- texture resolution --------------------------------------------------------------------------
private int[][] texture(String path) {
return textures.get(ResourceLocation.parse(path)).orElse(null);
}
@@ -849,9 +836,9 @@ public final class CemBaker implements EntityBaker<EntityState> {
return null;
}
// --- villagers: biome type + profession + level badge over the base body ---
private static final String[] LEVEL_BADGE = {"stone", "iron", "gold", "emerald", "diamond"};
/** Villager texture: biome type, profession and level badge over the base body. */
private int[][] compositeVillager(EntityState s, int[][] base, String folder, boolean babyModel) {
if (base == null) return null;
// Only the inputs the composite reads (babies: no profession).
@@ -887,10 +874,9 @@ public final class CemBaker implements EntityBaker<EntityState> {
TextureOps.overlay(dst, o);
}
// --- humanoid armor: the wearer's per-slot models <base>/HumanoidArmorLayer.modelSet.<slot>, posed like the body
private static final int GLINT_COLOR = 0xFF8040CC; // approximated enchantment-glint purple
/** The wearer's per-slot armor models {@code <base>/HumanoidArmorLayer.modelSet.<slot>}, posed like the body. */
private void addArmorLayers(EntityState s, Choice ch, Pose pose, Out out) {
EntityState.Equipment eq = s.equipment();
String base = ch.base;
@@ -241,7 +241,7 @@ public final class CemModelLoader {
try {
JsonObject entry = e.getValue().getAsJsonObject();
if (!entry.has("model")) continue;
// The dump nests the jem as an object; the CEM template set (and older dumps) embed a JSON string.
// The dump nests the jem as an object; the CEM template set embeds a JSON string.
JsonElement m = entry.get("model");
JsonObject model = m.isJsonObject()
? m.getAsJsonObject()
@@ -31,8 +31,8 @@ public final class DatapackVariants {
private DatapackVariants() {}
/**
* Scans {@code <level>/datapacks/*} for every world folder — the folder itself or the level directory
* above it (26.x keeps dimensions under {@code <level>/dimensions/<ns>/<name>}); returns the number of
* Scans {@code <level>/datapacks/*} for every world folder: the folder itself or the nearest of up to 4 parents
* that has one (26.x keeps dimensions under {@code <level>/dimensions/<ns>/<name>}); returns the number of
* variants registered.
*/
public static int load(CemModelLoader models, Iterable<File> worldFolders, Logger log) {
@@ -82,8 +82,6 @@ public final class ItemGeometry {
JsonObject specialModel, // the special model's own fields (chest texture/chest_type, …) or null
Affine specialTransform) {} // item-model transformation for special models (T·Rl·S·Rr) or null
// --- public API --------------------------------------------------------------------------------
/** An item in a display context ("thirdperson_righthand", "head", "ground", "fixed", "none", …). */
public void bake(ItemSpec item, String context, boolean leftHand, Affine toWorld, List<EntityCube> out) {
if (item == null) return;
@@ -159,8 +157,6 @@ public final class ItemGeometry {
if (state != null && blockEntities != null) blockEntities.bakeModel(state, toWorld, out);
}
// --- item definition / model resolution --------------------------------------------------------
private Info info(String id, String context) {
// Keyed by date: minecraft:local_time selects (christmas chest items) change with the day.
ZonedDateTime now = SpecialDates.now();
@@ -386,8 +382,6 @@ public final class ItemGeometry {
return o.has(k) && o.get(k).isJsonObject() ? o.getAsJsonObject(k) : null;
}
// --- geometry ------------------------------------------------------------------------------------
private void bakeSpecial(ItemSpec item, Info info, Affine m, List<EntityCube> out) {
Affine xf = info.specialTransform != null ? m.mul(info.specialTransform) : m;
switch (info.special) {
@@ -39,8 +39,9 @@ public final class ElementIntersector {
double hitT, hitS, hitT2, hitNx, hitNy, hitNz;
Direction hitDir;
// Depth; coplanar within 1e-4: later elements first. Not transitive, so the tie order depends on the sort
// algorithm: keep Arrays.sort over objects (TimSort).
// Depth; coplanar within 1e-4: later elements first, like vanilla's draw order (overlays such as the grass
// side tint). Not transitive, so the tie order depends on the sort algorithm: keep Arrays.sort over objects
// (TimSort).
final Comparator<Integer> byDepth = (a, b) -> {
if (Math.abs(t[a] - t[b]) > 1e-4) return Double.compare(t[a], t[b]);
return Integer.compare(order[b], order[a]);
@@ -131,7 +132,6 @@ public final class ElementIntersector {
if (n == 0) return null;
Integer[] idx = sc.idx;
for (int k = 0; k < n; k++) idx[k] = k; // Integer cache for k < 128
// Coplanar faces: later elements first, like vanilla's draw order (overlays such as the grass side tint).
if (n > 1) Arrays.sort(idx, 0, n, sc.byDepth);
for (int j = 0; j < n; j++) {
@@ -23,7 +23,7 @@ import org.bukkit.block.data.Waterlogged;
* under the same fluid, averaged with the neighbours like {@code calculateAverageHeight}) split into the triangles
* NW-SW-SE / NW-SE-NE; side faces against every neighbour that is not the same fluid and does not cover the face;
* a bottom face. Top and sides are double-sided (the top only if {@code shouldRenderBackwardUpFace}), except side
* faces against {@code HalfTransparentBlock}/{@code LeavesBlock}, which use the overlay texture one-sided; the
* water faces against {@code HalfTransparentBlock}/{@code LeavesBlock}, which use the overlay texture one-sided; the
* bottom is seen from below only. Waterlogged blocks and seagrass/kelp/bubble columns hold a water source.
*/
public final class FluidRenderer {
@@ -74,13 +74,13 @@ public final class FluidRenderer {
try {
set.add(Material.valueOf(n));
} catch (IllegalArgumentException ignored) {
/* older/newer server */
// Older/newer server
}
}
return set;
}
// --- fluid state: type (bits 0-1), amount 1..8 (bits 2-5), falling (bit 6), source (bit 7) -------------
// Packed fluid state: type (bits 0-1), amount 1..8 (bits 2-5), falling (bit 6), source (bit 7).
private static final int WATER_SOURCE = WATER | 8 << 2 | 1 << 7;
@@ -144,8 +144,6 @@ public final class FluidRenderer {
}
}
// --- sky light under water ----------------------------------------------------------------------------
/**
* Vanilla sky light level of the cell (x, y, z) under water: 15 minus one per block from this cell up to the
* topmost water block of the column (water and non-occluding blocks below the surface cost one level each).
@@ -165,8 +163,6 @@ public final class FluidRenderer {
return 15 - water;
}
// --- textures ------------------------------------------------------------------------------------------
private final BlockModelRegistry registry;
private final int[][] waterStill, waterFlow, waterOverlay, lavaStill, lavaFlow;
// Bit per Direction: the model covers that whole cell face with opaque texels (approximates vanilla's face
@@ -196,8 +192,6 @@ public final class FluidRenderer {
return tex[y][x];
}
// --- face occlusion ------------------------------------------------------------------------------------
private int fullFaces(BlockData data) {
if (data.getMaterial().isAir()) return 0;
return fullFaces(registry.get(data));
@@ -254,8 +248,6 @@ public final class FluidRenderer {
return (fullFaces & 1 << face.ordinal()) != 0;
}
// --- heights -------------------------------------------------------------------------------------------
/** FluidRenderer.getHeight: 1 under the same fluid, own height in it, 0 for non-solid blocks, -1 for solids. */
private static float height(BlockLookup level, int type, int x, int y, int z, BlockData data, int state) {
if (type(state) == type) {
@@ -287,8 +279,6 @@ public final class FluidRenderer {
return h >= 0.8F ? 10.0F : h >= 0.0F ? 1.0F : 0.0F;
}
// --- flow (top texture direction) ---------------------------------------------------------------------
private static final int[][] HORIZONTAL = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
/**
@@ -346,8 +336,6 @@ public final class FluidRenderer {
return false;
}
// --- ray crossing --------------------------------------------------------------------------------------
/** A drawn fluid polygon crossed by a ray. */
static final class Crossing {
double t;
@@ -2,7 +2,6 @@ package eu.mhsl.minecraft.pixelpics.render.render;
import com.google.common.base.Preconditions;
/** Output size of a photo in pixels. */
public record Resolution(int width, int height) {
public Resolution {
@@ -383,7 +383,7 @@ public final class BlockEntitySnapshotBuilder {
/**
* BeaconBlockEntity.tick's beam scan from the beacon up to the WORLD_SURFACE height: the beacon starts a
* white section, stained glass (panes) starts a section of its dye colour (the first one over the beacon
* replaces white, later ones average with the previous colour, equal colours just extend it), other
* is not blended with white, later ones average with the previous colour, equal colours just extend it), other
* blocks extend the current section, and a light-blocking block (except bedrock) stops the beam.
*/
private static BlockEntityState.Beam beam(World world, int bx, int by, int bz, Location eye) {
@@ -175,7 +175,7 @@ public final class EntitySnapshotBuilder {
boolean powered = false;
try {
if (e instanceof Slime sl) sizeScale = sl.getSize(); // incl. magma cubes
// Order matters for subtypes (MushroomCow extends Cow, Llama extends ChestedHorse extends AbstractHorse).
// Order matters for subtypes (Llama extends ChestedHorse extends AbstractHorse).
if (e instanceof Sheep sh) {
tint = isJebSheep(sh) ? jebWoolColor(sh.getTicksLived()) : sheepWoolColor(sh.getColor());
if (sh.isSheared()) variant = "sheared";
@@ -601,7 +601,7 @@ public final class EntitySnapshotBuilder {
}
}
/** One armor slot -> EquipPiece (asset, leather dye, trim, glint); null for empty / non-armor items. */
/** Null for empty / non-armor items. */
private static EntityState.EquipPiece armorPiece(ItemStack it) {
if (it == null || it.getType().isAir()) return null;
String asset = armorAsset(it.getType());
@@ -10,7 +10,7 @@ import org.bukkit.block.data.BlockData;
*
* <p>Performance: the chunks live in a primitive open-addressing table (linear probing, Fibonacci hash of the
* full {@link #chunkKey}), not a {@code HashMap<Long, …>}: {@code Long.hashCode} is {@code cx ^ cz}, so every
* diagonal collided into treeified bins and each block lookup paid for boxing and {@code TreeNode.find}.
* diagonal would collide into treeified bins and each block lookup would pay for boxing.
*/
public final class WorldSnapshot {
@@ -26,7 +26,7 @@ public final class BiomeClimate {
public static final int NONE = -1;
/**
* @param water ARGB-less RGB water color
* @param water RGB water colour (no alpha)
* @param grass explicit grass color override, or {@link #NONE}
* @param foliage explicit foliage color override, or {@link #NONE}
* @param dryFoliage explicit dry-foliage color override, or {@link #NONE}
@@ -1,8 +1,6 @@
package eu.mhsl.minecraft.pixelpics.render.tint;
/**
* The biome-dependent tint colors (RGB) for the colormap-driven channels.
*/
/** Biome tint colours (opaque ARGB) of the colormap-driven channels. */
public record BiomeTint(int grass, int foliage, int dryFoliage, int water) {
/** Plains colours: the fallback without a colormap, and the tint of blocks without biome context. */
public static final BiomeTint PLAINS = new BiomeTint(0xFF91BD59, 0xFF77AB2F, 0xFFA9A05B, 0xFF3F76E4);
@@ -104,7 +104,6 @@ public final class ColorUtil {
| clamp(blue(dst) + blue(src));
}
/** Clamps a channel value to 0..255. */
public static int clamp(int v) {
return v < 0 ? 0 : Math.min(v, 255);
}
+2 -3
View File
@@ -71,9 +71,8 @@ final class Harness {
}
/**
* Server-free BlockData stand-in: material + blockstate string are all the model registry needs. (Each proxy
* kind gets its own handler class: a shared generic one made the hot getMaterial/hashCode calls megamorphic and
* EntityBenchmark measurably slower.)
* Server-free BlockData stand-in: material + blockstate string are all the model registry needs. Each proxy kind
* has its own handler: a shared generic one made the hot getMaterial/hashCode calls megamorphic (slower benchmark).
*/
static BlockData block(Material m, String state) {
String s = "minecraft:" + m.getKey().getKey() + (state == null ? "" : "[" + state + "]");
-2
View File
@@ -317,8 +317,6 @@ public class VanillaModelDump {
}
}
// ---- render types, emissive layers, state textures ----------------------------------------------------
/** Render type name -> pipeline properties ({@link #renderTypeJson}); written as root "renderTypes". */
static final Map<String, JsonObject> RENDER_TYPES = new TreeMap<>();
+2 -2
View File
@@ -2,8 +2,8 @@
# Builds the runtime resource pack PixelPics renders from (blockstates, models, textures, items,
# atlases, font) straight from Mojang's CLIENT jar — no launcher install needed. The plugin ships no
# assets itself; drop the resulting zip into plugins/PixelPics/resourcepack/. Run it once per
# Minecraft update. No pack.mcmeta needed (the client jar has none; the loader detects the pack by
# its assets/minecraft/blockstates folder).
# Minecraft update. No pack.mcmeta needed (the client jar has none; the loader detects a zip by its
# vanilla blockstate files).
#
# Needs: curl, jq, unzip, zip.
#
+16 -34
View File
@@ -105,8 +105,8 @@ public class PPTest extends JavaPlugin {
scene("chests_e", this::chests, 16, 5, 3, 4.5, 0, 3);
scene("chests_w", this::chests, -7, 5, 3, 4.5, 0, 3);
scene("translucent", this::translucent, 4.5, 2.8, -5.5, 4.5, 0.8, 2);
// Projectile orientation (vanilla ArrowRenderer/ThrownTridentRenderer): flying stars around gold pillars
// (tips point radially outward, rings level / +35 deg / -35 deg) and arrows/tridents stuck in stone pillars.
// Projectile orientation (vanilla ArrowRenderer/ThrownTridentRenderer): hovering stars of arrows (gold pillar)
// and tridents (diamond pillar), tips radially outward, rings level / +35 / -35 deg; arrows/tridents stuck in stone.
scene("proj_fly_top", this::projFly, 6.5, 13, 2.2, 6.5, 0, 2);
scene("proj_fly_side", this::projFly, 6.5, 2.6, -8, 6.5, 2.4, 2);
scene("proj_fly_obl", this::projFly, -2, 6, -6, 6.5, 2, 2);
@@ -131,7 +131,7 @@ public class PPTest extends JavaPlugin {
scene("layers_top", this::layers, 5.5, 6, 2.0, 5.5, 0, 2.5);
scene("layers_wither", this::withers, 3.5, 4, -9, 3.5, 2.2, 1);
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
// Block entities (vanilla BlockEntityRenderer poses): signs and banners in all 16 rotations, heads in 5, wall
// mounts, and bells/pots/shulker boxes/lecterns/conduits, each layout seen from two sides.
scene("be_signs_s", this::beSigns, 7, 3, 8, 7, 0.5, 1);
scene("be_signs_n", this::beSigns, 7, 1.3, -8, 7, 0.6, 1);
@@ -237,7 +237,7 @@ public class PPTest extends JavaPlugin {
scene("be_contents_close", this::beContents, 1.2, 1.8, 2.2, 1.5, 0.6, 0);
scene("be_contents_beam", this::beContents, 4, 6, 26, 10, 9, 6);
scene("be_contents_portal", this::beContents, 11.5, 3.6, -1.8, 11.5, 0, 1.2);
// Items in detail (decompiled 26.3 special renderers): shields (plain, base colour, banner patterns), chests,
// Items in detail (vanilla special renderers): shields (plain, base colour, banner patterns), chests,
// spawner/vault held by armor stands (+ a baby zombie: baby hand offset), item displays (fixed), item frames
// (rotation 0/1) and dropped (ground: lift = 0.1625 - model min y). items_inverted: spawner, trial spawner and
// vault as block, block display, item display and dropped item (inverted inner faces). xmas_chests: chests,
@@ -465,7 +465,7 @@ public class PPTest extends JavaPlugin {
ts.update(true, false);
put(o, 4, 0, 6, "trial_spawner[trial_spawner_state=waiting_for_players]");
}
// Beacon on a 3x3 iron pyramid, red glass 2 above, blue glass 4 above (red+blue = averaged section).
// Beacon on a 3x3 iron pyramid, red glass 2 above, blue glass pane 4 above (red+blue = averaged section).
for (int x = -1; x <= 1; x++) for (int z = -1; z <= 1; z++) put(o, 10 + x, -1, 6 + z, "iron_block");
put(o, 10, 0, 6, "beacon");
put(o, 10, 2, 6, "red_stained_glass");
@@ -642,10 +642,9 @@ public class PPTest extends JavaPlugin {
}
}
// --- poses ------------------------------------------------------------------------------------
private void posePets(Location o) {
// Row 1 (z=0): foxes — standing, sitting, sleeping, crouching, crouching+leaping, faceplanted.
// Row 1 (z=0): foxes — standing, sitting, sleeping, crouching, crouching+leaping, faceplanted, baby
// sleeping/sitting.
Fox f0 = rfSpawn(o, 0, 0, 0, 180, Fox.class);
hands(f0, Material.SWEET_BERRIES, null);
Fox f1 = rfSpawn(o, 1.2, 0, 0, 180, Fox.class);
@@ -866,8 +865,8 @@ public class PPTest extends JavaPlugin {
/**
* Loads the scene's chunks and waits until they are entity-ticking before building: entities spawned into a
* chunk that only becomes entity-ticking a few (variable) ticks later start moving late (projectile scenes were
* not reproducible).
* chunk that only becomes entity-ticking a few (variable) ticks later start moving late (non-reproducible
* projectile scenes).
*/
private void run(Scene sc, Location o) {
loadArea(o);
@@ -911,7 +910,7 @@ public class PPTest extends JavaPlugin {
private static final List<String> LARGE = List.of("proj_", "rf_", "pose_", "frames", "geo_", "liq_", "items_", "xmas_");
/** *_sq scenes square, newer scene families 960x640, the rest 480x320. */
/** *_sq scenes 384x384, the LARGE families 960x640, the rest 480x320. */
private static Resolution resolution(String name) {
if (name.endsWith("_sq")) return new Resolution(384, 384);
return LARGE.stream().anyMatch(name::startsWith) ? new Resolution(960, 640) : new Resolution(480, 320);
@@ -1013,8 +1012,7 @@ public class PPTest extends JavaPlugin {
/** Clears the area (blocks + entities) and lays a 24x24 stone floor at the origin's y-1. */
private void prepareArea(Location o) {
// Blocks first: removing a campfire/shelf/container drops its items, which the entity sweep then removes
// (before, the previous run's cooked campfire food lay around at random).
// Blocks first: removing a campfire/shelf/container drops its items, which the entity sweep then removes.
for (int x = -6; x < 18; x++)
for (int z = -12; z < 12; z++)
for (int y = -1; y < 12; y++)
@@ -1023,8 +1021,6 @@ public class PPTest extends JavaPlugin {
for (Entity e : w.getNearbyEntities(o.clone().add(6, 5, 2), 20, 20, 20)) if (!(e instanceof Player)) e.remove();
}
// --- helpers ---------------------------------------------------------------------------------
/** {@link #rfSpawn} with the body turned to {@code yaw} (spawned mobs otherwise keep a random body yaw). */
private <T extends Entity> T spawn(Location o, double x, double y, double z, float yaw, Class<T> type) {
T e = rfSpawn(o, x, y, z, yaw, type);
@@ -1075,8 +1071,6 @@ public class PPTest extends JavaPlugin {
if (off != null) le.getEquipment().setItemInOffHand(it(off));
}
// --- scenes ----------------------------------------------------------------------------------
private void held(Location o) {
hands(spawn(o, 0, 0, 0, 180, Zombie.class), Material.IRON_SWORD, Material.SHIELD);
hands(spawn(o, 1.6, 0, 0, 180, Skeleton.class), Material.BOW, null);
@@ -1196,7 +1190,7 @@ public class PPTest extends JavaPlugin {
i3.setItemStack(it(Material.PLAYER_HEAD));
ItemDisplay i4 = spawn(o, 2, 0.5, 2.5, 0, ItemDisplay.class);
i4.setItemStack(it(Material.SHIELD));
// Block displays: rotated stairs, scaled chest (block entity!), banner, bed, glass.
// Block displays: rotated + scaled stairs, chest (block entity), banner, glass, sign.
BlockDisplay b1 = spawn(o, 3.5, 0, 2.5, 0, BlockDisplay.class);
b1.setBlock(Bukkit.createBlockData("minecraft:oak_stairs[facing=east]"));
b1.setTransformation(new Transformation(new Vector3f(), new Quaternionf().rotateY(0.6f), new Vector3f(0.8f), new Quaternionf()));
@@ -1296,7 +1290,7 @@ public class PPTest extends JavaPlugin {
put(o, STEP[i][0], 1 + age, STEP[i][1], "shelf_mushroom[age=" + age + ",facing=" + DIRS[i] + "]");
}
/** Blockstate y-rotations: ladders, stairs, wall torches, furnaces, logs on all sides of stone pillars. */
/** Blockstate y-rotations: ladders, wall torches, stairs, furnaces on all sides of stone pillars; fence, door, logs. */
private void rotated(Location o) {
String[] kinds = {"ladder[facing=%s]", "wall_torch[facing=%s]", "oak_stairs[facing=%s]", "furnace[facing=%s]"};
for (int k = 0; k < kinds.length; k++) {
@@ -1440,8 +1434,6 @@ public class PPTest extends JavaPlugin {
t.setBillboard(Display.Billboard.CENTER);
}
// --- conditional layers ----------------------------------------------------------------------
private void layers(Location o) {
tame(spawn(o, 0, 0, 0, 180, Cat.class), DyeColor.RED);
tame(spawn(o, 1.2, 0, 0, 180, Cat.class), DyeColor.BLUE);
@@ -1476,8 +1468,6 @@ public class PPTest extends JavaPlugin {
half.setHealth(half.getAttribute(Attribute.MAX_HEALTH).getValue() / 2 - 1);
}
// --- data-driven variants / pose flags / armor wearers ------------------------------------------
private static <T extends Keyed> List<T> registry(RegistryKey<T> key) {
List<T> out = new ArrayList<>();
RegistryAccess.registryAccess().getRegistry(key).forEach(out::add);
@@ -1682,8 +1672,6 @@ public class PPTest extends JavaPlugin {
faceNorth(o);
}
// --- projectiles -----------------------------------------------------------------------------
/** Spawns a projectile at p moving along dir, rotated like vanilla Projectile#shoot (yaw atan2(x,z), pitch up). */
private Projectile projectile(Location p, Vector dir, double speed, boolean gravity, Class<? extends Projectile> type) {
Vector d = dir.clone().normalize();
@@ -1741,7 +1729,7 @@ public class PPTest extends JavaPlugin {
Vector n = dir(out, 0);
Vector t = new Vector(n.getZ(), 0, -n.getX());
double dist = 1.5 + 3.5;
// rising from low, level but 20 deg sideways, falling from high
// Rising from low, level but 20 deg sideways, falling from high.
double[][] shots = {{-1, 0.6, 25, 0}, {0, 1.6, 0, 20}, {1, 3.4, -25, 0}};
for (double[] sh : shots) {
Location p = o.clone().add(cx + n.getX() * dist + t.getX() * sh[0], sh[1], cz + n.getZ() * dist + t.getZ() * sh[0]);
@@ -1749,7 +1737,7 @@ public class PPTest extends JavaPlugin {
if (trident) projectile(p, d, 2.0, true, Trident.class);
else projectile(p, d, 1.5, true, (Class<? extends AbstractArrow>) (side % 2 == 0 ? Arrow.class : SpectralArrow.class));
}
// steep shot into the floor, diagonally away from the pillar
// Steep shot into the floor, diagonally away from the pillar.
Vector diag = dir(out + 45, 0);
Location p = o.clone().add(cx + diag.getX() * 2.6, 3, cz + diag.getZ() * 2.6);
Vector d = dir(out + 45, trident ? -70 : -60);
@@ -1759,8 +1747,6 @@ 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);
}
@@ -1877,8 +1863,6 @@ public class PPTest extends JavaPlugin {
}
}
// --- item frames -------------------------------------------------------------------------------
private void frames(Location o) {
for (int x = -2; x <= 11; x++)
for (int y = 0; y <= 5; y++) put(o, x, y, 4, "stone");
@@ -1942,7 +1926,7 @@ public class PPTest extends JavaPlugin {
frame(o, 5, 5, 5, BlockFace.NORTH, false, synthMap(), 0, true);
}
/** Rows of cross/rescaled plants on grass (z=0..3), potted plants and stems; see the geo_* scenes. */
/** Rows of cross/rescaled plants on grass (z=0..3), potted plants and stems. */
private void geoPlants(Location o) {
for (int x = -1; x <= 12; x++) for (int z = -1; z <= 5; z++) put(o, x, -1, z, "grass_block");
String[] row0 = {"short_grass", "fern", "dandelion", "poppy", "azure_bluet", "oxeye_daisy", "cornflower",
@@ -2130,8 +2114,6 @@ public class PPTest extends JavaPlugin {
return mapItem(v);
}
// --- fluids ------------------------------------------------------------------------------------------------
/** Sets the biome of the scene area (deterministic water colour / water fog). */
private void biome(Location o, Biome biome) {
for (int x = -8; x < 20; x += 2)
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Updates the vendored CEM template model set (Ewan Howell / wynem: vanilla Java entity models as JSON). It is
# only the rest-pose FALLBACK for entity types missing from the vanilla client dump (+ the elytra fallback);
# mob and block-entity models come from tools/update-vanilla-models.sh.
# only the rest-pose FALLBACK for entity types missing from the vanilla client dump; mob and block-entity models
# come from tools/update-vanilla-models.sh.
#
# Usage: tools/update-cem-models.sh # update if newer
# tools/update-cem-models.sh --force # always overwrite