[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:
octarine-noise
2020-01-06 17:09:40 +01:00
parent a3d99c3076
commit 4efa831296
43 changed files with 530 additions and 633 deletions

View File

@@ -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. * Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks.
*/ */
@Mixin(BlockState.class) @Mixin(BlockState.class)
@SuppressWarnings({"UnnecessaryQualifiedMemberReference", "deprecation"})
public class BlockStateMixin { 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 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"; 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";

View File

@@ -18,7 +18,7 @@ import java.util.Random;
@Mixin(ChunkRender.class) @Mixin(ChunkRender.class)
public class ChunkRenderMixin { 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"; 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)) @Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = renderBlock))

View File

@@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.Slice;
@Mixin(ChunkRender.class) @Mixin(ChunkRender.class)
public class ChunkRenderOptifineMixin { 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 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"; private static final String forgeBlockCanRender = "Lnet/minecraft/client/renderer/chunk/ChunkRender;FORGE_BLOCK_CAN_RENDER_IN_LAYER:Z";

View File

@@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ChunkRender.class) @Mixin(ChunkRender.class)
public class ChunkRenderVanillaMixin { 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"; private static final String canRenderInLayer = "Lnet/minecraft/block/BlockState;canRenderInLayer(Lnet/minecraft/util/BlockRenderLayer;)Z";
@Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = canRenderInLayer)) @Redirect(method = rebuildChunk, at = @At(value = "INVOKE", target = canRenderInLayer))

View File

@@ -1,7 +1,7 @@
package mods.betterfoliage.mixin; package mods.betterfoliage.mixin;
import mods.betterfoliage.client.Hooks; import mods.betterfoliage.BetterFoliage;
import mods.octarinecore.client.resource.AsyncSpriteProviderManager; import mods.octarinecore.client.resource.AsnycSpriteProviderManager;
import net.minecraft.client.renderer.model.ModelBakery; import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.profiler.IProfiler; import net.minecraft.profiler.IProfiler;
@@ -9,18 +9,19 @@ import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ModelBakery.class) @Mixin(ModelBakery.class)
abstract public class ModelBakeryMixin { abstract public class ModelBakeryMixin {
private static final String processLoading = "Lnet/minecraft/client/renderer/model/ModelBakery;processLoading(Lnet/minecraft/profiler/IProfiler;)V"; private static final String processLoading = "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;"; 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)) @Redirect(method = processLoading, at = @At(value = "INVOKE", target = stitch))
AtlasTexture.SheetData onStitchModelTextures(AtlasTexture atlas, IResourceManager manager, Iterable<ResourceLocation> idList, IProfiler profiler) { 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;
} }
} }

View File

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

View File

@@ -1,14 +1,21 @@
package mods.betterfoliage package mods.betterfoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.render.RisingSoulTextures
import mods.octarinecore.client.resource.GeneratorPack import mods.octarinecore.client.gui.textComponent
import net.alexwells.kottle.FMLKotlinModLoadingContext 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.minecraft.client.Minecraft
import net.minecraftforge.fml.ModLoadingContext import net.minecraft.client.particle.ParticleManager
import net.minecraftforge.fml.common.Mod import net.minecraft.client.renderer.model.ModelBakery
import net.minecraftforge.fml.config.ModConfig import net.minecraft.util.math.BlockPos
import org.apache.logging.log4j.Level.DEBUG 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.LogManager
import org.apache.logging.log4j.simple.SimpleLogger import org.apache.logging.log4j.simple.SimpleLogger
import org.apache.logging.log4j.util.PropertiesUtil import org.apache.logging.log4j.util.PropertiesUtil
@@ -16,17 +23,11 @@ import java.io.File
import java.io.PrintStream import java.io.PrintStream
import java.util.* import java.util.*
@Mod(BetterFoliage.MOD_ID)
object BetterFoliage { object BetterFoliage {
const val MOD_ID = ""
const val MOD_NAME = "Better Foliage"
val modBus = FMLKotlinModLoadingContext.get().modEventBus
var log = LogManager.getLogger("BetterFoliage") var log = LogManager.getLogger("BetterFoliage")
var logDetail = SimpleLogger( var logDetail = SimpleLogger(
"BetterFoliage", "BetterFoliage",
DEBUG, Level.DEBUG,
false, false, true, false, false, false, true, false,
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss",
null, null,
@@ -37,16 +38,39 @@ object BetterFoliage {
}) })
) )
val genPack = GeneratorPack( val blockSprites = AsnycSpriteProviderManager<ModelBakery>("bf-blocks-extra")
"bf_gen", val particleSprites = AsnycSpriteProviderManager<ParticleManager>("bf-particles-extra")
"Better Foliage generated assets", val asyncPack = GeneratedBlockTexturePack("bf_gen", "Better Foliage generated assets", logDetail)
"bf_generated_pack.png"
) fun getSpriteManager(atlas: Atlas) = when(atlas) {
Atlas.BLOCKS -> blockSprites
Atlas.PARTICLES -> particleSprites
} as AsnycSpriteProviderManager<Any>
init { init {
log.log(DEBUG, "Constructing mod") blockSprites.providers.add(asyncPack)
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.build()) }
Minecraft.getInstance().resourcePackList.addPackFinder(genPack.packFinder)
Client.init() 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")
} }
} }

View 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()
}
}

View File

