├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── itemmodelfix │ │ │ ├── icon.png │ │ │ └── lang │ │ │ ├── en_us.json │ │ │ ├── fr_fr.json │ │ │ └── ru_ru.json │ ├── itemmodelfix.mixins.json │ └── fabric.mod.json │ └── java │ └── me │ └── pepperbell │ └── itemmodelfix │ ├── model │ ├── ItemModelGenerationType.java │ ├── PixelDirection.java │ └── ItemModelUtil.java │ ├── config │ ├── ModMenuApiImpl.java │ ├── Config.java │ └── ClothConfigFactory.java │ ├── util │ ├── ParsingUtil.java │ └── MathUtil.java │ ├── ItemModelFix.java │ └── mixin │ └── ItemModelGeneratorMixin.java ├── README.md ├── settings.gradle ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── gradlew └── LICENSE /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PepperCode1/Item-Model-Fix/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/itemmodelfix/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PepperCode1/Item-Model-Fix/HEAD/src/main/resources/assets/itemmodelfix/icon.png -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/model/ItemModelGenerationType.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.model; 2 | 3 | public enum ItemModelGenerationType { 4 | VANILLA, 5 | UNLERP, 6 | OUTLINE, 7 | PIXEL; 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Item-Model-Fix 2 | 3 | A Fabric mod that fixes gaps in generated item models. See the CurseForge page for more information. 4 | 5 | CurseForge project page: https://www.curseforge.com/minecraft/mc-mods/item-model-fix 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name = 'Fabric' 6 | url = 'https://maven.fabricmc.net/' 7 | } 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # fabric 28 | 29 | run/ 30 | -------------------------------------------------------------------------------- /src/main/resources/itemmodelfix.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.pepperbell.itemmodelfix.mixin", 5 | "compatibilityLevel": "JAVA_16", 6 | "client": [ 7 | "ItemModelGeneratorMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/config/ModMenuApiImpl.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.config; 2 | 3 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 4 | import com.terraformersmc.modmenu.api.ModMenuApi; 5 | 6 | import me.pepperbell.itemmodelfix.ItemModelFix; 7 | import net.fabricmc.loader.api.FabricLoader; 8 | 9 | public class ModMenuApiImpl implements ModMenuApi { 10 | @Override 11 | public ConfigScreenFactory getModConfigScreenFactory() { 12 | if (FabricLoader.getInstance().isModLoaded("cloth-config2")) { 13 | return new ClothConfigFactory(ItemModelFix.getConfig()); 14 | } 15 | return screen -> null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/util/ParsingUtil.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.util; 2 | 3 | import net.minecraft.text.Text; 4 | import net.minecraft.util.Language; 5 | 6 | public class ParsingUtil { 7 | public static Text[] parseNewlines(String translationKey) { 8 | if (!Language.getInstance().hasTranslation(translationKey)) { 9 | return null; 10 | } 11 | 12 | String[] strings = Language.getInstance().get(translationKey).split("\n|\\\\n"); 13 | Text[] texts = new Text[strings.length]; 14 | for (int i = 0; i < strings.length; i++) { 15 | texts[i] = Text.literal(strings[i]); 16 | } 17 | 18 | return texts; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to Gradle. 2 | org.gradle.jvmargs = -Xmx1G 3 | 4 | # Fabric Properties 5 | loom_version = 0.12-SNAPSHOT 6 | minecraft_version = 1.19 7 | # https://maven.fabricmc.net/net/fabricmc/yarn 8 | yarn_mappings = 1.19+build.2 9 | # https://maven.fabricmc.net/net/fabricmc/fabric-loader 10 | loader_version = 0.14.7 11 | 12 | # Mod Properties 13 | mod_version = 1.0.3+1.19 14 | maven_group = me.pepperbell 15 | archives_base_name = item-model-fix 16 | 17 | # Dependencies 18 | # https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu 19 | modmenu_version = 4.0.0 20 | # https://www.curseforge.com/minecraft/mc-mods/cloth-config/files 21 | cloth_config_version = 7.0.72 22 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/util/MathUtil.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.util; 2 | 3 | import net.minecraft.client.texture.Sprite; 4 | 5 | public class MathUtil { 6 | public static float unlerp(float delta, float start, float end) { 7 | return (start - delta * end) / (1 - delta); 8 | } 9 | 10 | public static float[] unlerpUVs(float[] uvs, float delta) { 11 | float centerU = (uvs[0] + uvs[2]) / 2.0F; 12 | float centerV = (uvs[1] + uvs[3]) / 2.0F; 13 | uvs[0] = unlerp(delta, uvs[0], centerU); 14 | uvs[2] = unlerp(delta, uvs[2], centerU); 15 | uvs[1] = unlerp(delta, uvs[1], centerV); 16 | uvs[3] = unlerp(delta, uvs[3], centerV); 17 | return uvs; 18 | } 19 | 20 | public static float[] unlerpUVs(float[] uvs, Sprite sprite) { 21 | return unlerpUVs(uvs, sprite.getAnimationFrameDelta()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/ItemModelFix.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix; 2 | 3 | import java.io.File; 4 | import java.nio.file.Path; 5 | 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | import me.pepperbell.itemmodelfix.config.Config; 10 | import net.fabricmc.loader.api.FabricLoader; 11 | 12 | public class ItemModelFix { 13 | public static final String ID = "itemmodelfix"; 14 | public static final Logger LOGGER = LogManager.getLogger("ItemModelFix"); 15 | 16 | private static Config config; 17 | 18 | public static Config getConfig() { 19 | if (config == null) { 20 | loadConfig(); 21 | } 22 | return config; 23 | } 24 | 25 | private static void loadConfig() { 26 | Path configPath = FabricLoader.getInstance().getConfigDir(); 27 | File configFile = new File(configPath.toFile(), "itemmodelfix.json"); 28 | config = new Config(configFile); 29 | config.load(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/model/PixelDirection.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.model; 2 | 3 | import net.minecraft.util.math.Direction; 4 | 5 | public enum PixelDirection { 6 | LEFT(Direction.WEST, -1, 0), 7 | RIGHT(Direction.EAST, 1, 0), 8 | UP(Direction.UP, 0, -1), 9 | DOWN(Direction.DOWN, 0, 1); 10 | 11 | public static final PixelDirection[] VALUES = values(); 12 | 13 | private final Direction direction; 14 | private final int offsetX; 15 | private final int offsetY; 16 | 17 | PixelDirection(Direction direction, int offsetX, int offsetY) { 18 | this.direction = direction; 19 | this.offsetX = offsetX; 20 | this.offsetY = offsetY; 21 | } 22 | 23 | public Direction getDirection() { 24 | return direction; 25 | } 26 | 27 | public int getOffsetX() { 28 | return offsetX; 29 | } 30 | 31 | public int getOffsetY() { 32 | return offsetY; 33 | } 34 | 35 | public boolean isVertical() { 36 | return this == DOWN || this == UP; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "itemmodelfix", 4 | "version": "${version}", 5 | 6 | "name": "Item Model Fix", 7 | "description": "Fixes gaps in generated item models.\nTo access the configuration, follow the instructions on the mod website.", 8 | "authors": [ 9 | "Pepper_Bell" 10 | ], 11 | "contact": { 12 | "homepage": "https://www.curseforge.com/minecraft/mc-mods/item-model-fix", 13 | "issues": "https://github.com/PepperCode1/Item-Model-Fix/issues", 14 | "sources": "https://github.com/PepperCode1/Item-Model-Fix" 15 | }, 16 | 17 | "license": "LGPL-3.0-only", 18 | "icon": "assets/itemmodelfix/icon.png", 19 | 20 | "environment": "client", 21 | "entrypoints": { 22 | "modmenu": [ 23 | "me.pepperbell.itemmodelfix.config.ModMenuApiImpl" 24 | ] 25 | }, 26 | "mixins": [ 27 | "itemmodelfix.mixins.json" 28 | ], 29 | 30 | "depends": { 31 | "fabricloader": ">=0.14.7", 32 | "minecraft": ">=1.19", 33 | "java": ">=17" 34 | }, 35 | "recommends": { 36 | "modmenu": "*", 37 | "cloth-config2": "*" 38 | }, 39 | 40 | "custom": { 41 | "modmenu": { 42 | "links": { 43 | "modmenu.discord": "https://discord.gg/7rnTYXu" 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/assets/itemmodelfix/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "screen.itemmodelfix.config.title": "Item Model Fix Configuration", 3 | "category.itemmodelfix.general": "General", 4 | "option.itemmodelfix.generation_type": "Item Model Generation Type", 5 | "option.itemmodelfix.generation_type.vanilla": "Vanilla", 6 | "option.itemmodelfix.generation_type.vanilla.tooltip": "Performance impact: \u00A7b\u00A7lnone\u00A7r\nUses Vanilla model generation.", 7 | "option.itemmodelfix.generation_type.unlerp": "Unlerp", 8 | "option.itemmodelfix.generation_type.unlerp.tooltip": "Performance impact: \u00A7b\u00A7lnone\u00A7r\nUses Vanilla model generation, but\napplies some math to make it look correct.\nMay cause flickering along model edges.", 9 | "option.itemmodelfix.generation_type.outline": "Outline", 10 | "option.itemmodelfix.generation_type.outline.tooltip": "Performance impact: \u00A7b\u00A7lnone\u00A7r\nSame as Unlerp mode, but uses a\ndifferent generation algorithm to\nprevent enchantment glint flickering.", 11 | "option.itemmodelfix.generation_type.pixel": "Pixel", 12 | "option.itemmodelfix.generation_type.pixel.tooltip": "Performance impact: \u00A7b\u00A7lnone\u00A7r-\u00A7a\u00A7llow\u00A7r\nGenerates all pixels individually. Provides\nbest results but causes a larger impact\nwith higher resolution resource packs." 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/assets/itemmodelfix/lang/fr_fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "screen.itemmodelfix.config.title": "Paramètres d'Item Model Fix", 3 | "category.itemmodelfix.general": "Général", 4 | "option.itemmodelfix.generation_type": "Type de génération du modèle d'objets", 5 | "option.itemmodelfix.generation_type.vanilla": "Vanilla", 6 | "option.itemmodelfix.generation_type.vanilla.tooltip": "Impact sur les performances: \u00A7b\u00A7laucun\u00A7r\nUtilise la génération de modèle Vanilla.", 7 | "option.itemmodelfix.generation_type.unlerp": "Unlerp", 8 | "option.itemmodelfix.generation_type.unlerp.tooltip": "Impact sur les performances: \u00A7b\u00A7laucun\u00A7r\nUtilise la génération de modèle Vanilla,\nmais recalcule partiellement pour\naméliorer le résultat. Peut causer du\nclignotement sur les bords des modèles.", 9 | "option.itemmodelfix.generation_type.outline": "Outline", 10 | "option.itemmodelfix.generation_type.outline.tooltip": "Impact sur les performances: \u00A7b\u00A7laucun\u00A7r\nMême chose que le mode unlerp, mais\nutilise un algorithme différent pour\nempêcher les enchantements de clignoter.", 11 | "option.itemmodelfix.generation_type.pixel": "Pixel", 12 | "option.itemmodelfix.generation_type.pixel.tooltip": "Impact sur les performances: \u00A7b\u00A7laucun\u00A7r-\u00A7a\u00A7lfaible\u00A7r\nGénère tous les pixels individuellement.\nDonne les meilleurs résultats, mais a\nun plus grand impact avec les packs de\ntextures de plus hautes résolutions." 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/assets/itemmodelfix/lang/ru_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "screen.itemmodelfix.config.title": "Настройки Item Model Fix", 3 | "category.itemmodelfix.general": "Основные", 4 | "option.itemmodelfix.generation_type": "Тип генерации модели предмета", 5 | "option.itemmodelfix.generation_type.vanilla": "Ванильный", 6 | "option.itemmodelfix.generation_type.vanilla.tooltip": "Влияние на производительность: \u00A7b\u00A7lотсутствует\u00A7r\nИспользует ванильную генерацию модели.", 7 | "option.itemmodelfix.generation_type.unlerp": "Unlerp", 8 | "option.itemmodelfix.generation_type.unlerp.tooltip": "Влияние на производительность: \u00A7b\u00A7lотсутствует\u00A7r\nИспользует ванильную генерацию модели, но\nс некоторыми математическими вычислениями,\nчтобы она выглядела правильно. Может вызвать\nмерцание эффекта зачарования по краям модели.", 9 | "option.itemmodelfix.generation_type.outline": "Границы", 10 | "option.itemmodelfix.generation_type.outline.tooltip": "Влияние на производительность: \u00A7b\u00A7lотсутствует\u00A7r\nПохож на предыдущий, но использует другой\nалгоритм генерации, чтобы исправить\nмерцание зачарованного предмета.", 11 | "option.itemmodelfix.generation_type.pixel": "Воксели", 12 | "option.itemmodelfix.generation_type.pixel.tooltip": "Влияние на производительность: \u00A7a\u00A7lнизкая\u00A7r\nГенерирует каждый воксель отдельно.\nОбеспечивает наилучший результат, но\nвызывает больше нагрузки с текстурами\nвысокого разрешения." 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/config/Config.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.config; 2 | 3 | import java.io.File; 4 | import java.io.FileReader; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | 11 | import me.pepperbell.itemmodelfix.ItemModelFix; 12 | import me.pepperbell.itemmodelfix.model.ItemModelGenerationType; 13 | 14 | public class Config { 15 | private static final Gson GSON = new GsonBuilder() 16 | .setPrettyPrinting() 17 | .create(); 18 | 19 | private File file; 20 | private Options options = null; 21 | 22 | public Config(File file) { 23 | this.file = file; 24 | } 25 | 26 | public Options getOptions() { 27 | return options; 28 | } 29 | 30 | public void load() { 31 | if (file.exists()) { 32 | try (FileReader reader = new FileReader(file)) { 33 | options = GSON.fromJson(reader, Options.class); 34 | } catch (IOException e) { 35 | ItemModelFix.LOGGER.error("Error loading config", e); 36 | } 37 | if (options != null) { 38 | if (options.replaceInvalidOptions(Options.DEFAULT)) { 39 | save(); 40 | } 41 | } 42 | } 43 | if (options == null) { 44 | options = new Options(); 45 | save(); 46 | } 47 | } 48 | 49 | public void save() { 50 | try (FileWriter writer = new FileWriter(file)) { 51 | writer.write(GSON.toJson(options)); 52 | } catch (IOException e) { 53 | ItemModelFix.LOGGER.error("Error saving config", e); 54 | } 55 | } 56 | 57 | public static class Options { 58 | public static final Options DEFAULT = new Options(); 59 | 60 | public ItemModelGenerationType generationType = ItemModelGenerationType.OUTLINE; 61 | 62 | public boolean replaceInvalidOptions(Options options) { 63 | boolean invalid = false; 64 | if (generationType == null) { 65 | generationType = options.generationType; 66 | invalid = true; 67 | } 68 | return invalid; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/mixin/ItemModelGeneratorMixin.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.mixin; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 11 | 12 | import me.pepperbell.itemmodelfix.ItemModelFix; 13 | import me.pepperbell.itemmodelfix.model.ItemModelGenerationType; 14 | import me.pepperbell.itemmodelfix.model.ItemModelUtil; 15 | import net.minecraft.client.render.model.json.ItemModelGenerator; 16 | import net.minecraft.client.render.model.json.ModelElement; 17 | import net.minecraft.client.render.model.json.ModelElementFace; 18 | import net.minecraft.client.texture.Sprite; 19 | import net.minecraft.util.math.Direction; 20 | 21 | @Mixin(ItemModelGenerator.class) 22 | public class ItemModelGeneratorMixin { 23 | @Inject(at = @At(value = "HEAD"), method = "addLayerElements(ILjava/lang/String;Lnet/minecraft/client/texture/Sprite;)Ljava/util/List;", cancellable = true) 24 | private void onHeadAddLayerElements(int layer, String key, Sprite sprite, CallbackInfoReturnable> cir) { 25 | if (ItemModelFix.getConfig().getOptions().generationType == ItemModelGenerationType.OUTLINE) { 26 | cir.setReturnValue(ItemModelUtil.createOutlineLayerElements(layer, key, sprite)); 27 | } else if (ItemModelFix.getConfig().getOptions().generationType == ItemModelGenerationType.PIXEL) { 28 | cir.setReturnValue(ItemModelUtil.createPixelLayerElements(layer, key, sprite)); 29 | } 30 | } 31 | 32 | @Inject(at = @At(value = "TAIL"), method = "addLayerElements(ILjava/lang/String;Lnet/minecraft/client/texture/Sprite;)Ljava/util/List;", locals = LocalCapture.CAPTURE_FAILHARD) 33 | private void onTailAddLayerElements(int layer, String key, Sprite sprite, CallbackInfoReturnable> cir, Map map, List list) { 34 | if (ItemModelFix.getConfig().getOptions().generationType == ItemModelGenerationType.UNLERP) { 35 | ItemModelUtil.unlerpElements(list, sprite.getAnimationFrameDelta()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/config/ClothConfigFactory.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.config; 2 | 3 | import java.util.Locale; 4 | import java.util.Optional; 5 | 6 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 7 | 8 | import me.pepperbell.itemmodelfix.model.ItemModelGenerationType; 9 | import me.pepperbell.itemmodelfix.util.ParsingUtil; 10 | import me.shedaniel.clothconfig2.api.ConfigBuilder; 11 | import me.shedaniel.clothconfig2.api.ConfigCategory; 12 | import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; 13 | import net.minecraft.client.MinecraftClient; 14 | import net.minecraft.client.gui.screen.Screen; 15 | import net.minecraft.text.Text; 16 | 17 | public class ClothConfigFactory implements ConfigScreenFactory { 18 | private Config config; 19 | 20 | public ClothConfigFactory(Config config) { 21 | this.config = config; 22 | } 23 | 24 | @Override 25 | public Screen create(Screen parent) { 26 | SavingRunnable savingRunnable = new SavingRunnable(); 27 | 28 | ConfigBuilder builder = ConfigBuilder.create() 29 | .setParentScreen(parent) 30 | .setTitle(Text.translatable("screen.itemmodelfix.config.title")) 31 | .setSavingRunnable(savingRunnable); 32 | ConfigEntryBuilder entryBuilder = builder.entryBuilder(); 33 | 34 | ConfigCategory general = builder.getOrCreateCategory(Text.translatable("category.itemmodelfix.general")); 35 | general.addEntry(entryBuilder.startEnumSelector(Text.translatable("option.itemmodelfix.generation_type"), ItemModelGenerationType.class, config.getOptions().generationType) 36 | .setSaveConsumer((value) -> { 37 | if (config.getOptions().generationType != value) { 38 | savingRunnable.reloadResources = true; 39 | } 40 | config.getOptions().generationType = value; 41 | }) 42 | .setEnumNameProvider((value) -> { 43 | return Text.translatable("option.itemmodelfix.generation_type." + value.name().toLowerCase(Locale.ROOT)); 44 | }) 45 | .setTooltipSupplier((value) -> { 46 | return Optional.ofNullable(ParsingUtil.parseNewlines("option.itemmodelfix.generation_type." + value.name().toLowerCase(Locale.ROOT) + ".tooltip")); 47 | }) 48 | .setDefaultValue(Config.Options.DEFAULT.generationType) 49 | .build()); 50 | 51 | return builder.build(); 52 | } 53 | 54 | private class SavingRunnable implements Runnable { 55 | public boolean reloadResources = false; 56 | 57 | @Override 58 | public void run() { 59 | config.save(); 60 | if (reloadResources) { 61 | MinecraftClient.getInstance().reloadResources(); 62 | } 63 | reloadResources = false; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/main/java/me/pepperbell/itemmodelfix/model/ItemModelUtil.java: -------------------------------------------------------------------------------- 1 | package me.pepperbell.itemmodelfix.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.EnumMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import me.pepperbell.itemmodelfix.util.MathUtil; 9 | import net.minecraft.client.render.model.json.ModelElement; 10 | import net.minecraft.client.render.model.json.ModelElementFace; 11 | import net.minecraft.client.render.model.json.ModelElementTexture; 12 | import net.minecraft.client.texture.Sprite; 13 | import net.minecraft.util.math.Direction; 14 | import net.minecraft.util.math.Vec3f; 15 | 16 | public class ItemModelUtil { 17 | public static void unlerpElements(List elements, float delta) { 18 | for (ModelElement element : elements) { 19 | for (ModelElementFace face : element.faces.values()) { 20 | MathUtil.unlerpUVs(face.textureData.uvs, delta); 21 | } 22 | } 23 | } 24 | 25 | public static List createOutlineLayerElements(int layer, String key, Sprite sprite) { 26 | List elements = new ArrayList<>(); 27 | 28 | int width = sprite.getWidth(); 29 | int height = sprite.getHeight(); 30 | float xFactor = width / 16.0F; 31 | float yFactor = height / 16.0F; 32 | float animationFrameDelta = sprite.getAnimationFrameDelta(); 33 | int[] frames = sprite.getDistinctFrameCount().toArray(); 34 | 35 | Map map = new EnumMap<>(Direction.class); 36 | map.put(Direction.SOUTH, new ModelElementFace(null, layer, key, createUnlerpedTexture(new float[] { 0.0F, 0.0F, 16.0F, 16.0F }, 0, animationFrameDelta))); 37 | map.put(Direction.NORTH, new ModelElementFace(null, layer, key, createUnlerpedTexture(new float[] { 16.0F, 0.0F, 0.0F, 16.0F }, 0, animationFrameDelta))); 38 | elements.add(new ModelElement(new Vec3f(0.0F, 0.0F, 7.5F), new Vec3f(16.0F, 16.0F, 8.5F), map, null, true)); 39 | 40 | int first1 = -1; 41 | int first2 = -1; 42 | int last1 = -1; 43 | int last2 = -1; 44 | 45 | for (int y = 0; y < height; ++y) { 46 | for (int x = 0; x < width; ++x) { 47 | if (!isPixelAlwaysTransparent(sprite, frames, x, y)) { 48 | if (doesPixelHaveEdge(sprite, frames, x, y, PixelDirection.DOWN)) { 49 | if (first1 == -1) { 50 | first1 = x; 51 | } 52 | last1 = x; 53 | } 54 | if (doesPixelHaveEdge(sprite, frames, x, y, PixelDirection.UP)) { 55 | if (first2 == -1) { 56 | first2 = x; 57 | } 58 | last2 = x; 59 | } 60 | } else { 61 | if (first1 != -1) { 62 | elements.add(createHorizontalOutlineElement(Direction.DOWN, layer, key, first1, last1, y, height, animationFrameDelta, xFactor, yFactor)); 63 | first1 = -1; 64 | } 65 | if (first2 != -1) { 66 | elements.add(createHorizontalOutlineElement(Direction.UP, layer, key, first2, last2, y, height, animationFrameDelta, xFactor, yFactor)); 67 | first2 = -1; 68 | } 69 | } 70 | } 71 | 72 | if (first1 != -1) { 73 | elements.add(createHorizontalOutlineElement(Direction.DOWN, layer, key, first1, last1, y, height, animationFrameDelta, xFactor, yFactor)); 74 | first1 = -1; 75 | } 76 | if (first2 != -1) { 77 | elements.add(createHorizontalOutlineElement(Direction.UP, layer, key, first2, last2, y, height, animationFrameDelta, xFactor, yFactor)); 78 | first2 = -1; 79 | } 80 | } 81 | 82 | for (int x = 0; x < width; ++x) { 83 | for (int y = 0; y < height; ++y) { 84 | if (!isPixelAlwaysTransparent(sprite, frames, x, y)) { 85 | if (doesPixelHaveEdge(sprite, frames, x, y, PixelDirection.RIGHT)) { 86 | if (first1 == -1) { 87 | first1 = y; 88 | } 89 | last1 = y; 90 | } 91 | if (doesPixelHaveEdge(sprite, frames, x, y, PixelDirection.LEFT)) { 92 | if (first2 == -1) { 93 | first2 = y; 94 | } 95 | last2 = y; 96 | } 97 | } else { 98 | if (first1 != -1) { 99 | elements.add(createVerticalOutlineElement(Direction.EAST, layer, key, first1, last1, x, height, animationFrameDelta, xFactor, yFactor)); 100 | first1 = -1; 101 | } 102 | if (first2 != -1) { 103 | elements.add(createVerticalOutlineElement(Direction.WEST, layer, key, first2, last2, x, height, animationFrameDelta, xFactor, yFactor)); 104 | first2 = -1; 105 | } 106 | } 107 | } 108 | 109 | if (first1 != -1) { 110 | elements.add(createVerticalOutlineElement(Direction.EAST, layer, key, first1, last1, x, height, animationFrameDelta, xFactor, yFactor)); 111 | first1 = -1; 112 | } 113 | if (first2 != -1) { 114 | elements.add(createVerticalOutlineElement(Direction.WEST, layer, key, first2, last2, x, height, animationFrameDelta, xFactor, yFactor)); 115 | first2 = -1; 116 | } 117 | } 118 | 119 | return elements; 120 | } 121 | 122 | public static ModelElement createHorizontalOutlineElement(Direction direction, int layer, String key, int start, int end, int y, int height, float animationFrameDelta, float xFactor, float yFactor) { 123 | Map faces = new EnumMap<>(Direction.class); 124 | faces.put(direction, new ModelElementFace(null, layer, key, createUnlerpedTexture(new float[] { start / xFactor, y / yFactor, (end + 1) / xFactor, (y + 1) / yFactor }, 0, animationFrameDelta))); 125 | return new ModelElement(new Vec3f(start / xFactor, (height - (y + 1)) / yFactor, 7.5F), new Vec3f((end + 1) / xFactor, (height - y) / yFactor, 8.5F), faces, null, true); 126 | } 127 | 128 | public static ModelElement createVerticalOutlineElement(Direction direction, int layer, String key, int start, int end, int x, int height, float animationFrameDelta, float xFactor, float yFactor) { 129 | Map faces = new EnumMap<>(Direction.class); 130 | faces.put(direction, new ModelElementFace(null, layer, key, createUnlerpedTexture(new float[] { (x + 1) / xFactor, start / yFactor, x / xFactor, (end + 1) / yFactor }, 0, animationFrameDelta))); 131 | return new ModelElement(new Vec3f(x / xFactor, (height - (end + 1)) / yFactor, 7.5F), new Vec3f((x + 1) / xFactor, (height - start) / yFactor, 8.5F), faces, null, true); 132 | } 133 | 134 | public static ModelElementTexture createUnlerpedTexture(float[] uvs, int rotation, float delta) { 135 | return new ModelElementTexture(MathUtil.unlerpUVs(uvs, delta), rotation); 136 | } 137 | 138 | public static List createPixelLayerElements(int layer, String key, Sprite sprite) { 139 | List elements = new ArrayList<>(); 140 | 141 | int width = sprite.getWidth(); 142 | int height = sprite.getHeight(); 143 | float xFactor = width / 16.0F; 144 | float yFactor = height / 16.0F; 145 | int[] frames = sprite.getDistinctFrameCount().toArray(); 146 | 147 | for (int y = 0; y < height; ++y) { 148 | for (int x = 0; x < width; ++x) { 149 | if (!isPixelAlwaysTransparent(sprite, frames, x, y)) { 150 | Map faces = new EnumMap<>(Direction.class); 151 | ModelElementFace face = new ModelElementFace(null, layer, key, new ModelElementTexture(new float[] { x / xFactor, y / yFactor, (x + 1) / xFactor, (y + 1) / yFactor }, 0)); 152 | ModelElementFace flippedFace = new ModelElementFace(null, layer, key, new ModelElementTexture(new float[] { (x + 1) / xFactor, y / yFactor, x / xFactor, (y + 1) / yFactor }, 0)); 153 | 154 | faces.put(Direction.SOUTH, face); 155 | faces.put(Direction.NORTH, flippedFace); 156 | for (PixelDirection pixelDirection : PixelDirection.VALUES) { 157 | if (doesPixelHaveEdge(sprite, frames, x, y, pixelDirection)) { 158 | faces.put(pixelDirection.getDirection(), pixelDirection.isVertical() ? face : flippedFace); 159 | } 160 | } 161 | 162 | elements.add(new ModelElement(new Vec3f(x / xFactor, (height - (y + 1)) / yFactor, 7.5F), new Vec3f((x + 1) / xFactor, (height - y) / yFactor, 8.5F), faces, null, true)); 163 | } 164 | } 165 | } 166 | 167 | return elements; 168 | } 169 | 170 | public static boolean isPixelOutsideSprite(Sprite sprite, int x, int y) { 171 | return x < 0 || y < 0 || x >= sprite.getWidth() || y >= sprite.getHeight(); 172 | } 173 | 174 | public static boolean isPixelTransparent(Sprite sprite, int frame, int x, int y) { 175 | return isPixelOutsideSprite(sprite, x, y) ? true : sprite.isPixelTransparent(frame, x, y); 176 | } 177 | 178 | public static boolean isPixelAlwaysTransparent(Sprite sprite, int[] frames, int x, int y) { 179 | for (int frame : frames) { 180 | if (!isPixelTransparent(sprite, frame, x, y)) { 181 | return false; 182 | } 183 | } 184 | return true; 185 | } 186 | 187 | public static boolean doesPixelHaveEdge(Sprite sprite, int[] frames, int x, int y, PixelDirection direction) { 188 | int x1 = x + direction.getOffsetX(); 189 | int y1 = y + direction.getOffsetY(); 190 | if (isPixelOutsideSprite(sprite, x1, y1)) { 191 | return true; 192 | } 193 | for (int frame : frames) { 194 | if (!isPixelTransparent(sprite, frame, x, y) && isPixelTransparent(sprite, frame, x1, y1)) { 195 | return true; 196 | } 197 | } 198 | return false; 199 | } 200 | } 201 | --------------------------------------------------------------------------------