From be26742f6f755a1bf9790ffff402c5da983b19ba Mon Sep 17 00:00:00 2001 From: Byte Dice Date: Mon, 18 Nov 2024 20:25:34 +0100 Subject: [PATCH] i got too carried away. but i added a lot of stuff --- .../bytedice/bde_particles/Bde_particles.kt | 3 +- .../com/bytedice/bde_particles/math/main.kt | 59 ++++++++++++++-- .../bde_particles/particle/ForceFields.kt | 15 ++-- .../bde_particles/particle/Particle.kt | 51 ++++++++++++-- .../bde_particles/particle/ParticleEmitter.kt | 69 ++++++++++++++----- .../particle/ParticleEmitterParams.kt | 32 +++++---- .../bde_particles/particle/ParticleParams.kt | 29 +++++--- .../bytedice/bde_particles/particle/Shape.kt | 5 -- .../bytedice/bde_particles/particle/Shapes.kt | 24 +++++++ 9 files changed, 227 insertions(+), 60 deletions(-) delete mode 100644 src/main/kotlin/com/bytedice/bde_particles/particle/Shape.kt create mode 100644 src/main/kotlin/com/bytedice/bde_particles/particle/Shapes.kt 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 527bed5..452107a 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -20,7 +20,8 @@ import net.minecraft.world.World // TODO: finish the particle ticking -// TODO: save particle emitters in world file (so they don't linger forever) +// TODO: save particle emitters in world file, or kill them on server restart (so they don't linger forever) +// TODO: make custom commands to create particles easier (only temporarily saved) var ALL_PARTICLE_EMITTERS: Array = emptyArray() diff --git a/src/main/kotlin/com/bytedice/bde_particles/math/main.kt b/src/main/kotlin/com/bytedice/bde_particles/math/main.kt index c1c81c3..1e7b5f5 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/math/main.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/math/main.kt @@ -41,6 +41,11 @@ fun randomFloatBetween(min: Float, max: Float) : Float { } +fun randomIntBetween(min: Int, max: Int) : Int { + return Random.nextInt(min, max) +} + + fun randomBetweenVector3f(min: Vector3f, max: Vector3f) : Vector3f { return Vector3f( randomFloatBetween(min.x, max.x), @@ -70,6 +75,7 @@ fun eulerToQuat(euler: Vector3f): Vector4f { return Vector4f(x.toFloat(), y.toFloat(), z.toFloat(), w.toFloat()) } + fun quatToEuler(quat: Vector4f): Vector3f { val x = quat.x val y = quat.y @@ -104,7 +110,7 @@ fun randomInCircle(r: Float) : Vector2f { } -fun randomInSphere(r: Float) : Vec3d { +fun randomInSphere(r: Float) : Vector3f { val randRadius = r * Random.nextDouble().pow(1.0 / 3.0) val phi = Random.nextDouble(0.0, 2 * Math.PI) @@ -116,12 +122,21 @@ fun randomInSphere(r: Float) : Vec3d { val y = randRadius * sin(theta) * sin(phi) val z = randRadius * cos(theta) - return Vec3d(x, y, z) + return Vector3f(x.toFloat(), y.toFloat(), z.toFloat()) } -fun randomInRect(w: Float, h: Float) : Vector2f { - return Vector2f(randomFloatBetween(0.0f, w), randomFloatBetween(0.0f, h)) +fun randomInRect(size: Vector2f) : Vector2f { + return Vector2f(randomFloatBetween(0.0f, size.x), randomFloatBetween(0.0f, size.y)) +} + + +fun randomInCube(size: Vector3f) : Vector3f { + return Vector3f( + randomFloatBetween(0.0f, size.x), + randomFloatBetween(0.0f, size.y), + randomFloatBetween(0.0f, size.z) + ) } @@ -175,4 +190,40 @@ fun interpolateCurve(curve: Array, t: Float): Vector3f { start.y + (end.y - start.y) * segmentT, start.z + (end.z - start.z) * segmentT ) +} + + +fun sdfSphere(point: Vec3d, radius: Double): Double { + val x = point.x + val y = point.y + val z = point.z + + val distanceFromCenter = sqrt(x * x + y * y + z * z) + return distanceFromCenter - radius +} + + +fun sdfCube(point: Vec3d, size: Vec3d): Double { + val px = point.x + val py = point.y + val pz = point.z + + val sx = size.x + val sy = size.y + val sz = size.z + + val halfSize = Triple(sx / 2, sy / 2, sz / 2) + + val absPoint = Triple(abs(px), abs(py), abs(pz)) + + val dx = absPoint.first - halfSize.first + val dy = absPoint.second - halfSize.second + val dz = absPoint.third - halfSize.third + + val outside = Triple(max(dx, 0.0), max(dy, 0.0), max(dz, 0.0)) + val outsideDistance = sqrt(outside.first * outside.first + outside.second * outside.second + outside.third * outside.third) + + val insideDistance = min(max(dx, max(dy, dz)), 0.0) + + return outsideDistance + insideDistance } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particle/ForceFields.kt b/src/main/kotlin/com/bytedice/bde_particles/particle/ForceFields.kt index c5c4842..f129307 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particle/ForceFields.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particle/ForceFields.kt @@ -1,10 +1,11 @@ package com.bytedice.bde_particles.particle data class ForceField ( - val x: Float = 0.0f, - val y: Float = 0.0f, - val z: Float = 0.0f, - val minForce: Float = 0.0f, - val maxForce: Float = 0.0f, - val radius: Float = -1.0f -) \ No newline at end of file + val x: Float = 0.0f, + val y: Float = 0.0f, + val z: Float = 0.0f, + val minForce: Float = 0.0f, + val maxForce: Float = 0.0f, + val shape: ForceFieldShape = ForceFieldShape.Sphere() +) + diff --git a/src/main/kotlin/com/bytedice/bde_particles/particle/Particle.kt b/src/main/kotlin/com/bytedice/bde_particles/particle/Particle.kt index 22382a1..91396b0 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particle/Particle.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particle/Particle.kt @@ -22,6 +22,7 @@ class Particle(private val particleParams: ParticleParams) { private var scale = randomBetweenVector3f(particleParams.sizeRandom.first, particleParams.sizeRandom.second) private var blockDisplay: DisplayEntity? = null + private var lifeTime = randomIntBetween(particleParams.lifeTime.first, particleParams.lifeTime.second) var isDead = false private var timeAlive = 0 @@ -39,12 +40,41 @@ class Particle(private val particleParams: ParticleParams) { scale.z = randomUniform } + val shape = particleParams.shape + val newSpawnPos: Vector3f + + when (shape) { + is SpawningShape.Circle -> { + val posInCircle = randomInCircle(shape.radius) + newSpawnPos = Vector3f(posInCircle.x, 0.0f, posInCircle.y) + } + + is SpawningShape.Sphere -> { + newSpawnPos = randomInSphere(shape.radius) + } + + is SpawningShape.Rect -> { + val posInRect = randomInRect(shape.size) + newSpawnPos = Vector3f(posInRect.x, 0.0f, posInRect.y) + } + + is SpawningShape.Cube -> { + newSpawnPos = randomInCube(shape.size) + } + + null -> { + newSpawnPos = Vector3f(0.0f, 0.0f, 0.0f) + } + } + + offset = newSpawnPos + val (quatRot, newOffset) = calcTransformOffset(rot, initOffset, scale) val properties = DisplayEntityProperties( pos = this.pos, blockType = particleParams.blockCurve[0], - translation = newOffset, + translation = newOffset.add(offset), leftRotation = quatRot, scale = scale, tags = arrayOf("BPS_Particle") @@ -69,7 +99,7 @@ class Particle(private val particleParams: ParticleParams) { blockDisplay?.updateProperties(newProperties) - if (timeAlive > particleParams.lifeTime) { isDead = true; blockDisplay?.kill() } + if (timeAlive > lifeTime) { isDead = true; blockDisplay?.kill() } timeAlive += 1 } @@ -88,7 +118,7 @@ class Particle(private val particleParams: ParticleParams) { } - // TODO: forceFields, shape + // TODO: forceFields private fun calcNewProperties() : DisplayEntityProperties { fun calcBlockCurve(timeAliveClamped: Float) : String { @@ -96,6 +126,14 @@ class Particle(private val particleParams: ParticleParams) { return particleParams.blockCurve[newBlockIdx] } + /*fun calcVel() { + for (forceField in particleParams.forceFields) { + if (forceField.shape::class == ForceFieldShape.Sphere::class) { + + } + } + }*/ + fun calcRot(timeAliveClamped: Float) : Vector3f { val newRot = Vector3f(rot) val newVel = Vector3f(rotVel) @@ -125,7 +163,7 @@ class Particle(private val particleParams: ParticleParams) { return scale } - val timeAliveClamped = (timeAlive.toFloat() / particleParams.lifeTime.toFloat()).coerceIn(0.0f, 1.0f) + val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f) val newBlock = calcBlockCurve(timeAliveClamped) val (newVel, newOffset) = calcOffset() @@ -150,4 +188,9 @@ class Particle(private val particleParams: ParticleParams) { return newProperties } + + + fun kill() { + blockDisplay?.kill() + } } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleEmitter.kt b/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleEmitter.kt index ef49024..93f73da 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleEmitter.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleEmitter.kt @@ -3,10 +3,7 @@ package com.bytedice.bde_particles.particle import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.Vec3d -class ParticleEmitter(emitterPos: Vec3d, emitterWorld: ServerWorld, emitterParams: ParticleEmitterParams) { - private val world = emitterWorld - private val pos = emitterPos - private val params = emitterParams +class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: ServerWorld, private val emitterParams: ParticleEmitterParams) { private var allParticles: Array = emptyArray() @@ -25,14 +22,7 @@ class ParticleEmitter(emitterPos: Vec3d, emitterWorld: ServerWorld, emitterParam if (isCounting) { count() } if (!isCounting && allParticles.isEmpty()) { isDead = true } - if (!loopDelayActive) { - repeat(this.params.spawnsPerTick) { - val particle = Particle(ParticleParams()) - particle.init(this.pos) - particle.spawn(world) - allParticles += particle - } - } + addParticle() for (particle in allParticles) { if (particle.isDead) { allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray() } @@ -41,20 +31,67 @@ class ParticleEmitter(emitterPos: Vec3d, emitterWorld: ServerWorld, emitterParam } + private fun addParticle() { + if (loopDelayActive) { return } + + for (i in emitterParams.particleTypes.indices) { + repeat(this.emitterParams.spawnsPerTick) { + if (allParticles.size < emitterParams.maxCount) { return } + + val particle = Particle(emitterParams.particleTypes[i]) + particle.init(this.emitterPos) + particle.spawn(emitterWorld) + + allParticles += particle + } + } + } + + private fun count() { - if (loopDur >= params.loopDur) { + // this is the only section I have comments + // because im so damn good at cooking spaghetti + + // if max duration is 0 or less, skip counting, its infinite + if (emitterParams.loopDur <= 0) { return } + + // if the duration is greater than max duration, add 1 loopCount + // if the delay is greater than 0 then enable it + if (loopDur > emitterParams.loopDur) { loopCount += 1 loopDur = 0 - loopDelayActive = true - if (!params.loop) { isCounting = false } + if (emitterParams.loopDelay > 0) { loopDelayActive = true } } - else if (loopDelay >= params.loopDelay) { + + // if the delay is greater than max delay, disable the delay + else if (loopDelay > emitterParams.loopDelay) { loopDelay = 0 loopDelayActive = false } + // if loopCount is greater than max loopCount then stop immediately + // only loops once if max loopCount is 0 + // if max loopCount is below 0 then don't do anything, loop infinitely + if (emitterParams.loopCount in 0.. = emptyArray(), - val loop: Boolean = false, - val loopDur: Int = 20, - val loopDelay: Int = 10, - val loopCount: Int = 2, + val maxCount: Int = 25, + val spawnsPerTick: Int = 25, + val particleTypes: Array = emptyArray(), + val loopDur: Int = 25, + val loopDelay: Int = 10, + val loopCount: Int = 3, ) \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleParams.kt b/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleParams.kt index 2127287..dc557f9 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particle/ParticleParams.kt @@ -2,13 +2,26 @@ package com.bytedice.bde_particles.particle import org.joml.Vector3f -// TODO: order these correctly, explain all -// sizeRandom: if uniformSize is true then sizeRandom will be uniform (cubic) and the values are taken from X -// lifeTime: any value below 1 will result in a particle not spawning -// minVel: values 0 or less mean the particle can travel however slow it wants -// gravity: Y being -0.025 is close to regular gravity +/** + * ParticleParams defines the parameters for particle behavior, including shape, size, velocity, and other physics properties. + * + * @param shape The shape of the particle's spawning area, `null` is a single point. + * @param blockCurve An array of block names that gets cycled through during the particle's lifetime. + * @param rotRandom The random ***initial*** rotation range for the particle's rotation in X, Y, and Z axes. + * @param rotVelRandom The random velocity for particle rotation in the X, Y, and Z axes. + * @param rotVelCurve A curve which the rotation velocity gets multiplied by during the particle's lifetime. + * @param sizeRandom The random ***initial*** size range for the particle. If `uniformSize` is true, this will be uniform (cubic). + * @param uniformSize If true, particles will have a uniform (cubic) size. Otherwise, their sizes on different axis can vary. + * @param sizeCurve A curve which the size gets multiplied by during the particle's lifetime. + * @param velRandom The random ***initial*** velocity range for the particle in the X, Y, and Z axes. + * @param forceFields An array of force fields applied to the particle. + * @param gravity A constant force applied to the particle every tick. It's often used as gravity. + * @param drag A drag force that affects the speed overtime. The formula is (velocity * (1.0 - drag)) + * @param minVel If particles travel slower than this their speed is automatically 0. Values less than or equal to 0 mean the particle can travel at any speed. + * @param lifeTime A range for the particle's lifetime in ticks. A value below 1 will result in the particle not spawning. + */ data class ParticleParams ( - //val shape: Shape, // add later, start with single point + val shape: SpawningShape? = SpawningShape.Circle(1.0f), val blockCurve: Array = arrayOf("minecraft:shroomlight", "minecraft:orange_concrete", "minecraft:orange_stained_glass", "minecraft:gray_stained_glass", "minecraft:light_gray_stained_glass"), val rotRandom: Pair = Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)), val rotVelRandom: Pair = Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)), @@ -17,9 +30,9 @@ data class ParticleParams ( val uniformSize: Boolean = true, val sizeCurve: Array = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f)), val velRandom: Pair = Pair(Vector3f(-0.1f, 0.4f, -0.1f), Vector3f(0.1f, 0.8f, 0.1f)), - val forceFields: ForceField = ForceField(), + val forceFields: Array = arrayOf(ForceField()), // emptyArray(), val gravity: Vector3f = Vector3f(0.0f, -0.01f, 0.0f), val drag: Float = 0.075f, val minVel: Float = 0.0f, - val lifeTime: Int = 30 + val lifeTime: Pair = Pair(15, 45) ) \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particle/Shape.kt b/src/main/kotlin/com/bytedice/bde_particles/particle/Shape.kt deleted file mode 100644 index b63f571..0000000 --- a/src/main/kotlin/com/bytedice/bde_particles/particle/Shape.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.bytedice.bde_particles.particle - -class Shape { - -} \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particle/Shapes.kt b/src/main/kotlin/com/bytedice/bde_particles/particle/Shapes.kt new file mode 100644 index 0000000..b70f515 --- /dev/null +++ b/src/main/kotlin/com/bytedice/bde_particles/particle/Shapes.kt @@ -0,0 +1,24 @@ +package com.bytedice.bde_particles.particle + +import org.joml.Vector2f +import org.joml.Vector3f + +sealed class SpawningShape { + data class Circle(val radius: Float = 1.0f) : SpawningShape() + data class Rect (val size: Vector2f = Vector2f(1.0f, 1.0f)) : SpawningShape() + data class Cube (val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f)) : SpawningShape() + data class Sphere(val radius: Float = 1.0f) : SpawningShape() +} + +sealed class ForceFieldShape { + data class Sphere( + val pos: Vector3f = Vector3f(0.0f, 0.0f, 0.0f), + val radius: Float = 1.0f + ) : ForceFieldShape() + + data class Rect( + val pos: Vector3f = Vector3f(0.0f, 0.0f, 0.0f), + val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), + val forceDir: Vector3f = Vector3f(0.0f, 0.0f, 0.0f) // 0.0 on all means from center + ) : ForceFieldShape() +} \ No newline at end of file