Resource Pack Conversion

This page is meant to explain the process of converting a Java resource pack containing predicates for use with Geyser, which currently include custom model data, damage, and unbreaking status. Though some code snipets may be given as examples, the primary purpose of this page is to explain the general process, whether in a manual or automated fashion.

Introduction

Resource packs are inherently complex, and converting and using your particular pack with Geyser may be more complex. This information here is largely general and geared towards basic 2D and 3D models added through custom model data or damage predicates. Broader support for features like block models, blockstates, armor, sounds, emoji, custom bows, eating animations, inventory for 3D models, item frame displays, etc. may be added to converters and Geyser in the future. But recognize that at this time, you are largely on your own with more complex use cases like this, as the systems to automate these processes are still in development. The bulk of this overview is based on what I have learned from writing an automated bash script called java2bedrock. Do note, however, that this page does contain steps that the converter does not currently carry out. Some of this is due to a lack of support on Geyser's end, while some is due to implementation difficulties on the converter's end.

Concept

When converting a resource pack from Java to Bedrock, even if the conversion process has been automated, it is important to understand what is being done. In the case of predicates commonly used in Java Edition, it must be noted that these do not directly exist on Bedrock Edition. However, Bedrock edition does have the ability to implement fully custom items. Geyser gets around this by allowing the user to map Java predicates to a Bedrock custom item. For more information on Bedrock custom items, please refer to the Items section of the Bedrock Wiki. The following is an example of a Geyser item mappings file. More information about these files can be found on the Custom Items page of the Geyser Wiki.

1{
2 "format_version": "1",
3 "items": {
4 "minecraft:apple": [
5 {
6 "name": "test_food",
7 "custom_model_data": 1,
8 "icon": "apple_golden"
9 }
10 ],
11 "minecraft:diamond_sword": [
12 {
13 "name": "test_sword_cmd",
14 "custom_model_data": 1,
15 "icon": "sword"
16 },
17 {
18 "name": "test_sword_damage",
19 "damage_predicate": 1,
20 "icon": "sword"
21 }
22 ]
23 }
24}

Any converter that operates without a Geyser extension to register its items will need to provide a mappings file in the same format.

Gathering Models

In order to begin the conversion process, what needs converting must first be identified. Since no one item entry depends on another, each model gathering subtype may be done in parallel.

Predicate Entries

