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