@@ -1,6 +1,6 @@
package mods.betterfoliage.client package mods.betterfoliage.client
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.integration.ForestryIntegration 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.OptifineCustomColors
import mods.betterfoliage.client.integration.TechRebornRubberIntegration import mods.betterfoliage.client.integration.TechRebornRubberIntegration
import mods.betterfoliage.client.render.* 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.gui.textComponent
import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.resource.CenteringTextureGenerator
import mods.octarinecore.client.resource.AsyncSpriteProviderManager
import mods.octarinecore.client.resource.IConfigChangeListener import mods.octarinecore.client.resource.IConfigChangeListener
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
@@ -32,15 +32,7 @@ object Client {
val suppressRenderErrors = mutableSetOf<BlockState>() 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() { fun init() {
// add resource generators to pack
listOf(genGrass, genLeaves, genReeds).forEach { BetterFoliage.genPack.generators.add(it) }
// init renderers // init renderers
renderers = listOf( renderers = listOf(
RenderGrass(), RenderGrass(),
@@ -60,7 +52,6 @@ object Client {
// init other singletons // init other singletons
val singletons = listOf( val singletons = listOf(
BlockConfig, BlockConfig,
LeafParticleRegistry,
ChunkOverlayManager, ChunkOverlayManager,
LeafWindTracker, LeafWindTracker,
RisingSoulTextures RisingSoulTextures
@@ -75,6 +66,8 @@ object Client {
TechRebornRubberIntegration TechRebornRubberIntegration
) )
LeafParticleRegistry.init()
// add basic block support instances as last // add basic block support instances as last
AsyncLeafDiscovery.init() AsyncLeafDiscovery.init()
AsyncGrassDiscovery.init() AsyncGrassDiscovery.init()
@@ -84,28 +77,5 @@ object Client {
configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>() configListeners = listOf(renderers, singletons, integrations).flatten().filterIsInstance<IConfigChangeListener>()
configListeners.forEach { it.onConfigChange() } 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")
}
} }

View File

@@ -1,7 +1,6 @@
@file:JvmName("Hooks") @file:JvmName("Hooks")
package mods.betterfoliage.client package mods.betterfoliage.client
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
@@ -9,16 +8,13 @@ import mods.betterfoliage.client.render.*
import mods.octarinecore.ThreadLocalDelegate import mods.octarinecore.ThreadLocalDelegate
import mods.octarinecore.client.render.* import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.DefaultLightingCtx import mods.octarinecore.client.render.lighting.DefaultLightingCtx
import mods.octarinecore.client.render.lighting.LightingCtx
import mods.octarinecore.common.plus import mods.octarinecore.common.plus
import mods.octarinecore.metaprog.allAvailable
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.Blocks import net.minecraft.block.Blocks
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.BlockRendererDispatcher import net.minecraft.client.renderer.BlockRendererDispatcher
import net.minecraft.client.renderer.BufferBuilder import net.minecraft.client.renderer.BufferBuilder
import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.client.world.ClientWorld import net.minecraft.client.world.ClientWorld
import net.minecraft.util.BlockRenderLayer import net.minecraft.util.BlockRenderLayer
import net.minecraft.util.BlockRenderLayer.CUTOUT import net.minecraft.util.BlockRenderLayer.CUTOUT

View File

@@ -1,14 +1,17 @@
package mods.betterfoliage.client.config package mods.betterfoliage.client.config
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.octarinecore.common.config.* import mods.octarinecore.common.config.*
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.config.ModConfig
private fun featureEnable() = boolean(true).lang("enabled") private fun featureEnable() = boolean(true).lang("enabled")
// Config singleton // 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 enabled by boolean(true)
val nVidia by boolean(false) val nVidia by boolean(false)
@@ -160,7 +163,15 @@ object BlockConfig {
val cactus = blocks("cactus_default.cfg") val cactus = blocks("cactus_default.cfg")
val netherrack = blocks("netherrack_blocks_default.cfg") val netherrack = blocks("netherrack_blocks_default.cfg")
init { BetterFoliage.modBus.register(this) } init { BetterFoliageMod.bus.register(this) }
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) } private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, ResourceLocation(BetterFoliageMod.MOD_ID, cfgName)).apply { list.add(this) }
private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) } private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, ResourceLocation(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()
} }
}
} }

View File

@@ -18,6 +18,7 @@ import mods.octarinecore.metaprog.ClassRef.Companion.boolean
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.resources.IResourceManager
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader import net.minecraft.world.IBlockReader
import net.minecraftforge.fml.ModList 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 override val logger = BetterFoliage.logDetail
var idToValue = emptyMap<Identifier, LeafInfo>() var idToValue = emptyMap<Identifier, LeafInfo>()
@@ -79,11 +80,11 @@ object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider, ModelRenderRegist
return idToValue[textureLoc] 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>>() val futures = mutableMapOf<Identifier, CompletableFuture<LeafInfo>>()
return StitchPhases( return StitchPhases(
discovery = bakeryFuture.thenRunAsync { discovery = bakeryF.thenRunAsync {
val allLeaves = TextureLeaves_leafTextures.getStatic() val allLeaves = TextureLeaves_leafTextures.getStatic()
allLeaves.entries.forEach { (type, leaves) -> allLeaves.entries.forEach { (type, leaves) ->
log("base leaf type $type") 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() } idToValue = futures.mapValues { it.value.get() }
} }
) )
@@ -124,7 +125,7 @@ object ForestryLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
val heartSprite = atlas.sprite(heart) val heartSprite = atlas.sprite(heart)
val barkSprite = atlas.sprite(bark) val barkSprite = atlas.sprite(bark)
return atlas.afterStitch { return atlas.mapAfter {
SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get())) SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get()))
} }
} }

View File

@@ -1,5 +1,6 @@
package mods.betterfoliage.client.integration package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.octarinecore.* import mods.octarinecore.*
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
@@ -22,7 +23,7 @@ object OptifineCustomColors {
val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier) val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
init { 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() } val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() }

View File

@@ -1,7 +1,6 @@
package mods.betterfoliage.client.integration package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.render.LogRegistry import mods.betterfoliage.client.render.LogRegistry
import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo
@@ -13,14 +12,12 @@ import mods.octarinecore.common.rotate
import mods.octarinecore.metaprog.ClassRef import mods.octarinecore.metaprog.ClassRef
import mods.octarinecore.metaprog.allAvailable import mods.octarinecore.metaprog.allAvailable
import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.client.renderer.texture.AtlasTexture
import net.minecraft.client.renderer.texture.TextureAtlasSprite import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.util.Direction import net.minecraft.util.Direction
import net.minecraft.util.Direction.* import net.minecraft.util.Direction.*
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraftforge.fml.ModList import net.minecraftforge.fml.ModList
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.apache.logging.log4j.Logger
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
object IC2RubberIntegration { object IC2RubberIntegration {
@@ -29,9 +26,9 @@ object IC2RubberIntegration {
init { init {
if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) { 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) LogRegistry.registries.add(IC2LogDiscovery)
AsyncSpriteProviderManager.providers.add(IC2LogDiscovery) BetterFoliage.blockSprites.providers.add(IC2LogDiscovery)
} }
} }
} }
@@ -42,9 +39,9 @@ object TechRebornRubberIntegration {
init { init {
if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) { 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) 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]}") log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}")
val endSprite = atlas.sprite(textureNames[0]) val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1]) val sideSprite = atlas.sprite(textureNames[1])
return atlas.afterStitch { return atlas.mapAfter {
SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get())) SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
} }
} }
@@ -111,9 +108,9 @@ object IC2LogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
val downSprite = atlas.sprite(textureNames[1]) val downSprite = atlas.sprite(textureNames[1])
val sideSprite = atlas.sprite(textureNames[2]) val sideSprite = atlas.sprite(textureNames[2])
val spotSprite = atlas.sprite(textureNames[3]) 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())) 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())) 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 endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1]) val sideSprite = atlas.sprite(textureNames[1])
val sapSprite = atlas.sprite(textureNames[2]) 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())) 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" }) { if (textureNames.all { it != "missingno" }) {
val endSprite = atlas.sprite(textureNames[0]) val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1]) val sideSprite = atlas.sprite(textureNames[1])
return atlas.afterStitch { return atlas.mapAfter {
SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get())) SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
} }
} }

View File

@@ -1,5 +1,6 @@
package mods.betterfoliage.client.integration package mods.betterfoliage.client.integration
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
@@ -37,7 +38,7 @@ object ShadersModIntegration {
} }
init { 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. */ /** 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. */ /** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = 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. */ /** Quads rendered inside this block will behave as leaf blocks in shader programs. */
inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) = 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)
} }

View File

