diff --git a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt index 72e9e24..4a628cd 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -42,9 +42,11 @@ import java.util.* // velocity based, relative coordinate based, or rotation based // localOffsetCurve (curve) // spawnOffset (Vec3f) + // originOffset (Vec3f) // velRot (bool) // rotates the particle to face the velocity // spawnChance (Float) + // shape: spawnOnlyOnEdge (Boolean) // Move most (if not all) particle params to emitters. // 3D curves (curves for all X Y Z) @@ -108,8 +110,6 @@ class Bde_particles : ModInitializer { fun init() { 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 { +fun emitterParamsToJson(params: EmitterParams) : Map { // TODO: map to new params val allParticleParamsJSON: MutableList> = mutableListOf() val particle = params.particle diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt index d0be586..517c374 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt @@ -20,6 +20,9 @@ import java.awt.Color import java.util.concurrent.CompletableFuture +// TODO: completely redesign + + // 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") diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt index 4734b11..9e47b21 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt @@ -23,6 +23,9 @@ import java.awt.Color import java.util.concurrent.CompletableFuture +// TODO: completely redesign + + // ManageEmitters // // create diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt index bb18d2c..d96e490 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt @@ -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 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 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. */ data class EmitterParams ( @@ -48,9 +48,9 @@ data class EmitterParams ( var constVel: Vector3f = Vector3f(0.0f, -0.01f, 0.0f), var drag: Float = 0.075f, 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 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, LerpCurves> = Pair(arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"), LerpCurves.Sqrt), ) { diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt index 67d166c..3411e5c 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt @@ -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 { class LerpVec3f( private val fromX: Float = 0.0f, @@ -106,5 +112,16 @@ class ParamClasses { 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) + } + } + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt index 33e0cc2..7352d46 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt @@ -3,6 +3,7 @@ package com.bytedice.bde_particles.particles import com.bytedice.bde_particles.* import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.Vec3d +import org.joml.Vector2f import org.joml.Vector3f import org.joml.Vector4f import kotlin.math.* @@ -15,14 +16,20 @@ class Particle(private val emitterParams: EmitterParams) { private val initOffset = Vector3f(-0.5f, -0.5f, -0.5f) private var offset = Vector3f(0.0f, 0.0f, 0.0f) 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 val rotVel = randomBetweenVector3f(particleParams.rotVelRandom.first, particleParams.rotVelRandom.second) - private var vel = randomBetweenVector3f(particleParams.velRandom.first, particleParams.velRandom.second) - private var scale = randomBetweenVector3f(particleParams.sizeRandom.first, particleParams.sizeRandom.second) + private var rot = emitterParams.initRot.randomize() + private val rotVel = emitterParams.rotVel.randomize() + private var vel = emitterParams.initVel.randomize() + + 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 lifeTime = randomIntBetween(particleParams.lifeTime.first, particleParams.lifeTime.second) + private var lifeTime = emitterParams.lifeTime.randomize() var isDead = false private var timeAlive = 0 @@ -30,17 +37,11 @@ class Particle(private val emitterParams: EmitterParams) { private var isInit = false - fun init(pos: Vec3d) { + fun init(pos: Vec3d, rot: Vector2f) { // TODO: replace initOffset with originOffset param this.pos = pos + this.entityRot = rot - if ( particleParams.uniformSize ) { - 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 shape = emitterParams.shape val newSpawnPos: Vector3f when (shape) { @@ -53,11 +54,12 @@ class Particle(private val emitterParams: EmitterParams) { offset = newSpawnPos - val (quatRot, newOffset) = calcTransformOffset(rot, initOffset, scale) + val (quatRot, newOffset) = calcTransformOffset(this.rot, initOffset, scale) val properties = DisplayEntityProperties( pos = this.pos, - blockType = particleParams.blockCurve[0], + rot = rot, + blockType = emitterParams.blockCurve.first[0], // TODO: curve this translation = newOffset.add(offset), leftRotation = quatRot, scale = scale, @@ -104,66 +106,6 @@ class Particle(private val emitterParams: EmitterParams) { 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 { - 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 newBlock = calcBlockCurve(timeAliveClamped) @@ -175,7 +117,7 @@ class Particle(private val emitterParams: EmitterParams) { offset = newOffset rot = newRot - for (forceField in particleParams.forceFields) { + for (forceField in emitterParams.forceFields) { if (forceField.shape is ForceFieldShape.Sphere) { vel.add(sphereSdfVel(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 { + 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() { - blockDisplay?.kill() + if (!isDead) { blockDisplay?.kill() } } } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt index ebf089a..acdf7d8 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt @@ -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 particle = emitter.particle @@ -62,7 +62,7 @@ fun updateSingleParticleParam(emitterId: String, paramName: String, paramValue: } -fun updateSingleEmitterParam(params: EmitterParams, paramName: String, paramValue: Any) : Pair { +fun updateSingleEmitterParam(params: EmitterParams, paramName: String, paramValue: Any) : Pair { // TODO: map to new params val value: Any? = when (paramName) { "maxCount" -> 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 { +fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramValue: Any) : Pair { // TODO: map to new params val value: Any? = when (paramName) { "shape" -> paramValue as SpawningShape? "blockCurve" -> paramValue as Array diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/Shapes.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/Shapes.kt index 66576d8..1d17adb 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/Shapes.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/Shapes.kt @@ -18,17 +18,17 @@ sealed class ForceFieldShape { ) : ForceFieldShape() data class Cube( val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), - val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f), - val force: Pair = Pair(0.0f, 0.02f) + val dir: Vector3f = Vector3f(0.0f, 1.0f, 0.0f), + val force: Float = 0.02f ) : ForceFieldShape() data class Cone( 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 = Pair(0.0f, 0.02f) ) data class Cylinder( val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), - val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f), - val force: Pair = Pair(0.0f, 0.02f) + val dir: Vector3f = Vector3f(0.0f, 1.0f, 0.0f), + val force: Float = 0.02f ) } \ No newline at end of file