Update models to use gt base texture

This commit is contained in:
2025-08-17 14:14:53 +02:00
parent a92bc72136
commit 7b8dd1bad6
25 changed files with 271 additions and 21 deletions

1
.gitignore vendored
View File

@@ -25,3 +25,4 @@ src/generated/resources/.cache/
*.DS_Store
.factorypath
run-data

1
models/lathe.bbmodel Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

View File

@@ -4,6 +4,7 @@ import com.imbgt.ibg.block.MachineSets;
import com.imbgt.ibg.block.ModBlocks;
import com.imbgt.ibg.block.entity.ModBlockEntities;
import com.imbgt.ibg.block.entity.client.AnimatedBlockRenderer;
import com.imbgt.ibg.datagen.TierTextureBakeProvider;
import com.gregtechceu.gtceu.api.machine.MachineDefinition;
import com.gregtechceu.gtceu.api.registry.GTRegistries;
@@ -11,6 +12,7 @@ import com.gregtechceu.gtceu.api.registry.GTRegistries;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -61,4 +63,15 @@ public class IBG {
});
}
}
@Mod.EventBusSubscriber(modid = IBG.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public final class DataGenHook {
@SubscribeEvent
public static void gather(GatherDataEvent e) {
var out = e.getGenerator().getPackOutput();
var ef = e.getExistingFileHelper();
e.getGenerator().addProvider(e.includeClient(), new TierTextureBakeProvider(out, ef));
}
}
}

View File

