[WIP] Falling leaves working
+ more cleanup + fix double-tinted leaves
This commit is contained in:
@@ -17,6 +17,8 @@ dependencies {
|
|||||||
"minecraft"("net.minecraftforge:forge:${properties["mcVersion"]}-${properties["forgeVersion"]}")
|
"minecraft"("net.minecraftforge:forge:${properties["mcVersion"]}-${properties["forgeVersion"]}")
|
||||||
|
|
||||||
"api"(fg.deobf("curse.maven:clothconfig-348521:2938583"))
|
"api"(fg.deobf("curse.maven:clothconfig-348521:2938583"))
|
||||||
|
|
||||||
|
"implementation"(fg.deobf("curse.maven:biomesoplenty-220318:2988999"))
|
||||||
"implementation"("kottle:Kottle:${properties["kottleVersion"]}")
|
"implementation"("kottle:Kottle:${properties["kottleVersion"]}")
|
||||||
// "implementation"("org.spongepowered:mixin:0.8-SNAPSHOT")
|
// "implementation"("org.spongepowered:mixin:0.8-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package mods.betterfoliage.mixin;
|
package mods.betterfoliage.mixin;
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod;
|
import mods.betterfoliage.BetterFoliageMod;
|
||||||
import mods.betterfoliage.ModelDefinitionsLoadedEvent;
|
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager;
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent;
|
||||||
import net.minecraft.client.renderer.model.*;
|
import net.minecraft.client.renderer.model.*;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
import net.minecraft.profiler.IProfiler;
|
import net.minecraft.profiler.IProfiler;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ 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.LeafWindTracker
|
|
||||||
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.StandardDirtKey
|
||||||
import mods.betterfoliage.render.block.vanilla.StandardDirtModel
|
import mods.betterfoliage.render.block.vanilla.StandardDirtModel
|
||||||
@@ -17,9 +16,11 @@ import mods.betterfoliage.render.block.vanilla.StandardMyceliumModel
|
|||||||
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.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.generated.GeneratedTexturePack
|
import mods.betterfoliage.resource.generated.GeneratedTexturePack
|
||||||
|
import mods.betterfoliage.texture.LeafParticleRegistry
|
||||||
import net.minecraft.block.BlockState
|
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
|
||||||
@@ -39,6 +40,7 @@ object Client {
|
|||||||
fun init() {
|
fun init() {
|
||||||
// discoverers
|
// discoverers
|
||||||
BetterFoliageMod.bus.register(BakeWrapperManager)
|
BetterFoliageMod.bus.register(BakeWrapperManager)
|
||||||
|
BetterFoliageMod.bus.register(LeafParticleRegistry)
|
||||||
listOf(
|
listOf(
|
||||||
StandardLeafDiscovery,
|
StandardLeafDiscovery,
|
||||||
StandardGrassDiscovery,
|
StandardGrassDiscovery,
|
||||||
|
|||||||
@@ -6,6 +6,3 @@ import net.minecraft.resources.IResourceManager
|
|||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.eventbus.api.Event
|
import net.minecraftforge.eventbus.api.Event
|
||||||
|
|
||||||
data class ModelDefinitionsLoadedEvent(
|
|
||||||
val bakery: ModelBakery
|
|
||||||
) : Event()
|
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
@file:JvmName("Hooks")
|
@file:JvmName("Hooks")
|
||||||
package mods.betterfoliage
|
package mods.betterfoliage
|
||||||
|
|
||||||
|
import mods.betterfoliage.config.Config
|
||||||
|
import mods.betterfoliage.model.getActualRenderModel
|
||||||
|
import mods.betterfoliage.render.particle.FallingLeafParticle
|
||||||
|
import mods.betterfoliage.texture.LeafBlockModel
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
|
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.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.world.IBlockReader
|
import net.minecraft.world.IBlockReader
|
||||||
@@ -33,14 +39,17 @@ fun onRandomDisplayTick(block: Block, state: BlockState, world: World, pos: Bloc
|
|||||||
// Math.random() < Config.risingSoul.chance) {
|
// Math.random() < Config.risingSoul.chance) {
|
||||||
// EntityRisingSoulFX(world, pos).addIfValid()
|
// EntityRisingSoulFX(world, pos).addIfValid()
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// if (Config.enabled &&
|
if (Config.enabled &&
|
||||||
// Config.fallingLeaves.enabled &&
|
Config.fallingLeaves.enabled &&
|
||||||
// BlockConfig.leafBlocks.matchesClass(state.block) &&
|
random.nextDouble() < Config.fallingLeaves.chance &&
|
||||||
// world.isAirBlock(pos + down1) &&
|
world.isAirBlock(pos.offset(DOWN))
|
||||||
// Math.random() < Config.fallingLeaves.chance) {
|
) {
|
||||||
// EntityFallingLeavesFX(world, pos).addIfValid()
|
(getActualRenderModel(world, pos, state, random) as? LeafBlockModel)?.let { leafModel ->
|
||||||
// }
|
val blockColor = Minecraft.getInstance().blockColors.getColor(state, world, pos, 0)
|
||||||
|
FallingLeafParticle(world, pos, leafModel.key, blockColor, random).addIfValid()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
fun getVoxelShapeOverride(state: BlockState, reader: IBlockReader, pos: BlockPos, dir: Direction): VoxelShape {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ object Config : DelegatingConfig(BetterFoliageMod.MOD_ID, BetterFoliageMod.MOD_I
|
|||||||
val size by double(min=0.75, max=2.5, default=1.4).lang("size")
|
val size by double(min=0.75, max=2.5, default=1.4).lang("size")
|
||||||
val dense by boolean(false)
|
val dense by boolean(false)
|
||||||
val hideInternal by boolean(true)
|
val hideInternal by boolean(true)
|
||||||
|
val saturationThreshold by double(default=0.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
object shortGrass : PopulationConfigCategory(){
|
object shortGrass : PopulationConfigCategory(){
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package mods.betterfoliage.model
|
|||||||
|
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.util.HasLogger
|
import mods.betterfoliage.util.HasLogger
|
||||||
|
import net.minecraft.block.BlockState
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.renderer.model.IBakedModel
|
import net.minecraft.client.renderer.model.IBakedModel
|
||||||
import net.minecraft.client.renderer.model.Material
|
import net.minecraft.client.renderer.model.Material
|
||||||
import net.minecraft.client.renderer.model.ModelBakery
|
import net.minecraft.client.renderer.model.ModelBakery
|
||||||
@@ -9,6 +11,8 @@ import net.minecraft.client.renderer.model.VariantList
|
|||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraft.util.WeightedRandom
|
import net.minecraft.util.WeightedRandom
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.world.ILightReader
|
||||||
import org.apache.logging.log4j.Level.WARN
|
import org.apache.logging.log4j.Level.WARN
|
||||||
import java.util.Random
|
import java.util.Random
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
@@ -62,3 +66,12 @@ class SpecialRenderVariantList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getActualRenderModel(world: ILightReader, pos: BlockPos, state: BlockState, random: Random): SpecialRenderModel? {
|
||||||
|
val model = Minecraft.getInstance().blockRendererDispatcher.blockModelShapes.getModel(state) as? SpecialRenderModel ?: return null
|
||||||
|
if (model is SpecialRenderVariantList) {
|
||||||
|
random.setSeed(state.getPositionRandom(pos))
|
||||||
|
return model.getModel(random).model
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package mods.betterfoliage.model
|
package mods.betterfoliage.model
|
||||||
|
|
||||||
import mods.betterfoliage.render.block.vanilla.getColorOverride
|
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.Double3
|
import mods.betterfoliage.util.Double3
|
||||||
import mods.betterfoliage.util.PI2
|
import mods.betterfoliage.util.PI2
|
||||||
@@ -63,22 +62,17 @@ fun tuftModelSet(shapes: Array<TuftShapeKey>, overrideColor: Color?, spriteGette
|
|||||||
|
|
||||||
fun fullCubeTextured(
|
fun fullCubeTextured(
|
||||||
spriteLocation: ResourceLocation,
|
spriteLocation: ResourceLocation,
|
||||||
overrideColor: Color?,
|
tintIndex: Int,
|
||||||
scrambleUV: Boolean = true
|
scrambleUV: Boolean = true
|
||||||
): List<HalfBakedQuad> {
|
): List<HalfBakedQuad> {
|
||||||
val sprite = Atlas.BLOCKS[spriteLocation]
|
val sprite = Atlas.BLOCKS[spriteLocation]
|
||||||
return allDirections.map { Quad.faceQuad(it) }
|
return allDirections.map { Quad.faceQuad(it) }
|
||||||
.map { if (!scrambleUV) it else it.rotateUV(randomI(max = 4)) }
|
.map { if (!scrambleUV) it else it.rotateUV(randomI(max = 4)) }
|
||||||
.map { it.sprite(sprite) }
|
.map { it.sprite(sprite) }
|
||||||
.map { it.colorAndIndex(overrideColor) }
|
.map { it.colorIndex(tintIndex) }
|
||||||
.bake(true)
|
.bake(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fullCubeTinted(spriteLocation: ResourceLocation, threshold: Double, scrambleUV: Boolean = true): List<HalfBakedQuad> {
|
|
||||||
val overrideColor = Atlas.BLOCKS[spriteLocation].getColorOverride(threshold)
|
|
||||||
return fullCubeTextured(spriteLocation, overrideColor, scrambleUV)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Array<List<Quad>> {
|
fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Array<List<Quad>> {
|
||||||
return Array(num) { idx ->
|
return Array(num) { idx ->
|
||||||
listOf(
|
listOf(
|
||||||
@@ -91,33 +85,22 @@ fun crossModelsRaw(num: Int, size: Double, hOffset: Double, vOffset: Double): Ar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun crossModelSingle(base: List<Quad>, sprite: TextureAtlasSprite, overrideColor: Color?, scrambleUV: Boolean) =
|
fun crossModelSingle(base: List<Quad>, sprite: TextureAtlasSprite, tintIndex: Int,scrambleUV: Boolean) =
|
||||||
base.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
base.map { if (scrambleUV) it.scrambleUV(random, canFlipU = true, canFlipV = true, canRotate = true) else it }
|
||||||
.map { it.colorAndIndex(overrideColor) }
|
.map { it.colorIndex(tintIndex) }
|
||||||
.mapIndexed { idx, quad -> quad.sprite(sprite) }
|
.mapIndexed { idx, quad -> quad.sprite(sprite) }
|
||||||
.withOpposites()
|
.withOpposites()
|
||||||
.bake(false)
|
.bake(false)
|
||||||
|
|
||||||
fun crossModelsTextured(
|
fun crossModelsTextured(
|
||||||
leafBase: Array<List<Quad>>,
|
leafBase: Array<List<Quad>>,
|
||||||
overrideColor: Color?,
|
tintIndex: Int,
|
||||||
scrambleUV: Boolean,
|
scrambleUV: Boolean,
|
||||||
spriteGetter: (Int) -> ResourceLocation
|
spriteGetter: (Int) -> ResourceLocation
|
||||||
) = leafBase.mapIndexed { idx, leaf ->
|
) = leafBase.mapIndexed { idx, leaf ->
|
||||||
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], overrideColor, scrambleUV)
|
crossModelSingle(leaf, Atlas.BLOCKS[spriteGetter(idx)], tintIndex, scrambleUV)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
fun crossModelsTinted(
|
|
||||||
leafBase: Array<List<Quad>>,
|
|
||||||
threshold: Double,
|
|
||||||
scrambleUV: Boolean = true,
|
|
||||||
spriteGetter: (Int) -> ResourceLocation
|
|
||||||
) = leafBase.mapIndexed { idx, leaf ->
|
|
||||||
val sprite = Atlas.BLOCKS[spriteGetter(idx)]
|
|
||||||
val overrideColor = sprite.getColorOverride(threshold)
|
|
||||||
crossModelSingle(leaf, sprite, overrideColor, scrambleUV)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun List<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
fun List<Quad>.withOpposites() = flatMap { listOf(it, it.flipped) }
|
||||||
fun List<List<Quad>>.buildTufts(applyDiffuseLighting: Boolean = false) =
|
fun List<List<Quad>>.buildTufts(applyDiffuseLighting: Boolean = false) =
|
||||||
map { it.withOpposites().bake(applyDiffuseLighting) }.toTypedArray()
|
map { it.withOpposites().bake(applyDiffuseLighting) }.toTypedArray()
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
package mods.betterfoliage.render
|
package mods.betterfoliage.render
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.IVertexBuilder
|
||||||
|
import mods.betterfoliage.BetterFoliage
|
||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.render.old.AbstractEntityFX
|
import mods.betterfoliage.render.old.AbstractEntityFX
|
||||||
import mods.betterfoliage.model.HSB
|
import mods.betterfoliage.model.HSB
|
||||||
|
import mods.betterfoliage.model.getActualRenderModel
|
||||||
|
import mods.betterfoliage.render.particle.AbstractParticle
|
||||||
|
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.get
|
||||||
import mods.betterfoliage.util.minmax
|
import mods.betterfoliage.util.minmax
|
||||||
|
import mods.betterfoliage.util.randomB
|
||||||
import mods.betterfoliage.util.randomD
|
import mods.betterfoliage.util.randomD
|
||||||
|
import mods.betterfoliage.util.randomF
|
||||||
|
import mods.betterfoliage.util.randomI
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.particle.IParticleRenderType
|
||||||
|
import net.minecraft.client.renderer.ActiveRenderInfo
|
||||||
import net.minecraft.client.renderer.BufferBuilder
|
import net.minecraft.client.renderer.BufferBuilder
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import net.minecraft.util.math.MathHelper
|
import net.minecraft.util.math.MathHelper
|
||||||
@@ -23,116 +35,3 @@ import kotlin.math.sin
|
|||||||
|
|
||||||
const val rotationFactor = PI2.toFloat() / 64.0f
|
const val rotationFactor = PI2.toFloat() / 64.0f
|
||||||
|
|
||||||
class EntityFallingLeavesFX(
|
|
||||||
world: World, pos: BlockPos
|
|
||||||
) : AbstractEntityFX(
|
|
||||||
world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble() + 0.5
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic val biomeBrightnessMultiplier = 0.5f
|
|
||||||
}
|
|
||||||
|
|
||||||
var particleRot = rand.nextInt(64)
|
|
||||||
var rotPositive = true
|
|
||||||
val isMirrored = (rand.nextInt() and 1) == 1
|
|
||||||
var wasCollided = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
maxAge = MathHelper.floor(randomD(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
|
|
||||||
motionY = -Config.fallingLeaves.speed
|
|
||||||
|
|
||||||
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
|
|
||||||
|
|
||||||
val state = world.getBlockState(pos)
|
|
||||||
// val leafInfo = LeafRegistry[state, world, pos]
|
|
||||||
val blockColor = Minecraft.getInstance().blockColors.getColor(state, world, pos, 0)
|
|
||||||
// if (leafInfo != null) {
|
|
||||||
// sprite = leafInfo.particleTextures[rand.nextInt(1024)]
|
|
||||||
// calculateParticleColor(leafInfo.averageColor, blockColor)
|
|
||||||
// } else {
|
|
||||||
// sprite = LeafParticleRegistry["default"][rand.nextInt(1024)]
|
|
||||||
// setColor(blockColor)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isValid: Boolean get() = (sprite != null)
|
|
||||||
|
|
||||||
override fun update() {
|
|
||||||
if (rand.nextFloat() > 0.95f) rotPositive = !rotPositive
|
|
||||||
if (age > maxAge - 20) particleAlpha = 0.05f * (maxAge - age)
|
|
||||||
|
|
||||||
if (onGround || wasCollided) {
|
|
||||||
velocity.setTo(0.0, 0.0, 0.0)
|
|
||||||
if (!wasCollided) {
|
|
||||||
age = age.coerceAtLeast(maxAge - 20)
|
|
||||||
wasCollided = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
velocity.setTo(cos[particleRot], 0.0, sin[particleRot]).mul(Config.fallingLeaves.perturb)
|
|
||||||
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(Config.fallingLeaves.speed)
|
|
||||||
particleRot = (particleRot + (if (rotPositive) 1 else -1)) and 63
|
|
||||||
particleAngle = rotationFactor * particleRot.toFloat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun render(worldRenderer: BufferBuilder, partialTickTime: Float) {
|
|
||||||
// if (Config.fallingLeaves.opacityHack) GL11.glDepthMask(true)
|
|
||||||
// renderParticleQuad(worldRenderer, partialTickTime, rotation = particleRot, isMirrored = isMirrored)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun calculateParticleColor(textureAvgColor: Int, blockColor: Int) {
|
|
||||||
val texture = HSB.fromColor(textureAvgColor)
|
|
||||||
val block = HSB.fromColor(blockColor)
|
|
||||||
|
|
||||||
val weightTex = texture.saturation / (texture.saturation + block.saturation)
|
|
||||||
val weightBlock = 1.0f - weightTex
|
|
||||||
|
|
||||||
// avoid circular average for hue for performance reasons
|
|
||||||
// one of the color components should dominate anyway
|
|
||||||
val particle = HSB(
|
|
||||||
weightTex * texture.hue + weightBlock * block.hue,
|
|
||||||
weightTex * texture.saturation + weightBlock * block.saturation,
|
|
||||||
weightTex * texture.brightness + weightBlock * block.brightness * biomeBrightnessMultiplier
|
|
||||||
)
|
|
||||||
setColor(particle.asColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object LeafWindTracker {
|
|
||||||
var random = Random()
|
|
||||||
val target = Double3.zero
|
|
||||||
val current = Double3.zero
|
|
||||||
var nextChange: Long = 0
|
|
||||||
|
|
||||||
init {
|
|
||||||
MinecraftForge.EVENT_BUS.register(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeWind(world: World) {
|
|
||||||
nextChange = world.worldInfo.gameTime + 120 + random.nextInt(80)
|
|
||||||
val direction = PI2 * random.nextDouble()
|
|
||||||
val speed = abs(random.nextGaussian()) * Config.fallingLeaves.windStrength +
|
|
||||||
(if (!world.isRaining) 0.0 else abs(random.nextGaussian()) * Config.fallingLeaves.stormStrength)
|
|
||||||
target.setTo(cos(direction) * speed, 0.0, sin(direction) * speed)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handleWorldTick(event: TickEvent.ClientTickEvent) {
|
|
||||||
if (event.phase == TickEvent.Phase.START) Minecraft.getInstance().world?.let { world ->
|
|
||||||
// change target wind speed
|
|
||||||
if (world.worldInfo.dayTime >= nextChange) changeWind(world)
|
|
||||||
|
|
||||||
// change current wind speed
|
|
||||||
val changeRate = if (world.isRaining) 0.015 else 0.005
|
|
||||||
current.add(
|
|
||||||
(target.x - current.x).minmax(-changeRate, changeRate),
|
|
||||||
0.0,
|
|
||||||
(target.z - current.z).minmax(-changeRate, changeRate)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isRemote) changeWind(event.world.world) }
|
|
||||||
}
|
|
||||||
@@ -6,25 +6,25 @@ import mods.betterfoliage.config.BlockConfig
|
|||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.config.ConfigurableBlockMatcher
|
import mods.betterfoliage.config.ConfigurableBlockMatcher
|
||||||
import mods.betterfoliage.config.ModelTextureList
|
import mods.betterfoliage.config.ModelTextureList
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.render.lighting.LightingPreferredFace
|
|
||||||
import mods.betterfoliage.model.Color
|
import mods.betterfoliage.model.Color
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
|
import mods.betterfoliage.model.SpriteSetDelegate
|
||||||
|
import mods.betterfoliage.model.buildTufts
|
||||||
|
import mods.betterfoliage.model.fullCubeTextured
|
||||||
|
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.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
|
||||||
import mods.betterfoliage.model.buildTufts
|
|
||||||
import mods.betterfoliage.model.fullCubeTextured
|
|
||||||
import mods.betterfoliage.model.fullCubeTinted
|
|
||||||
import mods.betterfoliage.model.tuftModelSet
|
|
||||||
import mods.betterfoliage.model.tuftShapeSet
|
|
||||||
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
|
||||||
|
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
|
||||||
@@ -44,7 +44,7 @@ object StandardGrassDiscovery : ConfigurableModelDiscovery() {
|
|||||||
sprites: MutableSet<ResourceLocation>,
|
sprites: MutableSet<ResourceLocation>,
|
||||||
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
replacements: MutableMap<ResourceLocation, ModelBakingKey>
|
||||||
): Boolean {
|
): Boolean {
|
||||||
replacements[location] = StandardGrassKey(textureMatch[0])
|
replacements[location] = StandardGrassKey(textureMatch[0], null)
|
||||||
Client.blockTypes.grass.add(state)
|
Client.blockTypes.grass.add(state)
|
||||||
// RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutout())
|
// RenderTypeLookup.setRenderLayer(state.block, RenderType.getCutout())
|
||||||
return true
|
return true
|
||||||
@@ -52,11 +52,17 @@ object StandardGrassDiscovery : ConfigurableModelDiscovery() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class StandardGrassKey(
|
data class StandardGrassKey(
|
||||||
val grassLocation: ResourceLocation
|
val grassLocation: ResourceLocation,
|
||||||
|
val overrideColor: Color?
|
||||||
) : HalfBakedWrapperKey() {
|
) : HalfBakedWrapperKey() {
|
||||||
|
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||||
|
|
||||||
override fun replace(wrapped: SpecialRenderModel): SpecialRenderModel {
|
override fun replace(wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||||
Atlas.BLOCKS[grassLocation].logColorOverride(detailLogger, Config.shortGrass.saturationThreshold)
|
val grassSpriteColor = Atlas.BLOCKS[grassLocation].averageColor.let { hsb ->
|
||||||
return StandardGrassModel(wrapped, this)
|
logColorOverride(BetterFoliageMod.detailLogger(this), Config.shortGrass.saturationThreshold, hsb)
|
||||||
|
hsb.colorOverride(Config.shortGrass.saturationThreshold)
|
||||||
|
}
|
||||||
|
return StandardGrassModel(wrapped, this.copy(overrideColor = grassSpriteColor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,17 +107,16 @@ class StandardGrassModel(
|
|||||||
Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
Config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
|
||||||
}
|
}
|
||||||
val grassTuftMeshesNormal = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
val grassTuftMeshesNormal = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
||||||
val overrideColor = Atlas.BLOCKS[key.grassLocation].getColorOverride(Config.shortGrass.saturationThreshold)
|
tuftModelSet(grassTuftShapes, key.overrideColor) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
||||||
tuftModelSet(grassTuftShapes, overrideColor) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
|
||||||
}
|
}
|
||||||
val grassTuftMeshesSnowed = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
val grassTuftMeshesSnowed = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
||||||
tuftModelSet(grassTuftShapes, Color.white) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
tuftModelSet(grassTuftShapes, Color.white) { idx -> grassTuftSprites[randomI()] }.buildTufts()
|
||||||
}
|
}
|
||||||
val grassFullBlockMeshes = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
val grassFullBlockMeshes = LazyMapInvalidatable(BakeWrapperManager) { key: StandardGrassKey ->
|
||||||
Array(64) { fullCubeTinted(key.grassLocation, Config.shortGrass.saturationThreshold) }
|
Array(64) { fullCubeTextured(key.grassLocation, key.tintIndex) }
|
||||||
}
|
}
|
||||||
val snowFullBlockMeshes by LazyInvalidatable(BakeWrapperManager) {
|
val snowFullBlockMeshes by LazyInvalidatable(BakeWrapperManager) {
|
||||||
Array(64) { fullCubeTextured(ResourceLocation("block/snow"), Color.white) }
|
Array(64) { fullCubeTextured(ResourceLocation("block/snow"), -1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,30 +6,32 @@ import mods.betterfoliage.config.BlockConfig
|
|||||||
import mods.betterfoliage.config.Config
|
import mods.betterfoliage.config.Config
|
||||||
import mods.betterfoliage.config.ConfigurableBlockMatcher
|
import mods.betterfoliage.config.ConfigurableBlockMatcher
|
||||||
import mods.betterfoliage.config.ModelTextureList
|
import mods.betterfoliage.config.ModelTextureList
|
||||||
import mods.betterfoliage.model.SpecialRenderModel
|
|
||||||
import mods.betterfoliage.render.lighting.RoundLeafLighting
|
|
||||||
import mods.betterfoliage.model.Color
|
import mods.betterfoliage.model.Color
|
||||||
|
import mods.betterfoliage.model.HSB
|
||||||
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
import mods.betterfoliage.model.HalfBakedSpecialWrapper
|
||||||
import mods.betterfoliage.model.HalfBakedWrapperKey
|
import mods.betterfoliage.model.HalfBakedWrapperKey
|
||||||
|
import mods.betterfoliage.model.SpecialRenderModel
|
||||||
|
import mods.betterfoliage.model.SpriteSetDelegate
|
||||||
|
import mods.betterfoliage.model.crossModelsRaw
|
||||||
|
import mods.betterfoliage.model.crossModelsTextured
|
||||||
|
import mods.betterfoliage.render.lighting.RoundLeafLighting
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
import mods.betterfoliage.render.pipeline.RenderCtxBase
|
||||||
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
import mods.betterfoliage.render.pipeline.RenderCtxVanilla
|
||||||
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
import mods.betterfoliage.resource.discovery.BakeWrapperManager
|
||||||
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
import mods.betterfoliage.resource.discovery.ConfigurableModelDiscovery
|
||||||
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
import mods.betterfoliage.resource.discovery.ModelBakingKey
|
||||||
import mods.betterfoliage.resource.generated.GeneratedLeaf
|
import mods.betterfoliage.resource.generated.GeneratedLeaf
|
||||||
import mods.betterfoliage.model.SpriteSetDelegate
|
import mods.betterfoliage.texture.LeafBlockModel
|
||||||
import mods.betterfoliage.model.crossModelsRaw
|
import mods.betterfoliage.texture.LeafParticleKey
|
||||||
import mods.betterfoliage.model.crossModelsTextured
|
|
||||||
import mods.betterfoliage.model.crossModelsTinted
|
|
||||||
import mods.betterfoliage.texture.LeafParticleRegistry
|
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.block.BlockState
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|
||||||
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.INFO
|
import org.apache.logging.log4j.Level.INFO
|
||||||
import org.apache.logging.log4j.Logger
|
import org.apache.logging.log4j.Logger
|
||||||
|
|
||||||
@@ -50,13 +52,12 @@ object StandardLeafDiscovery : ConfigurableModelDiscovery() {
|
|||||||
.apply { sprites.add(this) }
|
.apply { sprites.add(this) }
|
||||||
|
|
||||||
detailLogger.log(INFO, " particle $leafType")
|
detailLogger.log(INFO, " particle $leafType")
|
||||||
replacements[location] = StandardLeafKey(generated, leafType)
|
replacements[location] = StandardLeafKey(generated, leafType, null)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TextureAtlasSprite.logColorOverride(logger: Logger, threshold: Double) {
|
fun logColorOverride(logger: Logger, threshold: Double, hsb: HSB) {
|
||||||
val hsb = averageColor
|
|
||||||
return if (hsb.saturation >= threshold) {
|
return if (hsb.saturation >= threshold) {
|
||||||
logger.log(INFO, " brightness ${hsb.brightness}")
|
logger.log(INFO, " brightness ${hsb.brightness}")
|
||||||
logger.log(INFO, " saturation ${hsb.saturation} >= ${threshold}, will use texture color")
|
logger.log(INFO, " saturation ${hsb.saturation} >= ${threshold}, will use texture color")
|
||||||
@@ -65,24 +66,30 @@ fun TextureAtlasSprite.logColorOverride(logger: Logger, threshold: Double) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TextureAtlasSprite.getColorOverride(threshold: Double) = averageColor.let {
|
fun HSB.colorOverride(threshold: Double) =
|
||||||
if (it.saturation < threshold) null else it.copy(brightness = (it.brightness * 2.0f).coerceAtMost(0.9f))
|
if (saturation < threshold) null else copy(brightness = (brightness * 2.0f).coerceAtMost(0.9f)).asColor.let { Color(it) }
|
||||||
}?.asColor?.let { Color(it) }
|
|
||||||
|
|
||||||
data class StandardLeafKey(
|
data class StandardLeafKey(
|
||||||
val roundLeafTexture: ResourceLocation,
|
val roundLeafTexture: ResourceLocation,
|
||||||
val leafType: String
|
override val leafType: String,
|
||||||
) : HalfBakedWrapperKey() {
|
override val overrideColor: Color?
|
||||||
|
) : HalfBakedWrapperKey(), LeafParticleKey {
|
||||||
|
val tintIndex: Int get() = if (overrideColor == null) 0 else -1
|
||||||
|
|
||||||
override fun replace(wrapped: SpecialRenderModel): SpecialRenderModel {
|
override fun replace(wrapped: SpecialRenderModel): SpecialRenderModel {
|
||||||
Atlas.BLOCKS[roundLeafTexture].logColorOverride(BetterFoliageMod.detailLogger(this), 0.1)
|
val leafSpriteColor = Atlas.BLOCKS[roundLeafTexture].averageColor.let { hsb ->
|
||||||
return StandardLeafModel(wrapped, this)
|
logColorOverride(BetterFoliageMod.detailLogger(this), Config.leaves.saturationThreshold, hsb)
|
||||||
|
hsb.colorOverride(Config.leaves.saturationThreshold)
|
||||||
|
}
|
||||||
|
detailLogger.log(DEBUG, "roundLeaf=$roundLeafTexture overrideColor=$leafSpriteColor")
|
||||||
|
return StandardLeafModel(wrapped, this.copy(overrideColor = leafSpriteColor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandardLeafModel(
|
class StandardLeafModel(
|
||||||
model: SpecialRenderModel,
|
model: SpecialRenderModel,
|
||||||
key: StandardLeafKey
|
override val key: StandardLeafKey
|
||||||
) : HalfBakedSpecialWrapper(model) {
|
) : HalfBakedSpecialWrapper(model), LeafBlockModel {
|
||||||
val leafNormal by leafModelsNormal.delegate(key)
|
val leafNormal by leafModelsNormal.delegate(key)
|
||||||
val leafSnowed by leafModelsSnowed.delegate(key)
|
val leafSnowed by leafModelsSnowed.delegate(key)
|
||||||
|
|
||||||
@@ -103,10 +110,10 @@ class StandardLeafModel(
|
|||||||
Config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
|
Config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
|
||||||
}
|
}
|
||||||
val leafModelsNormal = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
val leafModelsNormal = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
||||||
crossModelsTinted(leafModelsBase[key], Config.shortGrass.saturationThreshold) { key.roundLeafTexture }
|
crossModelsTextured(leafModelsBase[key], key.tintIndex, true) { key.roundLeafTexture }
|
||||||
}
|
}
|
||||||
val leafModelsSnowed = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
val leafModelsSnowed = LazyMapInvalidatable(BakeWrapperManager) { key: StandardLeafKey ->
|
||||||
crossModelsTextured(leafModelsBase[key], Color.white, false) { leafSpritesSnowed[it].name }
|
crossModelsTextured(leafModelsBase[key], -1, false) { leafSpritesSnowed[it].name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package mods.betterfoliage.render.particle
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.IVertexBuilder
|
||||||
|
import mods.betterfoliage.util.Double3
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.particle.SpriteTexturedParticle
|
||||||
|
import net.minecraft.client.renderer.ActiveRenderInfo
|
||||||
|
import net.minecraft.client.renderer.Vector3f
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||||
|
import net.minecraft.util.math.MathHelper
|
||||||
|
import net.minecraft.world.World
|
||||||
|
|
||||||
|
abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) : SpriteTexturedParticle(world, x, y, z) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// @JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
|
||||||
|
// @JvmStatic val cos = Array(64) { idx -> Math.cos(PI2 / 64.0 * idx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val billboardRot = Pair(Double3.zero, Double3.zero)
|
||||||
|
val currentPos = Double3.zero
|
||||||
|
val prevPos = Double3.zero
|
||||||
|
val velocity = Double3.zero
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
super.tick()
|
||||||
|
currentPos.setTo(posX, posY, posZ)
|
||||||
|
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
||||||
|
velocity.setTo(motionX, motionY, motionZ)
|
||||||
|
update()
|
||||||
|
posX = currentPos.x; posY = currentPos.y; posZ = currentPos.z;
|
||||||
|
motionX = velocity.x; motionY = velocity.y; motionZ = velocity.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update particle on world tick. */
|
||||||
|
abstract fun update()
|
||||||
|
|
||||||
|
/** True if the particle is renderable. */
|
||||||
|
abstract val isValid: Boolean
|
||||||
|
|
||||||
|
/** Add the particle to the effect renderer if it is valid. */
|
||||||
|
fun addIfValid() { if (isValid) Minecraft.getInstance().particles.addEffect(this) }
|
||||||
|
|
||||||
|
override fun renderParticle(vertexBuilder: IVertexBuilder, camera: ActiveRenderInfo, tickDelta: Float) {
|
||||||
|
super.renderParticle(vertexBuilder, camera, tickDelta)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a particle quad.
|
||||||
|
*
|
||||||
|
* @param[tessellator] the [Tessellator] instance to use
|
||||||
|
* @param[tickDelta] partial tick time
|
||||||
|
* @param[currentPos] render position
|
||||||
|
* @param[prevPos] previous tick position for interpolation
|
||||||
|
* @param[size] particle size
|
||||||
|
* @param[currentAngle] viewpoint-dependent particle rotation (64 steps)
|
||||||
|
* @param[sprite] particle texture
|
||||||
|
* @param[isMirrored] mirror particle texture along V-axis
|
||||||
|
* @param[alpha] aplha blending
|
||||||
|
*/
|
||||||
|
fun renderParticleQuad(vertexConsumer: IVertexBuilder,
|
||||||
|
camera: ActiveRenderInfo,
|
||||||
|
tickDelta: Float,
|
||||||
|
currentPos: Double3 = this.currentPos,
|
||||||
|
prevPos: Double3 = this.prevPos,
|
||||||
|
size: Double = particleScale.toDouble(),
|
||||||
|
currentAngle: Float = this.particleAngle,
|
||||||
|
prevAngle: Float = this.prevParticleAngle,
|
||||||
|
sprite: TextureAtlasSprite = this.sprite,
|
||||||
|
alpha: Float = this.particleAlpha) {
|
||||||
|
|
||||||
|
val center = Double3.lerp(tickDelta.toDouble(), prevPos, currentPos)
|
||||||
|
val angle = MathHelper.lerp(tickDelta, prevAngle, currentAngle)
|
||||||
|
val rotation = camera.rotation.copy().apply { multiply(Vector3f.ZP.rotation(angle)) }
|
||||||
|
val lightmapCoord = getBrightnessForRender(tickDelta)
|
||||||
|
|
||||||
|
val coords = arrayOf(
|
||||||
|
Double3(-1.0, -1.0, 0.0),
|
||||||
|
Double3(-1.0, 1.0, 0.0),
|
||||||
|
Double3(1.0, 1.0, 0.0),
|
||||||
|
Double3(1.0, -1.0, 0.0)
|
||||||
|
).map { it.rotate(rotation).mul(size).add(center).sub(camera.projectedView.x, camera.projectedView.y, camera.projectedView.z) }
|
||||||
|
|
||||||
|
fun renderVertex(vertex: Double3, u: Float, v: Float) = vertexConsumer
|
||||||
|
.pos(vertex.x, vertex.y, vertex.z).tex(u, v)
|
||||||
|
.color(particleRed, particleGreen, particleBlue, alpha).lightmap(lightmapCoord)
|
||||||
|
.endVertex()
|
||||||
|
|
||||||
|
renderVertex(coords[0], sprite.maxU, sprite.maxV)
|
||||||
|
renderVertex(coords[1], sprite.maxU, sprite.minV)
|
||||||
|
renderVertex(coords[2], sprite.minU, sprite.minV)
|
||||||
|
renderVertex(coords[3], sprite.minU, sprite.maxV)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setColor(color: Int) {
|
||||||
|
particleBlue = (color and 255) / 256.0f
|
||||||
|
particleGreen = ((color shr 8) and 255) / 256.0f
|
||||||
|
particleRed = ((color shr 16) and 255) / 256.0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package mods.betterfoliage.render.particle
|
||||||
|
|
||||||
|
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.PI2
|
||||||
|
import mods.betterfoliage.util.minmax
|
||||||
|
import mods.betterfoliage.util.randomB
|
||||||
|
import mods.betterfoliage.util.randomD
|
||||||
|
import mods.betterfoliage.util.randomF
|
||||||
|
import mods.betterfoliage.util.randomI
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.particle.IParticleRenderType
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.util.math.MathHelper
|
||||||
|
import net.minecraft.world.World
|
||||||
|
import net.minecraftforge.common.MinecraftForge
|
||||||
|
import net.minecraftforge.event.TickEvent
|
||||||
|
import net.minecraftforge.event.world.WorldEvent
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
|
import java.util.Random
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
class FallingLeafParticle(
|
||||||
|
world: World, pos: BlockPos, leaf: LeafParticleKey, blockColor: Int, random: Random
|
||||||
|
) : AbstractParticle(
|
||||||
|
world, pos.x.toDouble() + 0.5, pos.y.toDouble(), pos.z.toDouble() + 0.5
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic val biomeBrightnessMultiplier = 0.5f
|
||||||
|
}
|
||||||
|
|
||||||
|
var rotationSpeed = random.randomF(min = PI2 / 80.0, max = PI2 / 50.0)
|
||||||
|
val isMirrored = randomB()
|
||||||
|
var wasCollided = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
particleAngle = random.randomF(max = PI2)
|
||||||
|
prevParticleAngle = particleAngle - rotationSpeed
|
||||||
|
|
||||||
|
maxAge = MathHelper.floor(randomD(0.6, 1.0) * Config.fallingLeaves.lifetime * 20.0)
|
||||||
|
motionY = -Config.fallingLeaves.speed
|
||||||
|
|
||||||
|
particleScale = Config.fallingLeaves.size.toFloat() * 0.1f
|
||||||
|
setColor(leaf.overrideColor?.asInt ?: blockColor)
|
||||||
|
sprite = LeafParticleRegistry[leaf.leafType][randomI(max = 1024)]
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isValid: Boolean get() = (sprite != null)
|
||||||
|
|
||||||
|
|
||||||
|
override fun update() {
|
||||||
|
if (rand.nextFloat() > 0.95f) rotationSpeed *= -1.0f
|
||||||
|
if (age > maxAge - 20) particleAlpha = 0.05f * (maxAge - age)
|
||||||
|
|
||||||
|
if (onGround || wasCollided) {
|
||||||
|
velocity.setTo(0.0, 0.0, 0.0)
|
||||||
|
if (!wasCollided) {
|
||||||
|
age = age.coerceAtLeast(maxAge - 20)
|
||||||
|
wasCollided = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val cosRotation = cos(particleAngle).toDouble(); val sinRotation = sin(particleAngle).toDouble()
|
||||||
|
velocity.setTo(cosRotation, 0.0, sinRotation).mul(Config.fallingLeaves.perturb)
|
||||||
|
.add(LeafWindTracker.current).add(0.0, -1.0, 0.0).mul(Config.fallingLeaves.speed)
|
||||||
|
prevParticleAngle = particleAngle
|
||||||
|
particleAngle += rotationSpeed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateParticleColor(textureAvgColor: Int, blockColor: Int) {
|
||||||
|
val texture = HSB.fromColor(textureAvgColor)
|
||||||
|
val block = HSB.fromColor(blockColor)
|
||||||
|
|
||||||
|
val weightTex = texture.saturation / (texture.saturation + block.saturation)
|
||||||
|
val weightBlock = 1.0f - weightTex
|
||||||
|
|
||||||
|
// avoid circular average for hue for performance reasons
|
||||||
|
// one of the color components should dominate anyway
|
||||||
|
val particle = HSB(
|
||||||
|
weightTex * texture.hue + weightBlock * block.hue,
|
||||||
|
weightTex * texture.saturation + weightBlock * block.saturation,
|
||||||
|
weightTex * texture.brightness + weightBlock * block.brightness * biomeBrightnessMultiplier
|
||||||
|
)
|
||||||
|
setColor(particle.asColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRenderType(): IParticleRenderType = IParticleRenderType.PARTICLE_SHEET_TRANSLUCENT
|
||||||
|
}
|
||||||
|
|
||||||
|
object LeafWindTracker {
|
||||||
|
var random = Random()
|
||||||
|
val target = Double3.zero
|
||||||
|
val current = Double3.zero
|
||||||
|
var nextChange: Long = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
MinecraftForge.EVENT_BUS.register(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeWind(world: World) {
|
||||||
|
nextChange = world.worldInfo.gameTime + 120 + random.nextInt(80)
|
||||||
|
val direction = PI2 * random.nextDouble()
|
||||||
|
val speed = abs(random.nextGaussian()) * Config.fallingLeaves.windStrength +
|
||||||
|
(if (!world.isRaining) 0.0 else abs(random.nextGaussian()) * Config.fallingLeaves.stormStrength)
|
||||||
|
target.setTo(cos(direction) * speed, 0.0, sin(direction) * speed)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handleWorldTick(event: TickEvent.ClientTickEvent) {
|
||||||
|
if (event.phase == TickEvent.Phase.START) Minecraft.getInstance().world?.let { world ->
|
||||||
|
// change target wind speed
|
||||||
|
if (world.worldInfo.dayTime >= nextChange) changeWind(world)
|
||||||
|
|
||||||
|
// change current wind speed
|
||||||
|
val changeRate = if (world.isRaining) 0.015 else 0.005
|
||||||
|
current.add(
|
||||||
|
(target.x - current.x).minmax(-changeRate, changeRate),
|
||||||
|
0.0,
|
||||||
|
(target.z - current.z).minmax(-changeRate, changeRate)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handleWorldLoad(event: WorldEvent.Load) { if (event.world.isRemote) changeWind(event.world.world) }
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package mods.betterfoliage.resource.discovery
|
package mods.betterfoliage.resource.discovery
|
||||||
|
|
||||||
import mods.betterfoliage.ModelDefinitionsLoadedEvent
|
|
||||||
import mods.betterfoliage.model.SpecialRenderVariantList
|
import mods.betterfoliage.model.SpecialRenderVariantList
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
import mods.betterfoliage.util.HasLogger
|
import mods.betterfoliage.util.HasLogger
|
||||||
@@ -16,12 +15,18 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
|||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import net.minecraftforge.client.event.ModelBakeEvent
|
import net.minecraftforge.client.event.ModelBakeEvent
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
|
import net.minecraftforge.eventbus.api.Event
|
||||||
|
import net.minecraftforge.eventbus.api.EventPriority
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager
|
import net.minecraftforge.fml.loading.progress.StartupMessageManager
|
||||||
import org.apache.logging.log4j.Level.INFO
|
import org.apache.logging.log4j.Level.INFO
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
|
|
||||||
|
data class ModelDefinitionsLoadedEvent(
|
||||||
|
val bakery: ModelBakery
|
||||||
|
) : Event()
|
||||||
|
|
||||||
interface ModelDiscovery {
|
interface ModelDiscovery {
|
||||||
fun onModelsLoaded(
|
fun onModelsLoaded(
|
||||||
bakery: ModelBakery,
|
bakery: ModelBakery,
|
||||||
@@ -50,7 +55,7 @@ object BakeWrapperManager : Invalidator, HasLogger() {
|
|||||||
private val replacements = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
private val replacements = mutableMapOf<ResourceLocation, ModelBakingKey>()
|
||||||
private val sprites = mutableSetOf<ResourceLocation>()
|
private val sprites = mutableSetOf<ResourceLocation>()
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||||
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||||
modelsValid.invalidate()
|
modelsValid.invalidate()
|
||||||
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
StartupMessageManager.addModMessage("BetterFoliage: discovering models")
|
||||||
|
|||||||
@@ -1,43 +1,70 @@
|
|||||||
package mods.betterfoliage.texture
|
package mods.betterfoliage.texture
|
||||||
|
|
||||||
import mods.betterfoliage.BetterFoliageMod
|
import mods.betterfoliage.BetterFoliageMod
|
||||||
|
import mods.betterfoliage.model.Color
|
||||||
|
import mods.betterfoliage.model.FixedSpriteSet
|
||||||
|
import mods.betterfoliage.model.SpriteSet
|
||||||
|
import mods.betterfoliage.resource.discovery.ModelDefinitionsLoadedEvent
|
||||||
import mods.betterfoliage.util.Atlas
|
import mods.betterfoliage.util.Atlas
|
||||||
|
import mods.betterfoliage.util.HasLogger
|
||||||
import mods.betterfoliage.util.get
|
import mods.betterfoliage.util.get
|
||||||
import mods.betterfoliage.util.getLines
|
import mods.betterfoliage.util.getLines
|
||||||
import mods.betterfoliage.util.resourceManager
|
import mods.betterfoliage.util.resourceManager
|
||||||
import mods.betterfoliage.util.stripStart
|
import mods.betterfoliage.util.stripStart
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
import net.minecraft.client.renderer.texture.MissingTextureSprite
|
||||||
import net.minecraft.util.ResourceLocation
|
import net.minecraft.util.ResourceLocation
|
||||||
import java.util.concurrent.CompletableFuture
|
import net.minecraftforge.client.event.TextureStitchEvent
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||||
|
import org.apache.logging.log4j.Level
|
||||||
|
|
||||||
object LeafParticleRegistry {
|
interface LeafBlockModel {
|
||||||
val targetAtlas = Atlas.PARTICLES
|
val key: LeafParticleKey
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LeafParticleKey {
|
||||||
|
val leafType: String
|
||||||
|
val overrideColor: Color?
|
||||||
|
}
|
||||||
|
|
||||||
|
object LeafParticleRegistry : HasLogger() {
|
||||||
val typeMappings = TextureMatcher()
|
val typeMappings = TextureMatcher()
|
||||||
// val particles = hashMapOf<String, SpriteSet>()
|
val particles = hashMapOf<String, SpriteSet>()
|
||||||
|
|
||||||
val futures = mutableMapOf<String, List<CompletableFuture<TextureAtlasSprite>>>()
|
operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
||||||
|
|
||||||
// operator fun get(type: String) = particles[type] ?: particles["default"]!!
|
@SubscribeEvent
|
||||||
|
fun handleModelLoad(event: ModelDefinitionsLoadedEvent) {
|
||||||
fun discovery() {
|
|
||||||
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
|
typeMappings.loadMappings(ResourceLocation(BetterFoliageMod.MOD_ID, "leaf_texture_mappings.cfg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
fun handlePreStitch(event: TextureStitchEvent.Pre) {
|
||||||
|
if (event.map.textureLocation == Atlas.PARTICLES.resourceId) {
|
||||||
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
||||||
val ids = (0 until 16).map { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") }
|
val locations = (0 until 16).map { idx ->
|
||||||
val wids = ids.map { Atlas.PARTICLES.file(it) }
|
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/falling_leaf_${leafType}_$idx")
|
||||||
// futures[leafType] = (0 until 16).map { idx -> ResourceLocation(BetterFoliageMod.MOD_ID, "falling_leaf_${leafType}_$idx") }
|
}.filter { resourceManager.hasResource(Atlas.PARTICLES.file(it)) }
|
||||||
// .filter { manager.hasResource(Atlas.PARTICLES.wrap(it)) }
|
|
||||||
// .map { atlasFuture.sprite(it) }
|
detailLogger.log(Level.INFO, "Registering sprites for leaf particle type [$leafType], ${locations.size} sprites found")
|
||||||
|
locations.forEach { event.addSprite(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanup() {
|
@SubscribeEvent
|
||||||
// futures.forEach { leafType, spriteFutures ->
|
fun handlePostStitch(event: TextureStitchEvent.Post) {
|
||||||
// val sprites = spriteFutures.filter { !it.isCompletedExceptionally }.map { it.get() }
|
if (event.map.textureLocation == Atlas.PARTICLES.resourceId) {
|
||||||
// if (sprites.isNotEmpty()) particles[leafType] = FixedSpriteSet(sprites)
|
(typeMappings.mappings.map { it.type } + "default").distinct().forEach { leafType ->
|
||||||
// }
|
val sprites = (0 until 16).map { idx ->
|
||||||
// if (particles["default"] == null) particles["default"] = FixedSpriteSet(listOf(atlasFuture.missing.get()!!))
|
ResourceLocation(BetterFoliageMod.MOD_ID, "particle/falling_leaf_${leafType}_$idx")
|
||||||
|
}
|
||||||
|
.map { event.map.getSprite(it) }
|
||||||
|
.filter { it !is MissingTextureSprite }
|
||||||
|
detailLogger.log(Level.INFO, "Leaf particle type [$leafType], ${sprites.size} sprites in atlas")
|
||||||
|
particles[leafType] = FixedSpriteSet(sprites)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextureMatcher {
|
class TextureMatcher {
|
||||||
@@ -61,7 +88,13 @@ class TextureMatcher {
|
|||||||
if (line2.size == 2) {
|
if (line2.size == 2) {
|
||||||
val mapping = line2[0].trim().split(':')
|
val mapping = line2[0].trim().split(':')
|
||||||
if (mapping.size == 1) mappings.add(Mapping(null, mapping[0].trim(), line2[1].trim()))
|
if (mapping.size == 1) mappings.add(Mapping(null, mapping[0].trim(), line2[1].trim()))
|
||||||
else if (mapping.size == 2) mappings.add(Mapping(mapping[0].trim(), mapping[1].trim(), line2[1].trim()))
|
else if (mapping.size == 2) mappings.add(
|
||||||
|
Mapping(
|
||||||
|
mapping[0].trim(),
|
||||||
|
mapping[1].trim(),
|
||||||
|
line2[1].trim()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package mods.betterfoliage.util
|
package mods.betterfoliage.util
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.Quaternion
|
||||||
import net.minecraft.util.Direction
|
import net.minecraft.util.Direction
|
||||||
import net.minecraft.util.Direction.*
|
import net.minecraft.util.Direction.*
|
||||||
import net.minecraft.util.Direction.Axis.*
|
import net.minecraft.util.Direction.Axis.*
|
||||||
@@ -66,6 +67,7 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
|||||||
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
|
val zero: Double3 get() = Double3(0.0, 0.0, 0.0)
|
||||||
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
|
fun weight(v1: Double3, weight1: Double, v2: Double3, weight2: Double) =
|
||||||
Double3(v1.x * weight1 + v2.x * weight2, v1.y * weight1 + v2.y * weight2, v1.z * weight1 + v2.z * weight2)
|
Double3(v1.x * weight1 + v2.x * weight2, v1.y * weight1 + v2.y * weight2, v1.z * weight1 + v2.z * weight2)
|
||||||
|
fun lerp(delta: Double, first: Double3, second: Double3) = first + (second - first) * delta
|
||||||
}
|
}
|
||||||
|
|
||||||
// immutable operations
|
// immutable operations
|
||||||
@@ -82,6 +84,13 @@ data class Double3(var x: Double, var y: Double, var z: Double) {
|
|||||||
rot.rotatedComponent(SOUTH, x, y, z)
|
rot.rotatedComponent(SOUTH, x, y, z)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** Rotate vector by the given [Quaternion] */
|
||||||
|
fun rotate(quat: Quaternion) =
|
||||||
|
quat.copy()
|
||||||
|
.apply { multiply(Quaternion(x, y, z, 0.0F)) }
|
||||||
|
.apply { multiply(quat.copy().apply(Quaternion::conjugate)) }
|
||||||
|
.let { Double3(it.x, it.y, it.z) }
|
||||||
|
|
||||||
// mutable operations
|
// mutable operations
|
||||||
fun setTo(other: Double3): Double3 { x = other.x; y = other.y; z = other.z; return this }
|
fun setTo(other: Double3): Double3 { x = other.x; y = other.y; z = other.z; return this }
|
||||||
fun setTo(x: Double, y: Double, z: Double): Double3 { this.x = x; this.y = y; this.z = z; return this }
|
fun setTo(x: Double, y: Double, z: Double): Double3 { this.x = x; this.y = y; this.z = z; return this }
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ val random = Random(System.nanoTime())
|
|||||||
|
|
||||||
fun randomB() = random.nextBoolean()
|
fun randomB() = random.nextBoolean()
|
||||||
fun randomI(min: Int = 0, max: Int = Int.MAX_VALUE) = min + random.nextInt(max - min)
|
fun randomI(min: Int = 0, max: Int = Int.MAX_VALUE) = min + random.nextInt(max - min)
|
||||||
fun randomD(min: Double = 0.0, max: Double = 1.0) = random.nextDouble() * (max - min) + min
|
fun randomF(min: Float = 0.0f, max: Float = 1.0f) = random.randomF(min, max)
|
||||||
|
fun randomD(min: Double = 0.0, max: Double = 1.0) = random.randomD(min, max)
|
||||||
|
|
||||||
|
fun Random.randomF(min: Float = 0.0f, max: Float = 1.0f) = nextFloat() * (max - min) + min
|
||||||
|
fun Random.randomF(min: Double = 0.0, max: Double = 1.0) = randomF(min.toFloat(), max.toFloat())
|
||||||
|
fun Random.randomD(min: Double = 0.0, max: Double = 1.0) = nextDouble() * (max - min) + min
|
||||||
|
|
||||||
fun semiRandom(x: Int, y: Int, z: Int, seed: Int): Int {
|
fun semiRandom(x: Int, y: Int, z: Int, seed: Int): Int {
|
||||||
var value = (x * x + y * y + z * z + x * y + y * z + z * x + (seed * seed))
|
var value = (x * x + y * y + z * z + x * y + y * z + z * x + (seed * seed))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
minecraft:block/leaves,all
|
minecraft:block/leaves,all
|
||||||
minecraft:block/cube_all,all
|
minecraft:block/cube_all,all
|
||||||
|
|
||||||
biomesoplenty:block/leaves_overlay,under
|
biomesoplenty:block/leaves_overlay,leaves
|
||||||
|
|||||||
Reference in New Issue
Block a user