@@ -1,8 +1,10 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.AbstractEntityFX import mods.octarinecore.client.render.AbstractEntityFX
import mods.octarinecore.client.resource.Atlas import mods.octarinecore.client.resource.Atlas
import mods.octarinecore.client.resource.ResourceHandler 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, prevPos = previous,
size = scale, size = scale,
alpha = alpha, alpha = alpha,
icon = RisingSoulTextures.trackIcon.icon!! icon = RisingSoulTextures.trackIcon
) )
} }
} }
} }
object RisingSoulTextures : ResourceHandler(BetterFoliage.MOD_ID, BetterFoliage.modBus, targetAtlas = Atlas.PARTICLES) { object RisingSoulTextures : ResourceHandler(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus, targetAtlas = Atlas.PARTICLES) {
val headIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "rising_soul_$idx") } val headIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "rising_soul_$idx") }
val trackIcon = iconStatic(BetterFoliage.MOD_ID, "soul_track") val trackIcon by sprite(Identifier(BetterFoliageMod.MOD_ID, "soul_track"))
override fun afterPreStitch() {
Client.log(DEBUG, "Registered ${headIcons.num} soul particle textures")
}
} }

View File

@@ -1,6 +1,7 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.integration.ShadersModIntegration
@@ -12,17 +13,13 @@ import net.minecraft.util.ResourceLocation
import net.minecraft.world.biome.Biome import net.minecraft.world.biome.Biome
import org.apache.logging.log4j.Level.DEBUG 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 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) } 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) = override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.algae.enabled && Config.enabled && Config.algae.enabled &&
ctx.state(up2).material == Material.WATER && ctx.state(up2).material == Material.WATER &&
@@ -38,7 +35,7 @@ class RenderAlgae : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus)
ShadersModIntegration.grass(ctx, Config.algae.shaderWind) { ShadersModIntegration.grass(ctx, Config.algae.shaderWind) {
ctx.render( ctx.render(
algaeModels[rand[2]], algaeModels[rand[2]],
icon = { _, qi, _ -> algaeIcons[rand[qi and 1]]!! } icon = { _, qi, _ -> algaeIcons[rand[qi and 1]] }
) )
} }
} }

View File

@@ -1,7 +1,7 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.render.column.ColumnTextureInfo import mods.betterfoliage.client.render.column.ColumnTextureInfo
import mods.betterfoliage.client.render.column.SimpleColumnInfo import mods.betterfoliage.client.render.column.SimpleColumnInfo
@@ -15,7 +15,6 @@ import mods.octarinecore.common.config.SimpleBlockMatcher
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.block.CactusBlock import net.minecraft.block.CactusBlock
import net.minecraft.util.Direction.* import net.minecraft.util.Direction.*
import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level.DEBUG import org.apache.logging.log4j.Level.DEBUG
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@@ -25,7 +24,7 @@ object AsyncCactusDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side")) override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? { override fun processModel(state: BlockState, textures: List<String>, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
val sprites = textures.map { atlas.sprite(Identifier(it)) } val sprites = textures.map { atlas.sprite(Identifier(it)) }
return atlas.afterStitch { return atlas.mapAfter {
SimpleColumnInfo( SimpleColumnInfo(
Axis.Y, Axis.Y,
sprites[0].get(), sprites[0].get(),
@@ -36,17 +35,17 @@ object AsyncCactusDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
} }
fun init() { 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 cactusStemRadius = 0.4375
val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] } val cactusArmRotation = listOf(NORTH, SOUTH, EAST, WEST).map { Rotation.rot90[it.ordinal] }
val iconCross = iconStatic(ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_cactus")) val iconCross by sprite(Identifier(BetterFoliageMod.MOD_ID, "blocks/better_cactus"))
val iconArm = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_cactus_arm_$idx") } val iconArm = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_cactus_arm_$idx") }
val modelStem = model { val modelStem = model {
horizontalRectangle(x1 = -cactusStemRadius, x2 = cactusStemRadius, z1 = -cactusStemRadius, z2 = cactusStemRadius, y = 0.5) 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() .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 = override fun isEligible(ctx: CombinedContext): Boolean =
Config.enabled && Config.cactus.enabled && Config.enabled && Config.cactus.enabled &&
AsyncCactusDiscovery[ctx] != null AsyncCactusDiscovery[ctx] != null
@@ -97,13 +92,13 @@ class RenderCactus : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus)
) )
ctx.render( ctx.render(
modelCross[ctx.semiRandom(0)], modelCross[ctx.semiRandom(0)],
icon = { _, _, _ -> iconCross.icon!!} icon = { _, _, _ -> iconCross }
) )
ctx.render( ctx.render(
modelArm[ctx.semiRandom(1)], modelArm[ctx.semiRandom(1)],
cactusArmRotation[ctx.semiRandom(2) % 4], cactusArmRotation[ctx.semiRandom(2) % 4],
icon = { _, _, _ -> iconArm[ctx.semiRandom(3)]!!} icon = { _, _, _ -> iconArm[ctx.semiRandom(3)] }
) )
} }
} }

View File

@@ -1,6 +1,6 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.texture.GrassRegistry import mods.betterfoliage.client.texture.GrassRegistry
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
@@ -10,7 +10,7 @@ import mods.octarinecore.common.horizontalDirections
import mods.octarinecore.common.offset import mods.octarinecore.common.offset
import net.minecraft.tags.BlockTags 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) = override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.connectedGrass.enabled && Config.enabled && Config.connectedGrass.enabled &&
BlockTags.DIRT_LIKE.contains(ctx.state.block) && 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) = override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass && Config.enabled && Config.roundLogs.enabled && Config.roundLogs.connectGrass &&

View File

@@ -1,34 +1,27 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.octarinecore.client.render.* import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.* 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.allDirections
import mods.octarinecore.common.offset
import mods.octarinecore.random import mods.octarinecore.random
import net.minecraft.block.material.Material 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.Axis
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraft.world.biome.Biome import net.minecraft.world.biome.Biome
import net.minecraftforge.client.model.data.IModelData
import org.apache.logging.log4j.Level.DEBUG 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 noise = simplexNoise()
val coralIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_coral_$idx") } val coralIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_coral_$idx") }
val crustIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_crust_$idx") } val crustIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_crust_$idx") }
val coralModels = modelSet(64) { modelIdx -> val coralModels = modelSet(64) { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = 0.0, yTop = 1.0) 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) .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) = override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.coral.enabled && Config.enabled && Config.coral.enabled &&
(ctx.state(up2).material == Material.WATER || Config.coral.shallowWater) && (ctx.state(up2).material == Material.WATER || Config.coral.shallowWater) &&
@@ -67,7 +55,7 @@ class RenderCoral : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus)
ctx.render( ctx.render(
coralModels[variation++], coralModels[variation++],
rotationFromUp[idx], 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)] }
) )
} }
} }

View File

