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