diff --git a/src/main/java/com/cricketcraft/chisel/api/rendering/TextureType.java b/src/main/java/com/cricketcraft/chisel/api/rendering/TextureType.java index c4f1462b..b6cd6744 100644 --- a/src/main/java/com/cricketcraft/chisel/api/rendering/TextureType.java +++ b/src/main/java/com/cricketcraft/chisel/api/rendering/TextureType.java @@ -31,6 +31,7 @@ import team.chisel.ctmlib.ISubmapManager; import team.chisel.ctmlib.RenderBlocksCTM; import team.chisel.ctmlib.RenderBlocksColumn; import team.chisel.ctmlib.TextureSubmap; +import team.chisel.ctmlib.TextureSubmapCTM; /** * Handles all default {@link ISubmapManager} behavior @@ -197,8 +198,8 @@ public enum TextureType { IIcon baseIcon = register.registerIcon(modName + ":" + texturePath); return Triple.of( baseIcon, - new TextureSubmap(register.registerIcon(modName + ":" + texturePath + "-ctm"), 4, 4), - new TextureSubmap(baseIcon, 2, 2) + new TextureSubmapCTM(register.registerIcon(modName + ":" + texturePath + "-ctm"), 4), + new TextureSubmapCTM(baseIcon, 2) ); } diff --git a/src/main/java/com/cricketcraft/chisel/mixins/MixinFallbackResourceManager.java b/src/main/java/com/cricketcraft/chisel/mixins/MixinFallbackResourceManager.java index 3457cc0b..2a0c5b11 100644 --- a/src/main/java/com/cricketcraft/chisel/mixins/MixinFallbackResourceManager.java +++ b/src/main/java/com/cricketcraft/chisel/mixins/MixinFallbackResourceManager.java @@ -19,8 +19,8 @@ import team.chisel.ctmlib.TextureSubmapResource; /** * Provides generated resources for independently stitched CTM submap sprites. *
- * For example, a request for chisel:textures/blocks/__chisel_submap/4x4/2_1/foo.png is resolved from
- * chisel:textures/blocks/foo.png and cropped to the requested cell. This lets Minecraft and other mods treat each
+ * For example, a request for chisel:textures/blocks/__chisel_submap/2x2/1_0/foo.png is resolved from
+ * chisel:textures/blocks/foo.png and cropped to its top-right tile. This lets Minecraft and other mods treat each
* submap sprite like an ordinary texture resource.
*/
@Mixin(FallbackResourceManager.class)
@@ -54,6 +54,7 @@ public abstract class MixinFallbackResourceManager {
List
+ * The number of sub-icons is determined by the submap's width and height,
+ * while spriteColumns and spriteRows determine how many separate atlas sprites
+ * are used. Each sprite can contain one or more sub-icons.
+ *
+ * For example, a 4x4 submap backed by a 2x2 sprite grid uses four atlas sprites,
+ * each containing a 2x2 group of sub-icons.
+ */
+ protected final void registerSpriteGrid(TextureMap textureMap, int spriteColumns, int spriteRows) {
if (baseIcon == null || width <= 0 || height <= 0) {
return;
}
+ if (spriteColumns <= 0 || spriteRows <= 0 || width % spriteColumns != 0 || height % spriteRows != 0) {
+ throw new IllegalArgumentException(
+ "Logical submap " + width
+ + "x"
+ + height
+ + " cannot be backed by sprite grid "
+ + spriteColumns
+ + "x"
+ + spriteRows);
+ }
ResourceLocation sourceLocation = new ResourceLocation(baseIcon.getIconName());
+ IIcon[][] sprites = new IIcon[spriteColumns][spriteRows];
+
+ for (int spriteX = 0; spriteX < spriteColumns; spriteX++) {
+ for (int spriteY = 0; spriteY < spriteRows; spriteY++) {
+ sprites[spriteX][spriteY] = getBackingSprite(
+ textureMap,
+ sourceLocation,
+ spriteColumns,
+ spriteRows,
+ spriteX,
+ spriteY);
+ }
+ }
+
+ int cellsPerSpriteX = width / spriteColumns;
+ int cellsPerSpriteY = height / spriteRows;
+ boolean wholeSprite = cellsPerSpriteX == 1 && cellsPerSpriteY == 1;
+
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
- String name = TextureSubmapResource.getSubIconName(sourceLocation, width, height, x, y);
- icons[x][y] = textureMap.registerIcon(name);
+ IIcon sprite = sprites[x / cellsPerSpriteX][y / cellsPerSpriteY];
+ icons[x][y] = wholeSprite ? sprite
+ : new TextureRegion(
+ sprite,
+ cellsPerSpriteX,
+ cellsPerSpriteY,
+ x % cellsPerSpriteX,
+ y % cellsPerSpriteY);
}
}
}
+
+ private IIcon getBackingSprite(TextureMap textureMap, ResourceLocation sourceLocation, int columns, int rows, int x,
+ int y) {
+
+ if (columns == 1 && rows == 1) {
+ return baseIcon;
+ }
+
+ String name = TextureSubmapResource.getSubIconName(sourceLocation, columns, rows, x, y);
+ return textureMap.registerIcon(name);
+ }
+
+ private static final class TextureRegion implements IIcon {
+
+ private final IIcon parent;
+ private final int columns;
+ private final int rows;
+ private final int x;
+ private final int y;
+
+ private TextureRegion(IIcon parent, int columns, int rows, int x, int y) {
+ this.parent = parent;
+ this.columns = columns;
+ this.rows = rows;
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public float getMinU() {
+ return getInterpolatedU(0);
+ }
+
+ @Override
+ public float getMaxU() {
+ return getInterpolatedU(16);
+ }
+
+ @Override
+ public float getInterpolatedU(double u) {
+ return parent.getInterpolatedU((16.0 * x + u) / columns);
+ }
+
+ @Override
+ public float getMinV() {
+ return getInterpolatedV(0);
+ }
+
+ @Override
+ public float getMaxV() {
+ return getInterpolatedV(16);
+ }
+
+ @Override
+ public float getInterpolatedV(double v) {
+ return parent.getInterpolatedV((16.0 * y + v) / rows);
+ }
+
+ @Override
+ public String getIconName() {
+ return parent.getIconName();
+ }
+
+ @Override
+ public int getIconWidth() {
+ return parent.getIconWidth() / columns;
+ }
+
+ @Override
+ public int getIconHeight() {
+ return parent.getIconHeight() / rows;
+ }
+ }
}
diff --git a/src/main/java/team/chisel/ctmlib/TextureSubmapCTM.java b/src/main/java/team/chisel/ctmlib/TextureSubmapCTM.java
new file mode 100644
index 00000000..41f2958d
--- /dev/null
+++ b/src/main/java/team/chisel/ctmlib/TextureSubmapCTM.java
@@ -0,0 +1,27 @@
+package team.chisel.ctmlib;
+
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.util.IIcon;
+
+/**
+ * A CTM submap that keeps each tile in one atlas sprite while exposing its four quadrants as sub-icons.
+ */
+public final class TextureSubmapCTM extends TextureSubmap {
+
+ /**
+ * @param baseIcon The registered texture sheet.
+ * @param size The number of logical quadrants per axis: 4 for the CTM sheet, 2 for the normal texture.
+ */
+ public TextureSubmapCTM(IIcon baseIcon, int size) {
+ super(baseIcon, size, size);
+
+ if (size <= 0 || (size & 1) != 0) {
+ throw new IllegalArgumentException("CTM submap size must be a positive even number: " + size);
+ }
+ }
+
+ @Override
+ public void registerSubIcons(TextureMap textureMap) {
+ registerSpriteGrid(textureMap, getWidth() / 2, getHeight() / 2);
+ }
+}
diff --git a/src/main/java/team/chisel/ctmlib/TextureSubmapResource.java b/src/main/java/team/chisel/ctmlib/TextureSubmapResource.java
index eb11c40a..4137c39a 100644
--- a/src/main/java/team/chisel/ctmlib/TextureSubmapResource.java
+++ b/src/main/java/team/chisel/ctmlib/TextureSubmapResource.java
@@ -6,9 +6,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
import javax.imageio.IIOImage;
@@ -35,7 +35,7 @@ public final class TextureSubmapResource implements IResource {
private record SubmapCacheKey(ResourceLocation source, int columns, int rows) {}
@Nullable
- private static HashMap