@@ -1,10 +1,13 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.OptifineCustomColors import mods.betterfoliage.client.integration.OptifineCustomColors
import mods.betterfoliage.client.integration.ShadersModIntegration 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.betterfoliage.client.texture.GrassRegistry
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.Model import mods.octarinecore.client.render.Model
@@ -18,10 +21,9 @@ import mods.octarinecore.common.allDirections
import mods.octarinecore.random import mods.octarinecore.random
import net.minecraft.tags.BlockTags import net.minecraft.tags.BlockTags
import net.minecraft.util.Direction.* import net.minecraft.util.Direction.*
import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level.DEBUG 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 { companion object {
@JvmStatic fun grassTopQuads(heightMin: Double, heightMax: Double): Model.(Int)->Unit = { modelIdx -> @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 noise = simplexNoise()
val normalIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx") } val normalIcons = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_grass_long_$idx") }
val snowedIcons = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_grass_snowed_$idx") } val snowedIcons = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_grass_snowed_$idx") }
val normalGenIcon = iconStatic { Client.genGrass.register(texture = "minecraft:blocks/tallgrass", isSnowed = false) } val normalGenIcon by sprite { GeneratedGrass(sprite = "minecraft:blocks/tallgrass", isSnowed = false).register(BetterFoliage.asyncPack) }
val snowedGenIcon = iconStatic { Client.genGrass.register(texture = "minecraft:blocks/tallgrass", isSnowed = true) } 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) } 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) = override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.enabled &&
(Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) && (Config.shortGrass.grassEnabled || Config.connectedGrass.enabled) &&
@@ -97,7 +94,7 @@ class RenderGrass : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus)
ctx.render( ctx.render(
grassModels[rand[0]], grassModels[rand[0]],
translation = ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero), 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) } postProcess = { _, _, _, _, _ -> if (isSnowed) setGrey(1.0f) else multiplyColor(grass.overrideColor ?: blockColor) }
) )
} }

View File

@@ -1,9 +1,10 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.OptifineCustomColors import mods.betterfoliage.client.integration.OptifineCustomColors
import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.resource.Identifier
import mods.betterfoliage.client.texture.LeafRegistry import mods.betterfoliage.client.texture.LeafRegistry
import mods.octarinecore.PI2 import mods.octarinecore.PI2
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
@@ -17,11 +18,10 @@ import mods.octarinecore.common.allDirections
import mods.octarinecore.common.vec import mods.octarinecore.common.vec
import mods.octarinecore.random import mods.octarinecore.random
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
import java.lang.Math.cos import java.lang.Math.cos
import java.lang.Math.sin import java.lang.Math.sin
class RenderLeaves : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus) { class RenderLeaves : RenderDecorator(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus) {
val leavesModel = model { 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) 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) .scale(Config.leaves.size)
.toCross(UP).addAll() .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 perturbs = vectorSet(64) { idx ->
val angle = PI2 * idx / 64.0 val angle = PI2 * idx / 64.0

View File

@@ -1,20 +1,21 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.lighting.FlatOffsetNoColor import mods.octarinecore.client.render.lighting.FlatOffsetNoColor
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import net.minecraft.util.Direction.DOWN import net.minecraft.util.Direction.DOWN
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level.DEBUG 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 { val rootModel = model {
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -1.5, yTop = -0.5) 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)) .setFlatShader(FlatOffsetNoColor(Int3.zero))
.toCross(UP).addAll() .toCross(UP).addAll()
} }
val rootIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_lilypad_roots_$idx") } val rootIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_roots_$idx") }
val flowerIcon = iconSet { idx -> ResourceLocation(BetterFoliage.MOD_ID, "blocks/better_lilypad_flower_$idx") } val flowerIcon = spriteSet { idx -> Identifier(BetterFoliageMod.MOD_ID, "blocks/better_lilypad_flower_$idx") }
val perturbs = vectorSet(64) { modelIdx -> xzDisk(modelIdx) * Config.lilypad.hOffset } 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 = override fun isEligible(ctx: CombinedContext): Boolean =
Config.enabled && Config.lilypad.enabled && Config.enabled && Config.lilypad.enabled &&
BlockConfig.lilypad.matchesClass(ctx.state.block) BlockConfig.lilypad.matchesClass(ctx.state.block)
@@ -49,7 +45,7 @@ class RenderLilypad : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus
rootModel.model, rootModel.model,
translation = ctx.blockCenter.add(perturbs[rand[2]]), translation = ctx.blockCenter.add(perturbs[rand[2]]),
forceFlat = true, 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, flowerModel.model,
translation = ctx.blockCenter.add(perturbs[rand[4]]), translation = ctx.blockCenter.add(perturbs[rand[4]]),
forceFlat = true, forceFlat = true,
icon = { _, _, _ -> flowerIcon[rand[0]]!! } icon = { _, _, _ -> flowerIcon[rand[0]] }
) )
} }
} }

View File

@@ -1,6 +1,7 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
@@ -20,7 +21,7 @@ import net.minecraft.util.Direction.Axis
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import java.util.concurrent.CompletableFuture 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 override val renderOnCutout: Boolean get() = false
@@ -57,7 +58,7 @@ object AsyncLogDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
val axis = getAxis(state) val axis = getAxis(state)
logger.log(Level.DEBUG, "$logName: axis $axis") logger.log(Level.DEBUG, "$logName: axis $axis")
val spriteList = textures.map { atlas.sprite(Identifier(it)) } val spriteList = textures.map { atlas.sprite(Identifier(it)) }
return atlas.afterStitch { return atlas.mapAfter {
SimpleColumnInfo( SimpleColumnInfo(
axis, axis,
spriteList[0].get(), spriteList[0].get(),
@@ -80,6 +81,6 @@ object AsyncLogDiscovery : ConfigurableModelDiscovery<ColumnTextureInfo>() {
fun init() { fun init() {
LogRegistry.registries.add(this) LogRegistry.registries.add(this)
AsyncSpriteProviderManager.providers.add(this) BetterFoliage.blockSprites.providers.add(this)
} }
} }

View File

@@ -1,26 +1,23 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.noPost import mods.octarinecore.client.render.noPost
import mods.octarinecore.common.Double3 import mods.octarinecore.common.Double3
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level.DEBUG 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) } 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 { override fun isEligible(ctx: CombinedContext): Boolean {
if (!Config.enabled || !Config.shortGrass.myceliumEnabled) return false if (!Config.enabled || !Config.shortGrass.myceliumEnabled) return false
return BlockConfig.mycelium.matchesClass(ctx.state.block) return BlockConfig.mycelium.matchesClass(ctx.state.block)
@@ -38,7 +35,7 @@ class RenderMycelium : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBu
ctx.render( ctx.render(
myceliumModel[rand[0]], myceliumModel[rand[0]],
translation = ctx.blockCenter + (if (isSnowed) snowOffset else Double3.zero), 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 postProcess = if (isSnowed) whitewash else noPost
) )
} }

View File

@@ -1,27 +1,21 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.* import mods.octarinecore.client.render.*
import mods.octarinecore.client.render.lighting.* import mods.octarinecore.client.render.lighting.*
import mods.octarinecore.common.Int3
import mods.octarinecore.common.Rotation
import mods.octarinecore.random 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.Axis
import net.minecraft.util.Direction.* 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 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 -> val netherrackModel = modelSet(64) { modelIdx ->
verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yTop = -0.5, 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)) 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 { override fun isEligible(ctx: CombinedContext): Boolean {
if (!Config.enabled || !Config.netherrack.enabled) return false if (!Config.enabled || !Config.netherrack.enabled) return false
return BlockConfig.netherrack.matchesClass(ctx.state.block) return BlockConfig.netherrack.matchesClass(ctx.state.block)
@@ -48,7 +38,7 @@ class RenderNetherrack : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.mod
val rand = ctx.semiRandomArray(2) val rand = ctx.semiRandomArray(2)
ctx.render( ctx.render(
netherrackModel[rand[0]], netherrackModel[rand[0]],
icon = { _, qi, _ -> netherrackIcon[rand[qi and 1]]!! } icon = { _, qi, _ -> netherrackIcon[rand[qi and 1]] }
) )
} }
} }

