[WIP] async block texture reloading

This commit is contained in:
octarine-noise
2020-01-05 16:32:45 +01:00
parent b4f18c1e1d
commit c4ee768025
31 changed files with 587 additions and 704 deletions

View File

@@ -1,6 +1,5 @@
package mods.betterfoliage; package mods.betterfoliage;
import net.minecraftforge.fml.ModLoader;
import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.connect.IMixinConnector; import org.spongepowered.asm.mixin.connect.IMixinConnector;

View File

@@ -5,7 +5,6 @@ import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.BlockRendererDispatcher; import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.chunk.ChunkRender; import net.minecraft.client.renderer.chunk.ChunkRender;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IEnviromentBlockReader; import net.minecraft.world.IEnviromentBlockReader;
import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.client.MinecraftForgeClient;

View File

@@ -2,20 +2,12 @@ package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks; import mods.betterfoliage.client.Hooks;
import net.minecraft.block.BlockState; 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.client.renderer.chunk.ChunkRender;
import net.minecraft.util.BlockRenderLayer; 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.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Random;
@Mixin(ChunkRender.class) @Mixin(ChunkRender.class)
public class ChunkRenderVanillaMixin { public class ChunkRenderVanillaMixin {

View File

@@ -1,21 +1,26 @@
package mods.betterfoliage.mixin; package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks; import mods.betterfoliage.client.Hooks;
import mods.octarinecore.client.resource.AsyncSpriteProviderManager;
import net.minecraft.client.renderer.model.ModelBakery; import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.profiler.IProfiler; 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.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ModelBakery.class) @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 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")) @Redirect(method = processLoading, at = @At(value = "INVOKE", target = stitch))
void preStitchTextures(IProfiler profiler, CallbackInfo ci) { AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable<ResourceLocation> idList, IProfiler profiler) {
Hooks.onLoadModelDefinitions(this); return AsyncSpriteProviderManager.INSTANCE.onStitchBlockAtlas(this, atlas, manager, idList, profiler);
} }
} }

View File

@@ -2,17 +2,12 @@ package mods.betterfoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.isAfterPostInit
import mods.octarinecore.client.resource.GeneratorPack import mods.octarinecore.client.resource.GeneratorPack
import net.alexwells.kottle.FMLKotlinModLoadingContext import net.alexwells.kottle.FMLKotlinModLoadingContext
import net.minecraft.client.Minecraft 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.ModLoadingContext
import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.config.ModConfig 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.Level.DEBUG
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.simple.SimpleLogger import org.apache.logging.log4j.simple.SimpleLogger
@@ -23,7 +18,7 @@ import java.util.*
@Mod(BetterFoliage.MOD_ID) @Mod(BetterFoliage.MOD_ID)
object BetterFoliage { object BetterFoliage {
const val MOD_ID = "betterfoliage" const val MOD_ID = ""
const val MOD_NAME = "Better Foliage" const val MOD_NAME = "Better Foliage"
val modBus = FMLKotlinModLoadingContext.get().modEventBus val modBus = FMLKotlinModLoadingContext.get().modEventBus

View File

@@ -12,6 +12,7 @@ import mods.betterfoliage.client.texture.*
import mods.octarinecore.client.gui.textComponent import mods.octarinecore.client.gui.textComponent
import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.resource.CenteringTextureGenerator import mods.octarinecore.client.resource.CenteringTextureGenerator
import mods.octarinecore.client.resource.AsyncSpriteProviderManager
import mods.octarinecore.client.resource.IConfigChangeListener import mods.octarinecore.client.resource.IConfigChangeListener
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
@@ -59,7 +60,6 @@ object Client {
// init other singletons // init other singletons
val singletons = listOf( val singletons = listOf(
BlockConfig, BlockConfig,
StandardCactusRegistry,
LeafParticleRegistry, LeafParticleRegistry,
ChunkOverlayManager, ChunkOverlayManager,
LeafWindTracker, LeafWindTracker,
@@ -76,9 +76,10 @@ object Client {
) )
// add basic block support instances as last // add basic block support instances as last
GrassRegistry.addRegistry(StandardGrassRegistry) AsyncLeafDiscovery.init()
LeafRegistry.addRegistry(StandardLeafRegistry) AsyncGrassDiscovery.init()
LogRegistry.addRegistry(StandardLogRegistry) AsyncLogDiscovery.init()
AsyncCactusDiscovery.init()
configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>() configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>()
configListeners.forEach { it.onConfigChange() } configListeners.forEach { it.onConfigChange() }

View File

@@ -6,12 +6,10 @@ import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.* import mods.betterfoliage.client.render.*
import mods.betterfoliage.loader.Refs
import mods.octarinecore.ThreadLocalDelegate import mods.octarinecore.ThreadLocalDelegate
import mods.octarinecore.client.render.* import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.DefaultLightingCtx import mods.octarinecore.client.render.lighting.DefaultLightingCtx
import mods.octarinecore.client.render.lighting.LightingCtx import mods.octarinecore.client.render.lighting.LightingCtx
import mods.octarinecore.client.resource.LoadModelDataEvent
import mods.octarinecore.common.plus import mods.octarinecore.common.plus
import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.allAvailable
import net.minecraft.block.Block import net.minecraft.block.Block
@@ -35,9 +33,6 @@ import net.minecraft.world.World
import net.minecraftforge.client.model.data.IModelData import net.minecraftforge.client.model.data.IModelData
import java.util.* import java.util.*
var isAfterPostInit = false
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float { fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat(); if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat();
return original 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 { fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty() if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty()
return state.func_215702_a(reader, pos, dir) return state.func_215702_a(reader, pos, dir)

View File

@@ -1,10 +1,9 @@
package mods.betterfoliage.client.chunk package mods.betterfoliage.client.chunk
import mods.betterfoliage.BetterFoliage import mods.octarinecore.ChunkCacheOF
import mods.betterfoliage.client.Client
import mods.betterfoliage.loader.Refs
import mods.octarinecore.client.render.BasicBlockCtx
import mods.octarinecore.client.render.BlockCtx 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.renderer.chunk.ChunkRenderCache
import net.minecraft.client.world.ClientWorld import net.minecraft.client.world.ClientWorld
import net.minecraft.util.math.BlockPos 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.ChunkEvent
import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.event.world.WorldEvent
import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.eventbus.api.SubscribeEvent
import org.apache.logging.log4j.Level
import java.util.* 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 { val IEnviromentBlockReader.dimType: DimensionType get() = when {
this is IWorldReader -> dimension.type this is IWorldReader -> dimension.type
this is ChunkRenderCache -> world.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!") else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
} }
@@ -42,7 +47,6 @@ object ChunkOverlayManager {
var tempCounter = 0 var tempCounter = 0
init { init {
Client.log(Level.INFO, "Initializing client overlay manager")
MinecraftForge.EVENT_BUS.register(this) MinecraftForge.EVENT_BUS.register(this)
} }
@@ -84,13 +88,11 @@ object ChunkOverlayManager {
@SubscribeEvent @SubscribeEvent
fun handleLoadWorld(event: WorldEvent.Load) = (event.world as? ClientWorld)?.let { world -> 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() chunkData[world.dimType] = mutableMapOf()
} }
@SubscribeEvent @SubscribeEvent
fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world -> 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) chunkData.remove(world.dimType)
} }
@@ -121,26 +123,3 @@ class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
val validYRange = 0 until 256 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) {}
}
*/

View File

@@ -2,10 +2,8 @@ package mods.betterfoliage.client.config
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.client.resource.LoadModelDataEvent
import mods.octarinecore.common.config.* import mods.octarinecore.common.config.*
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraftforge.eventbus.api.SubscribeEvent
private fun featureEnable() = boolean(true).lang("enabled") private fun featureEnable() = boolean(true).lang("enabled")
@@ -165,12 +163,4 @@ object BlockConfig {
init { BetterFoliage.modBus.register(this) } init { BetterFoliage.modBus.register(this) }
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(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) } 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()
} }
}
} }

