added async particle ticking and made new params fully working.
This commit is contained in:
@@ -36,25 +36,16 @@ import java.util.*
|
|||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// Allowing the use of regular Minecraft particles instead of just BDEs.
|
// 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)
|
// Better command auto-completion. (and change names of args)
|
||||||
// force field "config" option
|
// force field "config" option
|
||||||
|
|
||||||
// Parameters
|
|
||||||
// rotWithVel / scaleWithVel (bool)
|
|
||||||
// rotates/scales the particle to face the velocity
|
|
||||||
|
|
||||||
// Custom curve equation command args (parse from strings)
|
// Custom curve equation command args (parse from strings)
|
||||||
// Cylinder and cone force field shape.
|
// Cylinder and cone force field shape.
|
||||||
// Better particle performance.
|
// Better particle performance.
|
||||||
|
|
||||||
// Emitter groups (multiple emitters for EmitterTool)
|
// Emitter groups (multiple emitters for EmitterTool)
|
||||||
// Private functions/classes
|
// 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<ParticleEmitter> = emptyArray()
|
var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray()
|
||||||
@@ -119,6 +110,8 @@ class Bde_particles : ModInitializer {
|
|||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
addToRegister("DEFAULT", EmitterParams.DEFAULT)
|
addToRegister("DEFAULT", EmitterParams.DEFAULT)
|
||||||
|
addToRegister("DEBUG", EmitterParams.DEBUG)
|
||||||
|
addToRegister("STRESS_TEST", EmitterParams.STRESS_TEST)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,12 +132,18 @@ fun tick(server: MinecraftServer) {
|
|||||||
}.awaitAll()
|
}.awaitAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
for (emitter in ALL_PARTICLE_EMITTERS) {
|
ALL_PARTICLE_EMITTERS.map { emitter ->
|
||||||
if (emitter.isDead) {
|
async {
|
||||||
ALL_PARTICLE_EMITTERS = ALL_PARTICLE_EMITTERS.toMutableList().apply { remove(emitter) }.toTypedArray()
|
if (emitter.isDead) {
|
||||||
}
|
synchronized(ALL_PARTICLE_EMITTERS) {
|
||||||
else { emitter.tick() }
|
ALL_PARTICLE_EMITTERS = ALL_PARTICLE_EMITTERS.toMutableList().apply { remove(emitter) }.toTypedArray()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emitter.tick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.awaitAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) {
|
|||||||
put("right_rotation", rightRotList)
|
put("right_rotation", rightRotList)
|
||||||
})
|
})
|
||||||
|
|
||||||
put("view_range", NbtCompound().apply { properties.viewRange })
|
putFloat("view_range", properties.viewRange)
|
||||||
|
|
||||||
if (properties.name != null) {
|
if (properties.name != null) {
|
||||||
putString("CustomName", properties.name)
|
putString("CustomName", properties.name)
|
||||||
|
|||||||
@@ -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 {
|
fun eulerToQuat(euler: Vector3f): Vector4f {
|
||||||
val roll = euler.x
|
val roll = euler.x * (Math.PI / 180).toFloat()
|
||||||
val pitch = euler.y
|
val pitch = euler.y * (Math.PI / 180).toFloat()
|
||||||
val yaw = euler.z
|
val yaw = euler.z * (Math.PI / 180).toFloat()
|
||||||
|
|
||||||
val cy = cos(yaw * 0.5)
|
val cy = cos(yaw * 0.5)
|
||||||
val sy = sin(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 {
|
fun normalizeSdf(sdf: Float, radius: Float): Float {
|
||||||
return if (sdf < 0) (sdf + radius) / radius else 0f
|
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)
|
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,88 @@ data class EmitterParams (
|
|||||||
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
|
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
|
||||||
scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Constant),
|
scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Constant),
|
||||||
modelCurve = Pair(
|
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
|
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
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,10 @@ import com.bytedice.bde_particles.LerpCurves
|
|||||||
import com.bytedice.bde_particles.randomFloatBetween
|
import com.bytedice.bde_particles.randomFloatBetween
|
||||||
import com.bytedice.bde_particles.randomIntBetween
|
import com.bytedice.bde_particles.randomIntBetween
|
||||||
import org.joml.Vector3f
|
import org.joml.Vector3f
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.atan2
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
class ParamClasses {
|
class ParamClasses {
|
||||||
sealed class PairVec3f {
|
sealed class PairVec3f {
|
||||||
@@ -118,9 +122,38 @@ class ParamClasses {
|
|||||||
val loopDelay: Int
|
val loopDelay: Int
|
||||||
) : Duration()
|
) : Duration()
|
||||||
}
|
}
|
||||||
enum class TransformWithVel {
|
sealed class TransformWithVel {
|
||||||
RotOnly,
|
data object RotOnly : TransformWithVel() {
|
||||||
ScaleAndRot,
|
fun velToRot(velocity: Vector3f): Vector3f {
|
||||||
None
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,13 +10,6 @@ import org.joml.Vector4f
|
|||||||
import kotlin.math.*
|
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(
|
class Particle(
|
||||||
private val emitterParams: EmitterParams,
|
private val emitterParams: EmitterParams,
|
||||||
private var entityPos: Vec3d,
|
private var entityPos: Vec3d,
|
||||||
@@ -139,13 +132,30 @@ class Particle(
|
|||||||
|
|
||||||
val newModel = lerpArray(emitterParams.modelCurve.first as Array<Any>, timeAliveClamped, emitterParams.modelCurve.second) as String
|
val newModel = lerpArray(emitterParams.modelCurve.first as Array<Any>, timeAliveClamped, emitterParams.modelCurve.second) as String
|
||||||
val (newVel, newPos) = calcPos()
|
val (newVel, newPos) = calcPos()
|
||||||
val newRot = calcRot(timeAliveClamped)
|
|
||||||
val newScale = calcScale(timeAliveClamped)
|
|
||||||
val newOriginOffset = Vector3f(originOffset.mul(emitterParams.offsetCurve.lerpToVector3f(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
|
vel = newVel
|
||||||
pos = newPos
|
pos = newPos
|
||||||
rot = newRot
|
|
||||||
|
|
||||||
for (forceField in emitterParams.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)) }
|
||||||
@@ -197,17 +207,10 @@ class Particle(
|
|||||||
|
|
||||||
private fun calcRot(t: Float) : Vector3f {
|
private fun calcRot(t: Float) : Vector3f {
|
||||||
val newRot = Vector3f(rot)
|
val newRot = Vector3f(rot)
|
||||||
|
val newVel = Vector3f(rotVel)
|
||||||
|
|
||||||
if (emitterParams.transformWithVel == ParamClasses.TransformWithVel.None) {
|
newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t))
|
||||||
val newVel = Vector3f(rotVel)
|
return newRot.add(newVel)
|
||||||
|
|
||||||
newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t))
|
|
||||||
return newRot.add(newVel)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val rotWithVel = velToRot(vel)
|
|
||||||
return newRot.add(rotWithVel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -233,9 +236,6 @@ class Particle(
|
|||||||
|
|
||||||
|
|
||||||
private fun tickDebug(world: ServerWorld) {
|
private fun tickDebug(world: ServerWorld) {
|
||||||
// spawn particle at particle origin
|
|
||||||
// show trail pointing toward particle direction
|
|
||||||
|
|
||||||
// origin debug
|
// origin debug
|
||||||
val greenDustEffect = DustParticleEffect(Vector3f(0.0f, 1.0f, 0.0f), 2.0f)
|
val greenDustEffect = DustParticleEffect(Vector3f(0.0f, 1.0f, 0.0f), 2.0f)
|
||||||
world.spawnParticles(
|
world.spawnParticles(
|
||||||
@@ -251,23 +251,18 @@ class Particle(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// vel debug
|
// vel debug
|
||||||
var timesLooped = 0
|
val redDustEffect = DustParticleEffect(Vector3f(1.0f, 0.0f, 0.0f), 1.0f)
|
||||||
repeat(3) {
|
world.spawnParticles(
|
||||||
val redDustEffect = DustParticleEffect(Vector3f(1.0f, 0.0f, 0.0f), 1.0f)
|
redDustEffect,
|
||||||
world.spawnParticles(
|
entityPos.x + pos.x + originOffset.x + vel.x * 10,
|
||||||
redDustEffect,
|
entityPos.y + pos.y + originOffset.y + vel.y * 10,
|
||||||
entityPos.x + pos.x + originOffset.x + vel.x * timesLooped,
|
entityPos.z + pos.z + originOffset.z + vel.z * 10,
|
||||||
entityPos.y + pos.y + originOffset.y + vel.y * timesLooped,
|
1,
|
||||||
entityPos.z + pos.z + originOffset.z + vel.z * timesLooped,
|
0.0,
|
||||||
1,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0
|
||||||
0.0,
|
)
|
||||||
0.0
|
|
||||||
)
|
|
||||||
|
|
||||||
timesLooped += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user