From fed7beab89031a4ca3786dcda4d4c21f702f6dfb Mon Sep 17 00:00:00 2001 From: David Quintana Date: Mon, 2 Nov 2020 02:09:49 +0100 Subject: [PATCH] Introduce custom loader additions to the model data generators. (#7450) Currently implemented loaders: * OBJ * Composite * Multi-layer * Item layers (vanilla item/generated but with fullbright texture support) * Bucket * Separate Perspective --- build.gradle | 4 +- src/generated_test/resources/.cache/cache | 4 + .../blockstates/obj_block.json | 7 ++ .../models/block/obj_block.json | 9 ++ .../models/item/item_layers.json | 11 +++ .../models/item/separate_perspective.json | 15 +++ .../model/SeparatePerspectiveModel.java | 3 +- .../model/generators/CustomLoaderBuilder.java | 55 +++++++++++ .../client/model/generators/ModelBuilder.java | 29 +++++- .../model/generators/ModelProvider.java | 42 +++++++- .../loaders/CompositeModelBuilder.java | 50 ++++++++++ .../loaders/DynamicBucketModelBuilder.java | 83 ++++++++++++++++ .../loaders/ItemLayersModelBuilder.java | 49 ++++++++++ .../loaders/MultiLayerModelBuilder.java | 52 ++++++++++ .../generators/loaders/OBJLoaderBuilder.java | 98 +++++++++++++++++++ .../SeparatePerspectiveModelBuilder.java | 66 +++++++++++++ .../common/data/ExistingFileHelper.java | 28 +++++- .../client/model/NewModelLoaderTest.java | 75 +++++++++++++- 18 files changed, 660 insertions(+), 20 deletions(-) create mode 100644 src/generated_test/resources/assets/new_model_loader_test/blockstates/obj_block.json create mode 100644 src/generated_test/resources/assets/new_model_loader_test/models/block/obj_block.json create mode 100644 src/generated_test/resources/assets/new_model_loader_test/models/item/item_layers.json create mode 100644 src/generated_test/resources/assets/new_model_loader_test/models/item/separate_perspective.json create mode 100644 src/main/java/net/minecraftforge/client/model/generators/CustomLoaderBuilder.java create mode 100644 src/main/java/net/minecraftforge/client/model/generators/loaders/CompositeModelBuilder.java create mode 100644 src/main/java/net/minecraftforge/client/model/generators/loaders/DynamicBucketModelBuilder.java create mode 100644 src/main/java/net/minecraftforge/client/model/generators/loaders/ItemLayersModelBuilder.java create mode 100644 src/main/java/net/minecraftforge/client/model/generators/loaders/MultiLayerModelBuilder.java create mode 100644 src/main/java/net/minecraftforge/client/model/generators/loaders/OBJLoaderBuilder.java create mode 100644 src/main/java/net/minecraftforge/client/model/generators/loaders/SeparatePerspectiveModelBuilder.java diff --git a/build.gradle b/build.gradle index 9b0147915..36ad772cb 100644 --- a/build.gradle +++ b/build.gradle @@ -353,8 +353,10 @@ project(':forge') { '--mod', 'global_loot_test', '--mod', 'scaffolding_test', '--mod', 'custom_tag_types_test', + '--mod', 'new_model_loader_test', '--output', rootProject.file('src/generated_test/resources/'), - '--existing', sourceSets.main.resources.srcDirs[0] + '--existing', sourceSets.main.resources.srcDirs[0], + '--existing', sourceSets.test.resources.srcDirs[0] } } } diff --git a/src/generated_test/resources/.cache/cache b/src/generated_test/resources/.cache/cache index 4a2d7fb38..229c89f3b 100644 --- a/src/generated_test/resources/.cache/cache +++ b/src/generated_test/resources/.cache/cache @@ -63,6 +63,10 @@ bf2e445b48b024354a69138b20a4a4a8aa5d15d1 assets/minecraft/blockstates/oak_trapdo 9c4cc92efb78811e8d70a383a1a89eb75b69db04 assets/minecraft/blockstates/stone.json 570fdd86046df75a073b759ff7aaf4c4caf43115 assets/minecraft/blockstates/torch.json a012d6d92bab1c91913bd0f2dc0492a0fce916f2 assets/minecraft/blockstates/wall_torch.json +e615d11ed9a452bca5d8b242e733a43ba2df19a3 assets/new_model_loader_test/blockstates/obj_block.json +bab7cd04569c8aa63320772acdc3c91a5ceda14a assets/new_model_loader_test/models/block/obj_block.json +9fa44308a52fbc384d5befee4e117531642299fb assets/new_model_loader_test/models/item/item_layers.json +70c8c81f3a157e4d698f213d5c1a13c13eaa5157 assets/new_model_loader_test/models/item/separate_perspective.json 273e0ed992d227f09f1c83bc22d066fb68d03c84 assets/piston_event_test/blockstates/shiftonmove.json d2c8e860521c5e738ed0798337f5728d610825ff assets/piston_event_test/models/block/shiftonmove.json f51f026a75e27a0a53a76771d553c0e3385a2966 assets/piston_event_test/models/item/shiftonmove.json diff --git a/src/generated_test/resources/assets/new_model_loader_test/blockstates/obj_block.json b/src/generated_test/resources/assets/new_model_loader_test/blockstates/obj_block.json new file mode 100644 index 000000000..1fb14a4cf --- /dev/null +++ b/src/generated_test/resources/assets/new_model_loader_test/blockstates/obj_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "new_model_loader_test:block/obj_block" + } + } +} \ No newline at end of file diff --git a/src/generated_test/resources/assets/new_model_loader_test/models/block/obj_block.json b/src/generated_test/resources/assets/new_model_loader_test/models/block/obj_block.json new file mode 100644 index 000000000..9593d1353 --- /dev/null +++ b/src/generated_test/resources/assets/new_model_loader_test/models/block/obj_block.json @@ -0,0 +1,9 @@ +{ + "textures": { + "qr": "minecraft:block/oak_planks", + "particle": "#qr" + }, + "loader": "forge:obj", + "model": "new_model_loader_test:models/item/sugar_glider.obj", + "flip-v": true +} \ No newline at end of file diff --git a/src/generated_test/resources/assets/new_model_loader_test/models/item/item_layers.json b/src/generated_test/resources/assets/new_model_loader_test/models/item/item_layers.json new file mode 100644 index 000000000..a46691d2e --- /dev/null +++ b/src/generated_test/resources/assets/new_model_loader_test/models/item/item_layers.json @@ -0,0 +1,11 @@ +{ + "parent": "forge:item/default", + "textures": { + "layer0": "minecraft:item/coal", + "layer1": "minecraft:item/stick" + }, + "loader": "forge:item-layers", + "fullbright_layers": [ + 1 + ] +} \ No newline at end of file diff --git a/src/generated_test/resources/assets/new_model_loader_test/models/item/separate_perspective.json b/src/generated_test/resources/assets/new_model_loader_test/models/item/separate_perspective.json new file mode 100644 index 000000000..fde0ecec2 --- /dev/null +++ b/src/generated_test/resources/assets/new_model_loader_test/models/item/separate_perspective.json @@ -0,0 +1,15 @@ +{ + "parent": "forge:item/default", + "loader": "forge:separate-perspective", + "base": { + "parent": "minecraft:item/coal" + }, + "perspectives": { + "gui": { + "parent": "minecraft:item/snowball" + }, + "first_person_left_hand": { + "parent": "minecraft:item/bone" + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java b/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java index 646a1fa1f..a24a9551b 100644 --- a/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java +++ b/src/main/java/net/minecraftforge/client/model/SeparatePerspectiveModel.java @@ -19,6 +19,7 @@ package net.minecraftforge.client.model; +import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -164,7 +165,7 @@ public class SeparatePerspectiveModel implements IModelGeometry PERSPECTIVES = ImmutableMap.builder() + public static final ImmutableBiMap PERSPECTIVES = ImmutableBiMap.builder() .put("none", ItemCameraTransforms.TransformType.NONE) .put("third_person_left_hand", ItemCameraTransforms.TransformType.THIRD_PERSON_LEFT_HAND) .put("third_person_right_hand", ItemCameraTransforms.TransformType.THIRD_PERSON_RIGHT_HAND) diff --git a/src/main/java/net/minecraftforge/client/model/generators/CustomLoaderBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/CustomLoaderBuilder.java new file mode 100644 index 000000000..ba5b4f3ed --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/CustomLoaderBuilder.java @@ -0,0 +1,55 @@ +package net.minecraftforge.client.model.generators; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.data.ExistingFileHelper; + +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class CustomLoaderBuilder> +{ + protected final ResourceLocation loaderId; + protected final T parent; + protected final ExistingFileHelper existingFileHelper; + protected final Map visibility = new LinkedHashMap<>(); + + protected CustomLoaderBuilder(ResourceLocation loaderId, T parent, ExistingFileHelper existingFileHelper) + { + this.loaderId = loaderId; + this.parent = parent; + this.existingFileHelper = existingFileHelper; + } + + public CustomLoaderBuilder visibility(String partName, boolean show) + { + Preconditions.checkNotNull(partName, "partName must not be null"); + this.visibility.put(partName, show); + return this; + } + + public T end() + { + return parent; + } + + public JsonObject toJson(JsonObject json) + { + json.addProperty("loader", loaderId.toString()); + + if (visibility.size() > 0) + { + JsonObject visibilityObj = new JsonObject(); + + for(Map.Entry entry : visibility.entrySet()) + { + visibilityObj.addProperty(entry.getKey(), entry.getValue()); + } + + json.add("visibility", visibilityObj); + } + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/generators/ModelBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/ModelBuilder.java index 77e6a4187..6d5be7549 100644 --- a/src/main/java/net/minecraftforge/client/model/generators/ModelBuilder.java +++ b/src/main/java/net/minecraftforge/client/model/generators/ModelBuilder.java @@ -19,13 +19,10 @@ package net.minecraftforge.client.model.generators; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -75,6 +72,8 @@ public class ModelBuilder> extends ModelFile { protected final List elements = new ArrayList<>(); + protected CustomLoaderBuilder customLoader = null; + protected ModelBuilder(ResourceLocation outputLocation, ExistingFileHelper existingFileHelper) { super(outputLocation); this.existingFileHelper = existingFileHelper; @@ -178,6 +177,7 @@ public class ModelBuilder> extends ModelFile { } public ElementBuilder element() { + Preconditions.checkState(customLoader == null, "Cannot use elements and custom loaders at the same time"); ElementBuilder ret = new ElementBuilder(); elements.add(ret); return ret; @@ -191,13 +191,29 @@ public class ModelBuilder> extends ModelFile { * @throws IndexOutOfBoundsException if {@code} index is out of bounds */ public ElementBuilder element(int index) { + Preconditions.checkState(customLoader == null, "Cannot use elements and custom loaders at the same time"); Preconditions.checkElementIndex(index, elements.size(), "Element index"); return elements.get(index); } + /** + * Use a custom loader instead of the vanilla elements. + * @param customLoaderFactory + * @return the custom loader builder + */ + public > L customLoader(BiFunction customLoaderFactory) + { + Preconditions.checkState(elements.size() == 0, "Cannot use elements and custom loaders at the same time"); + Preconditions.checkNotNull(customLoaderFactory, "customLoaderFactory must not be null"); + L customLoader = customLoaderFactory.apply(self(), existingFileHelper); + this.customLoader = customLoader; + return customLoader; + } + @VisibleForTesting public JsonObject toJson() { JsonObject root = new JsonObject(); + if (this.parent != null) { root.addProperty("parent", this.parent.getLocation().toString()); } @@ -290,6 +306,9 @@ public class ModelBuilder> extends ModelFile { root.add("elements", elements); } + if (customLoader != null) + return customLoader.toJson(root); + return root; } diff --git a/src/main/java/net/minecraftforge/client/model/generators/ModelProvider.java b/src/main/java/net/minecraftforge/client/model/generators/ModelProvider.java index 5ae8aece2..54da7baf0 100644 --- a/src/main/java/net/minecraftforge/client/model/generators/ModelProvider.java +++ b/src/main/java/net/minecraftforge/client/model/generators/ModelProvider.java @@ -35,6 +35,7 @@ import com.google.gson.GsonBuilder; import net.minecraft.data.DataGenerator; import net.minecraft.data.DirectoryCache; import net.minecraft.data.IDataProvider; +import net.minecraft.resources.IResource; import net.minecraft.resources.ResourcePackType; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.data.ExistingFileHelper; @@ -51,12 +52,39 @@ public abstract class ModelProvider> implements IDataP } @Override - public boolean exists(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) { - if (generatedModels.containsKey(loc)) { - return true; + public boolean exists(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) + { + if (pathPrefix.equals("models") && pathSuffix.equals(".json")) + { + if (generatedModels.containsKey(loc)) + { + return true; + } } return delegate.exists(loc, type, pathSuffix, pathPrefix); } + + @Override + public boolean exists(ResourceLocation loc, ResourcePackType type) { + if (loc.getPath().startsWith("models/") && loc.getPath().endsWith(".json")) + { + ResourceLocation modelLoc = new ResourceLocation( + loc.getNamespace(), + loc.getPath().substring("models/".length(), loc.getPath().length() - ".json".length()) + ); + if (generatedModels.containsKey(modelLoc)) + { + return true; + } + } + return delegate.exists(loc, type); + } + + @Override + public IResource getResource(ResourceLocation loc, ResourcePackType type) throws IOException + { + return delegate.getResource(loc, type); + } } public static final String BLOCK_FOLDER = "block"; @@ -357,6 +385,14 @@ public abstract class ModelProvider> implements IDataP return singleTexture(name, BLOCK_FOLDER + "/carpet", "wool", wool); } + /** + * Gets a model builder that's not directly saved to disk. Meant for use in custom model loaders. + */ + public T nested() + { + return factory.apply(new ResourceLocation("dummy:dummy")); + } + public ModelFile.ExistingModelFile getExistingFile(ResourceLocation path) { ModelFile.ExistingModelFile ret = new ModelFile.ExistingModelFile(extendWithFolder(path), existingFileHelper); ret.assertExistence(); diff --git a/src/main/java/net/minecraftforge/client/model/generators/loaders/CompositeModelBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/loaders/CompositeModelBuilder.java new file mode 100644 index 000000000..65638705c --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/loaders/CompositeModelBuilder.java @@ -0,0 +1,50 @@ +package net.minecraftforge.client.model.generators.loaders; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.minecraft.fluid.Fluid; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.generators.CustomLoaderBuilder; +import net.minecraftforge.client.model.generators.ModelBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class CompositeModelBuilder> extends CustomLoaderBuilder +{ + public static > CompositeModelBuilder begin(T parent, ExistingFileHelper existingFileHelper) + { + return new CompositeModelBuilder<>(parent, existingFileHelper); + } + + private final Map childModels = new LinkedHashMap<>(); + + protected CompositeModelBuilder(T parent, ExistingFileHelper existingFileHelper) + { + super(new ResourceLocation("forge:composite"), parent, existingFileHelper); + } + + public CompositeModelBuilder submodel(String name, T modelBuilder) + { + Preconditions.checkNotNull(name, "name must not be null"); + Preconditions.checkNotNull(modelBuilder, "modelBuilder must not be null"); + childModels.put(name, modelBuilder); + return this; + } + + @Override + public JsonObject toJson(JsonObject json) + { + json = super.toJson(json); + + JsonObject parts = new JsonObject(); + for(Map.Entry entry : childModels.entrySet()) + { + parts.add(entry.getKey(), entry.getValue().toJson()); + } + json.add("parts", parts); + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/generators/loaders/DynamicBucketModelBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/loaders/DynamicBucketModelBuilder.java new file mode 100644 index 000000000..2c47b87d7 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/loaders/DynamicBucketModelBuilder.java @@ -0,0 +1,83 @@ +package net.minecraftforge.client.model.generators.loaders; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.minecraft.fluid.Fluid; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.generators.CustomLoaderBuilder; +import net.minecraftforge.client.model.generators.ModelBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; + +public class DynamicBucketModelBuilder> extends CustomLoaderBuilder +{ + public static > DynamicBucketModelBuilder begin(T parent, ExistingFileHelper existingFileHelper) + { + return new DynamicBucketModelBuilder<>(parent, existingFileHelper); + } + + private ResourceLocation fluid; + private Boolean flipGas; + private Boolean applyTint; + private Boolean coverIsMask; + private Boolean applyFluidLuminosity; + + protected DynamicBucketModelBuilder(T parent, ExistingFileHelper existingFileHelper) + { + super(new ResourceLocation("forge:bucket"), parent, existingFileHelper); + } + + public DynamicBucketModelBuilder fluid(Fluid fluid) + { + Preconditions.checkNotNull(fluid, "fluid must not be null"); + this.fluid = fluid.getRegistryName(); + return this; + } + + public DynamicBucketModelBuilder flipGas(boolean flip) + { + this.flipGas = flip; + return this; + } + + public DynamicBucketModelBuilder applyTint(boolean tint) + { + this.applyTint = tint; + return this; + } + + public DynamicBucketModelBuilder coverIsMask(boolean coverIsMask) + { + this.coverIsMask = coverIsMask; + return this; + } + + public DynamicBucketModelBuilder applyFluidLuminosity(boolean applyFluidLuminosity) + { + this.applyFluidLuminosity = applyFluidLuminosity; + return this; + } + + @Override + public JsonObject toJson(JsonObject json) + { + json = super.toJson(json); + + Preconditions.checkNotNull(fluid, "fluid must not be null"); + + json.addProperty("fluid", fluid.toString()); + + if (flipGas != null) + json.addProperty("flipGas", flipGas); + + if (applyTint != null) + json.addProperty("applyTint", applyTint); + + if (coverIsMask != null) + json.addProperty("coverIsMask", coverIsMask); + + if (applyFluidLuminosity != null) + json.addProperty("applyFluidLuminosity", applyFluidLuminosity); + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/generators/loaders/ItemLayersModelBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/loaders/ItemLayersModelBuilder.java new file mode 100644 index 000000000..712ec2e3a --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/loaders/ItemLayersModelBuilder.java @@ -0,0 +1,49 @@ +package net.minecraftforge.client.model.generators.loaders; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.generators.CustomLoaderBuilder; +import net.minecraftforge.client.model.generators.ModelBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class ItemLayersModelBuilder> extends CustomLoaderBuilder +{ + public static > ItemLayersModelBuilder begin(T parent, ExistingFileHelper existingFileHelper) + { + return new ItemLayersModelBuilder<>(parent, existingFileHelper); + } + + private final Set fullbright = new LinkedHashSet<>(); + + protected ItemLayersModelBuilder(T parent, ExistingFileHelper existingFileHelper) + { + super(new ResourceLocation("forge:item-layers"), parent, existingFileHelper); + } + + public ItemLayersModelBuilder fullbright(int layer) + { + Preconditions.checkArgument(layer >= 0, "layer must be >= 0"); + fullbright.add(layer); + return this; + } + + @Override + public JsonObject toJson(JsonObject json) + { + json = super.toJson(json); + + JsonArray parts = new JsonArray(); + for(int entry : fullbright) + { + parts.add(entry); + } + json.add("fullbright_layers", parts); + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/generators/loaders/MultiLayerModelBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/loaders/MultiLayerModelBuilder.java new file mode 100644 index 000000000..be690db5d --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/loaders/MultiLayerModelBuilder.java @@ -0,0 +1,52 @@ +package net.minecraftforge.client.model.generators.loaders; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.MultiLayerModel; +import net.minecraftforge.client.model.generators.CustomLoaderBuilder; +import net.minecraftforge.client.model.generators.ModelBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class MultiLayerModelBuilder> extends CustomLoaderBuilder +{ + public static > MultiLayerModelBuilder begin(T parent, ExistingFileHelper existingFileHelper) + { + return new MultiLayerModelBuilder<>(parent, existingFileHelper); + } + + private final Map childModels = new LinkedHashMap<>(); + + protected MultiLayerModelBuilder(T parent, ExistingFileHelper existingFileHelper) + { + super(new ResourceLocation("forge:multi-layer"), parent, existingFileHelper); + } + + public MultiLayerModelBuilder submodel(RenderType layer, T modelBuilder) + { + Preconditions.checkNotNull(layer, "layer must not be null"); + Preconditions.checkArgument(MultiLayerModel.Loader.BLOCK_LAYERS.containsValue(layer), "layer must be supported by MultiLayerModel"); + Preconditions.checkNotNull(modelBuilder, "modelBuilder must not be null"); + childModels.put(MultiLayerModel.Loader.BLOCK_LAYERS.inverse().get(layer), modelBuilder); + return this; + } + + @Override + public JsonObject toJson(JsonObject json) + { + json = super.toJson(json); + + JsonObject parts = new JsonObject(); + for(Map.Entry entry : childModels.entrySet()) + { + parts.add(entry.getKey(), entry.getValue().toJson()); + } + json.add("layers", parts); + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/generators/loaders/OBJLoaderBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/loaders/OBJLoaderBuilder.java new file mode 100644 index 000000000..343dd3c27 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/loaders/OBJLoaderBuilder.java @@ -0,0 +1,98 @@ +package net.minecraftforge.client.model.generators.loaders; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.minecraft.resources.ResourcePackType; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.generators.CustomLoaderBuilder; +import net.minecraftforge.client.model.generators.ModelBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; + +public class OBJLoaderBuilder> extends CustomLoaderBuilder +{ + public static > OBJLoaderBuilder begin(T parent, ExistingFileHelper existingFileHelper) + { + return new OBJLoaderBuilder<>(parent, existingFileHelper); + } + + private ResourceLocation modelLocation; + private Boolean detectCullableFaces; + private Boolean diffuseLighting; + private Boolean flipV; + private Boolean ambientToFullbright; + private ResourceLocation materialLibraryOverrideLocation; + + protected OBJLoaderBuilder(T parent, ExistingFileHelper existingFileHelper) + { + super(new ResourceLocation("forge:obj"), parent, existingFileHelper); + } + + public OBJLoaderBuilder modelLocation(ResourceLocation modelLocation) + { + Preconditions.checkNotNull(modelLocation, "modelLocation must not be null"); + Preconditions.checkArgument(existingFileHelper.exists(modelLocation, ResourcePackType.CLIENT_RESOURCES), + "OBJ Model %s does not exist in any known resource pack", modelLocation); + this.modelLocation = modelLocation; + return this; + } + + public OBJLoaderBuilder detectCullableFaces(boolean detectCullableFaces) + { + this.detectCullableFaces = detectCullableFaces; + return this; + } + + public OBJLoaderBuilder diffuseLighting(boolean diffuseLighting) + { + this.diffuseLighting = diffuseLighting; + return this; + } + + public OBJLoaderBuilder flipV(boolean flipV) + { + this.flipV = flipV; + return this; + } + + public OBJLoaderBuilder ambientToFullbright(boolean ambientToFullbright) + { + this.ambientToFullbright = ambientToFullbright; + return this; + } + + public OBJLoaderBuilder overrideMaterialLibrary(ResourceLocation materialLibraryOverrideLocation) + { + Preconditions.checkNotNull(materialLibraryOverrideLocation, "materialLibraryOverrideLocation must not be null"); + Preconditions.checkArgument(existingFileHelper.exists(materialLibraryOverrideLocation, ResourcePackType.CLIENT_RESOURCES), + "OBJ Model %s does not exist in any known resource pack", materialLibraryOverrideLocation); + this.materialLibraryOverrideLocation = materialLibraryOverrideLocation; + return this; + } + + @Override + public JsonObject toJson(JsonObject json) + { + json = super.toJson(json); + + Preconditions.checkNotNull(modelLocation, "modelLocation must not be null"); + + json.addProperty("model", modelLocation.toString()); + + if (detectCullableFaces != null) + json.addProperty("detectCullableFaces", detectCullableFaces); + + if (diffuseLighting != null) + json.addProperty("diffuseLighting", diffuseLighting); + + if (flipV != null) + json.addProperty("flip-v", flipV); + + if (ambientToFullbright != null) + json.addProperty("ambientToFullbright", ambientToFullbright); + + if (materialLibraryOverrideLocation != null) + json.addProperty("materialLibraryOverrideLocation", materialLibraryOverrideLocation.toString()); + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/generators/loaders/SeparatePerspectiveModelBuilder.java b/src/main/java/net/minecraftforge/client/model/generators/loaders/SeparatePerspectiveModelBuilder.java new file mode 100644 index 000000000..401b52ab1 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/generators/loaders/SeparatePerspectiveModelBuilder.java @@ -0,0 +1,66 @@ +package net.minecraftforge.client.model.generators.loaders; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import net.minecraft.client.renderer.model.ItemCameraTransforms; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.MultiLayerModel; +import net.minecraftforge.client.model.SeparatePerspectiveModel; +import net.minecraftforge.client.model.generators.CustomLoaderBuilder; +import net.minecraftforge.client.model.generators.ModelBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class SeparatePerspectiveModelBuilder> extends CustomLoaderBuilder +{ + public static > SeparatePerspectiveModelBuilder begin(T parent, ExistingFileHelper existingFileHelper) + { + return new SeparatePerspectiveModelBuilder<>(parent, existingFileHelper); + } + + private T base; + private final Map childModels = new LinkedHashMap<>(); + + protected SeparatePerspectiveModelBuilder(T parent, ExistingFileHelper existingFileHelper) + { + super(new ResourceLocation("forge:separate-perspective"), parent, existingFileHelper); + } + + public SeparatePerspectiveModelBuilder base(T modelBuilder) + { + Preconditions.checkNotNull(modelBuilder, "modelBuilder must not be null"); + base = modelBuilder; + return this; + } + + public SeparatePerspectiveModelBuilder perspective(ItemCameraTransforms.TransformType perspective, T modelBuilder) + { + Preconditions.checkNotNull(perspective, "layer must not be null"); + Preconditions.checkArgument(SeparatePerspectiveModel.Loader.PERSPECTIVES.containsValue(perspective), "perspective is not included in SeparatePerspectiveModel. New mc version?"); + Preconditions.checkNotNull(modelBuilder, "modelBuilder must not be null"); + childModels.put(SeparatePerspectiveModel.Loader.PERSPECTIVES.inverse().get(perspective), modelBuilder); + return this; + } + + @Override + public JsonObject toJson(JsonObject json) + { + json = super.toJson(json); + + if (base != null) + { + json.add("base", base.toJson()); + } + + JsonObject parts = new JsonObject(); + for(Map.Entry entry : childModels.entrySet()) + { + parts.add(entry.getKey(), entry.getValue().toJson()); + } + json.add("perspectives", parts); + + return json; + } +} diff --git a/src/main/java/net/minecraftforge/common/data/ExistingFileHelper.java b/src/main/java/net/minecraftforge/common/data/ExistingFileHelper.java index 81a0ac803..3d3f82c00 100644 --- a/src/main/java/net/minecraftforge/common/data/ExistingFileHelper.java +++ b/src/main/java/net/minecraftforge/common/data/ExistingFileHelper.java @@ -70,6 +70,22 @@ public class ExistingFileHelper { return new ResourceLocation(base.getNamespace(), prefix + "/" + base.getPath() + suffix); } + /** + * Check if a given resource exists in the known resource packs. + * + * @param loc the base location of the resource, e.g. + * {@code "minecraft:block/stone"} + * @param type the type of resources to check + * @return {@code true} if the resource exists in any pack, {@code false} + * otherwise + */ + public boolean exists(ResourceLocation loc, ResourcePackType type) { + if (!enable) { + return true; + } + return getManager(type).hasResource(loc); + } + /** * Check if a given resource exists in the known resource packs. * @@ -83,15 +99,17 @@ public class ExistingFileHelper { * otherwise */ public boolean exists(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) { - if (!enable) { - return true; - } - return getManager(type).hasResource(getLocation(loc, pathSuffix, pathPrefix)); + return exists(getLocation(loc, pathSuffix, pathPrefix), type); } @VisibleForTesting public IResource getResource(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) throws IOException { - return getManager(type).getResource(getLocation(loc, pathSuffix, pathPrefix)); + return getResource(getLocation(loc, pathSuffix, pathPrefix), type); + } + + @VisibleForTesting + public IResource getResource(ResourceLocation loc, ResourcePackType type) throws IOException { + return getManager(type).getResource(loc); } /** diff --git a/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java b/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java index b87772b5c..002b9133b 100644 --- a/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java +++ b/src/test/java/net/minecraftforge/debug/client/model/NewModelLoaderTest.java @@ -26,13 +26,11 @@ import com.mojang.datafixers.util.Pair; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.material.Material; -import net.minecraft.client.renderer.model.IModelTransform; -import net.minecraft.client.renderer.model.IUnbakedModel; -import net.minecraft.client.renderer.model.ModelBakery; -import net.minecraft.client.renderer.model.RenderMaterial; +import net.minecraft.client.renderer.model.*; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.data.DataGenerator; import net.minecraft.entity.Entity; import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.item.*; @@ -50,12 +48,18 @@ import net.minecraftforge.client.model.IModelBuilder; import net.minecraftforge.client.model.IModelConfiguration; import net.minecraftforge.client.model.IModelLoader; import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.client.model.generators.BlockStateProvider; +import net.minecraftforge.client.model.generators.ItemModelProvider; +import net.minecraftforge.client.model.generators.loaders.ItemLayersModelBuilder; +import net.minecraftforge.client.model.generators.loaders.OBJLoaderBuilder; +import net.minecraftforge.client.model.generators.loaders.SeparatePerspectiveModelBuilder; import net.minecraftforge.client.model.geometry.ISimpleModelGeometry; import net.minecraftforge.client.model.pipeline.BakedQuadBuilder; +import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.RegistryObject; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; @@ -136,6 +140,7 @@ public class NewModelLoaderTest ITEMS.register(modEventBus); modEventBus.addListener(this::modelRegistry); + modEventBus.addListener(this::datagen); } public void modelRegistry(ModelRegistryEvent event) @@ -209,4 +214,64 @@ public class NewModelLoaderTest return Collections.singleton(owner.resolveTexture("particle")); } } + + private void datagen(GatherDataEvent event) + { + DataGenerator gen = event.getGenerator(); + + if (event.includeClient()) + { + // Let blockstate provider see generated item models by passing its existing file helper + ItemModelProvider itemModels = new ItemModels(gen, event.getExistingFileHelper()); + gen.addProvider(itemModels); + gen.addProvider(new BlockStates(gen, itemModels.existingFileHelper)); + } + } + + public static class ItemModels extends ItemModelProvider + { + public ItemModels(DataGenerator generator, ExistingFileHelper existingFileHelper) + { + super(generator, MODID, existingFileHelper); + } + + @Override + protected void registerModels() + { + withExistingParent(NewModelLoaderTest.item_layers.getId().getPath(), "forge:item/default") + .texture("layer0", "minecraft:item/coal") + .texture("layer1", "minecraft:item/stick") + .customLoader(ItemLayersModelBuilder::begin) + .fullbright(1) + .end(); + withExistingParent(NewModelLoaderTest.separate_perspective.getId().getPath(), "forge:item/default") + .customLoader(SeparatePerspectiveModelBuilder::begin) + .base(nested().parent(getExistingFile(mcLoc("minecraft:item/coal")))) + .perspective(ItemCameraTransforms.TransformType.GUI, nested().parent(getExistingFile(mcLoc("minecraft:item/snowball")))) + .perspective(ItemCameraTransforms.TransformType.FIRST_PERSON_LEFT_HAND, nested().parent(getExistingFile(mcLoc("minecraft:item/bone")))) + .end(); + } + } + + public static class BlockStates extends BlockStateProvider + { + public BlockStates(DataGenerator gen, ExistingFileHelper exFileHelper) + { + super(gen, MODID, exFileHelper); + } + + @Override + protected void registerStatesAndModels() + { + simpleBlock(NewModelLoaderTest.obj_block.get(), models() + .getBuilder(NewModelLoaderTest.obj_block.getId().getPath()) + .customLoader(OBJLoaderBuilder::begin) + .modelLocation(new ResourceLocation("new_model_loader_test:models/item/sugar_glider.obj")) + .flipV(true) + .end() + .texture("qr", "minecraft:block/oak_planks") + .texture("particle", "#qr") + ); + } + } }