View File

@@ -1,153 +1,133 @@
package mods.betterfoliage.client.integration package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.render.LogRegistry import mods.betterfoliage.client.render.AsyncLogDiscovery
import mods.betterfoliage.client.render.StandardLogRegistry
import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.betterfoliage.client.resource.Identifier
import mods.betterfoliage.client.texture.LeafInfo import mods.betterfoliage.client.texture.LeafInfo
import mods.betterfoliage.client.texture.LeafRegistry import mods.betterfoliage.client.texture.defaultRegisterLeaf
import mods.betterfoliage.client.texture.StandardLeafKey import mods.octarinecore.HasLogger
import mods.betterfoliage.loader.Refs import mods.octarinecore.Map
import mods.octarinecore.client.resource.ModelRenderKey import mods.octarinecore.ResourceLocation
import mods.octarinecore.client.resource.ModelRenderRegistry import mods.octarinecore.String
import mods.octarinecore.client.resource.ModelRenderRegistryBase import mods.octarinecore.client.resource.*
import mods.octarinecore.getTileEntitySafe import mods.octarinecore.metaprog.*
import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.ClassRef.Companion.boolean
import mods.octarinecore.metaprog.FieldRef
import mods.octarinecore.metaprog.MethodRef
import mods.octarinecore.metaprog.allAvailable
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.model.IUnbakedModel import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.client.renderer.model.ModelResourceLocation
import net.minecraft.util.ResourceLocation
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader 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 net.minecraftforge.fml.ModList
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import kotlin.collections.Map import java.util.concurrent.CompletableFuture
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.emptyMap
import kotlin.collections.find val TextureLeaves = ClassRef<Any>("forestry.arboriculture.models.TextureLeaves")
import kotlin.collections.forEach val TextureLeaves_leafTextures = FieldRef(TextureLeaves, "leafTextures", Map)
import kotlin.collections.get val TextureLeaves_plain = FieldRef(TextureLeaves, "plain", ResourceLocation)
import kotlin.collections.listOf val TextureLeaves_fancy = FieldRef(TextureLeaves, "fancy", ResourceLocation)
import kotlin.collections.mapValues val TextureLeaves_pollinatedPlain = FieldRef(TextureLeaves, "pollinatedPlain", ResourceLocation)
import kotlin.collections.mutableMapOf val TextureLeaves_pollinatedFancy = FieldRef(TextureLeaves, "pollinatedFancy", ResourceLocation)
import kotlin.collections.set
val TileLeaves = ClassRef<Any>("forestry.arboriculture.tiles.TileLeaves")
val TileLeaves_getLeaveSprite = MethodRef(TileLeaves, "getLeaveSprite", ResourceLocation, boolean)
val PropertyWoodType = ClassRef<Any>("forestry.arboriculture.blocks.PropertyWoodType")
val IWoodType = ClassRef<Any>("forestry.api.arboriculture.IWoodType")
val IWoodType_barkTex = MethodRef(IWoodType, "getBarkTexture", String)
val IWoodType_heartTex = MethodRef(IWoodType, "getHeartTexture", String)
val PropertyTreeType = ClassRef<Any>("forestry.arboriculture.blocks.PropertyTreeType")
val IAlleleTreeSpecies = ClassRef<Any>("forestry.api.arboriculture.IAlleleTreeSpecies")
val ILeafSpriteProvider = ClassRef<Any>("forestry.api.arboriculture.ILeafSpriteProvider")
val TreeDefinition = ClassRef<Any>("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 { 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 { init {
if (ModList.get().isLoaded("forestry") && allAvailable(TiLgetLeaveSprite, getLeafSpriteProvider, getSprite)) { if (ModList.get().isLoaded("forestry") && allAvailable(TileLeaves_getLeaveSprite, IAlleleTreeSpecies_getLeafSpriteProvider, ILeafSpriteProvider_getSprite)) {
Client.log(Level.INFO, "Forestry support initialized")
LeafRegistry.addRegistry(ForestryLeafRegistry)
LogRegistry.addRegistry(ForestryLogRegistry)
} }
} }
} }
object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> { object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry<LeafInfo> {
val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
val textureToKey = mutableMapOf<ResourceLocation, ModelRenderKey<LeafInfo>>() var idToValue = emptyMap<Identifier, LeafInfo>()
var textureToValue = emptyMap<ResourceLocation, LeafInfo>()
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos): LeafInfo? { override fun get(state: BlockState, world: IBlockReader, pos: BlockPos): LeafInfo? {
// check variant property (used in decorative leaves) // check variant property (used in decorative leaves)
state.values.entries.find { state.values.entries.find {
ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value) PropertyTreeType.isInstance(it.key) && TreeDefinition.isInstance(it.value)
} ?.let { } ?.let {
val species = ForestryIntegration.TdSpecies.get(it.value) val species = it.value[TreeDefinition_species]
val spriteProvider = ForestryIntegration.getLeafSpriteProvider.invoke(species!!) val spriteProvider = species[IAlleleTreeSpecies_getLeafSpriteProvider]()
val textureLoc = ForestryIntegration.getSprite.invoke(spriteProvider!!, false, Minecraft.isFancyGraphicsEnabled()) val textureLoc = spriteProvider[ILeafSpriteProvider_getSprite](false, Minecraft.isFancyGraphicsEnabled())
return textureToValue[textureLoc] return idToValue[textureLoc]
} }
// extract leaf texture information from TileEntity // extract leaf texture information from TileEntity
val tile = world.getTileEntity(pos) ?: return null val tile = world.getTileEntity(pos) ?: return null
if (!ForestryIntegration.TileLeaves.isInstance(tile)) return null if (!TileLeaves.isInstance(tile)) return null
val textureLoc = ForestryIntegration.TiLgetLeaveSprite.invoke(tile, Minecraft.isFancyGraphicsEnabled()) ?: return null val textureLoc = tile[TileLeaves_getLeaveSprite](Minecraft.isFancyGraphicsEnabled())
return textureToValue[textureLoc] return idToValue[textureLoc]
} }
@SubscribeEvent override fun setup(bakeryFuture: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases {
fun handlePreStitch(event: TextureStitchEvent.Pre) { val futures = mutableMapOf<Identifier, CompletableFuture<LeafInfo>>()
textureToValue = emptyMap()
val allLeaves = ForestryIntegration.TeLleafTextures.getStatic() as Map<*, *> return StitchPhases(
allLeaves.entries.forEach { discovery = bakeryFuture.thenRunAsync {
logger.log(Level.DEBUG, "ForestryLeavesSupport: base leaf type ${it.key.toString()}") val allLeaves = TextureLeaves_leafTextures.getStatic()
allLeaves.entries.forEach { (type, leaves) ->
log("base leaf type $type")
leaves!!
listOf( listOf(
ForestryIntegration.TeLplain.get(it.value) as ResourceLocation, leaves[TextureLeaves_plain], leaves[TextureLeaves_pollinatedPlain],
ForestryIntegration.TeLfancy.get(it.value) as ResourceLocation, leaves[TextureLeaves_fancy], leaves[TextureLeaves_pollinatedFancy]
ForestryIntegration.TeLpollplain.get(it.value) as ResourceLocation,
ForestryIntegration.TeLpollfancy.get(it.value) as ResourceLocation
).forEach { textureLocation -> ).forEach { textureLocation ->
val key = StandardLeafKey(logger, textureLocation.toString()).apply { onPreStitch(event) } futures[textureLocation] = defaultRegisterLeaf(textureLocation, atlasFuture)
textureToKey[textureLocation] = key
} }
} }
},
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<ColumnTextureInfo>() { object ForestryLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<ColumnTextureInfo>? {
// respect class list to avoid triggering on fences, stairs, etc. // 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 // find wood type property
val woodType = state.values.entries.find { val woodType = ctx.state.values.entries.find {
ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value) PropertyWoodType.isInstance(it.key) && IWoodType.isInstance(it.value)
} ?: return null }
if (woodType != null) {
logger.log(Level.DEBUG, "ForestryLogRegistry: block state $state") logger.log(Level.DEBUG, "ForestryLogRegistry: block state ${ctx.state}")
logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}") logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}")
// get texture names for wood type // get texture names for wood type
val bark = ForestryIntegration.barkTex.invoke(woodType.value) as String? val bark = woodType.value[IWoodType_barkTex]()
val heart = ForestryIntegration.heartTex.invoke(woodType.value) as String? val heart = woodType.value[IWoodType_heartTex]()
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]") 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 return null
} }
} }

