Maybe its doing something
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -36,6 +36,10 @@ public final class TierTextureBakeProvider implements DataProvider {
|
||||
|
||||
private record TierTiles(BufferedImage side, BufferedImage top, BufferedImage bottom) {}
|
||||
|
||||
// Tuning knobs for tier tint appearance
|
||||
private static final float TINT_SATURATION_BOOST = 0.15f; // +15% saturation
|
||||
private static final float TINT_LIGHTNESS_BOOST = 0.12f; // +12% lightness to counter multiply darkening
|
||||
|
||||
private final PackOutput out;
|
||||
private final ExistingFileHelper ef;
|
||||
// Tinting removed: always use GT casing tiles + overlay
|
||||
@@ -76,13 +80,18 @@ public final class TierTextureBakeProvider implements DataProvider {
|
||||
// Fallback to plain overlay if tiles are missing
|
||||
IBG.LOGGER.warn("Missing casing tiles for tier {} — rendering overlay only", tier);
|
||||
}
|
||||
|
||||
BufferedImage overlay = tryReadPng("ibg", "textures/block/" + key + ".png");
|
||||
if (overlay != null) {
|
||||
int tierRgb = tilesOpt.map(TierTextureBakeProvider::deriveCasingMidRGB)
|
||||
.orElse(GTValues.VC[tierId]);
|
||||
applyTierTintForFirstMatchingBone(overlay, geo, tierRgb, "tier_tint");
|
||||
g.drawImage(overlay, 0, 0, null);
|
||||
} else {
|
||||
IBG.LOGGER.debug("Overlay texture missing for key {} ({}): {}", key, tier,
|
||||
"assets/ibg/textures/block/" + key + ".png");
|
||||
}
|
||||
|
||||
g.dispose();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
@@ -148,13 +157,248 @@ public final class TierTextureBakeProvider implements DataProvider {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Tier-color tinting removed
|
||||
// --- tier-color tinting on a specific bone region ---
|
||||
|
||||
/**
|
||||
* Multiply-blend the tier color over the UV rectangles of the first matching
|
||||
* bone.
|
||||
* Returns true if any bone matched and was tinted.
|
||||
*/
|
||||
private static void applyTierTintForFirstMatchingBone(BufferedImage atlas, JsonObject geo, int tierRgb,
|
||||
String boneName) {
|
||||
var geos = geo.getAsJsonArray("minecraft:geometry");
|
||||
if (geos == null)
|
||||
return;
|
||||
for (var ge : geos) {
|
||||
var bones = ge.getAsJsonObject().getAsJsonArray("bones");
|
||||
if (bones == null)
|
||||
continue;
|
||||
for (var bEl : bones) {
|
||||
var bone = bEl.getAsJsonObject();
|
||||
if (!bone.has("name"))
|
||||
continue;
|
||||
if (!boneName.equals(bone.get("name").getAsString()))
|
||||
continue;
|
||||
tintBoneUVMultiply(atlas, bone, tierRgb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the given atlas pixels inside the bone's UV rectangles by the
|
||||
* provided tier color.
|
||||
*/
|
||||
private static boolean tintBoneUVMultiply(BufferedImage atlas, JsonObject bone, int tierRgb) {
|
||||
var cubes = bone.getAsJsonArray("cubes");
|
||||
if (cubes == null || cubes.size() == 0)
|
||||
return false;
|
||||
int w = atlas.getWidth();
|
||||
int h = atlas.getHeight();
|
||||
boolean didAny = false;
|
||||
for (var cEl : cubes) {
|
||||
var cube = cEl.getAsJsonObject();
|
||||
if (cube.has("uv") && cube.get("uv").isJsonObject()) {
|
||||
var uv = cube.getAsJsonObject("uv");
|
||||
for (var e : uv.entrySet()) {
|
||||
var f = e.getValue().getAsJsonObject();
|
||||
if (!f.has("uv") || !f.has("uv_size"))
|
||||
continue;
|
||||
int u = f.getAsJsonArray("uv").get(0).getAsInt();
|
||||
int v = f.getAsJsonArray("uv").get(1).getAsInt();
|
||||
int us = f.getAsJsonArray("uv_size").get(0).getAsInt();
|
||||
int vs = f.getAsJsonArray("uv_size").get(1).getAsInt();
|
||||
didAny |= multiplyRect(atlas, clampRect(w, h, u, v, us, vs), tierRgb);
|
||||
}
|
||||
} else if (cube.has("uv") && cube.get("uv").isJsonArray() && cube.has("size")) {
|
||||
var size = cube.getAsJsonArray("size");
|
||||
int sx = size.get(0).getAsInt();
|
||||
int sy = size.get(1).getAsInt();
|
||||
int u = cube.getAsJsonArray("uv").get(0).getAsInt();
|
||||
int v = cube.getAsJsonArray("uv").get(1).getAsInt();
|
||||
didAny |= multiplyRect(atlas, clampRect(w, h, u, v, sx, sy), tierRgb);
|
||||
}
|
||||
}
|
||||
return didAny;
|
||||
}
|
||||
|
||||
private static int[] clampRect(int atlasW, int atlasH, int u, int v, int us, int vs) {
|
||||
int x0 = us >= 0 ? u : u + us;
|
||||
int y0 = vs >= 0 ? v : v + vs;
|
||||
int rw = Math.abs(us);
|
||||
int rh = Math.abs(vs);
|
||||
if (x0 < 0) {
|
||||
rw += x0;
|
||||
x0 = 0;
|
||||
}
|
||||
if (y0 < 0) {
|
||||
rh += y0;
|
||||
y0 = 0;
|
||||
}
|
||||
if (x0 >= atlasW || y0 >= atlasH)
|
||||
return new int[] { 0, 0, 0, 0 };
|
||||
if (x0 + rw > atlasW)
|
||||
rw = atlasW - x0;
|
||||
if (y0 + rh > atlasH)
|
||||
rh = atlasH - y0;
|
||||
if (rw <= 0 || rh <= 0)
|
||||
return new int[] { 0, 0, 0, 0 };
|
||||
return new int[] { x0, y0, rw, rh };
|
||||
}
|
||||
|
||||
private static boolean multiplyRect(BufferedImage atlas, int[] rect, int tierRgb) {
|
||||
int x0 = rect[0], y0 = rect[1], rw = rect[2], rh = rect[3];
|
||||
if (rw <= 0 || rh <= 0)
|
||||
return false;
|
||||
return multiplyRect(atlas, x0, y0, rw, rh, tierRgb);
|
||||
}
|
||||
|
||||
private static boolean multiplyRect(BufferedImage atlas, int x0, int y0, int rw, int rh, int rgb) {
|
||||
int tr = (rgb >> 16) & 0xFF;
|
||||
int tg = (rgb >> 8) & 0xFF;
|
||||
int tb = (rgb) & 0xFF;
|
||||
for (int y = y0; y < y0 + rh; y++) {
|
||||
for (int x = x0; x < x0 + rw; x++) {
|
||||
int argb = atlas.getRGB(x, y);
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
if (a == 0)
|
||||
continue; // skip fully transparent
|
||||
int r = (argb >> 16) & 0xFF;
|
||||
int g = (argb >> 8) & 0xFF;
|
||||
int b = (argb) & 0xFF;
|
||||
r = (r * tr) / 255;
|
||||
g = (g * tg) / 255;
|
||||
b = (b * tb) / 255;
|
||||
atlas.setRGB(x, y, (a << 24) | (r << 16) | (g << 8) | b);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- representative casing color derivation ---
|
||||
private static int deriveCasingMidRGB(TierTiles tiles) {
|
||||
int rgb = sampleRepresentativeRGB(tiles.bottom);
|
||||
if (rgb == 0) rgb = sampleRepresentativeRGB(tiles.top);
|
||||
if (rgb == 0) rgb = sampleRepresentativeRGB(tiles.side);
|
||||
if (rgb == 0) rgb = 0x808080;
|
||||
// Push saturation first for color punch, then lift lightness to offset multiply darkening
|
||||
rgb = adjustSaturation(rgb, TINT_SATURATION_BOOST);
|
||||
return adjustLightness(rgb, TINT_LIGHTNESS_BOOST);
|
||||
}
|
||||
|
||||
private static int sampleRepresentativeRGB(BufferedImage img) {
|
||||
if (img == null) return 0;
|
||||
int w = img.getWidth(), h = img.getHeight();
|
||||
if (w <= 0 || h <= 0) return 0;
|
||||
int x0 = Math.max(0, w / 8), y0 = Math.max(0, h / 8);
|
||||
int x1 = Math.min(w, w - x0), y1 = Math.min(h, h - y0);
|
||||
long sumH = 0, sumS = 0, sumL = 0, n = 0;
|
||||
long sumR = 0, sumG = 0, sumB = 0, nRaw = 0;
|
||||
for (int y = y0; y < y1; y++) {
|
||||
for (int x = x0; x < x1; x++) {
|
||||
int argb = img.getRGB(x, y);
|
||||
int a = (argb >>> 24) & 0xFF;
|
||||
if (a < 16) continue;
|
||||
int r = (argb >> 16) & 0xFF;
|
||||
int g = (argb >> 8) & 0xFF;
|
||||
int b = (argb) & 0xFF;
|
||||
sumR += r;
|
||||
sumG += g;
|
||||
sumB += b;
|
||||
nRaw++;
|
||||
float[] hsl = rgbToHsl(r, g, b);
|
||||
float S = hsl[1], L = hsl[2];
|
||||
if (S >= 0.08f && S <= 0.80f && L >= 0.25f && L <= 0.75f) {
|
||||
sumH += (int) (hsl[0] * 360f);
|
||||
sumS += (int) (S * 100f);
|
||||
sumL += (int) (L * 100f);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n > 0) {
|
||||
float H = (sumH / (float) n) / 360f;
|
||||
float S = (sumS / (float) n) / 100f;
|
||||
float L = (sumL / (float) n) / 100f;
|
||||
if (L < 0.55f) L = 0.55f;
|
||||
else if (L > 0.65f) L = 0.65f;
|
||||
return hslToRgb(H, S, L);
|
||||
} else if (nRaw > 0) {
|
||||
int r = (int) (sumR / nRaw);
|
||||
int g = (int) (sumG / nRaw);
|
||||
int b = (int) (sumB / nRaw);
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int adjustLightness(int rgb, float deltaL) {
|
||||
int r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF;
|
||||
float[] hsl = rgbToHsl(r, g, b);
|
||||
hsl[2] = clamp01(hsl[2] + deltaL);
|
||||
return hslToRgb(hsl[0], hsl[1], hsl[2]);
|
||||
}
|
||||
|
||||
private static int adjustSaturation(int rgb, float deltaS) {
|
||||
int r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF;
|
||||
float[] hsl = rgbToHsl(r, g, b);
|
||||
hsl[1] = clamp01(hsl[1] + deltaS);
|
||||
return hslToRgb(hsl[0], hsl[1], hsl[2]);
|
||||
}
|
||||
|
||||
private static float[] rgbToHsl(int r8, int g8, int b8) {
|
||||
float r = r8 / 255f, g = g8 / 255f, b = b8 / 255f;
|
||||
float max = Math.max(r, Math.max(g, b));
|
||||
float min = Math.min(r, Math.min(g, b));
|
||||
float h, s, l = (max + min) / 2f;
|
||||
if (max == min) {
|
||||
h = 0f;
|
||||
s = 0f;
|
||||
} else {
|
||||
float d = max - min;
|
||||
s = l > 0.5f ? d / (2f - max - min) : d / (max + min);
|
||||
if (max == r) h = (g - b) / d + (g < b ? 6f : 0f);
|
||||
else if (max == g) h = (b - r) / d + 2f;
|
||||
else h = (r - g) / d + 4f;
|
||||
h /= 6f;
|
||||
}
|
||||
return new float[] { h, s, l };
|
||||
}
|
||||
|
||||
private static int hslToRgb(float h, float s, float l) {
|
||||
float r, g, b;
|
||||
if (s == 0f) {
|
||||
r = g = b = l;
|
||||
} else {
|
||||
float q = l < 0.5f ? l * (1f + s) : l + s - l * s;
|
||||
float p = 2f * l - q;
|
||||
r = hueToRgb(p, q, h + 1f / 3f);
|
||||
g = hueToRgb(p, q, h);
|
||||
b = hueToRgb(p, q, h - 1f / 3f);
|
||||
}
|
||||
int R = Math.round(r * 255f), G = Math.round(g * 255f), B = Math.round(b * 255f);
|
||||
return (R << 16) | (G << 8) | B;
|
||||
}
|
||||
|
||||
private static float hueToRgb(float p, float q, float t) {
|
||||
if (t < 0f) t += 1f;
|
||||
if (t > 1f) t -= 1f;
|
||||
if (t < 1f / 6f) return p + (q - p) * 6f * t;
|
||||
if (t < 1f / 2f) return q;
|
||||
if (t < 2f / 3f) return p + (q - p) * (2f / 3f - t) * 6f;
|
||||
return p;
|
||||
}
|
||||
|
||||
private static float clamp01(float v) {
|
||||
return v < 0f ? 0f : (v > 1f ? 1f : v);
|
||||
}
|
||||
|
||||
// --- casing underlay path (tiling GT tiles into UV regions) ---
|
||||
private static void paintTile(Graphics2D g, BufferedImage tile, int x, int y, int w, int h) {
|
||||
int tw = tile.getWidth();
|
||||
int th = tile.getHeight();
|
||||
if (tw <= 0 || th <= 0 || w <= 0 || h <= 0) return;
|
||||
if (tw <= 0 || th <= 0 || w <= 0 || h <= 0)
|
||||
return;
|
||||
|
||||
var oldClip = g.getClip();
|
||||
g.setClip(x, y, w, h);
|
||||
@@ -185,16 +429,20 @@ public final class TierTextureBakeProvider implements DataProvider {
|
||||
h += y0;
|
||||
y0 = 0;
|
||||
}
|
||||
if (x0 >= atlasW || y0 >= atlasH || w <= 0 || h <= 0) return;
|
||||
if (x0 + w > atlasW) w = atlasW - x0;
|
||||
if (y0 + h > atlasH) h = atlasH - y0;
|
||||
if (x0 >= atlasW || y0 >= atlasH || w <= 0 || h <= 0)
|
||||
return;
|
||||
if (x0 + w > atlasW)
|
||||
w = atlasW - x0;
|
||||
if (y0 + h > atlasH)
|
||||
h = atlasH - y0;
|
||||
|
||||
paintTile(g, tile, x0, y0, w, h);
|
||||
}
|
||||
|
||||
private void paintCasingFromGeo(Graphics2D g, TierTiles tiles, JsonObject geo) {
|
||||
var geos = geo.getAsJsonArray("minecraft:geometry");
|
||||
if (geos == null) return;
|
||||
if (geos == null)
|
||||
return;
|
||||
|
||||
JsonObject description = geos.get(0).getAsJsonObject().getAsJsonObject("description");
|
||||
int atlasW = description.get("texture_width").getAsInt();
|
||||
@@ -202,12 +450,15 @@ public final class TierTextureBakeProvider implements DataProvider {
|
||||
|
||||
for (var ge : geos) {
|
||||
var bones = ge.getAsJsonObject().getAsJsonArray("bones");
|
||||
if (bones == null) continue;
|
||||
if (bones == null)
|
||||
continue;
|
||||
for (var bEl : bones) {
|
||||
var bone = bEl.getAsJsonObject();
|
||||
if (!bone.has("name") || !"base".equals(bone.get("name").getAsString())) continue;
|
||||
if (!bone.has("name") || !"base".equals(bone.get("name").getAsString()))
|
||||
continue;
|
||||
var cubes = bone.getAsJsonArray("cubes");
|
||||
if (cubes == null) continue;
|
||||
if (cubes == null)
|
||||
continue;
|
||||
|
||||
for (var cEl : cubes) {
|
||||
var cube = cEl.getAsJsonObject();
|
||||
@@ -239,5 +490,4 @@ public final class TierTextureBakeProvider implements DataProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
// All tinting helpers removed
|
||||
}
|
||||
|
||||
@@ -15,19 +15,6 @@
|
||||
"name": "bb_main",
|
||||
"pivot": [0, 0, 0],
|
||||
"cubes": [
|
||||
{
|
||||
"origin": [-6, 4.44975, -3.5],
|
||||
"size": [6, 7, 7],
|
||||
"pivot": [-3, 7.94975, 0],
|
||||
"rotation": [45, 0, 0],
|
||||
"uv": {
|
||||
"north": {"uv": [44, 14], "uv_size": [6, 7]},
|
||||
"east": {"uv": [32, 34], "uv_size": [7, 7]},
|
||||
"south": {"uv": [0, 45], "uv_size": [6, 7]},
|
||||
"west": {"uv": [37, 14], "uv_size": [7, 7]},
|
||||
"up": {"uv": [6, 45], "uv_size": [6, 7]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"origin": [-6, 12.24264, 5.41422],
|
||||
"size": [6, 4, 4],
|
||||
@@ -91,19 +78,6 @@
|
||||
"down": {"uv": [2, 59], "uv_size": [2, -3]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"origin": [17.93, 5.2916, -3.60607],
|
||||
"size": [6, 4.7, 7],
|
||||
"pivot": [20.93, 7.6416, -0.10607],
|
||||
"rotation": [45, 0, 0],
|
||||
"uv": {
|
||||
"north": {"uv": [26, 45], "uv_size": [6, 5]},
|
||||
"east": {"uv": [12, 45], "uv_size": [7, 5]},
|
||||
"south": {"uv": [45, 37], "uv_size": [6, 5]},
|
||||
"west": {"uv": [19, 45], "uv_size": [7, 5]},
|
||||
"up": {"uv": [39, 33], "uv_size": [6, 7]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"origin": [18, 8.8636, -0.42929],
|
||||
"size": [-3, 1, 1],
|
||||
@@ -609,18 +583,6 @@
|
||||
"up": {"uv": [59, 50], "uv_size": [2, 2]},
|
||||
"down": {"uv": [56, 61], "uv_size": [2, -2]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"origin": [12.71802, 14.65202, 1.7184],
|
||||
"size": [2, 2, 2],
|
||||
"uv": {
|
||||
"north": {"uv": [41, 60], "uv_size": [2, 2]},
|
||||
"east": {"uv": [49, 60], "uv_size": [2, 2]},
|
||||
"south": {"uv": [51, 60], "uv_size": [2, 2]},
|
||||
"west": {"uv": [54, 60], "uv_size": [2, 2]},
|
||||
"up": {"uv": [60, 54], "uv_size": [2, 2]},
|
||||
"down": {"uv": [60, 58], "uv_size": [2, -2]}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -644,18 +606,33 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "led",
|
||||
"pivot": [-7, 3, -7],
|
||||
"name": "tier_tint",
|
||||
"pivot": [-3, 7.94975, 0],
|
||||
"cubes": [
|
||||
{
|
||||
"origin": [-7.6, 1.4, -7.32],
|
||||
"size": [2, 2, 2],
|
||||
"origin": [-6, 4.44975, -3.5],
|
||||
"size": [6, 7, 7],
|
||||
"pivot": [-3, 7.94975, 0],
|
||||
"rotation": [45, 0, 0],
|
||||
"uv": {
|
||||
"north": {"uv": [60, 58], "uv_size": [2, 2]},
|
||||
"east": {"uv": [60, 60], "uv_size": [2, 2]},
|
||||
"south": {"uv": [0, 61], "uv_size": [2, 2]},
|
||||
"west": {"uv": [2, 61], "uv_size": [2, 2]},
|
||||
"up": {"uv": [16, 61], "uv_size": [2, 2]}
|
||||
"north": {"uv": [44, 14], "uv_size": [6, 7]},
|
||||
"east": {"uv": [32, 34], "uv_size": [7, 7]},
|
||||
"south": {"uv": [0, 45], "uv_size": [6, 7]},
|
||||
"west": {"uv": [37, 14], "uv_size": [7, 7]},
|
||||
"up": {"uv": [6, 45], "uv_size": [6, 7]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"origin": [17.93, 5.2916, -3.60607],
|
||||
"size": [6, 4.7, 7],
|
||||
"pivot": [20.93, 7.6416, -0.10607],
|
||||
"rotation": [45, 0, 0],
|
||||
"uv": {
|
||||
"north": {"uv": [26, 45], "uv_size": [6, 5]},
|
||||
"east": {"uv": [12, 45], "uv_size": [7, 5]},
|
||||
"south": {"uv": [45, 37], "uv_size": [6, 5]},
|
||||
"west": {"uv": [19, 45], "uv_size": [7, 5]},
|
||||
"up": {"uv": [39, 33], "uv_size": [6, 7]}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |