103 lines
4.1 KiB
Kotlin
103 lines
4.1 KiB
Kotlin
package mods.betterfoliage.render.particle
|
|
|
|
import mods.betterfoliage.util.Double3
|
|
import net.minecraft.client.MinecraftClient
|
|
import net.minecraft.client.particle.SpriteBillboardParticle
|
|
import net.minecraft.client.render.Camera
|
|
import net.minecraft.client.render.VertexConsumer
|
|
import net.minecraft.client.texture.Sprite
|
|
import net.minecraft.client.util.math.Vector3f
|
|
import net.minecraft.client.world.ClientWorld
|
|
import net.minecraft.util.math.MathHelper
|
|
import net.minecraft.world.World
|
|
|
|
abstract class AbstractParticle(world: ClientWorld, x: Double, y: Double, z: Double) : SpriteBillboardParticle(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(x, y, z)
|
|
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
|
|
velocity.setTo(velocityX, velocityY, velocityZ)
|
|
update()
|
|
x = currentPos.x; y = currentPos.y; z = currentPos.z;
|
|
velocityX = velocity.x; velocityY = velocity.y; velocityZ = 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) MinecraftClient.getInstance().particleManager.addParticle(this) }
|
|
|
|
override fun buildGeometry(vertexConsumer: VertexConsumer, camera: Camera, tickDelta: Float) {
|
|
renderParticleQuad(vertexConsumer, 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: VertexConsumer,
|
|
camera: Camera,
|
|
tickDelta: Float,
|
|
currentPos: Double3 = this.currentPos,
|
|
prevPos: Double3 = this.prevPos,
|
|
size: Double = scale.toDouble(),
|
|
currentAngle: Float = this.angle,
|
|
prevAngle: Float = this.prevAngle,
|
|
sprite: Sprite = this.sprite,
|
|
alpha: Float = this.colorAlpha) {
|
|
|
|
val center = Double3.lerp(tickDelta.toDouble(), prevPos, currentPos)
|
|
val angle = MathHelper.lerp(tickDelta, prevAngle, currentAngle)
|
|
val rotation = camera.rotation.copy().apply { hamiltonProduct(Vector3f.POSITIVE_Z.getRadialQuaternion(angle)) }
|
|
val lightmapCoord = getColorMultiplier(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.pos.x, camera.pos.y, camera.pos.z) }
|
|
|
|
fun renderVertex(vertex: Double3, u: Float, v: Float) = vertexConsumer
|
|
.vertex(vertex.x, vertex.y, vertex.z).texture(u, v)
|
|
.color(colorRed, colorGreen, colorBlue, alpha).light(lightmapCoord)
|
|
.next()
|
|
|
|
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) {
|
|
colorBlue = (color and 255) / 256.0f
|
|
colorGreen = ((color shr 8) and 255) / 256.0f
|
|
colorRed = ((color shr 16) and 255) / 256.0f
|
|
}
|
|
}
|
|
|