[WIP] async block texture reloading
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package mods.betterfoliage;
|
||||
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import org.spongepowered.asm.mixin.Mixins;
|
||||
import org.spongepowered.asm.mixin.connect.IMixinConnector;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRender;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IEnviromentBlockReader;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
|
||||
@@ -2,20 +2,12 @@ package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.client.Hooks;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRender;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IEnviromentBlockReader;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Mixin(ChunkRender.class)
|
||||
public class ChunkRenderVanillaMixin {
|
||||
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
package mods.betterfoliage.mixin;
|
||||
|
||||
import mods.betterfoliage.client.Hooks;
|
||||
import mods.octarinecore.client.resource.AsyncSpriteProviderManager;
|
||||
import net.minecraft.client.renderer.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
public class ModelBakeryMixin {
|
||||
abstract public class ModelBakeryMixin {
|
||||
|
||||
private static final String processLoading = "Lnet/minecraft/client/renderer/model/ModelBakery;processLoading(Lnet/minecraft/profiler/IProfiler;)V";
|
||||
private static final String endStartSection = "Lnet/minecraft/profiler/IProfiler;endStartSection(Ljava/lang/String;)V";
|
||||
private static final String stitch = "Lnet/minecraft/client/renderer/texture/AtlasTexture;stitch(Lnet/minecraft/resources/IResourceManager;Ljava/lang/Iterable;Lnet/minecraft/profiler/IProfiler;)Lnet/minecraft/client/renderer/texture/AtlasTexture$SheetData;";
|
||||
|
||||
@Inject(method = processLoading, at = @At(value = "INVOKE_STRING", target = endStartSection, args = "ldc=stitching"))
|
||||
void preStitchTextures(IProfiler profiler, CallbackInfo ci) {
|
||||
Hooks.onLoadModelDefinitions(this);
|
||||
@Redirect(method = processLoading, at = @At(value = "INVOKE", target = stitch))
|
||||
AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable<ResourceLocation> idList, IProfiler profiler) {
|
||||
return AsyncSpriteProviderManager.INSTANCE.onStitchBlockAtlas(this, atlas, manager, idList, profiler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,12 @@ package mods.betterfoliage
|
||||
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.betterfoliage.client.isAfterPostInit
|
||||
import mods.octarinecore.client.resource.GeneratorPack
|
||||
import net.alexwells.kottle.FMLKotlinModLoadingContext
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraftforge.eventbus.api.IEventBus
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.ModLoadingContext
|
||||
import net.minecraftforge.fml.common.Mod
|
||||
import net.minecraftforge.fml.config.ModConfig
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent
|
||||
import org.apache.logging.log4j.Level.DEBUG
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.simple.SimpleLogger
|
||||
@@ -23,7 +18,7 @@ import java.util.*
|
||||
|
||||
@Mod(BetterFoliage.MOD_ID)
|
||||
object BetterFoliage {
|
||||
const val MOD_ID = "betterfoliage"
|
||||
const val MOD_ID = ""
|
||||
const val MOD_NAME = "Better Foliage"
|
||||
|
||||
val modBus = FMLKotlinModLoadingContext.get().modEventBus
|
||||
@@ -12,6 +12,7 @@ import mods.betterfoliage.client.texture.*
|
||||
import mods.octarinecore.client.gui.textComponent
|
||||
import mods.octarinecore.client.render.RenderDecorator
|
||||
import mods.octarinecore.client.resource.CenteringTextureGenerator
|
||||
import mods.octarinecore.client.resource.AsyncSpriteProviderManager
|
||||
import mods.octarinecore.client.resource.IConfigChangeListener
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -59,7 +60,6 @@ object Client {
|
||||
// init other singletons
|
||||
val singletons = listOf(
|
||||
BlockConfig,
|
||||
StandardCactusRegistry,
|
||||
LeafParticleRegistry,
|
||||
ChunkOverlayManager,
|
||||
LeafWindTracker,
|
||||
@@ -76,9 +76,10 @@ object Client {
|
||||
)
|
||||
|
||||
// add basic block support instances as last
|
||||
GrassRegistry.addRegistry(StandardGrassRegistry)
|
||||
LeafRegistry.addRegistry(StandardLeafRegistry)
|
||||
LogRegistry.addRegistry(StandardLogRegistry)
|
||||
AsyncLeafDiscovery.init()
|
||||
AsyncGrassDiscovery.init()
|
||||
AsyncLogDiscovery.init()
|
||||
AsyncCactusDiscovery.init()
|
||||
|
||||
configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>()
|
||||
configListeners.forEach { it.onConfigChange() }
|
||||
|
||||
@@ -6,12 +6,10 @@ import mods.betterfoliage.client.chunk.ChunkOverlayManager
|
||||
import mods.betterfoliage.client.config.BlockConfig
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.betterfoliage.client.render.*
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import mods.octarinecore.ThreadLocalDelegate
|
||||
import mods.octarinecore.client.render.*
|
||||
import mods.octarinecore.client.render.lighting.DefaultLightingCtx
|
||||
import mods.octarinecore.client.render.lighting.LightingCtx
|
||||
import mods.octarinecore.client.resource.LoadModelDataEvent
|
||||
import mods.octarinecore.common.plus
|
||||
import mods.octarinecore.metaprog.allAvailable
|
||||
import net.minecraft.block.Block
|
||||
@@ -35,9 +33,6 @@ import net.minecraft.world.World
|
||||
import net.minecraftforge.client.model.data.IModelData
|
||||
import java.util.*
|
||||
|
||||
var isAfterPostInit = false
|
||||
val isOptifinePresent = allAvailable(Refs.OptifineClassTransformer)
|
||||
|
||||
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
||||
if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat();
|
||||
return original
|
||||
@@ -69,10 +64,6 @@ fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: Bloc
|
||||
}
|
||||
}
|
||||
|
||||
fun onLoadModelDefinitions(bakery: Any) {
|
||||
BetterFoliage.modBus.post(LoadModelDataEvent(bakery as ModelBakery))
|
||||
}
|
||||
|
||||
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
||||
if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty()
|
||||
return state.func_215702_a(reader, pos, dir)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package mods.betterfoliage.client.chunk
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import mods.octarinecore.client.render.BasicBlockCtx
|
||||
import mods.octarinecore.ChunkCacheOF
|
||||
import mods.octarinecore.client.render.BlockCtx
|
||||
import mods.octarinecore.metaprog.get
|
||||
import mods.octarinecore.metaprog.isInstance
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderCache
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
@@ -16,13 +15,19 @@ import net.minecraftforge.common.MinecraftForge
|
||||
import net.minecraftforge.event.world.ChunkEvent
|
||||
import net.minecraftforge.event.world.WorldEvent
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import org.apache.logging.log4j.Level
|
||||
import java.util.*
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.MutableMap
|
||||
import kotlin.collections.associateWith
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.set
|
||||
|
||||
val IEnviromentBlockReader.dimType: DimensionType get() = when {
|
||||
this is IWorldReader -> dimension.type
|
||||
this is ChunkRenderCache -> world.dimension.type
|
||||
Refs.OptifineChunkCache.isInstance(this) -> (Refs.CCOFChunkCache.get(this) as ChunkRenderCache).world.dimension.type
|
||||
this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache].world.dimension.type
|
||||
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
|
||||
}
|
||||
|
||||
@@ -42,7 +47,6 @@ object ChunkOverlayManager {
|
||||
var tempCounter = 0
|
||||
|
||||
init {
|
||||
Client.log(Level.INFO, "Initializing client overlay manager")
|
||||
MinecraftForge.EVENT_BUS.register(this)
|
||||
}
|
||||
|
||||
@@ -84,13 +88,11 @@ object ChunkOverlayManager {
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleLoadWorld(event: WorldEvent.Load) = (event.world as? ClientWorld)?.let { world ->
|
||||
BetterFoliage.log.debug("Unloaded world: id=${world.dimType.id} name=${world.dimType.registryName}")
|
||||
chunkData[world.dimType] = mutableMapOf()
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleUnloadWorld(event: WorldEvent.Unload) = (event.world as? ClientWorld)?.let { world ->
|
||||
BetterFoliage.log.debug("Unloaded world: id=${world.dimType.id} name=${world.dimType.registryName}")
|
||||
chunkData.remove(world.dimType)
|
||||
}
|
||||
|
||||
@@ -121,26 +123,3 @@ class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
|
||||
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) {}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -2,10 +2,8 @@ package mods.betterfoliage.client.config
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.client.integration.ShadersModIntegration
|
||||
import mods.octarinecore.client.resource.LoadModelDataEvent
|
||||
import mods.octarinecore.common.config.*
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
|
||||
private fun featureEnable() = boolean(true).lang("enabled")
|
||||
|
||||
@@ -165,12 +163,4 @@ object BlockConfig {
|
||||
init { BetterFoliage.modBus.register(this) }
|
||||
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
|
||||
|
||||
@SubscribeEvent
|
||||
fun handleLoadModelData(event: LoadModelDataEvent) {
|
||||
list.forEach { when(it) {
|
||||
is ConfigurableBlockMatcher -> it.readDefaults()
|
||||
is ModelTextureListConfiguration -> it.readDefaults()
|
||||
} }
|
||||
}
|
||||
}
|
||||
@@ -1,153 +1,133 @@
|
||||
package mods.betterfoliage.client.integration
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.BlockConfig
|
||||
import mods.betterfoliage.client.render.LogRegistry
|
||||
import mods.betterfoliage.client.render.StandardLogRegistry
|
||||
import mods.betterfoliage.client.render.AsyncLogDiscovery
|
||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
||||
import mods.betterfoliage.client.resource.Identifier
|
||||
import mods.betterfoliage.client.texture.LeafInfo
|
||||
import mods.betterfoliage.client.texture.LeafRegistry
|
||||
import mods.betterfoliage.client.texture.StandardLeafKey
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import mods.octarinecore.client.resource.ModelRenderKey
|
||||
import mods.octarinecore.client.resource.ModelRenderRegistry
|
||||
import mods.octarinecore.client.resource.ModelRenderRegistryBase
|
||||
import mods.octarinecore.getTileEntitySafe
|
||||
import mods.octarinecore.metaprog.ClassRef
|
||||
import mods.octarinecore.metaprog.FieldRef
|
||||
import mods.octarinecore.metaprog.MethodRef
|
||||
import mods.octarinecore.metaprog.allAvailable
|
||||
import mods.betterfoliage.client.texture.defaultRegisterLeaf
|
||||
import mods.octarinecore.HasLogger
|
||||
import mods.octarinecore.Map
|
||||
import mods.octarinecore.ResourceLocation
|
||||
import mods.octarinecore.String
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.metaprog.*
|
||||
import mods.octarinecore.metaprog.ClassRef.Companion.boolean
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.client.renderer.model.ModelBakery
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.IBlockReader
|
||||
import net.minecraft.world.IWorldReader
|
||||
import net.minecraftforge.client.event.TextureStitchEvent
|
||||
import net.minecraftforge.client.model.IModel
|
||||
import net.minecraftforge.eventbus.api.EventPriority
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.ModList
|
||||
import org.apache.logging.log4j.Level
|
||||
import kotlin.collections.Map
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.emptyMap
|
||||
import kotlin.collections.find
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.get
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.mapValues
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.set
|
||||
|
||||
val TextureLeaves = ClassRef<Any>("forestry.arboriculture.models.TextureLeaves")
|
||||
val TextureLeaves_leafTextures = FieldRef(TextureLeaves, "leafTextures", Map)
|
||||
val TextureLeaves_plain = FieldRef(TextureLeaves, "plain", ResourceLocation)
|
||||
val TextureLeaves_fancy = FieldRef(TextureLeaves, "fancy", ResourceLocation)
|
||||
val TextureLeaves_pollinatedPlain = FieldRef(TextureLeaves, "pollinatedPlain", ResourceLocation)
|
||||
val TextureLeaves_pollinatedFancy = FieldRef(TextureLeaves, "pollinatedFancy", ResourceLocation)
|
||||
|
||||
|
||||
val TileLeaves = ClassRef<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 {
|
||||
|
||||
val TextureLeaves = ClassRef("forestry.arboriculture.models.TextureLeaves")
|
||||
val TeLleafTextures = FieldRef(TextureLeaves, "leafTextures", Refs.Map)
|
||||
val TeLplain = FieldRef(TextureLeaves, "plain", Refs.ResourceLocation)
|
||||
val TeLfancy = FieldRef(TextureLeaves, "fancy", Refs.ResourceLocation)
|
||||
val TeLpollplain = FieldRef(TextureLeaves, "pollinatedPlain", Refs.ResourceLocation)
|
||||
val TeLpollfancy = FieldRef(TextureLeaves, "pollinatedFancy", Refs.ResourceLocation)
|
||||
val TileLeaves = ClassRef("forestry.arboriculture.tiles.TileLeaves")
|
||||
val TiLgetLeaveSprite = MethodRef(TileLeaves, "getLeaveSprite", Refs.ResourceLocation, ClassRef.boolean)
|
||||
|
||||
val PropertyWoodType = ClassRef("forestry.arboriculture.blocks.PropertyWoodType")
|
||||
val IWoodType = ClassRef("forestry.api.arboriculture.IWoodType")
|
||||
val barkTex = MethodRef(IWoodType, "getBarkTexture", Refs.String)
|
||||
val heartTex = MethodRef(IWoodType, "getHeartTexture", Refs.String)
|
||||
|
||||
val PropertyTreeType = ClassRef("forestry.arboriculture.blocks.PropertyTreeType")
|
||||
val TreeDefinition = ClassRef("forestry.arboriculture.genetics.TreeDefinition")
|
||||
val IAlleleTreeSpecies = ClassRef("forestry.api.arboriculture.IAlleleTreeSpecies")
|
||||
val ILeafSpriteProvider = ClassRef("forestry.api.arboriculture.ILeafSpriteProvider")
|
||||
val TdSpecies = FieldRef(TreeDefinition, "species", IAlleleTreeSpecies)
|
||||
val getLeafSpriteProvider = MethodRef(IAlleleTreeSpecies, "getLeafSpriteProvider", ILeafSpriteProvider)
|
||||
val getSprite = MethodRef(ILeafSpriteProvider, "getSprite", Refs.ResourceLocation, ClassRef.boolean, ClassRef.boolean)
|
||||
|
||||
init {
|
||||
if (ModList.get().isLoaded("forestry") && allAvailable(TiLgetLeaveSprite, getLeafSpriteProvider, getSprite)) {
|
||||
Client.log(Level.INFO, "Forestry support initialized")
|
||||
LeafRegistry.addRegistry(ForestryLeafRegistry)
|
||||
LogRegistry.addRegistry(ForestryLogRegistry)
|
||||
if (ModList.get().isLoaded("forestry") && allAvailable(TileLeaves_getLeaveSprite, IAlleleTreeSpecies_getLeafSpriteProvider, ILeafSpriteProvider_getSprite)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ForestryLeafRegistry : ModelRenderRegistry<LeafInfo> {
|
||||
val logger = BetterFoliage.logDetail
|
||||
val textureToKey = mutableMapOf<ResourceLocation, ModelRenderKey<LeafInfo>>()
|
||||
var textureToValue = emptyMap<ResourceLocation, LeafInfo>()
|
||||
object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegistry<LeafInfo> {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
var idToValue = emptyMap<Identifier, LeafInfo>()
|
||||
|
||||
override fun get(state: BlockState, world: IBlockReader, pos: BlockPos): LeafInfo? {
|
||||
// check variant property (used in decorative leaves)
|
||||
state.values.entries.find {
|
||||
ForestryIntegration.PropertyTreeType.isInstance(it.key) && ForestryIntegration.TreeDefinition.isInstance(it.value)
|
||||
PropertyTreeType.isInstance(it.key) && TreeDefinition.isInstance(it.value)
|
||||
} ?.let {
|
||||
val species = ForestryIntegration.TdSpecies.get(it.value)
|
||||
val spriteProvider = ForestryIntegration.getLeafSpriteProvider.invoke(species!!)
|
||||
val textureLoc = ForestryIntegration.getSprite.invoke(spriteProvider!!, false, Minecraft.isFancyGraphicsEnabled())
|
||||
return textureToValue[textureLoc]
|
||||
val species = it.value[TreeDefinition_species]
|
||||
val spriteProvider = species[IAlleleTreeSpecies_getLeafSpriteProvider]()
|
||||
val textureLoc = spriteProvider[ILeafSpriteProvider_getSprite](false, Minecraft.isFancyGraphicsEnabled())
|
||||
return idToValue[textureLoc]
|
||||
}
|
||||
|
||||
// extract leaf texture information from TileEntity
|
||||
val tile = world.getTileEntity(pos) ?: return null
|
||||
if (!ForestryIntegration.TileLeaves.isInstance(tile)) return null
|
||||
val textureLoc = ForestryIntegration.TiLgetLeaveSprite.invoke(tile, Minecraft.isFancyGraphicsEnabled()) ?: return null
|
||||
return textureToValue[textureLoc]
|
||||
if (!TileLeaves.isInstance(tile)) return null
|
||||
val textureLoc = tile[TileLeaves_getLeaveSprite](Minecraft.isFancyGraphicsEnabled())
|
||||
return idToValue[textureLoc]
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||
textureToValue = emptyMap()
|
||||
override fun setup(bakeryFuture: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases {
|
||||
val futures = mutableMapOf<Identifier, CompletableFuture<LeafInfo>>()
|
||||
|
||||
val allLeaves = ForestryIntegration.TeLleafTextures.getStatic() as Map<*, *>
|
||||
allLeaves.entries.forEach {
|
||||
logger.log(Level.DEBUG, "ForestryLeavesSupport: base leaf type ${it.key.toString()}")
|
||||
listOf(
|
||||
ForestryIntegration.TeLplain.get(it.value) as ResourceLocation,
|
||||
ForestryIntegration.TeLfancy.get(it.value) as ResourceLocation,
|
||||
ForestryIntegration.TeLpollplain.get(it.value) as ResourceLocation,
|
||||
ForestryIntegration.TeLpollfancy.get(it.value) as ResourceLocation
|
||||
).forEach { textureLocation ->
|
||||
val key = StandardLeafKey(logger, textureLocation.toString()).apply { onPreStitch(event) }
|
||||
textureToKey[textureLocation] = key
|
||||
return StitchPhases(
|
||||
discovery = bakeryFuture.thenRunAsync {
|
||||
val allLeaves = TextureLeaves_leafTextures.getStatic()
|
||||
allLeaves.entries.forEach { (type, leaves) ->
|
||||
log("base leaf type $type")
|
||||
leaves!!
|
||||
listOf(
|
||||
leaves[TextureLeaves_plain], leaves[TextureLeaves_pollinatedPlain],
|
||||
leaves[TextureLeaves_fancy], leaves[TextureLeaves_pollinatedFancy]
|
||||
).forEach { textureLocation ->
|
||||
futures[textureLocation] = defaultRegisterLeaf(textureLocation, atlasFuture)
|
||||
}
|
||||
}
|
||||
},
|
||||
cleanup = atlasFuture.sheet.thenRunAsync {
|
||||
idToValue = futures.mapValues { it.value.get() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
||||
textureToValue = textureToKey.mapValues { (_, key) -> key.resolveSprites(event.map) }
|
||||
textureToKey.clear()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object ForestryLogRegistry : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||
object ForestryLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
|
||||
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>? {
|
||||
// respect class list to avoid triggering on fences, stairs, etc.
|
||||
if (!BlockConfig.logBlocks.matchesClass(state.block)) return null
|
||||
if (!BlockConfig.logBlocks.matchesClass(ctx.state.block)) return null
|
||||
|
||||
// find wood type property
|
||||
val woodType = state.values.entries.find {
|
||||
ForestryIntegration.PropertyWoodType.isInstance(it.key) && ForestryIntegration.IWoodType.isInstance(it.value)
|
||||
} ?: return null
|
||||
val woodType = ctx.state.values.entries.find {
|
||||
PropertyWoodType.isInstance(it.key) && IWoodType.isInstance(it.value)
|
||||
}
|
||||
if (woodType != null) {
|
||||
logger.log(Level.DEBUG, "ForestryLogRegistry: block state ${ctx.state}")
|
||||
logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}")
|
||||
|
||||
logger.log(Level.DEBUG, "ForestryLogRegistry: block state $state")
|
||||
logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}")
|
||||
// get texture names for wood type
|
||||
val bark = woodType.value[IWoodType_barkTex]()
|
||||
val heart = woodType.value[IWoodType_heartTex]()
|
||||
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]")
|
||||
|
||||
// get texture names for wood type
|
||||
val bark = ForestryIntegration.barkTex.invoke(woodType.value) as String?
|
||||
val heart = ForestryIntegration.heartTex.invoke(woodType.value) as String?
|
||||
|
||||
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]")
|
||||
if (bark != null && heart != null) return SimpleColumnInfo.Key(logger, StandardLogRegistry.getAxis(state), listOf(heart, heart, bark))
|
||||
val heartSprite = atlas.sprite(heart)
|
||||
val barkSprite = atlas.sprite(bark)
|
||||
return atlas.afterStitch {
|
||||
SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get()))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package mods.betterfoliage.client.integration
|
||||
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import mods.octarinecore.ThreadLocalDelegate
|
||||
import mods.octarinecore.*
|
||||
import mods.octarinecore.client.render.CombinedContext
|
||||
import mods.octarinecore.common.Int3
|
||||
import mods.octarinecore.metaprog.allAvailable
|
||||
import mods.octarinecore.metaprog.reflectField
|
||||
import net.minecraft.block.BlockState
|
||||
@@ -13,7 +11,6 @@ import net.minecraft.client.renderer.model.BakedQuad
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
||||
import net.minecraft.util.Direction.UP
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.IBlockReader
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
/**
|
||||
@@ -22,9 +19,7 @@ import org.apache.logging.log4j.Level
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
object OptifineCustomColors {
|
||||
|
||||
val isColorAvailable = allAvailable(
|
||||
Refs.CustomColors, Refs.getColorMultiplier
|
||||
)
|
||||
val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
|
||||
|
||||
init {
|
||||
Client.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }")
|
||||
@@ -36,21 +31,19 @@ object OptifineCustomColors {
|
||||
fun getBlockColor(ctx: CombinedContext): Int {
|
||||
val ofColor = if (isColorAvailable && Minecraft.getInstance().gameSettings.reflectField<Boolean>("ofCustomColors") == true) {
|
||||
renderEnv.reset(ctx.state, ctx.pos)
|
||||
Refs.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int
|
||||
CustomColors.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int
|
||||
} else null
|
||||
return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor
|
||||
}
|
||||
}
|
||||
|
||||
class OptifineRenderEnv {
|
||||
val wrapped: Any = Refs.RenderEnv.element!!.getDeclaredConstructor(
|
||||
Refs.BlockState.element, Refs.BlockPos.element
|
||||
).let {
|
||||
val wrapped: Any = RenderEnv.element!!.getDeclaredConstructor(BlockState.element, BlockPos.element).let {
|
||||
it.isAccessible = true
|
||||
it.newInstance(null, null)
|
||||
}
|
||||
|
||||
fun reset(state: BlockState, pos: BlockPos) {
|
||||
Refs.RenderEnv_reset.invoke(wrapped, state, pos)
|
||||
RenderEnv.reset.invoke(wrapped, state, pos)
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,11 @@ import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
||||
import mods.octarinecore.client.render.CombinedContext
|
||||
import mods.octarinecore.client.render.Quad
|
||||
import mods.octarinecore.client.render.lighting.QuadIconResolver
|
||||
import mods.octarinecore.client.render.lighting.LightingCtx
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.common.rotate
|
||||
import mods.octarinecore.metaprog.ClassRef
|
||||
import mods.octarinecore.metaprog.allAvailable
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.Direction
|
||||
@@ -24,29 +20,31 @@ import net.minecraft.util.Direction.*
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.fml.ModList
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Level.DEBUG
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
object IC2RubberIntegration {
|
||||
|
||||
val BlockRubWood = ClassRef("ic2.core.block.BlockRubWood")
|
||||
val BlockRubWood = ClassRef<Any>("ic2.core.block.BlockRubWood")
|
||||
|
||||
init {
|
||||
if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) {
|
||||
Client.log(Level.INFO, "IC2 rubber support initialized")
|
||||
LogRegistry.addRegistry(IC2LogSupport)
|
||||
LogRegistry.registries.add(IC2LogDiscovery)
|
||||
AsyncSpriteProviderManager.providers.add(IC2LogDiscovery)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TechRebornRubberIntegration {
|
||||
|
||||
val BlockRubberLog = ClassRef("techreborn.blocks.BlockRubberLog")
|
||||
val BlockRubberLog = ClassRef<Any>("techreborn.blocks.BlockRubberLog")
|
||||
|
||||
init {
|
||||
if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) {
|
||||
Client.log(Level.INFO, "TechReborn rubber support initialized")
|
||||
LogRegistry.addRegistry(TechRebornLogSupport)
|
||||
LogRegistry.registries.add(TechRebornLogDiscovery)
|
||||
AsyncSpriteProviderManager.providers.add(TechRebornLogDiscovery)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,27 +65,16 @@ class RubberLogInfo(
|
||||
this.sideTextures[sideIdx]
|
||||
}
|
||||
}
|
||||
|
||||
class Key(override val logger: Logger, val axis: Axis?, val spotDir: Direction, val textures: List<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 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
|
||||
if (!IC2RubberIntegration.BlockRubWood.isInstance(state.block)) return null
|
||||
val blockLoc = models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
|
||||
val type = state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
|
||||
if (!IC2RubberIntegration.BlockRubWood.isInstance(ctx.state.block)) return null
|
||||
val blockLoc = ctx.models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
|
||||
val type = ctx.state.values.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
|
||||
|
||||
// logs with no rubber spot
|
||||
if (blockLoc.derivesFrom(ResourceLocation("block/cube_column"))) {
|
||||
@@ -97,11 +84,15 @@ object IC2LogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||
"plain_z" -> Axis.Z
|
||||
else -> null
|
||||
}
|
||||
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||
if (textureNames.any { it == "missingno" }) return null
|
||||
logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}")
|
||||
logger.log(DEBUG, "IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[2]}")
|
||||
return SimpleColumnInfo.Key(logger, axis, textureNames)
|
||||
log("IC2LogSupport: block state ${ctx.state.toString()}")
|
||||
log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}")
|
||||
val endSprite = atlas.sprite(textureNames[0])
|
||||
val sideSprite = atlas.sprite(textureNames[1])
|
||||
return atlas.afterStitch {
|
||||
SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
|
||||
}
|
||||
}
|
||||
|
||||
// logs with rubber spot
|
||||
@@ -114,32 +105,53 @@ object IC2LogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||
}
|
||||
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTextureName(it) }
|
||||
if (textureNames.any { it == "missingno" }) return null
|
||||
logger.log(DEBUG, "IC2LogSupport: block state ${state.toString()}")
|
||||
logger.log(DEBUG, "IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
||||
return if (spotDir != null) RubberLogInfo.Key(logger, Axis.Y, spotDir, textureNames) else SimpleColumnInfo.Key(logger, Axis.Y, textureNames)
|
||||
log("IC2LogSupport: block state ${ctx.state.toString()}")
|
||||
log("IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
||||
val upSprite = atlas.sprite(textureNames[0])
|
||||
val downSprite = atlas.sprite(textureNames[1])
|
||||
val sideSprite = atlas.sprite(textureNames[2])
|
||||
val spotSprite = atlas.sprite(textureNames[3])
|
||||
return if (spotDir != null) atlas.afterStitch {
|
||||
RubberLogInfo(Axis.Y, spotDir, upSprite.get(), downSprite.get(), spotSprite.get(), listOf(sideSprite.get()))
|
||||
} else atlas.afterStitch {
|
||||
SimpleColumnInfo(Axis.Y, upSprite.get(), downSprite.get(), listOf(sideSprite.get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TechRebornLogSupport : ModelRenderRegistryBase<ColumnTextureInfo>() {
|
||||
object TechRebornLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
|
||||
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
|
||||
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(state.block)) return null
|
||||
val blockLoc = models.firstOrNull() as Pair<BlockModel, ResourceLocation> ?: return null
|
||||
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(ctx.state.block)) 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 sapSide = state.values.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null
|
||||
val hasSap = ctx.state.values.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null
|
||||
val sapSide = ctx.state.values.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null
|
||||
|
||||
logger.log(DEBUG, "$logName: block state $state")
|
||||
log("$logName: block state ${ctx.state}")
|
||||
if (hasSap) {
|
||||
val textureNames = listOf("end", "end", "sapside", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||
logger.log(DEBUG, "$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
||||
if (textureNames.all { it != "missingno" }) return RubberLogInfo.Key(logger, Axis.Y, sapSide, textureNames)
|
||||
val textureNames = listOf("end", "side", "sapside").map { blockLoc.first.resolveTextureName(it) }
|
||||
log("$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
|
||||
if (textureNames.all { it != "missingno" }) {
|
||||
val endSprite = atlas.sprite(textureNames[0])
|
||||
val sideSprite = atlas.sprite(textureNames[1])
|
||||
val sapSprite = atlas.sprite(textureNames[2])
|
||||
return atlas.afterStitch {
|
||||
RubberLogInfo(Axis.Y, sapSide, endSprite.get(), endSprite.get(), sapSprite.get(), listOf(sideSprite.get()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val textureNames = listOf("end", "end", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||
logger.log(DEBUG, "$logName: end=${textureNames[0]}, side=${textureNames[2]}")
|
||||
if (textureNames.all { it != "missingno" })return SimpleColumnInfo.Key(logger, Axis.Y, textureNames)
|
||||
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTextureName(it) }
|
||||
log("$logName: end=${textureNames[0]}, side=${textureNames[1]}")
|
||||
if (textureNames.all { it != "missingno" }) {
|
||||
val endSprite = atlas.sprite(textureNames[0])
|
||||
val sideSprite = atlas.sprite(textureNames[1])
|
||||
return atlas.afterStitch {
|
||||
SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package mods.betterfoliage.client.integration
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.BlockConfig
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import mods.octarinecore.SVertexBuilder
|
||||
import mods.octarinecore.client.render.CombinedContext
|
||||
import mods.octarinecore.metaprog.allAvailable
|
||||
import mods.octarinecore.metaprog.get
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockRenderType.MODEL
|
||||
import net.minecraft.block.BlockState
|
||||
@@ -20,7 +21,7 @@ import org.apache.logging.log4j.Level.INFO
|
||||
*/
|
||||
object ShadersModIntegration {
|
||||
|
||||
@JvmStatic val isAvailable = allAvailable(Refs.sVertexBuilder, Refs.pushEntity_state, Refs.pushEntity_num, Refs.popEntity)
|
||||
@JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.pushNum, SVertexBuilder.pop)
|
||||
|
||||
val grassDefaultBlockId = 31L
|
||||
val leavesDefaultBlockId = 18L
|
||||
@@ -42,10 +43,10 @@ object ShadersModIntegration {
|
||||
/** Quads rendered inside this block will use the given block entity data in shader programs. */
|
||||
inline fun renderAs(blockId: Long, renderType: BlockRenderType, renderer: BufferBuilder, enabled: Boolean = true, func: ()->Unit) {
|
||||
if ((isAvailable && enabled)) {
|
||||
val vertexBuilder = Refs.sVertexBuilder.get(renderer)!!
|
||||
Refs.pushEntity_num.invoke(vertexBuilder, blockId)
|
||||
val vertexBuilder = renderer[mods.octarinecore.BufferBuilder.sVertexBuilder]!!
|
||||
SVertexBuilder.pushNum.invoke(vertexBuilder, blockId)
|
||||
func()
|
||||
Refs.popEntity.invoke(vertexBuilder)
|
||||
SVertexBuilder.pop.invoke(vertexBuilder)
|
||||
} else {
|
||||
func()
|
||||
}
|
||||
|
||||
@@ -5,30 +5,39 @@ import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
||||
import mods.betterfoliage.client.resource.Identifier
|
||||
import mods.octarinecore.client.render.*
|
||||
import mods.octarinecore.client.render.lighting.*
|
||||
import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable
|
||||
import mods.octarinecore.common.Int3
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.common.Rotation
|
||||
import mods.octarinecore.common.config.ModelTextureList
|
||||
import mods.octarinecore.common.config.SimpleBlockMatcher
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.CactusBlock
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||
import net.minecraft.client.renderer.BufferBuilder
|
||||
import net.minecraft.util.BlockRenderLayer
|
||||
import net.minecraft.util.Direction.*
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.client.model.data.IModelData
|
||||
import org.apache.logging.log4j.Level.DEBUG
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
object StandardCactusRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() {
|
||||
object AsyncCactusDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java)
|
||||
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
|
||||
override fun processModel(state: BlockState, textures: List<String>) = SimpleColumnInfo.Key(logger, Axis.Y, textures)
|
||||
init { BetterFoliage.modBus.register(this) }
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
|
||||
val sprites = textures.map { atlas.sprite(Identifier(it)) }
|
||||
return atlas.afterStitch {
|
||||
SimpleColumnInfo(
|
||||
Axis.Y,
|
||||
sprites[0].get(),
|
||||
sprites[1].get(),
|
||||
sprites.drop(2).map { it.get() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun init() {
|
||||
AsyncSpriteProviderManager.providers.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||
@@ -73,12 +82,12 @@ class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus)
|
||||
|
||||
override fun isEligible(ctx: CombinedContext): Boolean =
|
||||
Config.enabled && Config.cactus.enabled &&
|
||||
StandardCactusRegistry[ctx] != null
|
||||
AsyncCactusDiscovery[ctx] != null
|
||||
|
||||
override val onlyOnCutout get() = true
|
||||
|
||||
override fun render(ctx: CombinedContext) {
|
||||
val icons = StandardCactusRegistry[ctx]!!
|
||||
val icons = AsyncCactusDiscovery[ctx]!!
|
||||
|
||||
ctx.render(
|
||||
modelStem.model,
|
||||
|
||||
@@ -8,18 +8,17 @@ import mods.betterfoliage.client.render.column.AbstractRenderColumn
|
||||
import mods.betterfoliage.client.render.column.ColumnRenderLayer
|
||||
import mods.betterfoliage.client.render.column.ColumnTextureInfo
|
||||
import mods.betterfoliage.client.render.column.SimpleColumnInfo
|
||||
import mods.octarinecore.client.render.BlockCtx
|
||||
import mods.betterfoliage.client.resource.Identifier
|
||||
import mods.octarinecore.client.render.CombinedContext
|
||||
import mods.octarinecore.client.resource.ModelRenderRegistry
|
||||
import mods.octarinecore.client.resource.ModelRenderRegistryConfigurable
|
||||
import mods.octarinecore.client.resource.ModelRenderRegistryRoot
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
||||
import mods.octarinecore.common.config.ModelTextureList
|
||||
import mods.octarinecore.tryDefault
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.LogBlock
|
||||
import net.minecraft.util.Direction.Axis
|
||||
import net.minecraft.world.IEnviromentBlockReader
|
||||
import org.apache.logging.log4j.Level
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class RenderLog : AbstractRenderColumn(BetterFoliage.MOD_ID, BetterFoliage.modBus) {
|
||||
|
||||
@@ -49,12 +48,24 @@ class RoundLogOverlayLayer : ColumnRenderLayer() {
|
||||
|
||||
object LogRegistry : ModelRenderRegistryRoot<ColumnTextureInfo>()
|
||||
|
||||
object StandardLogRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>() {
|
||||
object AsyncLogDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks
|
||||
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? {
|
||||
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
|
||||
@@ -66,4 +77,9 @@ object StandardLogRegistry : ModelRenderRegistryConfigurable<ColumnTextureInfo>(
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun init() {
|
||||
LogRegistry.registries.add(this)
|
||||
AsyncSpriteProviderManager.providers.add(this)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,9 @@
|
||||
package mods.betterfoliage.client.render.column
|
||||
|
||||
import mods.octarinecore.client.render.lighting.QuadIconResolver
|
||||
import mods.octarinecore.client.resource.ModelRenderKey
|
||||
import mods.octarinecore.client.resource.get
|
||||
import mods.octarinecore.client.resource.missingSprite
|
||||
import mods.octarinecore.common.rotate
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.util.Direction.*
|
||||
import org.apache.logging.log4j.Logger
|
||||
|
||||
interface ColumnTextureInfo {
|
||||
val axis: Axis?
|
||||
@@ -34,13 +29,4 @@ open class SimpleColumnInfo(
|
||||
val sideIdx = if (sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % sideTextures.size else 0
|
||||
sideTextures[sideIdx]
|
||||
}
|
||||
|
||||
class Key(override val logger: Logger, val axis: Axis?, val textures: List<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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package mods.octarinecore.resource
|
||||
package mods.betterfoliage.client.resource
|
||||
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
@@ -3,16 +3,16 @@ package mods.betterfoliage.client.texture
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.client.config.BlockConfig
|
||||
import mods.betterfoliage.client.config.Config
|
||||
import mods.betterfoliage.client.resource.Identifier
|
||||
import mods.octarinecore.client.render.lighting.HSB
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
||||
import mods.octarinecore.common.config.ModelTextureList
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.lang.Math.min
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
const val defaultGrassColor = 0
|
||||
|
||||
@@ -32,28 +32,34 @@ class GrassInfo(
|
||||
|
||||
object GrassRegistry : ModelRenderRegistryRoot<GrassInfo>()
|
||||
|
||||
object StandardGrassRegistry : ModelRenderRegistryConfigurable<GrassInfo>() {
|
||||
object AsyncGrassDiscovery : ConfigurableModelDiscovery<GrassInfo>() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
|
||||
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 resolveSprites(atlas: AtlasTexture): GrassInfo {
|
||||
val logName = "StandardGrassKey"
|
||||
val texture = atlas[textureName] ?: missingSprite
|
||||
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
||||
val hsb = HSB.fromColor(texture.averageColor ?: defaultGrassColor)
|
||||
val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
|
||||
logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}")
|
||||
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color")
|
||||
hsb.copy(brightness = min(0.9f, hsb.brightness * 2.0f)).asColor
|
||||
} else {
|
||||
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color")
|
||||
null
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<GrassInfo> {
|
||||
val textureName = textures[0]
|
||||
val spriteF = atlas.sprite(Identifier(textureName))
|
||||
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
||||
return atlas.afterStitch {
|
||||
val sprite = spriteF.get()
|
||||
logger.log(Level.DEBUG, "$logName: block state $state")
|
||||
logger.log(Level.DEBUG, "$logName: texture $textureName")
|
||||
val hsb = HSB.fromColor(sprite.averageColor ?: defaultGrassColor)
|
||||
val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
|
||||
logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}")
|
||||
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color")
|
||||
hsb.copy(brightness = min(0.9f, hsb.brightness * 2.0f)).asColor
|
||||
} else {
|
||||
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} < ${Config.shortGrass.saturationThreshold}, using block color")
|
||||
null
|
||||
}
|
||||
GrassInfo(sprite, overrideColor)
|
||||
}
|
||||
return GrassInfo(texture, overrideColor)
|
||||
}
|
||||
|
||||
fun init() {
|
||||
GrassRegistry.registries.add(this)
|
||||
AsyncSpriteProviderManager.providers.add(this)
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,14 @@ package mods.betterfoliage.client.texture
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.client.Client
|
||||
import mods.betterfoliage.client.config.BlockConfig
|
||||
import mods.betterfoliage.client.resource.Identifier
|
||||
import mods.octarinecore.HasLogger
|
||||
import mods.octarinecore.client.resource.*
|
||||
import mods.octarinecore.common.config.ConfigurableBlockMatcher
|
||||
import mods.octarinecore.common.config.ModelTextureList
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.client.event.TextureStitchEvent
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
const val defaultLeafColor = 0
|
||||
|
||||
@@ -25,7 +23,7 @@ class LeafInfo(
|
||||
val leafType: String,
|
||||
|
||||
/** Average color of the round leaf texture. */
|
||||
val averageColor: Int = roundLeafTexture.averageColor ?: defaultLeafColor
|
||||
val averageColor: Int = roundLeafTexture.averageColor
|
||||
) {
|
||||
/** [IconSet] of the textures to use for leaf particles emitted from this block. */
|
||||
val particleTextures: IconSet get() = LeafParticleRegistry[leafType]
|
||||
@@ -33,27 +31,27 @@ class LeafInfo(
|
||||
|
||||
object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>()
|
||||
|
||||
object StandardLeafRegistry : ModelRenderRegistryConfigurable<LeafInfo>() {
|
||||
object AsyncLeafDiscovery : ConfigurableModelDiscovery<LeafInfo>() {
|
||||
override val logger = BetterFoliage.logDetail
|
||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
|
||||
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) }
|
||||
|
||||
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture) = defaultRegisterLeaf(Identifier(textures[0]), atlas)
|
||||
|
||||
fun init() {
|
||||
LeafRegistry.registries.add(this)
|
||||
AsyncSpriteProviderManager.providers.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
class StandardLeafKey(override val logger: Logger, val textureName: String) : ModelRenderKey<LeafInfo> {
|
||||
lateinit var leafType: String
|
||||
lateinit var generated: ResourceLocation
|
||||
fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture<LeafInfo> {
|
||||
val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default"
|
||||
val generated = Client.genLeaves.register(sprite, leafType)
|
||||
val roundLeaf = atlas.sprite(generated)
|
||||
|
||||
override fun onPreStitch(event: TextureStitchEvent.Pre) {
|
||||
val logName = "StandardLeafKey"
|
||||
leafType = LeafParticleRegistry.typeMappings.getType(textureName) ?: "default"
|
||||
generated = Client.genLeaves.register(ResourceLocation(textureName), leafType)
|
||||
event.addSprite(generated)
|
||||
|
||||
logger.log(Level.DEBUG, "$logName: leaf texture $textureName")
|
||||
logger.log(Level.DEBUG, "$logName: particle $leafType")
|
||||
log(" leaf texture $sprite")
|
||||
log(" particle $leafType")
|
||||
return atlas.afterStitch {
|
||||
LeafInfo(roundLeaf.get(), leafType)
|
||||
}
|
||||
|
||||
override fun resolveSprites(atlas: AtlasTexture) = LeafInfo(atlas[generated] ?: missingSprite, leafType)
|
||||
}
|
||||
@@ -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!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -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)
|
||||
}
|
||||
68
src/main/kotlin/mods/octarinecore/CommonRefs.kt
Normal file
68
src/main/kotlin/mods/octarinecore/CommonRefs.kt
Normal 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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
package mods.octarinecore
|
||||
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import net.minecraft.tileentity.TileEntity
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.*
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Logger
|
||||
import kotlin.reflect.KProperty
|
||||
import java.lang.Math.*
|
||||
|
||||
@@ -118,4 +119,11 @@ fun nextPowerOf2(x: Int): Int {
|
||||
*/
|
||||
fun IWorldReader.getTileEntitySafe(pos: BlockPos): TileEntity? = tryDefault(null as TileEntity?) {
|
||||
if (isBlockLoaded(pos)) getTileEntity(pos) else null
|
||||
}
|
||||
|
||||
interface HasLogger {
|
||||
val logger: Logger
|
||||
val logName: String get() = this::class.simpleName!!
|
||||
fun log(msg: String) = log(Level.DEBUG, msg)
|
||||
fun log(level: Level, msg: String) = logger.log(level, "[$logName] $msg")
|
||||
}
|
||||
@@ -2,23 +2,21 @@ package mods.octarinecore.client.render
|
||||
|
||||
import mods.betterfoliage.client.render.canRenderInCutout
|
||||
import mods.betterfoliage.client.render.isCutout
|
||||
import mods.betterfoliage.loader.Refs
|
||||
import mods.octarinecore.BufferBuilder
|
||||
import mods.octarinecore.client.render.lighting.*
|
||||
import mods.octarinecore.common.Double3
|
||||
import mods.octarinecore.common.Int3
|
||||
import mods.octarinecore.common.Rotation
|
||||
import mods.octarinecore.common.plus
|
||||
import mods.octarinecore.metaprog.get
|
||||
import mods.octarinecore.metaprog.set
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher
|
||||
import net.minecraft.client.renderer.BufferBuilder
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.util.BlockRenderLayer
|
||||
import net.minecraft.util.Direction
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.IEnviromentBlockReader
|
||||
import net.minecraft.world.LightType
|
||||
import net.minecraft.world.biome.Biomes
|
||||
import java.util.*
|
||||
|
||||
class CombinedContext(
|
||||
val blockCtx: BlockCtx, val renderCtx: RenderCtx, val lightingCtx: DefaultLightingCtx
|
||||
@@ -64,7 +62,7 @@ class CombinedContext(
|
||||
if (drawIcon != null) {
|
||||
// let OptiFine know the texture we're using, so it can
|
||||
// transform UV coordinates to quad-relative
|
||||
Refs.quadSprite.set(renderCtx!!.renderBuffer, drawIcon)
|
||||
renderCtx.renderBuffer[BufferBuilder.quadSprite] = drawIcon
|
||||
|
||||
quad.verts.forEachIndexed { vertIdx, vert ->
|
||||
temp.init(vert).rotate(lightingCtx.modelRotation).translate(translation)
|
||||
|
||||
@@ -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())
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package mods.octarinecore.client.resource
|
||||
|
||||
import mods.betterfoliage.client.resource.Identifier
|
||||
import mods.betterfoliage.client.resource.Sprite
|
||||
import mods.octarinecore.client.render.Model
|
||||
import mods.octarinecore.common.Double3
|
||||
import mods.octarinecore.common.Int3
|
||||
import mods.octarinecore.resource.Identifier
|
||||
import mods.octarinecore.resource.Sprite
|
||||
import mods.octarinecore.stripEnd
|
||||
import mods.octarinecore.stripStart
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture
|
||||
@@ -63,10 +63,10 @@ open class ResourceHandler(
|
||||
// ============================
|
||||
// Resource declarations
|
||||
// ============================
|
||||
fun iconStatic(location: ()->Identifier) = IconHolder(location).apply { resources.add(this) }
|
||||
fun iconStatic(location: ()-> Identifier) = IconHolder(location).apply { resources.add(this) }
|
||||
fun iconStatic(location: Identifier) = iconStatic { location }
|
||||
fun iconStatic(domain: String, path: String) = iconStatic(Identifier(domain, path))
|
||||
fun iconSet(targetAtlas: Atlas = Atlas.BLOCKS, location: (Int)->Identifier) = IconSet(targetAtlas, location).apply { this@ResourceHandler.resources.add(this) }
|
||||
fun iconSet(targetAtlas: Atlas = Atlas.BLOCKS, location: (Int)-> Identifier) = IconSet(targetAtlas, location).apply { this@ResourceHandler.resources.add(this) }
|
||||
fun model(init: Model.()->Unit) = ModelHolder(init).apply { resources.add(this) }
|
||||
fun modelSet(num: Int, init: Model.(Int)->Unit) = ModelSet(num, init).apply { resources.add(this) }
|
||||
fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) }
|
||||
@@ -102,7 +102,7 @@ open class ResourceHandler(
|
||||
// ============================
|
||||
// Resource container classes
|
||||
// ============================
|
||||
class IconHolder(val location: ()->Identifier) : IStitchListener {
|
||||
class IconHolder(val location: ()-> Identifier) : IStitchListener {
|
||||
var iconRes: Identifier? = null
|
||||
var icon: Sprite? = null
|
||||
override fun onPreStitch(event: TextureStitchEvent.Pre) {
|
||||
@@ -119,7 +119,7 @@ class ModelHolder(val init: Model.()->Unit): IConfigChangeListener {
|
||||
override fun onConfigChange() { model = Model().apply(init) }
|
||||
}
|
||||
|
||||
class IconSet(val targetAtlas: Atlas, val location: (Int)->Identifier) : IStitchListener {
|
||||
class IconSet(val targetAtlas: Atlas, val location: (Int)-> Identifier) : IStitchListener {
|
||||
val resources = arrayOfNulls<Identifier>(16)
|
||||
val icons = arrayOfNulls<Sprite>(16)
|
||||
var num = 0
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.lang.Math.*
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.math.atan2
|
||||
|
||||
/** Concise getter for the Minecraft resource manager. */
|
||||
val resourceManager: SimpleReloadableResourceManager
|
||||
@@ -65,19 +66,15 @@ val BufferedImage.asStream: InputStream get() =
|
||||
* Only non-transparent pixels are considered. Averages are taken in the HSB color space (note: Hue is a circular average),
|
||||
* and the result transformed back to the RGB color space.
|
||||
*/
|
||||
val TextureAtlasSprite.averageColor: Int? get() {
|
||||
// val locationNoDirs = ResourceLocation(iconName).stripStart("blocks/")
|
||||
// val locationWithDirs = ResourceLocation(locationNoDirs.namespace, "textures/blocks/%s.png".format(locationNoDirs.path))
|
||||
// val image = resourceManager[locationWithDirs]?.loadImage() ?: return null
|
||||
|
||||
val TextureAtlasSprite.averageColor: Int get() {
|
||||
var numOpaque = 0
|
||||
var sumHueX = 0.0
|
||||
var sumHueY = 0.0
|
||||
var sumSaturation = 0.0f
|
||||
var sumBrightness = 0.0f
|
||||
for (x in 0..width - 1)
|
||||
for (y in 0..height - 1) {
|
||||
val pixel = getPixelRGBA(0, x, y);
|
||||
for (x in 0 until width)
|
||||
for (y in 0 until height) {
|
||||
val pixel = getPixelRGBA(0, x, y)
|
||||
val alpha = (pixel shr 24) and 255
|
||||
val hsb = HSB.fromColor(pixel)
|
||||
if (alpha == 255) {
|
||||
@@ -90,7 +87,7 @@ val TextureAtlasSprite.averageColor: Int? get() {
|
||||
}
|
||||
|
||||
// circular average - transform sum vector to polar angle
|
||||
val avgHue = (atan2(sumHueY.toDouble(), sumHueX.toDouble()) / PI2 + 0.5).toFloat()
|
||||
val avgHue = (atan2(sumHueY, sumHueX) / PI2 + 0.5).toFloat()
|
||||
return HSB(avgHue, sumSaturation / numOpaque.toFloat(), sumBrightness / numOpaque.toFloat()).asColor
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import mods.octarinecore.metaprog.Namespace.*
|
||||
import mods.octarinecore.tryDefault
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.reflect.KFunction
|
||||
|
||||
/** Get a Java class with the given name. */
|
||||
fun getJavaClass(name: String) = tryDefault(null) { Class.forName(name) }
|
||||
@@ -66,19 +68,19 @@ fun allAvailable(vararg codeElement: Resolvable<*>) = codeElement.all { it.eleme
|
||||
*
|
||||
* @param[name] MCP name of the class
|
||||
*/
|
||||
open class ClassRef(val name: String) : Resolvable<Class<*>>() {
|
||||
open class ClassRef<T: Any?>(val name: String) : Resolvable<Class<T>>() {
|
||||
|
||||
companion object {
|
||||
val int = ClassRefPrimitive("I", Int::class.java)
|
||||
val long = ClassRefPrimitive("J", Long::class.java)
|
||||
val float = ClassRefPrimitive("F", Float::class.java)
|
||||
val boolean = ClassRefPrimitive("Z", Boolean::class.java)
|
||||
val void = ClassRefPrimitive("V", null)
|
||||
val void = ClassRefPrimitive("V", Void::class.java)
|
||||
}
|
||||
|
||||
open fun asmDescriptor(namespace: Namespace) = "L${name.replace(".", "/")};"
|
||||
|
||||
override fun resolve() = getJavaClass(name)
|
||||
override fun resolve() = getJavaClass(name) as Class<T>?
|
||||
|
||||
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[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 resolve() = clazz
|
||||
}
|
||||
|
||||
class ClassRefArray(name: String) : ClassRef(name) {
|
||||
override fun asmDescriptor(namespace: Namespace) = "[" + super.asmDescriptor(namespace)
|
||||
override fun resolve() = getJavaClass("[L$name;")
|
||||
}
|
||||
|
||||
fun ClassRef.array() = ClassRefArray(name)
|
||||
|
||||
/**
|
||||
* Reference to a method.
|
||||
*
|
||||
@@ -110,13 +105,13 @@ fun ClassRef.array() = ClassRefArray(name)
|
||||
* @param[returnType] reference to the return type
|
||||
* @param[returnType] references to the argument types
|
||||
*/
|
||||
class MethodRef(val parentClass: ClassRef,
|
||||
class MethodRef<T: Any?>(val parentClass: ClassRef<*>,
|
||||
val mcpName: String,
|
||||
val srgName: String,
|
||||
val returnType: ClassRef,
|
||||
vararg val argTypes: ClassRef
|
||||
val returnType: ClassRef<T>,
|
||||
vararg val argTypes: ClassRef<*>
|
||||
) : 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)
|
||||
|
||||
fun name(namespace: Namespace) = when(namespace) { SRG -> srgName; MCP -> mcpName }
|
||||
@@ -133,11 +128,10 @@ class MethodRef(val parentClass: ClassRef,
|
||||
}
|
||||
|
||||
/** Invoke this method using reflection. */
|
||||
fun invoke(receiver: Any, vararg args: Any?) = element?.invoke(receiver, *args)
|
||||
operator fun invoke(receiver: Any, vararg args: Any?) = element?.invoke(receiver, *args) as T
|
||||
|
||||
/** Invoke this static method using reflection. */
|
||||
fun invokeStatic(vararg args: Any) = element?.invoke(null, *args)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,30 +142,41 @@ class MethodRef(val parentClass: ClassRef,
|
||||
* @param[srgName] SRG name of the field
|
||||
* @param[type] reference to the field type
|
||||
*/
|
||||
class FieldRef(val parentClass: ClassRef,
|
||||
class FieldRef<T>(val parentClass: ClassRef<*>,
|
||||
val mcpName: String,
|
||||
val srgName: String,
|
||||
val type: ClassRef?
|
||||
val type: ClassRef<T>
|
||||
) : 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 asmDescriptor(namespace: Namespace) = type!!.asmDescriptor(namespace)
|
||||
fun asmDescriptor(namespace: Namespace) = type.asmDescriptor(namespace)
|
||||
|
||||
override fun resolve(): Field? =
|
||||
if (parentClass.element == null) null
|
||||
else {
|
||||
listOf(srgName, mcpName).map { tryDefault(null) {
|
||||
listOf(srgName, mcpName).mapNotNull { tryDefault(null) {
|
||||
parentClass.element!!.getDeclaredField(it)
|
||||
}}.filterNotNull().firstOrNull()
|
||||
} }.firstOrNull()
|
||||
?.apply{ isAccessible = true }
|
||||
}
|
||||
|
||||
/** Get this field using reflection. */
|
||||
fun get(receiver: Any?) = element?.get(receiver)
|
||||
operator fun get(receiver: Any?) = element?.get(receiver) as T
|
||||
|
||||
/** Get this static field using reflection. */
|
||||
fun getStatic() = get(null)
|
||||
|
||||
fun set(receiver: Any?, obj: Any?) { element?.set(receiver, obj) }
|
||||
}
|
||||
|
||||
|
||||
fun Any.isInstance(cls: ClassRef<*>) = cls.isInstance(this)
|
||||
interface ReflectionCallable<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)
|
||||
}
|
||||
@@ -7,14 +7,4 @@ public net.minecraft.block.BlockState$Cache
|
||||
|
||||
public net.minecraft.client.renderer.chunk.ChunkRenderCache field_212408_i #world
|
||||
|
||||
#public net.minecraft.client.renderer.block.model.ModelBakery field_177610_k # blockModelShapes
|
||||
|
||||
#public net.minecraft.client.renderer.block.statemap.BlockStateMapper field_178450_a # blockStateMap
|
||||
|
||||
#public net.minecraft.client.renderer.BufferBuilder field_178999_b # rawIntBuffer
|
||||
#public net.minecraft.client.renderer.BufferBuilder func_181670_b(I)V # growBuffer
|
||||
#public net.minecraft.client.renderer.BufferBuilder func_181664_j()I # getBufferSize
|
||||
|
||||
#public net.minecraft.client.renderer.BlockModelRenderer field_187499_a # blockColors
|
||||
|
||||
#public net.minecraft.world.ChunkCache field_72815_e # world
|
||||
public net.minecraft.client.renderer.texture.AtlasTexture$SheetData field_217808_d # sprites
|
||||
|
||||
Reference in New Issue
Block a user