@@ -12,7 +12,7 @@ import java.util.List;
@UtilityClass
public final class MachineSets {
public static final String NS = "gtceu";
public final String NS = "gtceu";
public record Definition(String suffix, FootprintSpec spec) {
@@ -21,7 +21,7 @@ public final class MachineSets {
}
}
public static final List<Definition> DEFINITIONS = List.of(
public final List<Definition> DEFINITIONS = List.of(
new Definition(
"lathe",
FootprintSpec.builder()
@@ -31,17 +31,17 @@ public final class MachineSets {
.build()));
/** Does this machine belong to any configured family? */
public static boolean matches(ResourceLocation id) {
public boolean matches(ResourceLocation id) {
return DEFINITIONS.stream().anyMatch(f -> f.matches(id));
}
/** The footprint spec for this machine, or null. */
public static FootprintSpec getSpec(ResourceLocation id) {
public FootprintSpec getSpec(ResourceLocation id) {
return DEFINITIONS.stream().filter(f -> f.matches(id)).map(Definition::spec).findFirst().orElse(null);
}
/** Asset key (filename stem) for this machine, or fallback to last segment. */
public static String assetKeyFor(ResourceLocation id) {
public String assetKeyFor(ResourceLocation id) {
return DEFINITIONS.stream().filter(f -> f.matches(id)).map(Definition::suffix).findFirst()
.orElseGet(() -> {
var p = id.getPath();

View File

@@ -28,8 +28,11 @@ public class AnimatedBlockModel<T extends GeoAnimatable> extends GeoModel<T> {
@Override
public ResourceLocation getTextureResource(T anim) {
String key = keyFrom(anim);
return new ResourceLocation(IBG.MOD_ID, "textures/block/" + key + ".png");
if (anim instanceof MetaMachineBlockEntity mme) {
var id = mme.getMetaMachine().getDefinition().getId();
return new ResourceLocation(IBG.MOD_ID, "textures/block/" + id.getPath() + ".png");
}
return new ResourceLocation("ibg", "textures/block/fallback.png");
}
@Override

View File

@@ -0,0 +1,232 @@
// src/main/java/com/imbgt/ibg/datagen/TierTextureBakeProvider.java
package com.imbgt.ibg.datagen;
import com.imbgt.ibg.block.MachineSets;
import com.gregtechceu.gtceu.api.GTValues;
import net.minecraft.Util;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;
import net.minecraftforge.common.data.ExistingFileHelper;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.gson.*;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import javax.imageio.ImageIO;
public final class TierTextureBakeProvider implements DataProvider {
private record TierTiles(BufferedImage side, BufferedImage top, BufferedImage bottom) {}
private final PackOutput out;
private final ExistingFileHelper ef;
public TierTextureBakeProvider(PackOutput out, ExistingFileHelper ef) {
this.out = out;
this.ef = ef;
}
@Override
public String getName() {
return "IBG tier texture baker";
}
@Override
public CompletableFuture<?> run(CachedOutput cache) {
return CompletableFuture.runAsync(() -> {
try {
for (var def : MachineSets.DEFINITIONS) {
String key = def.suffix();
for (int tierId = GTValues.LV; tierId <= GTValues.MAX; tierId++) {
String tier = GTValues.VN[tierId].toLowerCase(Locale.ROOT);
JsonObject geo = readJson("ibg", "geo/" + key + ".geo.json");
JsonObject geometry0 = geo.getAsJsonArray("minecraft:geometry").get(0).getAsJsonObject();
JsonObject description = geometry0.getAsJsonObject("description");
int atlasW = description.get("texture_width").getAsInt();
int atlasH = description.get("texture_height").getAsInt();
BufferedImage atlas = new BufferedImage(atlasW, atlasH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = atlas.createGraphics();
TierTiles tiles = loadTierTiles("gtceu", tier)
.orElseThrow(
() -> new FileNotFoundException("Missing GTCEu casing tiles for tier " + tier));
paintCasingFromGeo(g, tiles, geo);
BufferedImage overlay = tryReadPng("ibg", "textures/block/" + key + ".png");
if (overlay != null)
g.drawImage(overlay, 0, 0, null);
g.dispose();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(atlas, "PNG", baos);
byte[] bytes = baos.toByteArray();
HashCode hash = Hashing.sha256().hashBytes(bytes);
Path dst = out.getOutputFolder()
.resolve("assets/ibg/textures/block/" + tier + "_" +
key + ".png");
// No need to create directories manually; writeIfNeeded will.
cache.writeIfNeeded(dst, bytes, hash);
}
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}, Util.backgroundExecutor());
}
// ---------- resource helpers ----------
private JsonObject readJson(String ns, String path) throws IOException {
try (InputStream is = open(ns, path).orElseThrow(() -> new FileNotFoundException(ns + ":" + path))) {
return JsonParser.parseReader(new InputStreamReader(is)).getAsJsonObject();
}
}
private BufferedImage tryReadPng(String ns, String path) throws IOException {
var opt = open(ns, path);
if (opt.isEmpty())
return null;
try (InputStream is = opt.get()) {
return ImageIO.read(is);
}
}
private Optional<TierTiles> loadTierTiles(String ns, String tier) throws IOException {
String base = "textures/block/casings/voltage/" + tier + "/";
BufferedImage side = tryReadPng(ns, base + "side.png");
BufferedImage top = tryReadPng(ns, base + "top.png");
BufferedImage bot = tryReadPng(ns, base + "bottom.png");
if (side != null && top != null && bot != null)
return Optional.of(new TierTiles(side, top, bot));
return Optional.empty();
}
private Optional<InputStream> open(String ns, String path) throws IOException {
try {
var rl = new ResourceLocation(ns, path);
var res = ef.getResource(rl, PackType.CLIENT_RESOURCES);
if (res != null)
return Optional.of(res.open());
} catch (FileNotFoundException e) {
String cpPath = "assets/" + ns + "/" + path;
var cl = Thread.currentThread().getContextClassLoader();
InputStream is = cl.getResourceAsStream(cpPath);
return Optional.ofNullable(is);
}
return Optional.empty();
}
// ---------- painting ----------
private static void paintTile(Graphics2D g, BufferedImage tile, int x, int y, int w, int h) {
int tw0 = tile.getWidth();
int th0 = tile.getHeight();
for (int yy = 0; yy < h; yy += th0) {
for (int xx = 0; xx < w; xx += tw0) {
int tw = Math.min(tw0, w - xx);
int th = Math.min(th0, h - yy);
g.drawImage(tile, x + xx, y + yy, x + xx + tw, y + yy + th, 0, 0, tw, th, null);
}
}
}
private static void paintFace(Graphics2D g, BufferedImage tile, 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 w = Math.abs(us);
int h = Math.abs(vs);
// clamp to atlas
if (x0 < 0) {
w += x0;
x0 = 0;
}
if (y0 < 0) {
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;
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;
// atlas size from description
JsonObject description = geos.get(0).getAsJsonObject().getAsJsonObject("description");
int atlasW = description.get("texture_width").getAsInt();
int atlasH = description.get("texture_height").getAsInt();
for (var ge : geos) {
var bones = ge.getAsJsonObject().getAsJsonArray("bones");
if (bones == null)
continue;
for (var bEl : bones) {
var cubes = bEl.getAsJsonObject().getAsJsonArray("cubes");
if (cubes == null || !bEl.getAsJsonObject().get("name").getAsString().equals("bone"))
continue;
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()) {
String face = e.getKey(); // north/east/south/west/up/down
var f = e.getValue().getAsJsonObject();
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();
BufferedImage tile = switch (face) {
case "up" -> tiles.top();
case "down" -> tiles.bottom();
default -> tiles.side();
};
paintFace(g, tile, atlasW, atlasH, u, v, us, vs);
}
} else if (cube.has("uv") && cube.get("uv").isJsonArray() && cube.has("size")) {
// Better fallback: use cube sizes per face like Bedrock layout
var size = cube.getAsJsonArray("size");
int sx = size.get(0).getAsInt();
int sy = size.get(1).getAsInt();
int sz = size.get(2).getAsInt();
int u = cube.getAsJsonArray("uv").get(0).getAsInt();
int v = cube.getAsJsonArray("uv").get(1).getAsInt();
// Minimal conservative mapping: fill a sx×sy area with side tile at (u,v)
paintFace(g, tiles.side(), atlasW, atlasH, u, v, sx, sy);
}
}
}
}
}
}

View File

@@ -8,8 +8,6 @@ import com.gregtechceu.gtceu.api.capability.IWorkable;
import com.gregtechceu.gtceu.api.capability.forge.GTCapability;
import com.gregtechceu.gtceu.api.machine.MetaMachine;
import net.minecraftforge.common.util.LazyOptional;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -41,16 +39,13 @@ public abstract class MetaMachineBlockEntityGeoMixin implements GeoBlockEntity {
@Unique
private static boolean ibg$isActuallyRunning(MetaMachine m) {
// Manually enabled?
boolean enabled = MetaMachineBlockEntity.getCapability(m, GTCapability.CAPABILITY_CONTROLLABLE, null)
.map(IControllable::isWorkingEnabled).orElse(true);
boolean active = MetaMachineBlockEntity.getCapability(m, GTCapability.CAPABILITY_WORKABLE, null)
.map(IWorkable::isActive).orElse(false);
return enabled && active;
}
@Unique

View File

@@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "ibg:block/animated_block" }
}
}

View File

@@ -13,17 +13,27 @@
"bones": [
{
"name": "bone",
"pivot": [17, 18, 0],
"pivot": [7, 6, 0],
"cubes": [
{"origin": [-7, 1, -7], "size": [30, 12, 14], "inflate": 1, "uv": [0, 0]}
{
"origin": [-8, 0, -8],
"size": [32, 14, 16],
"uv": {
"north": {"uv": [16, 16], "uv_size": [32, 14]},
"east": {"uv": [0, 16], "uv_size": [16, 14]},
"south": {"uv": [64, 16], "uv_size": [32, 14]},
"west": {"uv": [48, 16], "uv_size": [16, 14]},
"up": {"uv": [16, 0], "uv_size": [32, 16]},
"down": {"uv": [48, 16], "uv_size": [32, -16]}
}
}
]
},
{
"name": "bone2",
"parent": "bone",
"pivot": [17, 18, 0],
"cubes": [
{"origin": [-8, 7, -1], "size": [2, 11, 2], "pivot": [-7, 18, 0], "rotation": [-90, -90, 0], "uv": [0, 26]}
{"origin": [-8, 7, -1], "size": [2, 11, 2], "pivot": [-7, 18, 0], "rotation": [-90, -90, 0], "uv": [0, 30]}
]
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B