refactor: simplify rendering logic, remove redundant cases, and replace bounds checks with Math.clamp

This commit is contained in:
2026-06-21 18:09:11 +02:00
parent 1d3040610e
commit dce7804fed
10 changed files with 30 additions and 34 deletions
@@ -44,8 +44,8 @@ public final class Face {
int px = (int) Math.floor(u * w);
int py = (int) Math.floor(v * h);
px = Math.max(0, Math.min(w - 1, px));
py = Math.max(0, Math.min(h - 1, py));
px = Math.clamp(px, 0, w - 1);
py = Math.clamp(py, 0, h - 1);
return texture[py][px];
}
}
@@ -94,7 +94,6 @@ public final class BlockEntityModels {
private static void headTextures(List<String> paths, String headType) {
if (headType == null) { paths.add("entity/skeleton/skeleton"); return; }
switch (headType) {
case "skeleton" -> paths.add("entity/skeleton/skeleton");
case "wither_skeleton" -> paths.add("entity/skeleton/wither_skeleton");
case "zombie" -> paths.add("entity/zombie/zombie");
case "creeper" -> paths.add("entity/creeper/creeper");
@@ -61,11 +61,10 @@ public final class DecorationBaker implements EntityBaker<DecorationState> {
List<EntityCube> cubes = new ArrayList<>(3);
// Wood border 12x12 (behind), leather back 10x10 (1px proud), item 8x8 (in front). Front = local +Z.
if (wood != null) {
Face[] f = new Face[6];
f[si] = new Face(wood, 1, 0, 0, 1, 0, -1); // tileable; flip matches the others
cubes.add(new EntityCube(px(-6, -6, 0), px(6, 6, 1), f, toWorld));
}
// `wood` is never null here (orElse(leather) and leather is non-null past the guard above).
Face[] woodFace = new Face[6];
woodFace[si] = new Face(wood, 1, 0, 0, 1, 0, -1); // tileable; flip matches the others
cubes.add(new EntityCube(px(-6, -6, 0), px(6, 6, 1), woodFace, toWorld));
Face[] back = new Face[6];
back[si] = new Face(leather, 13.0 / 16, 3.0 / 16, 3.0 / 16, 13.0 / 16, 0, -1); // centre 10x10, flipped
cubes.add(new EntityCube(px(-5, -5, 1), px(5, 5, 2), back, toWorld));
@@ -138,26 +138,26 @@ public final class EntityModels {
* fallbacks, so an unknown variant still degrades to the base texture.
*/
private static List<String> variantPaths(String typeKey, String v) {
switch (typeKey) {
case "cat": return List.of("entity/cat/cat_" + v);
case "axolotl": return List.of("entity/axolotl/axolotl_" + v);
case "wolf": return List.of("entity/wolf/wolf_" + v, "entity/wolf/wolf");
case "horse": return List.of("entity/horse/horse_" + HORSE_COLOR.getOrDefault(v, v));
case "llama": return List.of("entity/llama/llama_" + v);
case "cow": return List.of("entity/cow/cow_" + v);
case "pig": return List.of("entity/pig/pig_" + v);
case "chicken": return List.of("entity/chicken/chicken_" + v);
case "frog": return List.of("entity/frog/frog_" + v);
case "panda": return List.of(v.equals("normal") ? "entity/panda/panda" : "entity/panda/panda_" + v);
case "fox": return List.of(v.equals("snow") ? "entity/fox/fox_snow" : "entity/fox/fox");
case "parrot": return List.of("entity/parrot/parrot_" + PARROT_COLOR.getOrDefault(v, v));
case "rabbit": return List.of("entity/rabbit/rabbit_" + RABBIT_TYPE.getOrDefault(v, v));
case "mooshroom": return List.of("entity/cow/mooshroom_" + v);
case "shulker": return List.of("entity/shulker/shulker_" + v);
return switch (typeKey) {
case "cat" -> List.of("entity/cat/cat_" + v);
case "axolotl" -> List.of("entity/axolotl/axolotl_" + v);
case "wolf" -> List.of("entity/wolf/wolf_" + v, "entity/wolf/wolf");
case "horse" -> List.of("entity/horse/horse_" + HORSE_COLOR.getOrDefault(v, v));
case "llama" -> List.of("entity/llama/llama_" + v);
case "cow" -> List.of("entity/cow/cow_" + v);
case "pig" -> List.of("entity/pig/pig_" + v);
case "chicken" -> List.of("entity/chicken/chicken_" + v);
case "frog" -> List.of("entity/frog/frog_" + v);
case "panda" -> List.of(v.equals("normal") ? "entity/panda/panda" : "entity/panda/panda_" + v);
case "fox" -> List.of(v.equals("snow") ? "entity/fox/fox_snow" : "entity/fox/fox");
case "parrot" -> List.of("entity/parrot/parrot_" + PARROT_COLOR.getOrDefault(v, v));
case "rabbit" -> List.of("entity/rabbit/rabbit_" + RABBIT_TYPE.getOrDefault(v, v));
case "mooshroom" -> List.of("entity/cow/mooshroom_" + v);
case "shulker" -> List.of("entity/shulker/shulker_" + v);
// villager/zombie_villager: type/<biome> and profession are transparent OVERLAYS (clothing
// only); the opaque base body is entity/<folder>/<folder> — handled by the generic candidates.
default: return List.of();
}
default -> List.of();
};
}
private static final Map<String, String> HORSE_COLOR = Map.of("dark_brown", "darkbrown");
@@ -10,7 +10,7 @@ public final class ModelCube {
public final double inflate; // px, expands the box on all sides (overlay layers)
public final double[] uv; // 2, box-UV offset (texels)
public final boolean mirror;
/** Optional modern per-face UV, indexed by {@link Direction#ordinal()}: {u, v, w, h} texels (h/w may be negative for flips). Null = use box-UV. */
/** Optional modern per-face UV, indexed by {@code Direction.ordinal()}: {u, v, w, h} texels (h/w may be negative for flips). Null = use box-UV. */
public final double[][] faceUv;
public ModelCube(double[] origin, double[] size, double inflate, double[] uv, boolean mirror) {
@@ -63,7 +63,7 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
if (layers.isEmpty()) return null;
// Head model depends on the texture aspect (skeleton 64x32 vs zombie/player 64x64), so resolve it
// from the chosen texture rather than statically.
CemModelLoader.CemModel model = models.get(modelName(s, layers.get(0).tex));
CemModelLoader.CemModel model = models.get(modelName(s, layers.getFirst().tex));
if (model == null) return null;
Place p = place(s);
@@ -230,7 +230,6 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
return switch (s.kind()) {
case SIGN -> new Place(yaw, SIGN_SCALE, 0, 0, 0);
case WALL_SIGN -> new Place(yaw, SIGN_SCALE, 0, -0.3125, 0.4375); // drop to mid-block, push to wall
case HANGING_SIGN -> new Place(yaw, 1.0, 0, 0, 0);
case WALL_HEAD -> new Place(yaw, 1.0, 0, 0.25, 0.25); // mid-height, against the wall
case WALL_BANNER -> new Place(yaw, 1.0, 0, -0.16, 0.4375);
default -> new Place(yaw, 1.0, 0, 0, 0);
@@ -270,7 +270,6 @@ public final class BlockEntitySnapshotBuilder {
case "CREEPER_HEAD" -> "creeper";
case "DRAGON_HEAD" -> "dragon";
case "PIGLIN_HEAD" -> "piglin";
case "SKELETON_SKULL" -> "skeleton";
case "WITHER_SKELETON_SKULL" -> "wither_skeleton";
default -> "skeleton";
};
@@ -71,8 +71,8 @@ public final class BiomeTintProvider {
int y = (int) ((1.0 - down) * 255.0);
int h = colormap.length;
int w = colormap[0].length;
x = Math.max(0, Math.min(w - 1, x));
y = Math.max(0, Math.min(h - 1, y));
x = Math.clamp(x, 0, w - 1);
y = Math.clamp(y, 0, h - 1);
return 0xFF000000 | (colormap[y][x] & 0xFFFFFF);
}
}
@@ -77,7 +77,7 @@ public final class CameraItems {
/** Returns a copy of {@code camera} with its film count set to {@code newCount} and lore refreshed. */
public static ItemStack withFilmCount(ItemStack camera, int newCount) {
int count = Math.max(0, Math.min(MAX_FILM, newCount));
int count = Math.clamp(newCount, 0, MAX_FILM);
ItemStack copy = camera.clone();
SkullMeta meta = (SkullMeta) copy.getItemMeta();
meta.getPersistentDataContainer().set(Main.getInstance().filmCountKey, PersistentDataType.INTEGER, count);
@@ -129,7 +129,7 @@ public class CraftingListener implements Listener {
return SurvivalRecipes.LOAD.equals(key) || SurvivalRecipes.COPY.equals(key);
}
/** A second photo referencing the same {@link MapView} and picture id as {@code photo}. */
/** A second photo referencing the same {@link org.bukkit.map.MapView} and picture id as {@code photo}. */
private static ItemStack buildPhotoCopy(ItemStack photo) {
MapMeta src = (MapMeta) photo.getItemMeta();
if (!src.hasMapView() || src.getMapView() == null) return null;