The first step in the conversion process is finding all relavent predicate entries. This information is needed to construct the mappings file and decide which models need to be converted. This process can be broken down into steps as following steps, which must be performed on all vanilla item model definitions (contained in the Java resource pack under assets/minecraft/models/item/*.json) for each entry contained in their respective overrides arrays:

  1. Store the identifier of the overridden Java item, which matches the name of the model file (e.g. diamond_sword)
  2. Store or parse the relavent predicate entries, which currently consist of .predicate.damage,.predicate.damaged, and .predicate.custom_model_data:
    1. for .predicate.damage, convert the given float to a durability value by multiplying the given float by the max durability of the item, and taking the integer ceiling of the result
    2. for .predicate.damaged, convert the given integer to a boolean, using true if the given integer is 1, and false if the given integer is 0
    3. for .predicate.custom_model_data, simply store the given integer value
  3. From the .model field of the entry, parse and store the following:
    1. the namespace of the predicate entry's model (e.g. in minecraft:item/diamond_sword, the namespace is minecraft)
    2. the model path of the predicate entry's model (e.g. in minecraft:item/diamond_sword, the model path is item/)
    3. the model name of the predicate entry's model (e.g. in minecraft:item/diamond_sword, the model name is diamond_sword)
  4. Hash the Java item identifier and relavent predicate values to construct a unique hash for each predicate entry that will persist accross conversions

Vanilla Item Models

In some cases, the user may want to convert item models that are not implemented through predicate overrides. In such a case, the model will be displayed at a resource pack level with no intervention required from Geyser. A list of models should be made that satisfies the following criteria:

  1. The model's path also exists in the vanilla resources under assets/minecraft/models/item/*.json
  2. Less the overrides array, the contents of the file do not match that of the same model path in the vanilla resources
    1. this is intended to prevent the bloating of the pack by models that function the same as vanilla models

Block Models

Note that while the process for resolving block models for conversion is described here, Geyser does not currently have support for block mappings. The process for identifying which block models need to be converted should follow this outline:

  1. For all files matching assets/minecraft/blockstates/*.json from the vanilla resources, construct an array of all listed models, which are contained under the JSON paths .multipart.[].apply.model and .variants.*.model
  2. For all files in the given resource pack that correspond to a file matching assets/minecraft/blockstates/*.json from the vanilla resources construct an array of all listed models using the JSON paths defined above
  3. Combine these arrays and remove duplicate entries
  4. Construct an array of all model JSONs in the given resource pack
  5. Find the union of the model JSONs array and the array derrived from blockstate entries

Parental Filtering and Resolution

Next, the parental status of each model must be determined, whether the model is a predicate entry or a vanilla item model.

Parental Resolution

Nearly all models in the vanilla resources use parentals in order to prevent the repetition of common model elements. This means that the textures, elements, and display settings of a given model will not always be listed in a given model file. However, these must be known before the conversion of the model can occur. As a result, these must be resolved for any block or item model that has been identified for conversion. This is one of the most computationally expensive parts of the conversion process. Furthermore, all textures must be known for texture atlas generation to be completed, as the optimal atlas generation process relies on finding texture overlap accross models. Given that these values will be used quite a ways down the road, it may be impractical to store these all in memory, especially since some high element count models can sometimes have file sizes in the tens of megabytes. It would likely be useful to store the texture list of each model in memory since those will be used shortly in the atlas generation step. This process can be performed in parallel for each model. In the case of item models parental resolution should attempt to continue until the end of the parent chain is reached or elements, textures, and display settings have been found for the model. This is the same for block models, except resolution can stop when elements and textures are found, since display settings are not relavent to block models.

Parental Filtering

Based on the resolved parent, some decisions must be made with regards to the model conversion that will occur later. The most obvious case of this is the parent minecraft:builtin/generated. Java models that lead to this parental will render an extruded mesh from a provided texture. There are two ways to mimic this behavior on Bedrock, the pros and cons of which will be discussed in the geometry conversion section.

Texture Atlas Generation

Texture atlases must be generated for each item model, as Bedrock models when implemented as entities via attachables may only have one texture. Items with the minecraft:builtin/generated parent should be excluded from this process, since meither method by which they can be displayed allows for UV manipulation. For models that are used only for blocks, it is technically possible to use material instances instead of atlas generation. This would be most helpful in cases where many block models with shared textures are used, and the traditional method of atlas generation would lead to instability due to the size of the generated atlases.

It should be noted that this is the most difficult part of the pack conversion process from a clientside performance perspective. Since Bedrock supports mobile devices, it is unreasonable to expect that all Bedrock players will have access to a desktop grade GPU. At a maximum, one should aim for generated atlases to be no larger than 4096, though in particularly low spec devices like the Nintendo Switch, even this may be pushing the envelope.

Atlas Inclusion

For our purposes, atlas inclusion will be the process of deciding which textures will be combined into atlases. In my opinion, the optimal method for atlas inclusion follows a proposed algorithim, which should be looped over each item model's texture list. The first item model texture list should be placed as the first entry in a list of texture lists. For all subsequent item model texture lists:

  1. Check to see if the new item model texture lists has any overlapping textures with the current list of texture lists
  2. Any texture lists that overlap with new item model texture list should be combined with each other
  3. If no texture lists overlap with the new item model texture list, the item model texture list should be placed as its own entry in the list of texture lists
  4. The process should continue to the next item model texture list until all item model texture lists have been itterated over

There are, however, some drawbacks to this method. As previously stated, the size of generated atlases should be kept at a minimum. In packs in which models use mutliple large shared textures, this will lead to the generation of extremely large atlases. Unfortunately, the solution to this problem will often be to reduce texture resolution and reduce the size of shared texture networks in cases where they lead to this result. Such optimizations will likely need to be done manually.

Atlas Construction

Atlases should, of course, be constructed to minimize empty space. In addition, the location of all textures on atlases must be recorded. This is needed to direct UV shifts in the actual model conversion process. The X and Y pixel coordinates and texture path are sufficent.

Geometry Conversion

 Page Contributors
Kas-tle