View File

@@ -1,23 +1,28 @@
package mods.betterfoliage.client.render package mods.betterfoliage.client.render
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.BetterFoliageMod
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.Config import mods.betterfoliage.client.config.Config
import mods.betterfoliage.client.integration.ShadersModIntegration import mods.betterfoliage.client.integration.ShadersModIntegration
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.render.CombinedContext import mods.octarinecore.client.render.CombinedContext
import mods.octarinecore.client.render.RenderDecorator import mods.octarinecore.client.render.RenderDecorator
import mods.octarinecore.client.render.lighting.FlatOffsetNoColor import mods.octarinecore.client.render.lighting.FlatOffsetNoColor
import mods.octarinecore.client.resource.CenteredSprite
import mods.octarinecore.random import mods.octarinecore.random
import net.minecraft.block.material.Material import net.minecraft.block.material.Material
import net.minecraft.tags.BlockTags import net.minecraft.tags.BlockTags
import net.minecraft.util.Direction.UP import net.minecraft.util.Direction.UP
import net.minecraft.util.ResourceLocation
import org.apache.logging.log4j.Level.DEBUG 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 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 reedModels = modelSet(64) { modelIdx ->
val height = random(Config.reed.heightMin, Config.reed.heightMax) val height = random(Config.reed.heightMin, Config.reed.heightMax)
val waterline = 0.875f 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) = override fun isEligible(ctx: CombinedContext) =
Config.enabled && Config.reed.enabled && Config.enabled && Config.reed.enabled &&
ctx.state(up2).material == Material.AIR && ctx.state(up2).material == Material.AIR &&
@@ -59,7 +60,7 @@ class RenderReeds : RenderDecorator(BetterFoliage.MOD_ID, BetterFoliage.modBus)
ctx.render( ctx.render(
reedModels[ctx.semiRandom(0)], reedModels[ctx.semiRandom(0)],
forceFlat = true, forceFlat = true,
icon = { _, _, _ -> reedIcons[iconVar]!! } icon = { _, _, _ -> reedIcons[iconVar] }
) )
} }
} }

View File

@@ -1,5 +1,6 @@
package mods.betterfoliage.client.render.column package mods.betterfoliage.client.render.column
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client import mods.betterfoliage.client.Client
import mods.betterfoliage.client.chunk.ChunkOverlayManager import mods.betterfoliage.client.chunk.ChunkOverlayManager
import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs import mods.betterfoliage.client.integration.ShadersModIntegration.renderAs
@@ -97,7 +98,7 @@ abstract class AbstractRenderColumn(modId: String, modBus: IEventBus) : RenderDe
ColumnLayerData.SkipRender -> return ColumnLayerData.SkipRender -> return
ColumnLayerData.NormalRender -> return ctx.render() ColumnLayerData.NormalRender -> return ctx.render()
ColumnLayerData.ResolveError, null -> { ColumnLayerData.ResolveError, null -> {
Client.logRenderError(ctx.state, ctx.pos) BetterFoliage.logRenderError(ctx.state, ctx.pos)
return ctx.render() return ctx.render()
} }
} }

View File

@@ -1,11 +1,9 @@
package mods.betterfoliage.client.texture package mods.betterfoliage.client.texture
import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.client.resource.* import mods.octarinecore.client.resource.*
import mods.octarinecore.client.resource.Atlas import net.minecraft.resources.IResourceManager
import net.minecraft.util.ResourceLocation
import net.minecraftforge.resource.VanillaResourceType.TEXTURES
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.InputStream
/** /**
* Generate Short Grass textures from [Blocks.tallgrass] block textures. * Generate Short Grass textures from [Blocks.tallgrass] block textures.
@@ -13,16 +11,13 @@ import java.io.InputStream
* *
* @param[domain] Resource domain of generator * @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)) 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 baseTexture = resourceManager[Atlas.BLOCKS.wrap(key.texture)]?.loadImage() ?: return null
val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR) val result = BufferedImage(baseTexture.width, baseTexture.height, BufferedImage.TYPE_4BYTE_ABGR)
val graphics = result.createGraphics() val graphics = result.createGraphics()
@@ -31,7 +26,7 @@ class GrassGenerator(domain: String) : GeneratorBase<GrassGenerator.Key>(domain,
val frames = baseTexture.height / size val frames = baseTexture.height / size
// iterate all frames // 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 baseFrame = baseTexture.getSubimage(0, size * frame, size, size)
val grassFrame = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR) 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 // blend with white if snowed
if (key.isSnowed) { if (isSnowed) {
for (x in 0..result.width - 1) for (y in 0..result.height - 1) { for (x in 0..result.width - 1) for (y in 0..result.height - 1) {
result[x, y] = blendRGB(result[x, y], 16777215, 2, 3) result[x, y] = blendRGB(result[x, y], 16777215, 2, 3)
} }
} }
return result.bytes
return result.asStream
} }
data class Key(val texture: ResourceLocation, val isSnowed: Boolean)
} }

View File

@@ -1,47 +1,38 @@
package mods.betterfoliage.client.texture package mods.betterfoliage.client.texture
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliageMod
import mods.octarinecore.client.resource.* import mods.octarinecore.client.resource.*
import mods.octarinecore.client.resource.Atlas
import net.minecraft.resources.IResource import net.minecraft.resources.IResource
import net.minecraft.resources.IResourceManager
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraftforge.resource.VanillaResourceType.TEXTURES
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.InputStream
/** /**
* Generate round leaf textures from leaf block textures. * 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. * 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 * @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 size = baseTexture.width
val frames = baseTexture.height / size 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) fun scale(i: Int) = i * maskTexture!!.width / (size * 2)
val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR) val leafTexture = BufferedImage(size * 2, size * 2 * frames, BufferedImage.TYPE_4BYTE_ABGR)
val graphics = leafTexture.createGraphics() val graphics = leafTexture.createGraphics()
// iterate all frames // 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 baseFrame = baseTexture.getSubimage(0, size * frame, size, size)
val leafFrame = BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR) 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 // overlay alpha mask
if (maskTexture != null) { 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 basePixel = leafFrame[x, y].toLong() and 0xFFFFFFFFL
val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL val maskPixel = maskTexture[scale(x), scale(y)].toLong() and 0xFF000000L or 0xFFFFFFL
leafFrame[x, y] = (basePixel and maskPixel).toInt() 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) 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. * @param[maxSize] Preferred mask size.
*/ */
fun getLeafMask(type: String, maxSize: Int) = getMultisizeTexture(maxSize) { 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 } while(size > 2) { sizes.add(size); size /= 2 }
return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull() return sizes.map { resourceManager[maskPath(it)] }.filterNotNull().firstOrNull()
} }
data class Key(val texture: ResourceLocation, val leafType: String)
} }

View File

