reworked a bunch of stuff. Contains errors

This commit is contained in:
2024-11-28 19:53:49 +01:00
parent af7d29ce7d
commit 5b43c12c15
10 changed files with 256 additions and 161 deletions
@@ -27,6 +27,37 @@ import net.minecraft.world.World
import java.util.* import java.util.*
// TODO:
// Allowing the use of regular Minecraft particles instead of just BDEs.
// Better command auto-completion. (and change names of args)
// force field "config" option
// blockCurve array not needing "minecraft:"
// Adding a parameter for an initial velocity from an origin point (you currently have to use force fields)
// Pair<Pair<Vec3f, Vec3f>, Pair<Vec3f, Vec3f>>
// "min center vel", "max center vel", "min edge vel", "max edge vel"
// Parameters
// localOffset (Vec3f)
// velocity based, relative coordinate based, or rotation based
// localOffsetCurve (curve)
// spawnOffset (Vec3f)
// velRot (bool)
// rotates the particle to face the velocity
// spawnChance (Float)
// Move most (if not all) particle params to emitters.
// 3D curves (curves for all X Y Z)
// Custom curve equation command args (parse from strings)
// Cylinder and cone force field shape.
// Better particle performance.
// Global max particles variable.
// gamerule max particles
// Emitter groups (multiple emitters for EmitterTool)
// Private functions/classes
var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray() var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray()
val sessionUuid: UUID = UUID.randomUUID() val sessionUuid: UUID = UUID.randomUUID()
@@ -12,18 +12,18 @@ import kotlin.math.*
import kotlin.random.Random import kotlin.random.Random
class InterpolationCurves(val function: (Float) -> Float) { class LerpCurves(val function: (Float) -> Float) {
companion object { companion object {
val LINEAR = InterpolationCurves { y -> y } val Linear = LerpCurves { y -> y }
val SQRT = InterpolationCurves { y -> sqrt(y) } val Sqrt = LerpCurves { y -> sqrt(y) }
val EXPONENT = InterpolationCurves { y -> y.pow(2.0f) } val Exponent = LerpCurves { y -> y.pow(2.0f) }
val CUBIC = InterpolationCurves { y -> y.pow(3.0f) } val Cubic = LerpCurves { y -> y.pow(3.0f) }
val SINE = InterpolationCurves { y -> sin(y * PI.toFloat() / 2) } val Sine = LerpCurves { y -> sin(y * PI.toFloat() / 2) }
val COSINE = InterpolationCurves { y -> 1 - cos(y * PI.toFloat() / 2) } val Cosine = LerpCurves { y -> 1 - cos(y * PI.toFloat() / 2) }
val INVERSE = InterpolationCurves { y -> 1 - y } val Inverse = LerpCurves { y -> 1 - y }
val LOG = InterpolationCurves { y -> if (y > 0) ln(y + 1) else 0f } val Log = LerpCurves { y -> if (y > 0) ln(y + 1) else 0f }
val EXP = InterpolationCurves { y -> exp(y) - 1 } val Exp = LerpCurves { y -> exp(y) - 1 }
val BOUNCE = InterpolationCurves { y -> val Bounce = LerpCurves { y ->
val n1 = 7.5625f val n1 = 7.5625f
val d1 = 2.75f val d1 = 2.75f
when { when {
@@ -43,7 +43,7 @@ class InterpolationCurves(val function: (Float) -> Float) {
} }
} }
fun custom(equation: (Float) -> Float) = InterpolationCurves(equation) fun custom(equation: (Float) -> Float) = LerpCurves(equation)
} }
} }
@@ -209,7 +209,7 @@ fun lerp(x: Float, y: Float, t: Float) : Float {
} }
fun interpolateCurve(start: Vector3f, end: Vector3f, t: Float, curve: InterpolationCurves): Vector3f { fun interpolateCurve(start: Vector3f, end: Vector3f, t: Float, curve: LerpCurves): Vector3f {
val clampedT = t.coerceIn(0.0f, 1.0f) val clampedT = t.coerceIn(0.0f, 1.0f)
val curvedT = curve.function(clampedT) val curvedT = curve.function(clampedT)
@@ -1,6 +1,6 @@
package com.bytedice.bde_particles.commands package com.bytedice.bde_particles.commands
import com.bytedice.bde_particles.InterpolationCurves import com.bytedice.bde_particles.LerpCurves
import com.bytedice.bde_particles.particles.* import com.bytedice.bde_particles.particles.*
import com.mojang.brigadier.Command import com.mojang.brigadier.Command
import com.mojang.brigadier.arguments.BoolArgumentType import com.mojang.brigadier.arguments.BoolArgumentType
@@ -127,7 +127,7 @@ class ArgConfigParticleKeys {
.then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) .then(CommandManager.argument("Radius", FloatArgumentType.floatArg())
.executes { context -> .executes { context ->
val radius = FloatArgumentType.getFloat(context, "Radius") val radius = FloatArgumentType.getFloat(context, "Radius")
val feedback = updateParticleParam(context, "shape", SpawningShape.CIRCLE(radius)) val feedback = updateParticleParam(context, "shape", SpawningShape.Circle(radius))
context.source.sendFeedback( { feedback }, false) context.source.sendFeedback( { feedback }, false)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} }
@@ -139,7 +139,7 @@ class ArgConfigParticleKeys {
.executes { context -> .executes { context ->
val w = FloatArgumentType.getFloat(context, "Width") val w = FloatArgumentType.getFloat(context, "Width")
val h = FloatArgumentType.getFloat(context, "Height") val h = FloatArgumentType.getFloat(context, "Height")
val feedback = updateParticleParam(context, "shape", SpawningShape.RECT(Vector2f(w, h))) val feedback = updateParticleParam(context, "shape", SpawningShape.Rect(Vector2f(w, h)))
context.source.sendFeedback( { feedback }, false) context.source.sendFeedback( { feedback }, false)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} }
@@ -154,7 +154,7 @@ class ArgConfigParticleKeys {
val x = FloatArgumentType.getFloat(context, "X") val x = FloatArgumentType.getFloat(context, "X")
val y = FloatArgumentType.getFloat(context, "Y") val y = FloatArgumentType.getFloat(context, "Y")
val z = FloatArgumentType.getFloat(context, "Z") val z = FloatArgumentType.getFloat(context, "Z")
val feedback = updateParticleParam(context, "shape", SpawningShape.CUBE(Vector3f(x, y, z))) val feedback = updateParticleParam(context, "shape", SpawningShape.Cube(Vector3f(x, y, z)))
context.source.sendFeedback( { feedback }, false) context.source.sendFeedback( { feedback }, false)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} }
@@ -166,7 +166,7 @@ class ArgConfigParticleKeys {
.then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) .then(CommandManager.argument("Radius", FloatArgumentType.floatArg())
.executes { context -> .executes { context ->
val radius = FloatArgumentType.getFloat(context, "Radius") val radius = FloatArgumentType.getFloat(context, "Radius")
val feedback = updateParticleParam(context, "shape", SpawningShape.SPHERE(radius)) val feedback = updateParticleParam(context, "shape", SpawningShape.Sphere(radius))
context.source.sendFeedback( { feedback }, false) context.source.sendFeedback( { feedback }, false)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} }
@@ -174,7 +174,7 @@ class ArgConfigParticleKeys {
) )
.then(CommandManager.literal("point") .then(CommandManager.literal("point")
.executes { context -> .executes { context ->
val feedback = updateParticleParam(context, "shape", SpawningShape.POINT) val feedback = updateParticleParam(context, "shape", SpawningShape.Point)
context.source.sendFeedback( { feedback }, false) context.source.sendFeedback( { feedback }, false)
Command.SINGLE_SUCCESS Command.SINGLE_SUCCESS
} }
@@ -415,19 +415,19 @@ class ArgConfigParticleKeys {
} }
fun getCurveByString(str: String) : InterpolationCurves { fun getCurveByString(str: String) : LerpCurves {
return when (str) { return when (str) {
"LINEAR" -> InterpolationCurves.LINEAR "LINEAR" -> LerpCurves.Linear
"SQRT" -> InterpolationCurves.SQRT "SQRT" -> LerpCurves.Sqrt
"EXPONENT" -> InterpolationCurves.EXPONENT "EXPONENT" -> LerpCurves.Exponent
"CUBIC" -> InterpolationCurves.CUBIC "CUBIC" -> LerpCurves.Cubic
"SINE" -> InterpolationCurves.SINE "SINE" -> LerpCurves.Sine
"COSINE" -> InterpolationCurves.COSINE "COSINE" -> LerpCurves.Cosine
"INVERSE" -> InterpolationCurves.INVERSE "INVERSE" -> LerpCurves.Inverse
"LOG" -> InterpolationCurves.LOG "LOG" -> LerpCurves.Log
"EXP" -> InterpolationCurves.EXP "EXP" -> LerpCurves.Exp
"BOUNCE" -> InterpolationCurves.BOUNCE "BOUNCE" -> LerpCurves.Bounce
else -> InterpolationCurves.LINEAR else -> LerpCurves.Linear
} }
} }
@@ -538,7 +538,7 @@ fun forceFieldAddSphereExec(context: CommandContext<ServerCommandSource>) : Int
val minForce = FloatArgumentType.getFloat(context, "Min Force") val minForce = FloatArgumentType.getFloat(context, "Min Force")
val maxForce = FloatArgumentType.getFloat(context, "Max Force") val maxForce = FloatArgumentType.getFloat(context, "Max Force")
val shape = ForceFieldShape.SPHERE(radius, Pair(minForce, maxForce)) val shape = ForceFieldShape.Sphere(radius, Pair(minForce, maxForce))
val forceField = forceFieldGenericParams(context, shape) val forceField = forceFieldGenericParams(context, shape)
val currentParams = getEmitterParams(emitterIdVal) val currentParams = getEmitterParams(emitterIdVal)
@@ -567,7 +567,7 @@ fun forceFieldAddCubeExec(context: CommandContext<ServerCommandSource>) : Int {
val zForce = FloatArgumentType.getFloat(context, "Z Force") val zForce = FloatArgumentType.getFloat(context, "Z Force")
val emitterIdVal = StringArgumentType.getString(context, "Registered Emitter ID") val emitterIdVal = StringArgumentType.getString(context, "Registered Emitter ID")
val shape = ForceFieldShape.CUBE(Vector3f(xSize, ySize, zSize), Vector3f(xForce, yForce, zForce)) val shape = ForceFieldShape.Cube(Vector3f(xSize, ySize, zSize), Vector3f(xForce, yForce, zForce))
val forceField = forceFieldGenericParams(context, shape) val forceField = forceFieldGenericParams(context, shape)
val currentParams = getEmitterParams(emitterIdVal) val currentParams = getEmitterParams(emitterIdVal)
@@ -1,42 +1,60 @@
package com.bytedice.bde_particles.particles package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.LerpCurves
import org.joml.Vector3f
/** /**
* EmitterParams defines the parameters for controlling how particles are emitted, * EmitterParams defines the parameters for controlling how particles are emitted,
* including settings for looping, spawn frequency, and maximum particle count. * including settings for looping, spawn frequency, and maximum particle count.
* *
* @param maxCount The maximum number of particles that can exist. If set to 0 or below, no particles will spawn. * @param maxCount The maximum number of particles that can exist at any time. If set to 0 or below, no particles will spawn.
* @param spawnsPerTick The number of particles spawned per tick. If set to 0 or below, no particles will spawn. * @param spawnRate The number of particles to spawn each tick. If set to 0 or below, no particles will spawn.
* @param loopDur The duration for which the loop runs, in ticks. A value 0 or below means the loop is infinite, and `loopDelay` and `loopCount` will be ignored. * @param spawnChance The chance for a particle to spawn. 0.5 means a particle has a 50% chance of spawning. Values are between 0.0 and 1.0.
* @param loopDelay The delay between each loop cycle, in ticks. Negative values are treated as 0, meaning no delay. * @param loopDur The duration, in ticks, that each emission loop lasts. A value of 0 or below makes the loop infinite, ignoring `loopDelay` and `loopCount`.
* @param loopCount The number of times the loop will repeat. 0 means it will shoot a single burst of particles. A value below 0 means it will repeat indefinitely. * @param loopDelay The delay, in ticks, between consecutive loops. Negative values are treated as 0 (no delay).
* @param particle The particle params of the particle that will spawn. * @param loopCount The number of times the loop repeats. A value of 0 results in a single burst of particles. Values below 0 make the loop repeat indefinitely.
* @param shape The spawning shape for the particles, such as `SpawningShape.Sphere()`.
* @param initRot Specifies the initial random rotation range for the particles, in degrees, for the X, Y, and Z axes.
* @param rotVel Specifies the random range for rotational velocity, in degrees per tick, for the X, Y, and Z axes.
* @param rotTowardVel If true, particles will face the direction of velocity. It uses `initRot` as an offset rotation and ignores `rotVel`.
* @param initVel Specifies the random range for the initial velocity, in units per tick, for the X, Y, and Z axes.
* @param initScale Defines the random range for the initial particle size.
* @param scaleTowardVel If true, particles will scale to the direction of velocity.
* @param forceFields An array of force fields applied to particles, affecting their motion during their lifetime.
* @param constVel A constant velocity vector applied to particles each tick, useful for effects like gravity.
* @param drag A drag coefficient that slows down particle velocity over time. Calculated as `velocity * (1.0 - drag)`.
* @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 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 (
var maxCount: Int = 200, var maxCount: Int = 200,
var spawnsPerTick: Int = 1, var spawnRate: Int = 1,
var loopDur: Int = 25, var spawnChance: Float = 1.0f,
var loopDelay: Int = 0, var loopDur: Int = 25,
var loopCount: Int = 0, var loopDelay: Int = 0,
var particle: ParticleParams = ParticleParams.DEFAULT, var loopCount: Int = 0,
var shape: SpawningShape = SpawningShape.Circle(3.0f),
var initRot: ParamClasses.PairVec3f = ParamClasses.PairVec3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f),
var rotVel: ParamClasses.PairVec3f = ParamClasses.PairVec3f(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f),
var rotTowardVel: Boolean = true,
var initVel: ParamClasses.PairVec3f = ParamClasses.PairVec3f(-0.1f, 0.3f, -0.1f, 0.1f, 0.6f, 0.1f),
var initScale: ParamClasses.RandScale = ParamClasses.RandScale.Uniform(),
var scaleTowardVel: Boolean = true,
var forceFields: Array<ForceField> = emptyArray(),
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 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 blockCurve: Pair<Array<String>, LerpCurves> = Pair(arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"), LerpCurves.Sqrt),
) )
{ {
companion object Presets { companion object Presets {
val DEFAULT = EmitterParams() val DEFAULT = EmitterParams()
val FIRE_GEYSER = EmitterParams(
200,
1,
25,
0,
0,
ParticleParams.FIRE_GEYSER
)
val RING_EXPLOSION = EmitterParams(
200,
150,
1,
0,
0,
ParticleParams.RING_EXPLOSION
)
} }
} }
@@ -5,5 +5,5 @@ import org.joml.Vector3f
data class ForceField ( data class ForceField (
val name: String = "DEFAULT", val name: String = "DEFAULT",
val pos: Vector3f = Vector3f(0.0f, 5.0f, 0.0f), val pos: Vector3f = Vector3f(0.0f, 5.0f, 0.0f),
val shape: ForceFieldShape = ForceFieldShape.SPHERE() val shape: ForceFieldShape = ForceFieldShape.Sphere()
) )
@@ -0,0 +1,110 @@
package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.LerpCurves
import com.bytedice.bde_particles.randomFloatBetween
import com.bytedice.bde_particles.randomIntBetween
import org.joml.Vector3f
class ParamClasses {
class PairVec3f (
private val minX: Float = 0.0f,
private val minY: Float = 0.0f,
private val minZ: Float = 0.0f,
private val maxX: Float = 1.0f,
private val maxY: Float = 1.0f,
private val maxZ: Float = 1.0f,
) {
fun randomize() : Vector3f {
return Vector3f(
randomFloatBetween(minX, maxX),
randomFloatBetween(minY, maxY),
randomFloatBetween(minZ, maxZ),
)
}
}
sealed class RandScale {
class Uniform (
private val min: Float = 0.0f,
private val max: Float = 1.0f,
) : RandScale() {
fun randomize() : Vector3f {
val x = randomFloatBetween(min, max)
return Vector3f(x, x, x)
}
}
class NonUniform (
private val minX: Float = 0.0f,
private val minY: Float = 0.0f,
private val minZ: Float = 0.0f,
private val maxX: Float = 1.0f,
private val maxY: Float = 1.0f,
private val maxZ: Float = 1.0f
) : RandScale() {
fun randomize(): Vector3f {
return Vector3f(
randomFloatBetween(minX, maxX),
randomFloatBetween(minY, maxY),
randomFloatBetween(minZ, maxZ),
)
}
}
}
class PairVec2i (
private val minX: Int = 0,
private val minY: Int = 0,
private val maxX: Int = 1,
private val maxY: Int = 1,
) {
fun randomize() : Pair<Int, Int> {
return Pair(
randomIntBetween(minX, maxX),
randomIntBetween(minY, maxY),
)
}
}
sealed class LerpVal {
class LerpVec3f(
private val fromX: Float = 0.0f,
private val fromY: Float = 0.0f,
private val fromZ: Float = 0.0f,
private val toX: Float = 1.0f,
private val toY: Float = 1.0f,
private val toZ: Float = 1.0f,
private val curve: LerpCurves = LerpCurves.Linear,
) : LerpVal() {
fun lerp(t: Float): Vector3f {
val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curve.function(t)
val y = com.bytedice.bde_particles.lerp(fromY, toY, t) * curve.function(t)
val z = com.bytedice.bde_particles.lerp(fromZ, toZ, t) * curve.function(t)
return Vector3f(x, y, z)
}
}
class MultiLerpVec3f(
private val fromX: Float = 0.0f,
private val fromY: Float = 0.0f,
private val fromZ: Float = 0.0f,
private val toX: Float = 1.0f,
private val toY: Float = 1.0f,
private val toZ: Float = 1.0f,
private val curveX: LerpCurves = LerpCurves.Linear,
private val curveY: LerpCurves = LerpCurves.Linear,
private val curveZ: LerpCurves = LerpCurves.Linear
) : LerpVal() {
fun lerp(t: Float): Vector3f {
val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curveX.function(t)
val y = com.bytedice.bde_particles.lerp(fromY, toY, t) * curveY.function(t)
val z = com.bytedice.bde_particles.lerp(fromZ, toZ, t) * curveZ.function(t)
return Vector3f(x, y, z)
}
}
class LerpUniform(
private val from: Float = 0.0f,
private val to: Float = 1.0f,
private val curve: LerpCurves = LerpCurves.Linear,
) : LerpVal() {
fun lerp(t: Float): Float {
return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t)
}
}
}
}
@@ -11,7 +11,7 @@ import kotlin.math.*
// TODO: consider moving most params to emitter (such as shape, and forceFields) to store less data // TODO: consider moving most params to emitter (such as shape, and forceFields) to store less data
class Particle(private val particleParams: ParticleParams) { 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)
@@ -44,11 +44,11 @@ class Particle(private val particleParams: ParticleParams) {
val newSpawnPos: Vector3f val newSpawnPos: Vector3f
when (shape) { when (shape) {
is SpawningShape.CIRCLE -> { val posInCircle = randomInCircle(shape.radius); newSpawnPos = Vector3f(posInCircle.x, 0.0f, posInCircle.y) } 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.Sphere -> { newSpawnPos = randomInSphere(shape.radius) }
is SpawningShape.RECT -> { val posInRect = randomInRect(shape.size); newSpawnPos = Vector3f(posInRect.x, 0.0f, posInRect.y) } is SpawningShape.Rect -> { val posInRect = randomInRect(shape.size); newSpawnPos = Vector3f(posInRect.x, 0.0f, posInRect.y) }
is SpawningShape.CUBE -> { newSpawnPos = randomInCube(shape.size) } is SpawningShape.Cube -> { newSpawnPos = randomInCube(shape.size) }
is SpawningShape.POINT -> { newSpawnPos = Vector3f(0.0f, 0.0f, 0.0f) } is SpawningShape.Point -> { newSpawnPos = Vector3f(0.0f, 0.0f, 0.0f) }
} }
offset = newSpawnPos offset = newSpawnPos
@@ -110,7 +110,7 @@ class Particle(private val particleParams: ParticleParams) {
} }
fun sphereSdfVel(forceField: ForceField) : Vector3f { fun sphereSdfVel(forceField: ForceField) : Vector3f {
if (forceField.shape !is ForceFieldShape.SPHERE) { return Vector3f(0.0f, 0.0f, 0.0f) } if (forceField.shape !is ForceFieldShape.Sphere) { return Vector3f(0.0f, 0.0f, 0.0f) }
val shape = forceField.shape val shape = forceField.shape
val sdfVal = sdfSphere(forceField.pos, shape.radius, offset) val sdfVal = sdfSphere(forceField.pos, shape.radius, offset)
@@ -123,7 +123,7 @@ class Particle(private val particleParams: ParticleParams) {
} }
fun cubeSdfVel(forceField: ForceField) : Vector3f { fun cubeSdfVel(forceField: ForceField) : Vector3f {
if (forceField.shape !is ForceFieldShape.CUBE) { return Vector3f(0.0f, 0.0f, 0.0f) } if (forceField.shape !is ForceFieldShape.Cube) { return Vector3f(0.0f, 0.0f, 0.0f) }
val shape = forceField.shape val shape = forceField.shape
val sdfVal = sdfCube(forceField.pos, shape.size, offset) val sdfVal = sdfCube(forceField.pos, shape.size, offset)
@@ -176,8 +176,8 @@ class Particle(private val particleParams: ParticleParams) {
rot = newRot rot = newRot
for (forceField in particleParams.forceFields) { for (forceField in particleParams.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)) }
} }
val (quatRot, transformOffset) = calcTransformOffset(newRot, initOffset, newScale) val (quatRot, transformOffset) = calcTransformOffset(newRot, initOffset, newScale)
@@ -2,7 +2,7 @@
package com.bytedice.bde_particles.particles package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.InterpolationCurves import com.bytedice.bde_particles.LerpCurves
import org.joml.Vector3f import org.joml.Vector3f
@@ -103,8 +103,8 @@ fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramVa
"drag" -> paramValue as Float "drag" -> paramValue as Float
"minVel" -> paramValue as Float "minVel" -> paramValue as Float
"lifeTime" -> paramValue as Pair<Int, Int> "lifeTime" -> paramValue as Pair<Int, Int>
"rotVelCurve" -> paramValue as Triple<Vector3f, Vector3f, InterpolationCurves> "rotVelCurve" -> paramValue as Triple<Vector3f, Vector3f, LerpCurves>
"sizeCurve" -> paramValue as Triple<Vector3f, Vector3f, InterpolationCurves> "sizeCurve" -> paramValue as Triple<Vector3f, Vector3f, LerpCurves>
else -> null else -> null
} }
@@ -125,8 +125,8 @@ fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramVa
"drag" -> newParams.drag = value as Float "drag" -> newParams.drag = value as Float
"minVel" -> newParams.minVel = value as Float "minVel" -> newParams.minVel = value as Float
"lifeTime" -> newParams.lifeTime = value as Pair<Int, Int> "lifeTime" -> newParams.lifeTime = value as Pair<Int, Int>
"rotVelCurve" -> newParams.rotVelCurve = value as Triple<Vector3f, Vector3f, InterpolationCurves> "rotVelCurve" -> newParams.rotVelCurve = value as Triple<Vector3f, Vector3f, LerpCurves>
"sizeCurve" -> newParams.sizeCurve = value as Triple<Vector3f, Vector3f, InterpolationCurves> "sizeCurve" -> newParams.sizeCurve = value as Triple<Vector3f, Vector3f, LerpCurves>
else -> return Pair(params, false) else -> return Pair(params, false)
} }
@@ -1,75 +0,0 @@
package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.InterpolationCurves
import org.joml.Vector3f
/**
* 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. Empty defaults to "minecraft:air".
* @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 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 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.
* @param rotVelCurve A curve which the rotation velocity gets multiplied by during the particle's lifetime.
* @param sizeCurve A curve which the size gets multiplied by during the particle's lifetime.
*/
data class ParticleParams (
var shape: SpawningShape = SpawningShape.CIRCLE(3.0f),
var blockCurve: Array<String> = arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"),
var rotRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)),
var rotVelRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)),
var sizeRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)),
var uniformSize: Boolean = true,
var velRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(-0.1f, 0.3f, -0.1f), Vector3f(0.1f, 0.6f, 0.1f)),
var forceFields: Array<ForceField> = emptyArray(),
var gravity: Vector3f = Vector3f(0.0f, -0.01f, 0.0f),
var drag: Float = 0.075f,
var minVel: Float = 0.0f,
var lifeTime: Pair<Int, Int> = Pair(15, 45),
var rotVelCurve: Triple<Vector3f, Vector3f, InterpolationCurves> = Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f), InterpolationCurves.SQRT),
var sizeCurve: Triple<Vector3f, Vector3f, InterpolationCurves> = Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f), InterpolationCurves.LINEAR),
) {
companion object Presets {
val DEFAULT = ParticleParams()
val FIRE_GEYSER = ParticleParams(
SpawningShape.POINT,
arrayOf("minecraft:shroomlight", "minecraft:orange_concrete", "minecraft:orange_stained_glass", "minecraft:gray_stained_glass", "minecraft:light_gray_stained_glass"),
Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)),
Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)),
Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)),
true,
Pair(Vector3f(-0.1f, 0.4f, -0.1f), Vector3f(0.1f, 0.8f, 0.1f)),
emptyArray(),
Vector3f(0.0f, -0.01f, 0.0f),
0.075f,
0.0f,
Pair(15, 45),
Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f), InterpolationCurves.SQRT),
Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f), InterpolationCurves.SQRT)
)
val RING_EXPLOSION = ParticleParams(
SpawningShape.CIRCLE(0.1f),
arrayOf("minecraft:shroomlight", "minecraft:orange_concrete", "minecraft:orange_stained_glass", "minecraft:gray_stained_glass", "minecraft:light_gray_stained_glass"),
Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)),
Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)),
Pair(Vector3f(10.0f, 10.0f, 10.0f), Vector3f(15.0f, 15.0f, 15.0f)),
true,
Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(0.0f, 0.0f, 0.0f)),
arrayOf(ForceField("init", Vector3f(0.0f, -0.0001f, 0.0f), ForceFieldShape.SPHERE(0.1f, Pair(4.0f, 4.0f)))),
Vector3f(0.0f, 0.0f, 0.0f),
0.0075f,
0.0f,
Pair(50, 100),
Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f), InterpolationCurves.SQRT),
Triple(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(3.0f, 3.0f, 3.0f), InterpolationCurves.SQRT)
)
}
}
@@ -4,20 +4,31 @@ import org.joml.Vector2f
import org.joml.Vector3f import org.joml.Vector3f
sealed class SpawningShape { sealed class SpawningShape {
data class CIRCLE(val radius: Float = 1.0f) : SpawningShape() data class Circle(val radius: Float = 1.0f) : SpawningShape()
data class RECT (val size: Vector2f = Vector2f(1.0f, 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 Cube (val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f)) : SpawningShape()
data class SPHERE(val radius: Float = 1.0f) : SpawningShape() data class Sphere(val radius: Float = 1.0f) : SpawningShape()
data object POINT : SpawningShape() data object Point : SpawningShape()
} }
sealed class ForceFieldShape { sealed class ForceFieldShape {
data class SPHERE( data class Sphere(
val radius: Float = 3.0f, val radius: Float = 3.0f,
val force: Pair<Float, Float> = Pair(0.0f, 0.02f), val force: Pair<Float, Float> = Pair(0.0f, 0.02f),
) : 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 forceDir: Vector3f = Vector3f(0.0f, 0.0f, 0.0f) val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f),
val force: Pair<Float, Float> = Pair(0.0f, 0.02f)
) : ForceFieldShape() ) : ForceFieldShape()
data class Cone(
val size: Vector2f = Vector2f(1.0f, 1.0f),
val dir: Vector3f = Vector3f(0.0f, 0.0f, -1.0f),
val force: Pair<Float, Float> = 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<Float, Float> = Pair(0.0f, 0.02f)
)
} }