View File

@@ -1,10 +1,8 @@
package mods.betterfoliage.client.integration package mods.betterfoliage.client.integration
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.loader.Refs import mods.octarinecore.*
import mods.octarinecore.ThreadLocalDelegate
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.common.Int3
import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.allAvailable
import mods.octarinecore.metaprog.reflectField import mods.octarinecore.metaprog.reflectField
import net.minecraft.block.BlockState 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.client.renderer.vertex.DefaultVertexFormats
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
/** /**
@@ -22,9 +19,7 @@ import org.apache.logging.log4j.Level
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
object OptifineCustomColors { object OptifineCustomColors {
val isColorAvailable = allAvailable( val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
Refs.CustomColors, Refs.getColorMultiplier
)
init { init {
Client.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }") 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 { fun getBlockColor(ctx: CombinedContext): Int {
val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField<Boolean>("ofCustomColors") == true) { val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
renderEnv.reset(ctx.state, ctx.pos) 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 } else null
return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor
} }
} }
class OptifineRenderEnv { class OptifineRenderEnv {
val wrapped: Any = Refs.RenderEnv.element!!.getDeclaredConstructor( val wrapped: Any = RenderEnv.element!!.getDeclaredConstructor(BlockState.element, BlockPos.element).let {
Refs.BlockState.element, Refs.BlockPos.element
).let {
it.isAccessible = true it.isAccessible = true
it.newInstance(null, null) it.newInstance(null, null)
} }
fun reset(state: BlockState, pos: BlockPos) { fun reset(state: BlockState, pos: BlockPos) {
Refs.RenderEnv_reset.invoke(wrapped, state, pos) RenderEnv.reset.invoke(wrapped, state, pos)
} }
} }

View File

@@ -8,15 +8,11 @@ import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.Quad import mods.octarinecore.client.render.Quad
import mods.octarinecore.client.render.lighting.QuadIconResolver import mods.octarinecore.client.render.lighting.QuadIconResolver
import mods.octarinecore.client.render.lighting.LightingCtx
import mods.octarinecore.client.resource.* import mods.octarinecore.client.resource.*
import mods.octarinecore.common.rotate import mods.octarinecore.common.rotate
import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.ClassRef
import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.allAvailable
import net.minecraft.block.BlockState
import net.minecraft.client.renderer.model.BlockModel 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.AtlasTexture
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.util.Direction import net.minecraft.util.Direction
@@ -24,29 +20,31 @@ import net.minecraft.util.Direction.*
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraftforge.fml.ModList import net.minecraftforge.fml.ModList
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Level.DEBUG
import org.apache.logging.log4j.Logger import org.apache.logging.log4j.Logger
import java.util.concurrent.CompletableFuture
object IC2RubberIntegration { object IC2RubberIntegration {
val BlockRubWood = ClassRef("ic2.core.block.BlockRubWood") val BlockRubWood = ClassRef<Any>("ic2.core.block.BlockRubWood")
init { init {
if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) { if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) {
Client.log(Level.INFO, "IC2 rubber support initialized") Client.log(Level.INFO, "IC2 rubber support initialized")
LogRegistry.addRegistry(IC2LogSupport) LogRegistry.registries.add(IC2LogDiscovery)
AsyncSpriteProviderManager.providers.add(IC2LogDiscovery)
} }
} }
} }
object TechRebornRubberIntegration { object TechRebornRubberIntegration {
val BlockRubberLog = ClassRef("techreborn.blocks.BlockRubberLog") val BlockRubberLog = ClassRef<Any>("techreborn.blocks.BlockRubberLog")
init { init {
if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) { if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) {
Client.log(Level.INFO, "TechReborn rubber support initialized") 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] this.sideTextures[sideIdx]
} }
} }
class Key(override val logger: Logger, val axis: Axis?, val spotDir: Direction, val textures: List<String>): ModelRenderKey<ColumnTextureInfo> {
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<ColumnTextureInfo>() { object IC2LogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<ColumnTextureInfo>? { override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// check for proper block class, existence of ModelBlock, and "state" blockstate property // check for proper block class, existence of ModelBlock, and "state" blockstate property
if (!IC2RubberIntegration.BlockRubWood.isInstance(state.block)) return null if (!IC2RubberIntegration.BlockRubWood.isInstance(ctx.state.block)) return null
val blockLoc = models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null val blockLoc = ctx.models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
val type = state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null val type = ctx.state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
// logs with no rubber spot // logs with no rubber spot
if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) { if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) {
@@ -97,11 +84,15 @@ object IC2LogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
"plain_z" -> Axis.Z "plain_z" -> Axis.Z
else -> null 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 if (textureNames.any { it == "missingno" }) return null
logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}") log("IC2LogSupport: block state ${ctx.state.toString()}")
logger.log(DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}") log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}")
return SimpleColumnInfo.Key(logger, axis, textureNames) 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 // logs with rubber spot
@@ -114,32 +105,53 @@ object IC2LogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
} }
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) } val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) }
if (textureNames.any { it == "missingno" }) return null if (textureNames.any { it == "missingno" }) return null
logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}") log("IC2LogSupport: block state ${ctx.state.toString()}")
logger.log(DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}") log("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) 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<ColumnTextureInfo>() { object TechRebornLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<ColumnTextureInfo>? { override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// check for proper block class, existence of ModelBlock // check for proper block class, existence of ModelBlock
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(state.block)) return null if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(ctx.state.block)) return null
val blockLoc = models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null val blockLoc = ctx.models.map { it as? Pair<BlockModel, ResourceLocation> }.firstOrNull() ?: return null
val hasSap = state.values.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null val hasSap = ctx.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 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) { if (hasSap) {
val textureNames = listOf("end", "end", "sapside", "side").map { blockLoc.first.resolveTextureName(it) } val textureNames = listOf("end", "side", "sapside").map { blockLoc.first.resolveTextureName(it) }
logger.log(DEBUG, "$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}") log("$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) 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 { } else {
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) } val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) }
logger.log(DEBUG, "$logName: end=${textureNames[0]}, side=${textureNames[2]}") log("$logName: end=${textureNames[0]}, side=${textureNames[1]}")
if (textureNames.all { it != "missingno" })return SimpleColumnInfo.Key(logger, Axis.Y, textureNames) 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 return null
} }

View File

@@ -3,9 +3,10 @@ package mods.betterfoliage.client.integration
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.loader.Refs import mods.octarinecore.SVertexBuilder
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.allAvailable
import mods.octarinecore.metaprog.get
import net.minecraft.block.BlockRenderType import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockRenderType.MODEL import net.minecraft.block.BlockRenderType.MODEL
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
@@ -20,7 +21,7 @@ import org.apache.logging.log4j.Level.INFO
*/ */
object ShadersModIntegration { 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 grassDefaultBlockId = 31L
val leavesDefaultBlockId = 18L val leavesDefaultBlockId = 18L
@@ -42,10 +43,10 @@ object ShadersModIntegration {
/** Quads rendered inside this block will use the given block entity data in shader programs. */ /** 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) { inline fun renderAs(blockId: Long, renderType: BlockRenderType, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) {
if ((isAvailable && enabled)) { if ((isAvailable && enabled)) {
val vertexBuilder = Refs.sVertexBuilder.get(renderer)!! val vertexBuilder = renderer[mods.octarinecore.BufferBuilder.sVertexBuilder]!!
Refs.pushEntity_num.invoke(vertexBuilder, blockId) SVertexBuilder.pushNum.invoke(vertexBuilder, blockId)
func() func()
Refs.popEntity.invoke(vertexBuilder) SVertexBuilder.pop.invoke(vertexBuilder)
} else { } else {
func() func()
} }

View File

@@ -5,30 +5,39 @@ import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.* import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.* import mods.octarinecore.client.render.lighting.*
import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable import mods.octarinecore.client.resource.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation import mods.octarinecore.common.Rotation
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.common.config.SimpleBlockMatcher import mods.octarinecore.common.config.SimpleBlockMatcher
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.CactusBlock 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.Direction.*
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraftforge.client.model.data.IModelData
import org.apache.logging.log4j.Level.DEBUG import org.apache.logging.log4j.Level.DEBUG
import java.util.* import java.util.concurrent.CompletableFuture
object StandardCactusRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() { object AsyncCactusDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java) override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java)
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side")) override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
override fun processModel(state: BlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, Axis.Y, textures) override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
init { BetterFoliage.modBus.register(this) } 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) { 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 = override fun isEligible(ctx: CombinedContext): Boolean =
Config.enabled && Config.cactus.enabled && Config.enabled && Config.cactus.enabled &&
StandardCactusRegistry[ctx] != null AsyncCactusDiscovery[ctx] != null
override val onlyOnCutout get() = true override val onlyOnCutout get() = true
override fun render(ctx: CombinedContext) { override fun render(ctx: CombinedContext) {
val icons = StandardCactusRegistry[ctx]!! val icons = AsyncCactusDiscovery[ctx]!!
ctx.render( ctx.render(
modelStem.model, modelStem.model,

View File

@@ -8,18 +8,17 @@ import mods.betterfoliage.client.render.column.AbstractRenderColumn
import mods.betterfoliage.client.render.column.ColumnRenderLayer import mods.betterfoliage.client.render.column.ColumnRenderLayer
import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo 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.render.CombinedContext
import mods.octarinecore.client.resource.ModelRenderRegistry import mods.octarinecore.client.resource.*
import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable
import mods.octarinecore.client.resource.ModelRenderRegistryRoot
import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.tryDefault import mods.octarinecore.tryDefault
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.LogBlock import net.minecraft.block.LogBlock
import net.minecraft.util.Direction.Axis 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) { class RenderLog : AbstractRenderColumn(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
@@ -49,12 +48,24 @@ class RoundLogOverlayLayer : ColumnRenderLayer() {
object LogRegistry : ModelRenderRegistryRoot<ColumnTextureInfo>() object LogRegistry : ModelRenderRegistryRoot<ColumnTextureInfo>()
object StandardLogRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() { object AsyncLogDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks
override val modelTextures: List<ModelTextureList> get() = BlockConfig.logModels.modelList override val modelTextures: List<ModelTextureList> get() = BlockConfig.logModels.modelList
override fun processModel(state: BlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, getAxis(state), textures)
init { BetterFoliage.modBus.register(this) } override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo> {
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? { fun getAxis(state: BlockState): Axis? {
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?: val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
@@ -66,4 +77,9 @@ object StandardLogRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>(
else -> null else -> null
} }
} }
fun init() {
LogRegistry.registries.add(this)
AsyncSpriteProviderManager.providers.add(this)
}
} }

View File

@@ -1,14 +1,9 @@
package mods.betterfoliage.client.render.column package mods.betterfoliage.client.render.column
import mods.octarinecore.client.render.lighting.QuadIconResolver 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 mods.octarinecore.common.rotate
import net.minecraft.client.renderer.texture.AtlasTexture
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.util.Direction.* import net.minecraft.util.Direction.*
import org.apache.logging.log4j.Logger
interface ColumnTextureInfo { interface ColumnTextureInfo {
val axis: Axis? 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 val sideIdx = if (sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size else 0
sideTextures[sideIdx] sideTextures[sideIdx]
} }
class Key(override val logger: Logger, val axis: Axis?, val textures: List<String>) : ModelRenderKey<ColumnTextureInfo> {
override fun resolveSprites(atlas: AtlasTexture) = SimpleColumnInfo(
axis,
atlas[textures[0]] ?: missingSprite,
atlas[textures[1]] ?: missingSprite,
textures.drop(2).map { atlas[it] ?: missingSprite }
)
}
} }

View File

@@ -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.model.ModelResourceLocation
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite

View File

@@ -3,16 +3,16 @@ package mods.betterfoliage.client.texture
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.lighting.HSB import mods.octarinecore.client.render.lighting.HSB
import mods.octarinecore.client.resource.* import mods.octarinecore.client.resource.*
import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.renderer.texture.AtlasTexture
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.lang.Math.min import java.lang.Math.min
import java.util.concurrent.CompletableFuture
const val defaultGrassColor = 0 const val defaultGrassColor = 0
@@ -32,20 +32,20 @@ class GrassInfo(
object GrassRegistry : ModelRenderRegistryRoot<GrassInfo>() object GrassRegistry : ModelRenderRegistryRoot<GrassInfo>()
object StandardGrassRegistry : ModelRenderRegistryConfigurable<GrassInfo>() { object AsyncGrassDiscovery : ConfigurableModelDiscovery<GrassInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList
override fun processModel(state: BlockState, textures: List<String>) = StandardGrassKey(logger, textures[0])
init { BetterFoliage.modBus.register(this) }
}
class StandardGrassKey(override val logger: Logger, val textureName: String) : ModelRenderKey<GrassInfo> { override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<GrassInfo> {
override fun resolveSprites(atlas: AtlasTexture): GrassInfo { val textureName = textures[0]
val logName = "StandardGrassKey" val spriteF = atlas.sprite(Identifier(textureName))
val texture = atlas[textureName] ?: missingSprite
logger.log(Level.DEBUG, "$logName: texture $textureName") logger.log(Level.DEBUG, "$logName: texture $textureName")
val hsb = HSB.fromColor(texture.averageColor ?: defaultGrassColor) 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) { val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}") logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}")
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color") logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color")
@@ -54,6 +54,12 @@ class StandardGrassKey(override val logger: Logger, val textureName: String) : M
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color") logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color")
null null
} }
return GrassInfo(texture, overrideColor) GrassInfo(sprite, overrideColor)
}
}
fun init() {
GrassRegistry.registries.add(this)
AsyncSpriteProviderManager.providers.add(this)
} }
} }

View File

@@ -3,16 +3,14 @@ package mods.betterfoliage.client.texture
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.HasLogger
import mods.octarinecore.client.resource.* import mods.octarinecore.client.resource.*
import mods.octarinecore.common.config.ConfigurableBlockMatcher import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.renderer.texture.AtlasTexture import java.util.concurrent.CompletableFuture
import net.minecraft.util.ResourceLocation
import net.minecraftforge.client.event.TextureStitchEvent
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
const val defaultLeafColor = 0 const val defaultLeafColor = 0
@@ -25,7 +23,7 @@ class LeafInfo(
val leafType: String, val leafType: String,
/** Average color of the round leaf texture. */ /** 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. */ /** [IconSet] of the textures to use for leaf particles emitted from this block. */
val particleTextures: IconSet get() = LeafParticleRegistry[leafType] val particleTextures: IconSet get() = LeafParticleRegistry[leafType]
@@ -33,27 +31,27 @@ class LeafInfo(
object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>() object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>()
object StandardLeafRegistry : ModelRenderRegistryConfigurable<LeafInfo>() { object AsyncLeafDiscovery : ConfigurableModelDiscovery<LeafInfo>() {
override val logger = BetterFoliage.logDetail override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList
override fun processModel(state: BlockState, textures: List<String>) = StandardLeafKey(logger, textures[0])
init { BetterFoliage.modBus.register(this) }
}
class StandardLeafKey(override val logger: Logger, val textureName: String) : ModelRenderKey<LeafInfo> { override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture) = defaultRegisterLeaf(Identifier(textures[0]), atlas)
lateinit var leafType: String
lateinit var generated: ResourceLocation
override fun onPreStitch(event: TextureStitchEvent.Pre) { fun init() {
val logName = "StandardLeafKey" LeafRegistry.registries.add(this)
leafType = LeafParticleRegistry.typeMappings.getType(textureName) ?: "default" AsyncSpriteProviderManager.providers.add(this)
generated = Client.genLeaves.register(ResourceLocation(textureName), leafType) }
event.addSprite(generated) }
logger.log(Level.DEBUG, "$logName: leaf texture $textureName") fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture<LeafInfo> {
logger.log(Level.DEBUG, "$logName: particle $leafType") val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default"
val generated = Client.genLeaves.register(sprite, leafType)
val roundLeaf = atlas.sprite(generated)
log(" leaf texture $sprite")
log(" particle $leafType")
return atlas.afterStitch {
LeafInfo(roundLeaf.get(), leafType)
} }
override fun resolveSprites(atlas: AtlasTexture) = LeafInfo(atlas[generated] ?: missingSprite, leafType)
} }

View File

@@ -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!")
}
}
}
*/