@@ -41,11 +41,11 @@ object AsyncGrassDiscovery : ConfigurableModelDiscovery<GrassInfo>() {
val textureName = textures[0] val textureName = textures[0]
val spriteF = atlas.sprite(Identifier(textureName)) val spriteF = atlas.sprite(Identifier(textureName))
logger.log(Level.DEBUG, "$logName: texture $textureName") logger.log(Level.DEBUG, "$logName: texture $textureName")
return atlas.afterStitch { return atlas.mapAfter {
val sprite = spriteF.get() val sprite = spriteF.get()
logger.log(Level.DEBUG, "$logName: block state $state") logger.log(Level.DEBUG, "$logName: block state $state")
logger.log(Level.DEBUG, "$logName: texture $textureName") 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) { val overrideColor = if (hsb.saturation >= Config.shortGrass.saturationThreshold) {
logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}") logger.log(Level.DEBUG, "$logName: brightness ${hsb.brightness}")
logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color") logger.log(Level.DEBUG, "$logName: saturation ${hsb.saturation} >= ${Config.shortGrass.saturationThreshold}, using texture color")
@@ -60,6 +60,6 @@ object AsyncGrassDiscovery : ConfigurableModelDiscovery<GrassInfo>() {
fun init() { fun init() {
GrassRegistry.registries.add(this) GrassRegistry.registries.add(this)
AsyncSpriteProviderManager.providers.add(this) BetterFoliage.blockSprites.providers.add(this)
} }
} }

View File

@@ -1,42 +1,57 @@
package mods.betterfoliage.client.texture package mods.betterfoliage.client.texture
import mods.betterfoliage.BetterFoliage 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.client.resource.*
import mods.octarinecore.stripStart import mods.octarinecore.stripStart
import mods.octarinecore.client.resource.Atlas 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.minecraft.util.ResourceLocation
import net.minecraftforge.client.event.TextureStitchEvent import java.util.concurrent.CompletableFuture
import net.minecraftforge.eventbus.api.SubscribeEvent
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 targetAtlas = Atlas.PARTICLES
val typeMappings = TextureMatcher() val typeMappings = TextureMatcher()
val particles = hashMapOf<String, IconSet>() val particles = hashMapOf<String, SpriteSet>()
operator fun get(type: String) = particles[type] ?: particles["default"]!! operator fun get(type: String) = particles[type] ?: particles["default"]!!
init { BetterFoliage.modBus.register(this) } override fun setup(manager: IResourceManager, particleF: CompletableFuture<ParticleManager>, atlasFuture: AtlasFuture): StitchPhases {
@SubscribeEvent
fun handlePreStitch(event: TextureStitchEvent.Pre) {
if (!targetAtlas.matches(event)) return
particles.clear() 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() return StitchPhases(
allTypes.forEach { leafType -> discovery = particleF.sinkAsync {
val particleSet = IconSet(Atlas.PARTICLES) { typeMappings.loadMappings(Identifier(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
idx -> ResourceLocation(BetterFoliage.MOD_ID, "falling_leaf_${leafType}_$idx") (typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
}.apply { onPreStitch(event) } val ids = (0 until 16).map { idx -> Identifier(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") }
if (leafType == "default" || particleSet.num > 0) particles[leafType] = particleSet 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 init() {
fun handlePostStitch(event: TextureStitchEvent.Post) { BetterFoliage.particleSprites.providers.add(this)
if (!targetAtlas.matches(event)) return
particles.forEach { (_, particleSet) -> particleSet.onPostStitch(event.map) }
} }
} }

View File

@@ -1,7 +1,6 @@
package mods.betterfoliage.client.texture package mods.betterfoliage.client.texture
import mods.betterfoliage.BetterFoliage import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.Client
import mods.betterfoliage.client.config.BlockConfig import mods.betterfoliage.client.config.BlockConfig
import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.resource.Identifier
import mods.octarinecore.HasLogger import mods.octarinecore.HasLogger
@@ -26,7 +25,7 @@ class LeafInfo(
val averageColor: Int = roundLeafTexture.averageColor val averageColor: Int = roundLeafTexture.averageColor
) { ) {
/** [IconSet] of the textures to use for leaf particles emitted from this block. */ /** [IconSet] of the textures to use for leaf particles emitted from this block. */
val particleTextures: IconSet get() = LeafParticleRegistry[leafType] val particleTextures: SpriteSet get() = LeafParticleRegistry[leafType]
} }
object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>() object LeafRegistry : ModelRenderRegistryRoot<LeafInfo>()
@@ -40,18 +39,18 @@ object AsyncLeafDiscovery : ConfigurableModelDiscovery<LeafInfo>() {
fun init() { fun init() {
LeafRegistry.registries.add(this) LeafRegistry.registries.add(this)
AsyncSpriteProviderManager.providers.add(this) BetterFoliage.blockSprites.providers.add(this)
} }
} }
fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture<LeafInfo> { fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: AtlasFuture): CompletableFuture<LeafInfo> {
val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default" 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) val roundLeaf = atlas.sprite(generated)
log(" leaf texture $sprite") log(" leaf texture $sprite")
log(" particle $leafType") log(" particle $leafType")
return atlas.afterStitch { return atlas.mapAfter {
LeafInfo(roundLeaf.get(), leafType) LeafInfo(roundLeaf.get(), leafType)
} }
} }

View File

@@ -1,6 +1,13 @@
@file:JvmName("Utils") @file:JvmName("Utils")
package mods.betterfoliage.client.texture 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 { 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 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) val g = (((rgb1 shr 8) and 255) * weight1 + ((rgb2 shr 8) and 255) * weight2) / (weight1 + weight2)
@@ -9,3 +16,5 @@ fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
val result = ((a shl 24) or (r shl 16) or (g shl 8) or b).toInt() val result = ((a shl 24) or (r shl 16) or (g shl 8) or b).toInt()
return result return result
} }
fun IResourceManager.loadSprite(id: Identifier) = this.get(id)?.loadImage() ?: throw IOException("Cannot load resource $id")

View File

@@ -2,58 +2,86 @@ package mods.octarinecore.client.resource
import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.resource.Identifier
import mods.betterfoliage.client.resource.Sprite import mods.betterfoliage.client.resource.Sprite
import net.minecraft.client.Minecraft import mods.octarinecore.common.map
import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.client.renderer.texture.AtlasTexture
import net.minecraft.client.renderer.texture.MissingTextureSprite
import net.minecraft.profiler.IProfiler import net.minecraft.profiler.IProfiler
import net.minecraft.resources.IResourceManager import net.minecraft.resources.IResourceManager
import java.util.* import java.util.*
import java.util.concurrent.CompletableFuture 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( class StitchPhases(
val discovery: CompletableFuture<Void>, val discovery: CompletableFuture<Void>,
val cleanup: CompletableFuture<Void> val cleanup: CompletableFuture<Void>
) )
interface AsyncSpriteProvider { interface AsyncSpriteProvider<SOURCE: Any> {
fun setup(bakeryFuture: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases 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())

View File

@@ -1,23 +1,17 @@
package mods.octarinecore.client.resource package mods.octarinecore.client.resource
import net.minecraft.util.ResourceLocation import mods.betterfoliage.client.resource.Identifier
import net.minecraftforge.resource.VanillaResourceType import mods.betterfoliage.client.texture.loadSprite
import net.minecraft.resources.IResourceManager
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.InputStream import java.lang.Math.max
import java.lang.Math.*
class CenteringTextureGenerator( data class CenteredSprite(val sprite: Identifier, val atlas: Atlas = Atlas.BLOCKS, val aspectHeight: Int = 1, val aspectWidth: Int = 1) {
domain: String,
val aspectWidth: Int,
val aspectHeight: Int
) : GeneratorBase<ResourceLocation>(domain, VanillaResourceType.TEXTURES) {
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)) fun draw(resourceManager: IResourceManager): ByteArray {
val baseTexture = resourceManager.loadSprite(atlas.wrap(sprite))
override fun get(key: ResourceLocation): InputStream? {
val baseTexture = resourceManager[Atlas.BLOCKS.wrap(key)]?.loadImage() ?: return null
val frameWidth = baseTexture.width val frameWidth = baseTexture.width
val frameHeight = baseTexture.width * aspectHeight / aspectWidth val frameHeight = baseTexture.width * aspectHeight / aspectWidth
@@ -28,7 +22,7 @@ class CenteringTextureGenerator(
val graphics = resultTexture.createGraphics() val graphics = resultTexture.createGraphics()
// iterate all frames // 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 baseFrame = baseTexture.getSubimage(0, size * frame, frameWidth, frameHeight)
val resultFrame = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR) val resultFrame = BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR)
@@ -38,6 +32,6 @@ class CenteringTextureGenerator(
graphics.drawImage(resultFrame, 0, size * frame, null) graphics.drawImage(resultFrame, 0, size * frame, null)
} }
return resultTexture.asStream return resultTexture.bytes
} }
} }

View File

@@ -5,17 +5,18 @@ import mods.betterfoliage.client.resource.ModelIdentifier
import mods.octarinecore.HasLogger import mods.octarinecore.HasLogger
import mods.octarinecore.client.render.BlockCtx import mods.octarinecore.client.render.BlockCtx
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import mods.octarinecore.common.config.ConfigurableBlockMatcher
import mods.octarinecore.common.config.IBlockMatcher import mods.octarinecore.common.config.IBlockMatcher
import mods.octarinecore.common.config.ModelTextureList import mods.octarinecore.common.config.ModelTextureList
import mods.octarinecore.common.plus import mods.octarinecore.common.plus
import mods.octarinecore.findFirst import mods.octarinecore.findFirst
import mods.octarinecore.common.sinkAsync
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.renderer.BlockModelShapes import net.minecraft.client.renderer.BlockModelShapes
import net.minecraft.client.renderer.model.BlockModel import net.minecraft.client.renderer.model.BlockModel
import net.minecraft.client.renderer.model.IUnbakedModel import net.minecraft.client.renderer.model.IUnbakedModel
import net.minecraft.client.renderer.model.ModelBakery import net.minecraft.client.renderer.model.ModelBakery
import net.minecraft.client.renderer.model.VariantList import net.minecraft.client.renderer.model.VariantList
import net.minecraft.resources.IResourceManager
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.world.IBlockReader 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] } 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( class ModelDiscoveryContext(
bakery: ModelBakery, bakery: ModelBakery,
val state: BlockState, 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() var modelData: Map<BlockState, T> = emptyMap()
protected set protected set
@@ -57,22 +61,22 @@ abstract class ModelDiscovery<T> : HasLogger, AsyncSpriteProvider, ModelRenderRe
abstract fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<T>? 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>>() val modelDataTemp = mutableMapOf<BlockState, CompletableFuture<T>>()
return StitchPhases( return StitchPhases(
discovery = bakeryFuture.thenRunAsync { bakery -> discovery = bakeryF.sinkAsync { bakery ->
var errors = 0 var errors = 0
bakery.iterateModels { ctx -> bakery.iterateModels { ctx ->
try { try {
processModel(ctx, atlasFuture)?.let { modelDataTemp[ctx.state] = it } processModel(ctx, atlas)?.let { modelDataTemp[ctx.state] = it }
} catch (e: Exception) { } catch (e: Exception) {
errors++ errors++
} }
} }
log("${modelDataTemp.size} BlockStates discovered, $errors 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() } modelData = modelDataTemp.filterValues { !it.isCompletedExceptionally }.mapValues { it.value.get() }
val errors = modelDataTemp.values.filter { it.isCompletedExceptionally }.size val errors = modelDataTemp.values.filter { it.isCompletedExceptionally }.size
log("${modelData.size} BlockStates loaded, $errors errors") log("${modelData.size} BlockStates loaded, $errors errors")
@@ -125,8 +129,4 @@ abstract class ConfigurableModelDiscovery<T> : ModelDiscovery<T>() {
return null return null
} }
override fun setup(bakeryFuture: CompletableFuture<ModelBakery>, atlasFuture: AtlasFuture): StitchPhases {
(matchClasses as? ConfigurableBlockMatcher)?.readDefaults()
return super.setup(bakeryFuture, atlasFuture)
}
} }

View File

@@ -1,16 +1,21 @@
package mods.octarinecore.client.resource 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.*
import net.minecraft.resources.ResourcePackType.CLIENT_RESOURCES import net.minecraft.resources.ResourcePackType.CLIENT_RESOURCES
import net.minecraft.resources.data.IMetadataSectionSerializer 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.minecraft.util.text.StringTextComponent
import net.minecraftforge.resource.IResourceType import org.apache.logging.log4j.Logger
import net.minecraftforge.resource.ISelectiveResourceReloadListener import java.io.IOException
import java.io.InputStream import java.lang.IllegalStateException
import java.util.* import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.function.Predicate import java.util.function.Predicate
import java.util.function.Supplier import java.util.function.Supplier
@@ -20,149 +25,61 @@ import java.util.function.Supplier
* @param[name] Name of the resource pack * @param[name] Name of the resource pack
* @param[generators] List of resource generators * @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 getName() = packName
override fun getResourceNamespaces(type: ResourcePackType) = if (type == CLIENT_RESOURCES) generators.map { it.namespace }.toSet() else emptySet() override fun getResourceNamespaces(type: ResourcePackType) = setOf(nameSpace)
override fun <T : Any?> getMetadata(deserializer: IMetadataSectionSerializer<T>) = null
override fun <T : Any?> getMetadata(deserializer: IMetadataSectionSerializer<T>): T? { override fun getRootResourceStream(id: String) = null
if (deserializer.sectionName != "pack") return null override fun getAllResourceLocations(type: ResourcePackType, path: String, maxDepth: Int, filter: Predicate<String>) = emptyList<Identifier>()
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 close() {} override fun close() {}
class Finder(val pack: GeneratorPack) : IPackFinder { protected var manager: CompletableFuture<IResourceManager>? = null
override fun <T : ResourcePackInfo> addPackInfosToMap(nameToPackMap: MutableMap<String, T>, packInfoFactory: ResourcePackInfo.IFactory<T>) { val identifiers = Collections.synchronizedMap(mutableMapOf<Any, Identifier>())
val packInfo = ResourcePackInfo.createResourcePack( val resources = Collections.synchronizedMap(mutableMapOf<Identifier, CompletableFuture<ByteArray>>())
pack.packName,
true, fun register(key: Any, func: (IResourceManager)->ByteArray): Identifier {
Supplier { pack } as Supplier<IResourcePack>, if (manager == null) throw IllegalStateException("Cannot register resources unless block textures are being reloaded")
packInfoFactory, identifiers[key]?.let { return it }
ResourcePackInfo.Priority.BOTTOM
val id = Identifier(nameSpace, UUID.randomUUID().toString())
val resource = manager!!.map { func(it) }
identifiers[key] = id
resources[Atlas.BLOCKS.wrap(id)] = resource
log("generated resource $key -> $id")
return id
}
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
override fun resourceExists(type: ResourcePackType, id: Identifier) =
type == CLIENT_RESOURCES && resources.containsKey(id)
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()
}
) )
nameToPackMap[pack.packName] = packInfo!! }
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)
} }
} }
} }
/**
* 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 }
init { resourceManager.addReloadListener(this) }
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)
}
fun resourceExists(location: ResourceLocation?): Boolean {
val key = location?.let { locationMapper(it) }?.path?.let { idToKey[it] } ?: return false
return exists(key)
}
fun getInputStream(location: ResourceLocation?): InputStream? {
val key = location?.let { locationMapper(it) }?.path?.let { idToKey[it] } ?: return null
return get(key)
}
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
}
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 ?: ""))
}
*/

