Add multi-model support for multiparts

This commit is contained in:
Shadowfacts 2018-12-24 15:43:12 -05:00
parent 83a2a2e852
commit 0f8d9f1353
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
6 changed files with 155 additions and 30 deletions

View File

@ -13,11 +13,8 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.shadowfacts.simplemultipart.SimpleMultipart; import net.shadowfacts.simplemultipart.SimpleMultipart;
import net.shadowfacts.simplemultipart.container.MultipartContainerBlockState; 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.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -42,7 +39,16 @@ public class MultipartContainerBakedModel implements BakedModel {
if (model instanceof MultipartBakedModel) { if (model instanceof MultipartBakedModel) {
return ((MultipartBakedModel)model).getMultipartQuads(partState, side, random).stream(); return ((MultipartBakedModel)model).getMultipartQuads(partState, side, random).stream();
} else { } 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()); .collect(Collectors.toList());
@ -65,7 +71,7 @@ public class MultipartContainerBakedModel implements BakedModel {
@Override @Override
public Sprite getSprite() { public Sprite getSprite() {
return MinecraftClient.getInstance().getSpriteAtlas().getSprite("blocks/stone"); return MinecraftClient.getInstance().getSpriteAtlas().getSprite("block/stone");
} }
@Override @Override

View File

@ -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<Identifier, MultipartFakeBlock> fakeBlocks = new HashMap<>();
private final Multipart multipart;
private final StateFactory<Block, BlockState> 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<Block, BlockState> createFakeStateFactory() {
StateFactory.Builder<Block, BlockState> builder = new StateFactory.Builder<>(this);
multipart.getStateFactory().getProperties().forEach(builder::with);
return builder.build(BlockState::new);
}
@Override
public StateFactory<Block, BlockState> getStateFactory() {
return fakeStateFactory;
}
public BlockState getFakeState(MultipartState state) {
BlockState fakeState = getDefaultState();
for (Map.Entry<Property<?>, 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);
}
}

View File

@ -2,6 +2,7 @@ package net.shadowfacts.simplemultipart.client;
import net.fabricmc.fabric.api.client.model.ModelProvider; import net.fabricmc.fabric.api.client.model.ModelProvider;
import net.fabricmc.fabric.api.client.model.ModelProviderException; import net.fabricmc.fabric.api.client.model.ModelProviderException;
import net.minecraft.class_816;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.block.BlockModels; import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.model.UnbakedModel; import net.minecraft.client.render.model.UnbakedModel;
@ -68,40 +69,63 @@ public class MultipartModelProvider implements ModelProvider {
} }
} }
private UnbakedModel getOrLoadPartModel(ModelIdentifier id) throws ModelProviderException, IOException { private UnbakedModel getOrLoadPartModel(ModelIdentifier modelId) throws ModelProviderException, IOException {
UnbakedModel existing = unbakedModels.get(id); UnbakedModel existing = unbakedModels.get(modelId);
if (existing != null) { if (existing != null) {
return existing; return existing;
} }
return loadModel(id); return loadPartModel(modelId);
} }
private UnbakedModel loadModel(ModelIdentifier id) throws ModelProviderException, IOException { private UnbakedModel loadPartModel(ModelIdentifier modelId) throws ModelProviderException, IOException {
Identifier partStateId = new Identifier(id.getNamespace(), "multipartstates/" + id.getPath() + ".json"); Identifier partId = new Identifier(modelId.getNamespace(), modelId.getPath());
ModelVariantMap variantMap = loadVariantMap(partStateId); Identifier partStateDefId = new Identifier(partId.getNamespace(), "multipartstates/" + partId.getPath() + ".json");
Map<String, WeightedUnbakedModel> variants = variantMap.method_3423();
variants.forEach((variant, model) -> {
unbakedModels.put(new ModelIdentifier(new Identifier(id.getNamespace(), id.getPath()), variant), model);
});
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<String, WeightedUnbakedModel> variants = variantMap.method_3423();
variants.forEach((variant, model) -> {
unbakedModels.put(new ModelIdentifier(partId, variant), model);
});
}
UnbakedModel model = unbakedModels.get(modelId);
if (model == null) { 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; return model;
} }
private ModelVariantMap loadVariantMap(Identifier id) throws IOException { // private StateFactory<Block, BlockState> getStateFactory(class_816 multipartUnbakedModel) {
// try {
// Field f = class_816.class.getDeclaredField("field_4329");
// f.setAccessible(true);
// return (StateFactory<Block, BlockState>)f.get(multipartUnbakedModel);
// } catch (ReflectiveOperationException e) {
// throw new RuntimeException(e);
// }
// }
private ModelVariantMap loadPartVariantMap(MultipartFakeBlock blockAdapter, Identifier partStateDefId) throws IOException {
Resource resource = null; Resource resource = null;
Reader reader = null; Reader reader = null;
try { try {
resource = MinecraftClient.getInstance().getResourceManager().getResource(id); resource = MinecraftClient.getInstance().getResourceManager().getResource(partStateDefId);
reader = new InputStreamReader(resource.getInputStream()); reader = new InputStreamReader(resource.getInputStream());
ModelVariantMap.class_791 context = new ModelVariantMap.class_791(); ModelVariantMap.class_791 context = new ModelVariantMap.class_791();
// context.stateFactory = context.method_3426(blockAdapter.getStateFactory());
// TODO: ^ blockstate translation
return ModelVariantMap.method_3424(context, reader); return ModelVariantMap.method_3424(context, reader);
} finally { } finally {
IOUtils.closeQuietly(reader); IOUtils.closeQuietly(reader);

View File

@ -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" }
}
}
]
}

View File

@ -1,11 +1,15 @@
{ {
"variants": { "multipart": [
"slot=bottom": { "model": "multipart_test:multipart/bottom" }, {
"slot=top": { "model": "multipart_test:multipart/bottom", "x": 180 }, "apply": { "model": "multipart_test:multipart/center" }
"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 }, "when": { "slot": "bottom" },
"slot=west": { "model": "multipart_test:multipart/vertical", "y": 270 }, "apply": { "model": "multipart_test:multipart/bottom" }
"inventory": { "model": "multipart_test:multipart/vertical", "y": 180 } },
} {
"when": { "slot": "north" },
"apply": { "model": "multipart_test:multipart/vertical" }
}
]
} }

View File

@ -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 }
}
}