made particles compatible with updated param data. Though not the new ones. STILL FILLED WITH ERRORS

This commit is contained in:
2024-11-28 20:29:37 +01:00
parent 5b43c12c15
commit 6728445280
8 changed files with 122 additions and 92 deletions
@@ -42,9 +42,11 @@ import java.util.*
// velocity based, relative coordinate based, or rotation based // velocity based, relative coordinate based, or rotation based
// localOffsetCurve (curve) // localOffsetCurve (curve)
// spawnOffset (Vec3f) // spawnOffset (Vec3f)
// originOffset (Vec3f)
// velRot (bool) // velRot (bool)
// rotates the particle to face the velocity // rotates the particle to face the velocity
// spawnChance (Float) // spawnChance (Float)
// shape: spawnOnlyOnEdge (Boolean)
// Move most (if not all) particle params to emitters. // Move most (if not all) particle params to emitters.
// 3D curves (curves for all X Y Z) // 3D curves (curves for all X Y Z)
@@ -108,8 +110,6 @@ class Bde_particles : ModInitializer {
fun init() { fun init() {
addToEmitterRegister("DEFAULT", EmitterParams.DEFAULT) addToEmitterRegister("DEFAULT", EmitterParams.DEFAULT)
addToEmitterRegister("FIRE_GEYSER", EmitterParams.FIRE_GEYSER)
addToEmitterRegister("RING_EXPLOSION", EmitterParams.RING_EXPLOSION)
} }
@@ -156,7 +156,7 @@ fun onRightClick(player: PlayerEntity, world: ServerWorld, hand: Hand) : TypedAc
} }
fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { // TODO: map to new params
val allParticleParamsJSON: MutableList<Map<String, Any?>> = mutableListOf() val allParticleParamsJSON: MutableList<Map<String, Any?>> = mutableListOf()
val particle = params.particle val particle = params.particle
@@ -20,6 +20,9 @@ import java.awt.Color
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
// TODO: completely redesign
// CLOSE THIS FILE IMMEDIATELY, YOU ARE ENTERING EGYPTIAN (pyramid) AND ITALIAN (spaghetti) TERRITORY // CLOSE THIS FILE IMMEDIATELY, YOU ARE ENTERING EGYPTIAN (pyramid) AND ITALIAN (spaghetti) TERRITORY
val curves = arrayOf("LINEAR", "SQRT", "EXPONENT", "CUBIC", "SINE", "COSINE", "INVERSE", "LOG", "EXP", "BOUNCE") val curves = arrayOf("LINEAR", "SQRT", "EXPONENT", "CUBIC", "SINE", "COSINE", "INVERSE", "LOG", "EXP", "BOUNCE")
@@ -23,6 +23,9 @@ import java.awt.Color
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
// TODO: completely redesign
// ManageEmitters // ManageEmitters
// <create / remove / config / list> // <create / remove / config / list>
// create // create
@@ -27,7 +27,7 @@ import org.joml.Vector3f
* @param minVel The minimum speed a particle must maintain. Particles slower than this value are stopped. Values <= 0 disable this check. * @param minVel The minimum speed a particle must maintain. Particles slower than this value are stopped. Values <= 0 disable this check.
* @param lifeTime Specifies the range for particle lifetime, in ticks. Values below 1 prevent particles from spawning. * @param lifeTime Specifies the range for particle lifetime, in ticks. Values below 1 prevent particles from spawning.
* @param rotVelCurve A curve defining how rotational velocity evolves during the particle's lifetime. Multiplies the `rotVel` values. * @param rotVelCurve A curve defining how rotational velocity evolves during the particle's lifetime. Multiplies the `rotVel` values.
* @param sizeCurve A curve defining how the particle's size evolves over its lifetime. Multiplies the `initScale` values. * @param scaleCurve A curve defining how the particle's size evolves over its lifetime. Multiplies the `initScale` values.
* @param blockCurve Specifies an array of block names that the particle transitions through during its lifetime, combined with a curve to control transitions. Defaults to "minecraft:air" if empty. * @param blockCurve Specifies an array of block names that the particle transitions through during its lifetime, combined with a curve to control transitions. Defaults to "minecraft:air" if empty.
*/ */
data class EmitterParams ( data class EmitterParams (
@@ -48,9 +48,9 @@ data class EmitterParams (
var constVel: Vector3f = Vector3f(0.0f, -0.01f, 0.0f), var constVel: Vector3f = Vector3f(0.0f, -0.01f, 0.0f),
var drag: Float = 0.075f, var drag: Float = 0.075f,
var minVel: Float = 0.0f, var minVel: Float = 0.0f,
var lifeTime: ParamClasses.PairVec2i = ParamClasses.PairVec2i(0, 0, 1, 1), var lifeTime: ParamClasses.PairInt = ParamClasses.PairInt(25, 40),
var rotVelCurve: ParamClasses.LerpVal = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt), var rotVelCurve: ParamClasses.LerpVal = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
var sizeCurve: ParamClasses.LerpVal = ParamClasses.LerpVal.LerpUniform(1.0f, 0.5f, LerpCurves.Linear), var scaleCurve: ParamClasses.LerpVal = ParamClasses.LerpVal.LerpUniform(1.0f, 0.5f, LerpCurves.Linear),
var blockCurve: Pair<Array<String>, LerpCurves> = Pair(arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"), LerpCurves.Sqrt), var blockCurve: Pair<Array<String>, LerpCurves> = Pair(arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"), LerpCurves.Sqrt),
) )
{ {
@@ -62,6 +62,12 @@ class ParamClasses {
) )
} }
} }
class PairInt (
private val min: Int = 0,
private val max: Int = 1,
) {
fun randomize() : Int { return randomIntBetween(min, max) }
}
sealed class LerpVal { sealed class LerpVal {
class LerpVec3f( class LerpVec3f(
private val fromX: Float = 0.0f, private val fromX: Float = 0.0f,
@@ -106,5 +112,16 @@ class ParamClasses {
return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t) return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t)
} }
} }
fun lerpToVector3f(t: Float) : Vector3f {
when (this) {
is LerpVec3f -> return this.lerp(t)
is MultiLerpVec3f -> return this.lerp(t)
is LerpUniform -> {
val x = this.lerp(t)
return Vector3f(x, x, x)
}
}
}
} }
} }
@@ -3,6 +3,7 @@ package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.* import com.bytedice.bde_particles.*
import net.minecraft.server.world.ServerWorld import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3d
import org.joml.Vector2f
import org.joml.Vector3f import org.joml.Vector3f
import org.joml.Vector4f import org.joml.Vector4f
import kotlin.math.* import kotlin.math.*
@@ -15,14 +16,20 @@ class Particle(private val emitterParams: EmitterParams) {
private val initOffset = Vector3f(-0.5f, -0.5f, -0.5f) private val initOffset = Vector3f(-0.5f, -0.5f, -0.5f)
private var offset = Vector3f(0.0f, 0.0f, 0.0f) private var offset = Vector3f(0.0f, 0.0f, 0.0f)
private var pos = Vec3d(0.0, 0.0, 0.0) private var pos = Vec3d(0.0, 0.0, 0.0)
private var entityRot = Vector2f(0.0f, 0.0f)
private var rot = randomBetweenVector3f(particleParams.rotRandom.first, particleParams.rotRandom.second) private var rot = emitterParams.initRot.randomize()
private val rotVel = randomBetweenVector3f(particleParams.rotVelRandom.first, particleParams.rotVelRandom.second) private val rotVel = emitterParams.rotVel.randomize()
private var vel = randomBetweenVector3f(particleParams.velRandom.first, particleParams.velRandom.second) private var vel = emitterParams.initVel.randomize()
private var scale = randomBetweenVector3f(particleParams.sizeRandom.first, particleParams.sizeRandom.second)
private val scaleAny: Any = when (emitterParams.initScale) {
is ParamClasses.RandScale.Uniform -> { (emitterParams.initScale as ParamClasses.RandScale.Uniform).randomize() }
is ParamClasses.RandScale.NonUniform -> { (emitterParams.initScale as ParamClasses.RandScale.NonUniform).randomize() }
}
private var scale = if (scaleAny is Vector3f) { scaleAny } else { Vector3f(scaleAny as Float, scaleAny, scaleAny) }
private var blockDisplay: DisplayEntity? = null private var blockDisplay: DisplayEntity? = null
private var lifeTime = randomIntBetween(particleParams.lifeTime.first, particleParams.lifeTime.second) private var lifeTime = emitterParams.lifeTime.randomize()
var isDead = false var isDead = false
private var timeAlive = 0 private var timeAlive = 0
@@ -30,17 +37,11 @@ class Particle(private val emitterParams: EmitterParams) {
private var isInit = false private var isInit = false
fun init(pos: Vec3d) { fun init(pos: Vec3d, rot: Vector2f) { // TODO: replace initOffset with originOffset param
this.pos = pos this.pos = pos
this.entityRot = rot
if ( particleParams.uniformSize ) { val shape = emitterParams.shape
val randomUniform = randomFloatBetween(particleParams.sizeRandom.first.x, particleParams.sizeRandom.second.x)
scale.x = randomUniform
scale.y = randomUniform
scale.z = randomUniform
}
val shape = particleParams.shape
val newSpawnPos: Vector3f val newSpawnPos: Vector3f
when (shape) { when (shape) {
@@ -53,11 +54,12 @@ class Particle(private val emitterParams: EmitterParams) {
offset = newSpawnPos offset = newSpawnPos
val (quatRot, newOffset) = calcTransformOffset(rot, initOffset, scale) val (quatRot, newOffset) = calcTransformOffset(this.rot, initOffset, scale)
val properties = DisplayEntityProperties( val properties = DisplayEntityProperties(
pos = this.pos, pos = this.pos,
blockType = particleParams.blockCurve[0], rot = rot,
blockType = emitterParams.blockCurve.first[0], // TODO: curve this
translation = newOffset.add(offset), translation = newOffset.add(offset),
leftRotation = quatRot, leftRotation = quatRot,
scale = scale, scale = scale,
@@ -104,66 +106,6 @@ class Particle(private val emitterParams: EmitterParams) {
private fun calcNewProperties() : DisplayEntityProperties { private fun calcNewProperties() : DisplayEntityProperties {
fun calcBlockCurve(timeAliveClamped: Float) : String {
val newBlockIdx = round(lerp(0.0f, particleParams.blockCurve.lastIndex.toFloat(), timeAliveClamped)).toInt()
return particleParams.blockCurve[newBlockIdx]
}
fun sphereSdfVel(forceField: ForceField) : Vector3f {
if (forceField.shape !is ForceFieldShape.Sphere) { return Vector3f(0.0f, 0.0f, 0.0f) }
val shape = forceField.shape
val sdfVal = sdfSphere(forceField.pos, shape.radius, offset)
val normalizedSdf = normalizeSdf(sdfVal, shape.radius)
val velDir = Vector3f(offset).sub(forceField.pos).normalize()
val velMul = if (sdfVal > 0) { 0.0f }
else { lerp(shape.force.second, shape.force.second, 1 - normalizedSdf) }
return velDir.mul(velMul)
}
fun cubeSdfVel(forceField: ForceField) : Vector3f {
if (forceField.shape !is ForceFieldShape.Cube) { return Vector3f(0.0f, 0.0f, 0.0f) }
val shape = forceField.shape
val sdfVal = sdfCube(forceField.pos, shape.size, offset)
val velDir = if (sdfVal < 0) { shape.forceDir }
else { Vector3f(0.0f, 0.0f, 0.0f) }
return velDir
}
fun calcRot(timeAliveClamped: Float) : Vector3f {
val newRot = Vector3f(rot)
val newVel = Vector3f(rotVel)
val rotCurve = particleParams.rotVelCurve
newVel.mul(interpolateCurve(rotCurve.first, rotCurve.second, timeAliveClamped, rotCurve.third))
newRot.add(newVel)
return newRot
}
fun calcOffset() : Pair<Vector3f, Vector3f> {
var vel = Vector3f(vel)
vel = vel.add(particleParams.gravity)
vel = vel.mul(1.0f - particleParams.drag)
if (abs(vel.x) < particleParams.minVel) { vel.x = 0.0f }
if (abs(vel.y) < particleParams.minVel) { vel.y = 0.0f }
if (abs(vel.z) < particleParams.minVel) { vel.z = 0.0f }
var newOffset = Vector3f(offset)
newOffset = newOffset.add(vel)
return Pair(vel, newOffset)
}
fun calcSize(timeAliveClamped: Float) : Vector3f {
val scale = Vector3f(scale)
val sizeCurve = particleParams.sizeCurve
scale.mul(interpolateCurve(sizeCurve.first, sizeCurve.second, timeAliveClamped, sizeCurve.third))
return scale
}
val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f) val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f)
val newBlock = calcBlockCurve(timeAliveClamped) val newBlock = calcBlockCurve(timeAliveClamped)
@@ -175,7 +117,7 @@ class Particle(private val emitterParams: EmitterParams) {
offset = newOffset offset = newOffset
rot = newRot rot = newRot
for (forceField in particleParams.forceFields) { for (forceField in emitterParams.forceFields) {
if (forceField.shape is ForceFieldShape.Sphere) { vel.add(sphereSdfVel(forceField)) } if (forceField.shape is ForceFieldShape.Sphere) { vel.add(sphereSdfVel(forceField)) }
else if (forceField.shape is ForceFieldShape.Cube) { vel.add(cubeSdfVel(forceField)) } else if (forceField.shape is ForceFieldShape.Cube) { vel.add(cubeSdfVel(forceField)) }
} }
@@ -196,7 +138,72 @@ class Particle(private val emitterParams: EmitterParams) {
} }
private fun calcBlockCurve(t: Float) : String {
val newBlockIdx = round(lerp(0.0f, emitterParams.blockCurve.first.lastIndex.toFloat(), t)).toInt()
return emitterParams.blockCurve.first[newBlockIdx]
}
private fun sphereSdfVel(forceField: ForceField) : Vector3f {
if (forceField.shape !is ForceFieldShape.Sphere) { return Vector3f(0.0f, 0.0f, 0.0f) }
val shape = forceField.shape
val sdfVal = sdfSphere(forceField.pos, shape.radius, offset)
val normalizedSdf = normalizeSdf(sdfVal, shape.radius)
val velDir = Vector3f(offset).sub(forceField.pos).normalize()
val velMul = if (sdfVal > 0) { 0.0f }
else { lerp(shape.force.second, shape.force.second, 1 - normalizedSdf) }
return velDir.mul(velMul)
}
private fun cubeSdfVel(forceField: ForceField) : Vector3f {
if (forceField.shape !is ForceFieldShape.Cube) { return Vector3f(0.0f, 0.0f, 0.0f) }
val shape = forceField.shape
val sdfVal = sdfCube(forceField.pos, shape.size, offset)
val velDir = if (sdfVal < 0) { shape.dir.mul(shape.force) }
else { Vector3f(0.0f, 0.0f, 0.0f) }
return velDir
}
// TODO: cone & cylinder SDF
private fun calcRot(t: Float) : Vector3f {
val newRot = Vector3f(rot)
val newVel = Vector3f(rotVel)
newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t))
newRot.add(newVel)
return newRot
}
private fun calcOffset() : Pair<Vector3f, Vector3f> {
var vel = Vector3f(vel)
vel = vel.add(emitterParams.constVel)
vel = vel.mul(1.0f - emitterParams.drag)
if (abs(vel.x) < emitterParams.minVel) { vel.x = 0.0f }
if (abs(vel.y) < emitterParams.minVel) { vel.y = 0.0f }
if (abs(vel.z) < emitterParams.minVel) { vel.z = 0.0f }
var newOffset = Vector3f(offset)
newOffset = newOffset.add(vel)
return Pair(vel, newOffset)
}
private fun calcSize(t: Float) : Vector3f {
val newScale = Vector3f(scale)
newScale.mul(emitterParams.scaleCurve.lerpToVector3f(t))
return scale
}
fun kill() { fun kill() {
blockDisplay?.kill() if (!isDead) { blockDisplay?.kill() }
} }
} }
@@ -49,7 +49,7 @@ fun updateSingleEmitterParam(id: String, paramName: String, paramValue: Any) : B
} }
fun updateSingleParticleParam(emitterId: String, paramName: String, paramValue: Any) : Boolean { fun updateSingleParticleParam(emitterId: String, paramName: String, paramValue: Any) : Boolean { // TODO: map to new params
val emitter = getEmitterParams(emitterId) ?: return false val emitter = getEmitterParams(emitterId) ?: return false
val particle = emitter.particle val particle = emitter.particle
@@ -62,7 +62,7 @@ fun updateSingleParticleParam(emitterId: String, paramName: String, paramValue:
} }
fun updateSingleEmitterParam(params: EmitterParams, paramName: String, paramValue: Any) : Pair<EmitterParams, Boolean> { fun updateSingleEmitterParam(params: EmitterParams, paramName: String, paramValue: Any) : Pair<EmitterParams, Boolean> { // TODO: map to new params
val value: Any? = when (paramName) { val value: Any? = when (paramName) {
"maxCount" -> paramValue as Int "maxCount" -> paramValue as Int
"spawnsPerTick" -> paramValue as Int "spawnsPerTick" -> paramValue as Int
@@ -89,7 +89,7 @@ fun updateSingleEmitterParam(params: EmitterParams, paramName: String, paramValu
} }
fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramValue: Any) : Pair<ParticleParams, Boolean> { fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramValue: Any) : Pair<ParticleParams, Boolean> { // TODO: map to new params
val value: Any? = when (paramName) { val value: Any? = when (paramName) {
"shape" -> paramValue as SpawningShape? "shape" -> paramValue as SpawningShape?
"blockCurve" -> paramValue as Array<String> "blockCurve" -> paramValue as Array<String>
@@ -18,17 +18,17 @@ sealed class ForceFieldShape {
) : ForceFieldShape() ) : ForceFieldShape()
data class Cube( data class Cube(
val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f),
val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f), val dir: Vector3f = Vector3f(0.0f, 1.0f, 0.0f),
val force: Pair<Float, Float> = Pair(0.0f, 0.02f) val force: Float = 0.02f
) : ForceFieldShape() ) : ForceFieldShape()
data class Cone( data class Cone(
val size: Vector2f = Vector2f(1.0f, 1.0f), val size: Vector2f = Vector2f(1.0f, 1.0f),
val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f), val dir: Vector3f = Vector3f(0.0f, 1.0f, 0.0f),
val force: Pair<Float, Float> = Pair(0.0f, 0.02f) val force: Pair<Float, Float> = Pair(0.0f, 0.02f)
) )
data class Cylinder( data class Cylinder(
val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f),
val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f), val dir: Vector3f = Vector3f(0.0f, 1.0f, 0.0f),
val force: Pair<Float, Float> = Pair(0.0f, 0.02f) val force: Float = 0.02f
) )
} }