View File

@@ -1,13 +1,16 @@
package mods.octarinecore.client.resource package mods.octarinecore.client.resource
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.client.resource.Identifier import mods.betterfoliage.client.resource.Identifier
import mods.betterfoliage.client.resource.Sprite import mods.betterfoliage.client.resource.Sprite
import mods.octarinecore.client.render.Model import mods.octarinecore.client.render.Model
import mods.octarinecore.common.Double3 import mods.octarinecore.common.Double3
import mods.octarinecore.common.Int3 import mods.octarinecore.common.Int3
import mods.octarinecore.common.completedVoid
import mods.octarinecore.common.sink
import mods.octarinecore.stripEnd import mods.octarinecore.stripEnd
import mods.octarinecore.stripStart import mods.octarinecore.stripStart
import net.minecraft.client.renderer.texture.AtlasTexture import net.minecraft.resources.IResourceManager
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper import net.minecraft.util.math.MathHelper
import net.minecraft.world.IWorld import net.minecraft.world.IWorld
@@ -18,6 +21,9 @@ import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.eventbus.api.SubscribeEvent import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.config.ModConfig import net.minecraftforge.fml.config.ModConfig
import java.util.* import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
enum class Atlas(val basePath: String) { enum class Atlas(val basePath: String) {
BLOCKS("textures"), BLOCKS("textures"),
@@ -31,10 +37,6 @@ enum class Atlas(val basePath: String) {
// ============================ // ============================
// Resource types // Resource types
// ============================ // ============================
interface IStitchListener {
fun onPreStitch(event: TextureStitchEvent.Pre)
fun onPostStitch(atlas: AtlasTexture)
}
interface IConfigChangeListener { fun onConfigChange() } interface IConfigChangeListener { fun onConfigChange() }
interface IWorldLoadListener { fun onWorldLoad(world: IWorld) } interface IWorldLoadListener { fun onWorldLoad(world: IWorld) }
@@ -52,9 +54,6 @@ open class ResourceHandler(
) { ) {
val resources = mutableListOf<Any>() val resources = mutableListOf<Any>()
open fun afterPreStitch() {}
open fun afterPostStitch() {}
// ============================ // ============================
// Self-registration // Self-registration
// ============================ // ============================
@@ -63,10 +62,11 @@ open class ResourceHandler(
// ============================ // ============================
// Resource declarations // Resource declarations
// ============================ // ============================
fun iconStatic(location: ()-> Identifier) = IconHolder(location).apply { resources.add(this) } fun sprite(id: Identifier) = sprite { id }
fun iconStatic(location: Identifier) = iconStatic { location } fun sprite(idFunc: ()->Identifier) = AsyncSpriteDelegate(idFunc).apply { BetterFoliage.getSpriteManager(targetAtlas).providers.add(this) }
fun iconStatic(domain: String, path: String) = iconStatic(Identifier(domain, path)) fun spriteSet(idFunc: (Int)->Identifier) = AsyncSpriteSet(idFunc).apply { BetterFoliage.getSpriteManager(targetAtlas).providers.add(this) }
fun iconSet(targetAtlas: Atlas = Atlas.BLOCKS, location: (Int)-> Identifier) = IconSet(targetAtlas, location).apply { this@ResourceHandler.resources.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 model(init: Model.()->Unit) = ModelHolder(init).apply { resources.add(this) }
fun modelSet(num: Int, init: Model.(Int)->Unit) = ModelSet(num, init).apply { resources.add(this) } fun modelSet(num: Int, init: Model.(Int)->Unit) = ModelSet(num, init).apply { resources.add(this) }
fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) } fun vectorSet(num: Int, init: (Int)-> Double3) = VectorSet(num, init).apply { resources.add(this) }
@@ -75,20 +75,6 @@ open class ResourceHandler(
// ============================ // ============================
// Event registration // 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 @SubscribeEvent
fun handleModConfigChange(event: ModConfig.ModConfigEvent) { fun handleModConfigChange(event: ModConfig.ModConfigEvent) {
resources.forEach { (it as? IConfigChangeListener)?.onConfigChange() } resources.forEach { (it as? IConfigChangeListener)?.onConfigChange() }
@@ -102,16 +88,49 @@ open class ResourceHandler(
// ============================ // ============================
// Resource container classes // Resource container classes
// ============================ // ============================
class IconHolder(val location: ()-> Identifier) : IStitchListener { class AsyncSpriteDelegate(val idFunc: ()->Identifier) : ReadOnlyProperty<Any, Sprite>, AsyncSpriteProvider<Any> {
var iconRes: Identifier? = null protected lateinit var value: Sprite
var icon: Sprite? = null override fun getValue(thisRef: Any, property: KProperty<*>) = value
override fun onPreStitch(event: TextureStitchEvent.Pre) {
iconRes = location() override fun setup(manager: IResourceManager, sourceF: CompletableFuture<Any>, atlas: AtlasFuture): StitchPhases {
event.addSprite(iconRes) sourceF.thenRun {
val sprite = atlas.sprite(idFunc())
atlas.runAfter {
sprite.handle { sprite, error -> value = sprite ?: atlas.missing.get()!! }
} }
override fun onPostStitch(atlas: AtlasTexture) {
icon = atlas[iconRes!!]
} }
return StitchPhases(completedVoid(), completedVoid())
}
}
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 { 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) } 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 { class ModelSet(val num: Int, val init: Model.(Int)->Unit): IConfigChangeListener {
val models = Array(num) { Model() } val models = Array(num) { Model() }
override fun onConfigChange() { (0 until num).forEach { models[it] = Model().apply{ init(it) } } } override fun onConfigChange() { (0 until num).forEach { models[it] = Model().apply{ init(it) } } }

View File

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

View File

@@ -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. */ /** Get an [InputStream] to an image object in PNG format. */
val BufferedImage.asStream: InputStream get() = val BufferedImage.asStream: InputStream get() =
ByteArrayInputStream(ByteArrayOutputStream().let { ImageIO.write(this, "PNG", it); it.toByteArray() }) 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. * Calculate the average color of a texture.
* *

View 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()!!

View File

@@ -11,7 +11,8 @@
"BlockStateMixin", "BlockStateMixin",
"ChunkRenderMixin", "ChunkRenderMixin",
"ClientWorldMixin", "ClientWorldMixin",
"ModelBakeryMixin" "ModelBakeryMixin",
"ParticleManagerMixin"
], ],
"server": [ "server": [
], ],