[WIP] Cactus, netherrack, round logs work
+ lots more cleanup + Optifine x-ray fix
This commit is contained in:
@@ -9,7 +9,9 @@ import net.minecraft.util.math.shapes.VoxelShape;
|
|||||||
import net.minecraft.world.IBlockReader;
|
import net.minecraft.world.IBlockReader;
|
||||||
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.CallbackInfoReturnable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixin overriding the {@link VoxelShape} used for the neighbor block in {@link Block}.shouldSideBeRendered().
|
* Mixin overriding the {@link VoxelShape} used for the neighbor block in {@link Block}.shouldSideBeRendered().
|
||||||
@@ -22,6 +24,7 @@ public class MixinBlock {
|
|||||||
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z";
|
private static final String shouldSideBeRendered = "Lnet/minecraft/block/Block;shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z";
|
||||||
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;func_215702_a(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;func_215702_a(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
||||||
private static final String getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
private static final String getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
||||||
|
private static final String isOpaqueCube = "Lnet/minecraft/block/Block;isOpaqueCube(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;)Z";
|
||||||
|
|
||||||
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getFaceOcclusionShape, ordinal = 1))
|
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getFaceOcclusionShape, ordinal = 1))
|
||||||
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
||||||
|
|||||||
@@ -9,11 +9,16 @@ import net.minecraft.world.IBlockReader;
|
|||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Pseudo;
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||||
|
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.CallbackInfoReturnable;
|
||||||
|
|
||||||
@Pseudo
|
@Pseudo
|
||||||
@Mixin(targets = "net.optifine.util.BlockUtils")
|
@Mixin(targets = "net.optifine.util.BlockUtils")
|
||||||
public class MixinOptifineBlockUtils {
|
public class MixinOptifineBlockUtils {
|
||||||
|
private static final String shouldSideBeRendered = "shouldSideBeRendered(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;Lnet/optifine/render/RenderEnv;)Z";
|
||||||
|
|
||||||
private static final String shouldSideBeRenderedCached = "shouldSideBeRenderedCached(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;Lnet/optifine/render/RenderEnv;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;)Z";
|
private static final String shouldSideBeRenderedCached = "shouldSideBeRenderedCached(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;Lnet/optifine/render/RenderEnv;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;)Z";
|
||||||
private static final String getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
private static final String getFaceOcclusionShape = "Lnet/minecraft/block/BlockState;getFaceOcclusionShape(Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Lnet/minecraft/util/math/shapes/VoxelShape;";
|
||||||
|
|
||||||
@@ -22,4 +27,12 @@ public class MixinOptifineBlockUtils {
|
|||||||
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
private static VoxelShape getVoxelShapeOverride(BlockState state, IBlockReader reader, BlockPos pos, Direction dir) {
|
||||||
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
|
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("UnresolvedMixinReference")
|
||||||
|
@Inject(method = shouldSideBeRendered, at = @At(value = "HEAD"), cancellable = true)
|
||||||
|
private static void shouldForceSideRender(BlockState state, IBlockReader reader, BlockPos pos, Direction face, @Coerce Object renderEnv, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (Hooks.shouldForceSideRenderOF(state, reader, pos, face)) {
|
||||||
|
cir.setReturnValue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
package mods.betterfoliage.mixin;
|
|
||||||
|
|
||||||
import mods.betterfoliage.Hooks;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Slice;
|
|
||||||
|
|
||||||
@Mixin(targets = {"net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$ChunkRender$RebuildTask"})
|
|
||||||
public class MixinOptifineChunkRender {
|
|
||||||
|
|
||||||
private static final String compile = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$ChunkRender$RebuildTask;compile(FFFLnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$CompiledChunk;Lnet/minecraft/client/renderer/RegionRenderCacheBuilder;)Ljava/util/Set;";
|
|
||||||
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";
|
|
||||||
|
|
||||||
// @Redirect(
|
|
||||||
// method = compile,
|
|
||||||
// at = @At(value = "INVOKE", target = invokeReflector),
|
|
||||||
// slice = @Slice(
|
|
||||||
// from = @At(value = "FIELD", target = forgeBlockCanRender)
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
// @SuppressWarnings("UnresolvedMixinReference")
|
|
||||||
// boolean canRenderInLayer(Object state, @Coerce Object reflector, Object[] layer) {
|
|
||||||
// return Hooks.canRenderInLayerOverride((BlockState) state, (RenderType) layer[0]);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,10 @@ import mods.betterfoliage.chunk.ChunkOverlayManager
|
|||||||
import mods.betterfoliage.config.BlockConfig
|
import mods.betterfoliage.config.BlockConfig
|
||||||
import mods.betterfoliage.integration.OptifineCustomColors
|
import mods.betterfoliage.integration.OptifineCustomColors
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
import mods.betterfoliage.integration.ShadersModIntegration
|
||||||
|
import mods.betterfoliage.render.block.vanilla.RoundLogOverlayLayer
|
||||||
|
import mods.betterfoliage.render.block.vanilla.StandardCactusDiscovery
|
||||||
|
import mods.betterfoliage.render.block.vanilla.StandardCactusModel
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardDirtDiscovery
|
import mods.betterfoliage.render.block.vanilla.StandardDirtDiscovery
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardDirtKey
|
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardDirtModel
|
import mods.betterfoliage.render.block.vanilla.StandardDirtModel
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardGrassDiscovery
|
import mods.betterfoliage.render.block.vanilla.StandardGrassDiscovery
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardGrassModel
|
import mods.betterfoliage.render.block.vanilla.StandardGrassModel
|
||||||
@@ -13,43 +15,57 @@ import mods.betterfoliage.render.block.vanilla.StandardLeafDiscovery
|
|||||||
import mods.betterfoliage.render.block.vanilla.StandardLeafModel
|
import mods.betterfoliage.render.block.vanilla.StandardLeafModel
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardLilypadDiscovery
|
import mods.betterfoliage.render.block.vanilla.StandardLilypadDiscovery
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardLilypadModel
|
import mods.betterfoliage.render.block.vanilla.StandardLilypadModel
|
||||||
|
import mods.betterfoliage.render.block.vanilla.StandardLogDiscovery
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardMyceliumDiscovery
|
import mods.betterfoliage.render.block.vanilla.StandardMyceliumDiscovery
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardMyceliumModel
|
import mods.betterfoliage.render.block.vanilla.StandardMyceliumModel
|
||||||
|
import mods.betterfoliage.render.block.vanilla.StandardNetherrackDiscovery
|
||||||
|
import mods.betterfoliage.render.block.vanilla.StandardNetherrackModel
|
||||||
|
import mods.betterfoliage.render.block.vanilla.StandardRoundLogModel
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardSandDiscovery
|
import mods.betterfoliage.render.block.vanilla.StandardSandDiscovery
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardSandModel
|
import mods.betterfoliage.render.block.vanilla.StandardSandModel
|
||||||
import mods.betterfoliage.render.lighting.AoSideHelper
|
import mods.betterfoliage.render.lighting.AoSideHelper
|
||||||
import mods.betterfoliage.render.particle.LeafWindTracker
|
import mods.betterfoliage.render.particle.LeafWindTracker
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
import mods.betterfoliage.resource.discovery.BlockTypeCache
|
import mods.betterfoliage.resource.discovery.BlockTypeCache
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent
|
||||||
import mods.betterfoliage.resource.generated.GeneratedTexturePack
|
import mods.betterfoliage.resource.generated.GeneratedTexturePack
|
||||||
import mods.betterfoliage.texture.LeafParticleRegistry
|
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||||
|
import mods.betterfoliage.render.particle.RisingSoulParticle
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.Blocks
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.RenderType
|
import net.minecraft.resources.IReloadableResourceManager
|
||||||
import net.minecraft.client.renderer.RenderTypeLookup
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.common.ForgeConfig
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
|
* Object responsible for initializing (and holding a reference to) all the infrastructure of the mod
|
||||||
* except for the call hooks.
|
* except for the call hooks.
|
||||||
*/
|
*/
|
||||||
object Client {
|
object Client {
|
||||||
|
/** Resource pack holding generated assets */
|
||||||
val generatedPack = GeneratedTexturePack("bf_gen", "Better Foliage generated assets")
|
val generatedPack = GeneratedTexturePack("bf_gen", "Better Foliage generated assets")
|
||||||
var blockTypes = BlockTypeCache()
|
|
||||||
|
|
||||||
val suppressRenderErrors = mutableSetOf<BlockState>()
|
/** List of recognized [BlockState]s */
|
||||||
|
var blockTypes = BlockTypeCache()
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
// discoverers
|
// discoverers
|
||||||
BetterFoliageMod.bus.register(BakeWrapperManager)
|
BetterFoliageMod.bus.register(BakeWrapperManager)
|
||||||
BetterFoliageMod.bus.register(LeafParticleRegistry)
|
BetterFoliageMod.bus.register(LeafParticleRegistry)
|
||||||
|
BetterFoliageMod.bus.register(this)
|
||||||
|
(Minecraft.getInstance().resourceManager as IReloadableResourceManager).addReloadListener(LeafParticleRegistry)
|
||||||
|
|
||||||
|
ChunkOverlayManager.layers.add(RoundLogOverlayLayer)
|
||||||
|
|
||||||
listOf(
|
listOf(
|
||||||
StandardLeafDiscovery,
|
StandardLeafDiscovery,
|
||||||
StandardGrassDiscovery,
|
StandardGrassDiscovery,
|
||||||
StandardDirtDiscovery,
|
StandardDirtDiscovery,
|
||||||
StandardMyceliumDiscovery,
|
StandardMyceliumDiscovery,
|
||||||
StandardSandDiscovery,
|
StandardSandDiscovery,
|
||||||
StandardLilypadDiscovery
|
StandardLilypadDiscovery,
|
||||||
|
StandardCactusDiscovery,
|
||||||
|
StandardNetherrackDiscovery,
|
||||||
|
StandardLogDiscovery
|
||||||
).forEach {
|
).forEach {
|
||||||
BakeWrapperManager.discoverers.add(it)
|
BakeWrapperManager.discoverers.add(it)
|
||||||
}
|
}
|
||||||
@@ -68,7 +84,11 @@ object Client {
|
|||||||
StandardDirtModel.Companion,
|
StandardDirtModel.Companion,
|
||||||
StandardMyceliumModel.Companion,
|
StandardMyceliumModel.Companion,
|
||||||
StandardSandModel.Companion,
|
StandardSandModel.Companion,
|
||||||
StandardLilypadModel.Companion
|
StandardLilypadModel.Companion,
|
||||||
|
StandardCactusModel.Companion,
|
||||||
|
StandardNetherrackModel.Companion,
|
||||||
|
StandardRoundLogModel.Companion,
|
||||||
|
RisingSoulParticle.Companion
|
||||||
)
|
)
|
||||||
|
|
||||||
// init mod integrations
|
// init mod integrations
|
||||||
@@ -77,5 +97,11 @@ object Client {
|
|||||||
OptifineCustomColors
|
OptifineCustomColors
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||||
|
blockTypes = BlockTypeCache()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,47 @@
|
|||||||
@file:JvmName("Hooks")
|
@file:JvmName("Hooks")
|
||||||
package mods.betterfoliage
|
package mods.betterfoliage
|
||||||
|
|
||||||
|
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.model.getActualRenderModel
|
import mods.betterfoliage.model.getActualRenderModel
|
||||||
|
import mods.betterfoliage.render.block.vanilla.RoundLogKey
|
||||||
import mods.betterfoliage.render.particle.FallingLeafParticle
|
import mods.betterfoliage.render.particle.FallingLeafParticle
|
||||||
import mods.betterfoliage.texture.LeafBlockModel
|
import mods.betterfoliage.render.particle.LeafBlockModel
|
||||||
|
import mods.betterfoliage.render.particle.RisingSoulParticle
|
||||||
|
import mods.betterfoliage.util.plus
|
||||||
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.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.world.ClientWorld
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.util.Direction
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.Direction.DOWN
|
import net.minecraft.util.Direction.DOWN
|
||||||
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.shapes.VoxelShape
|
import net.minecraft.util.math.shapes.VoxelShape
|
||||||
|
import net.minecraft.util.math.shapes.VoxelShapes
|
||||||
import net.minecraft.world.IBlockReader
|
import net.minecraft.world.IBlockReader
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
import java.util.Random
|
import java.util.Random
|
||||||
|
|
||||||
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
|
||||||
// if (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block)) return Config.roundLogs.dimming.toFloat();
|
if (Config.enabled && Config.roundLogs.enabled && Client.blockTypes.stateKeys[state] is RoundLogKey)
|
||||||
return original
|
return Config.roundLogs.dimming.toFloat()
|
||||||
}
|
|
||||||
|
|
||||||
fun getUseNeighborBrightnessOverride(original: Boolean, state: BlockState): Boolean {
|
|
||||||
// return original || (Config.enabled && Config.roundLogs.enabled && BlockConfig.logBlocks.matchesClass(state.block));
|
|
||||||
return original
|
return original
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState, flags: Int) {
|
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState, flags: Int) {
|
||||||
// ChunkOverlayManager.onBlockChange(worldClient, pos)
|
ChunkOverlayManager.onBlockChange(worldClient, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: BlockPos, random: Random) {
|
fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: BlockPos, random: Random) {
|
||||||
// if (Config.enabled &&
|
if (Config.enabled &&
|
||||||
// Config.risingSoul.enabled &&
|
Config.risingSoul.enabled &&
|
||||||
// state.block == Blocks.SOUL_SAND &&
|
state.block == Blocks.SOUL_SAND &&
|
||||||
// world.isAirBlock(pos + up1) &&
|
world.isAirBlock(pos.offset(UP)) &&
|
||||||
// Math.random() < Config.risingSoul.chance) {
|
Math.random() < Config.risingSoul.chance) {
|
||||||
// EntityRisingSoulFX(world, pos).addIfValid()
|
RisingSoulParticle(world, pos).addIfValid()
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (Config.enabled &&
|
if (Config.enabled &&
|
||||||
Config.fallingLeaves.enabled &&
|
Config.fallingLeaves.enabled &&
|
||||||
@@ -53,6 +56,10 @@ fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: Bloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
||||||
// if (LogRegistry[state, reader, pos] != null) return VoxelShapes.empty()
|
if (Config.enabled && Config.roundLogs.enabled && Client.blockTypes.stateKeys[state] is RoundLogKey)
|
||||||
|
return VoxelShapes.empty()
|
||||||
return state.getFaceOcclusionShape(reader, pos, dir)
|
return state.getFaceOcclusionShape(reader, pos, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shouldForceSideRenderOF(state: BlockState, world: IBlockReader, pos: BlockPos, face: Direction) =
|
||||||
|
world.getBlockState(pos.offset(face)).let { neighbor -> Client.blockTypes.stateKeys[neighbor] is RoundLogKey }
|
||||||
@@ -27,8 +27,11 @@ interface BlockCtx {
|
|||||||
fun offset(offset: Int3): BlockCtx
|
fun offset(offset: Int3): BlockCtx
|
||||||
|
|
||||||
val state: BlockState get() = world.getBlockState(pos)
|
val state: BlockState get() = world.getBlockState(pos)
|
||||||
fun state(dir: Direction) = world.getBlockState(pos + dir.offset)
|
|
||||||
fun state(offset: Int3) = world.getBlockState(pos + offset)
|
fun state(offset: Int3) = world.getBlockState(pos + offset)
|
||||||
|
fun state(dir: Direction) = state(dir.offset)
|
||||||
|
|
||||||
|
fun isAir(offset: Int3) = (pos + offset).let { world.getBlockState(it).isAir(world, it) }
|
||||||
|
fun isAir(dir: Direction) = isAir(dir.offset)
|
||||||
|
|
||||||
val biome: Biome? get() =
|
val biome: Biome? get() =
|
||||||
(world as? IWorldReader)?.getBiome(pos) ?:
|
(world as? IWorldReader)?.getBiome(pos) ?:
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package mods.betterfoliage.config
|
package mods.betterfoliage.config
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliage
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
|
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelTextureListConfiguration
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.config.ModConfig
|
import net.minecraftforge.fml.config.ModConfig
|
||||||
@@ -67,7 +68,7 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_I
|
|||||||
|
|
||||||
object cactus : ConfigCategory(){
|
object cactus : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val size by double(min=0.5, max=1.5, default=0.8).lang("size")
|
val size by double(min=1.0, max=2.0, default=1.3).lang("size")
|
||||||
val sizeVariation by double(max=0.5, default=0.1)
|
val sizeVariation by double(max=0.5, default=0.1)
|
||||||
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
|
val hOffset by double(max=0.5, default=0.1).lang("hOffset")
|
||||||
}
|
}
|
||||||
@@ -117,8 +118,8 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_I
|
|||||||
object netherrack : ConfigCategory(){
|
object netherrack : ConfigCategory(){
|
||||||
val enabled by featureEnable()
|
val enabled by featureEnable()
|
||||||
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
val hOffset by double(max=0.4, default=0.2).lang("hOffset")
|
||||||
val heightMin by double(min=0.1, max=1.5, default=0.6).lang("heightMin")
|
val heightMin by double(min=0.1, max=1.5, default=0.2).lang("heightMin")
|
||||||
val heightMax by double(min=0.1, max=1.5, default=0.8).lang("heightMax")
|
val heightMax by double(min=0.1, max=1.5, default=0.5).lang("heightMax")
|
||||||
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
val size by double(min=0.5, max=1.5, default=1.0).lang("size")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +148,7 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_I
|
|||||||
val trailLength by int(min=2, max=128, default=48)
|
val trailLength by int(min=2, max=128, default=48)
|
||||||
val trailDensity by int(min=1, max=16, default=3)
|
val trailDensity by int(min=1, max=16, default=3)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object BlockConfig {
|
object BlockConfig {
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
package mods.betterfoliage.model
|
package mods.betterfoliage.model
|
||||||
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||||
import mods.betterfoliage.util.Double3
|
import mods.betterfoliage.util.Double3
|
||||||
import mods.betterfoliage.util.HasLogger
|
import mods.betterfoliage.util.HasLogger
|
||||||
import mods.betterfoliage.util.directionsAndNull
|
import mods.betterfoliage.util.directionsAndNull
|
||||||
|
import mods.betterfoliage.util.mapArray
|
||||||
import net.minecraft.client.renderer.model.BakedQuad
|
import net.minecraft.client.renderer.model.BakedQuad
|
||||||
import net.minecraft.client.renderer.model.IBakedModel
|
import net.minecraft.client.renderer.model.IBakedModel
|
||||||
import net.minecraft.client.renderer.model.IModelTransform
|
|
||||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
|
||||||
import net.minecraft.client.renderer.model.Material
|
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
import net.minecraft.client.renderer.model.SimpleBakedModel
|
import net.minecraft.client.renderer.model.SimpleBakedModel
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
||||||
import net.minecraft.client.renderer.vertex.VertexFormatElement
|
import net.minecraft.client.renderer.vertex.VertexFormatElement
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder
|
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder
|
||||||
import java.util.Random
|
import java.util.Random
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hybrid baked quad implementation, carrying both baked and unbaked information.
|
* Hybrid baked quad implementation, carrying both baked and unbaked information.
|
||||||
@@ -47,23 +42,16 @@ open class HalfBakedSpecialWrapper(val baseModel: SpecialRenderModel): IBakedMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() {
|
abstract class HalfBakedWrapperKey : ModelBakingKey, HasLogger() {
|
||||||
override fun bake(
|
override fun bake(ctx: ModelBakingContext): IBakedModel? {
|
||||||
location: ResourceLocation,
|
val baseModel = super.bake(ctx)
|
||||||
unbaked: IUnbakedModel,
|
|
||||||
transform: IModelTransform,
|
|
||||||
bakery: ModelBakery,
|
|
||||||
spriteGetter: Function<Material, TextureAtlasSprite>
|
|
||||||
): IBakedModel? {
|
|
||||||
val baseModel = super.bake(location, unbaked, transform, bakery, spriteGetter)
|
|
||||||
val halfBaked = when(baseModel) {
|
val halfBaked = when(baseModel) {
|
||||||
is SimpleBakedModel -> HalfBakedSimpleModelWrapper(baseModel)
|
is SimpleBakedModel -> HalfBakedSimpleModelWrapper(baseModel)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
return if (halfBaked == null) baseModel else replace(halfBaked)
|
return if (halfBaked == null) baseModel else bake(ctx, halfBaked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel
|
||||||
abstract fun replace(wrapped: SpecialRenderModel): SpecialRenderModel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun List<Quad>.bake(applyDiffuseLighting: Boolean) = map { quad ->
|
fun List<Quad>.bake(applyDiffuseLighting: Boolean) = map { quad ->
|
||||||
@@ -106,6 +94,8 @@ fun List<Quad>.bake(applyDiffuseLighting: Boolean) = map { quad ->
|
|||||||
HalfBakedQuad(quad, builder.build())
|
HalfBakedQuad(quad, builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Array<List<Quad>>.bake(applyDiffuseLighting: Boolean) = mapArray { it.bake(applyDiffuseLighting) }
|
||||||
|
|
||||||
fun BakedQuad.unbake(): HalfBakedQuad {
|
fun BakedQuad.unbake(): HalfBakedQuad {
|
||||||
val size = DefaultVertexFormats.BLOCK.integerSize
|
val size = DefaultVertexFormats.BLOCK.integerSize
|
||||||
val verts = Array(4) { vIdx ->
|
val verts = Array(4) { vIdx ->
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import mods.betterfoliage.util.nearestAngle
|
|||||||
import mods.betterfoliage.util.rotate
|
import mods.betterfoliage.util.rotate
|
||||||
import mods.betterfoliage.util.times
|
import mods.betterfoliage.util.times
|
||||||
import mods.betterfoliage.util.vec
|
import mods.betterfoliage.util.vec
|
||||||
|
import net.minecraft.client.renderer.texture.NativeImage
|
||||||
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 java.lang.Math.max
|
import java.lang.Math.max
|
||||||
@@ -83,6 +84,7 @@ data class Color(val alpha: Int, val red: Int, val green: Int, val blue: Int) {
|
|||||||
|
|
||||||
data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
data class HSB(var hue: Float, var saturation: Float, var brightness: Float) {
|
||||||
companion object {
|
companion object {
|
||||||
|
/** Red is assumed to be LSB, see [NativeImage.PixelFormat.RGBA] */
|
||||||
fun fromColorRGBA(color: Int): HSB {
|
fun fromColorRGBA(color: Int): HSB {
|
||||||
val hsbVals = java.awt.Color.RGBtoHSB(color and 255, (color shr 8) and 255, (color shr 16) and 255, null)
|
val hsbVals = java.awt.Color.RGBtoHSB(color and 255, (color shr 8) and 255, (color shr 16) and 255, null)
|
||||||
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
return HSB(hsbVals[0], hsbVals[1], hsbVals[2])
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ fun fullCubeTextured(
|
|||||||
.bake(true)
|
.bake(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Array<List<Quad>> {
|
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): List<List<Quad>> {
|
||||||
return Array(num) { idx ->
|
return (0 until num).map { idx ->
|
||||||
listOf(
|
listOf(
|
||||||
Quad.verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41),
|
Quad.verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41),
|
||||||
Quad.verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
|
Quad.verticalRectangle(x1 = -0.5, z1 = 0.5, x2 = 0.5, z2 = -0.5, yBottom = -0.5 * 1.41, yTop = 0.5 * 1.41)
|
||||||
@@ -93,7 +93,7 @@ fun crossModelSingle(base: List<Quad>, sprite: TextureAtlasSprite, tintIndex: In
|
|||||||
.bake(false)
|
.bake(false)
|
||||||
|
|
||||||
fun crossModelsTextured(
|
fun crossModelsTextured(
|
||||||
leafBase: Array<List<Quad>>,
|
leafBase: Iterable<List<Quad>>,
|
||||||
tintIndex: Int,
|
tintIndex: Int,
|
||||||
scrambleUV: Boolean,
|
scrambleUV: Boolean,
|
||||||
spriteGetter: (Int) -> ResourceLocation
|
spriteGetter: (Int) -> ResourceLocation
|
||||||
@@ -101,8 +101,8 @@ fun crossModelsTextured(
|
|||||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], tintIndex, scrambleUV)
|
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], tintIndex, scrambleUV)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
fun List<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
fun Iterable<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
||||||
fun List<List<Quad>>.buildTufts(applyDiffuseLighting: Boolean = false) =
|
fun Iterable<List<Quad>>.buildTufts(applyDiffuseLighting: Boolean = false) =
|
||||||
map { it.withOpposites().bake(applyDiffuseLighting) }.toTypedArray()
|
map { it.withOpposites().bake(applyDiffuseLighting) }.toTypedArray()
|
||||||
|
|
||||||
fun List<List<Quad>>.transform(trans: Quad.(Int)-> Quad) = mapIndexed { idx, qList -> qList.map { it.trans(idx) } }
|
fun Iterable<List<Quad>>.transform(trans: Quad.(Int)-> Quad) = mapIndexed { idx, qList -> qList.map { it.trans(idx) } }
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package mods.betterfoliage.render
|
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
|
||||||
import mods.betterfoliage.config.Config
|
|
||||||
import mods.betterfoliage.render.old.AbstractEntityFX
|
|
||||||
import mods.betterfoliage.util.Atlas
|
|
||||||
import mods.betterfoliage.util.Double3
|
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
|
||||||
import net.minecraft.util.math.BlockPos
|
|
||||||
import net.minecraft.util.math.MathHelper
|
|
||||||
import net.minecraft.world.World
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class EntityRisingSoulFX(world: World, pos: BlockPos) :
|
|
||||||
AbstractEntityFX(world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5) {
|
|
||||||
|
|
||||||
val particleTrail: Deque<Double3> = LinkedList<Double3>()
|
|
||||||
val initialPhase = rand.nextInt(64)
|
|
||||||
|
|
||||||
init {
|
|
||||||
motionY = 0.1
|
|
||||||
particleGravity = 0.0f
|
|
||||||
// sprite = RisingSoulTextures.headIcons[rand.nextInt(256)]
|
|
||||||
maxAge = MathHelper.floor((0.6 + 0.4 * rand.nextDouble()) * Config.risingSoul.lifetime * 20.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isValid: Boolean get() = true
|
|
||||||
|
|
||||||
override fun update() {
|
|
||||||
val phase = (initialPhase + age) % 64
|
|
||||||
velocity.setTo(cos[phase] * Config.risingSoul.perturb, 0.1, sin[phase] * Config.risingSoul.perturb)
|
|
||||||
|
|
||||||
particleTrail.addFirst(currentPos.copy())
|
|
||||||
while (particleTrail.size > Config.risingSoul.trailLength) particleTrail.removeLast()
|
|
||||||
|
|
||||||
if (!Config.enabled) setExpired()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
|
|
||||||
// var alpha = Config.risingSoul.opacity.toFloat()
|
|
||||||
// if (age > maxAge - 40) alpha *= (maxAge - age) / 40.0f
|
|
||||||
//
|
|
||||||
// renderParticleQuad(worldRenderer, partialTickTime,
|
|
||||||
// size = Config.risingSoul.headSize * 0.25,
|
|
||||||
// alpha = alpha
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// var scale = Config.risingSoul.trailSize * 0.25
|
|
||||||
// particleTrail.forEachPairIndexed { idx, current, previous ->
|
|
||||||
// scale *= Config.risingSoul.sizeDecay
|
|
||||||
// alpha *= Config.risingSoul.opacityDecay.toFloat()
|
|
||||||
// if (idx % Config.risingSoul.trailDensity == 0) renderParticleQuad(worldRenderer, partialTickTime,
|
|
||||||
// currentPos = current,
|
|
||||||
// prevPos = previous,
|
|
||||||
// size = scale,
|
|
||||||
// alpha = alpha,
|
|
||||||
// icon = RisingSoulTextures.trackIcon
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//object RisingSoulTextures : ResourceHandler(BetterFoliageMod.MOD_ID, BetterFoliageMod.bus, targetAtlas = Atlas.PARTICLES) {
|
|
||||||
// val headIcons = spriteSet { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "rising_soul_$idx") }
|
|
||||||
// val trackIcon by sprite(Identifier(BetterFoliageMod.MOD_ID, "soul_track"))
|
|
||||||
//}
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package mods.betterfoliage.render.block.vanilla
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
|
import mods.betterfoliage.Client
|
||||||
|
import mods.betterfoliage.config.Config
|
||||||
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
|
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
|
import mods.betterfoliage.model.SpriteDelegate
|
||||||
|
import mods.betterfoliage.model.SpriteSetDelegate
|
||||||
|
import mods.betterfoliage.model.buildTufts
|
||||||
|
import mods.betterfoliage.model.crossModelsRaw
|
||||||
|
import mods.betterfoliage.model.crossModelsTextured
|
||||||
|
import mods.betterfoliage.model.transform
|
||||||
|
import mods.betterfoliage.model.tuftModelSet
|
||||||
|
import mods.betterfoliage.model.tuftShapeSet
|
||||||
|
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||||
|
import mods.betterfoliage.render.lighting.RoundLeafLighting
|
||||||
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
|
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||||
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
|
import mods.betterfoliage.util.Atlas
|
||||||
|
import mods.betterfoliage.util.LazyInvalidatable
|
||||||
|
import mods.betterfoliage.util.Rotation
|
||||||
|
import mods.betterfoliage.util.get
|
||||||
|
import mods.betterfoliage.util.horizontalDirections
|
||||||
|
import mods.betterfoliage.util.randomD
|
||||||
|
import mods.betterfoliage.util.randomI
|
||||||
|
import net.minecraft.block.Blocks
|
||||||
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
|
import net.minecraft.util.Direction.DOWN
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
|
|
||||||
|
object StandardCactusDiscovery : AbstractModelDiscovery() {
|
||||||
|
val CACTUS_BLOCKS = listOf(Blocks.CACTUS)
|
||||||
|
|
||||||
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
|
val model = ctx.getUnbaked()
|
||||||
|
if (model is BlockModel && ctx.blockState.block in CACTUS_BLOCKS) {
|
||||||
|
Client.blockTypes.dirt.add(ctx.blockState)
|
||||||
|
ctx.addReplacement(StandardCactusKey)
|
||||||
|
}
|
||||||
|
super.processModel(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object StandardCactusKey : HalfBakedWrapperKey() {
|
||||||
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardCactusModel(wrapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandardCactusModel(
|
||||||
|
wrapped: SpecialRenderModel
|
||||||
|
) : HalfBakedSpecialWrapper(wrapped) {
|
||||||
|
|
||||||
|
val armLighting = horizontalDirections.map { LightingPreferredFace(it) }.toTypedArray()
|
||||||
|
|
||||||
|
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||||
|
ctx.checkSides = false
|
||||||
|
super.render(ctx, noDecorations)
|
||||||
|
if (!Config.enabled || !Config.cactus.enabled) return
|
||||||
|
|
||||||
|
val armSide = ctx.random.nextInt() and 3
|
||||||
|
ctx.vertexLighter = armLighting[armSide]
|
||||||
|
ctx.renderQuads(cactusArmModels[armSide][ctx.random])
|
||||||
|
ctx.vertexLighter = RoundLeafLighting
|
||||||
|
ctx.renderQuads(cactusCrossModels[ctx.random])
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val cactusCrossSprite by SpriteDelegate(Atlas.BLOCKS) {
|
||||||
|
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus")
|
||||||
|
}
|
||||||
|
val cactusArmSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||||
|
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_cactus_arm_$idx")
|
||||||
|
}
|
||||||
|
val cactusArmModels by LazyInvalidatable(BakeWrapperManager) {
|
||||||
|
val shapes = Config.cactus.let { tuftShapeSet(0.8, 0.8, 0.8, 0.2) }
|
||||||
|
val models = tuftModelSet(shapes, -1) { cactusArmSprites[randomI()] }
|
||||||
|
horizontalDirections.map { side ->
|
||||||
|
models.transform { move(0.0625 to DOWN).rotate(Rotation.fromUp[side.ordinal]) }.buildTufts()
|
||||||
|
}.toTypedArray()
|
||||||
|
}
|
||||||
|
val cactusCrossModels by LazyInvalidatable(BakeWrapperManager) {
|
||||||
|
val models = Config.cactus.let { config ->
|
||||||
|
crossModelsRaw(64, config.size, 0.0, 0.0)
|
||||||
|
.transform { rotateZ(randomD(-config.sizeVariation, config.sizeVariation)) }
|
||||||
|
}
|
||||||
|
crossModelsTextured(models, -1, true) { cactusCrossSprite.name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ import mods.betterfoliage.render.pipeline.RenderCtxBase
|
|||||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
import mods.betterfoliage.resource.generated.CenteredSprite
|
import mods.betterfoliage.resource.generated.CenteredSprite
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.Int3
|
import mods.betterfoliage.util.Int3
|
||||||
@@ -23,40 +24,36 @@ import mods.betterfoliage.util.LazyInvalidatable
|
|||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
import mods.betterfoliage.util.offset
|
import mods.betterfoliage.util.offset
|
||||||
import mods.betterfoliage.util.randomI
|
import mods.betterfoliage.util.randomI
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
import net.minecraft.block.Blocks
|
||||||
import net.minecraft.block.material.Material
|
import net.minecraft.block.material.Material
|
||||||
import net.minecraft.client.renderer.RenderType
|
import net.minecraft.client.renderer.RenderType
|
||||||
import net.minecraft.client.renderer.RenderTypeLookup
|
import net.minecraft.client.renderer.RenderTypeLookup
|
||||||
import net.minecraft.client.renderer.model.BlockModel
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
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
|
||||||
|
|
||||||
object StandardDirtDiscovery : AbstractModelDiscovery() {
|
object StandardDirtDiscovery : AbstractModelDiscovery() {
|
||||||
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.PODZOL)
|
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.PODZOL)
|
||||||
override fun processModel(
|
|
||||||
bakery: ModelBakery,
|
fun canRenderInLayer(layer: RenderType) = when {
|
||||||
state: BlockState,
|
!Config.enabled -> layer == RenderType.getSolid()
|
||||||
location: ResourceLocation,
|
!Config.connectedGrass.enabled && !Config.algae.enabled && !Config.reed.enabled -> layer == RenderType.getSolid()
|
||||||
sprites: MutableSet<ResourceLocation>,
|
else -> layer == RenderType.getCutoutMipped()
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
val model = bakery.getUnbakedModel(location)
|
|
||||||
if (model is BlockModel && state.block in DIRT_BLOCKS) {
|
|
||||||
Client.blockTypes.dirt.add(state)
|
|
||||||
replacements[location] = StandardDirtKey
|
|
||||||
// RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutout())
|
|
||||||
RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutoutMipped())
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return super.processModel(bakery, state, location, sprites, replacements)
|
|
||||||
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
|
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in DIRT_BLOCKS) {
|
||||||
|
Client.blockTypes.dirt.add(ctx.blockState)
|
||||||
|
ctx.addReplacement(StandardDirtKey)
|
||||||
|
RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer)
|
||||||
|
}
|
||||||
|
super.processModel(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StandardDirtKey : HalfBakedWrapperKey() {
|
object StandardDirtKey : HalfBakedWrapperKey() {
|
||||||
override fun replace(wrapped: SpecialRenderModel) = StandardDirtModel(wrapped)
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardDirtModel(wrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardDirtModel(
|
class StandardDirtModel(
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import mods.betterfoliage.BetterFoliageMod
|
|||||||
import mods.betterfoliage.Client
|
import mods.betterfoliage.Client
|
||||||
import mods.betterfoliage.config.BlockConfig
|
import mods.betterfoliage.config.BlockConfig
|
||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.config.ConfigurableBlockMatcher
|
|
||||||
import mods.betterfoliage.config.ModelTextureList
|
|
||||||
import mods.betterfoliage.integration.ShadersModIntegration
|
import mods.betterfoliage.integration.ShadersModIntegration
|
||||||
import mods.betterfoliage.model.Color
|
import mods.betterfoliage.model.Color
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
@@ -19,8 +17,11 @@ import mods.betterfoliage.model.tuftShapeSet
|
|||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
|
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
import mods.betterfoliage.util.LazyInvalidatable
|
||||||
import mods.betterfoliage.util.LazyMapInvalidatable
|
import mods.betterfoliage.util.LazyMapInvalidatable
|
||||||
@@ -28,7 +29,6 @@ import mods.betterfoliage.util.averageColor
|
|||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
import mods.betterfoliage.util.isSnow
|
import mods.betterfoliage.util.isSnow
|
||||||
import mods.betterfoliage.util.randomI
|
import mods.betterfoliage.util.randomI
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
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 net.minecraft.util.ResourceLocation
|
||||||
@@ -37,17 +37,9 @@ object StandardGrassDiscovery : ConfigurableModelDiscovery() {
|
|||||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
|
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.grassBlocks
|
||||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList
|
override val modelTextures: List<ModelTextureList> get() = BlockConfig.grassModels.modelList
|
||||||
|
|
||||||
override fun processModel(
|
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
|
||||||
state: BlockState,
|
ctx.addReplacement(StandardGrassKey(textureMatch[0], null))
|
||||||
location: ResourceLocation,
|
Client.blockTypes.grass.add(ctx.blockState)
|
||||||
textureMatch: List<ResourceLocation>,
|
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
replacements[location] = StandardGrassKey(textureMatch[0], null)
|
|
||||||
Client.blockTypes.grass.add(state)
|
|
||||||
// RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutout())
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +49,7 @@ data class StandardGrassKey(
|
|||||||
) : HalfBakedWrapperKey() {
|
) : HalfBakedWrapperKey() {
|
||||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||||
|
|
||||||
override fun replace(wrapped: SpecialRenderModel): SpecialRenderModel {
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||||
val grassSpriteColor = Atlas.BLOCKS[grassLocation].averageColor.let { hsb ->
|
val grassSpriteColor = Atlas.BLOCKS[grassLocation].averageColor.let { hsb ->
|
||||||
logColorOverride(BetterFoliageMod.detailLogger(this), Config.shortGrass.saturationThreshold, hsb)
|
logColorOverride(BetterFoliageMod.detailLogger(this), Config.shortGrass.saturationThreshold, hsb)
|
||||||
hsb.colorOverride(Config.shortGrass.saturationThreshold)
|
hsb.colorOverride(Config.shortGrass.saturationThreshold)
|
||||||
@@ -81,7 +73,7 @@ class StandardGrassModel(
|
|||||||
|
|
||||||
val stateBelow = ctx.state(DOWN)
|
val stateBelow = ctx.state(DOWN)
|
||||||
val stateAbove = ctx.state(UP)
|
val stateAbove = ctx.state(UP)
|
||||||
|
val isAir = ctx.isAir(UP)
|
||||||
val isSnowed = stateAbove.isSnow
|
val isSnowed = stateAbove.isSnow
|
||||||
val connected = Config.connectedGrass.enabled &&
|
val connected = Config.connectedGrass.enabled &&
|
||||||
(!isSnowed || Config.connectedGrass.snowEnabled) &&
|
(!isSnowed || Config.connectedGrass.snowEnabled) &&
|
||||||
@@ -93,7 +85,7 @@ class StandardGrassModel(
|
|||||||
super.render(ctx, noDecorations)
|
super.render(ctx, noDecorations)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.shortGrass.enabled(ctx.random) && !ctx.isNeighborSolid(UP)) {
|
if (Config.shortGrass.enabled(ctx.random) && (isAir || isSnowed)) {
|
||||||
ctx.vertexLighter = tuftLighting
|
ctx.vertexLighter = tuftLighting
|
||||||
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
|
ShadersModIntegration.grass(ctx, Config.shortGrass.shaderWind) {
|
||||||
ctx.renderQuads(if (isSnowed) tuftSnowed[ctx.random] else tuftNormal[ctx.random])
|
ctx.renderQuads(if (isSnowed) tuftSnowed[ctx.random] else tuftNormal[ctx.random])
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import mods.betterfoliage.BetterFoliageMod
|
|||||||
import mods.betterfoliage.Client
|
import mods.betterfoliage.Client
|
||||||
import mods.betterfoliage.config.BlockConfig
|
import mods.betterfoliage.config.BlockConfig
|
||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.config.ConfigurableBlockMatcher
|
|
||||||
import mods.betterfoliage.config.ModelTextureList
|
|
||||||
import mods.betterfoliage.model.Color
|
import mods.betterfoliage.model.Color
|
||||||
import mods.betterfoliage.model.HSB
|
import mods.betterfoliage.model.HSB
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
@@ -14,21 +12,22 @@ import mods.betterfoliage.model.SpecialRenderModel
|
|||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
import mods.betterfoliage.model.SpriteSetDelegate
|
||||||
import mods.betterfoliage.model.crossModelsRaw
|
import mods.betterfoliage.model.crossModelsRaw
|
||||||
import mods.betterfoliage.model.crossModelsTextured
|
import mods.betterfoliage.model.crossModelsTextured
|
||||||
import mods.betterfoliage.render.lighting.RoundLeafLighting
|
import mods.betterfoliage.render.lighting.RoundLeafLightingPreferUp
|
||||||
|
import mods.betterfoliage.render.particle.LeafBlockModel
|
||||||
|
import mods.betterfoliage.render.particle.LeafParticleKey
|
||||||
|
import mods.betterfoliage.render.particle.LeafParticleRegistry
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
|
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||||
import mods.betterfoliage.resource.generated.GeneratedLeaf
|
import mods.betterfoliage.resource.generated.GeneratedLeaf
|
||||||
import mods.betterfoliage.texture.LeafBlockModel
|
|
||||||
import mods.betterfoliage.texture.LeafParticleKey
|
|
||||||
import mods.betterfoliage.texture.LeafParticleRegistry
|
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.LazyMapInvalidatable
|
import mods.betterfoliage.util.LazyMapInvalidatable
|
||||||
import mods.betterfoliage.util.averageColor
|
import mods.betterfoliage.util.averageColor
|
||||||
import mods.betterfoliage.util.isSnow
|
import mods.betterfoliage.util.isSnow
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.util.Direction.UP
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import org.apache.logging.log4j.Level.DEBUG
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
@@ -39,21 +38,15 @@ object StandardLeafDiscovery : ConfigurableModelDiscovery() {
|
|||||||
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
|
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.leafBlocks
|
||||||
override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList
|
override val modelTextures: List<ModelTextureList> get() = BlockConfig.leafModels.modelList
|
||||||
|
|
||||||
override fun processModel(
|
|
||||||
state: BlockState,
|
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
|
||||||
location: ResourceLocation,
|
|
||||||
textureMatch: List<ResourceLocation>,
|
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
val leafType = LeafParticleRegistry.typeMappings.getType(textureMatch[0]) ?: "default"
|
val leafType = LeafParticleRegistry.typeMappings.getType(textureMatch[0]) ?: "default"
|
||||||
val generated = GeneratedLeaf(textureMatch[0], leafType)
|
val generated = GeneratedLeaf(textureMatch[0], leafType)
|
||||||
.register(Client.generatedPack)
|
.register(Client.generatedPack)
|
||||||
.apply { sprites.add(this) }
|
.apply { ctx.sprites.add(this) }
|
||||||
|
|
||||||
detailLogger.log(INFO, " particle $leafType")
|
detailLogger.log(INFO, " particle $leafType")
|
||||||
replacements[location] = StandardLeafKey(generated, leafType, null)
|
ctx.addReplacement(StandardLeafKey(generated, leafType, null))
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +69,7 @@ data class StandardLeafKey(
|
|||||||
) : HalfBakedWrapperKey(), LeafParticleKey {
|
) : HalfBakedWrapperKey(), LeafParticleKey {
|
||||||
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||||
|
|
||||||
override fun replace(wrapped: SpecialRenderModel): SpecialRenderModel {
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||||
val leafSpriteColor = Atlas.BLOCKS[roundLeafTexture].averageColor.let { hsb ->
|
val leafSpriteColor = Atlas.BLOCKS[roundLeafTexture].averageColor.let { hsb ->
|
||||||
logColorOverride(BetterFoliageMod.detailLogger(this), Config.leaves.saturationThreshold, hsb)
|
logColorOverride(BetterFoliageMod.detailLogger(this), Config.leaves.saturationThreshold, hsb)
|
||||||
hsb.colorOverride(Config.leaves.saturationThreshold)
|
hsb.colorOverride(Config.leaves.saturationThreshold)
|
||||||
@@ -97,9 +90,10 @@ class StandardLeafModel(
|
|||||||
super.render(ctx, noDecorations)
|
super.render(ctx, noDecorations)
|
||||||
if (!Config.enabled || !Config.leaves.enabled || noDecorations) return
|
if (!Config.enabled || !Config.leaves.enabled || noDecorations) return
|
||||||
|
|
||||||
(ctx as? RenderCtxVanilla)?.let { it.vertexLighter = RoundLeafLighting }
|
ctx.vertexLighter = RoundLeafLightingPreferUp
|
||||||
ctx.renderQuads(leafNormal[ctx.random.nextInt(64)])
|
val leafIdx = ctx.random.nextInt(64)
|
||||||
if (ctx.state(UP).isSnow) ctx.renderQuads(leafSnowed[ctx.random.nextInt(64)])
|
ctx.renderQuads(leafNormal[leafIdx])
|
||||||
|
if (ctx.state(UP).isSnow) ctx.renderQuads(leafSnowed[leafIdx])
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import mods.betterfoliage.model.tuftShapeSet
|
|||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
import mods.betterfoliage.util.LazyInvalidatable
|
||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
@@ -27,23 +29,17 @@ import net.minecraft.util.ResourceLocation
|
|||||||
|
|
||||||
object StandardLilypadDiscovery : AbstractModelDiscovery() {
|
object StandardLilypadDiscovery : AbstractModelDiscovery() {
|
||||||
val LILYPAD_BLOCKS = listOf(Blocks.LILY_PAD)
|
val LILYPAD_BLOCKS = listOf(Blocks.LILY_PAD)
|
||||||
override fun processModel(
|
|
||||||
bakery: ModelBakery,
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
state: BlockState,
|
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in LILYPAD_BLOCKS) {
|
||||||
location: ResourceLocation,
|
ctx.addReplacement(StandardLilypadKey)
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
val model = bakery.getUnbakedModel(location)
|
|
||||||
if (model is BlockModel && state.block in LILYPAD_BLOCKS) {
|
|
||||||
replacements[location] = StandardLilypadKey
|
|
||||||
}
|
}
|
||||||
return super.processModel(bakery, state, location, sprites, replacements)
|
super.processModel(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StandardLilypadKey : HalfBakedWrapperKey() {
|
object StandardLilypadKey : HalfBakedWrapperKey() {
|
||||||
override fun replace(wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped)
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardLilypadModel(wrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardLilypadModel(
|
class StandardLilypadModel(
|
||||||
|
|||||||
@@ -13,42 +13,33 @@ import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
import mods.betterfoliage.util.LazyInvalidatable
|
||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
import mods.betterfoliage.util.randomI
|
import mods.betterfoliage.util.randomI
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
import net.minecraft.block.Blocks
|
||||||
import net.minecraft.client.renderer.RenderType
|
import net.minecraft.client.renderer.RenderType
|
||||||
import net.minecraft.client.renderer.RenderTypeLookup
|
import net.minecraft.client.renderer.RenderTypeLookup
|
||||||
import net.minecraft.client.renderer.model.BlockModel
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
import net.minecraft.util.Direction
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
|
||||||
object StandardMyceliumDiscovery : AbstractModelDiscovery() {
|
object StandardMyceliumDiscovery : AbstractModelDiscovery() {
|
||||||
val MYCELIUM_BLOCKS = listOf(Blocks.MYCELIUM)
|
val MYCELIUM_BLOCKS = listOf(Blocks.MYCELIUM)
|
||||||
|
|
||||||
override fun processModel(
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
bakery: ModelBakery,
|
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in MYCELIUM_BLOCKS) {
|
||||||
state: BlockState,
|
ctx.addReplacement(StandardMyceliumKey)
|
||||||
location: ResourceLocation,
|
RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutout())
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
val model = bakery.getUnbakedModel(location)
|
|
||||||
if (model is BlockModel && state.block in MYCELIUM_BLOCKS) {
|
|
||||||
replacements[location] = StandardMyceliumKey
|
|
||||||
RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutout())
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return super.processModel(bakery, state, location, sprites, replacements)
|
super.processModel(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StandardMyceliumKey : HalfBakedWrapperKey() {
|
object StandardMyceliumKey : HalfBakedWrapperKey() {
|
||||||
override fun replace(wrapped: SpecialRenderModel) = StandardMyceliumModel(wrapped)
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardMyceliumModel(wrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardMyceliumModel(
|
class StandardMyceliumModel(
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package mods.betterfoliage.render.block.vanilla
|
||||||
|
|
||||||
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
|
import mods.betterfoliage.Client
|
||||||
|
import mods.betterfoliage.config.Config
|
||||||
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
|
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
|
import mods.betterfoliage.model.SpriteSetDelegate
|
||||||
|
import mods.betterfoliage.model.buildTufts
|
||||||
|
import mods.betterfoliage.model.transform
|
||||||
|
import mods.betterfoliage.model.tuftModelSet
|
||||||
|
import mods.betterfoliage.model.tuftShapeSet
|
||||||
|
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
||||||
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
|
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||||
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
|
import mods.betterfoliage.util.Atlas
|
||||||
|
import mods.betterfoliage.util.LazyInvalidatable
|
||||||
|
import mods.betterfoliage.util.Rotation
|
||||||
|
import mods.betterfoliage.util.get
|
||||||
|
import mods.betterfoliage.util.randomI
|
||||||
|
import net.minecraft.block.Blocks
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.client.renderer.RenderTypeLookup
|
||||||
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
|
import net.minecraft.util.Direction.DOWN
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
|
|
||||||
|
object StandardNetherrackDiscovery : AbstractModelDiscovery() {
|
||||||
|
val NETHERRACK_BLOCKS = listOf(Blocks.NETHERRACK)
|
||||||
|
|
||||||
|
fun canRenderInLayer(layer: RenderType) = when {
|
||||||
|
!Config.enabled -> layer == RenderType.getSolid()
|
||||||
|
!Config.netherrack.enabled -> layer == RenderType.getSolid()
|
||||||
|
else -> layer == RenderType.getCutoutMipped()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
|
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in NETHERRACK_BLOCKS) {
|
||||||
|
Client.blockTypes.dirt.add(ctx.blockState)
|
||||||
|
ctx.addReplacement(StandardNetherrackKey)
|
||||||
|
RenderTypeLookup.setRenderLayer(ctx.blockState.block, ::canRenderInLayer)
|
||||||
|
}
|
||||||
|
super.processModel(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object StandardNetherrackKey : HalfBakedWrapperKey() {
|
||||||
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardNetherrackModel(wrapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandardNetherrackModel(
|
||||||
|
wrapped: SpecialRenderModel
|
||||||
|
) : HalfBakedSpecialWrapper(wrapped) {
|
||||||
|
|
||||||
|
val tuftLighting = LightingPreferredFace(DOWN)
|
||||||
|
|
||||||
|
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||||
|
super.render(ctx, noDecorations)
|
||||||
|
if (!Config.enabled || !Config.netherrack.enabled) return
|
||||||
|
|
||||||
|
if (ctx.isAir(DOWN)) {
|
||||||
|
ctx.vertexLighter = tuftLighting
|
||||||
|
ctx.renderQuads(netherrackTuftModels[ctx.random])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val netherrackTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
|
||||||
|
ResourceLocation(BetterFoliageMod.MOD_ID, "blocks/better_netherrack_$idx")
|
||||||
|
}
|
||||||
|
val netherrackTuftModels by LazyInvalidatable(BakeWrapperManager) {
|
||||||
|
val shapes = Config.netherrack.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||||
|
tuftModelSet(shapes, -1) { netherrackTuftSprites[randomI()] }
|
||||||
|
.transform { rotate(Rotation.fromUp[DOWN.ordinal]).rotateUV(2) }
|
||||||
|
.buildTufts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,98 @@
|
|||||||
package mods.betterfoliage.render.block.vanilla
|
package mods.betterfoliage.render.block.vanilla
|
||||||
|
|
||||||
|
import mods.betterfoliage.Client
|
||||||
|
import mods.betterfoliage.config.BlockConfig
|
||||||
|
import mods.betterfoliage.config.Config
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelTextureList
|
||||||
|
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
import mods.betterfoliage.render.column.ColumnBlockKey
|
import mods.betterfoliage.render.column.ColumnBlockKey
|
||||||
|
import mods.betterfoliage.render.column.ColumnMeshSet
|
||||||
|
import mods.betterfoliage.render.column.ColumnModelBase
|
||||||
|
import mods.betterfoliage.render.column.ColumnRenderLayer
|
||||||
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
|
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
|
||||||
|
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||||
import net.minecraft.util.Direction
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
|
import mods.betterfoliage.util.Atlas
|
||||||
|
import mods.betterfoliage.util.LazyMapInvalidatable
|
||||||
|
import mods.betterfoliage.util.tryDefault
|
||||||
|
import net.minecraft.block.BlockState
|
||||||
|
import net.minecraft.block.LogBlock
|
||||||
|
import net.minecraft.util.Direction.Axis
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import org.apache.logging.log4j.Level.INFO
|
||||||
|
|
||||||
data class RoundLogKey(
|
interface RoundLogKey : ColumnBlockKey, ModelBakingKey {
|
||||||
override val axis: Direction.Axis?,
|
val barkSprite: ResourceLocation
|
||||||
val barkSprite: ResourceLocation,
|
|
||||||
val endSprite: ResourceLocation
|
val endSprite: ResourceLocation
|
||||||
) : ColumnBlockKey, ModelBakingKey {
|
}
|
||||||
|
|
||||||
|
object RoundLogOverlayLayer : ColumnRenderLayer() {
|
||||||
|
override fun getColumnKey(state: BlockState) = Client.blockTypes.getTyped<ColumnBlockKey>(state)
|
||||||
|
override val connectSolids: Boolean get() = Config.roundLogs.connectSolids
|
||||||
|
override val lenientConnect: Boolean get() = Config.roundLogs.lenientConnect
|
||||||
|
override val defaultToY: Boolean get() = Config.roundLogs.defaultY
|
||||||
|
}
|
||||||
|
|
||||||
|
object StandardLogDiscovery : ConfigurableModelDiscovery() {
|
||||||
|
override val matchClasses: ConfigurableBlockMatcher get() = BlockConfig.logBlocks
|
||||||
|
override val modelTextures: List<ModelTextureList> get() = BlockConfig.logModels.modelList
|
||||||
|
|
||||||
|
override fun processModel(ctx: ModelDiscoveryContext, textureMatch: List<ResourceLocation>) {
|
||||||
|
val axis = getAxis(ctx.blockState)
|
||||||
|
detailLogger.log(INFO, " axis $axis")
|
||||||
|
StandardRoundLogKey(axis, textureMatch[0], textureMatch[1]).let { key ->
|
||||||
|
ctx.addReplacement(key)
|
||||||
|
Client.blockTypes.stateKeys[ctx.blockState] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAxis(state: BlockState): Axis? {
|
||||||
|
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
|
||||||
|
state.values.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
|
||||||
|
return when (axis) {
|
||||||
|
"x" -> Axis.X
|
||||||
|
"y" -> Axis.Y
|
||||||
|
"z" -> Axis.Z
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class StandardRoundLogKey(
|
||||||
|
override val axis: Axis?,
|
||||||
|
override val barkSprite: ResourceLocation,
|
||||||
|
override val endSprite: ResourceLocation
|
||||||
|
) : RoundLogKey, HalfBakedWrapperKey() {
|
||||||
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardRoundLogModel(this, wrapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandardRoundLogModel(
|
||||||
|
val key: StandardRoundLogKey,
|
||||||
|
wrapped: SpecialRenderModel
|
||||||
|
) : ColumnModelBase(wrapped) {
|
||||||
|
override val enabled: Boolean get() = Config.enabled && Config.roundLogs.enabled
|
||||||
|
override val overlayLayer: ColumnRenderLayer get() = RoundLogOverlayLayer
|
||||||
|
override val connectPerpendicular: Boolean get() = Config.roundLogs.connectPerpendicular
|
||||||
|
|
||||||
|
val modelSet by modelSets.delegate(key)
|
||||||
|
override fun getMeshSet(axis: Axis, quadrant: Int) = modelSet
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val modelSets = LazyMapInvalidatable(BakeWrapperManager) { key: StandardRoundLogKey ->
|
||||||
|
val barkSprite = Atlas.BLOCKS[key.barkSprite]
|
||||||
|
val endSprite = Atlas.BLOCKS[key.endSprite]
|
||||||
|
Config.roundLogs.let { config ->
|
||||||
|
ColumnMeshSet(
|
||||||
|
config.radiusSmall, config.radiusLarge, config.zProtection,
|
||||||
|
key.axis ?: Axis.Y,
|
||||||
|
barkSprite, barkSprite,
|
||||||
|
endSprite, endSprite
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,8 @@ import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
import mods.betterfoliage.resource.discovery.AbstractModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingContext
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.LazyInvalidatable
|
import mods.betterfoliage.util.LazyInvalidatable
|
||||||
import mods.betterfoliage.util.Rotation
|
import mods.betterfoliage.util.Rotation
|
||||||
@@ -28,39 +29,30 @@ import mods.betterfoliage.util.mapArray
|
|||||||
import mods.betterfoliage.util.randomB
|
import mods.betterfoliage.util.randomB
|
||||||
import mods.betterfoliage.util.randomD
|
import mods.betterfoliage.util.randomD
|
||||||
import mods.betterfoliage.util.randomI
|
import mods.betterfoliage.util.randomI
|
||||||
import net.minecraft.block.BlockState
|
|
||||||
import net.minecraft.block.Blocks
|
import net.minecraft.block.Blocks
|
||||||
import net.minecraft.block.material.Material
|
import net.minecraft.block.material.Material
|
||||||
import net.minecraft.client.renderer.RenderType
|
import net.minecraft.client.renderer.RenderType
|
||||||
import net.minecraft.client.renderer.RenderTypeLookup
|
import net.minecraft.client.renderer.RenderTypeLookup
|
||||||
import net.minecraft.client.renderer.model.BlockModel
|
import net.minecraft.client.renderer.model.BlockModel
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
import net.minecraft.util.Direction
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.Direction.UP
|
import net.minecraft.util.Direction.UP
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
|
||||||
object StandardSandDiscovery : AbstractModelDiscovery() {
|
object StandardSandDiscovery : AbstractModelDiscovery() {
|
||||||
val SAND_BLOCKS = listOf(Blocks.SAND, Blocks.RED_SAND)
|
val SAND_BLOCKS = listOf(Blocks.SAND, Blocks.RED_SAND)
|
||||||
override fun processModel(
|
|
||||||
bakery: ModelBakery,
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
state: BlockState,
|
if (ctx.getUnbaked() is BlockModel && ctx.blockState.block in SAND_BLOCKS) {
|
||||||
location: ResourceLocation,
|
Client.blockTypes.dirt.add(ctx.blockState)
|
||||||
sprites: MutableSet<ResourceLocation>,
|
ctx.addReplacement(StandardSandKey)
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
RenderTypeLookup.setRenderLayer(ctx.blockState.block, RenderType.getCutoutMipped())
|
||||||
): Boolean {
|
|
||||||
val model = bakery.getUnbakedModel(location)
|
|
||||||
if (model is BlockModel && state.block in SAND_BLOCKS) {
|
|
||||||
Client.blockTypes.dirt.add(state)
|
|
||||||
replacements[location] = StandardSandKey
|
|
||||||
RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutoutMipped())
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return super.processModel(bakery, state, location, sprites, replacements)
|
super.processModel(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StandardSandKey : HalfBakedWrapperKey() {
|
object StandardSandKey : HalfBakedWrapperKey() {
|
||||||
override fun replace(wrapped: SpecialRenderModel) = StandardSandModel(wrapped)
|
override fun bake(ctx: ModelBakingContext, wrapped: SpecialRenderModel) = StandardSandModel(wrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardSandModel(
|
class StandardSandModel(
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package mods.betterfoliage.render.column
|
||||||
|
|
||||||
|
import mods.betterfoliage.config.Config
|
||||||
|
import mods.betterfoliage.model.Quad
|
||||||
|
import mods.betterfoliage.model.UV
|
||||||
|
import mods.betterfoliage.model.Vertex
|
||||||
|
import mods.betterfoliage.model.bake
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.INVISIBLE
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.LARGE_RADIUS
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SMALL_RADIUS
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||||
|
import mods.betterfoliage.util.Double3
|
||||||
|
import mods.betterfoliage.util.Rotation
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
|
import net.minecraft.util.Direction.Axis
|
||||||
|
import net.minecraft.util.Direction.EAST
|
||||||
|
import net.minecraft.util.Direction.SOUTH
|
||||||
|
import net.minecraft.util.Direction.UP
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of dynamically generated meshes used to render rounded columns.
|
||||||
|
*/
|
||||||
|
class ColumnMeshSet(
|
||||||
|
radiusSmall: Double,
|
||||||
|
radiusLarge: Double,
|
||||||
|
zProtection: Double,
|
||||||
|
val axis: Axis,
|
||||||
|
val spriteLeft: TextureAtlasSprite,
|
||||||
|
val spriteRight: TextureAtlasSprite,
|
||||||
|
val spriteTop: TextureAtlasSprite,
|
||||||
|
val spriteBottom: TextureAtlasSprite
|
||||||
|
) {
|
||||||
|
protected fun sideRounded(radius: Double, yBottom: Double, yTop: Double): List<Quad> {
|
||||||
|
val halfRadius = radius * 0.5
|
||||||
|
return listOf(
|
||||||
|
// left side of the diagonal
|
||||||
|
Quad.verticalRectangle(0.0, 0.5, 0.5 - radius, 0.5, yBottom, yTop).clampUV(minU = 0.0, maxU = 0.5 - radius),
|
||||||
|
Quad.verticalRectangle(0.5 - radius, 0.5, 0.5 - halfRadius, 0.5 - halfRadius, yBottom, yTop).clampUV(minU = 0.5 - radius),
|
||||||
|
// right side of the diagonal
|
||||||
|
Quad.verticalRectangle(0.5 - halfRadius, 0.5 - halfRadius, 0.5, 0.5 - radius, yBottom, yTop).clampUV(maxU = radius - 0.5),
|
||||||
|
Quad.verticalRectangle(0.5, 0.5 - radius, 0.5, 0.0, yBottom, yTop).clampUV(minU = radius - 0.5, maxU = 0.0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun sideRoundedTransition(radiusBottom: Double, radiusTop: Double, yBottom: Double, yTop: Double): List<Quad> {
|
||||||
|
val ySplit = 0.5 * (yBottom + yTop)
|
||||||
|
val modelTop = sideRounded(radiusTop, yBottom, yTop)
|
||||||
|
val modelBottom = sideRounded(radiusBottom, yBottom, yTop)
|
||||||
|
return (modelBottom zip modelTop).map { (quadBottom, quadTop) ->
|
||||||
|
Quad.mix(quadBottom, quadTop) { vBottom, vTop -> if (vBottom.xyz.y < ySplit) vBottom.copy() else vTop.copy() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun sideSquare(yBottom: Double, yTop: Double) = listOf(
|
||||||
|
Quad.verticalRectangle(0.0, 0.5, 0.5, 0.5, yBottom, yTop).clampUV(minU = 0.0),
|
||||||
|
Quad.verticalRectangle(0.5, 0.5, 0.5, 0.0, yBottom, yTop).clampUV(maxU = 0.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
protected fun lidRounded(radius: Double, y: Double, isBottom: Boolean) = Array(4) { quadrant ->
|
||||||
|
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||||
|
val v1 = Vertex(Double3(0.0, y, 0.0), UV(0.0, 0.0))
|
||||||
|
val v2 = Vertex(Double3(0.0, y, 0.5), UV(0.0, 0.5))
|
||||||
|
val v3 = Vertex(Double3(0.5 - radius, y, 0.5), UV(0.5 - radius, 0.5))
|
||||||
|
val v4 = Vertex(Double3(0.5 - radius * 0.5, y, 0.5 - radius * 0.5), UV(0.5, 0.5))
|
||||||
|
val v5 = Vertex(Double3(0.5, y, 0.5 - radius), UV(0.5, 0.5 - radius))
|
||||||
|
val v6 = Vertex(Double3(0.5, y, 0.0), UV(0.5, 0.0))
|
||||||
|
listOf(Quad(v1, v2, v3, v4), Quad(v1, v4, v5, v6))
|
||||||
|
.map { it.cycleVertices(if (isBottom xor Config.nVidia) 0 else 1) }
|
||||||
|
.map { it.rotate(rotation).rotateUV(quadrant) }
|
||||||
|
.map { it.sprite(if (isBottom) spriteBottom else spriteTop) }
|
||||||
|
.map { if (isBottom) it.flipped else it }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun lidSquare(y: Double, isBottom: Boolean) = Array(4) { quadrant ->
|
||||||
|
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||||
|
listOf(
|
||||||
|
Quad.horizontalRectangle(x1 = 0.0, x2 = 0.5, z1 = 0.0, z2 = 0.5, y = y).clampUV(minU = 0.0, minV = 0.0)
|
||||||
|
.rotate(rotation).rotateUV(quadrant)
|
||||||
|
.sprite(if (isBottom) spriteBottom else spriteTop)
|
||||||
|
.let { if (isBottom) it.flipped else it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val zProtectionScale = zProtection.let { Double3(it, 1.0, it) }
|
||||||
|
|
||||||
|
protected fun List<Quad>.extendTop(size: Double) = map { q -> q.clampUV(minV = 0.5 - size).transformV { v ->
|
||||||
|
if (v.xyz.y > 0.501) v.copy(xyz = v.xyz * zProtectionScale) else v }
|
||||||
|
}
|
||||||
|
protected fun List<Quad>.extendBottom(size: Double) = map { q -> q.clampUV(maxV = -0.5 + size).transformV { v ->
|
||||||
|
if (v.xyz.y < -0.501) v.copy(xyz = v.xyz * zProtectionScale) else v }
|
||||||
|
}
|
||||||
|
protected fun List<Quad>.buildSides(quadsPerSprite: Int) = Array(4) { quadrant ->
|
||||||
|
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
|
||||||
|
this.map { it.rotate(rotation) }
|
||||||
|
.mapIndexed { idx, q -> if (idx % (2 * quadsPerSprite) >= quadsPerSprite) q.sprite(spriteRight) else q.sprite(spriteLeft) }
|
||||||
|
.bake(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun baseRotation(axis: Axis) = when(axis) {
|
||||||
|
Axis.X -> Rotation.fromUp[EAST.ordinal]
|
||||||
|
Axis.Y -> Rotation.fromUp[UP.ordinal]
|
||||||
|
Axis.Z -> Rotation.fromUp[SOUTH.ordinal]
|
||||||
|
}
|
||||||
|
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mesh definitions
|
||||||
|
// 4-element arrays hold prebuild meshes for each of the rotations around the axis
|
||||||
|
//
|
||||||
|
val sideSquare = sideSquare(-0.5, 0.5).buildSides(quadsPerSprite = 1)
|
||||||
|
val sideRoundSmall = sideRounded(radiusSmall, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||||
|
val sideRoundLarge = sideRounded(radiusLarge, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||||
|
|
||||||
|
val sideExtendTopSquare = sideSquare(0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 1)
|
||||||
|
val sideExtendTopRoundSmall = sideRounded(radiusSmall, 0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||||
|
val sideExtendTopRoundLarge = sideRounded(radiusLarge, 0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||||
|
|
||||||
|
val sideExtendBottomSquare = sideSquare(-0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 1)
|
||||||
|
val sideExtendBottomRoundSmall = sideRounded(radiusSmall, -0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||||
|
val sideExtendBottomRoundLarge = sideRounded(radiusLarge, -0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 2)
|
||||||
|
|
||||||
|
val lidTopSquare = lidSquare(0.5, false).bake(false)
|
||||||
|
val lidTopRoundSmall = lidRounded(radiusSmall, 0.5, false).bake(false)
|
||||||
|
val lidTopRoundLarge = lidRounded(radiusLarge, 0.5, false).bake(false)
|
||||||
|
|
||||||
|
val lidBottomSquare = lidSquare(-0.5, true).bake(false)
|
||||||
|
val lidBottomRoundSmall = lidRounded(radiusSmall, -0.5, true).bake(false)
|
||||||
|
val lidBottomRoundLarge = lidRounded(radiusLarge, -0.5, true).bake(false)
|
||||||
|
|
||||||
|
val transitionTop = sideRoundedTransition(radiusLarge, radiusSmall, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||||
|
val transitionBottom = sideRoundedTransition(radiusSmall, radiusLarge, -0.5, 0.5).buildSides(quadsPerSprite = 2)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helper fuctions for lids (block ends)
|
||||||
|
//
|
||||||
|
fun flatTop(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||||
|
SMALL_RADIUS -> lidTopRoundSmall[quadrant]
|
||||||
|
LARGE_RADIUS -> lidTopRoundLarge[quadrant]
|
||||||
|
SQUARE -> lidTopSquare[quadrant]
|
||||||
|
INVISIBLE -> lidTopSquare[quadrant]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun flatBottom(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||||
|
SMALL_RADIUS -> lidBottomRoundSmall[quadrant]
|
||||||
|
LARGE_RADIUS -> lidBottomRoundLarge[quadrant]
|
||||||
|
SQUARE -> lidBottomSquare[quadrant]
|
||||||
|
INVISIBLE -> lidBottomSquare[quadrant]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extendTop(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||||
|
SMALL_RADIUS -> sideExtendTopRoundSmall[quadrant]
|
||||||
|
LARGE_RADIUS -> sideExtendTopRoundLarge[quadrant]
|
||||||
|
SQUARE -> sideExtendTopSquare[quadrant]
|
||||||
|
INVISIBLE -> sideExtendTopSquare[quadrant]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extendBottom(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
|
||||||
|
SMALL_RADIUS -> sideExtendBottomRoundSmall[quadrant]
|
||||||
|
LARGE_RADIUS -> sideExtendBottomRoundLarge[quadrant]
|
||||||
|
SQUARE -> sideExtendBottomSquare[quadrant]
|
||||||
|
INVISIBLE -> sideExtendBottomSquare[quadrant]
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt
Normal file
112
src/main/kotlin/mods/betterfoliage/render/column/ColumnModel.kt
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package mods.betterfoliage.render.column
|
||||||
|
|
||||||
|
import mods.betterfoliage.chunk.ChunkOverlayManager
|
||||||
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.NormalRender
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.NONSOLID
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.PARALLEL
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.PERPENDICULAR
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.INVISIBLE
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.LARGE_RADIUS
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SMALL_RADIUS
|
||||||
|
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.SQUARE
|
||||||
|
import mods.betterfoliage.render.lighting.ColumnLighting
|
||||||
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
|
import net.minecraft.util.Direction.Axis
|
||||||
|
|
||||||
|
abstract class ColumnModelBase(
|
||||||
|
wrapped: SpecialRenderModel
|
||||||
|
) : HalfBakedSpecialWrapper(wrapped) {
|
||||||
|
|
||||||
|
abstract val enabled: Boolean
|
||||||
|
abstract val overlayLayer: ColumnRenderLayer
|
||||||
|
abstract val connectPerpendicular: Boolean
|
||||||
|
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
|
||||||
|
|
||||||
|
override fun render(ctx: RenderCtxBase, noDecorations: Boolean) {
|
||||||
|
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
|
||||||
|
|
||||||
|
when(roundLog) {
|
||||||
|
ColumnLayerData.SkipRender -> return
|
||||||
|
NormalRender -> return super.render(ctx, noDecorations)
|
||||||
|
ColumnLayerData.ResolveError, null -> {
|
||||||
|
return super.render(ctx, noDecorations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||||
|
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
|
||||||
|
return super.render(ctx, noDecorations)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.vertexLighter = ColumnLighting
|
||||||
|
|
||||||
|
val axis = roundLog.column.axis ?: Axis.Y
|
||||||
|
val baseRotation = ColumnMeshSet.baseRotation(axis)
|
||||||
|
ColumnMeshSet.quadrantRotations.forEachIndexed { idx, quadrantRotation ->
|
||||||
|
// set rotation for the current quadrant
|
||||||
|
val rotation = baseRotation + quadrantRotation
|
||||||
|
val meshSet = getMeshSet(axis, idx)
|
||||||
|
|
||||||
|
// disallow sharp discontinuities in the chamfer radius, or tapering-in where inappropriate
|
||||||
|
if (roundLog.quadrants[idx] == LARGE_RADIUS &&
|
||||||
|
roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] != LARGE_RADIUS &&
|
||||||
|
roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] != LARGE_RADIUS) {
|
||||||
|
roundLog.quadrants[idx] = SMALL_RADIUS
|
||||||
|
}
|
||||||
|
|
||||||
|
// select meshes for current quadrant based on connectivity rules
|
||||||
|
val sideMesh = when (roundLog.quadrants[idx]) {
|
||||||
|
SMALL_RADIUS -> meshSet.sideRoundSmall[idx]
|
||||||
|
LARGE_RADIUS -> if (roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] == SMALL_RADIUS) meshSet.transitionTop[idx]
|
||||||
|
else if (roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] == SMALL_RADIUS) meshSet.transitionBottom[idx]
|
||||||
|
else meshSet.sideRoundLarge[idx]
|
||||||
|
SQUARE -> meshSet.sideSquare[idx]
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val upMesh = when(roundLog.upType) {
|
||||||
|
NONSOLID -> meshSet.flatTop(roundLog.quadrants, idx)
|
||||||
|
PERPENDICULAR -> {
|
||||||
|
if (!connectPerpendicular) {
|
||||||
|
meshSet.flatTop(roundLog.quadrants, idx)
|
||||||
|
} else {
|
||||||
|
meshSet.extendTop(roundLog.quadrants, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PARALLEL -> {
|
||||||
|
if (roundLog.quadrants[idx] discontinuousWith roundLog.quadrantsTop[idx] &&
|
||||||
|
roundLog.quadrants[idx].let { it == SQUARE || it == INVISIBLE } )
|
||||||
|
meshSet.flatTop(roundLog.quadrants, idx)
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val downMesh = when(roundLog.downType) {
|
||||||
|
NONSOLID -> meshSet.flatBottom(roundLog.quadrants, idx)
|
||||||
|
PERPENDICULAR -> {
|
||||||
|
if (!connectPerpendicular) {
|
||||||
|
meshSet.flatBottom(roundLog.quadrants, idx)
|
||||||
|
} else {
|
||||||
|
meshSet.extendBottom(roundLog.quadrants, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PARALLEL -> {
|
||||||
|
if (roundLog.quadrants[idx] discontinuousWith roundLog.quadrantsBottom[idx] &&
|
||||||
|
roundLog.quadrants[idx].let { it == SQUARE || it == INVISIBLE } )
|
||||||
|
meshSet.flatBottom(roundLog.quadrants, idx)
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
// render
|
||||||
|
sideMesh?.let { ctx.renderQuads(it) }
|
||||||
|
upMesh?.let { ctx.renderQuads(it) }
|
||||||
|
downMesh?.let { ctx.renderQuads(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,7 +90,6 @@ abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
|
|||||||
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
override fun calculate(ctx: BlockCtx): ColumnLayerData {
|
||||||
// TODO detect round logs
|
// TODO detect round logs
|
||||||
if (allDirections.all { dir -> ctx.offset(dir).let { it.isNormalCube } }) return ColumnLayerData.SkipRender
|
if (allDirections.all { dir -> ctx.offset(dir).let { it.isNormalCube } }) return ColumnLayerData.SkipRender
|
||||||
// val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
|
|
||||||
val columnTextures = getColumnKey(ctx.state) ?: return ColumnLayerData.ResolveError
|
val columnTextures = getColumnKey(ctx.state) ?: return ColumnLayerData.ResolveError
|
||||||
|
|
||||||
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
// if log axis is not defined and "Default to vertical" config option is not set, render normally
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package mods.betterfoliage.render.lighting
|
|||||||
|
|
||||||
import mods.betterfoliage.model.HalfBakedQuad
|
import mods.betterfoliage.model.HalfBakedQuad
|
||||||
import mods.betterfoliage.util.Double3
|
import mods.betterfoliage.util.Double3
|
||||||
import mods.betterfoliage.util.EPSILON
|
import mods.betterfoliage.util.EPSILON_ONE
|
||||||
|
import mods.betterfoliage.util.EPSILON_ZERO
|
||||||
import mods.betterfoliage.util.minBy
|
import mods.betterfoliage.util.minBy
|
||||||
import net.minecraft.client.renderer.color.BlockColors
|
import net.minecraft.client.renderer.color.BlockColors
|
||||||
import net.minecraft.util.Direction
|
import net.minecraft.util.Direction
|
||||||
@@ -41,6 +42,12 @@ class VanillaQuadLighting {
|
|||||||
abstract class VanillaVertexLighter {
|
abstract class VanillaVertexLighter {
|
||||||
abstract fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting)
|
abstract fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update lighting for each vertex with AO values from one of the corners.
|
||||||
|
* Does not calculate missing AO values!
|
||||||
|
* @param quad the quad to shade
|
||||||
|
* @param func selector function from vertex position to directed corner index of desired AO values
|
||||||
|
*/
|
||||||
inline fun VanillaQuadLighting.updateWithCornerAo(quad: HalfBakedQuad, func: (Double3)->Int?) {
|
inline fun VanillaQuadLighting.updateWithCornerAo(quad: HalfBakedQuad, func: (Double3)->Int?) {
|
||||||
quad.raw.verts.forEachIndexed { idx, vertex ->
|
quad.raw.verts.forEachIndexed { idx, vertex ->
|
||||||
func(vertex.xyz)?.let {
|
func(vertex.xyz)?.let {
|
||||||
@@ -51,9 +58,13 @@ abstract class VanillaVertexLighter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replicates vanilla shading for full blocks. Interpolation for non-full blocks
|
||||||
|
* is not implemented.
|
||||||
|
*/
|
||||||
object VanillaFullBlockLighting : VanillaVertexLighter() {
|
object VanillaFullBlockLighting : VanillaVertexLighter() {
|
||||||
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
||||||
// TODO bounds checking
|
// TODO bounds checking & interpolation
|
||||||
val face = quad.raw.face()
|
val face = quad.raw.face()
|
||||||
lighting.calc.fillLightData(face, true)
|
lighting.calc.fillLightData(face, true)
|
||||||
lighting.updateWithCornerAo(quad) { nearestCornerOnFace(it, face) }
|
lighting.updateWithCornerAo(quad) { nearestCornerOnFace(it, face) }
|
||||||
@@ -62,7 +73,7 @@ object VanillaFullBlockLighting : VanillaVertexLighter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object RoundLeafLighting : VanillaVertexLighter() {
|
object RoundLeafLightingPreferUp : VanillaVertexLighter() {
|
||||||
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
||||||
val angles = getAngles45(quad)?.let { normalFaces ->
|
val angles = getAngles45(quad)?.let { normalFaces ->
|
||||||
lighting.calc.fillLightData(normalFaces.first)
|
lighting.calc.fillLightData(normalFaces.first)
|
||||||
@@ -79,6 +90,28 @@ object RoundLeafLighting : VanillaVertexLighter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lights vertices with the AO values from the nearest corner on either of
|
||||||
|
* the 2 faces the quad normal points towards.
|
||||||
|
*/
|
||||||
|
object RoundLeafLighting : VanillaVertexLighter() {
|
||||||
|
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
||||||
|
val angles = getAngles45(quad)?.let { normalFaces ->
|
||||||
|
lighting.calc.fillLightData(normalFaces.first)
|
||||||
|
lighting.calc.fillLightData(normalFaces.second)
|
||||||
|
lighting.updateWithCornerAo(quad) { vertex ->
|
||||||
|
val cornerUndir = AoSideHelper.getCornerUndir(vertex.x, vertex.y, vertex.z)
|
||||||
|
val preferredFace = normalFaces.minBy { faceDistance(it, vertex) }
|
||||||
|
AoSideHelper.boxCornersDirFromUndir[preferredFace.ordinal][cornerUndir]
|
||||||
|
}
|
||||||
|
lighting.updateBlockTint(quad.baked.tintIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lights vertices with the AO values from the nearest corner on the preferred face.
|
||||||
|
*/
|
||||||
class LightingPreferredFace(val face: Direction) : VanillaVertexLighter() {
|
class LightingPreferredFace(val face: Direction) : VanillaVertexLighter() {
|
||||||
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
||||||
lighting.calc.fillLightData(face)
|
lighting.calc.fillLightData(face)
|
||||||
@@ -87,6 +120,38 @@ class LightingPreferredFace(val face: Direction) : VanillaVertexLighter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ColumnLighting : VanillaVertexLighter() {
|
||||||
|
override fun updateLightmapAndColor(quad: HalfBakedQuad, lighting: VanillaQuadLighting) {
|
||||||
|
// faces pointing in cardinal directions
|
||||||
|
getNormalFace(quad)?.let { face ->
|
||||||
|
lighting.calc.fillLightData(face)
|
||||||
|
lighting.updateWithCornerAo(quad) { nearestCornerOnFace(it, face) }
|
||||||
|
lighting.updateBlockTint(quad.baked.tintIndex)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// faces pointing at 45deg angles
|
||||||
|
getAngles45(quad)?.let { (face1, face2) ->
|
||||||
|
lighting.calc.fillLightData(face1)
|
||||||
|
lighting.calc.fillLightData(face2)
|
||||||
|
quad.raw.verts.forEachIndexed { idx, vertex ->
|
||||||
|
val cornerUndir = AoSideHelper.getCornerUndir(vertex.xyz.x, vertex.xyz.y, vertex.xyz.z)
|
||||||
|
val cornerDir1 = AoSideHelper.boxCornersDirFromUndir[face1.ordinal][cornerUndir]
|
||||||
|
val cornerDir2 = AoSideHelper.boxCornersDirFromUndir[face2.ordinal][cornerUndir]
|
||||||
|
if (cornerDir1 == null || cornerDir2 == null) return@let
|
||||||
|
val ao1 = lighting.calc.aoData[cornerDir1]
|
||||||
|
val ao2 = lighting.calc.aoData[cornerDir2]
|
||||||
|
lighting.packedLight[idx] = ((ao1.packedLight + ao2.packedLight) shr 1) and 0xFF00FF
|
||||||
|
lighting.colorMultiplier[idx] = (ao1.colorMultiplier + ao2.colorMultiplier) * 0.5f
|
||||||
|
}
|
||||||
|
lighting.updateBlockTint(quad.baked.tintIndex)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// something is wrong...
|
||||||
|
lighting.updateWithCornerAo(quad) { nearestCornerOnFace(it, quad.raw.face()) }
|
||||||
|
lighting.updateBlockTint(quad.baked.tintIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the directed box corner index for the corner nearest the given vertex,
|
* Return the directed box corner index for the corner nearest the given vertex,
|
||||||
* which is on the given face. May return null if the vertex is closest to
|
* which is on the given face. May return null if the vertex is closest to
|
||||||
@@ -107,9 +172,9 @@ fun getAngles45(quad: HalfBakedQuad): Pair<Direction, Direction>? {
|
|||||||
val normal = quad.raw.normal
|
val normal = quad.raw.normal
|
||||||
// one of the components must be close to zero
|
// one of the components must be close to zero
|
||||||
val zeroAxis = when {
|
val zeroAxis = when {
|
||||||
abs(normal.x) < EPSILON -> Axis.X
|
abs(normal.x) < EPSILON_ZERO -> Axis.X
|
||||||
abs(normal.y) < EPSILON -> Axis.Y
|
abs(normal.y) < EPSILON_ZERO -> Axis.Y
|
||||||
abs(normal.z) < EPSILON -> Axis.Z
|
abs(normal.z) < EPSILON_ZERO -> Axis.Z
|
||||||
else -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
// the other two must be of similar magnitude
|
// the other two must be of similar magnitude
|
||||||
@@ -118,7 +183,7 @@ fun getAngles45(quad: HalfBakedQuad): Pair<Direction, Direction>? {
|
|||||||
Axis.Y -> abs(abs(normal.x) - abs(normal.z))
|
Axis.Y -> abs(abs(normal.x) - abs(normal.z))
|
||||||
Axis.Z -> abs(abs(normal.x) - abs(normal.y))
|
Axis.Z -> abs(abs(normal.x) - abs(normal.y))
|
||||||
}
|
}
|
||||||
if (diff > EPSILON) return null
|
if (diff > EPSILON_ZERO) return null
|
||||||
return when(zeroAxis) {
|
return when(zeroAxis) {
|
||||||
Axis.X -> Pair(if (normal.y > 0.0f) UP else DOWN, if (normal.z > 0.0f) SOUTH else NORTH)
|
Axis.X -> Pair(if (normal.y > 0.0f) UP else DOWN, if (normal.z > 0.0f) SOUTH else NORTH)
|
||||||
Axis.Y -> Pair(if (normal.x > 0.0f) EAST else WEST, if (normal.z > 0.0f) SOUTH else NORTH)
|
Axis.Y -> Pair(if (normal.x > 0.0f) EAST else WEST, if (normal.z > 0.0f) SOUTH else NORTH)
|
||||||
@@ -126,6 +191,18 @@ fun getAngles45(quad: HalfBakedQuad): Pair<Direction, Direction>? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getNormalFace(quad: HalfBakedQuad) = quad.raw.normal.let { normal ->
|
||||||
|
when {
|
||||||
|
normal.x > EPSILON_ONE -> EAST
|
||||||
|
normal.x < -EPSILON_ONE -> WEST
|
||||||
|
normal.y > EPSILON_ONE -> UP
|
||||||
|
normal.y < -EPSILON_ONE -> DOWN
|
||||||
|
normal.z > EPSILON_ONE -> SOUTH
|
||||||
|
normal.z < -EPSILON_ONE -> NORTH
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun faceDistance(face: Direction, pos: Double3) = when(face) {
|
fun faceDistance(face: Direction, pos: Double3) = when(face) {
|
||||||
WEST -> pos.x; EAST -> 1.0 - pos.x
|
WEST -> pos.x; EAST -> 1.0 - pos.x
|
||||||
DOWN -> pos.y; UP -> 1.0 - pos.y
|
DOWN -> pos.y; UP -> 1.0 - pos.y
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package mods.betterfoliage.render.particle
|
package mods.betterfoliage.render.particle
|
||||||
|
|
||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.model.HSB
|
|
||||||
import mods.betterfoliage.texture.LeafParticleKey
|
|
||||||
import mods.betterfoliage.texture.LeafParticleRegistry
|
|
||||||
import mods.betterfoliage.util.Double3
|
import mods.betterfoliage.util.Double3
|
||||||
import mods.betterfoliage.util.PI2
|
import mods.betterfoliage.util.PI2
|
||||||
import mods.betterfoliage.util.minmax
|
import mods.betterfoliage.util.minmax
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package mods.betterfoliage.texture
|
package mods.betterfoliage.render.particle
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.model.Color
|
import mods.betterfoliage.model.Color
|
||||||
import mods.betterfoliage.model.FixedSpriteSet
|
import mods.betterfoliage.model.FixedSpriteSet
|
||||||
import mods.betterfoliage.model.SpriteSet
|
import mods.betterfoliage.model.SpriteSet
|
||||||
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent
|
import mods.betterfoliage.resource.VeryEarlyReloadListener
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.HasLogger
|
import mods.betterfoliage.util.HasLogger
|
||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
@@ -15,7 +15,7 @@ import net.minecraft.client.renderer.texture.MissingTextureSprite
|
|||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level.INFO
|
||||||
|
|
||||||
interface LeafBlockModel {
|
interface LeafBlockModel {
|
||||||
val key: LeafParticleKey
|
val key: LeafParticleKey
|
||||||
@@ -26,26 +26,28 @@ interface LeafParticleKey {
|
|||||||
val overrideColor: Color?
|
val overrideColor: Color?
|
||||||
}
|
}
|
||||||
|
|
||||||
object LeafParticleRegistry : HasLogger() {
|
object LeafParticleRegistry : HasLogger(), VeryEarlyReloadListener {
|
||||||
val typeMappings = TextureMatcher()
|
val typeMappings = TextureMatcher()
|
||||||
|
val allTypes get() = (typeMappings.mappings.map { it.type } + "default").distinct()
|
||||||
|
|
||||||
val particles = hashMapOf<String, SpriteSet>()
|
val particles = hashMapOf<String, SpriteSet>()
|
||||||
|
|
||||||
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
||||||
|
|
||||||
@SubscribeEvent
|
override fun onReloadStarted() {
|
||||||
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
|
||||||
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
|
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
|
||||||
|
detailLogger.log(INFO, "Loaded leaf particle mappings, types = [${allTypes.joinToString(", ")}]")
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||||
if (event.map.textureLocation == Atlas.PARTICLES.resourceId) {
|
if (event.map.textureLocation == Atlas.PARTICLES.resourceId) {
|
||||||
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
allTypes.forEach { leafType ->
|
||||||
val locations = (0 until 16).map { idx ->
|
val locations = (0 until 16).map { idx ->
|
||||||
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/falling_leaf_${leafType}_$idx")
|
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/falling_leaf_${leafType}_$idx")
|
||||||
}.filter { resourceManager.hasResource(Atlas.PARTICLES.file(it)) }
|
}.filter { resourceManager.hasResource(Atlas.PARTICLES.file(it)) }
|
||||||
|
|
||||||
detailLogger.log(Level.INFO, "Registering sprites for leaf particle type [$leafType], ${locations.size} sprites found")
|
detailLogger.log(INFO, "Registering sprites for leaf particle type [$leafType], ${locations.size} sprites found")
|
||||||
locations.forEach { event.addSprite(it) }
|
locations.forEach { event.addSprite(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +62,7 @@ object LeafParticleRegistry : HasLogger() {
|
|||||||
}
|
}
|
||||||
.map { event.map.getSprite(it) }
|
.map { event.map.getSprite(it) }
|
||||||
.filter { it !is MissingTextureSprite }
|
.filter { it !is MissingTextureSprite }
|
||||||
detailLogger.log(Level.INFO, "Leaf particle type [$leafType], ${sprites.size} sprites in atlas")
|
detailLogger.log(INFO, "Leaf particle type [$leafType], ${sprites.size} sprites in atlas")
|
||||||
particles[leafType] = FixedSpriteSet(sprites)
|
particles[leafType] = FixedSpriteSet(sprites)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package mods.betterfoliage.render.particle
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.IVertexBuilder
|
||||||
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
|
import mods.betterfoliage.config.Config
|
||||||
|
import mods.betterfoliage.model.SpriteDelegate
|
||||||
|
import mods.betterfoliage.model.SpriteSetDelegate
|
||||||
|
import mods.betterfoliage.util.Atlas
|
||||||
|
import mods.betterfoliage.util.Double3
|
||||||
|
import mods.betterfoliage.util.PI2
|
||||||
|
import mods.betterfoliage.util.forEachPairIndexed
|
||||||
|
import mods.betterfoliage.util.randomD
|
||||||
|
import mods.betterfoliage.util.randomI
|
||||||
|
import net.minecraft.client.particle.IParticleRenderType
|
||||||
|
import net.minecraft.client.renderer.ActiveRenderInfo
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.util.math.MathHelper
|
||||||
|
import net.minecraft.world.World
|
||||||
|
import java.util.Deque
|
||||||
|
import java.util.LinkedList
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
class RisingSoulParticle(
|
||||||
|
world: World, pos: BlockPos
|
||||||
|
) : AbstractParticle(
|
||||||
|
world, pos.x.toDouble() + 0.5, pos.y.toDouble() + 1.0, pos.z.toDouble() + 0.5
|
||||||
|
) {
|
||||||
|
|
||||||
|
val particleTrail: Deque<Double3> = LinkedList<Double3>()
|
||||||
|
val initialPhase = randomD(max = PI2)
|
||||||
|
|
||||||
|
init {
|
||||||
|
motionY = 0.1
|
||||||
|
particleGravity = 0.0f
|
||||||
|
sprite = headIcons[randomI(max = 1024)]
|
||||||
|
maxAge = MathHelper.floor((0.6 + 0.4 * randomD()) * Config.risingSoul.lifetime * 20.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isValid: Boolean get() = true
|
||||||
|
|
||||||
|
override fun update() {
|
||||||
|
val phase = initialPhase + (age.toDouble() * PI2 / 64.0)
|
||||||
|
val cosPhase = cos(phase);
|
||||||
|
val sinPhase = sin(phase)
|
||||||
|
velocity.setTo(Config.risingSoul.perturb.let { Double3(cosPhase * it, 0.1, sinPhase * it) })
|
||||||
|
|
||||||
|
particleTrail.addFirst(currentPos.copy())
|
||||||
|
while (particleTrail.size > Config.risingSoul.trailLength) particleTrail.removeLast()
|
||||||
|
|
||||||
|
if (!Config.enabled) setExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderParticle(vertexBuilder: IVertexBuilder, camera: ActiveRenderInfo, tickDelta: Float) {
|
||||||
|
var alpha = Config.risingSoul.opacity.toFloat()
|
||||||
|
if (age > maxAge - 40) alpha *= (maxAge - age) / 40.0f
|
||||||
|
|
||||||
|
renderParticleQuad(
|
||||||
|
vertexBuilder, camera, tickDelta,
|
||||||
|
size = Config.risingSoul.headSize * 0.25,
|
||||||
|
alpha = alpha
|
||||||
|
)
|
||||||
|
|
||||||
|
var scale = Config.risingSoul.trailSize * 0.25
|
||||||
|
particleTrail.forEachPairIndexed { idx, current, previous ->
|
||||||
|
scale *= Config.risingSoul.sizeDecay
|
||||||
|
alpha *= Config.risingSoul.opacityDecay.toFloat()
|
||||||
|
if (idx % Config.risingSoul.trailDensity == 0)
|
||||||
|
renderParticleQuad(
|
||||||
|
vertexBuilder, camera, tickDelta,
|
||||||
|
currentPos = current,
|
||||||
|
prevPos = previous,
|
||||||
|
size = scale,
|
||||||
|
alpha = alpha,
|
||||||
|
sprite = trackIcon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRenderType(): IParticleRenderType = IParticleRenderType.PARTICLE_SHEET_TRANSLUCENT
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val headIcons by SpriteSetDelegate(Atlas.PARTICLES) { idx ->
|
||||||
|
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/rising_soul_$idx")
|
||||||
|
}
|
||||||
|
val trackIcon by SpriteDelegate(Atlas.PARTICLES) { ResourceLocation(BetterFoliageMod.MOD_ID, "particle/soul_track") }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package mods.betterfoliage.resource
|
||||||
|
|
||||||
|
import net.minecraft.profiler.IProfiler
|
||||||
|
import net.minecraft.resources.IFutureReloadListener
|
||||||
|
import net.minecraft.resources.IResourceManager
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Catch resource reload extremely early, before any of the reloaders
|
||||||
|
* have started working.
|
||||||
|
*/
|
||||||
|
interface VeryEarlyReloadListener : IFutureReloadListener {
|
||||||
|
override fun reload(
|
||||||
|
stage: IFutureReloadListener.IStage,
|
||||||
|
resourceManager: IResourceManager,
|
||||||
|
preparationsProfiler: IProfiler,
|
||||||
|
reloadProfiler: IProfiler,
|
||||||
|
backgroundExecutor: Executor,
|
||||||
|
gameExecutor: Executor
|
||||||
|
): CompletableFuture<Void> {
|
||||||
|
onReloadStarted()
|
||||||
|
return stage.markCompleteAwaitingOthers(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onReloadStarted() {}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import mods.betterfoliage.util.Atlas
|
|||||||
import mods.betterfoliage.util.HasLogger
|
import mods.betterfoliage.util.HasLogger
|
||||||
import mods.betterfoliage.util.Invalidator
|
import mods.betterfoliage.util.Invalidator
|
||||||
import mods.betterfoliage.util.SimpleInvalidator
|
import mods.betterfoliage.util.SimpleInvalidator
|
||||||
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.renderer.model.IBakedModel
|
import net.minecraft.client.renderer.model.IBakedModel
|
||||||
import net.minecraft.client.renderer.model.IModelTransform
|
import net.minecraft.client.renderer.model.IModelTransform
|
||||||
import net.minecraft.client.renderer.model.IUnbakedModel
|
import net.minecraft.client.renderer.model.IUnbakedModel
|
||||||
@@ -19,7 +20,9 @@ import net.minecraftforge.eventbus.api.Event
|
|||||||
import net.minecraftforge.eventbus.api.EventPriority
|
import net.minecraftforge.eventbus.api.EventPriority
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager
|
import net.minecraftforge.fml.loading.progress.StartupMessageManager
|
||||||
|
import org.apache.logging.log4j.Level.DEBUG
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import org.apache.logging.log4j.Level.INFO
|
||||||
|
import org.apache.logging.log4j.Logger
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
|
|
||||||
@@ -27,6 +30,21 @@ data class ModelDefinitionsLoadedEvent(
|
|||||||
val bakery: ModelBakery
|
val bakery: ModelBakery
|
||||||
) : Event()
|
) : Event()
|
||||||
|
|
||||||
|
data class ModelDiscoveryContext(
|
||||||
|
val bakery: ModelBakery,
|
||||||
|
val blockState: BlockState,
|
||||||
|
val modelLocation: ResourceLocation,
|
||||||
|
val sprites: MutableSet<ResourceLocation>,
|
||||||
|
val replacements: MutableMap<ResourceLocation, ModelBakingKey>,
|
||||||
|
val logger: Logger
|
||||||
|
) {
|
||||||
|
fun getUnbaked(location: ResourceLocation = modelLocation) = bakery.getUnbakedModel(location)
|
||||||
|
fun addReplacement(key: ModelBakingKey) {
|
||||||
|
replacements[modelLocation] = key
|
||||||
|
logger.log(INFO, "Adding model replacement $modelLocation -> $key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface ModelDiscovery {
|
interface ModelDiscovery {
|
||||||
fun onModelsLoaded(
|
fun onModelsLoaded(
|
||||||
bakery: ModelBakery,
|
bakery: ModelBakery,
|
||||||
@@ -35,14 +53,20 @@ interface ModelDiscovery {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ModelBakingContext(
|
||||||
|
val bakery: ModelBakery,
|
||||||
|
val spriteGetter: Function<Material, TextureAtlasSprite>,
|
||||||
|
val location: ResourceLocation,
|
||||||
|
val transform: IModelTransform,
|
||||||
|
val logger: Logger
|
||||||
|
) {
|
||||||
|
fun getUnbaked() = bakery.getUnbakedModel(location)
|
||||||
|
fun getBaked() = bakery.getBakedModel(location, transform, spriteGetter)
|
||||||
|
}
|
||||||
|
|
||||||
interface ModelBakingKey {
|
interface ModelBakingKey {
|
||||||
fun bake(
|
fun bake(ctx: ModelBakingContext): IBakedModel? =
|
||||||
location: ResourceLocation,
|
ctx.getUnbaked().bakeModel(ctx.bakery, ctx.spriteGetter, ctx.transform, ctx.location)
|
||||||
unbaked: IUnbakedModel,
|
|
||||||
transform: IModelTransform,
|
|
||||||
bakery: ModelBakery,
|
|
||||||
spriteGetter: Function<Material, TextureAtlasSprite>
|
|
||||||
): IBakedModel? = unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object BakeWrapperManager : Invalidator, HasLogger() {
|
object BakeWrapperManager : Invalidator, HasLogger() {
|
||||||
@@ -57,14 +81,16 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
|||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||||
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
modelsValid.invalidate()
|
modelsValid.invalidate()
|
||||||
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
||||||
logger.log(INFO, "starting model discovery (${discoverers.size} listeners)")
|
logger.log(INFO, "starting model discovery (${discoverers.size} listeners)")
|
||||||
discoverers.forEach { listener ->
|
discoverers.forEach { listener ->
|
||||||
val replacementsLocal = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
val replacementsLocal = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
||||||
listener.onModelsLoaded(event.bakery, sprites, replacementsLocal)
|
listener.onModelsLoaded(event.bakery, sprites, replacements)
|
||||||
replacements.putAll(replacementsLocal)
|
|
||||||
}
|
}
|
||||||
|
val elapsed = System.currentTimeMillis() - startTime
|
||||||
|
logger.log(INFO, "finished model discovery in $elapsed ms, ${replacements.size} top-level replacements")
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
@@ -89,17 +115,12 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
|||||||
transform: IModelTransform,
|
transform: IModelTransform,
|
||||||
location: ResourceLocation
|
location: ResourceLocation
|
||||||
): IBakedModel? {
|
): IBakedModel? {
|
||||||
|
val ctx = ModelBakingContext(bakery, spriteGetter, location, transform, detailLogger)
|
||||||
// bake replacement if available
|
// bake replacement if available
|
||||||
replacements[location]?.let { replacement ->
|
replacements[location]?.let { replacement ->
|
||||||
detailLogger.log(INFO, "Baking replacement for [${unbaked::class.java.simpleName}] $location -> $replacement")
|
detailLogger.log(INFO, "Baking replacement for [${unbaked::class.java.simpleName}] $location -> $replacement")
|
||||||
return replacement.bake(location, unbaked, transform, bakery, spriteGetter)
|
return replacement.bake(ctx)
|
||||||
}
|
}
|
||||||
// container model support
|
|
||||||
if (unbaked is VariantList) SpecialRenderVariantList.bakeIfSpecial(location, unbaked, bakery, spriteGetter)?.let {
|
|
||||||
detailLogger.log(INFO, "Wrapping container [${unbaked::class.java.simpleName}] $location")
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
return unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
return unbaked.bakeModel(bakery, spriteGetter, transform, location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
package mods.betterfoliage.resource.discovery
|
package mods.betterfoliage.resource.discovery
|
||||||
|
|
||||||
import mods.betterfoliage.Client
|
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
|
|
||||||
class BlockTypeCache {
|
class BlockTypeCache {
|
||||||
val leaf = mutableSetOf<BlockState>()
|
val leaf = mutableSetOf<BlockState>()
|
||||||
val grass = mutableSetOf<BlockState>()
|
val grass = mutableSetOf<BlockState>()
|
||||||
val dirt = mutableSetOf<BlockState>()
|
val dirt = mutableSetOf<BlockState>()
|
||||||
|
|
||||||
companion object : ModelDiscovery {
|
val stateKeys = mutableMapOf<BlockState, ModelBakingKey>()
|
||||||
override fun onModelsLoaded(bakery: ModelBakery, sprites: MutableSet<ResourceLocation>, replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
) {
|
inline fun <reified T> getTyped(state: BlockState) = stateKeys[state] as? T
|
||||||
Client.blockTypes = BlockTypeCache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mods.betterfoliage.config
|
package mods.betterfoliage.resource.discovery
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.util.getJavaClass
|
import mods.betterfoliage.util.getJavaClass
|
||||||
@@ -6,7 +6,7 @@ import mods.betterfoliage.util.getLines
|
|||||||
import mods.betterfoliage.util.resourceManager
|
import mods.betterfoliage.util.resourceManager
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import org.apache.logging.log4j.Level.DEBUG
|
import org.apache.logging.log4j.Level.INFO
|
||||||
|
|
||||||
interface IBlockMatcher {
|
interface IBlockMatcher {
|
||||||
fun matchesClass(block: Block): Boolean
|
fun matchesClass(block: Block): Boolean
|
||||||
@@ -28,7 +28,6 @@ class ConfigurableBlockMatcher(val location: ResourceLocation) : IBlockMatcher {
|
|||||||
|
|
||||||
val blackList = mutableListOf<Class<*>>()
|
val blackList = mutableListOf<Class<*>>()
|
||||||
val whiteList = mutableListOf<Class<*>>()
|
val whiteList = mutableListOf<Class<*>>()
|
||||||
// override fun convertValue(line: String) = getJavaClass(line)
|
|
||||||
|
|
||||||
override fun matchesClass(block: Block): Boolean {
|
override fun matchesClass(block: Block): Boolean {
|
||||||
val blockClass = block.javaClass
|
val blockClass = block.javaClass
|
||||||
@@ -48,7 +47,7 @@ class ConfigurableBlockMatcher(val location: ResourceLocation) : IBlockMatcher {
|
|||||||
blackList.clear()
|
blackList.clear()
|
||||||
whiteList.clear()
|
whiteList.clear()
|
||||||
resourceManager.getAllResources(location).forEach { resource ->
|
resourceManager.getAllResources(location).forEach { resource ->
|
||||||
logger.log(DEBUG, "Reading resource $location from pack ${resource.packName}")
|
logger.log(INFO, "Reading block class configuration $location from pack ${resource.packName}")
|
||||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||||
if (line.startsWith("-")) getJavaClass(line.substring(1))?.let { blackList.add(it) }
|
if (line.startsWith("-")) getJavaClass(line.substring(1))?.let { blackList.add(it) }
|
||||||
else getJavaClass(line)?.let { whiteList.add(it) }
|
else getJavaClass(line)?.let { whiteList.add(it) }
|
||||||
@@ -68,7 +67,7 @@ class ModelTextureListConfiguration(val location: ResourceLocation) {
|
|||||||
val modelList = mutableListOf<ModelTextureList>()
|
val modelList = mutableListOf<ModelTextureList>()
|
||||||
fun readDefaults() {
|
fun readDefaults() {
|
||||||
resourceManager.getAllResources(location).forEach { resource ->
|
resourceManager.getAllResources(location).forEach { resource ->
|
||||||
logger.log(DEBUG, "Reading resource $location from pack ${resource.packName}")
|
logger.log(INFO, "Reading model/texture configuration $location from pack ${resource.packName}")
|
||||||
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
resource.getLines().map{ it.trim() }.filter { !it.startsWith("//") && it.isNotEmpty() }.forEach { line ->
|
||||||
val elements = line.split(",")
|
val elements = line.split(",")
|
||||||
modelList.add(ModelTextureList(ResourceLocation(elements.first()), elements.drop(1)))
|
modelList.add(ModelTextureList(ResourceLocation(elements.first()), elements.drop(1)))
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
package mods.betterfoliage.resource.discovery
|
package mods.betterfoliage.resource.discovery
|
||||||
|
|
||||||
import com.google.common.base.Joiner
|
import com.google.common.base.Joiner
|
||||||
import mods.betterfoliage.config.IBlockMatcher
|
|
||||||
import mods.betterfoliage.config.ModelTextureList
|
|
||||||
import mods.betterfoliage.util.HasLogger
|
import mods.betterfoliage.util.HasLogger
|
||||||
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.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.client.renderer.model.multipart.Multipart
|
|
||||||
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.registries.ForgeRegistries
|
import net.minecraftforge.registries.ForgeRegistries
|
||||||
@@ -25,36 +21,29 @@ abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
|||||||
.flatMap { block -> block.stateContainer.validStates }
|
.flatMap { block -> block.stateContainer.validStates }
|
||||||
.forEach { state ->
|
.forEach { state ->
|
||||||
val location = BlockModelShapes.getModelLocation(state)
|
val location = BlockModelShapes.getModelLocation(state)
|
||||||
|
val ctx = ModelDiscoveryContext(bakery, state, location, sprites, replacements, detailLogger)
|
||||||
try {
|
try {
|
||||||
val hasReplaced = processModel(bakery, state, location, sprites, replacements)
|
processModel(ctx)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.log(Level.WARN, "Discovery error in $location", e)
|
logger.log(Level.WARN, "Discovery error in $location", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun processModel(
|
open fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
bakery: ModelBakery,
|
val model = ctx.getUnbaked()
|
||||||
state: BlockState,
|
|
||||||
location: ResourceLocation,
|
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
// built-in support for container models
|
// built-in support for container models
|
||||||
return when (val model = bakery.getUnbakedModel(location)) {
|
if (model is VariantList) {
|
||||||
is VariantList -> {
|
// per-location replacements need to be scoped to the variant list, as replacement models
|
||||||
val hasReplaced = model.variantList.fold(false) { hasReplaced, variant ->
|
// may need information from the BlockState which is not available at baking time
|
||||||
processModel(bakery, state, variant.modelLocation, sprites, replacements) || hasReplaced
|
val scopedReplacements = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
||||||
|
model.variantList.forEach { variant ->
|
||||||
|
processModel(ctx.copy(modelLocation = variant.modelLocation, replacements = scopedReplacements))
|
||||||
}
|
}
|
||||||
if (hasReplaced) replacements[location]
|
if (scopedReplacements.isNotEmpty()) {
|
||||||
hasReplaced
|
ctx.addReplacement(SpecialRenderVariantList(scopedReplacements))
|
||||||
}
|
}
|
||||||
is Multipart -> model.variants.fold(false) { hasReplaced, variantList ->
|
|
||||||
variantList.variantList.fold(false) { hasReplaced, variant ->
|
|
||||||
processModel(bakery, state, variant.modelLocation, sprites, replacements) || hasReplaced
|
|
||||||
} || hasReplaced
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,37 +53,23 @@ abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
|||||||
abstract val modelTextures: List<ModelTextureList>
|
abstract val modelTextures: List<ModelTextureList>
|
||||||
|
|
||||||
abstract fun processModel(
|
abstract fun processModel(
|
||||||
state: BlockState,
|
ctx: ModelDiscoveryContext,
|
||||||
location: ResourceLocation,
|
textureMatch: List<ResourceLocation>
|
||||||
textureMatch: List<ResourceLocation>,
|
)
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean
|
|
||||||
|
|
||||||
override fun processModel(
|
override fun processModel(ctx: ModelDiscoveryContext) {
|
||||||
bakery: ModelBakery,
|
val model = ctx.getUnbaked()
|
||||||
state: BlockState,
|
|
||||||
location: ResourceLocation,
|
|
||||||
sprites: MutableSet<ResourceLocation>,
|
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
|
||||||
): Boolean {
|
|
||||||
val model = bakery.getUnbakedModel(location)
|
|
||||||
if (model is BlockModel) {
|
if (model is BlockModel) {
|
||||||
val matchClass = matchClasses.matchingClass(state.block) ?: return false
|
val matchClass = matchClasses.matchingClass(ctx.blockState.block) ?: return
|
||||||
|
|
||||||
detailLogger.log(Level.INFO, "block state $state")
|
detailLogger.log(Level.INFO, "block state ${ctx.blockState}")
|
||||||
detailLogger.log(Level.INFO, " model $location")
|
detailLogger.log(Level.INFO, " model ${ctx.modelLocation}")
|
||||||
replacements[location]?.let { existing ->
|
detailLogger.log(Level.INFO, " class ${ctx.blockState.block.javaClass.name} matches ${matchClass.name}")
|
||||||
detailLogger.log(Level.INFO, " already processed as $existing")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
detailLogger.log(Level.INFO, " class ${state.block.javaClass.name} matches ${matchClass.name}")
|
|
||||||
|
|
||||||
modelTextures
|
modelTextures
|
||||||
.filter { matcher -> bakery.modelDerivesFrom(model, location, matcher.modelLocation) }
|
.filter { matcher -> ctx.bakery.modelDerivesFrom(model, ctx.modelLocation, matcher.modelLocation) }
|
||||||
.forEach { match ->
|
.forEach { match ->
|
||||||
detailLogger.log(Level.INFO, " model ${model} matches ${match.modelLocation}")
|
detailLogger.log(Level.INFO, " model $model matches ${match.modelLocation}")
|
||||||
|
|
||||||
val materials = match.textureNames.map { it to model.resolveTextureName(it) }
|
val materials = match.textureNames.map { it to model.resolveTextureName(it) }
|
||||||
val texMapString = Joiner.on(", ").join(materials.map { "${it.first}=${it.second.textureLocation}" })
|
val texMapString = Joiner.on(", ").join(materials.map { "${it.first}=${it.second.textureLocation}" })
|
||||||
@@ -102,12 +77,11 @@ abstract class ConfigurableModelDiscovery : AbstractModelDiscovery() {
|
|||||||
|
|
||||||
if (materials.all { it.second.textureLocation != MissingTextureSprite.getLocation() }) {
|
if (materials.all { it.second.textureLocation != MissingTextureSprite.getLocation() }) {
|
||||||
// found a valid model (all required textures exist)
|
// found a valid model (all required textures exist)
|
||||||
if (processModel(state, location, materials.map { it.second.textureLocation }, sprites, replacements))
|
processModel(ctx, materials.map { it.second.textureLocation })
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.processModel(bakery, state, location, sprites, replacements)
|
return super.processModel(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package mods.betterfoliage.resource.discovery
|
||||||
|
|
||||||
|
import mods.betterfoliage.model.HalfBakedSimpleModelWrapper
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
|
import mods.betterfoliage.model.SpecialRenderVariantList
|
||||||
|
import net.minecraft.client.renderer.model.IBakedModel
|
||||||
|
import net.minecraft.client.renderer.model.SimpleBakedModel
|
||||||
|
import net.minecraft.client.renderer.model.VariantList
|
||||||
|
import net.minecraft.util.ResourceLocation
|
||||||
|
import org.apache.logging.log4j.Level
|
||||||
|
import org.apache.logging.log4j.Level.INFO
|
||||||
|
import org.apache.logging.log4j.Level.WARN
|
||||||
|
|
||||||
|
class SpecialRenderVariantList(
|
||||||
|
val replacements: Map<ResourceLocation, ModelBakingKey>
|
||||||
|
) : ModelBakingKey {
|
||||||
|
|
||||||
|
override fun bake(ctx: ModelBakingContext): IBakedModel? {
|
||||||
|
val unbaked = ctx.getUnbaked()
|
||||||
|
if (unbaked !is VariantList) return super.bake(ctx)
|
||||||
|
|
||||||
|
// bake all variants, replace as needed
|
||||||
|
val bakedModels = unbaked.variantList.mapNotNull {
|
||||||
|
val variantCtx = ctx.copy(location = it.modelLocation, transform = it)
|
||||||
|
val replacement = replacements[it.modelLocation]
|
||||||
|
val baked = replacement?.let { replacement ->
|
||||||
|
ctx.logger.log(INFO, "Baking replacement for variant [${variantCtx.getUnbaked()::class.java.simpleName}] ${variantCtx.location} -> $replacement")
|
||||||
|
replacement.bake(variantCtx)
|
||||||
|
} ?: variantCtx.getBaked()
|
||||||
|
when(baked) {
|
||||||
|
is SpecialRenderModel -> it to baked
|
||||||
|
is SimpleBakedModel -> it to HalfBakedSimpleModelWrapper(baked)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// something fishy going on, possibly unknown model type
|
||||||
|
// let it through unchanged
|
||||||
|
if (bakedModels.isEmpty()) return super.bake(ctx)
|
||||||
|
|
||||||
|
if (bakedModels.size < unbaked.variantList.size) {
|
||||||
|
SpecialRenderVariantList.detailLogger.log(
|
||||||
|
WARN,
|
||||||
|
"Dropped ${unbaked.variantList.size - bakedModels.size} variants from model ${ctx.location}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val weightedSpecials = bakedModels.map { (variant, model) ->
|
||||||
|
SpecialRenderVariantList.WeightedModel(model, variant.weight)
|
||||||
|
}
|
||||||
|
return SpecialRenderVariantList(weightedSpecials, weightedSpecials[0].model)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "[SpecialRenderVariantList, ${replacements.size} replacements]"
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package mods.betterfoliage.resource.generated
|
package mods.betterfoliage.resource.generated
|
||||||
|
|
||||||
import mods.betterfoliage.texture.loadSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.bytes
|
import mods.betterfoliage.util.bytes
|
||||||
|
import mods.betterfoliage.util.loadSprite
|
||||||
import net.minecraft.resources.IResourceManager
|
import net.minecraft.resources.IResourceManager
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package mods.betterfoliage.resource.generated
|
package mods.betterfoliage.resource.generated
|
||||||
|
|
||||||
import mods.betterfoliage.texture.blendRGB
|
|
||||||
import mods.betterfoliage.texture.loadSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
|
import mods.betterfoliage.util.blendRGB
|
||||||
import mods.betterfoliage.util.bytes
|
import mods.betterfoliage.util.bytes
|
||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
|
import mods.betterfoliage.util.loadSprite
|
||||||
import mods.betterfoliage.util.set
|
import mods.betterfoliage.util.set
|
||||||
import net.minecraft.resources.IResourceManager
|
import net.minecraft.resources.IResourceManager
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package mods.betterfoliage.resource.generated
|
package mods.betterfoliage.resource.generated
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
import mods.betterfoliage.texture.loadSprite
|
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.bytes
|
import mods.betterfoliage.util.bytes
|
||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
import mods.betterfoliage.util.loadImage
|
import mods.betterfoliage.util.loadImage
|
||||||
|
import mods.betterfoliage.util.loadSprite
|
||||||
import mods.betterfoliage.util.resourceManager
|
import mods.betterfoliage.util.resourceManager
|
||||||
import mods.betterfoliage.util.set
|
import mods.betterfoliage.util.set
|
||||||
import net.minecraft.resources.IResource
|
import net.minecraft.resources.IResource
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
@file:JvmName("Utils")
|
|
||||||
package mods.betterfoliage.texture
|
|
||||||
|
|
||||||
import mods.betterfoliage.util.get
|
|
||||||
import mods.betterfoliage.util.loadImage
|
|
||||||
import net.minecraft.resources.IResourceManager
|
|
||||||
import net.minecraft.util.ResourceLocation
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
fun blendRGB(rgb1: Int, rgb2: Int, weight1: Int, weight2: Int): Int {
|
|
||||||
val r = (((rgb1 shr 16) and 255) * weight1 + ((rgb2 shr 16) and 255) * weight2) / (weight1 + weight2)
|
|
||||||
val g = (((rgb1 shr 8) and 255) * weight1 + ((rgb2 shr 8) and 255) * weight2) / (weight1 + weight2)
|
|
||||||
val b = ((rgb1 and 255) * weight1 + (rgb2 and 255) * weight2) / (weight1 + weight2)
|
|
||||||
val a = (rgb1 shr 24) and 255
|
|
||||||
val result = ((a shl 24) or (r shl 16) or (g shl 8) or b)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun IResourceManager.loadSprite(id: ResourceLocation) = this.get(id)?.loadImage() ?: throw IOException("Cannot load resource $id")
|
|
||||||
@@ -8,7 +8,8 @@ import net.minecraft.util.Direction.AxisDirection.NEGATIVE
|
|||||||
import net.minecraft.util.Direction.AxisDirection.POSITIVE
|
import net.minecraft.util.Direction.AxisDirection.POSITIVE
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
|
|
||||||
val EPSILON = 0.05
|
val EPSILON_ZERO = 0.05
|
||||||
|
val EPSILON_ONE = 0.95
|
||||||
|
|
||||||
// ================================
|
// ================================
|
||||||
// Axes and directions
|
// Axes and directions
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
// Vanilla
|
// Vanilla
|
||||||
block/column_side,end,end,side
|
block/column_side,side,end
|
||||||
block/cube_column,end,end,side
|
block/cube_column,side,end
|
||||||
block/cube_all,all,all,all
|
block/cube_all,all,all
|
||||||
|
|
||||||
// Agricultural Revolution
|
|
||||||
agriculturalrevolution:block/palmlog,top,top,texture
|
|
||||||
|
|
||||||
// Lithos Core
|
|
||||||
block/column_top,end,end,side_a,side_b
|
|
||||||
block/column_side_x,end,end,side_a,side_b
|
|
||||||
block/column_side_z,end,end,side_a,side_b
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"mixins": [
|
"mixins": [
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
|
"MixinOptifineBlockUtils"
|
||||||
],
|
],
|
||||||
"server": [
|
"server": [
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user