From 0f8d9f1353e9a72b8048c0662ed3475672737282 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 24 Dec 2018 15:43:12 -0500 Subject: [PATCH] Add multi-model support for multiparts --- .../client/MultipartContainerBakedModel.java | 16 +++-- .../client/MultipartFakeBlock.java | 61 +++++++++++++++++++ .../client/MultipartModelProvider.java | 56 ++++++++++++----- .../models/multipart/center.json | 19 ++++++ .../multipartstates/test_part.json | 22 ++++--- .../multipartstates/variants_test_part.json | 11 ++++ 6 files changed, 155 insertions(+), 30 deletions(-) create mode 100644 src/main/java/net/shadowfacts/simplemultipart/client/MultipartFakeBlock.java create mode 100644 src/test/resources/assets/multipart_test/models/multipart/center.json create mode 100644 src/test/resources/assets/multipart_test/multipartstates/variants_test_part.json diff --git a/src/main/java/net/shadowfacts/simplemultipart/client/MultipartContainerBakedModel.java b/src/main/java/net/shadowfacts/simplemultipart/client/MultipartContainerBakedModel.java index 2401efd..daf295c 100644 --- a/src/main/java/net/shadowfacts/simplemultipart/client/MultipartContainerBakedModel.java +++ b/src/main/java/net/shadowfacts/simplemultipart/client/MultipartContainerBakedModel.java @@ -13,11 +13,8 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.Direction; import net.shadowfacts.simplemultipart.SimpleMultipart; import net.shadowfacts.simplemultipart.container.MultipartContainerBlockState; -import net.shadowfacts.simplemultipart.multipart.MultipartSlot; -import net.shadowfacts.simplemultipart.multipart.MultipartState; import java.util.List; -import java.util.Map; import java.util.Random; import java.util.stream.Collectors; @@ -42,7 +39,16 @@ public class MultipartContainerBakedModel implements BakedModel { if (model instanceof MultipartBakedModel) { return ((MultipartBakedModel)model).getMultipartQuads(partState, side, random).stream(); } else { - return model.getQuads(null, side, random).stream(); + BlockState fakeState = null; + + // Need to use the same fake block state as used when loading multi-models + // otherwise MultipartBakedModel will return no quads for a null state + MultipartFakeBlock fakeBlock = MultipartFakeBlock.fakeBlocks.get(partId); + if (fakeBlock != null) { + fakeState = fakeBlock.getFakeState(partState); + } + + return model.getQuads(fakeState, side, random).stream(); } }) .collect(Collectors.toList()); @@ -65,7 +71,7 @@ public class MultipartContainerBakedModel implements BakedModel { @Override public Sprite getSprite() { - return MinecraftClient.getInstance().getSpriteAtlas().getSprite("blocks/stone"); + return MinecraftClient.getInstance().getSpriteAtlas().getSprite("block/stone"); } @Override diff --git a/src/main/java/net/shadowfacts/simplemultipart/client/MultipartFakeBlock.java b/src/main/java/net/shadowfacts/simplemultipart/client/MultipartFakeBlock.java new file mode 100644 index 0000000..ccdf2fe --- /dev/null +++ b/src/main/java/net/shadowfacts/simplemultipart/client/MultipartFakeBlock.java @@ -0,0 +1,61 @@ +package net.shadowfacts.simplemultipart.client; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Material; +import net.minecraft.state.StateFactory; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; +import net.shadowfacts.simplemultipart.SimpleMultipart; +import net.shadowfacts.simplemultipart.multipart.Multipart; +import net.shadowfacts.simplemultipart.multipart.MultipartState; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author shadowfacts + */ +public class MultipartFakeBlock extends Block { + + public static final Map fakeBlocks = new HashMap<>(); + + private final Multipart multipart; + private final StateFactory fakeStateFactory; + + public MultipartFakeBlock(Multipart multipart) { + super(Settings.of(Material.AIR)); + + this.multipart = multipart; + this.fakeStateFactory = createFakeStateFactory(); + + setDefaultState(fakeStateFactory.getDefaultState()); + + Identifier partId = SimpleMultipart.MULTIPART.getId(multipart); + fakeBlocks.put(partId, this); + } + + private StateFactory createFakeStateFactory() { + StateFactory.Builder builder = new StateFactory.Builder<>(this); + multipart.getStateFactory().getProperties().forEach(builder::with); + return builder.build(BlockState::new); + } + + @Override + public StateFactory getStateFactory() { + return fakeStateFactory; + } + + public BlockState getFakeState(MultipartState state) { + BlockState fakeState = getDefaultState(); + for (Map.Entry, Comparable> e : state.getEntries().entrySet()) { + fakeState = with(fakeState, e.getKey(), e.getValue()); + } + return fakeState; + } + + private static BlockState with(BlockState state, Property prop, Comparable value) { + return state.with(prop, value); + } + +} diff --git a/src/main/java/net/shadowfacts/simplemultipart/client/MultipartModelProvider.java b/src/main/java/net/shadowfacts/simplemultipart/client/MultipartModelProvider.java index 3e365d6..92cfd12 100644 --- a/src/main/java/net/shadowfacts/simplemultipart/client/MultipartModelProvider.java +++ b/src/main/java/net/shadowfacts/simplemultipart/client/MultipartModelProvider.java @@ -2,6 +2,7 @@ package net.shadowfacts.simplemultipart.client; import net.fabricmc.fabric.api.client.model.ModelProvider; import net.fabricmc.fabric.api.client.model.ModelProviderException; +import net.minecraft.class_816; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.block.BlockModels; import net.minecraft.client.render.model.UnbakedModel; @@ -68,40 +69,63 @@ public class MultipartModelProvider implements ModelProvider { } } - private UnbakedModel getOrLoadPartModel(ModelIdentifier id) throws ModelProviderException, IOException { - UnbakedModel existing = unbakedModels.get(id); + private UnbakedModel getOrLoadPartModel(ModelIdentifier modelId) throws ModelProviderException, IOException { + UnbakedModel existing = unbakedModels.get(modelId); if (existing != null) { return existing; } - return loadModel(id); + return loadPartModel(modelId); } - private UnbakedModel loadModel(ModelIdentifier id) throws ModelProviderException, IOException { - Identifier partStateId = new Identifier(id.getNamespace(), "multipartstates/" + id.getPath() + ".json"); - ModelVariantMap variantMap = loadVariantMap(partStateId); - Map variants = variantMap.method_3423(); - variants.forEach((variant, model) -> { - unbakedModels.put(new ModelIdentifier(new Identifier(id.getNamespace(), id.getPath()), variant), model); - }); + private UnbakedModel loadPartModel(ModelIdentifier modelId) throws ModelProviderException, IOException { + Identifier partId = new Identifier(modelId.getNamespace(), modelId.getPath()); + Identifier partStateDefId = new Identifier(partId.getNamespace(), "multipartstates/" + partId.getPath() + ".json"); - UnbakedModel model = unbakedModels.get(id); + Multipart part = SimpleMultipart.MULTIPART.get(partId); + MultipartFakeBlock blockAdapter = new MultipartFakeBlock(part); + + ModelVariantMap variantMap = loadPartVariantMap(blockAdapter, partStateDefId); + + if (variantMap.method_3422()) { + class_816 multipartUnbakedModel = variantMap.method_3421(); + part.getStateFactory().getStates().forEach(state -> { + ModelIdentifier stateModelId = new ModelIdentifier(partId, BlockModels.propertyMapToString(state.getEntries())); + unbakedModels.put(stateModelId, multipartUnbakedModel); + }); + } else { + Map variants = variantMap.method_3423(); + variants.forEach((variant, model) -> { + unbakedModels.put(new ModelIdentifier(partId, variant), model); + }); + } + + UnbakedModel model = unbakedModels.get(modelId); if (model == null) { - throw new ModelProviderException("Loaded multipart state " + partStateId + " for model " + id + " but still missing model"); + throw new ModelProviderException("Loaded multipart state " + partStateDefId + " for model " + modelId + " but still missing model"); } return model; } - private ModelVariantMap loadVariantMap(Identifier id) throws IOException { +// private StateFactory getStateFactory(class_816 multipartUnbakedModel) { +// try { +// Field f = class_816.class.getDeclaredField("field_4329"); +// f.setAccessible(true); +// return (StateFactory)f.get(multipartUnbakedModel); +// } catch (ReflectiveOperationException e) { +// throw new RuntimeException(e); +// } +// } + + private ModelVariantMap loadPartVariantMap(MultipartFakeBlock blockAdapter, Identifier partStateDefId) throws IOException { Resource resource = null; Reader reader = null; try { - resource = MinecraftClient.getInstance().getResourceManager().getResource(id); + resource = MinecraftClient.getInstance().getResourceManager().getResource(partStateDefId); reader = new InputStreamReader(resource.getInputStream()); ModelVariantMap.class_791 context = new ModelVariantMap.class_791(); - // context.stateFactory = - // TODO: ^ blockstate translation + context.method_3426(blockAdapter.getStateFactory()); return ModelVariantMap.method_3424(context, reader); } finally { IOUtils.closeQuietly(reader); diff --git a/src/test/resources/assets/multipart_test/models/multipart/center.json b/src/test/resources/assets/multipart_test/models/multipart/center.json new file mode 100644 index 0000000..0307123 --- /dev/null +++ b/src/test/resources/assets/multipart_test/models/multipart/center.json @@ -0,0 +1,19 @@ +{ + "textures": { + "texture": "block/diamond_block" + }, + "elements": [ + { + "from": [6, 6, 6], + "to": [10, 10, 10], + "faces": { + "down": { "texture": "#texture" }, + "up": { "texture": "#texture" }, + "north": { "texture": "#texture" }, + "south": { "texture": "#texture" }, + "west": { "texture": "#texture" }, + "east": { "texture": "#texture" } + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/assets/multipart_test/multipartstates/test_part.json b/src/test/resources/assets/multipart_test/multipartstates/test_part.json index fd214d0..ccf7b3b 100644 --- a/src/test/resources/assets/multipart_test/multipartstates/test_part.json +++ b/src/test/resources/assets/multipart_test/multipartstates/test_part.json @@ -1,11 +1,15 @@ { - "variants": { - "slot=bottom": { "model": "multipart_test:multipart/bottom" }, - "slot=top": { "model": "multipart_test:multipart/bottom", "x": 180 }, - "slot=north": { "model": "multipart_test:multipart/vertical" }, - "slot=south": { "model": "multipart_test:multipart/vertical", "y": 180 }, - "slot=east": { "model": "multipart_test:multipart/vertical", "y": 90 }, - "slot=west": { "model": "multipart_test:multipart/vertical", "y": 270 }, - "inventory": { "model": "multipart_test:multipart/vertical", "y": 180 } - } + "multipart": [ + { + "apply": { "model": "multipart_test:multipart/center" } + }, + { + "when": { "slot": "bottom" }, + "apply": { "model": "multipart_test:multipart/bottom" } + }, + { + "when": { "slot": "north" }, + "apply": { "model": "multipart_test:multipart/vertical" } + } + ] } \ No newline at end of file diff --git a/src/test/resources/assets/multipart_test/multipartstates/variants_test_part.json b/src/test/resources/assets/multipart_test/multipartstates/variants_test_part.json new file mode 100644 index 0000000..fd214d0 --- /dev/null +++ b/src/test/resources/assets/multipart_test/multipartstates/variants_test_part.json @@ -0,0 +1,11 @@ +{ + "variants": { + "slot=bottom": { "model": "multipart_test:multipart/bottom" }, + "slot=top": { "model": "multipart_test:multipart/bottom", "x": 180 }, + "slot=north": { "model": "multipart_test:multipart/vertical" }, + "slot=south": { "model": "multipart_test:multipart/vertical", "y": 180 }, + "slot=east": { "model": "multipart_test:multipart/vertical", "y": 90 }, + "slot=west": { "model": "multipart_test:multipart/vertical", "y": 270 }, + "inventory": { "model": "multipart_test:multipart/vertical", "y": 180 } + } +} \ No newline at end of file