From c5cc910be3db6d3e8901e30856b537bb25d7531f Mon Sep 17 00:00:00 2001 From: Byte Dice Date: Sun, 1 Dec 2024 15:13:41 +0100 Subject: [PATCH] added async particle ticking and made new params fully working. --- .../bytedice/bde_particles/Bde_particles.kt | 31 ++++--- .../bytedice/bde_particles/DisplayEntity.kt | 2 +- .../kotlin/com/bytedice/bde_particles/Math.kt | 33 +------- .../bde_particles/particles/EmitterParams.kt | 83 ++++++++++++++++++- .../bde_particles/particles/ParamClasses.kt | 41 ++++++++- .../bde_particles/particles/Particle.kt | 75 ++++++++--------- 6 files changed, 172 insertions(+), 93 deletions(-) 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 bfd0d9b..63ad451 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -36,25 +36,16 @@ import java.util.* // TODO: // Allowing the use of regular Minecraft particles instead of just BDEs. + // not implementing in v1.0. Maybe in v2.0. // Better command auto-completion. (and change names of args) // force field "config" option -// Parameters - // rotWithVel / scaleWithVel (bool) - // rotates/scales the particle to face the velocity - // Custom curve equation command args (parse from strings) // Cylinder and cone force field shape. // Better particle performance. // Emitter groups (multiple emitters for EmitterTool) // Private functions/classes -// Debug tools - // RenderParticleDebug gamerule - // render emitter origin - // render particle origin - // name-tag particle with its data - // render particle velocity direction var ALL_PARTICLE_EMITTERS: Array = emptyArray() @@ -119,6 +110,8 @@ class Bde_particles : ModInitializer { fun init() { addToRegister("DEFAULT", EmitterParams.DEFAULT) + addToRegister("DEBUG", EmitterParams.DEBUG) + addToRegister("STRESS_TEST", EmitterParams.STRESS_TEST) } @@ -139,12 +132,18 @@ fun tick(server: MinecraftServer) { }.awaitAll() } - - for (emitter in ALL_PARTICLE_EMITTERS) { - if (emitter.isDead) { - ALL_PARTICLE_EMITTERS = ALL_PARTICLE_EMITTERS.toMutableList().apply { remove(emitter) }.toTypedArray() - } - else { emitter.tick() } + runBlocking { + ALL_PARTICLE_EMITTERS.map { emitter -> + async { + if (emitter.isDead) { + synchronized(ALL_PARTICLE_EMITTERS) { + ALL_PARTICLE_EMITTERS = ALL_PARTICLE_EMITTERS.toMutableList().apply { remove(emitter) }.toTypedArray() + } + } else { + emitter.tick() + } + } + }.awaitAll() } } diff --git a/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt b/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt index e82ae13..9b4200a 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt @@ -85,7 +85,7 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) { put("right_rotation", rightRotList) }) - put("view_range", NbtCompound().apply { properties.viewRange }) + putFloat("view_range", properties.viewRange) if (properties.name != null) { putString("CustomName", properties.name) diff --git a/src/main/kotlin/com/bytedice/bde_particles/Math.kt b/src/main/kotlin/com/bytedice/bde_particles/Math.kt index 028df3e..51a691e 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Math.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Math.kt @@ -83,19 +83,10 @@ fun randomIntBetween(min: Int, max: Int) : Int { } -fun randomBetweenVector3f(min: Vector3f, max: Vector3f) : Vector3f { - return Vector3f( - randomFloatBetween(min.x, max.x), - randomFloatBetween(min.y, max.y), - randomFloatBetween(min.z, max.z) - ) -} - - fun eulerToQuat(euler: Vector3f): Vector4f { - val roll = euler.x - val pitch = euler.y - val yaw = euler.z + val roll = euler.x * (Math.PI / 180).toFloat() + val pitch = euler.y * (Math.PI / 180).toFloat() + val yaw = euler.z * (Math.PI / 180).toFloat() val cy = cos(yaw * 0.5) val sy = sin(yaw * 0.5) @@ -216,22 +207,4 @@ fun sdfCube(pos: Vector3f, size: Vector3f, objectPos: Vector3f): Float { fun normalizeSdf(sdf: Float, radius: Float): Float { return if (sdf < 0) (sdf + radius) / radius else 0f -} - - -fun velToRot(velocity: Vector3f): Vector3f { - val normalizedVelocity = Vector3f(velocity).normalize() - - val yaw = Math.toDegrees(atan2(normalizedVelocity.z.toDouble(), normalizedVelocity.x.toDouble())).toFloat() - - val pitch = Math.toDegrees( - atan2( - -normalizedVelocity.y.toDouble(), - sqrt( - normalizedVelocity.x.toDouble().pow(2) + normalizedVelocity.z.toDouble().pow(2) - ) - ) - ).toFloat() - - return Vector3f(pitch, yaw, 0f) } \ No newline at end of file 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 18a7dc1..1adcf8a 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt @@ -57,9 +57,88 @@ data class EmitterParams ( rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt), scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Constant), modelCurve = Pair( - arrayOf("shroomlight", "orange_concrete", "orange_stained_glass", "gray_wool", "gray_stained_glass", "light_gray_stained_glass", "light_gray_stained_glass_pane"), + arrayOf( + "shroomlight", + "orange_concrete", + "orange_stained_glass", + "gray_wool", + "gray_stained_glass", + "light_gray_stained_glass" + ), LerpCurves.Sqrt - ), + ) + ) + val DEBUG = EmitterParams( + maxCount = 50, + spawnRate = 10, + spawnChance = 1.0f, + spawnDuration = ParamClasses.Duration.SingleBurst(5), + spawnPosOffset = Vector3f(0.0f, 2.0f, 0.0f), + lifeTime = ParamClasses.PairInt(25, 40), + shape = SpawningShape.Circle(1.0f, true), + offset = ParamClasses.PairVec3f.Uniform(0.0f, 0.0f), + initRot = ParamClasses.PairVec3f.Null, + rotVel = ParamClasses.PairVec3f.Null, + initVel = ParamClasses.PairVec3f.NonUniform(0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f), + initCenterVel = ParamClasses.PairFloat(1.5f, 1.5f), + initScale = ParamClasses.PairVec3f.Uniform(2.0f, 2.0f), + transformWithVel = ParamClasses.TransformWithVel.ScaleAndRot(0.75f), + forceFields = emptyArray(), + constVel = Vector3f(0.0f, -0.05f, 0.0f), + drag = 0.075f, + minVel = 0.0f, + offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, + rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Constant), + scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.0f, LerpCurves.Constant), + modelCurve = Pair( + arrayOf( + "shroomlight", + "orange_concrete", + "orange_stained_glass", + "gray_wool", + "gray_stained_glass", + "light_gray_stained_glass" + ), + LerpCurves.Constant + ) + ) + val STRESS_TEST = EmitterParams( + maxCount = 10000, + spawnRate = 50, + spawnChance = 1.0f, + spawnDuration = ParamClasses.Duration.SingleBurst(100), + spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f), + lifeTime = ParamClasses.PairInt(75, 100), + shape = SpawningShape.Circle(10.0f, true), + offset = ParamClasses.PairVec3f.Uniform(0.0f, 0.0f), + initRot = ParamClasses.PairVec3f.NonUniform(0.0f, 0.0f, 0.0f, 360.0f, 360.0f, 360.0f), + rotVel = ParamClasses.PairVec3f.NonUniform(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f), + initVel = ParamClasses.PairVec3f.NonUniform(0.0f, 4.0f, 0.0f, 0.0f, 8.0f, 0.0f), + initCenterVel = ParamClasses.PairFloat(-1.5f, -1.5f), + initScale = ParamClasses.PairVec3f.Uniform(10.0f, 15.0f), + transformWithVel = ParamClasses.TransformWithVel.None, + forceFields = arrayOf(ForceField( + "MUSHROOM_HEAD", + Vector3f(0.0f, 75.0f, 0.0f), + ForceFieldShape.Sphere(15.0f, Pair(0.1f, 0.2f)) + )), + constVel = Vector3f(0.0f, 0.001f, 0.0f), + drag = 0.075f, + minVel = 0.0f, + offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, + rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Constant), + scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.5f, LerpCurves.Constant), + modelCurve = Pair( + arrayOf( + "shroomlight", + "orange_concrete", + "orange_stained_glass", + "gray_wool", + "gray_stained_glass", + "light_gray_stained_glass" + ), + LerpCurves.Constant + ) ) } } \ No newline at end of file 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 91be621..bb49633 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt @@ -4,6 +4,10 @@ import com.bytedice.bde_particles.LerpCurves import com.bytedice.bde_particles.randomFloatBetween import com.bytedice.bde_particles.randomIntBetween import org.joml.Vector3f +import kotlin.math.abs +import kotlin.math.atan2 +import kotlin.math.pow +import kotlin.math.sqrt class ParamClasses { sealed class PairVec3f { @@ -118,9 +122,38 @@ class ParamClasses { val loopDelay: Int ) : Duration() } - enum class TransformWithVel { - RotOnly, - ScaleAndRot, - None + sealed class TransformWithVel { + data object RotOnly : TransformWithVel() { + fun velToRot(velocity: Vector3f): Vector3f { + val normalizedVelocity = Vector3f(velocity).normalize() + + val yaw = Math.toDegrees(atan2(normalizedVelocity.x.toDouble(), normalizedVelocity.z.toDouble())).toFloat() + + val horizontalMag = sqrt(normalizedVelocity.x.toDouble().pow(2) + normalizedVelocity.z.toDouble().pow(2)) + val pitch = Math.toDegrees(atan2(-normalizedVelocity.y.toDouble(), horizontalMag)).toFloat() + + return Vector3f(pitch, yaw, 0f) + } + } + class ScaleAndRot(private val scaleMul: Float) : TransformWithVel() { + fun velToRot(velocity: Vector3f) : Vector3f { + val normalizedVelocity = Vector3f(velocity).normalize() + + val yaw = Math.toDegrees(atan2(normalizedVelocity.x.toDouble(), normalizedVelocity.z.toDouble())).toFloat() + + val horizontalMag = sqrt(normalizedVelocity.x.toDouble().pow(2) + normalizedVelocity.z.toDouble().pow(2)) + val pitch = Math.toDegrees(atan2(-normalizedVelocity.y.toDouble(), horizontalMag)).toFloat() + + return Vector3f(pitch, yaw, 0f) + } + fun velToScale(velocity: Vector3f): Vector3f { + val velocityMagnitude = velocity.length() + val stretchFactor = velocityMagnitude * scaleMul + 1.0f + val contractFactor = 1.0f / stretchFactor + + return Vector3f(contractFactor, contractFactor, stretchFactor) + } + } + data object None : TransformWithVel() } } \ 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 c3c3096..a0020dd 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt @@ -10,13 +10,6 @@ import org.joml.Vector4f import kotlin.math.* -// TODO: add logic for new params (goes for ParticleEmitter too) -/* Changed or new params (haven't been implemented yet) -rotWithVel: Boolean (NEW) -scaleWithVel: Boolean (NEW) -*/ - - class Particle( private val emitterParams: EmitterParams, private var entityPos: Vec3d, @@ -139,13 +132,30 @@ class Particle( val newModel = lerpArray(emitterParams.modelCurve.first as Array, timeAliveClamped, emitterParams.modelCurve.second) as String val (newVel, newPos) = calcPos() - val newRot = calcRot(timeAliveClamped) - val newScale = calcScale(timeAliveClamped) val newOriginOffset = Vector3f(originOffset.mul(emitterParams.offsetCurve.lerpToVector3f(timeAliveClamped))) + val newScale: Vector3f + val newRot: Vector3f + + when (emitterParams.transformWithVel) { + is ParamClasses.TransformWithVel.RotOnly -> { + newScale = calcScale(timeAliveClamped) + newRot = (emitterParams.transformWithVel as ParamClasses.TransformWithVel.RotOnly).velToRot(vel) + } + is ParamClasses.TransformWithVel.ScaleAndRot -> { + newScale = Vector3f(scale).mul((emitterParams.transformWithVel as ParamClasses.TransformWithVel.ScaleAndRot).velToScale(vel)) + newRot = (emitterParams.transformWithVel as ParamClasses.TransformWithVel.ScaleAndRot).velToRot(vel) + } + else -> { + newScale = calcScale(timeAliveClamped) + newRot = calcRot(timeAliveClamped) + rot = newRot + } + } + vel = newVel pos = newPos - rot = newRot + for (forceField in emitterParams.forceFields) { if (forceField.shape is ForceFieldShape.Sphere) { vel.add(sphereSdfVel(forceField)) } @@ -197,17 +207,10 @@ class Particle( private fun calcRot(t: Float) : Vector3f { val newRot = Vector3f(rot) + val newVel = Vector3f(rotVel) - if (emitterParams.transformWithVel == ParamClasses.TransformWithVel.None) { - val newVel = Vector3f(rotVel) - - newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t)) - return newRot.add(newVel) - } - else { - val rotWithVel = velToRot(vel) - return newRot.add(rotWithVel) - } + newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t)) + return newRot.add(newVel) } @@ -233,9 +236,6 @@ class Particle( private fun tickDebug(world: ServerWorld) { - // spawn particle at particle origin - // show trail pointing toward particle direction - // origin debug val greenDustEffect = DustParticleEffect(Vector3f(0.0f, 1.0f, 0.0f), 2.0f) world.spawnParticles( @@ -251,23 +251,18 @@ class Particle( ) // vel debug - var timesLooped = 0 - repeat(3) { - val redDustEffect = DustParticleEffect(Vector3f(1.0f, 0.0f, 0.0f), 1.0f) - world.spawnParticles( - redDustEffect, - entityPos.x + pos.x + originOffset.x + vel.x * timesLooped, - entityPos.y + pos.y + originOffset.y + vel.y * timesLooped, - entityPos.z + pos.z + originOffset.z + vel.z * timesLooped, - 1, - 0.0, - 0.0, - 0.0, - 0.0 - ) - - timesLooped += 1 - } + val redDustEffect = DustParticleEffect(Vector3f(1.0f, 0.0f, 0.0f), 1.0f) + world.spawnParticles( + redDustEffect, + entityPos.x + pos.x + originOffset.x + vel.x * 10, + entityPos.y + pos.y + originOffset.y + vel.y * 10, + entityPos.z + pos.z + originOffset.z + vel.z * 10, + 1, + 0.0, + 0.0, + 0.0, + 0.0 + ) }