Split up turtle textures

Turtles currently read their textures from a single 128x128 sprite
sheet. Most of this texture is unused which means we end up wasting a
lot of the block texture atlas[^1].

This change splits up the turtle textures into individual 32x32
textures[^2], one for each side, and then an additional backpack
texture.

I'm very sorry to any resource pack artists out there. The
tools/update-resources.py script will update existing packs, but does
not (currently) handle non-standard resolutions.

[^1]: It used to be worse: https://github.com/dan200/ComputerCraft/issues/145

[^2]: Turtle textures are a bit weird, in that they mostly *look* 16x16,
  but have some detail in places.
This commit is contained in:
Jonathan Coates 2024-04-30 21:13:32 +01:00
parent 6e9799316a
commit 666f133680
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
49 changed files with 171 additions and 87 deletions

View File

@ -98,7 +98,6 @@ override fun visitFile(fileDetails: FileVisitDetails) {
}
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
println(sharedFiles)
// Copy shared files to the common directory
fsOperations.sync {

View File

@ -1 +1,12 @@
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_advanced"}}
{
"parent": "computercraft:block/turtle_base",
"textures": {
"back": "computercraft:block/turtle_advanced_back",
"backpack": "computercraft:block/turtle_advanced_backpack",
"bottom": "computercraft:block/turtle_advanced_bottom",
"front": "computercraft:block/turtle_advanced_front",
"left": "computercraft:block/turtle_advanced_left",
"right": "computercraft:block/turtle_advanced_right",
"top": "computercraft:block/turtle_advanced_top"
}
}

View File

@ -1 +1,12 @@
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_normal"}}
{
"parent": "computercraft:block/turtle_base",
"textures": {
"back": "computercraft:block/turtle_normal_back",
"backpack": "computercraft:block/turtle_normal_backpack",
"bottom": "computercraft:block/turtle_normal_bottom",
"front": "computercraft:block/turtle_normal_front",
"left": "computercraft:block/turtle_normal_left",
"right": "computercraft:block/turtle_normal_right",
"top": "computercraft:block/turtle_normal_top"
}
}

View File

@ -38,6 +38,9 @@
class BlockModelProvider {
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
private static final TextureSlot LEFT = TextureSlot.create("left");
private static final TextureSlot RIGHT = TextureSlot.create("right");
private static final TextureSlot BACKPACK = TextureSlot.create("backpack");
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
@ -58,7 +61,7 @@ class BlockModelProvider {
private static final ModelTemplate TURTLE = new ModelTemplate(
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_base")),
Optional.empty(),
TextureSlot.TEXTURE
TextureSlot.FRONT, TextureSlot.BACK, TextureSlot.TOP, TextureSlot.BOTTOM, LEFT, RIGHT, BACKPACK
);
private static final ModelTemplate TURTLE_UPGRADE_LEFT = new ModelTemplate(
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_upgrade_base_left")),
@ -167,7 +170,16 @@ private static void registerComputer(BlockModelGenerators generators, ComputerBl
}
private static void registerTurtle(BlockModelGenerators generators, TurtleBlock block) {
var model = TURTLE.create(block, TextureMapping.defaultTexture(block), generators.modelOutput);
var model = TURTLE.create(block, new TextureMapping()
.put(TextureSlot.FRONT, getBlockTexture(block, "_front"))
.put(TextureSlot.BACK, getBlockTexture(block, "_back"))
.put(TextureSlot.TOP, getBlockTexture(block, "_top"))
.put(TextureSlot.BOTTOM, getBlockTexture(block, "_bottom"))
.put(LEFT, getBlockTexture(block, "_left"))
.put(RIGHT, getBlockTexture(block, "_right"))
.put(BACKPACK, getBlockTexture(block, "_backpack")),
generators.modelOutput
);
generators.blockStateOutput.accept(
MultiVariantGenerator.multiVariant(block, Variant.variant().with(VariantProperties.MODEL, model))
.with(createHorizontalFacingDispatch())

View File

@ -2,30 +2,30 @@
"parent": "block/block",
"render_type": "translucent",
"textures": {
"particle": "#texture"
"particle": "#front"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#bottom" },
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
}
}
]

View File

@ -1,53 +1,67 @@
{
"parent": "computercraft:block/turtle_base",
"textures": {
"texture": "computercraft:block/turtle_colour"
"texture": "computercraft:block/turtle_colour",
"body_back": "computercraft:block/turtle_colour_body_back",
"body_backpack": "computercraft:block/turtle_colour_body_backpack",
"body_bottom": "computercraft:block/turtle_colour_body_bottom",
"body_front": "computercraft:block/turtle_colour_body_front",
"body_left": "computercraft:block/turtle_colour_body_left",
"body_right": "computercraft:block/turtle_colour_body_right",
"body_top": "computercraft:block/turtle_colour_body_top",
"frame_back": "computercraft:block/turtle_colour_frame_back",
"frame_backpack": "computercraft:block/turtle_colour_frame_backpack",
"frame_bottom": "computercraft:block/turtle_colour_frame_bottom",
"frame_front": "computercraft:block/turtle_colour_frame_front",
"frame_left": "computercraft:block/turtle_colour_frame_left",
"frame_right": "computercraft:block/turtle_colour_frame_right",
"frame_top": "computercraft:block/turtle_colour_frame_top"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 5.75, 8.5, 2.75, 5.75 ], "texture": "#texture", "tintindex": 0 },
"up": { "uv": [ 8.75, 5.75, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
"north": { "uv": [ 11.5, 11.5, 8.5, 8.5 ], "texture": "#texture", "tintindex": 0 },
"south": { "uv": [ 5.75, 11.5, 2.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
"west": { "uv": [ 8.5, 11.5, 5.75, 8.555 ], "texture": "#texture", "tintindex": 0 },
"east": { "uv": [ 2.75, 11.5, 0, 8.5 ], "texture": "#texture", "tintindex": 0 }
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_bottom", "tintindex": 0 },
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_top", "tintindex": 0 },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_front", "tintindex": 0 },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_back", "tintindex": 0 },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_left", "tintindex": 0 },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_right", "tintindex": 0 }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 11.75, 6.25, 9.25, 5.75 ], "texture": "#texture", "tintindex": 0 },
"up": { "uv": [ 14.25, 5.75, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
"south": { "uv": [ 11.75, 8, 9.25, 6.25 ], "texture": "#texture", "tintindex": 0 },
"west": { "uv": [ 12.25, 8, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
"east": { "uv": [ 9.25, 8, 8.75, 6.25 ], "texture": "#texture", "tintindex": 0 }
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#body_backpack", "tintindex": 0 },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#body_backpack", "tintindex": 0 },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#body_backpack", "tintindex": 0 },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#body_backpack", "tintindex": 0 },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#body_backpack", "tintindex": 0 }
}
},
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_bottom" },
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_top" },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_front" },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_back" },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_left" },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_right" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#frame_backpack" },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#frame_backpack" },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#frame_backpack" },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#frame_backpack" },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#frame_backpack" }
}
}
]

View File

@ -1,6 +1,35 @@
{
"parent": "computercraft:block/turtle_overlay",
"parent": "block/block",
"textures": {
"texture": "computercraft:block/turtle_elf_overlay"
}
}
"back": "computercraft:block/turtle_elf_overlay_back",
"backpack": "computercraft:block/turtle_elf_overlay_backpack",
"front": "computercraft:block/turtle_elf_overlay_front",
"left": "computercraft:block/turtle_elf_overlay_left",
"right": "computercraft:block/turtle_elf_overlay_right",
"top": "computercraft:block/turtle_elf_overlay_top"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
}
}
]
}

