diff --git a/src/main/java/mods/betterfoliage/mixin/BlockStateMixin.java b/src/main/java/mods/betterfoliage/mixin/BlockStateMixin.java index 0d22a6c..89f0aca 100644 --- a/src/main/java/mods/betterfoliage/mixin/BlockStateMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/BlockStateMixin.java @@ -15,6 +15,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; * Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks. */ @Mixin(BlockState.class) +@SuppressWarnings({"UnnecessaryQualifiedMemberReference", "deprecation"}) public class BlockStateMixin { private static final String callFrom = "Lnet/minecraft/block/BlockState;func_215703_d(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F"; private static final String callTo = "Lnet/minecraft/block/Block;func_220080_a(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)F"; diff --git a/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java b/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java index 40dc82c..cc2c1c0 100644 --- a/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java @@ -18,7 +18,7 @@ import java.util.Random; @Mixin(ChunkRender.class) public class ChunkRenderMixin { - private static final String rebuildChunk = "Lnet/minecraft/client/renderer/chunk/ChunkRender;rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V"; + private static final String rebuildChunk = "rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V"; private static final String renderBlock = "Lnet/minecraft/client/renderer/BlockRendererDispatcher;renderBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/IEnviromentBlockReader;Lnet/minecraft/client/renderer/BufferBuilder;Ljava/util/Random;Lnet/minecraftforge/client/model/data/IModelData;)Z"; @Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = renderBlock)) diff --git a/src/main/java/mods/betterfoliage/mixin/ChunkRenderOptifineMixin.java b/src/main/java/mods/betterfoliage/mixin/ChunkRenderOptifineMixin.java index f7bc0eb..5220a8c 100644 --- a/src/main/java/mods/betterfoliage/mixin/ChunkRenderOptifineMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ChunkRenderOptifineMixin.java @@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.Slice; @Mixin(ChunkRender.class) public class ChunkRenderOptifineMixin { - private static final String rebuildChunk = "Lnet/minecraft/client/renderer/chunk/ChunkRender;rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V"; + private static final String rebuildChunk = "rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V"; private static final String invokeReflector = "Lnet/optifine/reflect/Reflector;callBoolean(Ljava/lang/Object;Lnet/optifine/reflect/ReflectorMethod;[Ljava/lang/Object;)Z"; private static final String forgeBlockCanRender = "Lnet/minecraft/client/renderer/chunk/ChunkRender;FORGE_BLOCK_CAN_RENDER_IN_LAYER:Z"; diff --git a/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java b/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java index 1340994..18bbd9b 100644 --- a/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(ChunkRender.class) public class ChunkRenderVanillaMixin { - private static final String rebuildChunk = "Lnet/minecraft/client/renderer/chunk/ChunkRender;rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V"; + private static final String rebuildChunk = "rebuildChunk(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderTask;)V"; private static final String canRenderInLayer = "Lnet/minecraft/block/BlockState;canRenderInLayer(Lnet/minecraft/util/BlockRenderLayer;)Z"; @Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = canRenderInLayer)) diff --git a/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java b/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java index 662d9c2..25b66b8 100644 --- a/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java @@ -1,7 +1,7 @@ package mods.betterfoliage.mixin; -import mods.betterfoliage.client.Hooks; -import mods.octarinecore.client.resource.AsyncSpriteProviderManager; +import mods.betterfoliage.BetterFoliage; +import mods.octarinecore.client.resource.AsnycSpriteProviderManager; import net.minecraft.client.renderer.model.ModelBakery; import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.profiler.IProfiler; @@ -9,18 +9,19 @@ import net.minecraft.resources.IResourceManager; import net.minecraft.util.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ModelBakery.class) abstract public class ModelBakeryMixin { - private static final String processLoading = "Lnet/minecraft/client/renderer/model/ModelBakery;processLoading(Lnet/minecraft/profiler/IProfiler;)V"; + private static final String processLoading = "processLoading(Lnet/minecraft/profiler/IProfiler;)V"; private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/lang/Iterable;Lnet/minecraft/profiler/IProfiler;)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;"; @Redirect(method = processLoading, at = @At(value = "INVOKE", target = stitch)) AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable idList, IProfiler profiler) { - return AsyncSpriteProviderManager.INSTANCE.onStitchBlockAtlas(this, atlas, manager, idList, profiler); + AsnycSpriteProviderManager.StitchWrapper wrapper = BetterFoliage.INSTANCE.getBlockSprites().prepare(this, atlas, manager, idList, profiler); + AtlasTexture.SheetData sheet = atlas.stitch(manager, wrapper.getIdList(), profiler); + wrapper.complete(sheet); + return sheet; } } diff --git a/src/main/java/mods/betterfoliage/mixin/ParticleManagerMixin.java b/src/main/java/mods/betterfoliage/mixin/ParticleManagerMixin.java new file mode 100644 index 0000000..88c1942 --- /dev/null +++ b/src/main/java/mods/betterfoliage/mixin/ParticleManagerMixin.java @@ -0,0 +1,29 @@ +package mods.betterfoliage.mixin; + +import mods.betterfoliage.BetterFoliage; +import mods.octarinecore.client.resource.AsnycSpriteProviderManager; +import net.minecraft.client.particle.ParticleManager; +import net.minecraft.client.renderer.texture.AtlasTexture; +import net.minecraft.profiler.IProfiler; +import net.minecraft.resources.IResourceManager; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ParticleManager.class) +public class ParticleManagerMixin { + + private static final String reload = "reload(Lnet/minecraft/resources/IFutureReloadListener$IStage;Lnet/minecraft/resources/IResourceManager;Lnet/minecraft/profiler/IProfiler;Lnet/minecraft/profiler/IProfiler;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"; + private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/lang/Iterable;Lnet/minecraft/profiler/IProfiler;)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;"; + + // ewww :S + @SuppressWarnings("UnresolvedMixinReference") + @Redirect(method = "*", at = @At(value = "INVOKE", target = stitch)) + AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable idList, IProfiler profiler) { + AsnycSpriteProviderManager.StitchWrapper wrapper = BetterFoliage.INSTANCE.getParticleSprites().prepare(this, atlas, manager, idList, profiler); + AtlasTexture.SheetData sheet = atlas.stitch(manager, wrapper.getIdList(), profiler); + wrapper.complete(sheet); + return sheet; + } +} diff --git a/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt b/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt index ba0a08a..1873b9c 100644 --- a/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt +++ b/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt @@ -1,14 +1,21 @@ package mods.betterfoliage import mods.betterfoliage.client.Client -import mods.betterfoliage.client.config.Config -import mods.octarinecore.client.resource.GeneratorPack -import net.alexwells.kottle.FMLKotlinModLoadingContext +import mods.betterfoliage.client.render.RisingSoulTextures +import mods.octarinecore.client.gui.textComponent +import mods.octarinecore.client.resource.AsnycSpriteProviderManager +import mods.octarinecore.client.resource.AsyncSpriteProvider +import mods.octarinecore.client.resource.Atlas +import mods.octarinecore.client.resource.GeneratedBlockTexturePack +import net.minecraft.block.BlockState import net.minecraft.client.Minecraft -import net.minecraftforge.fml.ModLoadingContext -import net.minecraftforge.fml.common.Mod -import net.minecraftforge.fml.config.ModConfig -import org.apache.logging.log4j.Level.DEBUG +import net.minecraft.client.particle.ParticleManager +import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.util.math.BlockPos +import net.minecraft.util.text.TextFormatting +import net.minecraft.util.text.TranslationTextComponent +import net.minecraftforge.registries.ForgeRegistries +import org.apache.logging.log4j.Level import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.simple.SimpleLogger import org.apache.logging.log4j.util.PropertiesUtil @@ -16,17 +23,11 @@ import java.io.File import java.io.PrintStream import java.util.* -@Mod(BetterFoliage.MOD_ID) object BetterFoliage { - const val MOD_ID = "" - const val MOD_NAME = "Better Foliage" - - val modBus = FMLKotlinModLoadingContext.get().modEventBus - var log = LogManager.getLogger("BetterFoliage") var logDetail = SimpleLogger( "BetterFoliage", - DEBUG, + Level.DEBUG, false, false, true, false, "yyyy-MM-dd HH:mm:ss", null, @@ -37,16 +38,39 @@ object BetterFoliage { }) ) - val genPack = GeneratorPack( - "bf_gen", - "Better Foliage generated assets", - "bf_generated_pack.png" - ) + val blockSprites = AsnycSpriteProviderManager("bf-blocks-extra") + val particleSprites = AsnycSpriteProviderManager("bf-particles-extra") + val asyncPack = GeneratedBlockTexturePack("bf_gen", "Better Foliage generated assets", logDetail) + + fun getSpriteManager(atlas: Atlas) = when(atlas) { + Atlas.BLOCKS -> blockSprites + Atlas.PARTICLES -> particleSprites + } as AsnycSpriteProviderManager init { - log.log(DEBUG, "Constructing mod") - ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build()) - Minecraft.getInstance().resourcePackList.addPackFinder(genPack.packFinder) - Client.init() + blockSprites.providers.add(asyncPack) + } + + fun log(level: Level, msg: String) { + log.log(level, "[BetterFoliage] $msg") + logDetail.log(level, msg) + } + + fun logDetail(msg: String) { + logDetail.log(Level.DEBUG, msg) + } + + fun logRenderError(state: BlockState, location: BlockPos) { + if (state in Client.suppressRenderErrors) return + Client.suppressRenderErrors.add(state) + + val blockName = ForgeRegistries.BLOCKS.getKey(state.block).toString() + val blockLoc = "${location.x},${location.y},${location.z}" + Minecraft.getInstance().ingameGUI.chatGUI.printChatMessage(TranslationTextComponent( + "betterfoliage.rendererror", + textComponent(blockName, TextFormatting.GOLD), + textComponent(blockLoc, TextFormatting.GOLD) + )) + logDetail("Error rendering block $state at $blockLoc") } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt b/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt new file mode 100644 index 0000000..156c707 --- /dev/null +++ b/src/main/kotlin/mods/betterfoliage/BetterFoliageMod.kt @@ -0,0 +1,35 @@ +package mods.betterfoliage + +import mods.betterfoliage.client.Client +import mods.betterfoliage.client.config.BlockConfig +import mods.betterfoliage.client.config.Config +import mods.octarinecore.client.resource.AsnycSpriteProviderManager +import mods.octarinecore.client.resource.GeneratedBlockTexturePack +import net.alexwells.kottle.FMLKotlinModLoadingContext +import net.minecraft.client.Minecraft +import net.minecraft.client.particle.ParticleManager +import net.minecraft.client.renderer.model.ModelBakery +import net.minecraftforge.fml.ModLoadingContext +import net.minecraftforge.fml.common.Mod +import net.minecraftforge.fml.config.ModConfig +import org.apache.logging.log4j.Level.DEBUG +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.simple.SimpleLogger +import org.apache.logging.log4j.util.PropertiesUtil +import java.io.File +import java.io.PrintStream +import java.util.* + +@Mod(BetterFoliageMod.MOD_ID) +object BetterFoliageMod { + const val MOD_ID = "betterfoliage" + + val bus = FMLKotlinModLoadingContext.get().modEventBus + + init { + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build()) + Minecraft.getInstance().resourcePackList.addPackFinder(BetterFoliage.asyncPack.finder) + bus.register(BlockConfig) + Client.init() + } +} \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/Client.kt b/src/main/kotlin/mods/betterfoliage/client/Client.kt index f91d714..895b006 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Client.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Client.kt @@ -1,6 +1,6 @@ package mods.betterfoliage.client -import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.integration.ForestryIntegration @@ -8,11 +8,11 @@ import mods.betterfoliage.client.integration.IC2RubberIntegration import mods.betterfoliage.client.integration.OptifineCustomColors import mods.betterfoliage.client.integration.TechRebornRubberIntegration import mods.betterfoliage.client.render.* -import mods.betterfoliage.client.texture.* +import mods.betterfoliage.client.texture.AsyncGrassDiscovery +import mods.betterfoliage.client.texture.AsyncLeafDiscovery +import mods.betterfoliage.client.texture.LeafParticleRegistry import mods.octarinecore.client.gui.textComponent import mods.octarinecore.client.render.RenderDecorator -import mods.octarinecore.client.resource.CenteringTextureGenerator -import mods.octarinecore.client.resource.AsyncSpriteProviderManager import mods.octarinecore.client.resource.IConfigChangeListener import net.minecraft.block.BlockState import net.minecraft.client.Minecraft @@ -32,15 +32,7 @@ object Client { val suppressRenderErrors = mutableSetOf() - // texture generators - val genGrass = GrassGenerator("bf_gen_grass") - val genLeaves = LeafGenerator("bf_gen_leaves") - val genReeds = CenteringTextureGenerator("bf_gen_reeds", 1, 2) - fun init() { - // add resource generators to pack - listOf(genGrass, genLeaves, genReeds).forEach { BetterFoliage.genPack.generators.add(it) } - // init renderers renderers = listOf( RenderGrass(), @@ -60,7 +52,6 @@ object Client { // init other singletons val singletons = listOf( BlockConfig, - LeafParticleRegistry, ChunkOverlayManager, LeafWindTracker, RisingSoulTextures @@ -75,6 +66,8 @@ object Client { TechRebornRubberIntegration ) + LeafParticleRegistry.init() + // add basic block support instances as last AsyncLeafDiscovery.init() AsyncGrassDiscovery.init() @@ -84,28 +77,5 @@ object Client { configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance() configListeners.forEach { it.onConfigChange() } } - - fun log(level: Level, msg: String) { - BetterFoliage.log.log(level, "[BetterFoliage] $msg") - BetterFoliage.logDetail.log(level, msg) - } - - fun logDetail(msg: String) { - BetterFoliage.logDetail.log(Level.DEBUG, msg) - } - - fun logRenderError(state: BlockState, location: BlockPos) { - if (state in suppressRenderErrors) return - suppressRenderErrors.add(state) - - val blockName = ForgeRegistries.BLOCKS.getKey(state.block).toString() - val blockLoc = "${location.x},${location.y},${location.z}" - Minecraft.getInstance().ingameGUI.chatGUI.printChatMessage(TranslationTextComponent( - "betterfoliage.rendererror", - textComponent(blockName, TextFormatting.GOLD), - textComponent(blockLoc, TextFormatting.GOLD) - )) - logDetail("Error rendering block $state at $blockLoc") - } } diff --git a/src/main/kotlin/mods/betterfoliage/client/Hooks.kt b/src/main/kotlin/mods/betterfoliage/client/Hooks.kt index 46e36b0..7c2e9a8 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Hooks.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Hooks.kt @@ -1,7 +1,6 @@ @file:JvmName("Hooks") package mods.betterfoliage.client -import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config @@ -9,16 +8,13 @@ import mods.betterfoliage.client.render.* import mods.octarinecore.ThreadLocalDelegate import mods.octarinecore.client.render.* import mods.octarinecore.client.render.lighting.DefaultLightingCtx -import mods.octarinecore.client.render.lighting.LightingCtx import mods.octarinecore.common.plus -import mods.octarinecore.metaprog.allAvailable import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.client.Minecraft import net.minecraft.client.renderer.BlockRendererDispatcher import net.minecraft.client.renderer.BufferBuilder -import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.world.ClientWorld import net.minecraft.util.BlockRenderLayer import net.minecraft.util.BlockRenderLayer.CUTOUT diff --git a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt index 21361f7..4f7ac8b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt +++ b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt @@ -1,14 +1,17 @@ package mods.betterfoliage.client.config import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.integration.ShadersModIntegration import mods.octarinecore.common.config.* import net.minecraft.util.ResourceLocation +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.fml.config.ModConfig private fun featureEnable() = boolean(true).lang("enabled") // Config singleton -object Config : DelegatingConfig(BetterFoliage.MOD_ID, BetterFoliage.MOD_ID) { +object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_ID) { val enabled by boolean(true) val nVidia by boolean(false) @@ -160,7 +163,15 @@ object BlockConfig { val cactus = blocks("cactus_default.cfg") val netherrack = blocks("netherrack_blocks_default.cfg") - init { BetterFoliage.modBus.register(this) } - private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) } - private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) } + init { BetterFoliageMod.bus.register(this) } + private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) } + private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) } + + @SubscribeEvent + fun onConfig(event: ModConfig.ModConfigEvent) { + list.forEach { when(it) { + is ConfigurableBlockMatcher -> it.readDefaults() + is ModelTextureListConfiguration -> it.readDefaults() + } } + } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/ForestryIntegration.kt b/src/main/kotlin/mods/betterfoliage/client/integration/ForestryIntegration.kt index b98331b..bf06004 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/ForestryIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/ForestryIntegration.kt @@ -18,6 +18,7 @@ import mods.octarinecore.metaprog.ClassRef.Companion.boolean import net.minecraft.block.BlockState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.resources.IResourceManager import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader import net.minecraftforge.fml.ModList @@ -57,7 +58,7 @@ object ForestryIntegration { } } -object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry { +object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry { override val logger = BetterFoliage.logDetail var idToValue = emptyMap() @@ -79,11 +80,11 @@ object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegist return idToValue[textureLoc] } - override fun setup(bakeryFuture: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { + override fun setup(manager: IResourceManager, bakeryF: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { val futures = mutableMapOf>() return StitchPhases( - discovery = bakeryFuture.thenRunAsync { + discovery = bakeryF.thenRunAsync { val allLeaves = TextureLeaves_leafTextures.getStatic() allLeaves.entries.forEach { (type, leaves) -> log("base leaf type $type") @@ -96,7 +97,7 @@ object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegist } } }, - cleanup = atlasFuture.sheet.thenRunAsync { + cleanup = atlasFuture.runAfter { idToValue = futures.mapValues { it.value.get() } } ) @@ -124,7 +125,7 @@ object ForestryLogDiscovery : ModelDiscovery() { val heartSprite = atlas.sprite(heart) val barkSprite = atlas.sprite(bark) - return atlas.afterStitch { + return atlas.mapAfter { SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get())) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt b/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt index 7300986..d65c000 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt @@ -1,5 +1,6 @@ package mods.betterfoliage.client.integration +import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.Client import mods.octarinecore.* import mods.octarinecore.client.render.CombinedContext @@ -22,7 +23,7 @@ object OptifineCustomColors { val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier) init { - Client.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }") + BetterFoliage.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }") } val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() } diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt b/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt index aa1db31..87f1ffe 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt @@ -1,7 +1,6 @@ package mods.betterfoliage.client.integration import mods.betterfoliage.BetterFoliage -import mods.betterfoliage.client.Client import mods.betterfoliage.client.render.LogRegistry import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo @@ -13,14 +12,12 @@ import mods.octarinecore.common.rotate import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.allAvailable import net.minecraft.client.renderer.model.BlockModel -import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.util.Direction import net.minecraft.util.Direction.* import net.minecraft.util.ResourceLocation import net.minecraftforge.fml.ModList import org.apache.logging.log4j.Level -import org.apache.logging.log4j.Logger import java.util.concurrent.CompletableFuture object IC2RubberIntegration { @@ -29,9 +26,9 @@ object IC2RubberIntegration { init { if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) { - Client.log(Level.INFO, "IC2 rubber support initialized") + BetterFoliage.log(Level.INFO, "IC2 rubber support initialized") LogRegistry.registries.add(IC2LogDiscovery) - AsyncSpriteProviderManager.providers.add(IC2LogDiscovery) + BetterFoliage.blockSprites.providers.add(IC2LogDiscovery) } } } @@ -42,9 +39,9 @@ object TechRebornRubberIntegration { init { if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) { - Client.log(Level.INFO, "TechReborn rubber support initialized") + BetterFoliage.log(Level.INFO, "TechReborn rubber support initialized") LogRegistry.registries.add(TechRebornLogDiscovery) - AsyncSpriteProviderManager.providers.add(TechRebornLogDiscovery) + BetterFoliage.blockSprites.providers.add(TechRebornLogDiscovery) } } } @@ -90,7 +87,7 @@ object IC2LogDiscovery : ModelDiscovery() { log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}") val endSprite = atlas.sprite(textureNames[0]) val sideSprite = atlas.sprite(textureNames[1]) - return atlas.afterStitch { + return atlas.mapAfter { SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get())) } } @@ -111,9 +108,9 @@ object IC2LogDiscovery : ModelDiscovery() { val downSprite = atlas.sprite(textureNames[1]) val sideSprite = atlas.sprite(textureNames[2]) val spotSprite = atlas.sprite(textureNames[3]) - return if (spotDir != null) atlas.afterStitch { + return if (spotDir != null) atlas.mapAfter { RubberLogInfo(Axis.Y, spotDir, upSprite.get(), downSprite.get(), spotSprite.get(), listOf(sideSprite.get())) - } else atlas.afterStitch { + } else atlas.mapAfter { SimpleColumnInfo(Axis.Y, upSprite.get(), downSprite.get(), listOf(sideSprite.get())) } } @@ -138,7 +135,7 @@ object TechRebornLogDiscovery : ModelDiscovery() { val endSprite = atlas.sprite(textureNames[0]) val sideSprite = atlas.sprite(textureNames[1]) val sapSprite = atlas.sprite(textureNames[2]) - return atlas.afterStitch { + return atlas.mapAfter { RubberLogInfo(Axis.Y, sapSide, endSprite.get(), endSprite.get(), sapSprite.get(), listOf(sideSprite.get())) } } @@ -148,7 +145,7 @@ object TechRebornLogDiscovery : ModelDiscovery() { if (textureNames.all { it != "missingno" }) { val endSprite = atlas.sprite(textureNames[0]) val sideSprite = atlas.sprite(textureNames[1]) - return atlas.afterStitch { + return atlas.mapAfter { SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get())) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt b/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt index fa53db4..6a03a25 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt @@ -1,5 +1,6 @@ package mods.betterfoliage.client.integration +import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config @@ -37,7 +38,7 @@ object ShadersModIntegration { } init { - Client.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }") + BetterFoliage.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }") } /** Quads rendered inside this block will use the given block entity data in shader programs. */ @@ -59,9 +60,9 @@ object ShadersModIntegration { /** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */ inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = - renderAs(Config.shaders.grassId, MODEL, ctx.renderCtx!!.renderBuffer, enabled, func) + renderAs(Config.shaders.grassId, MODEL, ctx.renderCtx.renderBuffer, enabled, func) /** Quads rendered inside this block will behave as leaf blocks in shader programs. */ inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = - renderAs(Config.shaders.leavesId, MODEL, ctx.renderCtx!!.renderBuffer, enabled, func) + renderAs(Config.shaders.leavesId, MODEL, ctx.renderCtx.renderBuffer, enabled, func) } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/EntityRisingSoulFX.kt b/src/main/kotlin/mods/betterfoliage/client/render/EntityRisingSoulFX.kt index 951f01f..c20bb42 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/EntityRisingSoulFX.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/EntityRisingSoulFX.kt @@ -1,8 +1,10 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.AbstractEntityFX import mods.octarinecore.client.resource.Atlas import mods.octarinecore.client.resource.ResourceHandler @@ -59,17 +61,13 @@ AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.to prevPos = previous, size = scale, alpha = alpha, - icon = RisingSoulTextures.trackIcon.icon!! + icon = RisingSoulTextures.trackIcon ) } } } -object RisingSoulTextures : ResourceHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus, targetAtlas = Atlas.PARTICLES) { - val headIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "rising_soul_$idx") } - val trackIcon = iconStatic(BetterFoliage.MOD_ID, "soul_track") - - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${headIcons.num} soul particle textures") - } +object RisingSoulTextures : ResourceHandler(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus, targetAtlas = Atlas.PARTICLES) { + val headIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "rising_soul_$idx") } + val trackIcon by sprite(Identifier(BetterFoliageMod.MOD_ID, "soul_track")) } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt index 7168b4c..d8c78f1 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderAlgae.kt @@ -1,6 +1,7 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.integration.ShadersModIntegration @@ -12,17 +13,13 @@ import net.minecraft.util.ResourceLocation import net.minecraft.world.biome.Biome import org.apache.logging.log4j.Level.DEBUG -class RenderAlgae : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderAlgae : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { val noise = simplexNoise() - val algaeIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_algae_$idx") } + val algaeIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_algae_$idx") } val algaeModels = modelSet(64) { idx -> RenderGrass.grassTopQuads(Config.algae.heightMin, Config.algae.heightMax)(idx) } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${algaeIcons.num} algae textures") - } - override fun isEligible(ctx: CombinedContext) = Config.enabled && Config.algae.enabled && ctx.state(up2).material == Material.WATER && @@ -38,7 +35,7 @@ class RenderAlgae : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) ShadersModIntegration.grass(ctx, Config.algae.shaderWind) { ctx.render( algaeModels[rand[2]], - icon = { _, qi, _ -> algaeIcons[rand[qi and 1]]!! } + icon = { _, qi, _ -> algaeIcons[rand[qi and 1]] } ) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt index 9decbcc..c9978e4 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt @@ -1,7 +1,7 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage -import mods.betterfoliage.client.Client +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo @@ -15,7 +15,6 @@ import mods.octarinecore.common.config.SimpleBlockMatcher import net.minecraft.block.BlockState import net.minecraft.block.CactusBlock import net.minecraft.util.Direction.* -import net.minecraft.util.ResourceLocation import org.apache.logging.log4j.Level.DEBUG import java.util.concurrent.CompletableFuture @@ -25,7 +24,7 @@ object AsyncCactusDiscovery : ConfigurableModelDiscovery() { override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side")) override fun processModel(state: BlockState, textures: List, atlas: AtlasFuture): CompletableFuture? { val sprites = textures.map { atlas.sprite(Identifier(it)) } - return atlas.afterStitch { + return atlas.mapAfter { SimpleColumnInfo( Axis.Y, sprites[0].get(), @@ -36,17 +35,17 @@ object AsyncCactusDiscovery : ConfigurableModelDiscovery() { } fun init() { - AsyncSpriteProviderManager.providers.add(this) + BetterFoliage.blockSprites.providers.add(this) } } -class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderCactus : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { val cactusStemRadius = 0.4375 val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] } - val iconCross = iconStatic(ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_cactus")) - val iconArm = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_cactus_arm_$idx") } + val iconCross by sprite(Identifier(BetterFoliageMod.MOD_ID, "blocks/better_cactus")) + val iconArm = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_cactus_arm_$idx") } val modelStem = model { horizontalRectangle(x1 = -cactusStemRadius, x2 = cactusStemRadius, z1 = -cactusStemRadius, z2 = cactusStemRadius, y = 0.5) @@ -76,10 +75,6 @@ class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) .toCross(UP) { it.move(xzDisk(modelIdx) * Config.cactus.hOffset) }.addAll() } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${iconArm.num} cactus arm textures") - } - override fun isEligible(ctx: CombinedContext): Boolean = Config.enabled && Config.cactus.enabled && AsyncCactusDiscovery[ctx] != null @@ -97,13 +92,13 @@ class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) ) ctx.render( modelCross[ctx.semiRandom(0)], - icon = { _, _, _ -> iconCross.icon!!} + icon = { _, _, _ -> iconCross } ) ctx.render( modelArm[ctx.semiRandom(1)], cactusArmRotation[ctx.semiRandom(2) % 4], - icon = { _, _, _ -> iconArm[ctx.semiRandom(3)]!!} + icon = { _, _, _ -> iconArm[ctx.semiRandom(3)] } ) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt index dddf6d9..9d5515b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderConnectedGrass.kt @@ -1,6 +1,6 @@ package mods.betterfoliage.client.render -import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.texture.GrassRegistry import mods.octarinecore.client.render.CombinedContext @@ -10,7 +10,7 @@ import mods.octarinecore.common.horizontalDirections import mods.octarinecore.common.offset import net.minecraft.tags.BlockTags -class RenderConnectedGrass : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderConnectedGrass : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { override fun isEligible(ctx: CombinedContext) = Config.enabled && Config.connectedGrass.enabled && BlockTags.DIRT_LIKE.contains(ctx.state.block) && @@ -27,7 +27,7 @@ class RenderConnectedGrass : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage } } -class RenderConnectedGrassLog : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderConnectedGrassLog : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { override fun isEligible(ctx: CombinedContext) = Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass && diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt index f0aa74a..c5e3276 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderCoral.kt @@ -1,34 +1,27 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config import mods.octarinecore.client.render.* import mods.octarinecore.client.render.lighting.* -import mods.octarinecore.common.Int3 -import mods.octarinecore.common.allDirOffsets import mods.octarinecore.common.allDirections -import mods.octarinecore.common.offset import mods.octarinecore.random import net.minecraft.block.material.Material -import net.minecraft.client.renderer.BlockRendererDispatcher -import net.minecraft.client.renderer.BufferBuilder -import net.minecraft.util.BlockRenderLayer import net.minecraft.util.Direction.Axis import net.minecraft.util.Direction.UP import net.minecraft.util.ResourceLocation import net.minecraft.world.biome.Biome -import net.minecraftforge.client.model.data.IModelData import org.apache.logging.log4j.Level.DEBUG -import java.util.* -class RenderCoral : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderCoral : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { val noise = simplexNoise() - val coralIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_coral_$idx") } - val crustIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_crust_$idx") } + val coralIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_coral_$idx") } + val crustIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_crust_$idx") } val coralModels = modelSet(64) { modelIdx -> verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0) .scale(Config.coral.size).move(0.5 to UP) @@ -44,11 +37,6 @@ class RenderCoral : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) } } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${coralIcons.num} coral textures") - Client.log(DEBUG, "Registered ${crustIcons.num} coral crust textures") - } - override fun isEligible(ctx: CombinedContext) = Config.enabled && Config.coral.enabled && (ctx.state(up2).material == Material.WATER || Config.coral.shallowWater) && @@ -67,7 +55,7 @@ class RenderCoral : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) ctx.render( coralModels[variation++], rotationFromUp[idx], - icon = { _, qi, _ -> if (qi == 4) crustIcons[variation]!! else coralIcons[variation + (qi and 1)]!!} + icon = { _, qi, _ -> if (qi == 4) crustIcons[variation] else coralIcons[variation + (qi and 1)] } ) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt index 22251f0..bf7f529 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderGrass.kt @@ -1,10 +1,13 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.integration.OptifineCustomColors import mods.betterfoliage.client.integration.ShadersModIntegration +import mods.betterfoliage.client.resource.Identifier +import mods.betterfoliage.client.texture.GeneratedGrass import mods.betterfoliage.client.texture.GrassRegistry import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.Model @@ -18,10 +21,9 @@ import mods.octarinecore.common.allDirections import mods.octarinecore.random import net.minecraft.tags.BlockTags import net.minecraft.util.Direction.* -import net.minecraft.util.ResourceLocation import org.apache.logging.log4j.Level.DEBUG -class RenderGrass : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderGrass : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { companion object { @JvmStatic fun grassTopQuads(heightMin: Double, heightMax: Double): Model.(Int)->Unit = { modelIdx -> @@ -36,18 +38,13 @@ class RenderGrass : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) val noise = simplexNoise() - val normalIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx") } - val snowedIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_grass_snowed_$idx") } - val normalGenIcon = iconStatic { Client.genGrass.register(texture = "minecraft:blocks/tallgrass", isSnowed = false) } - val snowedGenIcon = iconStatic { Client.genGrass.register(texture = "minecraft:blocks/tallgrass", isSnowed = true) } + val normalIcons = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_grass_long_$idx") } + val snowedIcons = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_grass_snowed_$idx") } + val normalGenIcon by sprite { GeneratedGrass(sprite = "minecraft:blocks/tallgrass", isSnowed = false).register(BetterFoliage.asyncPack) } + val snowedGenIcon by sprite { GeneratedGrass(sprite = "minecraft:blocks/tallgrass", isSnowed = true).register(BetterFoliage.asyncPack) } val grassModels = modelSet(64) { idx -> grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax)(idx) } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${normalIcons.num} grass textures") - Client.log(DEBUG, "Registered ${snowedIcons.num} snowed grass textures") - } - override fun isEligible(ctx: CombinedContext) = Config.enabled && (Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) && @@ -97,7 +94,7 @@ class RenderGrass : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) ctx.render( grassModels[rand[0]], translation = ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero), - icon = { _, qi, _ -> if (Config.shortGrass.useGenerated) iconGen.icon!! else iconset[rand[qi and 1]]!! }, + icon = { _, qi, _ -> if (Config.shortGrass.useGenerated) iconGen else iconset[rand[qi and 1]] }, postProcess = { _, _, _, _, _ -> if (isSnowed) setGrey(1.0f) else multiplyColor(grass.overrideColor ?: blockColor) } ) } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt index 6fa5f58..b8ca01f 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLeaves.kt @@ -1,9 +1,10 @@ package mods.betterfoliage.client.render -import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.integration.OptifineCustomColors import mods.betterfoliage.client.integration.ShadersModIntegration +import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.texture.LeafRegistry import mods.octarinecore.PI2 import mods.octarinecore.client.render.CombinedContext @@ -17,11 +18,10 @@ import mods.octarinecore.common.allDirections import mods.octarinecore.common.vec import mods.octarinecore.random import net.minecraft.util.Direction.UP -import net.minecraft.util.ResourceLocation import java.lang.Math.cos import java.lang.Math.sin -class RenderLeaves : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderLeaves : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { val leavesModel = model { verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41) @@ -30,7 +30,7 @@ class RenderLeaves : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) .scale(Config.leaves.size) .toCross(UP).addAll() } - val snowedIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_leaves_snowed_$idx") } + val snowedIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_leaves_snowed_$idx") } val perturbs = vectorSet(64) { idx -> val angle = PI2 * idx / 64.0 diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt index e123276..c2e4376 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLilypad.kt @@ -1,20 +1,21 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.integration.ShadersModIntegration +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.lighting.FlatOffsetNoColor import mods.octarinecore.common.Int3 import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.UP -import net.minecraft.util.ResourceLocation import org.apache.logging.log4j.Level.DEBUG -class RenderLilypad : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderLilypad : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { val rootModel = model { verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -1.5, yTop = -0.5) @@ -27,15 +28,10 @@ class RenderLilypad : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus .setFlatShader(FlatOffsetNoColor(Int3.zero)) .toCross(UP).addAll() } - val rootIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_lilypad_roots_$idx") } - val flowerIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_lilypad_flower_$idx") } + val rootIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_roots_$idx") } + val flowerIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_flower_$idx") } val perturbs = vectorSet(64) { modelIdx -> xzDisk(modelIdx) * Config.lilypad.hOffset } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${rootIcon.num} lilypad root textures") - Client.log(DEBUG, "Registered ${flowerIcon.num} lilypad flower textures") - } - override fun isEligible(ctx: CombinedContext): Boolean = Config.enabled && Config.lilypad.enabled && BlockConfig.lilypad.matchesClass(ctx.state.block) @@ -49,7 +45,7 @@ class RenderLilypad : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus rootModel.model, translation = ctx.blockCenter.add(perturbs[rand[2]]), forceFlat = true, - icon = { ctx, qi, q -> rootIcon[rand[qi and 1]]!! } + icon = { ctx, qi, q -> rootIcon[rand[qi and 1]] } ) } @@ -57,7 +53,7 @@ class RenderLilypad : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus flowerModel.model, translation = ctx.blockCenter.add(perturbs[rand[4]]), forceFlat = true, - icon = { _, _, _ -> flowerIcon[rand[0]]!! } + icon = { _, _, _ -> flowerIcon[rand[0]] } ) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt index 0394263..7919d34 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt @@ -1,6 +1,7 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config @@ -20,7 +21,7 @@ import net.minecraft.util.Direction.Axis import org.apache.logging.log4j.Level import java.util.concurrent.CompletableFuture -class RenderLog : AbstractRenderColumn(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderLog : AbstractRenderColumn(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { override val renderOnCutout: Boolean get() = false @@ -57,7 +58,7 @@ object AsyncLogDiscovery : ConfigurableModelDiscovery() { val axis = getAxis(state) logger.log(Level.DEBUG, "$logName: axis $axis") val spriteList = textures.map { atlas.sprite(Identifier(it)) } - return atlas.afterStitch { + return atlas.mapAfter { SimpleColumnInfo( axis, spriteList[0].get(), @@ -80,6 +81,6 @@ object AsyncLogDiscovery : ConfigurableModelDiscovery() { fun init() { LogRegistry.registries.add(this) - AsyncSpriteProviderManager.providers.add(this) + BetterFoliage.blockSprites.providers.add(this) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderMycelium.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderMycelium.kt index 4792c5e..9b10f98 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderMycelium.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderMycelium.kt @@ -1,26 +1,23 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.noPost import mods.octarinecore.common.Double3 import net.minecraft.util.Direction.UP -import net.minecraft.util.ResourceLocation import org.apache.logging.log4j.Level.DEBUG -class RenderMycelium : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderMycelium : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { - val myceliumIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_mycel_$idx") } + val myceliumIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_mycel_$idx") } val myceliumModel = modelSet(64) { idx -> RenderGrass.grassTopQuads(Config.shortGrass.heightMin, Config.shortGrass.heightMax)(idx) } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${myceliumIcon.num} mycelium textures") - } - override fun isEligible(ctx: CombinedContext): Boolean { if (!Config.enabled || !Config.shortGrass.myceliumEnabled) return false return BlockConfig.mycelium.matchesClass(ctx.state.block) @@ -38,7 +35,7 @@ class RenderMycelium : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBu ctx.render( myceliumModel[rand[0]], translation = ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero), - icon = { _, qi, _ -> myceliumIcon[rand[qi and 1]]!! }, + icon = { _, qi, _ -> myceliumIcon[rand[qi and 1]] }, postProcess = if (isSnowed) whitewash else noPost ) } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderNetherrack.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderNetherrack.kt index f88b663..6c8e314 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderNetherrack.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderNetherrack.kt @@ -1,27 +1,21 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.* import mods.octarinecore.client.render.lighting.* -import mods.octarinecore.common.Int3 -import mods.octarinecore.common.Rotation import mods.octarinecore.random -import net.minecraft.client.renderer.BlockRendererDispatcher -import net.minecraft.client.renderer.BufferBuilder -import net.minecraft.util.BlockRenderLayer import net.minecraft.util.Direction.Axis import net.minecraft.util.Direction.* -import net.minecraft.util.ResourceLocation -import net.minecraftforge.client.model.data.IModelData import org.apache.logging.log4j.Level.DEBUG -import java.util.* -class RenderNetherrack : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderNetherrack : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { - val netherrackIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_netherrack_$idx") } + val netherrackIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_netherrack_$idx") } val netherrackModel = modelSet(64) { modelIdx -> verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yTop = -0.5, yBottom = -0.5 - random(Config.netherrack.heightMin, Config.netherrack.heightMax)) @@ -31,10 +25,6 @@ class RenderNetherrack : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.mod } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${netherrackIcon.num} netherrack textures") - } - override fun isEligible(ctx: CombinedContext): Boolean { if (!Config.enabled || !Config.netherrack.enabled) return false return BlockConfig.netherrack.matchesClass(ctx.state.block) @@ -48,7 +38,7 @@ class RenderNetherrack : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.mod val rand = ctx.semiRandomArray(2) ctx.render( netherrackModel[rand[0]], - icon = { _, qi, _ -> netherrackIcon[rand[qi and 1]]!! } + icon = { _, qi, _ -> netherrackIcon[rand[qi and 1]] } ) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt index e5939b7..9e1e1c5 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderReeds.kt @@ -1,23 +1,28 @@ package mods.betterfoliage.client.render import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.integration.ShadersModIntegration +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.lighting.FlatOffsetNoColor +import mods.octarinecore.client.resource.CenteredSprite import mods.octarinecore.random import net.minecraft.block.material.Material import net.minecraft.tags.BlockTags import net.minecraft.util.Direction.UP -import net.minecraft.util.ResourceLocation import org.apache.logging.log4j.Level.DEBUG -class RenderReeds : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { +class RenderReeds : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) { val noise = simplexNoise() - val reedIcons = iconSet { idx -> Client.genReeds.registerResource(ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_reed_$idx")) } + val reedIcons = spriteSetTransformed( + check = { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_reed_$idx")}, + register = { CenteredSprite(it).register(BetterFoliage.asyncPack) } + ) val reedModels = modelSet(64) { modelIdx -> val height = random(Config.reed.heightMin, Config.reed.heightMax) val waterline = 0.875f @@ -36,10 +41,6 @@ class RenderReeds : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) } } - override fun afterPreStitch() { - Client.log(DEBUG, "Registered ${reedIcons.num} reed textures") - } - override fun isEligible(ctx: CombinedContext) = Config.enabled && Config.reed.enabled && ctx.state(up2).material == Material.AIR && @@ -59,7 +60,7 @@ class RenderReeds : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) ctx.render( reedModels[ctx.semiRandom(0)], forceFlat = true, - icon = { _, _, _ -> reedIcons[iconVar]!! } + icon = { _, _, _ -> reedIcons[iconVar] } ) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/column/AbstractRenderer.kt b/src/main/kotlin/mods/betterfoliage/client/render/column/AbstractRenderer.kt index 884f32a..29f2fc6 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/column/AbstractRenderer.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/column/AbstractRenderer.kt @@ -1,5 +1,6 @@ package mods.betterfoliage.client.render.column +import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.Client import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs @@ -97,7 +98,7 @@ abstract class AbstractRenderColumn(modId: String, modBus: IEventBus) : RenderDe ColumnLayerData.SkipRender -> return ColumnLayerData.NormalRender -> return ctx.render() ColumnLayerData.ResolveError, null -> { - Client.logRenderError(ctx.state, ctx.pos) + BetterFoliage.logRenderError(ctx.state, ctx.pos) return ctx.render() } } diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/GrassGenerator.kt b/src/main/kotlin/mods/betterfoliage/client/texture/GeneratedGrass.kt similarity index 58% rename from src/main/kotlin/mods/betterfoliage/client/texture/GrassGenerator.kt rename to src/main/kotlin/mods/betterfoliage/client/texture/GeneratedGrass.kt index b0e116b..6454a15 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/GrassGenerator.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/GeneratedGrass.kt @@ -1,11 +1,9 @@ package mods.betterfoliage.client.texture +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.resource.* -import mods.octarinecore.client.resource.Atlas -import net.minecraft.util.ResourceLocation -import net.minecraftforge.resource.VanillaResourceType.TEXTURES +import net.minecraft.resources.IResourceManager import java.awt.image.BufferedImage -import java.io.InputStream /** * Generate Short Grass textures from [Blocks.tallgrass] block textures. @@ -13,16 +11,13 @@ import java.io.InputStream * * @param[domain] Resource domain of generator */ -class GrassGenerator(domain: String) : GeneratorBase(domain, TEXTURES) { +data class GeneratedGrass(val sprite: Identifier, val isSnowed: Boolean, val atlas: Atlas = Atlas.BLOCKS) { + constructor(sprite: String, isSnowed: Boolean) : this(Identifier(sprite), isSnowed) - override val locationMapper = Atlas.BLOCKS::unwrap + fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw) - fun register(texture: String, isSnowed: Boolean) = registerResource(Key(ResourceLocation(texture), isSnowed)) - - override fun exists(key: Key) = resourceManager.hasResource(Atlas.BLOCKS.wrap(key.texture)) - - override fun get(key: Key): InputStream? { - val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key.texture)]?.loadImage() ?: return null + fun draw(resourceManager: IResourceManager): ByteArray { + val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite)) val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR) val graphics = result.createGraphics() @@ -31,7 +26,7 @@ class GrassGenerator(domain: String) : GeneratorBase(domain, val frames = baseTexture.height / size // iterate all frames - for (frame in 0 .. frames - 1) { + for (frame in 0 until frames) { val baseFrame = baseTexture.getSubimage(0, size * frame, size, size) val grassFrame = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR) @@ -45,14 +40,11 @@ class GrassGenerator(domain: String) : GeneratorBase(domain, } // blend with white if snowed - if (key.isSnowed) { + if (isSnowed) { for (x in 0..result.width - 1) for (y in 0..result.height - 1) { result[x, y] = blendRGB(result[x, y], 16777215, 2, 3) } } - - return result.asStream + return result.bytes } - - data class Key(val texture: ResourceLocation, val isSnowed: Boolean) -} +} \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/LeafGenerator.kt b/src/main/kotlin/mods/betterfoliage/client/texture/GeneratedLeaf.kt similarity index 65% rename from src/main/kotlin/mods/betterfoliage/client/texture/LeafGenerator.kt rename to src/main/kotlin/mods/betterfoliage/client/texture/GeneratedLeaf.kt index a61aaa4..2a14bcf 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/LeafGenerator.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/GeneratedLeaf.kt @@ -1,47 +1,38 @@ package mods.betterfoliage.client.texture -import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod import mods.octarinecore.client.resource.* -import mods.octarinecore.client.resource.Atlas import net.minecraft.resources.IResource +import net.minecraft.resources.IResourceManager import net.minecraft.util.ResourceLocation -import net.minecraftforge.resource.VanillaResourceType.TEXTURES import java.awt.image.BufferedImage -import java.io.InputStream /** * Generate round leaf textures from leaf block textures. * The base texture is tiled 2x2, then parts of it are made transparent by applying a mask to the alpha channel. * - * Generator parameter _type_: Leaf type (configurable by user). Different leaf types may have their own alpha mask. + * Different leaf types may have their own alpha mask. * * @param[domain] Resource domain of generator */ -class LeafGenerator(domain: String) : GeneratorBase(domain, TEXTURES) { +data class GeneratedLeaf(val sprite: ResourceLocation, val leafType: String, val atlas: Atlas = Atlas.BLOCKS) { - override val locationMapper = Atlas.BLOCKS::unwrap + fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw) - fun register(texture: ResourceLocation, leafType: String) = registerResource(Key(texture, leafType)) + fun draw(resourceManager: IResourceManager): ByteArray { + val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite)) - override fun exists(key: Key) = resourceManager.hasResource(Atlas.BLOCKS.wrap(key.texture)) - - override fun get(key: Key): InputStream? { - -// val handDrawnLoc = Atlas.BLOCKS.wrap(key.texture) -// resourceManager[handDrawnLoc]?.loadImage()?.let { return it.asStream } - - val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key.texture)]?.loadImage() ?: return null val size = baseTexture.width val frames = baseTexture.height / size - val maskTexture = (getLeafMask(key.leafType, size * 2) ?: getLeafMask("default", size * 2))?.loadImage() + val maskTexture = (getLeafMask(leafType, size * 2) ?: getLeafMask("default", size * 2))?.loadImage() fun scale(i: Int) = i * maskTexture!!.width / (size * 2) val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR) val graphics = leafTexture.createGraphics() // iterate all frames - for (frame in 0 .. frames - 1) { + for (frame in 0 until frames) { val baseFrame = baseTexture.getSubimage(0, size * frame, size, size) val leafFrame = BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR) @@ -55,7 +46,7 @@ class LeafGenerator(domain: String) : GeneratorBase(domain, T // overlay alpha mask if (maskTexture != null) { - for (x in 0 .. size * 2 - 1) for (y in 0 .. size * 2 - 1) { + for (x in 0 until size * 2) for (y in 0 until size * 2) { val basePixel = leafFrame[x, y].toLong() and 0xFFFFFFFFL val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL leafFrame[x, y] = (basePixel and maskPixel).toInt() @@ -66,7 +57,7 @@ class LeafGenerator(domain: String) : GeneratorBase(domain, T graphics.drawImage(leafFrame, 0, size * frame * 2, null) } - return leafTexture.asStream + return leafTexture.bytes } /** @@ -76,7 +67,7 @@ class LeafGenerator(domain: String) : GeneratorBase(domain, T * @param[maxSize] Preferred mask size. */ fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { size -> - ResourceLocation(BetterFoliage.MOD_ID, "textures/blocks/leafmask_${size}_${type}.png") + ResourceLocation(BetterFoliageMod.MOD_ID, "textures/blocks/leafmask_${size}_${type}.png") } /** @@ -92,6 +83,4 @@ class LeafGenerator(domain: String) : GeneratorBase(domain, T while(size > 2) { sizes.add(size); size /= 2 } return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull() } - - data class Key(val texture: ResourceLocation, val leafType: String) } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt index 1655385..d831ef7 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt @@ -41,11 +41,11 @@ object AsyncGrassDiscovery : ConfigurableModelDiscovery() { val textureName = textures[0] val spriteF = atlas.sprite(Identifier(textureName)) logger.log(Level.DEBUG, "$logName: texture $textureName") - return atlas.afterStitch { + return atlas.mapAfter { val sprite = spriteF.get() logger.log(Level.DEBUG, "$logName: block state $state") logger.log(Level.DEBUG, "$logName: texture $textureName") - val hsb = HSB.fromColor(sprite.averageColor ?: defaultGrassColor) + val hsb = HSB.fromColor(sprite.averageColor) val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) { logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}") logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color") @@ -60,6 +60,6 @@ object AsyncGrassDiscovery : ConfigurableModelDiscovery() { fun init() { GrassRegistry.registries.add(this) - AsyncSpriteProviderManager.providers.add(this) + BetterFoliage.blockSprites.providers.add(this) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/LeafParticleRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/LeafParticleRegistry.kt index fe0e8ee..884802a 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/LeafParticleRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/LeafParticleRegistry.kt @@ -1,42 +1,57 @@ package mods.betterfoliage.client.texture import mods.betterfoliage.BetterFoliage +import mods.betterfoliage.BetterFoliageMod +import mods.betterfoliage.client.resource.Identifier +import mods.betterfoliage.client.resource.Sprite import mods.octarinecore.client.resource.* import mods.octarinecore.stripStart import mods.octarinecore.client.resource.Atlas +import mods.octarinecore.common.sinkAsync +import net.minecraft.client.particle.ParticleManager +import net.minecraft.resources.IResourceManager import net.minecraft.util.ResourceLocation -import net.minecraftforge.client.event.TextureStitchEvent -import net.minecraftforge.eventbus.api.SubscribeEvent +import java.util.concurrent.CompletableFuture -object LeafParticleRegistry { +class FixedSpriteSet(val sprites: List) : SpriteSet { + override val num = sprites.size + override fun get(idx: Int) = sprites[idx % num] +} + +object LeafParticleRegistry : AsyncSpriteProvider { val targetAtlas = Atlas.PARTICLES val typeMappings = TextureMatcher() - val particles = hashMapOf() + val particles = hashMapOf() operator fun get(type: String) = particles[type] ?: particles["default"]!! - init { BetterFoliage.modBus.register(this) } - - @SubscribeEvent - fun handlePreStitch(event: TextureStitchEvent.Pre) { - if (!targetAtlas.matches(event)) return - + override fun setup(manager: IResourceManager, particleF: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { particles.clear() - typeMappings.loadMappings(ResourceLocation(BetterFoliage.MOD_ID, "leaf_texture_mappings.cfg")) + val futures = mutableMapOf>>() - val allTypes = (typeMappings.mappings.map { it.type } + "default").distinct() - allTypes.forEach { leafType -> - val particleSet = IconSet(Atlas.PARTICLES) { - idx -> ResourceLocation(BetterFoliage.MOD_ID, "falling_leaf_${leafType}_$idx") - }.apply { onPreStitch(event) } - if (leafType == "default" || particleSet.num > 0) particles[leafType] = particleSet - } + return StitchPhases( + discovery = particleF.sinkAsync { + typeMappings.loadMappings(Identifier(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg")) + (typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType -> + val ids = (0 until 16).map { idx -> Identifier(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") } + val wids = ids.map { Atlas.PARTICLES.wrap(it) } + futures[leafType] = (0 until 16).map { idx -> Identifier(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") } + .filter { manager.hasResource(Atlas.PARTICLES.wrap(it)) } + .map { atlasFuture.sprite(it) } + } + }, + cleanup = atlasFuture.runAfter { + futures.forEach { leafType, spriteFutures -> + val sprites = spriteFutures.filter { !it.isCompletedExceptionally }.map { it.get() } + if (sprites.isNotEmpty()) particles[leafType] = FixedSpriteSet(sprites) + } + if (particles["default"] == null) particles["default"] = FixedSpriteSet(listOf(atlasFuture.missing.get()!!)) + } + ) } - @SubscribeEvent - fun handlePostStitch(event: TextureStitchEvent.Post) { - if (!targetAtlas.matches(event)) return - particles.forEach { (_, particleSet) -> particleSet.onPostStitch(event.map) } + fun init() { + BetterFoliage.particleSprites.providers.add(this) } } diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt index 923aede..dbe004d 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt @@ -1,7 +1,6 @@ package mods.betterfoliage.client.texture import mods.betterfoliage.BetterFoliage -import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.HasLogger @@ -26,7 +25,7 @@ class LeafInfo( val averageColor: Int = roundLeafTexture.averageColor ) { /** [IconSet] of the textures to use for leaf particles emitted from this block. */ - val particleTextures: IconSet get() = LeafParticleRegistry[leafType] + val particleTextures: SpriteSet get() = LeafParticleRegistry[leafType] } object LeafRegistry : ModelRenderRegistryRoot() @@ -40,18 +39,18 @@ object AsyncLeafDiscovery : ConfigurableModelDiscovery() { fun init() { LeafRegistry.registries.add(this) - AsyncSpriteProviderManager.providers.add(this) + BetterFoliage.blockSprites.providers.add(this) } } fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture { val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default" - val generated = Client.genLeaves.register(sprite, leafType) + val generated = GeneratedLeaf(sprite, leafType).register(BetterFoliage.asyncPack) val roundLeaf = atlas.sprite(generated) log(" leaf texture $sprite") log(" particle $leafType") - return atlas.afterStitch { + return atlas.mapAfter { LeafInfo(roundLeaf.get(), leafType) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/Utils.kt b/src/main/kotlin/mods/betterfoliage/client/texture/Utils.kt index e714990..e53cd9b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/Utils.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/Utils.kt @@ -1,6 +1,13 @@ @file:JvmName("Utils") package mods.betterfoliage.client.texture +import mods.betterfoliage.client.resource.Identifier +import mods.octarinecore.client.resource.Atlas +import mods.octarinecore.client.resource.get +import mods.octarinecore.client.resource.loadImage +import net.minecraft.resources.IResourceManager +import java.io.IOException + fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int { val r = (((rgb1 shr 16) and 255) * weight1 + ((rgb2 shr 16) and 255) * weight2) / (weight1 + weight2) val g = (((rgb1 shr 8) and 255) * weight1 + ((rgb2 shr 8) and 255) * weight2) / (weight1 + weight2) @@ -8,4 +15,6 @@ fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int { val a = (rgb1 shr 24) and 255 val result = ((a shl 24) or (r shl 16) or (g shl 8) or b).toInt() return result -} \ No newline at end of file +} + +fun IResourceManager.loadSprite(id: Identifier) = this.get(id)?.loadImage() ?: throw IOException("Cannot load resource $id") \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt b/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt index 01c61e2..9f920ef 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt @@ -2,58 +2,86 @@ package mods.octarinecore.client.resource import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.resource.Sprite -import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.model.ModelBakery +import mods.octarinecore.common.map import net.minecraft.client.renderer.texture.AtlasTexture +import net.minecraft.client.renderer.texture.MissingTextureSprite import net.minecraft.profiler.IProfiler import net.minecraft.resources.IResourceManager import java.util.* import java.util.concurrent.CompletableFuture -import java.util.function.Consumer -import java.util.function.Function + +/** + * Main entry point to atlas manipulation. Called from mixins that wrap [AtlasTexture.stitch] calls. + * + * 1. All registered providers receive an [AsyncSpriteProvider.setup] call. Providers can set up their + * processing chain at this point, but should not do anything yet except configuration and housekeeping. + * 2. The [CompletableFuture] of the stitch source finishes, starting the "discovery" phase. Providers + * may register sprites in the [AtlasFuture]. + * 3. After all providers finish their discovery, the atlas is stitched. + * 4. The [AtlasFuture] finishes, starting the "cleanup" phase. All [AtlasFuture.runAfter] and + * [AtlasFuture.mapAfter] tasks are processed. + * 5. After all providers finish their cleanup, we return to the original code path. + */ +class AsnycSpriteProviderManager(val profilerSection: String) { + + val providers = mutableListOf>() + + /** + * Needed in order to keep the actual [AtlasTexture.stitch] call in the original method, in case + * other modders want to modify it too. + */ + class StitchWrapper(val idList: Iterable, val onComplete: (AtlasTexture.SheetData)->Unit) { + fun complete(sheet: AtlasTexture.SheetData) = onComplete(sheet) + } + + @Suppress("UNCHECKED_CAST") + fun prepare(sourceObj: Any, atlas: AtlasTexture, manager: IResourceManager, idList: Iterable, profiler: IProfiler): StitchWrapper { + profiler.startSection(profilerSection) + + val source = CompletableFuture() + val atlasFuture = AtlasFuture(idList) + + val phases = providers.map { it.setup(manager, source, atlasFuture) } + source.complete(sourceObj as SOURCE) + phases.forEach { it.discovery.get() } + + return StitchWrapper(atlasFuture.idSet) { sheet -> + atlasFuture.complete(sheet) + phases.forEach { it.cleanup.get() } + profiler.endSection() + } + } +} + +/** + * Provides a way for [AsyncSpriteProvider]s to register sprites to receive [CompletableFuture]s. + * Tracks sprite ids that need to be stitched. + */ +class AtlasFuture(initial: Iterable) { + val idSet = Collections.synchronizedSet(mutableSetOf().apply { addAll(initial) }) + protected val sheet = CompletableFuture() + protected val finished = CompletableFuture() + + fun complete(sheetData: AtlasTexture.SheetData) { + sheet.complete(sheetData) + finished.complete(null) + } + + fun sprite(id: String) = sprite(Identifier(id)) + fun sprite(id: Identifier): CompletableFuture { + idSet.add(id) + return sheet.map { sheetData -> sheetData.sprites.find { it.name == id } ?: throw IllegalStateException("Atlas does not contain $id") } + } + val missing = sheet.map { sheetData -> sheetData.sprites.find { it.name == MissingTextureSprite.getLocation() } } + fun mapAfter(supplier: ()->T): CompletableFuture = finished.map{ supplier() } + fun runAfter(action: ()->Unit): CompletableFuture = finished.thenRun(action) +} class StitchPhases( val discovery: CompletableFuture, val cleanup: CompletableFuture ) -interface AsyncSpriteProvider { - fun setup(bakeryFuture: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases +interface AsyncSpriteProvider { + fun setup(manager: IResourceManager, source: CompletableFuture, atlas: AtlasFuture): StitchPhases } - -object AsyncSpriteProviderManager { - - val providers = mutableListOf>() - - fun onStitchBlockAtlas(bakeryObj: Any, atlas: AtlasTexture, manager: IResourceManager, idList: Iterable, profiler: IProfiler): AtlasTexture.SheetData { - profiler.startSection("additional-sprites") - - val bakery = CompletableFuture() - val atlasFuture = AtlasFuture(idList) - - val phases = providers.map { it.setup(bakery, atlasFuture) } - bakery.complete(bakeryObj as ModelBakery) - phases.forEach { it.discovery.get() } - val sheetData = atlas.stitch(manager, idList, profiler) - atlasFuture.sheet.complete(sheetData) - phases.forEach { it.cleanup.get() } - - profiler.endSection() - return sheetData - } -} - -class AtlasFuture(initial: Iterable) { - val idSet = Collections.synchronizedSet(mutableSetOf().apply { addAll(initial) }) - val sheet = CompletableFuture() - fun sprite(id: String) = sprite(Identifier(id)) - fun sprite(id: Identifier): CompletableFuture { - idSet.add(id) - return sheet.thenApply { sheetData -> sheetData.sprites.find { it.name == id } ?: throw IllegalStateException("Atlas does not contain $id") }.toCompletableFuture() - } - fun afterStitch(supplier: ()->T): CompletableFuture = sheet.thenApplyAsync(Function { supplier() }, Minecraft.getInstance()) -} - -fun completedVoid() = CompletableFuture.completedFuture(null) -fun CompletableFuture.thenRunAsync(run: (T)->Unit) = thenAcceptAsync(Consumer(run), Minecraft.getInstance()).toCompletableFuture()!! -fun Collection>.allComplete() = CompletableFuture.allOf(*toTypedArray()) \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/resource/CenteringTextureGenerator.kt b/src/main/kotlin/mods/octarinecore/client/resource/CenteringTextureGenerator.kt index 3347d1d..ddd9ba2 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/CenteringTextureGenerator.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/CenteringTextureGenerator.kt @@ -1,23 +1,17 @@ package mods.octarinecore.client.resource -import net.minecraft.util.ResourceLocation -import net.minecraftforge.resource.VanillaResourceType +import mods.betterfoliage.client.resource.Identifier +import mods.betterfoliage.client.texture.loadSprite +import net.minecraft.resources.IResourceManager import java.awt.image.BufferedImage -import java.io.InputStream -import java.lang.Math.* +import java.lang.Math.max -class CenteringTextureGenerator( - domain: String, - val aspectWidth: Int, - val aspectHeight: Int -) : GeneratorBase(domain, VanillaResourceType.TEXTURES) { +data class CenteredSprite(val sprite: Identifier, val atlas: Atlas = Atlas.BLOCKS, val aspectHeight: Int = 1, val aspectWidth: Int = 1) { - override val locationMapper = Atlas.BLOCKS::unwrap + fun register(pack: GeneratedBlockTexturePack) = pack.register(this, this::draw) - override fun exists(key: ResourceLocation) = resourceManager.hasResource(Atlas.BLOCKS.wrap(key)) - - override fun get(key: ResourceLocation): InputStream? { - val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key)]?.loadImage() ?: return null + fun draw(resourceManager: IResourceManager): ByteArray { + val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite)) val frameWidth = baseTexture.width val frameHeight = baseTexture.width * aspectHeight / aspectWidth @@ -28,7 +22,7 @@ class CenteringTextureGenerator( val graphics = resultTexture.createGraphics() // iterate all frames - for (frame in 0 .. frames - 1) { + for (frame in 0 until frames) { val baseFrame = baseTexture.getSubimage(0, size * frame, frameWidth, frameHeight) val resultFrame = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR) @@ -38,6 +32,6 @@ class CenteringTextureGenerator( graphics.drawImage(resultFrame, 0, size * frame, null) } - return resultTexture.asStream + return resultTexture.bytes } } \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt b/src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt index 74454ba..6e7f613 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt @@ -5,17 +5,18 @@ import mods.betterfoliage.client.resource.ModelIdentifier import mods.octarinecore.HasLogger import mods.octarinecore.client.render.BlockCtx import mods.octarinecore.common.Int3 -import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.IBlockMatcher import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.plus import mods.octarinecore.findFirst +import mods.octarinecore.common.sinkAsync import net.minecraft.block.BlockState import net.minecraft.client.renderer.BlockModelShapes import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.IUnbakedModel import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.VariantList +import net.minecraft.resources.IResourceManager import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader @@ -33,6 +34,9 @@ abstract class ModelRenderRegistryRoot : ModelRenderRegistry { override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = registries.findFirst { it[state, world, pos] } } +/** + * Information about a single BlockState and all the IUnbakedModel it could render as. + */ class ModelDiscoveryContext( bakery: ModelBakery, val state: BlockState, @@ -48,7 +52,7 @@ class ModelDiscoveryContext( } } -abstract class ModelDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry { +abstract class ModelDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry { var modelData: Map = emptyMap() protected set @@ -57,22 +61,22 @@ abstract class ModelDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRe abstract fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture? - override fun setup(bakeryFuture: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { + override fun setup(manager: IResourceManager, bakeryF: CompletableFuture, atlas: AtlasFuture): StitchPhases { val modelDataTemp = mutableMapOf>() return StitchPhases( - discovery = bakeryFuture.thenRunAsync { bakery -> + discovery = bakeryF.sinkAsync { bakery -> var errors = 0 bakery.iterateModels { ctx -> try { - processModel(ctx, atlasFuture)?.let { modelDataTemp[ctx.state] = it } + processModel(ctx, atlas)?.let { modelDataTemp[ctx.state] = it } } catch (e: Exception) { errors++ } } log("${modelDataTemp.size} BlockStates discovered, $errors errors") }, - cleanup = atlasFuture.sheet.thenRunAsync { sheetData -> + cleanup = atlas.runAfter { modelData = modelDataTemp.filterValues { !it.isCompletedExceptionally }.mapValues { it.value.get() } val errors = modelDataTemp.values.filter { it.isCompletedExceptionally }.size log("${modelData.size} BlockStates loaded, $errors errors") @@ -125,8 +129,4 @@ abstract class ConfigurableModelDiscovery : ModelDiscovery() { return null } - override fun setup(bakeryFuture: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { - (matchClasses as? ConfigurableBlockMatcher)?.readDefaults() - return super.setup(bakeryFuture, atlasFuture) - } } diff --git a/src/main/kotlin/mods/octarinecore/client/resource/ResourceGeneration.kt b/src/main/kotlin/mods/octarinecore/client/resource/ResourceGeneration.kt index feaba01..43f8c4b 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/ResourceGeneration.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/ResourceGeneration.kt @@ -1,16 +1,21 @@ package mods.octarinecore.client.resource -import net.minecraft.client.Minecraft +import mods.betterfoliage.client.resource.Identifier +import mods.octarinecore.HasLogger +import mods.octarinecore.common.completedVoid +import mods.octarinecore.common.map +import net.minecraft.client.renderer.model.ModelBakery +import net.minecraft.client.resources.ClientResourcePackInfo import net.minecraft.resources.* import net.minecraft.resources.ResourcePackType.CLIENT_RESOURCES import net.minecraft.resources.data.IMetadataSectionSerializer -import net.minecraft.resources.data.PackMetadataSection -import net.minecraft.util.ResourceLocation import net.minecraft.util.text.StringTextComponent -import net.minecraftforge.resource.IResourceType -import net.minecraftforge.resource.ISelectiveResourceReloadListener -import java.io.InputStream +import org.apache.logging.log4j.Logger +import java.io.IOException +import java.lang.IllegalStateException import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutionException import java.util.function.Predicate import java.util.function.Supplier @@ -20,149 +25,61 @@ import java.util.function.Supplier * @param[name] Name of the resource pack * @param[generators] List of resource generators */ -class GeneratorPack(val packName: String, val packDescription: String, val packImage: String) : IResourcePack { +class GeneratedBlockTexturePack(val nameSpace: String, val packName: String, override val logger: Logger) : HasLogger, IResourcePack, AsyncSpriteProvider { - val generators = mutableListOf>() - - val packFinder = Finder(this) override fun getName() = packName - override fun getResourceNamespaces(type: ResourcePackType) = if (type == CLIENT_RESOURCES) generators.map { it.namespace }.toSet() else emptySet() - - override fun getMetadata(deserializer: IMetadataSectionSerializer): T? { - if (deserializer.sectionName != "pack") return null - return PackMetadataSection(StringTextComponent(packDescription), 4) as? T - } - - override fun resourceExists(type: ResourcePackType, location: ResourceLocation?) = - location != null && - type == CLIENT_RESOURCES && - generators.find { it.namespace == location.namespace && it.resourceExists(location) } != null - - override fun getResourceStream(type: ResourcePackType, location: ResourceLocation) = - if (location != null && type == CLIENT_RESOURCES) - generators.firstOrNull { it.namespace == location.namespace && it.resourceExists(location) }?.getInputStream(location) - else - null - - override fun getAllResourceLocations(type: ResourcePackType, pathIn: String, maxDepth: Int, filter: Predicate) = emptyList() - override fun getRootResourceStream(fileName: String) = fileName.let { if (it == "pack.png") packImage else it }.let { this::class.java.classLoader.getResourceAsStream(it) } + override fun getResourceNamespaces(type: ResourcePackType) = setOf(nameSpace) + override fun getMetadata(deserializer: IMetadataSectionSerializer) = null + override fun getRootResourceStream(id: String) = null + override fun getAllResourceLocations(type: ResourcePackType, path: String, maxDepth: Int, filter: Predicate) = emptyList() override fun close() {} - class Finder(val pack: GeneratorPack) : IPackFinder { - override fun addPackInfosToMap(nameToPackMap: MutableMap, packInfoFactory: ResourcePackInfo.IFactory) { - val packInfo = ResourcePackInfo.createResourcePack( - pack.packName, - true, - Supplier { pack } as Supplier, - packInfoFactory, - ResourcePackInfo.Priority.BOTTOM - ) - nameToPackMap[pack.packName] = packInfo!! - } - } -} + protected var manager: CompletableFuture? = null + val identifiers = Collections.synchronizedMap(mutableMapOf()) + val resources = Collections.synchronizedMap(mutableMapOf>()) -/** - * Abstract base class for resource generators - * - * @param[namespace] Resource namespace of generator - * @param[generatedType] IResourceType of generated resources - */ -abstract class GeneratorBase(val namespace: String, val generatedType: IResourceType) : ISelectiveResourceReloadListener { - val keyToId = mutableMapOf() - val idToKey = mutableMapOf() - open val locationMapper: (ResourceLocation)->ResourceLocation = { it } + fun register(key: Any, func: (IResourceManager)->ByteArray): Identifier { + if (manager == null) throw IllegalStateException("Cannot register resources unless block textures are being reloaded") + identifiers[key]?.let { return it } - init { resourceManager.addReloadListener(this) } + val id = Identifier(nameSpace, UUID.randomUUID().toString()) + val resource = manager!!.map { func(it) } - abstract fun get(key: T): InputStream? - abstract fun exists(key: T): Boolean - - fun registerResource(key: T): ResourceLocation { - keyToId[key]?.let { return ResourceLocation(namespace, it) } - val id = UUID.randomUUID().toString() - keyToId[key] = id - idToKey[id] = key - return ResourceLocation(namespace, id) + identifiers[key] = id + resources[Atlas.BLOCKS.wrap(id)] = resource + log("generated resource $key -> $id") + return id } - fun resourceExists(location: ResourceLocation?): Boolean { - val key = location?.let { locationMapper(it) }?.path?.let { idToKey[it] } ?: return false - return exists(key) - } + override fun getResourceStream(type: ResourcePackType, id: Identifier) = + if (type != CLIENT_RESOURCES) null else + try { resources[id]!!.get().inputStream() } + catch (e: ExecutionException) { (e.cause as? IOException)?.let { throw it } } // rethrow wrapped IOException if present - fun getInputStream(location: ResourceLocation?): InputStream? { - val key = location?.let { locationMapper(it) }?.path?.let { idToKey[it] } ?: return null - return get(key) - } + override fun resourceExists(type: ResourcePackType, id: Identifier) = + type == CLIENT_RESOURCES && resources.containsKey(id) - open fun onReload(resourceManager: IResourceManager) { - keyToId.clear() - idToKey.clear() - } - - override fun onResourceManagerReload(resourceManager: IResourceManager, resourcePredicate: Predicate) { - if (resourcePredicate.test(generatedType)) onReload(resourceManager) - } - - -} - -/** - * Collection of named [String]-valued key-value pairs, with an extra unnamed (keyless) value. - * Meant to be encoded as a pipe-delimited list, and used as a [ResourceLocation] path - * to parametrized generated resources. - * - * @param[params] key-value pairs - * @param[value] keyless extra value - */ -/* -class ParameterList(val params: Map, val value: String?) { - override fun toString() = - params.entries - .sortedBy { it.key } - .fold("") { result, entry -> result + "|${entry.key}=${entry.value}"} + - (value?.let { "|$it" } ?: "") - - /** Return the value of the given parameter. */ - operator fun get(key: String) = params[key] - - /** Check if the given parameter exists in this list. */ - operator fun contains(key: String) = key in params - - /** Return a new [ParameterList] with the given key-value pair appended to it. */ - operator fun plus(pair: Pair) = ParameterList(params + pair, this.value) - - companion object { - /** - * Recreate the parameter list from the encoded string, i.e. the opposite of [toString]. - * - * Everything before the first pipe character is dropped, so the decoding works even if - * something is prepended to the list (like _textures/blocks/_) - */ - fun fromString(input: String): ParameterList { - val params = hashMapOf() - var value: String? = null - val slices = input.dropWhile { it != '|'}.split('|') - slices.forEach { - if (it.contains('=')) { - val keyValue = it.split('=') - if (keyValue.size == 2) params.put(keyValue[0], keyValue[1]) - } else value = it + override fun setup(manager: IResourceManager, bakeryF: CompletableFuture, atlas: AtlasFuture): StitchPhases { + this.manager = CompletableFuture.completedFuture(manager) + return StitchPhases( + completedVoid(), + atlas.runAfter { + this.manager = null + identifiers.clear() + resources.clear() } - return ParameterList(params, value) - } - + ) } -} -abstract class ParameterBasedGenerator(domain: String) : GeneratorBase(domain) { - abstract fun resourceExists(params: ParameterList): Boolean - abstract fun getInputStream(params: ParameterList): InputStream? - - override fun resourceExists(location: ResourceLocation?) = - resourceExists(ParameterList.fromString(location?.path ?: "")) - override fun getInputStream(location: ResourceLocation?) = - getInputStream(ParameterList.fromString(location?.path ?: "")) -} - */ + val finder = object : IPackFinder { + val packInfo = ClientResourcePackInfo( + packName, true, Supplier { this@GeneratedBlockTexturePack }, + StringTextComponent(packName), + StringTextComponent("Generated block textures resource pack"), + PackCompatibility.COMPATIBLE, ResourcePackInfo.Priority.TOP, true, null, true + ) + override fun addPackInfosToMap(nameToPackMap: MutableMap, packInfoFactory: ResourcePackInfo.IFactory) { + (nameToPackMap as MutableMap).put(packName, packInfo) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt b/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt index ddcd1b5..5ecb390 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt @@ -1,13 +1,16 @@ package mods.octarinecore.client.resource +import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.resource.Sprite import mods.octarinecore.client.render.Model import mods.octarinecore.common.Double3 import mods.octarinecore.common.Int3 +import mods.octarinecore.common.completedVoid +import mods.octarinecore.common.sink import mods.octarinecore.stripEnd import mods.octarinecore.stripStart -import net.minecraft.client.renderer.texture.AtlasTexture +import net.minecraft.resources.IResourceManager import net.minecraft.util.math.BlockPos import net.minecraft.util.math.MathHelper import net.minecraft.world.IWorld @@ -18,6 +21,9 @@ import net.minecraftforge.eventbus.api.IEventBus import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.config.ModConfig import java.util.* +import java.util.concurrent.CompletableFuture +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty enum class Atlas(val basePath: String) { BLOCKS("textures"), @@ -31,10 +37,6 @@ enum class Atlas(val basePath: String) { // ============================ // Resource types // ============================ -interface IStitchListener { - fun onPreStitch(event: TextureStitchEvent.Pre) - fun onPostStitch(atlas: AtlasTexture) -} interface IConfigChangeListener { fun onConfigChange() } interface IWorldLoadListener { fun onWorldLoad(world: IWorld) } @@ -52,9 +54,6 @@ open class ResourceHandler( ) { val resources = mutableListOf() - open fun afterPreStitch() {} - open fun afterPostStitch() {} - // ============================ // Self-registration // ============================ @@ -63,10 +62,11 @@ open class ResourceHandler( // ============================ // Resource declarations // ============================ - fun iconStatic(location: ()-> Identifier) = IconHolder(location).apply { resources.add(this) } - fun iconStatic(location: Identifier) = iconStatic { location } - fun iconStatic(domain: String, path: String) = iconStatic(Identifier(domain, path)) - fun iconSet(targetAtlas: Atlas = Atlas.BLOCKS, location: (Int)-> Identifier) = IconSet(targetAtlas, location).apply { this@ResourceHandler.resources.add(this) } + fun sprite(id: Identifier) = sprite { id } + fun sprite(idFunc: ()->Identifier) = AsyncSpriteDelegate(idFunc).apply { BetterFoliage.getSpriteManager(targetAtlas).providers.add(this) } + fun spriteSet(idFunc: (Int)->Identifier) = AsyncSpriteSet(idFunc).apply { BetterFoliage.getSpriteManager(targetAtlas).providers.add(this) } + fun spriteSetTransformed(check: (Int)->Identifier, register: (Identifier)->Identifier) = + AsyncSpriteSet(check, register).apply { BetterFoliage.getSpriteManager(targetAtlas).providers.add(this) } fun model(init: Model.()->Unit) = ModelHolder(init).apply { resources.add(this) } fun modelSet(num: Int, init: Model.(Int)->Unit) = ModelSet(num, init).apply { resources.add(this) } fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) } @@ -75,20 +75,6 @@ open class ResourceHandler( // ============================ // Event registration // ============================ - @SubscribeEvent - fun onPreStitch(event: TextureStitchEvent.Pre) { - if (!targetAtlas.matches(event)) return - resources.forEach { (it as? IStitchListener)?.onPreStitch(event) } - afterPreStitch() - } - - @SubscribeEvent - fun onPostStitch(event: TextureStitchEvent.Post) { - if (!targetAtlas.matches(event)) return - resources.forEach { (it as? IStitchListener)?.onPostStitch(event.map) } - afterPostStitch() - } - @SubscribeEvent fun handleModConfigChange(event: ModConfig.ModConfigEvent) { resources.forEach { (it as? IConfigChangeListener)?.onConfigChange() } @@ -102,16 +88,49 @@ open class ResourceHandler( // ============================ // Resource container classes // ============================ -class IconHolder(val location: ()-> Identifier) : IStitchListener { - var iconRes: Identifier? = null - var icon: Sprite? = null - override fun onPreStitch(event: TextureStitchEvent.Pre) { - iconRes = location() - event.addSprite(iconRes) +class AsyncSpriteDelegate(val idFunc: ()->Identifier) : ReadOnlyProperty, AsyncSpriteProvider { + protected lateinit var value: Sprite + override fun getValue(thisRef: Any, property: KProperty<*>) = value + + override fun setup(manager: IResourceManager, sourceF: CompletableFuture, atlas: AtlasFuture): StitchPhases { + sourceF.thenRun { + val sprite = atlas.sprite(idFunc()) + atlas.runAfter { + sprite.handle { sprite, error -> value = sprite ?: atlas.missing.get()!! } + } + } + return StitchPhases(completedVoid(), completedVoid()) } - override fun onPostStitch(atlas: AtlasTexture) { - icon = atlas[iconRes!!] +} + +interface SpriteSet { + val num: Int + operator fun get(idx: Int): Sprite +} + +class AsyncSpriteSet(val idFunc: (Int)->Identifier, val transform: (Identifier)->Identifier = { it }) : AsyncSpriteProvider { + var num = 0 + protected set + protected var sprites: List = emptyList() + + override fun setup(manager: IResourceManager, sourceF: CompletableFuture, atlas: AtlasFuture): StitchPhases { + var list: List> = emptyList() + + return StitchPhases( + discovery = sourceF.sink { + list = (0 until 16).map { idFunc(it) } + .filter { manager.hasResource( Atlas.BLOCKS.wrap(it)) } + .map { transform(it) } + .map { atlas.sprite(it) } + }, + cleanup = atlas.runAfter { + sprites = list.filter { !it.isCompletedExceptionally }.map { it.get() } + if (sprites.isEmpty()) sprites = listOf(atlas.missing.get()!!) + num = sprites.size + } + ) } + operator fun get(idx: Int) = sprites[idx % num] } class ModelHolder(val init: Model.()->Unit): IConfigChangeListener { @@ -119,27 +138,6 @@ class ModelHolder(val init: Model.()->Unit): IConfigChangeListener { override fun onConfigChange() { model = Model().apply(init) } } -class IconSet(val targetAtlas: Atlas, val location: (Int)-> Identifier) : IStitchListener { - val resources = arrayOfNulls(16) - val icons = arrayOfNulls(16) - var num = 0 - - override fun onPreStitch(event: TextureStitchEvent.Pre) { - num = 0 - (0..15).forEach { idx -> - icons[idx] = null - val loc = location(idx) - if (resourceManager[targetAtlas.wrap(loc)] != null) resources[num++] = loc.apply { event.addSprite(this) } - } - } - - override fun onPostStitch(atlas: AtlasTexture) { - (0 until num).forEach { idx -> icons[idx] = atlas[resources[idx]!!] } - } - - operator fun get(idx: Int) = if (num == 0) null else icons[idx % num] -} - class ModelSet(val num: Int, val init: Model.(Int)->Unit): IConfigChangeListener { val models = Array(num) { Model() } override fun onConfigChange() { (0 until num).forEach { models[it] = Model().apply{ init(it) } } } diff --git a/src/main/kotlin/mods/octarinecore/client/resource/TextureGenerator.kt b/src/main/kotlin/mods/octarinecore/client/resource/TextureGenerator.kt deleted file mode 100644 index 7ecb1cc..0000000 --- a/src/main/kotlin/mods/octarinecore/client/resource/TextureGenerator.kt +++ /dev/null @@ -1,89 +0,0 @@ -package mods.octarinecore.client.resource - -import mods.octarinecore.client.resource.ResourceType.* -import net.minecraft.resources.IResource -import net.minecraft.util.ResourceLocation -import java.awt.image.BufferedImage -import java.io.InputStream - -/** Type of generated texture resource */ -enum class ResourceType { - COLOR, // regular diffuse map - METADATA, // texture metadata - NORMAL, // ShadersMod normal map - SPECULAR // ShadersMod specular map -} - -/** - * Generator returning textures based on a single other texture. This texture is located with the - * _dom_ and _path_ parameters of a [ParameterList]. - * - * @param[domain] Resource domain of generator - */ -/* -abstract class TextureGenerator(domain: String) : ParameterBasedGenerator(domain) { - - /** - * Obtain a [ResourceLocation] to a generated texture - * - * @param[iconName] the name of the [TextureAtlasSprite] (not the full location) backing the generated texture - * @param[extraParams] additional parameters of the generated texture - */ - fun generatedResource(iconName: String, vararg extraParams: Pair) = ResourceLocation( - namespace, - textureLocation(iconName).let { - ParameterList( - mapOf("dom" to it.namespace, "path" to it.path) + - extraParams.map { Pair(it.first, it.second.toString()) }, - "generate" - ).toString() - } - ) - - /** Get the type and location of the texture resource encoded by the given [ParameterList]. */ - fun targetResource(params: ParameterList): Pair? { - val baseTexture = - if (listOf("dom", "path").all { it in params }) ResourceLocation(params["dom"]!!, params["path"]!!) - else return null - return when(params.value?.toLowerCase()) { - "generate.png" -> COLOR to baseTexture + ".png" - "generate.png.mcmeta" -> METADATA to baseTexture + ".png.mcmeta" - "generate_n.png" -> NORMAL to baseTexture + "_n.png" - "generate_s.png" -> SPECULAR to baseTexture + "_s.png" - else -> null - } - } - - override fun resourceExists(params: ParameterList) = - targetResource(params)?.second?.let { resourceManager[it] != null } ?: false - - override fun getInputStream(params: ParameterList): InputStream? { - val target = targetResource(params) - return when(target?.first) { - null -> null - METADATA -> resourceManager[target!!.second]?.inputStream - else -> generate(params)?.asStream - } - } - - /** - * Generate image data from the parameter list. - */ - abstract fun generate(params: ParameterList): BufferedImage? - - /** - * Get a texture resource when multiple sizes may exist. - * - * @param[maxSize] Maximum size to consider. This value is progressively halved when searching for smaller versions. - * @param[maskPath] Location of the texture of the given size - * - */ - fun getMultisizeTexture(maxSize: Int, maskPath: (Int)->ResourceLocation): IResource? { - var size = maxSize - val sizes = mutableListOf() - while(size > 2) { sizes.add(size); size /= 2 } - return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull() - } -} - - */ diff --git a/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt b/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt index 828289a..ccb310b 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt @@ -59,7 +59,8 @@ operator fun BufferedImage.set(x: Int, y: Int, value: Int) = this.setRGB(x, y, v /** Get an [InputStream] to an image object in PNG format. */ val BufferedImage.asStream: InputStream get() = ByteArrayInputStream(ByteArrayOutputStream().let { ImageIO.write(this, "PNG", it); it.toByteArray() }) - +val BufferedImage.bytes: ByteArray get() = + ByteArrayOutputStream().let { ImageIO.write(this, "PNG", it); it.toByteArray() } /** * Calculate the average color of a texture. * diff --git a/src/main/kotlin/mods/octarinecore/common/Futures.kt b/src/main/kotlin/mods/octarinecore/common/Futures.kt new file mode 100644 index 0000000..6e3afe3 --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/common/Futures.kt @@ -0,0 +1,15 @@ +package mods.octarinecore.common + +import net.minecraft.client.Minecraft +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionStage +import java.util.function.Consumer +import java.util.function.Function + +fun completedVoid() = CompletableFuture.completedFuture(null)!! + +fun CompletionStage.map(func: (T)->U) = thenApply(Function(func)).toCompletableFuture()!! +fun CompletionStage.mapAsync(func: (T)->U) = thenApplyAsync(Function(func), Minecraft.getInstance()).toCompletableFuture()!! + +fun CompletionStage.sink(func: (T)->Unit) = thenAccept(Consumer(func)).toCompletableFuture()!! +fun CompletionStage.sinkAsync(func: (T)->Unit) = thenAcceptAsync(Consumer(func), Minecraft.getInstance()).toCompletableFuture()!! \ No newline at end of file diff --git a/src/main/resources/betterfoliage.common.mixins.json b/src/main/resources/betterfoliage.common.mixins.json index 57a7336..5c179b4 100644 --- a/src/main/resources/betterfoliage.common.mixins.json +++ b/src/main/resources/betterfoliage.common.mixins.json @@ -11,7 +11,8 @@ "BlockStateMixin", "ChunkRenderMixin", "ClientWorldMixin", - "ModelBakeryMixin" + "ModelBakeryMixin", + "ParticleManagerMixin" ], "server": [ ],