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