View File

@@ -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)
}

View File

@@ -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<String>("java.lang.String")
val Map = ClassRef<Map<*, *>>("java.util.Map")
val List = ClassRef<List<*>>("java.util.List")
val Random = ClassRef<Random>("java.util.Random")
// Minecraft
val IBlockReader = ClassRef<IBlockReader>("net.minecraft.world.IBlockReader")
val IEnvironmentBlockReader = ClassRef<IEnviromentBlockReader>("net.minecraft.world.IEnvironmentBlockReader")
val BlockState = ClassRef<BlockState>("net.minecraft.block.state.BlockState")
val BlockPos = ClassRef<BlockPos>("net.minecraft.util.math.BlockPos")
val BlockRenderLayer = ClassRef<BlockRenderLayer>("net.minecraft.util.BlockRenderLayer")
val Block = ClassRef<Block>("net.minecraft.block.Block")
object BufferBuilder : ClassRef<BufferBuilder>("net.minecraft.client.renderer.BufferBuilder") {
/** Optifine only */
val sVertexBuilder = FieldRef(this, "sVertexBuilder", SVertexBuilder)
/** Optifine only */
val quadSprite = FieldRef(this, "quadSprite", TextureAtlasSprite)
}
val BlockRendererDispatcher = ClassRef<BlockRendererDispatcher>("net.minecraft.client.renderer.BlockRendererDispatcher")
val ChunkRenderCache = ClassRef<ChunkRenderCache>("net.minecraft.client.renderer.chunk.ChunkRenderCache")
val TextureAtlasSprite = ClassRef<TextureAtlasSprite>("net.minecraft.client.renderer.texture.TextureAtlasSprite")
val ResourceLocation = ClassRef<ResourceLocation>("net.minecraft.util.ResourceLocation")
// Optifine
val OptifineClassTransformer = ClassRef<Any>("optifine.OptiFineClassTransformer")
val BlockPosM = ClassRef<Any>("net.optifine.BlockPosM")
object ChunkCacheOF : ClassRef<Any>("net.optifine.override.ChunkCacheOF") {
val chunkCache = FieldRef(this, "chunkCache", ChunkRenderCache)
}
object RenderEnv : ClassRef<Any>("net.optifine.render.RenderEnv") {
val reset = MethodRef(this, "reset", void, BlockState, BlockPos)
}
// Optifine custom colors
val IColorizer = ClassRef<Any>("net.optifine.CustomColors\$IColorizer")
object CustomColors : ClassRef<Any>("net.optifine.CustomColors") {
val getColorMultiplier = MethodRef(this, "getSmoothColorMultiplier", int, BlockState, IEnvironmentBlockReader, BlockPos, IColorizer, BlockPosM)
}
// Optifine shaders
object SVertexBuilder : ClassRef<Any>("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)
}

