From c4ee76802533393413c27691403954b678932ca0 Mon Sep 17 00:00:00 2001 From: octarine-noise Date: Sun, 5 Jan 2020 16:32:45 +0100 Subject: [PATCH] [WIP] async block texture reloading --- .../mods/betterfoliage/MixinConnector.java | 1 - .../betterfoliage/mixin/ChunkRenderMixin.java | 1 - .../mixin/ChunkRenderVanillaMixin.java | 8 - .../betterfoliage/mixin/ModelBakeryMixin.java | 15 +- .../mods/betterfoliage/BetterFoliage.kt | 7 +- .../mods/betterfoliage/client/Client.kt | 9 +- .../kotlin/mods/betterfoliage/client/Hooks.kt | 9 - .../betterfoliage/client/chunk/Overlay.kt | 43 +--- .../betterfoliage/client/config/Config.kt | 10 - .../client/integration/ForestryIntegration.kt | 192 ++++++++---------- .../integration/OptifineCustomColors.kt | 17 +- .../client/integration/RubberIntegration.kt | 102 ++++++---- .../integration/ShadersModIntegration.kt | 11 +- .../client/render/RenderCactus.kt | 33 +-- .../betterfoliage/client/render/RenderLog.kt | 32 ++- .../client/render/column/RenderData.kt | 14 -- .../betterfoliage/client}/resource/Aliases.kt | 2 +- .../client/texture/GrassRegistry.kt | 46 +++-- .../client/texture/LeafRegistry.kt | 42 ++-- .../betterfoliage/loader/BetterFoliageCore.kt | 130 ------------ .../kotlin/mods/betterfoliage/loader/Refs.kt | 58 ------ .../kotlin/mods/octarinecore/CommonRefs.kt | 68 +++++++ src/main/kotlin/mods/octarinecore/Utils.kt | 10 +- .../client/render/CombinedContext.kt | 10 +- .../resource/AsyncSpriteProviderManager.kt | 59 ++++++ .../client/resource/ModelDiscovery.kt | 132 ++++++++++++ .../client/resource/ModelProcessor.kt | 138 ------------- .../client/resource/ResourceHandler.kt | 12 +- .../octarinecore/client/resource/Utils.kt | 15 +- .../mods/octarinecore/metaprog/Reflection.kt | 53 ++--- .../resources/META-INF/accesstransformer.cfg | 12 +- 31 files changed, 587 insertions(+), 704 deletions(-) rename src/{forge => main}/kotlin/mods/betterfoliage/BetterFoliage.kt (83%) rename src/{forge/kotlin/mods/octarinecore => main/kotlin/mods/betterfoliage/client}/resource/Aliases.kt (87%) delete mode 100644 src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt delete mode 100644 src/main/kotlin/mods/betterfoliage/loader/Refs.kt create mode 100644 src/main/kotlin/mods/octarinecore/CommonRefs.kt create mode 100644 src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt create mode 100644 src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt delete mode 100644 src/main/kotlin/mods/octarinecore/client/resource/ModelProcessor.kt diff --git a/src/main/java/mods/betterfoliage/MixinConnector.java b/src/main/java/mods/betterfoliage/MixinConnector.java index 24743f0..ee57666 100644 --- a/src/main/java/mods/betterfoliage/MixinConnector.java +++ b/src/main/java/mods/betterfoliage/MixinConnector.java @@ -1,6 +1,5 @@ package mods.betterfoliage; -import net.minecraftforge.fml.ModLoader; import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.connect.IMixinConnector; diff --git a/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java b/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java index 69c1079..40dc82c 100644 --- a/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ChunkRenderMixin.java @@ -5,7 +5,6 @@ import net.minecraft.block.BlockState; import net.minecraft.client.renderer.BlockRendererDispatcher; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.chunk.ChunkRender; -import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IEnviromentBlockReader; import net.minecraftforge.client.MinecraftForgeClient; diff --git a/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java b/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java index e4d3f16..1340994 100644 --- a/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ChunkRenderVanillaMixin.java @@ -2,20 +2,12 @@ package mods.betterfoliage.mixin; import mods.betterfoliage.client.Hooks; import net.minecraft.block.BlockState; -import net.minecraft.client.renderer.BlockRendererDispatcher; -import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.chunk.ChunkRender; import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IEnviromentBlockReader; -import net.minecraftforge.client.MinecraftForgeClient; -import net.minecraftforge.client.model.data.IModelData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import java.util.Random; - @Mixin(ChunkRender.class) public class ChunkRenderVanillaMixin { diff --git a/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java b/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java index 0de8725..662d9c2 100644 --- a/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java +++ b/src/main/java/mods/betterfoliage/mixin/ModelBakeryMixin.java @@ -1,21 +1,26 @@ package mods.betterfoliage.mixin; import mods.betterfoliage.client.Hooks; +import mods.octarinecore.client.resource.AsyncSpriteProviderManager; import net.minecraft.client.renderer.model.ModelBakery; +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.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ModelBakery.class) -public class ModelBakeryMixin { +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 endStartSection = "Lnet/minecraft/profiler/IProfiler;endStartSection(Ljava/lang/String;)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;"; - @Inject(method = processLoading, at = @At(value = "INVOKE_STRING", target = endStartSection, args = "ldc=stitching")) - void preStitchTextures(IProfiler profiler, CallbackInfo ci) { - Hooks.onLoadModelDefinitions(this); + @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); } } diff --git a/src/forge/kotlin/mods/betterfoliage/BetterFoliage.kt b/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt similarity index 83% rename from src/forge/kotlin/mods/betterfoliage/BetterFoliage.kt rename to src/main/kotlin/mods/betterfoliage/BetterFoliage.kt index d36b378..ba0a08a 100644 --- a/src/forge/kotlin/mods/betterfoliage/BetterFoliage.kt +++ b/src/main/kotlin/mods/betterfoliage/BetterFoliage.kt @@ -2,17 +2,12 @@ package mods.betterfoliage import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config -import mods.betterfoliage.client.isAfterPostInit import mods.octarinecore.client.resource.GeneratorPack import net.alexwells.kottle.FMLKotlinModLoadingContext import net.minecraft.client.Minecraft -import net.minecraftforge.eventbus.api.IEventBus -import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.ModLoadingContext import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.config.ModConfig -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent import org.apache.logging.log4j.Level.DEBUG import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.simple.SimpleLogger @@ -23,7 +18,7 @@ import java.util.* @Mod(BetterFoliage.MOD_ID) object BetterFoliage { - const val MOD_ID = "betterfoliage" + const val MOD_ID = "" const val MOD_NAME = "Better Foliage" val modBus = FMLKotlinModLoadingContext.get().modEventBus diff --git a/src/main/kotlin/mods/betterfoliage/client/Client.kt b/src/main/kotlin/mods/betterfoliage/client/Client.kt index 73b1248..f91d714 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Client.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Client.kt @@ -12,6 +12,7 @@ import mods.betterfoliage.client.texture.* 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 @@ -59,7 +60,6 @@ object Client { // init other singletons val singletons = listOf( BlockConfig, - StandardCactusRegistry, LeafParticleRegistry, ChunkOverlayManager, LeafWindTracker, @@ -76,9 +76,10 @@ object Client { ) // add basic block support instances as last - GrassRegistry.addRegistry(StandardGrassRegistry) - LeafRegistry.addRegistry(StandardLeafRegistry) - LogRegistry.addRegistry(StandardLogRegistry) + AsyncLeafDiscovery.init() + AsyncGrassDiscovery.init() + AsyncLogDiscovery.init() + AsyncCactusDiscovery.init() configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance() configListeners.forEach { it.onConfigChange() } diff --git a/src/main/kotlin/mods/betterfoliage/client/Hooks.kt b/src/main/kotlin/mods/betterfoliage/client/Hooks.kt index 50f5fb5..46e36b0 100644 --- a/src/main/kotlin/mods/betterfoliage/client/Hooks.kt +++ b/src/main/kotlin/mods/betterfoliage/client/Hooks.kt @@ -6,12 +6,10 @@ import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.render.* -import mods.betterfoliage.loader.Refs 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.client.resource.LoadModelDataEvent import mods.octarinecore.common.plus import mods.octarinecore.metaprog.allAvailable import net.minecraft.block.Block @@ -35,9 +33,6 @@ import net.minecraft.world.World import net.minecraftforge.client.model.data.IModelData import java.util.* -var isAfterPostInit = false -val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer) - fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float { if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat(); return original @@ -69,10 +64,6 @@ fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: Bloc } } -fun onLoadModelDefinitions(bakery: Any) { - BetterFoliage.modBus.post(LoadModelDataEvent(bakery as ModelBakery)) -} - fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape { if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty() return state.func_215702_a(reader, pos, dir) diff --git a/src/main/kotlin/mods/betterfoliage/client/chunk/Overlay.kt b/src/main/kotlin/mods/betterfoliage/client/chunk/Overlay.kt index 091b01d..8000a5b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/chunk/Overlay.kt +++ b/src/main/kotlin/mods/betterfoliage/client/chunk/Overlay.kt @@ -1,10 +1,9 @@ package mods.betterfoliage.client.chunk -import mods.betterfoliage.BetterFoliage -import mods.betterfoliage.client.Client -import mods.betterfoliage.loader.Refs -import mods.octarinecore.client.render.BasicBlockCtx +import mods.octarinecore.ChunkCacheOF import mods.octarinecore.client.render.BlockCtx +import mods.octarinecore.metaprog.get +import mods.octarinecore.metaprog.isInstance import net.minecraft.client.renderer.chunk.ChunkRenderCache import net.minecraft.client.world.ClientWorld import net.minecraft.util.math.BlockPos @@ -16,13 +15,19 @@ import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.eventbus.api.SubscribeEvent -import org.apache.logging.log4j.Level import java.util.* +import kotlin.collections.List +import kotlin.collections.MutableMap +import kotlin.collections.associateWith +import kotlin.collections.forEach +import kotlin.collections.mutableListOf +import kotlin.collections.mutableMapOf +import kotlin.collections.set val IEnviromentBlockReader.dimType: DimensionType get() = when { this is IWorldReader -> dimension.type this is ChunkRenderCache -> world.dimension.type - Refs.OptifineChunkCache.isInstance(this) -> (Refs.CCOFChunkCache.get(this) as ChunkRenderCache).world.dimension.type + this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache].world.dimension.type else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!") } @@ -42,7 +47,6 @@ object ChunkOverlayManager { var tempCounter = 0 init { - Client.log(Level.INFO, "Initializing client overlay manager") MinecraftForge.EVENT_BUS.register(this) } @@ -84,13 +88,11 @@ object ChunkOverlayManager { @SubscribeEvent fun handleLoadWorld(event: WorldEvent.Load) = (event.world as? ClientWorld)?.let { world -> - BetterFoliage.log.debug("Unloaded world: id=${world.dimType.id} name=${world.dimType.registryName}") chunkData[world.dimType] = mutableMapOf() } @SubscribeEvent fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world -> - BetterFoliage.log.debug("Unloaded world: id=${world.dimType.id} name=${world.dimType.registryName}") chunkData.remove(world.dimType) } @@ -121,26 +123,3 @@ class ChunkOverlayData(layers: List>) { val validYRange = 0 until 256 } } - -/** - * IWorldEventListener helper subclass - * No-op for everything except notifyBlockUpdate() - */ -/* -interface IBlockUpdateListener : IWorldEventListener { - override fun playSoundToAllNearExcept(player: EntityPlayer?, soundIn: SoundEvent, category: SoundCategory, x: Double, y: Double, z: Double, volume: Float, pitch: Float) {} - override fun onEntityAdded(entityIn: Entity) {} - override fun broadcastSound(soundID: Int, pos: BlockPos, data: Int) {} - override fun playEvent(player: EntityPlayer?, type: Int, blockPosIn: BlockPos, data: Int) {} - override fun onEntityRemoved(entityIn: Entity) {} - override fun notifyLightSet(pos: BlockPos) {} - override fun spawnParticle(particleID: Int, ignoreRange: Boolean, xCoord: Double, yCoord: Double, zCoord: Double, xSpeed: Double, ySpeed: Double, zSpeed: Double, vararg parameters: Int) {} - override fun spawnParticle(id: Int, ignoreRange: Boolean, minimiseParticleLevel: Boolean, x: Double, y: Double, z: Double, xSpeed: Double, ySpeed: Double, zSpeed: Double, vararg parameters: Int) {} - override fun playRecord(soundIn: SoundEvent, pos: BlockPos) {} - override fun sendBlockBreakProgress(breakerId: Int, pos: BlockPos, progress: Int) {} - override fun markBlockRangeForRenderUpdate(x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) {} - override fun addParticle(p0: IParticleData, p1: Boolean, p2: Double, p3: Double, p4: Double, p5: Double, p6: Double, p7: Double) {} - override fun addParticle(p0: IParticleData, p1: Boolean, p2: Boolean, p3: Double, p4: Double, p5: Double, p6: Double, p7: Double, p8: Double) {} -} - - */ \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt index a9d3e43..21361f7 100644 --- a/src/main/kotlin/mods/betterfoliage/client/config/Config.kt +++ b/src/main/kotlin/mods/betterfoliage/client/config/Config.kt @@ -2,10 +2,8 @@ package mods.betterfoliage.client.config import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.integration.ShadersModIntegration -import mods.octarinecore.client.resource.LoadModelDataEvent import mods.octarinecore.common.config.* import net.minecraft.util.ResourceLocation -import net.minecraftforge.eventbus.api.SubscribeEvent private fun featureEnable() = boolean(true).lang("enabled") @@ -165,12 +163,4 @@ object BlockConfig { 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) } - - @SubscribeEvent - fun handleLoadModelData(event: LoadModelDataEvent) { - 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 49a69b5..b98331b 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/ForestryIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/ForestryIntegration.kt @@ -1,153 +1,133 @@ package mods.betterfoliage.client.integration import mods.betterfoliage.BetterFoliage -import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig -import mods.betterfoliage.client.render.LogRegistry -import mods.betterfoliage.client.render.StandardLogRegistry +import mods.betterfoliage.client.render.AsyncLogDiscovery import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo +import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.texture.LeafInfo -import mods.betterfoliage.client.texture.LeafRegistry -import mods.betterfoliage.client.texture.StandardLeafKey -import mods.betterfoliage.loader.Refs -import mods.octarinecore.client.resource.ModelRenderKey -import mods.octarinecore.client.resource.ModelRenderRegistry -import mods.octarinecore.client.resource.ModelRenderRegistryBase -import mods.octarinecore.getTileEntitySafe -import mods.octarinecore.metaprog.ClassRef -import mods.octarinecore.metaprog.FieldRef -import mods.octarinecore.metaprog.MethodRef -import mods.octarinecore.metaprog.allAvailable +import mods.betterfoliage.client.texture.defaultRegisterLeaf +import mods.octarinecore.HasLogger +import mods.octarinecore.Map +import mods.octarinecore.ResourceLocation +import mods.octarinecore.String +import mods.octarinecore.client.resource.* +import mods.octarinecore.metaprog.* +import mods.octarinecore.metaprog.ClassRef.Companion.boolean import net.minecraft.block.BlockState import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.model.IUnbakedModel -import net.minecraft.client.renderer.model.ModelResourceLocation -import net.minecraft.util.ResourceLocation +import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.util.math.BlockPos import net.minecraft.world.IBlockReader -import net.minecraft.world.IWorldReader -import net.minecraftforge.client.event.TextureStitchEvent -import net.minecraftforge.client.model.IModel -import net.minecraftforge.eventbus.api.EventPriority -import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.fml.ModList import org.apache.logging.log4j.Level -import kotlin.collections.Map +import java.util.concurrent.CompletableFuture import kotlin.collections.component1 import kotlin.collections.component2 -import kotlin.collections.emptyMap -import kotlin.collections.find -import kotlin.collections.forEach -import kotlin.collections.get -import kotlin.collections.listOf -import kotlin.collections.mapValues -import kotlin.collections.mutableMapOf -import kotlin.collections.set + +val TextureLeaves = ClassRef("forestry.arboriculture.models.TextureLeaves") +val TextureLeaves_leafTextures = FieldRef(TextureLeaves, "leafTextures", Map) +val TextureLeaves_plain = FieldRef(TextureLeaves, "plain", ResourceLocation) +val TextureLeaves_fancy = FieldRef(TextureLeaves, "fancy", ResourceLocation) +val TextureLeaves_pollinatedPlain = FieldRef(TextureLeaves, "pollinatedPlain", ResourceLocation) +val TextureLeaves_pollinatedFancy = FieldRef(TextureLeaves, "pollinatedFancy", ResourceLocation) + + +val TileLeaves = ClassRef("forestry.arboriculture.tiles.TileLeaves") +val TileLeaves_getLeaveSprite = MethodRef(TileLeaves, "getLeaveSprite", ResourceLocation, boolean) +val PropertyWoodType = ClassRef("forestry.arboriculture.blocks.PropertyWoodType") +val IWoodType = ClassRef("forestry.api.arboriculture.IWoodType") +val IWoodType_barkTex = MethodRef(IWoodType, "getBarkTexture", String) +val IWoodType_heartTex = MethodRef(IWoodType, "getHeartTexture", String) + +val PropertyTreeType = ClassRef("forestry.arboriculture.blocks.PropertyTreeType") +val IAlleleTreeSpecies = ClassRef("forestry.api.arboriculture.IAlleleTreeSpecies") +val ILeafSpriteProvider = ClassRef("forestry.api.arboriculture.ILeafSpriteProvider") +val TreeDefinition = ClassRef("forestry.arboriculture.genetics.TreeDefinition") + +val IAlleleTreeSpecies_getLeafSpriteProvider = MethodRef(IAlleleTreeSpecies, "getLeafSpriteProvider", ILeafSpriteProvider) +val TreeDefinition_species = FieldRef(TreeDefinition, "species", IAlleleTreeSpecies) +val ILeafSpriteProvider_getSprite = MethodRef(ILeafSpriteProvider, "getSprite", ResourceLocation, boolean, boolean) object ForestryIntegration { - - val TextureLeaves = ClassRef("forestry.arboriculture.models.TextureLeaves") - val TeLleafTextures = FieldRef(TextureLeaves, "leafTextures", Refs.Map) - val TeLplain = FieldRef(TextureLeaves, "plain", Refs.ResourceLocation) - val TeLfancy = FieldRef(TextureLeaves, "fancy", Refs.ResourceLocation) - val TeLpollplain = FieldRef(TextureLeaves, "pollinatedPlain", Refs.ResourceLocation) - val TeLpollfancy = FieldRef(TextureLeaves, "pollinatedFancy", Refs.ResourceLocation) - val TileLeaves = ClassRef("forestry.arboriculture.tiles.TileLeaves") - val TiLgetLeaveSprite = MethodRef(TileLeaves, "getLeaveSprite", Refs.ResourceLocation, ClassRef.boolean) - - val PropertyWoodType = ClassRef("forestry.arboriculture.blocks.PropertyWoodType") - val IWoodType = ClassRef("forestry.api.arboriculture.IWoodType") - val barkTex = MethodRef(IWoodType, "getBarkTexture", Refs.String) - val heartTex = MethodRef(IWoodType, "getHeartTexture", Refs.String) - - val PropertyTreeType = ClassRef("forestry.arboriculture.blocks.PropertyTreeType") - val TreeDefinition = ClassRef("forestry.arboriculture.genetics.TreeDefinition") - val IAlleleTreeSpecies = ClassRef("forestry.api.arboriculture.IAlleleTreeSpecies") - val ILeafSpriteProvider = ClassRef("forestry.api.arboriculture.ILeafSpriteProvider") - val TdSpecies = FieldRef(TreeDefinition, "species", IAlleleTreeSpecies) - val getLeafSpriteProvider = MethodRef(IAlleleTreeSpecies, "getLeafSpriteProvider", ILeafSpriteProvider) - val getSprite = MethodRef(ILeafSpriteProvider, "getSprite", Refs.ResourceLocation, ClassRef.boolean, ClassRef.boolean) - init { - if (ModList.get().isLoaded("forestry") && allAvailable(TiLgetLeaveSprite, getLeafSpriteProvider, getSprite)) { - Client.log(Level.INFO, "Forestry support initialized") - LeafRegistry.addRegistry(ForestryLeafRegistry) - LogRegistry.addRegistry(ForestryLogRegistry) + if (ModList.get().isLoaded("forestry") && allAvailable(TileLeaves_getLeaveSprite, IAlleleTreeSpecies_getLeafSpriteProvider, ILeafSpriteProvider_getSprite)) { } } } -object ForestryLeafRegistry : ModelRenderRegistry { - val logger = BetterFoliage.logDetail - val textureToKey = mutableMapOf>() - var textureToValue = emptyMap() +object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry { + override val logger = BetterFoliage.logDetail + var idToValue = emptyMap() override fun get(state: BlockState, world: IBlockReader, pos: BlockPos): LeafInfo? { // check variant property (used in decorative leaves) state.values.entries.find { - ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value) + PropertyTreeType.isInstance(it.key) && TreeDefinition.isInstance(it.value) } ?.let { - val species = ForestryIntegration.TdSpecies.get(it.value) - val spriteProvider = ForestryIntegration.getLeafSpriteProvider.invoke(species!!) - val textureLoc = ForestryIntegration.getSprite.invoke(spriteProvider!!, false, Minecraft.isFancyGraphicsEnabled()) - return textureToValue[textureLoc] + val species = it.value[TreeDefinition_species] + val spriteProvider = species[IAlleleTreeSpecies_getLeafSpriteProvider]() + val textureLoc = spriteProvider[ILeafSpriteProvider_getSprite](false, Minecraft.isFancyGraphicsEnabled()) + return idToValue[textureLoc] } // extract leaf texture information from TileEntity val tile = world.getTileEntity(pos) ?: return null - if (!ForestryIntegration.TileLeaves.isInstance(tile)) return null - val textureLoc = ForestryIntegration.TiLgetLeaveSprite.invoke(tile, Minecraft.isFancyGraphicsEnabled()) ?: return null - return textureToValue[textureLoc] + if (!TileLeaves.isInstance(tile)) return null + val textureLoc = tile[TileLeaves_getLeaveSprite](Minecraft.isFancyGraphicsEnabled()) + return idToValue[textureLoc] } - @SubscribeEvent - fun handlePreStitch(event: TextureStitchEvent.Pre) { - textureToValue = emptyMap() + override fun setup(bakeryFuture: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { + val futures = mutableMapOf>() - val allLeaves = ForestryIntegration.TeLleafTextures.getStatic() as Map<*, *> - allLeaves.entries.forEach { - logger.log(Level.DEBUG, "ForestryLeavesSupport: base leaf type ${it.key.toString()}") - listOf( - ForestryIntegration.TeLplain.get(it.value) as ResourceLocation, - ForestryIntegration.TeLfancy.get(it.value) as ResourceLocation, - ForestryIntegration.TeLpollplain.get(it.value) as ResourceLocation, - ForestryIntegration.TeLpollfancy.get(it.value) as ResourceLocation - ).forEach { textureLocation -> - val key = StandardLeafKey(logger, textureLocation.toString()).apply { onPreStitch(event) } - textureToKey[textureLocation] = key + return StitchPhases( + discovery = bakeryFuture.thenRunAsync { + val allLeaves = TextureLeaves_leafTextures.getStatic() + allLeaves.entries.forEach { (type, leaves) -> + log("base leaf type $type") + leaves!! + listOf( + leaves[TextureLeaves_plain], leaves[TextureLeaves_pollinatedPlain], + leaves[TextureLeaves_fancy], leaves[TextureLeaves_pollinatedFancy] + ).forEach { textureLocation -> + futures[textureLocation] = defaultRegisterLeaf(textureLocation, atlasFuture) + } + } + }, + cleanup = atlasFuture.sheet.thenRunAsync { + idToValue = futures.mapValues { it.value.get() } } - } - } - - @SubscribeEvent(priority = EventPriority.LOW) - fun handlePostStitch(event: TextureStitchEvent.Post) { - textureToValue = textureToKey.mapValues { (_, key) -> key.resolveSprites(event.map) } - textureToKey.clear() + ) } } -object ForestryLogRegistry : ModelRenderRegistryBase() { +object ForestryLogDiscovery : ModelDiscovery() { override val logger = BetterFoliage.logDetail - - override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List>): ModelRenderKey? { + override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture? { // respect class list to avoid triggering on fences, stairs, etc. - if (!BlockConfig.logBlocks.matchesClass(state.block)) return null + if (!BlockConfig.logBlocks.matchesClass(ctx.state.block)) return null // find wood type property - val woodType = state.values.entries.find { - ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value) - } ?: return null + val woodType = ctx.state.values.entries.find { + PropertyWoodType.isInstance(it.key) && IWoodType.isInstance(it.value) + } + if (woodType != null) { + logger.log(Level.DEBUG, "ForestryLogRegistry: block state ${ctx.state}") + logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}") - logger.log(Level.DEBUG, "ForestryLogRegistry: block state $state") - logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}") + // get texture names for wood type + val bark = woodType.value[IWoodType_barkTex]() + val heart = woodType.value[IWoodType_heartTex]() + logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]") - // get texture names for wood type - val bark = ForestryIntegration.barkTex.invoke(woodType.value) as String? - val heart = ForestryIntegration.heartTex.invoke(woodType.value) as String? - - logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]") - if (bark != null && heart != null) return SimpleColumnInfo.Key(logger, StandardLogRegistry.getAxis(state), listOf(heart, heart, bark)) + val heartSprite = atlas.sprite(heart) + val barkSprite = atlas.sprite(bark) + return atlas.afterStitch { + SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get())) + } + } return null } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt b/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt index 517c971..7300986 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/OptifineCustomColors.kt @@ -1,10 +1,8 @@ package mods.betterfoliage.client.integration import mods.betterfoliage.client.Client -import mods.betterfoliage.loader.Refs -import mods.octarinecore.ThreadLocalDelegate +import mods.octarinecore.* import mods.octarinecore.client.render.CombinedContext -import mods.octarinecore.common.Int3 import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.reflectField import net.minecraft.block.BlockState @@ -13,7 +11,6 @@ import net.minecraft.client.renderer.model.BakedQuad import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.util.Direction.UP import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockReader import org.apache.logging.log4j.Level /** @@ -22,9 +19,7 @@ import org.apache.logging.log4j.Level @Suppress("UNCHECKED_CAST") object OptifineCustomColors { - val isColorAvailable = allAvailable( - Refs.CustomColors, Refs.getColorMultiplier - ) + val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier) init { Client.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }") @@ -36,21 +31,19 @@ object OptifineCustomColors { fun getBlockColor(ctx: CombinedContext): Int { val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField("ofCustomColors") == true) { renderEnv.reset(ctx.state, ctx.pos) - Refs.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int + CustomColors.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int } else null return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor } } class OptifineRenderEnv { - val wrapped: Any = Refs.RenderEnv.element!!.getDeclaredConstructor( - Refs.BlockState.element, Refs.BlockPos.element - ).let { + val wrapped: Any = RenderEnv.element!!.getDeclaredConstructor(BlockState.element, BlockPos.element).let { it.isAccessible = true it.newInstance(null, null) } fun reset(state: BlockState, pos: BlockPos) { - Refs.RenderEnv_reset.invoke(wrapped, state, pos) + RenderEnv.reset.invoke(wrapped, state, pos) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt b/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt index 90986fe..aa1db31 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/RubberIntegration.kt @@ -8,15 +8,11 @@ import mods.betterfoliage.client.render.column.SimpleColumnInfo import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.Quad import mods.octarinecore.client.render.lighting.QuadIconResolver -import mods.octarinecore.client.render.lighting.LightingCtx import mods.octarinecore.client.resource.* import mods.octarinecore.common.rotate import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.allAvailable -import net.minecraft.block.BlockState import net.minecraft.client.renderer.model.BlockModel -import net.minecraft.client.renderer.model.IUnbakedModel -import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.util.Direction @@ -24,29 +20,31 @@ 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.Level.DEBUG import org.apache.logging.log4j.Logger +import java.util.concurrent.CompletableFuture object IC2RubberIntegration { - val BlockRubWood = ClassRef("ic2.core.block.BlockRubWood") + val BlockRubWood = ClassRef("ic2.core.block.BlockRubWood") init { if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) { Client.log(Level.INFO, "IC2 rubber support initialized") - LogRegistry.addRegistry(IC2LogSupport) + LogRegistry.registries.add(IC2LogDiscovery) + AsyncSpriteProviderManager.providers.add(IC2LogDiscovery) } } } object TechRebornRubberIntegration { - val BlockRubberLog = ClassRef("techreborn.blocks.BlockRubberLog") + val BlockRubberLog = ClassRef("techreborn.blocks.BlockRubberLog") init { if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) { Client.log(Level.INFO, "TechReborn rubber support initialized") - LogRegistry.addRegistry(TechRebornLogSupport) + LogRegistry.registries.add(TechRebornLogDiscovery) + AsyncSpriteProviderManager.providers.add(TechRebornLogDiscovery) } } } @@ -67,27 +65,16 @@ class RubberLogInfo( this.sideTextures[sideIdx] } } - - class Key(override val logger: Logger, val axis: Axis?, val spotDir: Direction, val textures: List): ModelRenderKey { - override fun resolveSprites(atlas: AtlasTexture) = RubberLogInfo( - axis, - spotDir, - atlas[textures[0]] ?: missingSprite, - atlas[textures[1]] ?: missingSprite, - atlas[textures[2]] ?: missingSprite, - textures.drop(3).map { atlas[it] ?: missingSprite } - ) - } } -object IC2LogSupport : ModelRenderRegistryBase() { +object IC2LogDiscovery : ModelDiscovery() { override val logger = BetterFoliage.logDetail - override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List>): ModelRenderKey? { + override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture? { // check for proper block class, existence of ModelBlock, and "state" blockstate property - if (!IC2RubberIntegration.BlockRubWood.isInstance(state.block)) return null - val blockLoc = models.firstOrNull() as Pair ?: return null - val type = state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null + if (!IC2RubberIntegration.BlockRubWood.isInstance(ctx.state.block)) return null + val blockLoc = ctx.models.firstOrNull() as Pair ?: return null + val type = ctx.state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null // logs with no rubber spot if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) { @@ -97,11 +84,15 @@ object IC2LogSupport : ModelRenderRegistryBase() { "plain_z" -> Axis.Z else -> null } - val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) } + val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) } if (textureNames.any { it == "missingno" }) return null - logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}") - logger.log(DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}") - return SimpleColumnInfo.Key(logger, axis, textureNames) + log("IC2LogSupport: block state ${ctx.state.toString()}") + 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 { + SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get())) + } } // logs with rubber spot @@ -114,32 +105,53 @@ object IC2LogSupport : ModelRenderRegistryBase() { } val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) } if (textureNames.any { it == "missingno" }) return null - logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}") - logger.log(DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}") - return if (spotDir != null) RubberLogInfo.Key(logger, Axis.Y, spotDir, textureNames) else SimpleColumnInfo.Key(logger, Axis.Y, textureNames) + log("IC2LogSupport: block state ${ctx.state.toString()}") + log("IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}") + val upSprite = atlas.sprite(textureNames[0]) + val downSprite = atlas.sprite(textureNames[1]) + val sideSprite = atlas.sprite(textureNames[2]) + val spotSprite = atlas.sprite(textureNames[3]) + return if (spotDir != null) atlas.afterStitch { + RubberLogInfo(Axis.Y, spotDir, upSprite.get(), downSprite.get(), spotSprite.get(), listOf(sideSprite.get())) + } else atlas.afterStitch { + SimpleColumnInfo(Axis.Y, upSprite.get(), downSprite.get(), listOf(sideSprite.get())) + } } } -object TechRebornLogSupport : ModelRenderRegistryBase() { +object TechRebornLogDiscovery : ModelDiscovery() { override val logger = BetterFoliage.logDetail - override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List>): ModelRenderKey? { + override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture? { // check for proper block class, existence of ModelBlock - if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(state.block)) return null - val blockLoc = models.firstOrNull() as Pair ?: return null + if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(ctx.state.block)) return null + val blockLoc = ctx.models.map { it as? Pair }.firstOrNull() ?: return null - val hasSap = state.values.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null - val sapSide = state.values.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null + val hasSap = ctx.state.values.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null + val sapSide = ctx.state.values.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null - logger.log(DEBUG, "$logName: block state $state") + log("$logName: block state ${ctx.state}") if (hasSap) { - val textureNames = listOf("end", "end", "sapside", "side").map { blockLoc.first.resolveTextureName(it) } - logger.log(DEBUG, "$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}") - if (textureNames.all { it != "missingno" }) return RubberLogInfo.Key(logger, Axis.Y, sapSide, textureNames) + val textureNames = listOf("end", "side", "sapside").map { blockLoc.first.resolveTextureName(it) } + log("$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}") + if (textureNames.all { it != "missingno" }) { + val endSprite = atlas.sprite(textureNames[0]) + val sideSprite = atlas.sprite(textureNames[1]) + val sapSprite = atlas.sprite(textureNames[2]) + return atlas.afterStitch { + RubberLogInfo(Axis.Y, sapSide, endSprite.get(), endSprite.get(), sapSprite.get(), listOf(sideSprite.get())) + } + } } else { - val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) } - logger.log(DEBUG, "$logName: end=${textureNames[0]}, side=${textureNames[2]}") - if (textureNames.all { it != "missingno" })return SimpleColumnInfo.Key(logger, Axis.Y, textureNames) + val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) } + log("$logName: end=${textureNames[0]}, side=${textureNames[1]}") + if (textureNames.all { it != "missingno" }) { + val endSprite = atlas.sprite(textureNames[0]) + val sideSprite = atlas.sprite(textureNames[1]) + return atlas.afterStitch { + SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get())) + } + } } return null } diff --git a/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt b/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt index d8cf6d0..fa53db4 100644 --- a/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt +++ b/src/main/kotlin/mods/betterfoliage/client/integration/ShadersModIntegration.kt @@ -3,9 +3,10 @@ package mods.betterfoliage.client.integration import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config -import mods.betterfoliage.loader.Refs +import mods.octarinecore.SVertexBuilder import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.metaprog.allAvailable +import mods.octarinecore.metaprog.get import net.minecraft.block.BlockRenderType import net.minecraft.block.BlockRenderType.MODEL import net.minecraft.block.BlockState @@ -20,7 +21,7 @@ import org.apache.logging.log4j.Level.INFO */ object ShadersModIntegration { - @JvmStatic val isAvailable = allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity) + @JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.pushNum, SVertexBuilder.pop) val grassDefaultBlockId = 31L val leavesDefaultBlockId = 18L @@ -42,10 +43,10 @@ object ShadersModIntegration { /** Quads rendered inside this block will use the given block entity data in shader programs. */ inline fun renderAs(blockId: Long, renderType: BlockRenderType, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) { if ((isAvailable && enabled)) { - val vertexBuilder = Refs.sVertexBuilder.get(renderer)!! - Refs.pushEntity_num.invoke(vertexBuilder, blockId) + val vertexBuilder = renderer[mods.octarinecore.BufferBuilder.sVertexBuilder]!! + SVertexBuilder.pushNum.invoke(vertexBuilder, blockId) func() - Refs.popEntity.invoke(vertexBuilder) + SVertexBuilder.pop.invoke(vertexBuilder) } else { func() } diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt index c216cb0..9decbcc 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderCactus.kt @@ -5,30 +5,39 @@ import mods.betterfoliage.client.Client import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.* import mods.octarinecore.client.render.lighting.* -import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable -import mods.octarinecore.common.Int3 +import mods.octarinecore.client.resource.* import mods.octarinecore.common.Rotation import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.SimpleBlockMatcher import net.minecraft.block.BlockState import net.minecraft.block.CactusBlock -import net.minecraft.client.renderer.BlockRendererDispatcher -import net.minecraft.client.renderer.BufferBuilder -import net.minecraft.util.BlockRenderLayer 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.* +import java.util.concurrent.CompletableFuture -object StandardCactusRegistry : ModelRenderRegistryConfigurable() { +object AsyncCactusDiscovery : ConfigurableModelDiscovery() { override val logger = BetterFoliage.logDetail override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java) override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side")) - override fun processModel(state: BlockState, textures: List) = SimpleColumnInfo.Key(logger, Axis.Y, textures) - init { BetterFoliage.modBus.register(this) } + override fun processModel(state: BlockState, textures: List, atlas: AtlasFuture): CompletableFuture? { + val sprites = textures.map { atlas.sprite(Identifier(it)) } + return atlas.afterStitch { + SimpleColumnInfo( + Axis.Y, + sprites[0].get(), + sprites[1].get(), + sprites.drop(2).map { it.get() } + ) + } + } + + fun init() { + AsyncSpriteProviderManager.providers.add(this) + } } class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { @@ -73,12 +82,12 @@ class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) override fun isEligible(ctx: CombinedContext): Boolean = Config.enabled && Config.cactus.enabled && - StandardCactusRegistry[ctx] != null + AsyncCactusDiscovery[ctx] != null override val onlyOnCutout get() = true override fun render(ctx: CombinedContext) { - val icons = StandardCactusRegistry[ctx]!! + val icons = AsyncCactusDiscovery[ctx]!! ctx.render( modelStem.model, diff --git a/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt b/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt index 1e8924f..0394263 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/RenderLog.kt @@ -8,18 +8,17 @@ import mods.betterfoliage.client.render.column.AbstractRenderColumn import mods.betterfoliage.client.render.column.ColumnRenderLayer import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo -import mods.octarinecore.client.render.BlockCtx +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.CombinedContext -import mods.octarinecore.client.resource.ModelRenderRegistry -import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable -import mods.octarinecore.client.resource.ModelRenderRegistryRoot +import mods.octarinecore.client.resource.* import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.tryDefault import net.minecraft.block.BlockState import net.minecraft.block.LogBlock import net.minecraft.util.Direction.Axis -import net.minecraft.world.IEnviromentBlockReader +import org.apache.logging.log4j.Level +import java.util.concurrent.CompletableFuture class RenderLog : AbstractRenderColumn(BetterFoliage.MOD_ID, BetterFoliage.modBus) { @@ -49,12 +48,24 @@ class RoundLogOverlayLayer : ColumnRenderLayer() { object LogRegistry : ModelRenderRegistryRoot() -object StandardLogRegistry : ModelRenderRegistryConfigurable() { +object AsyncLogDiscovery : ConfigurableModelDiscovery() { override val logger = BetterFoliage.logDetail override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks override val modelTextures: List get() = BlockConfig.logModels.modelList - override fun processModel(state: BlockState, textures: List) = SimpleColumnInfo.Key(logger, getAxis(state), textures) - init { BetterFoliage.modBus.register(this) } + + override fun processModel(state: BlockState, textures: List, atlas: AtlasFuture): CompletableFuture { + val axis = getAxis(state) + logger.log(Level.DEBUG, "$logName: axis $axis") + val spriteList = textures.map { atlas.sprite(Identifier(it)) } + return atlas.afterStitch { + SimpleColumnInfo( + axis, + spriteList[0].get(), + spriteList[1].get(), + spriteList.drop(2).map { it.get() } + ) + } + } fun getAxis(state: BlockState): Axis? { val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?: @@ -66,4 +77,9 @@ object StandardLogRegistry : ModelRenderRegistryConfigurable( else -> null } } + + fun init() { + LogRegistry.registries.add(this) + AsyncSpriteProviderManager.providers.add(this) + } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/render/column/RenderData.kt b/src/main/kotlin/mods/betterfoliage/client/render/column/RenderData.kt index 8b13ebc..4593c92 100644 --- a/src/main/kotlin/mods/betterfoliage/client/render/column/RenderData.kt +++ b/src/main/kotlin/mods/betterfoliage/client/render/column/RenderData.kt @@ -1,14 +1,9 @@ package mods.betterfoliage.client.render.column import mods.octarinecore.client.render.lighting.QuadIconResolver -import mods.octarinecore.client.resource.ModelRenderKey -import mods.octarinecore.client.resource.get -import mods.octarinecore.client.resource.missingSprite import mods.octarinecore.common.rotate -import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.util.Direction.* -import org.apache.logging.log4j.Logger interface ColumnTextureInfo { val axis: Axis? @@ -34,13 +29,4 @@ open class SimpleColumnInfo( val sideIdx = if (sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size else 0 sideTextures[sideIdx] } - - class Key(override val logger: Logger, val axis: Axis?, val textures: List) : ModelRenderKey { - override fun resolveSprites(atlas: AtlasTexture) = SimpleColumnInfo( - axis, - atlas[textures[0]] ?: missingSprite, - atlas[textures[1]] ?: missingSprite, - textures.drop(2).map { atlas[it] ?: missingSprite } - ) - } } \ No newline at end of file diff --git a/src/forge/kotlin/mods/octarinecore/resource/Aliases.kt b/src/main/kotlin/mods/betterfoliage/client/resource/Aliases.kt similarity index 87% rename from src/forge/kotlin/mods/octarinecore/resource/Aliases.kt rename to src/main/kotlin/mods/betterfoliage/client/resource/Aliases.kt index 822c1dc..ca864c4 100644 --- a/src/forge/kotlin/mods/octarinecore/resource/Aliases.kt +++ b/src/main/kotlin/mods/betterfoliage/client/resource/Aliases.kt @@ -1,4 +1,4 @@ -package mods.octarinecore.resource +package mods.betterfoliage.client.resource import net.minecraft.client.renderer.model.ModelResourceLocation import net.minecraft.client.renderer.texture.TextureAtlasSprite diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt index 1ff7a8f..1655385 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/GrassRegistry.kt @@ -3,16 +3,16 @@ package mods.betterfoliage.client.texture import mods.betterfoliage.BetterFoliage import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.Config +import mods.betterfoliage.client.resource.Identifier import mods.octarinecore.client.render.lighting.HSB import mods.octarinecore.client.resource.* import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ModelTextureList import net.minecraft.block.BlockState -import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.texture.TextureAtlasSprite import org.apache.logging.log4j.Level -import org.apache.logging.log4j.Logger import java.lang.Math.min +import java.util.concurrent.CompletableFuture const val defaultGrassColor = 0 @@ -32,28 +32,34 @@ class GrassInfo( object GrassRegistry : ModelRenderRegistryRoot() -object StandardGrassRegistry : ModelRenderRegistryConfigurable() { +object AsyncGrassDiscovery : ConfigurableModelDiscovery() { override val logger = BetterFoliage.logDetail override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks override val modelTextures: List get() = BlockConfig.grassModels.modelList - override fun processModel(state: BlockState, textures: List) = StandardGrassKey(logger, textures[0]) - init { BetterFoliage.modBus.register(this) } -} -class StandardGrassKey(override val logger: Logger, val textureName: String) : ModelRenderKey { - override fun resolveSprites(atlas: AtlasTexture): GrassInfo { - val logName = "StandardGrassKey" - val texture = atlas[textureName] ?: missingSprite - logger.log(Level.DEBUG, "$logName: texture $textureName") - val hsb = HSB.fromColor(texture.averageColor ?: defaultGrassColor) - 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") - hsb.copy(brightness = min(0.9f, hsb.brightness * 2.0f)).asColor - } else { - logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color") - null + override fun processModel(state: BlockState, textures: List, atlas: AtlasFuture): CompletableFuture { + val textureName = textures[0] + val spriteF = atlas.sprite(Identifier(textureName)) + logger.log(Level.DEBUG, "$logName: texture $textureName") + return atlas.afterStitch { + 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 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") + hsb.copy(brightness = min(0.9f, hsb.brightness * 2.0f)).asColor + } else { + logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color") + null + } + GrassInfo(sprite, overrideColor) } - return GrassInfo(texture, overrideColor) + } + + fun init() { + GrassRegistry.registries.add(this) + AsyncSpriteProviderManager.providers.add(this) } } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt b/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt index db84d49..923aede 100644 --- a/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt +++ b/src/main/kotlin/mods/betterfoliage/client/texture/LeafRegistry.kt @@ -3,16 +3,14 @@ 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 import mods.octarinecore.client.resource.* import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ModelTextureList import net.minecraft.block.BlockState import net.minecraft.client.renderer.texture.TextureAtlasSprite -import net.minecraft.client.renderer.texture.AtlasTexture -import net.minecraft.util.ResourceLocation -import net.minecraftforge.client.event.TextureStitchEvent -import org.apache.logging.log4j.Level -import org.apache.logging.log4j.Logger +import java.util.concurrent.CompletableFuture const val defaultLeafColor = 0 @@ -25,7 +23,7 @@ class LeafInfo( val leafType: String, /** Average color of the round leaf texture. */ - val averageColor: Int = roundLeafTexture.averageColor ?: defaultLeafColor + val averageColor: Int = roundLeafTexture.averageColor ) { /** [IconSet] of the textures to use for leaf particles emitted from this block. */ val particleTextures: IconSet get() = LeafParticleRegistry[leafType] @@ -33,27 +31,27 @@ class LeafInfo( object LeafRegistry : ModelRenderRegistryRoot() -object StandardLeafRegistry : ModelRenderRegistryConfigurable() { +object AsyncLeafDiscovery : ConfigurableModelDiscovery() { override val logger = BetterFoliage.logDetail override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks override val modelTextures: List get() = BlockConfig.leafModels.modelList - override fun processModel(state: BlockState, textures: List) = StandardLeafKey(logger, textures[0]) - init { BetterFoliage.modBus.register(this) } + + override fun processModel(state: BlockState, textures: List, atlas: AtlasFuture) = defaultRegisterLeaf(Identifier(textures[0]), atlas) + + fun init() { + LeafRegistry.registries.add(this) + AsyncSpriteProviderManager.providers.add(this) + } } -class StandardLeafKey(override val logger: Logger, val textureName: String) : ModelRenderKey { - lateinit var leafType: String - lateinit var generated: ResourceLocation +fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture { + val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default" + val generated = Client.genLeaves.register(sprite, leafType) + val roundLeaf = atlas.sprite(generated) - override fun onPreStitch(event: TextureStitchEvent.Pre) { - val logName = "StandardLeafKey" - leafType = LeafParticleRegistry.typeMappings.getType(textureName) ?: "default" - generated = Client.genLeaves.register(ResourceLocation(textureName), leafType) - event.addSprite(generated) - - logger.log(Level.DEBUG, "$logName: leaf texture $textureName") - logger.log(Level.DEBUG, "$logName: particle $leafType") + log(" leaf texture $sprite") + log(" particle $leafType") + return atlas.afterStitch { + LeafInfo(roundLeaf.get(), leafType) } - - override fun resolveSprites(atlas: AtlasTexture) = LeafInfo(atlas[generated] ?: missingSprite, leafType) } \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt b/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt deleted file mode 100644 index 41c2835..0000000 --- a/src/main/kotlin/mods/betterfoliage/loader/BetterFoliageCore.kt +++ /dev/null @@ -1,130 +0,0 @@ -package mods.betterfoliage.loader - -//import mods.octarinecore.metaprog.Transformer -import mods.octarinecore.metaprog.allAvailable -import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES -import org.objectweb.asm.ClassWriter.COMPUTE_MAXS -import org.objectweb.asm.Opcodes.* - -/* -class BetterFoliageTransformer : Transformer() { - - val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer) - - init { - // where: WorldClient.animateTick(), replacing invocation to Block.animateTick - // what: invoke BF code for every random display tick - // why: allows us to catch random display ticks - transformMethod(Refs.WC_animeteTick) { - find(invokeRef(Refs.B_animateTick))?.replace { - log.info("[BetterFoliageLoader] Applying random display tick call hook") - invokeStatic(Refs.onRandomDisplayTick) - } ?: log.warn("[BetterFoliageLoader] Failed to apply random display tick call hook!") - } - - // where: BlockStateContainer$StateImplementation.getAmbientOcclusionLightValue() - // what: invoke BF code to overrule AO transparency value - // why: allows us to have light behave properly on non-solid log blocks - transformMethod(Refs.getAmbientOcclusionLightValue) { - find(FRETURN)?.insertBefore { - log.info("[BetterFoliageLoader] Applying getAmbientOcclusionLightValue() override") - varinsn(ALOAD, 0) - invokeStatic(Refs.getAmbientOcclusionLightValueOverride) - } ?: log.warn("[BetterFoliageLoader] Failed to apply getAmbientOcclusionLightValue() override!") - } - - // where: BlockStateContainer$StateImplementation.useNeighborBrightness() - // what: invoke BF code to overrule _useNeighborBrightness_ - // why: allows us to have light behave properly on non-solid log blocks - transformMethod(Refs.useNeighborBrightness) { - find(IRETURN)?.insertBefore { - log.info("[BetterFoliageLoader] Applying useNeighborBrightness() override") - varinsn(ALOAD, 0) - invokeStatic(Refs.useNeighborBrightnessOverride) - } ?: log.warn("[BetterFoliageLoader] Failed to apply useNeighborBrightness() override!") - } - - // where: BlockStateContainer$StateImplementation.doesSideBlockRendering() - // what: invoke BF code to overrule condition - // why: allows us to make log blocks non-solid - transformMethod(Refs.doesSideBlockRendering) { - find(IRETURN)?.insertBefore { - log.info("[BetterFoliageLoader] Applying doesSideBlockRendering() override") - varinsn(ALOAD, 0) - invokeStatic(Refs.doesSideBlockRenderingOverride) - } ?: log.warn("[BetterFoliageLoader] Failed to apply doesSideBlockRendering() override!") - } - - // where: BlockStateContainer$StateImplementation.isOpaqueCube() - // what: invoke BF code to overrule condition - // why: allows us to make log blocks non-solid - transformMethod(Refs.isOpaqueCube) { - find(IRETURN)?.insertBefore { - log.info("[BetterFoliageLoader] Applying isOpaqueCube() override") - varinsn(ALOAD, 0) - invokeStatic(Refs.isOpaqueCubeOverride) - } ?: log.warn("[BetterFoliageLoader] Failed to apply isOpaqueCube() override!") - } - - // where: ModelBakery.setupModelRegistry(), after all models are loaded - // what: invoke handler code with ModelBakery instance - // why: allows us to iterate the unbaked models in ModelBakery in time to register textures - transformMethod(Refs.setupModelRegistry) { - find(invokeName("newLinkedHashSet"))?.insertBefore { - log.info("[BetterFoliageLoader] Applying ModelLoader lifecycle callback") - varinsn(ALOAD, 0) - invokeStatic(Refs.onAfterLoadModelDefinitions) - } ?: log.warn("[BetterFoliageLoader] Failed to apply ModelLoader lifecycle callback!") - } - - // where: RenderChunk.rebuildChunk() - // what: replace call to BlockRendererDispatcher.renderBlock() - // why: allows us to perform additional rendering for each block - // what: invoke code to overrule result of Block.canRenderInLayer() - // why: allows us to render transparent quads for blocks which are only on the SOLID layer - transformMethod(Refs.rebuildChunk) { - applyWriterFlags(COMPUTE_FRAMES, COMPUTE_MAXS) - find(invokeRef(Refs.renderBlock))?.replace { - log.info("[BetterFoliageLoader] Applying RenderChunk block render override") - varinsn(ALOAD, if (isOptifinePresent) 22 else 20) - invokeStatic(Refs.renderWorldBlock) - } - if (isOptifinePresent) { - find(varinsn(ISTORE, 23))?.insertAfter { - log.info("[BetterFoliageLoader] Applying RenderChunk block layer override (Optifine)") - varinsn(ALOAD, 19) - varinsn(ALOAD, 18) - varinsn(ALOAD, 22) - invokeStatic(Refs.canRenderBlockInLayer) - varinsn(ISTORE, 23) - } - } else { - find(invokeRef(Refs.canRenderInLayer))?.replace { - log.info("[BetterFoliageLoader] Applying RenderChunk block layer override (non-Optifine)") - invokeStatic(Refs.canRenderBlockInLayer) - } - } - } - - // where: net.minecraft.client.renderer.BlockModelRenderer$AmbientOcclusionFace - // what: make constructor public - // why: use vanilla AO calculation at will without duplicating code - transformMethod(Refs.AOF_constructor) { - log.info("[BetterFoliageLoader] Setting AmbientOcclusionFace constructor public") - makePublic() - } - - // where: shadersmod.client.SVertexBuilder.pushEntity() - // what: invoke code to overrule block data - // why: allows us to change the block ID seen by shader programs - transformMethod(Refs.pushEntity_state) { - find(invokeRef(Refs.pushEntity_num))?.insertBefore { - log.info("[BetterFoliageLoader] Applying SVertexBuilder.pushEntity() block ID override") - varinsn(ALOAD, 0) - invokeStatic(Refs.getBlockIdOverride) - } ?: log.warn("[BetterFoliageLoader] Failed to apply SVertexBuilder.pushEntity() block ID override!") - } - } -} - - */ \ No newline at end of file diff --git a/src/main/kotlin/mods/betterfoliage/loader/Refs.kt b/src/main/kotlin/mods/betterfoliage/loader/Refs.kt deleted file mode 100644 index 5c02597..0000000 --- a/src/main/kotlin/mods/betterfoliage/loader/Refs.kt +++ /dev/null @@ -1,58 +0,0 @@ -package mods.betterfoliage.loader - -import mods.octarinecore.metaprog.ClassRef -import mods.octarinecore.metaprog.FieldRef -import mods.octarinecore.metaprog.MethodRef - -/** Singleton object holding references to foreign code elements. */ -object Refs { -// val mcVersion = FMLInjectionData.data()[4].toString() - - // Java - val String = ClassRef("java.lang.String") - val Map = ClassRef("java.util.Map") - val List = ClassRef("java.util.List") - val Random = ClassRef("java.util.Random") - - // Minecraft - val IBlockReader = ClassRef("net.minecraft.world.IBlockReader") - val IEnvironmentBlockReader = ClassRef("net.minecraft.world.IEnvironmentBlockReader") - val BlockState = ClassRef("net.minecraft.block.state.BlockState") - val BlockPos = ClassRef("net.minecraft.util.math.BlockPos") - val BlockRenderLayer = ClassRef("net.minecraft.util.BlockRenderLayer") - val Block = ClassRef("net.minecraft.block.Block") - val BufferBuilder = ClassRef("net.minecraft.client.renderer.BufferBuilder") - val BlockRendererDispatcher = ClassRef("net.minecraft.client.renderer.BlockRendererDispatcher") - val ChunkCache = ClassRef("net.minecraft.client.renderer.chunk.ChunkRenderCache") - val TextureAtlasSprite = ClassRef("net.minecraft.client.renderer.texture.TextureAtlasSprite") - val ResourceLocation = ClassRef("net.minecraft.util.ResourceLocation") - - // Optifine - val OptifineClassTransformer = ClassRef("optifine.OptiFineClassTransformer") - val OptifineChunkCache = ClassRef("net.optifine.override.ChunkCacheOF") - val CCOFChunkCache = FieldRef(OptifineChunkCache, "chunkCache", ChunkCache) - - val getBlockId = MethodRef(BlockState, "getBlockId", ClassRef.int); - val getMetadata = MethodRef(BlockState, "getMetadata", ClassRef.int); - - // Optifine - val RenderEnv = ClassRef("net.optifine.render.RenderEnv") - val RenderEnv_reset = MethodRef(RenderEnv, "reset", ClassRef.void, BlockState, BlockPos) - val quadSprite = FieldRef(BufferBuilder, "quadSprite", TextureAtlasSprite) - val BlockPosM = ClassRef("net.optifine.BlockPosM") - val IColorizer = ClassRef("net.optifine.CustomColors\$IColorizer") - - // Optifine: custom colors - val CustomColors = ClassRef("net.optifine.CustomColors") - val getColorMultiplier = MethodRef(CustomColors, "getSmoothColorMultiplier", ClassRef.int, BlockState, IEnvironmentBlockReader, BlockPos, IColorizer, BlockPosM) - - // Optifine: shaders - val SVertexBuilder = ClassRef("net.optifine.shaders.SVertexBuilder") - val sVertexBuilder = FieldRef(BufferBuilder, "sVertexBuilder", SVertexBuilder) - val pushEntity_state = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, BlockState, BlockPos, IBlockReader, BufferBuilder) - val pushEntity_num = MethodRef(SVertexBuilder, "pushEntity", ClassRef.void, ClassRef.long) - val popEntity = MethodRef(SVertexBuilder, "popEntity", ClassRef.void) - - val ShadersModIntegration = ClassRef("mods.betterfoliage.client.integration.ShadersModIntegration") - val getBlockIdOverride = MethodRef(ShadersModIntegration, "getBlockIdOverride", ClassRef.long, ClassRef.long, BlockState) -} \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/CommonRefs.kt b/src/main/kotlin/mods/octarinecore/CommonRefs.kt new file mode 100644 index 0000000..a25b221 --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/CommonRefs.kt @@ -0,0 +1,68 @@ +package mods.octarinecore + +import mods.octarinecore.metaprog.ClassRef +import mods.octarinecore.metaprog.FieldRef +import mods.octarinecore.metaprog.MethodRef +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.client.renderer.BlockRendererDispatcher +import net.minecraft.client.renderer.BufferBuilder +import net.minecraft.client.renderer.chunk.ChunkRenderCache +import net.minecraft.client.renderer.texture.TextureAtlasSprite +import net.minecraft.util.BlockRenderLayer +import net.minecraft.util.ResourceLocation +import net.minecraft.util.math.BlockPos +import net.minecraft.world.IBlockReader +import net.minecraft.world.IEnviromentBlockReader +import java.util.* + +// Java +val String = ClassRef("java.lang.String") +val Map = ClassRef>("java.util.Map") +val List = ClassRef>("java.util.List") +val Random = ClassRef("java.util.Random") + +// Minecraft +val IBlockReader = ClassRef("net.minecraft.world.IBlockReader") +val IEnvironmentBlockReader = ClassRef("net.minecraft.world.IEnvironmentBlockReader") +val BlockState = ClassRef("net.minecraft.block.state.BlockState") +val BlockPos = ClassRef("net.minecraft.util.math.BlockPos") +val BlockRenderLayer = ClassRef("net.minecraft.util.BlockRenderLayer") +val Block = ClassRef("net.minecraft.block.Block") +object BufferBuilder : ClassRef("net.minecraft.client.renderer.BufferBuilder") { + /** Optifine only */ + val sVertexBuilder = FieldRef(this, "sVertexBuilder", SVertexBuilder) + /** Optifine only */ + val quadSprite = FieldRef(this, "quadSprite", TextureAtlasSprite) +} +val BlockRendererDispatcher = ClassRef("net.minecraft.client.renderer.BlockRendererDispatcher") +val ChunkRenderCache = ClassRef("net.minecraft.client.renderer.chunk.ChunkRenderCache") +val TextureAtlasSprite = ClassRef("net.minecraft.client.renderer.texture.TextureAtlasSprite") +val ResourceLocation = ClassRef("net.minecraft.util.ResourceLocation") + +// Optifine +val OptifineClassTransformer = ClassRef("optifine.OptiFineClassTransformer") +val BlockPosM = ClassRef("net.optifine.BlockPosM") +object ChunkCacheOF : ClassRef("net.optifine.override.ChunkCacheOF") { + val chunkCache = FieldRef(this, "chunkCache", ChunkRenderCache) +} + +object RenderEnv : ClassRef("net.optifine.render.RenderEnv") { + val reset = MethodRef(this, "reset", void, BlockState, BlockPos) +} + +// Optifine custom colors +val IColorizer = ClassRef("net.optifine.CustomColors\$IColorizer") +object CustomColors : ClassRef("net.optifine.CustomColors") { + val getColorMultiplier = MethodRef(this, "getSmoothColorMultiplier", int, BlockState, IEnvironmentBlockReader, BlockPos, IColorizer, BlockPosM) +} + +// Optifine shaders +object SVertexBuilder : ClassRef("net.optifine.shaders.SVertexBuilder") { + val pushState = MethodRef(this, "pushEntity", void, BlockState, BlockPos, IBlockReader, BufferBuilder) + val pushNum = MethodRef(this, "pushEntity", void, long) + val pop = MethodRef(this, "popEntity", void) +} + + + diff --git a/src/main/kotlin/mods/octarinecore/Utils.kt b/src/main/kotlin/mods/octarinecore/Utils.kt index 4075ca1..f21411d 100644 --- a/src/main/kotlin/mods/octarinecore/Utils.kt +++ b/src/main/kotlin/mods/octarinecore/Utils.kt @@ -2,11 +2,12 @@ @file:Suppress("NOTHING_TO_INLINE") package mods.octarinecore -import mods.betterfoliage.loader.Refs import net.minecraft.tileentity.TileEntity import net.minecraft.util.ResourceLocation import net.minecraft.util.math.BlockPos import net.minecraft.world.* +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.Logger import kotlin.reflect.KProperty import java.lang.Math.* @@ -118,4 +119,11 @@ fun nextPowerOf2(x: Int): Int { */ fun IWorldReader.getTileEntitySafe(pos: BlockPos): TileEntity? = tryDefault(null as TileEntity?) { if (isBlockLoaded(pos)) getTileEntity(pos) else null +} + +interface HasLogger { + val logger: Logger + val logName: String get() = this::class.simpleName!! + fun log(msg: String) = log(Level.DEBUG, msg) + fun log(level: Level, msg: String) = logger.log(level, "[$logName] $msg") } \ No newline at end of file diff --git a/src/main/kotlin/mods/octarinecore/client/render/CombinedContext.kt b/src/main/kotlin/mods/octarinecore/client/render/CombinedContext.kt index 6e41725..a2a0c75 100644 --- a/src/main/kotlin/mods/octarinecore/client/render/CombinedContext.kt +++ b/src/main/kotlin/mods/octarinecore/client/render/CombinedContext.kt @@ -2,23 +2,21 @@ package mods.octarinecore.client.render import mods.betterfoliage.client.render.canRenderInCutout import mods.betterfoliage.client.render.isCutout -import mods.betterfoliage.loader.Refs +import mods.octarinecore.BufferBuilder import mods.octarinecore.client.render.lighting.* import mods.octarinecore.common.Double3 import mods.octarinecore.common.Int3 import mods.octarinecore.common.Rotation import mods.octarinecore.common.plus +import mods.octarinecore.metaprog.get +import mods.octarinecore.metaprog.set import net.minecraft.block.Blocks -import net.minecraft.client.renderer.BlockRendererDispatcher -import net.minecraft.client.renderer.BufferBuilder import net.minecraft.fluid.Fluids -import net.minecraft.util.BlockRenderLayer import net.minecraft.util.Direction import net.minecraft.util.math.BlockPos import net.minecraft.world.IEnviromentBlockReader import net.minecraft.world.LightType import net.minecraft.world.biome.Biomes -import java.util.* class CombinedContext( val blockCtx: BlockCtx, val renderCtx: RenderCtx, val lightingCtx: DefaultLightingCtx @@ -64,7 +62,7 @@ class CombinedContext( if (drawIcon != null) { // let OptiFine know the texture we're using, so it can // transform UV coordinates to quad-relative - Refs.quadSprite.set(renderCtx!!.renderBuffer, drawIcon) + renderCtx.renderBuffer[BufferBuilder.quadSprite] = drawIcon quad.verts.forEachIndexed { vertIdx, vert -> temp.init(vert).rotate(lightingCtx.modelRotation).translate(translation) diff --git a/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt b/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt new file mode 100644 index 0000000..01c61e2 --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/client/resource/AsyncSpriteProviderManager.kt @@ -0,0 +1,59 @@ +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 net.minecraft.client.renderer.texture.AtlasTexture +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 + +class StitchPhases( + val discovery: CompletableFuture, + val cleanup: CompletableFuture +) + +interface AsyncSpriteProvider { + fun setup(bakeryFuture: CompletableFuture, atlasFuture: 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/ModelDiscovery.kt b/src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt new file mode 100644 index 0000000..74454ba --- /dev/null +++ b/src/main/kotlin/mods/octarinecore/client/resource/ModelDiscovery.kt @@ -0,0 +1,132 @@ +package mods.octarinecore.client.resource + +import com.google.common.base.Joiner +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 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.util.ResourceLocation +import net.minecraft.util.math.BlockPos +import net.minecraft.world.IBlockReader +import net.minecraftforge.registries.ForgeRegistries +import java.util.concurrent.CompletableFuture + +interface ModelRenderRegistry { + operator fun get(ctx: BlockCtx) = get(ctx.state, ctx.world, ctx.pos) + operator fun get(ctx: BlockCtx, offset: Int3) = get(ctx.state(offset), ctx.world, ctx.pos + offset) + operator fun get(state: BlockState, world: IBlockReader, pos: BlockPos): T? +} + +abstract class ModelRenderRegistryRoot : ModelRenderRegistry { + val registries = mutableListOf>() + override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = registries.findFirst { it[state, world, pos] } +} + +class ModelDiscoveryContext( + bakery: ModelBakery, + val state: BlockState, + val modelId: ModelIdentifier +) { + val models = bakery.unwrapVariants(bakery.getUnbakedModel(modelId) to modelId) + .filter { it.second != bakery.getUnbakedModel(ModelBakery.MODEL_MISSING) } + + fun ModelBakery.unwrapVariants(modelAndLoc: Pair): List> = when(val model = modelAndLoc.first) { + is VariantList -> model.variantList.flatMap { variant -> unwrapVariants(getUnbakedModel(variant.modelLocation) to variant.modelLocation) } + is BlockModel -> listOf(modelAndLoc) + else -> emptyList() + } +} + +abstract class ModelDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry { + + var modelData: Map = emptyMap() + protected set + + override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = modelData[state] + + abstract fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture? + + override fun setup(bakeryFuture: CompletableFuture, atlasFuture: AtlasFuture): StitchPhases { + val modelDataTemp = mutableMapOf>() + + return StitchPhases( + discovery = bakeryFuture.thenRunAsync { bakery -> + var errors = 0 + bakery.iterateModels { ctx -> + try { + processModel(ctx, atlasFuture)?.let { modelDataTemp[ctx.state] = it } + } catch (e: Exception) { + errors++ + } + } + log("${modelDataTemp.size} BlockStates discovered, $errors errors") + }, + cleanup = atlasFuture.sheet.thenRunAsync { sheetData -> + modelData = modelDataTemp.filterValues { !it.isCompletedExceptionally }.mapValues { it.value.get() } + val errors = modelDataTemp.values.filter { it.isCompletedExceptionally }.size + log("${modelData.size} BlockStates loaded, $errors errors") + } + ) + } + + fun ModelBakery.iterateModels(func: (ModelDiscoveryContext)->Unit) { + ForgeRegistries.BLOCKS.flatMap { block -> + block.stateContainer.validStates.map { state -> state to BlockModelShapes.getModelLocation(state) } + }.forEach { (state, stateModelResource) -> + func(ModelDiscoveryContext(this, state, stateModelResource)) + } + } +} + +abstract class ConfigurableModelDiscovery : ModelDiscovery() { + + abstract val matchClasses: IBlockMatcher + abstract val modelTextures: List + + abstract fun processModel(state: BlockState, textures: List, atlas: AtlasFuture): CompletableFuture? + + override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture? { + val matchClass = matchClasses.matchingClass(ctx.state.block) ?: return null + log("block state ${ctx.state.toString()}") + log(" class ${ctx.state.block.javaClass.name} matches ${matchClass.name}") + + if (ctx.models.isEmpty()) { + log(" no models found") + return null + } + + ctx.models.filter { it.first is BlockModel }.forEach { (model, location) -> + model as BlockModel + val modelMatch = modelTextures.firstOrNull { (model to location).derivesFrom(it.modelLocation) } + if (modelMatch != null) { + log(" model ${model} matches ${modelMatch.modelLocation}") + + val textures = modelMatch.textureNames.map { it to model.resolveTextureName(it) } + val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" }) + log(" sprites [$texMapString]") + + if (textures.all { it.second != "missingno" }) { + // found a valid model (all required textures exist) + return processModel(ctx.state, textures.map { it.second}, atlas) + } + } + } + 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/ModelProcessor.kt b/src/main/kotlin/mods/octarinecore/client/resource/ModelProcessor.kt deleted file mode 100644 index 923623b..0000000 --- a/src/main/kotlin/mods/octarinecore/client/resource/ModelProcessor.kt +++ /dev/null @@ -1,138 +0,0 @@ -package mods.octarinecore.client.resource - -import com.google.common.base.Joiner -import mods.octarinecore.client.render.BlockCtx -import mods.octarinecore.client.render.CombinedContext -import mods.octarinecore.common.Int3 -import mods.octarinecore.common.config.IBlockMatcher -import mods.octarinecore.common.config.ModelTextureList -import mods.octarinecore.common.plus -import mods.octarinecore.findFirst -import net.minecraft.block.BlockState -import net.minecraft.client.renderer.BlockModelShapes -import net.minecraft.client.renderer.model.* -import net.minecraft.client.renderer.texture.AtlasTexture -import net.minecraft.util.ResourceLocation -import net.minecraft.util.math.BlockPos -import net.minecraft.world.IBlockReader -import net.minecraft.world.IEnviromentBlockReader -import net.minecraftforge.client.event.TextureStitchEvent -import net.minecraftforge.eventbus.api.Event -import net.minecraftforge.eventbus.api.EventPriority -import net.minecraftforge.eventbus.api.SubscribeEvent -import net.minecraftforge.registries.ForgeRegistries -import org.apache.logging.log4j.Level -import org.apache.logging.log4j.Logger - -class LoadModelDataEvent(val bakery: ModelBakery) : Event() - -interface ModelRenderRegistry { - operator fun get(ctx: BlockCtx) = get(ctx.state(Int3.zero), ctx.world, ctx.pos) - operator fun get(ctx: BlockCtx, offset: Int3) = get(ctx.state(offset), ctx.world, ctx.pos + offset) - operator fun get(state: BlockState, world: IBlockReader, pos: BlockPos): T? -} - -interface ModelRenderDataExtractor { - fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List>): ModelRenderKey? -} - -interface ModelRenderKey { - val logger: Logger? - fun onPreStitch(event: TextureStitchEvent.Pre) {} - fun resolveSprites(atlas: AtlasTexture): T -} - -abstract class ModelRenderRegistryRoot : ModelRenderRegistry { - val subRegistries = mutableListOf>() - override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = subRegistries.findFirst { it[state, world, pos] } - fun addRegistry(registry: ModelRenderRegistry) { - subRegistries.add(registry) - } -} - -abstract class ModelRenderRegistryBase : ModelRenderRegistry, ModelRenderDataExtractor { - open val logger: Logger? = null - open val logName: String get() = this::class.java.name - - val stateToKey = mutableMapOf>() - var stateToValue = mapOf() - - override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = stateToValue[state] - - @Suppress("UNCHECKED_CAST") - @SubscribeEvent - fun handleLoadModelData(event: LoadModelDataEvent) { - stateToValue = emptyMap() - - val stateMappings = ForgeRegistries.BLOCKS.flatMap { block -> - block.stateContainer.validStates.map { state -> state to BlockModelShapes.getModelLocation(state) } - } -// val unbakedModels = (Refs.unbakedModels.get(event.loader) as Map<*, *>).filter { it.value is IUnbakedModel } as Map - - val missingModel = event.bakery.getUnbakedModel(ModelBakery.MODEL_MISSING) - stateMappings.forEach { (state, stateModelResource) -> - val allModels = event.bakery.let { it.unwrapVariants(it.getUnbakedModel(stateModelResource) to stateModelResource) }.filter { it.second != missingModel } - try { - processModel(state, stateModelResource, allModels)?.let { stateToKey[state] = it } - } catch (e: Exception) { - logger?.warn("Exception while trying to process model ${stateModelResource}", e) - } - } - } - - @SubscribeEvent(priority = EventPriority.LOW) - fun handlePreStitch(event: TextureStitchEvent.Pre) { - if (event.map.basePath != "textures") return - stateToKey.forEach { (_, key) -> key.onPreStitch(event) } - } - - @SubscribeEvent(priority = EventPriority.LOW) - fun handlePostStitch(event: TextureStitchEvent.Post) { - if (event.map.basePath != "textures") return - stateToValue = stateToKey.mapValues { (_, key) -> key.resolveSprites(event.map) } - stateToKey.clear() - } -} - -abstract class ModelRenderRegistryConfigurable : ModelRenderRegistryBase() { - - abstract val matchClasses: IBlockMatcher - abstract val modelTextures: List - - override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List>): ModelRenderKey? { - val matchClass = matchClasses.matchingClass(state.block) ?: return null - logger?.log(Level.DEBUG, "$logName: block state ${state.toString()}") - logger?.log(Level.DEBUG, "$logName: class ${state.block.javaClass.name} matches ${matchClass.name}") - - if (models.isEmpty()) { - logger?.log(Level.DEBUG, "$logName: no models found") - return null - } - - models.filter { it.first is BlockModel }.forEach { (model, location) -> - model as BlockModel - val modelMatch = modelTextures.firstOrNull { (model to location).derivesFrom(it.modelLocation) } - if (modelMatch != null) { - logger?.log(Level.DEBUG, "$logName: model ${model} matches ${modelMatch.modelLocation}") - - val textures = modelMatch.textureNames.map { it to model.resolveTextureName(it) } - val texMapString = Joiner.on(", ").join(textures.map { "${it.first}=${it.second}" }) - logger?.log(Level.DEBUG, "$logName: textures [$texMapString]") - - if (textures.all { it.second != "missingno" }) { - // found a valid model (all required textures exist) - return processModel(state, textures.map { it.second} ) - } - } - } - return null - } - - abstract fun processModel(state: BlockState, textures: List) : ModelRenderKey? -} - -fun ModelBakery.unwrapVariants(modelAndLoc: Pair): List> = when(val model = modelAndLoc.first) { - is VariantList -> model.variantList.flatMap { variant -> unwrapVariants(getUnbakedModel(variant.modelLocation) to variant.modelLocation) } - is BlockModel -> listOf(modelAndLoc) - else -> emptyList() -} diff --git a/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt b/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt index d1a43a6..ddcd1b5 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/ResourceHandler.kt @@ -1,10 +1,10 @@ package mods.octarinecore.client.resource +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.resource.Identifier -import mods.octarinecore.resource.Sprite import mods.octarinecore.stripEnd import mods.octarinecore.stripStart import net.minecraft.client.renderer.texture.AtlasTexture @@ -63,10 +63,10 @@ open class ResourceHandler( // ============================ // Resource declarations // ============================ - fun iconStatic(location: ()->Identifier) = IconHolder(location).apply { resources.add(this) } + 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 iconSet(targetAtlas: Atlas = Atlas.BLOCKS, location: (Int)-> Identifier) = IconSet(targetAtlas, location).apply { this@ResourceHandler.resources.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) } @@ -102,7 +102,7 @@ open class ResourceHandler( // ============================ // Resource container classes // ============================ -class IconHolder(val location: ()->Identifier) : IStitchListener { +class IconHolder(val location: ()-> Identifier) : IStitchListener { var iconRes: Identifier? = null var icon: Sprite? = null override fun onPreStitch(event: TextureStitchEvent.Pre) { @@ -119,7 +119,7 @@ class ModelHolder(val init: Model.()->Unit): IConfigChangeListener { override fun onConfigChange() { model = Model().apply(init) } } -class IconSet(val targetAtlas: Atlas, val location: (Int)->Identifier) : IStitchListener { +class IconSet(val targetAtlas: Atlas, val location: (Int)-> Identifier) : IStitchListener { val resources = arrayOfNulls(16) val icons = arrayOfNulls(16) var num = 0 diff --git a/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt b/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt index 308d04b..828289a 100644 --- a/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt +++ b/src/main/kotlin/mods/octarinecore/client/resource/Utils.kt @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream import java.io.InputStream import java.lang.Math.* import javax.imageio.ImageIO +import kotlin.math.atan2 /** Concise getter for the Minecraft resource manager. */ val resourceManager: SimpleReloadableResourceManager @@ -65,19 +66,15 @@ val BufferedImage.asStream: InputStream get() = * Only non-transparent pixels are considered. Averages are taken in the HSB color space (note: Hue is a circular average), * and the result transformed back to the RGB color space. */ -val TextureAtlasSprite.averageColor: Int? get() { -// val locationNoDirs = ResourceLocation(iconName).stripStart("blocks/") -// val locationWithDirs = ResourceLocation(locationNoDirs.namespace, "textures/blocks/%s.png".format(locationNoDirs.path)) -// val image = resourceManager[locationWithDirs]?.loadImage() ?: return null - +val TextureAtlasSprite.averageColor: Int get() { var numOpaque = 0 var sumHueX = 0.0 var sumHueY = 0.0 var sumSaturation = 0.0f var sumBrightness = 0.0f - for (x in 0..width - 1) - for (y in 0..height - 1) { - val pixel = getPixelRGBA(0, x, y); + for (x in 0 until width) + for (y in 0 until height) { + val pixel = getPixelRGBA(0, x, y) val alpha = (pixel shr 24) and 255 val hsb = HSB.fromColor(pixel) if (alpha == 255) { @@ -90,7 +87,7 @@ val TextureAtlasSprite.averageColor: Int? get() { } // circular average - transform sum vector to polar angle - val avgHue = (atan2(sumHueY.toDouble(), sumHueX.toDouble()) / PI2 + 0.5).toFloat() + val avgHue = (atan2(sumHueY, sumHueX) / PI2 + 0.5).toFloat() return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat()).asColor } diff --git a/src/main/kotlin/mods/octarinecore/metaprog/Reflection.kt b/src/main/kotlin/mods/octarinecore/metaprog/Reflection.kt index 4b56c73..e9720b5 100644 --- a/src/main/kotlin/mods/octarinecore/metaprog/Reflection.kt +++ b/src/main/kotlin/mods/octarinecore/metaprog/Reflection.kt @@ -5,6 +5,8 @@ import java.lang.reflect.Field import java.lang.reflect.Method import mods.octarinecore.metaprog.Namespace.* import mods.octarinecore.tryDefault +import kotlin.reflect.KCallable +import kotlin.reflect.KFunction /** Get a Java class with the given name. */ fun getJavaClass(name: String) = tryDefault(null) { Class.forName(name) } @@ -66,19 +68,19 @@ fun allAvailable(vararg codeElement: Resolvable<*>) = codeElement.all { it.eleme * * @param[name] MCP name of the class */ -open class ClassRef(val name: String) : Resolvable>() { +open class ClassRef(val name: String) : Resolvable>() { companion object { val int = ClassRefPrimitive("I", Int::class.java) val long = ClassRefPrimitive("J", Long::class.java) val float = ClassRefPrimitive("F", Float::class.java) val boolean = ClassRefPrimitive("Z", Boolean::class.java) - val void = ClassRefPrimitive("V", null) + val void = ClassRefPrimitive("V", Void::class.java) } open fun asmDescriptor(namespace: Namespace) = "L${name.replace(".", "/")};" - override fun resolve() = getJavaClass(name) + override fun resolve() = getJavaClass(name) as Class? fun isInstance(obj: Any) = element?.isInstance(obj) ?: false } @@ -89,18 +91,11 @@ open class ClassRef(val name: String) : Resolvable>() { * @param[name] ASM descriptor of this primitive type * @param[clazz] class of this primitive type */ -class ClassRefPrimitive(name: String, val clazz: Class<*>?) : ClassRef(name) { +class ClassRefPrimitive(name: String, val clazz: Class?) : ClassRef(name) { override fun asmDescriptor(namespace: Namespace) = name override fun resolve() = clazz } -class ClassRefArray(name: String) : ClassRef(name) { - override fun asmDescriptor(namespace: Namespace) = "[" + super.asmDescriptor(namespace) - override fun resolve() = getJavaClass("[L$name;") -} - -fun ClassRef.array() = ClassRefArray(name) - /** * Reference to a method. * @@ -110,13 +105,13 @@ fun ClassRef.array() = ClassRefArray(name) * @param[returnType] reference to the return type * @param[returnType] references to the argument types */ -class MethodRef(val parentClass: ClassRef, +class MethodRef(val parentClass: ClassRef<*>, val mcpName: String, val srgName: String, - val returnType: ClassRef, - vararg val argTypes: ClassRef + val returnType: ClassRef, + vararg val argTypes: ClassRef<*> ) : Resolvable() { - constructor(parentClass: ClassRef, mcpName: String, returnType: ClassRef, vararg argTypes: ClassRef) : + constructor(parentClass: ClassRef<*>, mcpName: String, returnType: ClassRef, vararg argTypes: ClassRef<*>) : this(parentClass, mcpName, mcpName, returnType, *argTypes) fun name(namespace: Namespace) = when(namespace) { SRG -> srgName; MCP -> mcpName } @@ -133,11 +128,10 @@ class MethodRef(val parentClass: ClassRef, } /** Invoke this method using reflection. */ - fun invoke(receiver: Any, vararg args: Any?) = element?.invoke(receiver, *args) + operator fun invoke(receiver: Any, vararg args: Any?) = element?.invoke(receiver, *args) as T /** Invoke this static method using reflection. */ fun invokeStatic(vararg args: Any) = element?.invoke(null, *args) - } /** @@ -148,30 +142,41 @@ class MethodRef(val parentClass: ClassRef, * @param[srgName] SRG name of the field * @param[type] reference to the field type */ -class FieldRef(val parentClass: ClassRef, +class FieldRef(val parentClass: ClassRef<*>, val mcpName: String, val srgName: String, - val type: ClassRef? + val type: ClassRef ) : Resolvable() { - constructor(parentClass: ClassRef, mcpName: String, type: ClassRef?) : this(parentClass, mcpName, mcpName, type) + constructor(parentClass: ClassRef<*>, mcpName: String, type: ClassRef) : this(parentClass, mcpName, mcpName, type) fun name(namespace: Namespace) = when(namespace) { SRG -> srgName; MCP -> mcpName } - fun asmDescriptor(namespace: Namespace) = type!!.asmDescriptor(namespace) + fun asmDescriptor(namespace: Namespace) = type.asmDescriptor(namespace) override fun resolve(): Field? = if (parentClass.element == null) null else { - listOf(srgName, mcpName).map { tryDefault(null) { + listOf(srgName, mcpName).mapNotNull { tryDefault(null) { parentClass.element!!.getDeclaredField(it) - }}.filterNotNull().firstOrNull() + } }.firstOrNull() ?.apply{ isAccessible = true } } /** Get this field using reflection. */ - fun get(receiver: Any?) = element?.get(receiver) + operator fun get(receiver: Any?) = element?.get(receiver) as T /** Get this static field using reflection. */ fun getStatic() = get(null) fun set(receiver: Any?, obj: Any?) { element?.set(receiver, obj) } +} + + +fun Any.isInstance(cls: ClassRef<*>) = cls.isInstance(this) +interface ReflectionCallable { + operator fun invoke(vararg args: Any): T +} +inline operator fun Any.get(field: FieldRef) = field.get(this) +inline operator fun Any.set(field: FieldRef, value: T) = field.set(this, value) +inline operator fun Any.get(methodRef: MethodRef) = object : ReflectionCallable { + override fun invoke(vararg args: Any) = methodRef.invoke(this@get, args) } \ No newline at end of file diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 8c13014..366fe2b 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -7,14 +7,4 @@ public net.minecraft.block.BlockState$Cache public net.minecraft.client.renderer.chunk.ChunkRenderCache field_212408_i #world -#public net.minecraft.client.renderer.block.model.ModelBakery field_177610_k # blockModelShapes - -#public net.minecraft.client.renderer.block.statemap.BlockStateMapper field_178450_a # blockStateMap - -#public net.minecraft.client.renderer.BufferBuilder field_178999_b # rawIntBuffer -#public net.minecraft.client.renderer.BufferBuilder func_181670_b(I)V # growBuffer -#public net.minecraft.client.renderer.BufferBuilder func_181664_j()I # getBufferSize - -#public net.minecraft.client.renderer.BlockModelRenderer field_187499_a # blockColors - -#public net.minecraft.world.ChunkCache field_72815_e # world \ No newline at end of file +public net.minecraft.client.renderer.texture.AtlasTexture$SheetData field_217808_d # sprites