diff --git a/src/main/java/com/buuz135/functionalstorage/block/FramedDrawerBlock.java b/src/main/java/com/buuz135/functionalstorage/block/FramedDrawerBlock.java new file mode 100644 index 0000000..3811cf5 --- /dev/null +++ b/src/main/java/com/buuz135/functionalstorage/block/FramedDrawerBlock.java @@ -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 getTileEntityFactory() { + return (blockPos, state) -> new DrawerTile(this, (BlockEntityType) FunctionalStorage.DRAWER_TYPES.get(this.getType()).stream().filter(registryObjectRegistryObjectPair -> registryObjectRegistryObjectPair.getLeft().get().equals(this)).map(Pair::getRight).findFirst().get().get(), blockPos, state, this.getType()); + } +} diff --git a/src/main/java/com/buuz135/functionalstorage/block/tile/FramedDrawerTile.java b/src/main/java/com/buuz135/functionalstorage/block/tile/FramedDrawerTile.java new file mode 100644 index 0000000..93ec3d6 --- /dev/null +++ b/src/main/java/com/buuz135/functionalstorage/block/tile/FramedDrawerTile.java @@ -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 base, BlockEntityType blockEntityType, BlockPos pos, BlockState state, FunctionalStorage.DrawerType type) { + super(base, blockEntityType, pos, state, type); + } +} diff --git a/src/main/java/com/buuz135/functionalstorage/client/loader/RetexturedModel.java b/src/main/java/com/buuz135/functionalstorage/client/loader/RetexturedModel.java new file mode 100644 index 0000000..99b73f3 --- /dev/null +++ b/src/main/java/com/buuz135/functionalstorage/client/loader/RetexturedModel.java @@ -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 { + private final SimpleBlockModel model; + private final Set retextured; + + @Override + public Collection getTextures(IModelConfiguration owner, Function modelGetter, Set> missingTextureErrors) { + return model.getTextures(owner, modelGetter, missingTextureErrors); + } + + @Override + public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function 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 getAllRetextured(IModelConfiguration owner, SimpleBlockModel model, Set originalSet) { + Set retextured = Sets.newHashSet(originalSet); + for (Map> 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 { + 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 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 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 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 { + /** Cache of texture name to baked model */ + private final Map 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 retextured; + + public Baked(BakedModel baked, IModelConfiguration owner, SimpleBlockModel model, ModelState transform, Set 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 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 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 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); + } + } +} diff --git a/src/main/java/com/buuz135/functionalstorage/client/loader/SimpleBlockModel.java b/src/main/java/com/buuz135/functionalstorage/client/loader/SimpleBlockModel.java new file mode 100644 index 0000000..99777bc --- /dev/null +++ b/src/main/java/com/buuz135/functionalstorage/client/loader/SimpleBlockModel.java @@ -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 { + /** + * 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 parts; + /** + * Fallback textures in case the owner does not contain a block model + */ + private final Map> 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> textures, List 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 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 modelGetter) { + // no work if no parent or the parent is fetched already + if (parent != null || parentLocation == null) { + return; + } + + // iterate through model parents + Set 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 modelGetter, Set 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 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 getTextures(IModelConfiguration owner, List elements, Set> missingTextureErrors) { + // always need a particle texture + Set 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 getTextures(IModelConfiguration owner, Function modelGetter, Set> 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 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 elements, ModelState transform, ItemOverrides overrides, Function 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 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 spriteGetter, ResourceLocation location) { + return bakeModel(owner, this.getElements(), transform, overrides, spriteGetter, location); + } + + @Override + public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function 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> textureMap; + if (json.has("textures")) { + ImmutableMap.Builder> builder = new ImmutableMap.Builder<>(); + ResourceLocation atlas = InventoryMenu.BLOCK_ATLAS; + JsonObject textures = GsonHelper.getAsJsonObject(json, "textures"); + for (Entry 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 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 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 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 { + @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> getTextures() { + return textures; + } + + public BlockModel getParent() { + return parent; + } +} \ No newline at end of file diff --git a/src/main/java/com/buuz135/functionalstorage/client/model/FramedDrawerBlockModel.java b/src/main/java/com/buuz135/functionalstorage/client/model/FramedDrawerBlockModel.java new file mode 100644 index 0000000..289513f --- /dev/null +++ b/src/main/java/com/buuz135/functionalstorage/client/model/FramedDrawerBlockModel.java @@ -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>, Direction>, List> CACHE = CacheBuilder.newBuilder().build(); + private VertexFormat format; + private BakedModel previousModel; + private Map> prevQuads = new HashMap<>(); + + public FramedDrawerBlockModel(BakedModel previousConveyor) { + this.previousModel = previousConveyor; + this.format = DefaultVertexFormat.BLOCK; + } + + @Nonnull + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) { + List 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 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(); + } +} \ No newline at end of file diff --git a/src/main/java/com/buuz135/functionalstorage/client/model/FramedDrawerModelData.java b/src/main/java/com/buuz135/functionalstorage/client/model/FramedDrawerModelData.java new file mode 100644 index 0000000..876309a --- /dev/null +++ b/src/main/java/com/buuz135/functionalstorage/client/model/FramedDrawerModelData.java @@ -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 UPGRADE_PROPERTY = new ModelProperty<>(); + private Map design; + + public FramedDrawerModelData(Map design) { + this.design = design; + } + + public Map getDesign() { + return design; + } +} diff --git a/src/main/resources/assets/functionalstorage/models/block/base_framing_x_1_sides.json b/src/main/resources/assets/functionalstorage/models/block/base_framing_x_1_sides.json new file mode 100644 index 0000000..ac9f872 --- /dev/null +++ b/src/main/resources/assets/functionalstorage/models/block/base_framing_x_1_sides.json @@ -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] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/functionalstorage/models/block/framed_1.json b/src/main/resources/assets/functionalstorage/models/block/framed_1.json new file mode 100644 index 0000000..3452687 --- /dev/null +++ b/src/main/resources/assets/functionalstorage/models/block/framed_1.json @@ -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" ] +} \ No newline at end of file