Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Texture Atlases #221

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion docs/gui/screens.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The second `#blit` adds an additional integer at the end which represents the ti

#### `blitSprite`

`#blitSprite` is a special implementation of `#blit` where the texture is written to the GUI texture atlas. Most textures that overlay the background, such as the 'burn progress' overlay in furnace GUIs, are sprites. All sprite textures are relative to `textures/gui/sprites` and do not need to specify the file extension.
`#blitSprite` is a special implementation of `#blit` where the texture is written to the GUI [texture atlas][txtatlas]. Most textures that overlay the background, such as the 'burn progress' overlay in furnace GUIs, are sprites. All sprite textures are relative to `textures/gui/sprites` and do not need to specify the file extension.

```java
// Points to 'assets/examplemod/textures/gui/sprites/container/example_container/example_sprite.png'
Expand Down Expand Up @@ -424,3 +424,4 @@ private void registerScreens(RegisterMenuScreensEvent event) {
[component]: ../resources/client/i18n.md#components
[keymapping]: ../misc/keymappings.md#inside-a-gui
[modbus]: ../concepts/events.md#event-buses
[txtatlas]: ../resources/client/textures/atlases.md
276 changes: 276 additions & 0 deletions docs/resources/client/textures/atlases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
# Texture Atlases

Many textures in Minecraft are compiled within Atlases (also known as sprite sheets) for optimisation purposes. Most of them are automaticaly generated by Minecraft when registered throught the use of datapacks or [Registries][reg] (such as block textures).

Optionaly, datapacks can register their own [texture atlas sources][txtatlassource], giving more control over wich sprite or image gets included within an atlas.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not giving more control. The sprite sources quite literally determine what is within the final stitched texture.


Minecraft includes 14 different atlases for different purposes:

|Altas name |Description|
|-----------------|-|
|`blocks` |Block textures|
|`banner_patterns`|Banner patterns|
|`beds` |Every bed variant (i.e. colors)|
|`chest` |Chest textures|
|`shield_patterns`|Shield textures and patterns|
|`shulker_boxes` |Every shulker related textures (Shulker box variants, head and projectile)|
|`signs` |Sign textures|
|`particles` |Particle textures|
|`paintings` |Every painting|
|`mob_effects` |Potion effect icons|
|`gui` |Every GUI wigets (buttons, scroll bars...)|
|`map_decorations`|Icons displayed on a map (banners, locations...)|
|`armor_trims` |Armor trim patterns for every material|
|`decorated_pot` |Texture for the decorated pot and every sherd|
Comment on lines +9 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once again, how are the atlases stored in code? How do I use them?


## Texture Atlas Sources

Texture atlas sources are JSON configuration files in ressource packs, that control wich textures of a resource pack are included in the atlases.

Texture atlas sources are located within the `altases` asset folder, under the namespace of the datapack it is gathering textures from. For a datapack named `example_mod` the directory will be located in `resources/assets/example_mod/atlases`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resources doesn't exist within the compiled jar.


### Creating a texture atlas source

To add textures of a datapack to an atlas, a JSON file with the name of the atlas needs to be declared within the `atlases` asset folder.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this refer to creating a new sprite source list, or adding to an existing sprite source list. I can tell in the codebase it works similar to tags, but it should be explicitly stated.

For example, here is a texture atlas source adding textures to the `blocks` atlas for the `example_mod` datapack: `resources/assets/example_mod/atlases/blocks.json`.

The file will then be able to declare [sprite sources][spritesource] inside of an array named `sources`.
```json5
// Inside blocks.json
{
"sources": [
// This texture altas source does not
// declare any sprite sources.
Comment on lines +42 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then what's the point of this example? Just say 'Add sources here'.

]
}
```

### Sprite sources

Minecraft, by default, includes 4 types of sprite sources:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about NeoForge? They add neoforge:namespaced_directory to only read textures from a given namespace/modid.

Also, you write four when there are five listed below.


|Sprite source type |Description|
|-----------------------|---|
|`directory` |Lists all files in a directory and its subdirectories|
|`single` |Adds a single file|
|`filter` |Removes sprites matching the given pattern|
|`unstitch` |Copies rectangular regions of a file (sprite sheet)|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't just imply a sprite sheet, it means any image.

|`paletted_permutations`|Dynamically generate textures in-memory with color sets|

### Directory source

A `directory` sprite source adds all files of a directory into the targeted atlas, using the name of a file and a prefix to create a [`ResourceLocation`][resourceloc].

|Field |Content|
|--------|--|
|`type` |Should be `minecraft:directory`|
|`prefix`|A prefix that prepends itself to every file's `ResourceLocation` path|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not well explained. It means that when storing the loaded resources, its identifier will be appended with provided prefix on its path. It does not affect the source loading itself, only the identifier to reference.

For example, a given block texture located at assets/examplemod/textures/block/example_texture.png will be evaluated by the block atlas to the id examplemod:example_texture as FileToIdConverter strips out the textures and provided source path. Then, the prefix is added back to the identifier to store the resource, making the texture referenced by specifying examplemod:block/example_texture.

|`source`|The path to the targeted directory (relative to the `assets/<namespace>/textures` directory)|

As an example, the folowing file will add all textures within `assets/exmaple_mod/textures/blocks` to the `blocks` atlas:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will add much more than that. It applies to any namespace.

```json5
// Inside assets/example_mod/atlases/blocks.json
{
"sources": [
{
"type": "minecraft:directory",
// Prepend all paths with block/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong for the reasons explained above.

"prefix": "block/",
// Targets the `assets/exmaple_mod/textures/blocks` directory
"source": "blocks"
}
]
}
```

### Single file source

A `single` sprite source adds a single texture to the targeted atlas.

|Field |Content|
|----------|--|
|`type` |Should be `minecraft:single`
|`resource`|Location of the file (relative to the `assets/<namespace>/textures` directory, implied `.png` and `.png.mcmeta` extension)|
|`sprite` |Optional name for the sprite (defaults to the content of `resource`)|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is not well explained. sprite would be the optional identifier to reference within the codebase. It does not affect the resource itself.


As an example, the folowing file will add `assets/exmaple_mod/textures/misc/smiley_face.png` to the `banner_patterns` atlas:
```json5
// Inside assets/example_mod/atlases/banner_patterns.json
{
"sources": [
{
"type": "minecraft:single",
// Adds `assets/exmaple_mod/textures/misc/smiley_face.png` to banner patterns
"resource": "example_mod:misc/smiley_face",
// replaces the default ResourceLocation
"sprite": "example_mod:banner_patterns/smiley_face"
}
]
}
```

### Filtering resources

A `filter` sprite soure, is not an actual source, it will remove already existing resources from the atlas by matching them with regular expressions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still a sprite source, it just remove entries instead of adding them.


:::info
The order in wich are placed sprite sources matters especialy when using the `filter` source, as it can only remove already existing resources.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to just explicitly state that the source filter can only remove registered resources from previously loaded sources, so this should be added after. Also, this admonition should be a warning.

:::

|Field |Content|
|-----------|--|
|`type` |Should be `minecraft:filter`|
|`namespace`|A regular expression matching the namespace of resources|
|`path` |A reguler experssion matching the path of resources|
Comment on lines +123 to +124
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to state that it uses regex compiled by Pattern as it does have more defined character classes compared to normal regex.


Taking back the [Directory source][spritesource_directory] example, this time the `filter` will exclude all textures located within the `work_in_progress` directory.

```json5
// Inside assets/example_mod/atlases/blocks.json
{
"sources": [
{
"type": "minecraft:directory",
// Prepend all paths with block/
"prefix": "block/",
// Targets the `assets/exmaple_mod/textures/blocks` directory
"source": "blocks"
},
// We place the filter after registering block textures
{
"type": "minecraft:filter",
// Only remove resources inside of the `example_mod` datapack
"namespace": "exmaple_mod",
Comment on lines +142 to +143
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also match example_modrandom, fds_example_mod. You did not specify it has to be the start and end of a string.

// Remove resources that starts woth `block/` and contain the word `work_in_progress`
"path":"block/.*work_in_progress.*"
Comment on lines +144 to +145
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here. It just needs to contain the string, not only start with it.

}
]
}
```

### Unstitching sprite sheets

An `unstitch` sprite source, takes a single file containing multiple sprites and unstitches them to create multiple resources using rectagular regions to locate them within a texture.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pedantic, but it does not need to contain multiple sprites. While that is the most common usecase, you can use it in many different ways.


An `unstitch` sprite source contains the folowing information:
|Field |Content|
|-----------|--|
|`type` |Should be `minecraft:unstitch` |
|`resource` |Location of the file (relative to the `assets/<namespace>/textures` directory, implied `.png` and `.png.mcmeta` extension)|
|`divisor_x`|Used for determining the scale of coordinates on the x axis|
|`divisor_y`|Used for determining the scale of coordinates on the y axis|
Comment on lines +160 to +161
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, the divisors should be larger than the texture in question as these are inversely scaling the coordinates.

|`regions` |List of regions to copy from the source image.|

A `region` is a single sprite within a texture file:
|Field |Content|
|--------|----|
|`sprite`|`ResourceLocation` of the sprite|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the identifier of the resource. Saying it's a resource location could mean anything.

|`x` |x coordinate within the file (before scaling by `divisor_x`)|
|`y` |y coordinate within the file (before scaling by `divisor_y`)|
|`width` |width of the sprite (before scaling by `divisor_x`)|
|`height`|height of the sprite (before scaling by `divisor_y`)|
Comment on lines +168 to +171
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scaling will affect the rectangle that is being copied.


The following file declares 2 sprites contained within the same texture located in `assets/example_mod/textures/gui/sprites.png`:
```json5
// Inside assets/example_mod/atlases/gui.json
{
"sources": [
{
"type": "minecraft:unstitch",
// Targets the `assets/example_mod/textures/gui/sprites.png` file
"resource": "example_mod:gui/sprites",
// Normal scaling
"divisor_x": 1.0,
"divisor_y": 1.0,
"regions": [
// Declares the new `example_mod:gui/batery_full` resource
{
"sprite": "example_mod:gui/batery_full",
"x": 0,
"y": 0,
"width": 8,
"height": 8
},
// Declares the new `example_mod:gui/batery_low` resource
{
"sprite": "example_mod:gui/batery_low",
"x": 8,
"y": 0,
"width": 8,
"height": 8
}
]
}
]
}
```

### Permutating textures

:::info
//// TODO `paletted_permutations`
:::

## Datagen

Like many resources in Minecraft, texture atlas sources can be proceduraly generated using [datagen][dtgn].
To do so, we extend `SpriteSourceProvider` and override the `gather()` method:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention that SpriteSourceProvider is added by neoforge.


```Java
public class MySpriteSourceProvider extends SpriteSourceProvider {

// Parameters obtained via GatherDataEvent
public MySpriteSourceProvider (
PackOutput output,
CompletableFuture<HolderLookup.Provider> lookupProvider
) {
super(output, lookupProvider, "example_mod");
}

@Override
protected void gather ()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should match what's in the example JSON.

{
// Creates a new texture atlas source for the "blocks" atlas
SourceList blocks = atlas(
ResourceLocation.fromNamespaceAndPath("example_mod", "blocks")
);

/* Adds the "resources/assets/example_mod/textures/blocks"
directory as a source for the atlas.

Adding "block/" at the start of every ResourceLocation's path.

This will result in "example_mod:block/<my_block>".
*/
gui.addSource(new DirectoryLister("blocks", "block/"));
Comment on lines +233 to +245
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still wrong. gui doesn't exist, and blocks is never used.

}
}
```

This new subclass of `SpriteSourceProvider` can then be registered under the `GatherDataEvent.Client` event:

```java
@EventBusSubscriber(modid = "example_mod", bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
public class DataGenerators {

@SubscribeEvent
public static void gatherData(GatherDataEvent.Client event) {

DataGenerator generator = event.getGenerator();
PackOutput packOutput = generator.getPackOutput();
CompletableFuture<HolderLookup.Provider> lookupProvider = event.getLookupProvider();

// Registers the new `SpriteSourceProvider`
// The fisrt parameter is `true` because Texture Atlases are client
// resources and we know that this event is only fired for client resources
generator.addProvider(true, new MySpriteSourceProvider(packOutput, lookupProvider));
Comment on lines +263 to +266
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would assume we recommend using createProvider now instead of using this.

}
}
```

[reg]:../../../concepts/registries.md
[txtatlassource]:#texture-atlas-sources
[spritesource]:#sprite-sources
[resourceloc]:../../../misc/resourcelocation.md
[spritesource_directory]:#directory-source
[dtgn]:../../index.md#data-generation
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ To actually be animated and not just be displayed as a distorted texture, there
}
```

[rl]: ../../misc/resourcelocation.md
[rl]: ../../../misc/resourcelocation.md
8 changes: 5 additions & 3 deletions docs/resources/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Resource packs may contain folders with files affecting the following things:

| Folder Name | Contents |
|---------------|-----------------------------------------|
| `atlases` | Texture Atlas Sources |
| `atlases` | [Texture Atlas Sources][txtatlas] |
| `blockstates` | [Blockstate Files][bsfile] |
| `equipment` | [Equipment Info][equipment] |
| `font` | Font Definitions |
Expand Down Expand Up @@ -88,7 +88,7 @@ All data providers extend the `DataProvider` interface and usually require one m
| [`LanguageProvider`][langprovider] | `addTranslations()` | Translations | Client | Also requires passing the language in the constructor. |
| [`ParticleDescriptionProvider`][particleprovider] | `addDescriptions()` | Particle definitions | Client | |
| [`SoundDefinitionsProvider`][soundprovider] | `registerSounds()` | Sound definitions | Client | |
| `SpriteSourceProvider` | `gather()` | Sprite sources / atlases | Client | |
| [`SpriteSourceProvider`][spritesourceprovider] | `gather()` | Sprite sources / atlases | Client | |
| [`AdvancementProvider`][advancementprovider] | `generate()` | Advancements | Server | Make sure to use the NeoForge variant, not the Minecraft one. |
| [`LootTableProvider`][loottableprovider] | `generate()` | Loot tables | Server | Requires extra methods and classes to work properly, see linked article for details. |
| [`RecipeProvider`][recipeprovider] | `buildRecipes(RecipeOutput)` | Recipes | Server | |
Expand Down Expand Up @@ -229,5 +229,7 @@ runs {
[sounds]: client/sounds.md
[tags]: server/tags.md
[tagsprovider]: server/tags.md#datagen
[textures]: client/textures.md
[textures]: client/textures/index.md
[translations]: client/i18n.md#language-files
[txtatlas]: client/textures/atlases.md#texture-atlas-sources
[spritesourceprovider]: client/textures/atlases.md#datagen