WIP Framned stuff
This commit is contained in:
parent
622749b0cc
commit
8ea488059a
|
@ -0,0 +1,20 @@
|
||||||
|
package com.buuz135.functionalstorage.block;
|
||||||
|
|
||||||
|
import com.buuz135.functionalstorage.FunctionalStorage;
|
||||||
|
import com.buuz135.functionalstorage.block.tile.DrawerTile;
|
||||||
|
import com.buuz135.functionalstorage.util.DrawerWoodType;
|
||||||
|
import com.buuz135.functionalstorage.util.IWoodType;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
public class FramedDrawerBlock extends DrawerBlock{
|
||||||
|
|
||||||
|
public FramedDrawerBlock(FunctionalStorage.DrawerType type) {
|
||||||
|
super(DrawerWoodType.FRAMED, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockEntityType.BlockEntitySupplier<DrawerTile> getTileEntityFactory() {
|
||||||
|
return (blockPos, state) -> new DrawerTile(this, (BlockEntityType<DrawerTile>) FunctionalStorage.DRAWER_TYPES.get(this.getType()).stream().filter(registryObjectRegistryObjectPair -> registryObjectRegistryObjectPair.getLeft().get().equals(this)).map(Pair::getRight).findFirst().get().get(), blockPos, state, this.getType());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.buuz135.functionalstorage.block.tile;
|
||||||
|
|
||||||
|
import com.buuz135.functionalstorage.FunctionalStorage;
|
||||||
|
import com.hrznstudio.titanium.block.BasicTileBlock;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class FramedDrawerTile extends DrawerTile{
|
||||||
|
|
||||||
|
public FramedDrawerTile(BasicTileBlock<DrawerTile> base, BlockEntityType<DrawerTile> blockEntityType, BlockPos pos, BlockState state, FunctionalStorage.DrawerType type) {
|
||||||
|
super(base, blockEntityType, pos, state, type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
package com.buuz135.functionalstorage.client.loader;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||||
|
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.client.resources.model.Material;
|
||||||
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
import net.minecraft.client.resources.model.ModelState;
|
||||||
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
import net.minecraft.util.GsonHelper;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.client.model.IModelConfiguration;
|
||||||
|
import net.minecraftforge.client.model.IModelLoader;
|
||||||
|
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class from Mantle {@url https://github.com/SlimeKnights/Mantle/blob/1.18.2/src/main/java/slimeknights/mantle/client/model/RetexturedModel.java}
|
||||||
|
*
|
||||||
|
* Model that dynamically retextures a list of textures based on data from {@link RetexturedHelper}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
|
||||||
|
public class RetexturedModel implements IModelGeometry<RetexturedModel> {
|
||||||
|
private final SimpleBlockModel model;
|
||||||
|
private final Set<String> retextured;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Material> getTextures(IModelConfiguration owner, Function<ResourceLocation,UnbakedModel> modelGetter, Set<Pair<String,String>> missingTextureErrors) {
|
||||||
|
return model.getTextures(owner, modelGetter, missingTextureErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<Material,TextureAtlasSprite> spriteGetter, ModelState transform, ItemOverrides overrides, ResourceLocation location) {
|
||||||
|
// bake the model and return
|
||||||
|
BakedModel baked = model.bakeModel(owner, transform, overrides, spriteGetter, location);
|
||||||
|
return new Baked(baked, owner, model, transform, getAllRetextured(owner, this.model, retextured));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of all names to retexture based on the block model texture references
|
||||||
|
* @param owner Model config instance
|
||||||
|
* @param model Model fallback
|
||||||
|
* @param originalSet Original list of names to retexture
|
||||||
|
* @return Set of textures including parent textures
|
||||||
|
*/
|
||||||
|
public static Set<String> getAllRetextured(IModelConfiguration owner, SimpleBlockModel model, Set<String> originalSet) {
|
||||||
|
Set<String> retextured = Sets.newHashSet(originalSet);
|
||||||
|
for (Map<String,Either<Material, String>> textures : ModelTextureIteratable.of(owner, model)) {
|
||||||
|
textures.forEach((name, either) ->
|
||||||
|
either.ifRight(parent -> {
|
||||||
|
if (retextured.contains(parent)) {
|
||||||
|
retextured.add(name);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ImmutableSet.copyOf(retextured);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Registered model loader instance registered */
|
||||||
|
public static class Loader implements IModelLoader<RetexturedModel> {
|
||||||
|
public static final Loader INSTANCE = new Loader();
|
||||||
|
private Loader() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResourceManagerReload(ResourceManager resourceManager) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RetexturedModel read(JsonDeserializationContext context, JsonObject json) {
|
||||||
|
// get base model
|
||||||
|
SimpleBlockModel model = SimpleBlockModel.deserialize(context, json);
|
||||||
|
|
||||||
|
// get list of textures to retexture
|
||||||
|
Set<String> retextured = getRetextured(json);
|
||||||
|
|
||||||
|
// return retextured model
|
||||||
|
return new RetexturedModel(model, retextured);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of retextured textures from the model
|
||||||
|
* @param json Model json
|
||||||
|
* @return List of textures
|
||||||
|
*/
|
||||||
|
public static Set<String> getRetextured(JsonObject json) {
|
||||||
|
if (json.has("retextured")) {
|
||||||
|
// if an array, set from each texture in array
|
||||||
|
JsonElement retextured = json.get("retextured");
|
||||||
|
if (retextured.isJsonArray()) {
|
||||||
|
JsonArray array = retextured.getAsJsonArray();
|
||||||
|
if (array.size() == 0) {
|
||||||
|
throw new JsonSyntaxException("Must have at least one texture in retextured");
|
||||||
|
}
|
||||||
|
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
||||||
|
for (int i = 0; i < array.size(); i++) {
|
||||||
|
builder.add(GsonHelper.convertToString(array.get(i), "retextured[" + i + "]"));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
// if string, single texture
|
||||||
|
if (retextured.isJsonPrimitive()) {
|
||||||
|
return ImmutableSet.of(retextured.getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if neither or missing, error
|
||||||
|
throw new JsonSyntaxException("Missing retextured, expected to find a String or a JsonArray");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Baked variant of the model, used to swap out quads based on the texture */
|
||||||
|
public static class Baked extends DynamicBakedWrapper<BakedModel> {
|
||||||
|
/** Cache of texture name to baked model */
|
||||||
|
private final Map<ResourceLocation,BakedModel> cache = new ConcurrentHashMap<>();
|
||||||
|
/* Properties for rebaking */
|
||||||
|
private final IModelConfiguration owner;
|
||||||
|
private final SimpleBlockModel model;
|
||||||
|
private final ModelState transform;
|
||||||
|
/** List of texture names that are retextured */
|
||||||
|
private final Set<String> retextured;
|
||||||
|
|
||||||
|
public Baked(BakedModel baked, IModelConfiguration owner, SimpleBlockModel model, ModelState transform, Set<String> retextured) {
|
||||||
|
super(baked);
|
||||||
|
this.model = model;
|
||||||
|
this.owner = owner;
|
||||||
|
this.transform = transform;
|
||||||
|
this.retextured = retextured;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the model with the given texture applied
|
||||||
|
* @param name Texture location
|
||||||
|
* @return Retextured model
|
||||||
|
*/
|
||||||
|
private BakedModel getRetexturedModel(ResourceLocation name) {
|
||||||
|
return model.bakeDynamic(new RetexturedConfiguration(owner, retextured, name), transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a cached retextured model, computing it if missing from the cache
|
||||||
|
* @param block Block determining the texture
|
||||||
|
* @return Retextured model
|
||||||
|
*/
|
||||||
|
private BakedModel getCachedModel(Block block) {
|
||||||
|
return cache.computeIfAbsent(ModelHelper.getParticleTexture(block), this::getRetexturedModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextureAtlasSprite getParticleIcon(IModelData data) {
|
||||||
|
// if particle is retextured, fetch particle from the cached model
|
||||||
|
if (retextured.contains("particle")) {
|
||||||
|
Block block = data.getData(RetexturedHelper.BLOCK_PROPERTY);
|
||||||
|
if (block != null) {
|
||||||
|
return getCachedModel(block).getParticleIcon(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalModel.getParticleIcon(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction direction, Random random, IModelData data) {
|
||||||
|
Block block = data.getData(RetexturedHelper.BLOCK_PROPERTY);
|
||||||
|
if (block == null) {
|
||||||
|
return originalModel.getQuads(state, direction, random, data);
|
||||||
|
}
|
||||||
|
return getCachedModel(block).getQuads(state, direction, random, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemOverrides getOverrides() {
|
||||||
|
return RetexturedOverride.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model configuration wrapper to retexture the block
|
||||||
|
*/
|
||||||
|
public static class RetexturedConfiguration extends ModelConfigurationWrapper {
|
||||||
|
/** List of textures to retexture */
|
||||||
|
private final Set<String> retextured;
|
||||||
|
/** Replacement texture */
|
||||||
|
private final Material texture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new configuration wrapper
|
||||||
|
* @param base Original model configuration
|
||||||
|
* @param retextured Set of textures that should be retextured
|
||||||
|
* @param texture New texture to replace those in the set
|
||||||
|
*/
|
||||||
|
public RetexturedConfiguration(IModelConfiguration base, Set<String> retextured, ResourceLocation texture) {
|
||||||
|
super(base);
|
||||||
|
this.retextured = retextured;
|
||||||
|
this.texture = ModelLoaderRegistry.blockMaterial(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTexturePresent(String name) {
|
||||||
|
if (retextured.contains(name)) {
|
||||||
|
return !MissingTextureAtlasSprite.getLocation().equals(texture.texture());
|
||||||
|
}
|
||||||
|
return super.isTexturePresent(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Material resolveTexture(String name) {
|
||||||
|
if (retextured.contains(name)) {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
return super.resolveTexture(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Override list to swap the texture in from NBT */
|
||||||
|
private static class RetexturedOverride extends ItemOverrides {
|
||||||
|
private static final RetexturedOverride INSTANCE = new RetexturedOverride();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BakedModel resolve(BakedModel originalModel, ItemStack stack, @Nullable ClientLevel world, @Nullable LivingEntity entity, int pSeed) {
|
||||||
|
if (stack.isEmpty() || !stack.hasTag()) {
|
||||||
|
return originalModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the block first, ensuring its valid
|
||||||
|
Block block = RetexturedBlockItem.getTexture(stack);
|
||||||
|
if (block == Blocks.AIR) {
|
||||||
|
return originalModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if valid, use the block
|
||||||
|
return ((Baked)originalModel).getCachedModel(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,412 @@
|
||||||
|
package com.buuz135.functionalstorage.client.loader;
|
||||||
|
|
||||||
|
import com.buuz135.functionalstorage.FunctionalStorage;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import com.mojang.datafixers.util.Either;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||||
|
import net.minecraft.client.renderer.block.model.BlockElement;
|
||||||
|
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
||||||
|
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||||
|
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.client.resources.model.Material;
|
||||||
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
import net.minecraft.client.resources.model.ModelState;
|
||||||
|
import net.minecraft.client.resources.model.SimpleBakedModel;
|
||||||
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
import net.minecraft.util.GsonHelper;
|
||||||
|
import net.minecraft.world.inventory.InventoryMenu;
|
||||||
|
import net.minecraftforge.client.model.ForgeModelBakery;
|
||||||
|
import net.minecraftforge.client.model.IModelConfiguration;
|
||||||
|
import net.minecraftforge.client.model.IModelLoader;
|
||||||
|
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplier version of {@link BlockModel} for use in an {@link net.minecraftforge.client.model.IModelLoader}, as the owner handles most block model properties
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class SimpleBlockModel implements IModelGeometry<SimpleBlockModel> {
|
||||||
|
/**
|
||||||
|
* Model loader for vanilla block model, mainly intended for use in fallback registration
|
||||||
|
*/
|
||||||
|
public static final Loader LOADER = new Loader();
|
||||||
|
/**
|
||||||
|
* Location used for baking dynamic models, name does not matter so just using a constant
|
||||||
|
*/
|
||||||
|
private static final ResourceLocation BAKE_LOCATION = new ResourceLocation(FunctionalStorage.MOD_ID, "dynamic_model_baking");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent model location, used to fetch parts and for textures if the owner is not a block model
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ResourceLocation parentLocation;
|
||||||
|
/**
|
||||||
|
* Model parts for baked model, if empty uses parent parts
|
||||||
|
*/
|
||||||
|
private final List<BlockElement> parts;
|
||||||
|
/**
|
||||||
|
* Fallback textures in case the owner does not contain a block model
|
||||||
|
*/
|
||||||
|
private final Map<String, Either<Material, String>> textures;
|
||||||
|
|
||||||
|
private BlockModel parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new simple block model
|
||||||
|
*
|
||||||
|
* @param parentLocation Location of the parent model, if unset has no parent
|
||||||
|
* @param textures List of textures for iteration, in case the owner is not BlockModel
|
||||||
|
* @param parts List of parts in the model
|
||||||
|
*/
|
||||||
|
public SimpleBlockModel(@Nullable ResourceLocation parentLocation, Map<String, Either<Material, String>> textures, List<BlockElement> parts) {
|
||||||
|
this.parts = parts;
|
||||||
|
this.textures = textures;
|
||||||
|
this.parentLocation = parentLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Properties */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the elements in this simple block model
|
||||||
|
*
|
||||||
|
* @return Elements in the model
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public List<BlockElement> getElements() {
|
||||||
|
return parts.isEmpty() && parent != null ? parent.getElements() : parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textures */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches parent models for this model and its parents
|
||||||
|
*
|
||||||
|
* @param modelGetter Model getter function
|
||||||
|
*/
|
||||||
|
public void fetchParent(IModelConfiguration owner, Function<ResourceLocation, UnbakedModel> modelGetter) {
|
||||||
|
// no work if no parent or the parent is fetched already
|
||||||
|
if (parent != null || parentLocation == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through model parents
|
||||||
|
Set<UnbakedModel> chain = Sets.newLinkedHashSet();
|
||||||
|
|
||||||
|
// load the first model directly
|
||||||
|
parent = getParent(modelGetter, chain, parentLocation, owner.getModelName());
|
||||||
|
// null means no model, so set missing
|
||||||
|
if (parent == null) {
|
||||||
|
parent = getMissing(modelGetter);
|
||||||
|
parentLocation = ModelBakery.MISSING_MODEL_LOCATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop through each parent, adding in parents
|
||||||
|
for (BlockModel link = parent; link.parentLocation != null && link.parent == null; link = link.parent) {
|
||||||
|
chain.add(link);
|
||||||
|
|
||||||
|
// fetch model parent
|
||||||
|
link.parent = getParent(modelGetter, chain, link.parentLocation, link.name);
|
||||||
|
|
||||||
|
// null means no model, so set missing
|
||||||
|
if (link.parent == null) {
|
||||||
|
link.parent = getMissing(modelGetter);
|
||||||
|
link.parentLocation = ModelBakery.MISSING_MODEL_LOCATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the parent for a model
|
||||||
|
*
|
||||||
|
* @param modelGetter Model getter function
|
||||||
|
* @param chain Chain of models that are in progress
|
||||||
|
* @param location Location to fetch
|
||||||
|
* @param name Name of the model being fetched
|
||||||
|
* @return Block model instance, null if there was an error
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private static BlockModel getParent(Function<ResourceLocation, UnbakedModel> modelGetter, Set<UnbakedModel> chain, ResourceLocation location, String name) {
|
||||||
|
// model must exist
|
||||||
|
UnbakedModel unbaked = modelGetter.apply(location);
|
||||||
|
if (unbaked == null) {
|
||||||
|
FunctionalStorage.LOGGER.warn("No parent '{}' while loading model '{}'", location, name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// no loops in chain
|
||||||
|
if (chain.contains(unbaked)) {
|
||||||
|
FunctionalStorage.LOGGER.warn("Found 'parent' loop while loading model '{}' in chain: {} -> {}", name, chain.stream().map(Object::toString).collect(Collectors.joining(" -> ")), location);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// model must be block model, this is a serious error in vanilla
|
||||||
|
if (!(unbaked instanceof BlockModel)) {
|
||||||
|
throw new IllegalStateException("BlockModel parent has to be a block model.");
|
||||||
|
}
|
||||||
|
return (BlockModel) unbaked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the missing model, ensuring its the right type
|
||||||
|
*
|
||||||
|
* @param modelGetter Model getter function
|
||||||
|
* @return Missing model as a {@link BlockModel}
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
private static BlockModel getMissing(Function<ResourceLocation, UnbakedModel> modelGetter) {
|
||||||
|
UnbakedModel model = modelGetter.apply(ModelBakery.MISSING_MODEL_LOCATION);
|
||||||
|
if (!(model instanceof BlockModel)) {
|
||||||
|
throw new IllegalStateException("Failed to load missing model");
|
||||||
|
}
|
||||||
|
return (BlockModel) model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the texture dependencies for a list of elements, allows calling outside a simple block model
|
||||||
|
*
|
||||||
|
* @param owner Model configuration
|
||||||
|
* @param elements List of elements to check for textures
|
||||||
|
* @param missingTextureErrors Missing texture set
|
||||||
|
* @return Textures dependencies
|
||||||
|
*/
|
||||||
|
public static Collection<Material> getTextures(IModelConfiguration owner, List<BlockElement> elements, Set<Pair<String, String>> missingTextureErrors) {
|
||||||
|
// always need a particle texture
|
||||||
|
Set<Material> textures = Sets.newHashSet(owner.resolveTexture("particle"));
|
||||||
|
// iterate all elements, fetching needed textures from the material
|
||||||
|
for (BlockElement part : elements) {
|
||||||
|
for (BlockElementFace face : part.faces.values()) {
|
||||||
|
Material material = owner.resolveTexture(face.texture);
|
||||||
|
if (Objects.equals(material.texture(), MissingTextureAtlasSprite.getLocation())) {
|
||||||
|
missingTextureErrors.add(Pair.of(face.texture, owner.getModelName()));
|
||||||
|
}
|
||||||
|
textures.add(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the texture and model dependencies for a block model
|
||||||
|
*
|
||||||
|
* @param owner Model configuration
|
||||||
|
* @param modelGetter Model getter to fetch parent models
|
||||||
|
* @param missingTextureErrors Missing texture set
|
||||||
|
* @return Textures dependencies
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Material> getTextures(IModelConfiguration owner, Function<ResourceLocation, UnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors) {
|
||||||
|
this.fetchParent(owner, modelGetter);
|
||||||
|
return getTextures(owner, getElements(), missingTextureErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Baking */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bakes a single part of the model into the builder
|
||||||
|
*
|
||||||
|
* @param builder Baked model builder
|
||||||
|
* @param owner Model owner
|
||||||
|
* @param part Part to bake
|
||||||
|
* @param transform Model transforms
|
||||||
|
* @param spriteGetter Sprite getter
|
||||||
|
* @param location Model location
|
||||||
|
*/
|
||||||
|
public static void bakePart(SimpleBakedModel.Builder builder, IModelConfiguration owner, BlockElement part, ModelState transform, Function<Material, TextureAtlasSprite> spriteGetter, ResourceLocation location) {
|
||||||
|
for (Direction direction : part.faces.keySet()) {
|
||||||
|
BlockElementFace face = part.faces.get(direction);
|
||||||
|
// ensure the name is not prefixed (it always is)
|
||||||
|
String texture = face.texture;
|
||||||
|
if (texture.charAt(0) == '#') {
|
||||||
|
texture = texture.substring(1);
|
||||||
|
}
|
||||||
|
// bake the face
|
||||||
|
TextureAtlasSprite sprite = spriteGetter.apply(owner.resolveTexture(texture));
|
||||||
|
BakedQuad bakedQuad = BlockModel.makeBakedQuad(part, face, sprite, direction, transform, location);
|
||||||
|
// apply cull face
|
||||||
|
if (face.cullForDirection == null) {
|
||||||
|
builder.addUnculledFace(bakedQuad);
|
||||||
|
} else {
|
||||||
|
builder.addCulledFace(Direction.rotate(transform.getRotation().getMatrix(), face.cullForDirection), bakedQuad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bakes a list of block part elements into a model
|
||||||
|
*
|
||||||
|
* @param owner Model configuration
|
||||||
|
* @param elements Model elements
|
||||||
|
* @param transform Model transform
|
||||||
|
* @param overrides Model overrides
|
||||||
|
* @param spriteGetter Sprite getter instance
|
||||||
|
* @param location Model bake location
|
||||||
|
* @return Baked model
|
||||||
|
*/
|
||||||
|
public static BakedModel bakeModel(IModelConfiguration owner, List<BlockElement> elements, ModelState transform, ItemOverrides overrides, Function<Material, TextureAtlasSprite> spriteGetter, ResourceLocation location) {
|
||||||
|
// iterate parts, adding to the builder
|
||||||
|
TextureAtlasSprite particle = spriteGetter.apply(owner.resolveTexture("particle"));
|
||||||
|
SimpleBakedModel.Builder builder = new SimpleBakedModel.Builder(owner, overrides).particle(particle);
|
||||||
|
for (BlockElement part : elements) {
|
||||||
|
bakePart(builder, owner, part, transform, spriteGetter, location);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #bakeModel(IModelConfiguration, List, ModelState, ItemOverrides, Function, ResourceLocation)}, but passes in sensible defaults for values unneeded in dynamic models
|
||||||
|
*
|
||||||
|
* @param owner Model configuration
|
||||||
|
* @param elements Elements to bake
|
||||||
|
* @param transform Model transform
|
||||||
|
* @return Baked model
|
||||||
|
*/
|
||||||
|
public static BakedModel bakeDynamic(IModelConfiguration owner, List<BlockElement> elements, ModelState transform) {
|
||||||
|
return bakeModel(owner, elements, transform, ItemOverrides.EMPTY, ForgeModelBakery.defaultTextureGetter(), BAKE_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bakes the given block model
|
||||||
|
*
|
||||||
|
* @param owner Model configuration
|
||||||
|
* @param transform Transform to apply
|
||||||
|
* @param overrides Item overrides in baking
|
||||||
|
* @param spriteGetter Sprite getter instance
|
||||||
|
* @param location Bake location
|
||||||
|
* @return Baked model
|
||||||
|
*/
|
||||||
|
public BakedModel bakeModel(IModelConfiguration owner, ModelState transform, ItemOverrides overrides, Function<Material, TextureAtlasSprite> spriteGetter, ResourceLocation location) {
|
||||||
|
return bakeModel(owner, this.getElements(), transform, overrides, spriteGetter, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState transform, ItemOverrides overrides, ResourceLocation location) {
|
||||||
|
return bakeModel(owner, transform, overrides, spriteGetter, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #bakeModel(IModelConfiguration, ModelState, ItemOverrides, Function, ResourceLocation)}, but passes in sensible defaults for values unneeded in dynamic models
|
||||||
|
*
|
||||||
|
* @param owner Model configuration
|
||||||
|
* @param transform Transform to apply
|
||||||
|
* @return Baked model
|
||||||
|
*/
|
||||||
|
public BakedModel bakeDynamic(IModelConfiguration owner, ModelState transform) {
|
||||||
|
return bakeDynamic(owner, this.getElements(), transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Deserializing */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes a SimpleBlockModel from JSON
|
||||||
|
*
|
||||||
|
* @param context Json Context
|
||||||
|
* @param json Json element containing the model
|
||||||
|
* @return Serialized JSON
|
||||||
|
*/
|
||||||
|
public static SimpleBlockModel deserialize(JsonDeserializationContext context, JsonObject json) {
|
||||||
|
// parent, null if missing
|
||||||
|
String parentName = GsonHelper.getAsString(json, "parent", "");
|
||||||
|
ResourceLocation parent = parentName.isEmpty() ? null : new ResourceLocation(parentName);
|
||||||
|
|
||||||
|
// textures, empty map if missing
|
||||||
|
Map<String, Either<Material, String>> textureMap;
|
||||||
|
if (json.has("textures")) {
|
||||||
|
ImmutableMap.Builder<String, Either<Material, String>> builder = new ImmutableMap.Builder<>();
|
||||||
|
ResourceLocation atlas = InventoryMenu.BLOCK_ATLAS;
|
||||||
|
JsonObject textures = GsonHelper.getAsJsonObject(json, "textures");
|
||||||
|
for (Entry<String, JsonElement> entry : textures.entrySet()) {
|
||||||
|
builder.put(entry.getKey(), BlockModel.Deserializer.parseTextureLocationOrReference(atlas, entry.getValue().getAsString()));
|
||||||
|
}
|
||||||
|
textureMap = builder.build();
|
||||||
|
} else {
|
||||||
|
textureMap = Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// elements, empty list if missing
|
||||||
|
List<BlockElement> parts;
|
||||||
|
if (json.has("elements")) {
|
||||||
|
parts = getModelElements(context, GsonHelper.getAsJsonArray(json, "elements"), "elements");
|
||||||
|
} else {
|
||||||
|
parts = Collections.emptyList();
|
||||||
|
}
|
||||||
|
return new SimpleBlockModel(parent, textureMap, parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of models from a JSON array
|
||||||
|
*
|
||||||
|
* @param context Json Context
|
||||||
|
* @param array Json array
|
||||||
|
* @return Model list
|
||||||
|
*/
|
||||||
|
public static List<BlockElement> getModelElements(JsonDeserializationContext context, JsonElement array, String name) {
|
||||||
|
// if just one element, array is optional
|
||||||
|
if (array.isJsonObject()) {
|
||||||
|
return ImmutableList.of(context.deserialize(array.getAsJsonObject(), BlockElement.class));
|
||||||
|
}
|
||||||
|
// if an array, get array of elements
|
||||||
|
if (array.isJsonArray()) {
|
||||||
|
ImmutableList.Builder<BlockElement> builder = ImmutableList.builder();
|
||||||
|
for (JsonElement json : array.getAsJsonArray()) {
|
||||||
|
builder.add((BlockElement) context.deserialize(json, BlockElement.class));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonSyntaxException("Missing " + name + ", expected to find a JsonArray or JsonObject");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logic to implement a vanilla block model
|
||||||
|
*/
|
||||||
|
private static class Loader implements IModelLoader<SimpleBlockModel> {
|
||||||
|
@Override
|
||||||
|
public void onResourceManagerReload(ResourceManager resourceManager) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SimpleBlockModel read(JsonDeserializationContext context, JsonObject json) {
|
||||||
|
return deserialize(context, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ResourceLocation getParentLocation() {
|
||||||
|
return parentLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Either<Material, String>> getTextures() {
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockModel getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Industrial Foregoing.
|
||||||
|
*
|
||||||
|
* Copyright 2021, Buuz135
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in the
|
||||||
|
* Software without restriction, including without limitation the rights to use, copy,
|
||||||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the
|
||||||
|
* following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies
|
||||||
|
* or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.buuz135.functionalstorage.client.model;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.client.model.data.IDynamicBakedModel;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class FramedDrawerBlockModel implements IDynamicBakedModel {
|
||||||
|
|
||||||
|
public static Cache<Pair<Pair<String, Pair<Direction, Direction>>, Direction>, List<BakedQuad>> CACHE = CacheBuilder.newBuilder().build();
|
||||||
|
private VertexFormat format;
|
||||||
|
private BakedModel previousModel;
|
||||||
|
private Map<Direction, List<BakedQuad>> prevQuads = new HashMap<>();
|
||||||
|
|
||||||
|
public FramedDrawerBlockModel(BakedModel previousConveyor) {
|
||||||
|
this.previousModel = previousConveyor;
|
||||||
|
this.format = DefaultVertexFormat.BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {
|
||||||
|
List<BakedQuad> bakedQuads = new ArrayList<>();
|
||||||
|
BakedModel otherModel = Minecraft.getInstance().getBlockRenderer().getBlockModel(Blocks.STONE.defaultBlockState());
|
||||||
|
for (BakedQuad quad : previousModel.getQuads(state, side, rand)) {
|
||||||
|
if (otherModel.getQuads(state, side, rand).size() > 0) {
|
||||||
|
BakedQuadBuilder bakedQuadBuilder = new BakedQuadBuilder(otherModel.getQuads(state, side, rand).get(0).getSprite());
|
||||||
|
bakedQuadBuilder.setQuadOrientation(quad.getDirection());
|
||||||
|
bakedQuadBuilder.setQuadTint(quad.getTintIndex());
|
||||||
|
bakedQuadBuilder.setApplyDiffuseLighting(quad.isShade());
|
||||||
|
quad.pipe(bakedQuadBuilder);
|
||||||
|
|
||||||
|
bakedQuadBuilder.setTexture(otherModel.getQuads(state, side, rand).get(0).getSprite());
|
||||||
|
System.out.println(otherModel.getQuads(state, side, rand).get(0).getVertices().length);
|
||||||
|
BakedQuad extra = bakedQuadBuilder.build();
|
||||||
|
System.out.println(Arrays.toString(extra.getVertices()));
|
||||||
|
bakedQuads.add(extra);
|
||||||
|
//bakedQuadBuilder.getVertexFormat()
|
||||||
|
//bakedQuads.add(bakedQuadBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bakedQuads;/*
|
||||||
|
if (state == null) {
|
||||||
|
if (!prevQuads.containsKey(side))
|
||||||
|
prevQuads.put(side, previousModel.getQuads(state, side, rand));
|
||||||
|
return prevQuads.get(side);
|
||||||
|
}
|
||||||
|
if (!prevQuads.containsKey(side))
|
||||||
|
prevQuads.put(side, previousModel.getQuads(state, side, rand));
|
||||||
|
List<BakedQuad> quads = new ArrayList<>(prevQuads.get(side));
|
||||||
|
return quads;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useAmbientOcclusion() {
|
||||||
|
return previousModel.useAmbientOcclusion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGui3d() {
|
||||||
|
return previousModel.isGui3d();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean usesBlockLight() {
|
||||||
|
return previousModel.usesBlockLight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCustomRenderer() {
|
||||||
|
return previousModel.isCustomRenderer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public TextureAtlasSprite getParticleIcon() {
|
||||||
|
return previousModel.getParticleIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public ItemOverrides getOverrides() {
|
||||||
|
return previousModel.getOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BakedModel handlePerspective(ItemTransforms.TransformType cameraTransformType, PoseStack mat) {
|
||||||
|
return previousModel.handlePerspective(cameraTransformType, mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemTransforms getTransforms() {
|
||||||
|
return previousModel.getTransforms();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Industrial Foregoing.
|
||||||
|
*
|
||||||
|
* Copyright 2021, Buuz135
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in the
|
||||||
|
* Software without restriction, including without limitation the rights to use, copy,
|
||||||
|
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the
|
||||||
|
* following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies
|
||||||
|
* or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.buuz135.functionalstorage.client.model;
|
||||||
|
|
||||||
|
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraftforge.client.model.data.ModelProperty;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FramedDrawerModelData {
|
||||||
|
|
||||||
|
public static final ModelProperty<FramedDrawerModelData> UPGRADE_PROPERTY = new ModelProperty<>();
|
||||||
|
private Map<Integer, Item> design;
|
||||||
|
|
||||||
|
public FramedDrawerModelData(Map<Integer, Item> design) {
|
||||||
|
this.design = design;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Item> getDesign() {
|
||||||
|
return design;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
{
|
||||||
|
"credit": "Made with Blockbench",
|
||||||
|
"textures": {
|
||||||
|
"lock_icon": "functionalstorage:blocks/unlock"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"from": [15, 1, 0],
|
||||||
|
"to": [16, 15, 15],
|
||||||
|
"rotation": {"angle": 0, "axis": "y", "origin": [-8, 0, -8]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [1, 15, 15, 16], "rotation": 90, "texture": "#side", "cullface": "north"},
|
||||||
|
"east": {"uv": [1, 1, 16, 15], "texture": "#side", "cullface": "east"},
|
||||||
|
"west": {"uv": [0, 1, 15, 15], "texture": "#side"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": [0, 15, 0],
|
||||||
|
"to": [16, 16, 16],
|
||||||
|
"rotation": {"angle": 0, "axis": "y", "origin": [-8, 0, -8]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [0, 0, 1, 16], "rotation": 90, "texture": "#side", "cullface": "north"},
|
||||||
|
"east": {"uv": [0, 0, 16, 1], "rotation": 180, "texture": "#side", "cullface": "east"},
|
||||||
|
"south": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "south"},
|
||||||
|
"west": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "west"},
|
||||||
|
"up": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "up"},
|
||||||
|
"down": {"uv": [0, 0, 16, 16], "texture": "#side"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": [0, 1, 0],
|
||||||
|
"to": [1, 15, 15],
|
||||||
|
"rotation": {"angle": 0, "axis": "y", "origin": [-8, 0, -8]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [1, 15, 15, 16], "rotation": 90, "texture": "#side", "cullface": "north"},
|
||||||
|
"east": {"uv": [1, 1, 16, 15], "texture": "#side"},
|
||||||
|
"west": {"uv": [0, 1, 15, 15], "texture": "#side", "cullface": "west"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": [0, 0, 0],
|
||||||
|
"to": [16, 1, 16],
|
||||||
|
"rotation": {"angle": 0, "axis": "y", "origin": [-8, 0, -8]},
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [0, 0, 1, 16], "rotation": 90, "texture": "#side", "cullface": "north"},
|
||||||
|
"east": {"uv": [0, 0, 16, 1], "rotation": 180, "texture": "#side", "cullface": "east"},
|
||||||
|
"south": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "south"},
|
||||||
|
"west": {"uv": [0, 0, 16, 1], "texture": "#side", "cullface": "west"},
|
||||||
|
"up": {"uv": [0, 0, 16, 16], "texture": "#side"},
|
||||||
|
"down": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "down"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": [0, 1, 15],
|
||||||
|
"to": [16, 15, 16],
|
||||||
|
"rotation": {"angle": 0, "axis": "y", "origin": [-8, 0, -8]},
|
||||||
|
"faces": {
|
||||||
|
"east": {"uv": [1, 0, 15, 1], "rotation": 90, "texture": "#side", "cullface": "east"},
|
||||||
|
"south": {"uv": [0, 1, 16, 15], "texture": "#side", "cullface": "south"},
|
||||||
|
"west": {"uv": [1, 15, 15, 16], "rotation": 90, "texture": "#side", "cullface": "west"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": [7.5, 15, -0.01],
|
||||||
|
"to": [8.5, 16, 16],
|
||||||
|
"faces": {
|
||||||
|
"north": {"uv": [0, 0, 16, 16], "texture": "#lock_icon", "cullface": "north"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"display": {
|
||||||
|
"thirdperson_righthand": {
|
||||||
|
"rotation": [75, 45, 0],
|
||||||
|
"translation": [0, 2.5, 0],
|
||||||
|
"scale": [0.375, 0.375, 0.375]
|
||||||
|
},
|
||||||
|
"thirdperson_lefthand": {
|
||||||
|
"rotation": [75, 45, 0],
|
||||||
|
"translation": [0, 2.5, 0],
|
||||||
|
"scale": [0.375, 0.375, 0.375]
|
||||||
|
},
|
||||||
|
"firstperson_righthand": {
|
||||||
|
"rotation": [0, 45, 0],
|
||||||
|
"scale": [0.4, 0.4, 0.4]
|
||||||
|
},
|
||||||
|
"firstperson_lefthand": {
|
||||||
|
"rotation": [0, 225, 0],
|
||||||
|
"scale": [0.4, 0.4, 0.4]
|
||||||
|
},
|
||||||
|
"ground": {
|
||||||
|
"translation": [0, 3, 0],
|
||||||
|
"scale": [0.25, 0.25, 0.25]
|
||||||
|
},
|
||||||
|
"gui": {
|
||||||
|
"rotation": [30, 225, 0],
|
||||||
|
"scale": [0.625, 0.625, 0.625]
|
||||||
|
},
|
||||||
|
"fixed": {
|
||||||
|
"scale": [0.5, 0.5, 0.5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"loader": "functionalstorage:framed",
|
||||||
|
"parent": "functionalstorage:block/base_x_1",
|
||||||
|
"textures": {
|
||||||
|
"particle": "functionalstorage:blocks/acacia_front_1",
|
||||||
|
"front": "functionalstorage:blocks/acacia_front_1",
|
||||||
|
"side": "functionalstorage:blocks/acacia_side"
|
||||||
|
},
|
||||||
|
"retextured": [ "particle", "side" ]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user