added async particle ticking and made new params fully working.
This commit is contained in:
@@ -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<ParticleEmitter> = 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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<Any>, 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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user