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

Add a FabricBlockModelSupplier, FabricTextureMap and SimpleBlockStateSupplier for easier data generation with Block Models & States. #3685

Open
wants to merge 10 commits into
base: 1.20.4
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package net.fabricmc.fabric.api.datagen.v1;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.util.Identifier;
import java.util.HashMap;
import java.util.function.Supplier;
import java.util.Map;

/**
* Acceptable class for {@link net.minecraft.data.client.BlockStateModelGenerator}'s modelCollector.
* makes generating more specific block models easier for modders.
*/

public class FabricBlockModelSupplier implements Supplier<JsonElement> {
protected final JsonObject jsonObject;

/**
* Constructor for vanilla parent types.
*
* @param type The parent type for the model.
*/
public FabricBlockModelSupplier(String type)
{
this.jsonObject = new JsonObject();
this.jsonObject.addProperty("parent", "minecraft:block/" + type);
}

/**
* Have an acceptable <modID> parameter in case of custom model parents.
*
* @param modID The modID as the prefix to the parent type in the Identifier.
* @param type The parent type for the model.
*/
public FabricBlockModelSupplier(String modID, String type)
{
this.jsonObject = new JsonObject();
this.jsonObject.addProperty("parent", modID + ":block/" + type);
}

/**
* Add HashMap textures to the model's JsonObject.
*
* @param fabricTextureMap {@link FabricTextureMap} containing the data for the textures.
*/
public FabricBlockModelSupplier addTextureData(FabricTextureMap fabricTextureMap)
{
HashMap<String, Identifier> textureMap = fabricTextureMap.get();
JsonObject textureData = new JsonObject();
for (Map.Entry<String, Identifier> entry : textureMap.entrySet()) {
String key = entry.getKey();
Identifier identifier = entry.getValue();
textureData.addProperty(key, identifier.getNamespace() + ":" + identifier.getPath());
}

this.jsonObject.add("textures", textureData);
return this;
}

/**
* Add a sample texture as 'all' of the textures. Can only be used on the cube_all parent.
*
* @param texture {@link Identifier} for the location of the texture.
*/
public FabricBlockModelSupplier simpleCubeAllTextures(Identifier texture)
{
JsonObject textureData = new JsonObject();
textureData.addProperty("all", texture.getNamespace() + ":" + texture.getPath());

this.jsonObject.add("textures", textureData);
return this;
}

/**
* Returns the {@link JsonObject} for the Model.
*
* @return the supplier's {@link JsonObject}
*/
@Override
public JsonElement get() {
return this.jsonObject;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package net.fabricmc.fabric.api.datagen.v1;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import net.minecraft.util.Identifier;

/**
* Constructor class for making Maps for BlockModel textures.
* {@see FabricBlockModelSupplier}
*/

public class FabricTextureMap {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain the difference between this and vanilla TextureMap?

Copy link
Author

Choose a reason for hiding this comment

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

Can't actually remember why I wrote that, I will probably have to exclude it from the commits - I'll look into it soon though as I'm currently away.

Copy link
Author

@karatebiscuit karatebiscuit Apr 15, 2024

Choose a reason for hiding this comment

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

The difference between the two appears to be that the FabricTextureMap will just return the raw HashMap because it is much easier to access that way, the vanilla TextureMap appears to have no direct way to actually get the entries from a separate class, the existence of FabricTextureMap is to make it easier to create inline without the use of double-bracket indentation (since some don't know of it) for HashMaps, although initially it was just taking in a HashMap.

This is how we get the entries for a FabricTextureMap:

HashMap<String, Identifier> textureMap = fabricTextureMap.get();

You can't get the entries from a TextureMap - in fact, you can't access any of the inputted data without at least having one bit of pre-existing data. I'm contemplating whether to just revert back to the HashMap being a parameter or keeping it as FabricTextureMap, since some could argue it is technically redundant.

Choose a reason for hiding this comment

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

couldn't this be a simple interface injection to add a method to do so?

Copy link
Author

@karatebiscuit karatebiscuit Apr 15, 2024

Choose a reason for hiding this comment

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

Yeah, it doesn't really matter to me which implementation we go for (HashMap parameter, interface injection, etc.) - I'm mainly focused on the FabricBlockModelSupplier and the SimpleBlockStateSupplier, the existence of FabricTextureMap isn't necessarily required, although helpful. It's more like syntax sugar.

private final HashMap<String, Identifier> map;
private final List<String> bufferNames;

/**
* Constructor for the FabricTextureMap. Sets the private variables for later accession.
*
* @param textureNames Different texture names.
*/
public FabricTextureMap(String... textureNames) {
this.map = new HashMap<>();
this.bufferNames = Arrays.stream(textureNames).toList();
}

/**
* Link the texture names to the locations of their textures.
*
* @param textureLocations Identifiers for the location of each texture.
*/
public FabricTextureMap set(Identifier... textureLocations) {
List<Identifier> textureList = Arrays.stream(textureLocations).toList();
int difference = textureList.size() - this.bufferNames.size();
if (difference > 0) {
throw new IllegalStateException(String.format("You need to provide %s more texture names to allocate to texture locations!", difference));
}
if (difference < 0) {
throw new IllegalStateException(String.format("You need to provide %s more texture locations to link to texture names!", -difference));
}

for (int i = 0; i < bufferNames.size(); i++) {
map.put(bufferNames.get(i), textureList.get(i));
}

return this;
}

/**
* Return the resulting {@link HashMap}.
*
* @return this HashMap.
*/
public HashMap<String, Identifier> get() {
return this.map;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package net.fabricmc.fabric.api.datagen.v1;

import com.google.gson.JsonElement;

import com.google.gson.JsonObject;

import net.minecraft.block.Block;
import net.minecraft.data.client.BlockStateSupplier;
import net.minecraft.util.Identifier;

/**
* Basic constructor class for generating non-variant aligned blockstates,
* since there is no built-in class for non-variant blockstates.
*/
public class SimpleBlockStateSupplier implements BlockStateSupplier {
private final Block block;
private final JsonObject jsonObject;

/**
* Constructor for the SimpleBlockStateSupplier. Generates the entire jsonObject.
*
* @param block Block to be applied to.
* @param modelLocation Location of the block's default model.
*/
public SimpleBlockStateSupplier(Block block, Identifier modelLocation)
{
this.block=block;
this.jsonObject=new JsonObject();

JsonObject variants = new JsonObject();
JsonObject defaultVariant = new JsonObject();
defaultVariant.addProperty("model", modelLocation.getNamespace() + ":" + modelLocation.getPath());
variants.add("", defaultVariant);

this.jsonObject.add("variants", variants);
}

/**
* Returns the block this is being applied to.
*
* @return this block
*/
@Override
public Block getBlock() {
return this.block;
}

/**
* Returns the resulting {@link JsonElement} from the class.
*
* @return this jsonObject
*/
@Override
public JsonElement get() {
return this.jsonObject;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;

import net.fabricmc.fabric.api.datagen.v1.FabricBlockModelSupplier;

import net.fabricmc.fabric.api.datagen.v1.FabricTextureMap;

import net.fabricmc.fabric.api.datagen.v1.SimpleBlockStateSupplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -294,11 +302,29 @@ private TestModelProvider(FabricDataOutput output) {

@Override
public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) {
blockStateModelGenerator.registerSimpleCubeAll(SIMPLE_BLOCK);
// blockStateModelGenerator.registerSimpleCubeAll(SIMPLE_BLOCK);
blockStateModelGenerator.registerSimpleCubeAll(BLOCK_WITHOUT_ITEM);
blockStateModelGenerator.registerSimpleCubeAll(BLOCK_WITHOUT_LOOT_TABLE);
blockStateModelGenerator.registerSimpleCubeAll(BLOCK_WITH_VANILLA_LOOT_TABLE);
blockStateModelGenerator.registerSimpleCubeAll(BLOCK_THAT_DROPS_NOTHING);

// registerSimpleCubeAll() -> FabricBlockModelSupplier::new
blockStateModelGenerator.blockStateCollector.accept(new SimpleBlockStateSupplier(SIMPLE_BLOCK, new Identifier(MOD_ID, "simple_block")));
blockStateModelGenerator.modelCollector.accept(new Identifier(MOD_ID, "simple_block"),
() -> new FabricBlockModelSupplier("cube_all")
.addTextureData(new FabricTextureMap("all")
.set(new Identifier(MOD_ID, "block/simple_block")))
.get());

/* In reality, for cube_all textures you'd want to try and do a .registerSimpleCubeAll() from
the blockStateModelGenerator. You could also do something like this if you needed to access
it using the FabricBlockModelSupplier:

blockStateModelGenerator.modelCollector.accept(new Identifier(MOD_ID, "simple_block"),
() -> new FabricBlockModelSupplier("cube_all")
.simpleCubeAllTextures(new Identifier(MOD_ID, "block/simple_block"))
.get());
*/
}

@Override
Expand Down