View File

@@ -2,11 +2,12 @@
@file:Suppress("NOTHING_TO_INLINE") @file:Suppress("NOTHING_TO_INLINE")
package mods.octarinecore package mods.octarinecore
import mods.betterfoliage.loader.Refs
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.* import net.minecraft.world.*
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import java.lang.Math.* import java.lang.Math.*
@@ -119,3 +120,10 @@ fun nextPowerOf2(x: Int): Int {
fun IWorldReader.getTileEntitySafe(pos: BlockPos): TileEntity? = tryDefault(null as TileEntity?) { fun IWorldReader.getTileEntitySafe(pos: BlockPos): TileEntity? = tryDefault(null as TileEntity?) {
if (isBlockLoaded(pos)) getTileEntity(pos) else null 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")
}

View File

@@ -2,23 +2,21 @@ package mods.octarinecore.client.render
import mods.betterfoliage.client.render.canRenderInCutout import mods.betterfoliage.client.render.canRenderInCutout
import mods.betterfoliage.client.render.isCutout import mods.betterfoliage.client.render.isCutout
import mods.betterfoliage.loader.Refs import mods.octarinecore.BufferBuilder
import mods.octarinecore.client.render.lighting.* import mods.octarinecore.client.render.lighting.*
import mods.octarinecore.common.Double3 import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation import mods.octarinecore.common.Rotation
import mods.octarinecore.common.plus import mods.octarinecore.common.plus
import mods.octarinecore.metaprog.get
import mods.octarinecore.metaprog.set
import net.minecraft.block.Blocks 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.fluid.Fluids
import net.minecraft.util.BlockRenderLayer
import net.minecraft.util.Direction import net.minecraft.util.Direction
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IEnviromentBlockReader import net.minecraft.world.IEnviromentBlockReader
import net.minecraft.world.LightType import net.minecraft.world.LightType
import net.minecraft.world.biome.Biomes import net.minecraft.world.biome.Biomes
import java.util.*
class CombinedContext( class CombinedContext(
val blockCtx: BlockCtx, val renderCtx: RenderCtx, val lightingCtx: DefaultLightingCtx val blockCtx: BlockCtx, val renderCtx: RenderCtx, val lightingCtx: DefaultLightingCtx
@@ -64,7 +62,7 @@ class CombinedContext(
if (drawIcon != null) { if (drawIcon != null) {
// let OptiFine know the texture we're using, so it can // let OptiFine know the texture we're using, so it can
// transform UV coordinates to quad-relative // transform UV coordinates to quad-relative
Refs.quadSprite.set(renderCtx!!.renderBuffer, drawIcon) renderCtx.renderBuffer[BufferBuilder.quadSprite] = drawIcon
quad.verts.forEachIndexed { vertIdx, vert -> quad.verts.forEachIndexed { vertIdx, vert ->
temp.init(vert).rotate(lightingCtx.modelRotation).translate(translation) temp.init(vert).rotate(lightingCtx.modelRotation).translate(translation)

View File

@@ -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<Void>,
val cleanup: CompletableFuture<Void>
)
interface AsyncSpriteProvider {
fun setup(bakeryFuture: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases
}
object AsyncSpriteProviderManager {
val providers = mutableListOf<ModelDiscovery<*>>()
fun onStitchBlockAtlas(bakeryObj: Any, atlas: AtlasTexture, manager: IResourceManager, idList: Iterable<Identifier>, profiler: IProfiler): AtlasTexture.SheetData {
profiler.startSection("additional-sprites")
val bakery = CompletableFuture<ModelBakery>()
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<Identifier>) {
val idSet = Collections.synchronizedSet(mutableSetOf<Identifier>().apply { addAll(initial) })
val sheet = CompletableFuture<AtlasTexture.SheetData>()
fun sprite(id: String) = sprite(Identifier(id))
fun sprite(id: Identifier): CompletableFuture<Sprite> {
idSet.add(id)
return sheet.thenApply { sheetData -> sheetData.sprites.find { it.name == id } ?: throw IllegalStateException("Atlas does not contain $id") }.toCompletableFuture()
}
fun <T> afterStitch(supplier: ()->T): CompletableFuture<T> = sheet.thenApplyAsync(Function { supplier() }, Minecraft.getInstance())
}
fun completedVoid() = CompletableFuture.completedFuture<Void>(null)
fun <T> CompletableFuture<T>.thenRunAsync(run: (T)->Unit) = thenAcceptAsync(Consumer(run), Minecraft.getInstance()).toCompletableFuture()!!
fun Collection<CompletableFuture<*>>.allComplete() = CompletableFuture.allOf(*toTypedArray())

View File

@@ -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<T> {
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<T> : ModelRenderRegistry<T> {
val registries = mutableListOf<ModelRenderRegistry<T>>()
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<IUnbakedModel, ResourceLocation>): List<Pair<IUnbakedModel, ResourceLocation>> = 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<T> : HasLogger, AsyncSpriteProvider, ModelRenderRegistry<T> {
var modelData: Map<BlockState, T> = emptyMap()
protected set
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = modelData[state]
abstract fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<T>?
override fun setup(bakeryFuture: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases {
val modelDataTemp = mutableMapOf<BlockState, CompletableFuture<T>>()
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<T> : ModelDiscovery<T>() {
abstract val matchClasses: IBlockMatcher
abstract val modelTextures: List<ModelTextureList>
abstract fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<T>?
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<T>? {
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<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases {
(matchClasses as? ConfigurableBlockMatcher)?.readDefaults()
return super.setup(bakeryFuture, atlasFuture)
}
}

View File

@@ -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<T> {
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<T> {
fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<T>?
}
interface ModelRenderKey<T> {
val logger: Logger?
fun onPreStitch(event: TextureStitchEvent.Pre) {}
fun resolveSprites(atlas: AtlasTexture): T
}
abstract class ModelRenderRegistryRoot<T> : ModelRenderRegistry<T> {
val subRegistries = mutableListOf<ModelRenderRegistry<T>>()
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos) = subRegistries.findFirst { it[state, world, pos] }
fun addRegistry(registry: ModelRenderRegistry<T>) {
subRegistries.add(registry)
}
}
abstract class ModelRenderRegistryBase<T> : ModelRenderRegistry<T>, ModelRenderDataExtractor<T> {
open val logger: Logger? = null
open val logName: String get() = this::class.java.name
val stateToKey = mutableMapOf<BlockState, ModelRenderKey<T>>()
var stateToValue = mapOf<BlockState, T>()
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<ModelResourceLocation, IUnbakedModel>
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<T> : ModelRenderRegistryBase<T>() {
abstract val matchClasses: IBlockMatcher
abstract val modelTextures: List<ModelTextureList>
override fun processModel(state: BlockState, modelLoc: ModelResourceLocation, models: List<Pair<IUnbakedModel, ResourceLocation>>): ModelRenderKey<T>? {
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<String>) : ModelRenderKey<T>?
}
fun ModelBakery.unwrapVariants(modelAndLoc: Pair<IUnbakedModel, ResourceLocation>): List<Pair<IUnbakedModel, ResourceLocation>> = when(val model = modelAndLoc.first) {
is VariantList -> model.variantList.flatMap { variant -> unwrapVariants(getUnbakedModel(variant.modelLocation) to variant.modelLocation) }
is BlockModel -> listOf(modelAndLoc)
else -> emptyList()
}

View File

@@ -1,10 +1,10 @@
package mods.octarinecore.client.resource 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.client.render.Model
import mods.octarinecore.common.Double3 import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import mods.octarinecore.resource.Identifier
import mods.octarinecore.resource.Sprite
import mods.octarinecore.stripEnd import mods.octarinecore.stripEnd
import mods.octarinecore.stripStart import mods.octarinecore.stripStart
import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.texture.AtlasTexture
@@ -63,10 +63,10 @@ open class ResourceHandler(
// ============================ // ============================
// Resource declarations // 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(location: Identifier) = iconStatic { location }
fun iconStatic(domain: String, path: String) = iconStatic(Identifier(domain, path)) 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 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 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) } fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) }
@@ -102,7 +102,7 @@ open class ResourceHandler(
// ============================ // ============================
// Resource container classes // Resource container classes
// ============================ // ============================
class IconHolder(val location: ()->Identifier) : IStitchListener { class IconHolder(val location: ()-> Identifier) : IStitchListener {
var iconRes: Identifier? = null var iconRes: Identifier? = null
var icon: Sprite? = null var icon: Sprite? = null
override fun onPreStitch(event: TextureStitchEvent.Pre) { override fun onPreStitch(event: TextureStitchEvent.Pre) {
@@ -119,7 +119,7 @@ class ModelHolder(val init: Model.()->Unit): IConfigChangeListener {
override fun onConfigChange() { model = Model().apply(init) } 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<Identifier>(16) val resources = arrayOfNulls<Identifier>(16)
val icons = arrayOfNulls<Sprite>(16) val icons = arrayOfNulls<Sprite>(16)
var num = 0 var num = 0

View File

@@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.lang.Math.* import java.lang.Math.*
import javax.imageio.ImageIO import javax.imageio.ImageIO
import kotlin.math.atan2
/** Concise getter for the Minecraft resource manager. */ /** Concise getter for the Minecraft resource manager. */
val resourceManager: SimpleReloadableResourceManager 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), * 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. * and the result transformed back to the RGB color space.
*/ */
val TextureAtlasSprite.averageColor: Int? get() { 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
var numOpaque = 0 var numOpaque = 0
var sumHueX = 0.0 var sumHueX = 0.0
var sumHueY = 0.0 var sumHueY = 0.0
var sumSaturation = 0.0f var sumSaturation = 0.0f
var sumBrightness = 0.0f var sumBrightness = 0.0f
for (x in 0..width - 1) for (x in 0 until width)
for (y in 0..height - 1) { for (y in 0 until height) {
val pixel = getPixelRGBA(0, x, y); val pixel = getPixelRGBA(0, x, y)
val alpha = (pixel shr 24) and 255 val alpha = (pixel shr 24) and 255
val hsb = HSB.fromColor(pixel) val hsb = HSB.fromColor(pixel)
if (alpha == 255) { if (alpha == 255) {
@@ -90,7 +87,7 @@ val TextureAtlasSprite.averageColor: Int? get() {
} }
// circular average - transform sum vector to polar angle // 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 return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat()).asColor
} }

View File

@@ -5,6 +5,8 @@ import java.lang.reflect.Field
import java.lang.reflect.Method import java.lang.reflect.Method
import mods.octarinecore.metaprog.Namespace.* import mods.octarinecore.metaprog.Namespace.*
import mods.octarinecore.tryDefault import mods.octarinecore.tryDefault
import kotlin.reflect.KCallable
import kotlin.reflect.KFunction
/** Get a Java class with the given name. */ /** Get a Java class with the given name. */
fun getJavaClass(name: String) = tryDefault(null) { Class.forName(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 * @param[name] MCP name of the class
*/ */
open class ClassRef(val name: String) : Resolvable<Class<*>>() { open class ClassRef<T: Any?>(val name: String) : Resolvable<Class<T>>() {
companion object { companion object {
val int = ClassRefPrimitive("I", Int::class.java) val int = ClassRefPrimitive("I", Int::class.java)
val long = ClassRefPrimitive("J", Long::class.java) val long = ClassRefPrimitive("J", Long::class.java)
val float = ClassRefPrimitive("F", Float::class.java) val float = ClassRefPrimitive("F", Float::class.java)
val boolean = ClassRefPrimitive("Z", Boolean::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(".", "/")};" open fun asmDescriptor(namespace: Namespace) = "L${name.replace(".", "/")};"
override fun resolve() = getJavaClass(name) override fun resolve() = getJavaClass(name) as Class<T>?
fun isInstance(obj: Any) = element?.isInstance(obj) ?: false fun isInstance(obj: Any) = element?.isInstance(obj) ?: false
} }
@@ -89,18 +91,11 @@ open class ClassRef(val name: String) : Resolvable<Class<*>>() {
* @param[name] ASM descriptor of this primitive type * @param[name] ASM descriptor of this primitive type
* @param[clazz] class of this primitive type * @param[clazz] class of this primitive type
*/ */
class ClassRefPrimitive(name: String, val clazz: Class<*>?) : ClassRef(name) { class ClassRefPrimitive<T>(name: String, val clazz: Class<T>?) : ClassRef<T>(name) {
override fun asmDescriptor(namespace: Namespace) = name override fun asmDescriptor(namespace: Namespace) = name
override fun resolve() = clazz 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. * Reference to a method.
* *
@@ -110,13 +105,13 @@ fun ClassRef.array() = ClassRefArray(name)
* @param[returnType] reference to the return type * @param[returnType] reference to the return type
* @param[returnType] references to the argument types * @param[returnType] references to the argument types
*/ */
class MethodRef(val parentClass: ClassRef, class MethodRef<T: Any?>(val parentClass: ClassRef<*>,
val mcpName: String, val mcpName: String,
val srgName: String, val srgName: String,
val returnType: ClassRef, val returnType: ClassRef<T>,
vararg val argTypes: ClassRef vararg val argTypes: ClassRef<*>
) : Resolvable<Method>() { ) : Resolvable<Method>() {
constructor(parentClass: ClassRef, mcpName: String, returnType: ClassRef, vararg argTypes: ClassRef) : constructor(parentClass: ClassRef<*>, mcpName: String, returnType: ClassRef<T>, vararg argTypes: ClassRef<*>) :
this(parentClass, mcpName, mcpName, returnType, *argTypes) this(parentClass, mcpName, mcpName, returnType, *argTypes)
fun name(namespace: Namespace) = when(namespace) { SRG -> srgName; MCP -> mcpName } fun name(namespace: Namespace) = when(namespace) { SRG -> srgName; MCP -> mcpName }
@@ -133,11 +128,10 @@ class MethodRef(val parentClass: ClassRef,
} }
/** Invoke this method using reflection. */ /** 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. */ /** Invoke this static method using reflection. */
fun invokeStatic(vararg args: Any) = element?.invoke(null, *args) 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[srgName] SRG name of the field
* @param[type] reference to the field type * @param[type] reference to the field type
*/ */
class FieldRef(val parentClass: ClassRef, class FieldRef<T>(val parentClass: ClassRef<*>,
val mcpName: String, val mcpName: String,
val srgName: String, val srgName: String,
val type: ClassRef? val type: ClassRef<T>
) : Resolvable<Field>() { ) : Resolvable<Field>() {
constructor(parentClass: ClassRef, mcpName: String, type: ClassRef?) : this(parentClass, mcpName, mcpName, type) constructor(parentClass: ClassRef<*>, mcpName: String, type: ClassRef<T>) : this(parentClass, mcpName, mcpName, type)
fun name(namespace: Namespace) = when(namespace) { SRG -> srgName; MCP -> mcpName } 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? = override fun resolve(): Field? =
if (parentClass.element == null) null if (parentClass.element == null) null
else { else {
listOf(srgName, mcpName).map { tryDefault(null) { listOf(srgName, mcpName).mapNotNull { tryDefault(null) {
parentClass.element!!.getDeclaredField(it) parentClass.element!!.getDeclaredField(it)
}}.filterNotNull().firstOrNull() } }.firstOrNull()
?.apply{ isAccessible = true } ?.apply{ isAccessible = true }
} }
/** Get this field using reflection. */ /** 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. */ /** Get this static field using reflection. */
fun getStatic() = get(null) fun getStatic() = get(null)
fun set(receiver: Any?, obj: Any?) { element?.set(receiver, obj) } fun set(receiver: Any?, obj: Any?) { element?.set(receiver, obj) }
} }
fun Any.isInstance(cls: ClassRef<*>) = cls.isInstance(this)
interface ReflectionCallable<T> {
operator fun invoke(vararg args: Any): T
}
inline operator fun <reified T> Any.get(field: FieldRef<T>) = field.get(this)
inline operator fun <reified T> Any.set(field: FieldRef<T>, value: T) = field.set(this, value)
inline operator fun <T> Any.get(methodRef: MethodRef<T>) = object : ReflectionCallable<T> {
override fun invoke(vararg args: Any) = methodRef.invoke(this@get, args)
}

View File

@@ -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.chunk.ChunkRenderCache field_212408_i #world
#public net.minecraft.client.renderer.block.model.ModelBakery field_177610_k # blockModelShapes public net.minecraft.client.renderer.texture.AtlasTexture$SheetData field_217808_d # sprites
#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