View File

@ -1,43 +0,0 @@
{
"parent": "block/block",
"textures": {
"particle": "#texture"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 2.75, 0, 5.75, 2.75 ], "texture": "#texture" },
"up": { "uv": [ 5.75, 0, 8.75, 2.75 ], "texture": "#texture" },
"north": { "uv": [ 8.5, 5.75, 11.5, 2.75 ], "texture": "#texture" },
"south": { "uv": [ 2.75, 5.75, 5.75, 2.75 ], "texture": "#texture" },
"west": { "uv": [ 0, 5.75, 2.75, 2.75 ], "texture": "#texture" },
"east": { "uv": [ 5.75, 5.75, 8.5, 2.75 ], "texture": "#texture" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 9.25, 0, 11.75, 0.5 ], "texture": "#texture" },
"up": { "uv": [ 11.75, 0, 14.25, 0.5 ], "texture": "#texture" },
"south": { "uv": [ 9.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
"west": { "uv": [ 8.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
"east": { "uv": [ 11.75, 2.25, 12.25, 0.5 ], "texture": "#texture" }
}
},
{
"from": [ 1.5, 1.5, 1.5 ],
"to": [ 14.5, 14.5, 13.5 ],
"faces": {
"down": { "uv": [ 2.75, 8, 5.75, 10.75 ], "texture": "#texture" },
"up": { "uv": [ 5.75, 8, 8.75, 10.75 ], "texture": "#texture" },
"north": { "uv": [ 8.5, 13.75, 11.5, 10.75 ], "texture": "#texture" },
"south": { "uv": [ 2.75, 13.75, 5.75, 10.75 ], "texture": "#texture" },
"west": { "uv": [ 0, 13.75, 2.75, 10.75 ], "texture": "#texture" },
"east": { "uv": [ 5.75, 13.75, 8.5, 10.75 ], "texture": "#texture" }
}
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 154 B

51
tools/update-resources.py Normal file → Executable file
View File

@ -76,6 +76,45 @@ def unstitch_corners(input_file: pathlib.Path, family: str):
sidebar.save(output_dir / f"sidebar_{family}.png")
def unstitch_turtle(input_file: pathlib.Path, family: str, *, dy: int = 0):
input = Image.open(input_file)
output_dir = input_file.parent
fragments = [
("bottom", 22, 0, 24, 22, Image.Transpose.ROTATE_180),
("top", 46, 0, 24, 22, Image.Transpose.FLIP_LEFT_RIGHT),
("right", 0, 22, 22, 24, Image.Transpose.ROTATE_180),
("back", 22, 22, 24, 24, Image.Transpose.ROTATE_180),
("left", 46, 22, 22, 24, Image.Transpose.ROTATE_180),
("front", 68, 22, 24, 24, Image.Transpose.ROTATE_180),
]
for suffix, x, y, w, h, transform in fragments:
if family == "elf_overlay" and suffix == "bottom":
continue
slice = input.crop(box(x, dy + y, w, h)).transpose(transform)
fragment = Image.new("RGBA", (32, 32))
fragment.paste(slice)
fragment.save(output_dir / f"turtle_{family}_{suffix}.png")
backpack = Image.new("RGBA", (32, 32))
backpack.paste(
input.crop(box(70, dy + 4, 28, 14)).transpose(Image.Transpose.ROTATE_180),
(0, 4),
)
backpack.paste(
input.crop(box(74, dy + 0, 20, 4)).transpose(Image.Transpose.ROTATE_180),
(4, 18),
)
backpack.paste(
input.crop(box(94, dy + 0, 20, 4)).transpose(Image.Transpose.FLIP_LEFT_RIGHT),
(4, 0),
)
backpack.save(output_dir / f"turtle_{family}_backpack.png")
def main() -> None:
spec = argparse.ArgumentParser()
spec.add_argument("dir", type=pathlib.Path)
@ -97,6 +136,18 @@ def main() -> None:
unstitch_corners(path, family)
transformed.append(path)
for family in ("normal", "advanced", "elf_overlay"):
path = texture_path / "block" / f"turtle_{family}.png"
if path.exists():
unstitch_turtle(path, family)
transformed.append(path)
path = texture_path / "block" / f"turtle_colour.png"
if path.exists():
unstitch_turtle(path, "colour_frame")
unstitch_turtle(path, "colour_body", dy=46)
transformed.append(path)
if len(transformed) == 0:
print("No files were transformed")
return