added emitter debug tools. More testing required before 1.0!

This commit is contained in:
2024-12-26 16:34:19 +01:00
parent c416432f7b
commit 46498d25f4
7 changed files with 130 additions and 52 deletions
@@ -53,10 +53,6 @@ import kotlin.reflect.full.declaredMemberProperties
// Custom curve equation command args (parse from strings). // Custom curve equation command args (parse from strings).
// Force field "config" option. // Force field "config" option.
// TODO: (now)
// Better particle debug tools.
// Use more BDEs instead of particles, and show more values than origin and velocity.
var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray() var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray()
var LIVING_PARTICLE_COUNT: Int = 0 var LIVING_PARTICLE_COUNT: Int = 0
@@ -3,6 +3,7 @@ package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.Billboard import com.bytedice.bde_particles.Billboard
import com.bytedice.bde_particles.DisplayEntity import com.bytedice.bde_particles.DisplayEntity
import com.bytedice.bde_particles.DisplayEntityProperties import com.bytedice.bde_particles.DisplayEntityProperties
import com.bytedice.bde_particles.SESSION_UUID
import net.minecraft.server.world.ServerWorld import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3d
import org.joml.Vector2f import org.joml.Vector2f
@@ -14,26 +15,31 @@ class EmitterDebug(
eOrigin: Vec3d, eOrigin: Vec3d,
spawnPosOffset: Vector3f, spawnPosOffset: Vector3f,
shape: SpawningShape, shape: SpawningShape,
forceFields: ParamClasses.ForceFieldArray
) { ) {
private val eOrigin = EOrigin(rot, eOrigin) private val eOriginDebug = EOrigin(rot, eOrigin)
private val shapeDebug = ShapeDebug(rot, eOrigin, spawnPosOffset, shape) private val shapeDebug = ShapeDebug(eOrigin, spawnPosOffset, shape)
private val forceFieldDebug = ForceFieldDebug()
fun spawn(world: ServerWorld) { fun spawn(world: ServerWorld, eOrigin: Vec3d, forceFields: ParamClasses.ForceFieldArray, spawnPosOffset: Vector3f) {
eOrigin.spawn(world) eOriginDebug.spawn(world)
shapeDebug.spawn(world) shapeDebug.spawn(world)
forceFieldDebug.makeArray(eOrigin, forceFields, spawnPosOffset)
forceFieldDebug.spawn(world)
} }
fun update( fun update(
rot: Vector2f, rot: Vector2f,
eOrigin: Vec3d, eOrigin: Vec3d,
pOrigin: Vector3f, spawnPosOffset: Vector3f
pVel: Vector3f,
pScale: Vector3f,
spawnPosOffset: Vector3f,
shape: SpawningShape,
) { ) {
eOriginDebug.update(rot, eOrigin)
shapeDebug.update(rot, eOrigin, spawnPosOffset)
}
fun kill() {
eOriginDebug.kill()
shapeDebug.kill()
forceFieldDebug.kill()
} }
} }
@@ -44,11 +50,10 @@ class EOrigin(rot: Vector2f, eOrigin: Vec3d) {
pos = eOrigin, pos = eOrigin,
rot = rot, rot = rot,
model = "minecraft:red_concrete", model = "minecraft:red_concrete",
tags = arrayOf("DEBUG", "E_DEBUG", "eOrigin_DEBUG"), tags = arrayOf("DEBUG", "E_DEBUG", "eOrigin_DEBUG", "BPS_UUID", SESSION_UUID.toString()),
brightnessOverride = 15, brightnessOverride = 15,
scale = Vector3f(0.25f, 0.25f, 0.25f) scale = Vector3f(0.25f, 0.25f, 0.25f)
) ))
)
fun spawn(world: ServerWorld) { fun spawn(world: ServerWorld) {
eOriginDE.spawn(world) eOriginDE.spawn(world)
@@ -61,36 +66,52 @@ class EOrigin(rot: Vector2f, eOrigin: Vec3d) {
eOriginDE.updateProperties(newProperties!!) eOriginDE.updateProperties(newProperties!!)
} }
fun kill() {
eOriginDE.kill()
}
} }
class ShapeDebug(rot: Vector2f, eOrigin: Vec3d, spawnPosOffset: Vector3f, shape: SpawningShape) { class ShapeDebug(eOrigin: Vec3d, spawnPosOffset: Vector3f, shape: SpawningShape) {
val shapeScale: Vector3f? = when (shape) { val shapeScale: Vector3f? = when (shape) {
is SpawningShape.Circle -> Vector3f(shape.radius * 1.75f, shape.radius * 1.75f, 0.0f) is SpawningShape.Circle -> Vector3f(shape.radius * 1.75f * 2, shape.radius * 1.75f * 2, 0.0001f)
is SpawningShape.Sphere -> Vector3f(shape.radius * 1.75f, shape.radius * 1.75f, 0.0f) is SpawningShape.Sphere -> Vector3f(shape.radius * 1.75f * 2, shape.radius * 1.75f * 2, 0.0001f)
is SpawningShape.Rect -> Vector3f(shape.size.x, 0.0f, shape.size.y) is SpawningShape.Rect -> Vector3f(shape.size.x, 0.0001f, shape.size.y)
is SpawningShape.Cube -> Vector3f(shape.size.x, shape.size.y, shape.size.z) is SpawningShape.Cube -> Vector3f(shape.size.x, shape.size.y, shape.size.z)
else -> null else -> null
} }
val shapeRot = if (shape is SpawningShape.Circle) { Vector4f(-0.707f, 0.0f, 0.0f, 0.707f) } val shapeRot = when (shape) {
else { Vector4f(0.0f, 0.0f, 0.0f, 1.0f) } is SpawningShape.Circle -> Vector4f(-0.707f, 0.0f, 0.0f, 0.707f)
else -> Vector4f(0.0f, 0.0f, 0.0f, 1.0f)
}
private fun determineModel(shape: SpawningShape): String =
when (shape) {
is SpawningShape.Circle, is SpawningShape.Sphere -> "minecraft:sunflower"
else -> "minecraft:yellow_stained_glass"
}
private fun determineBillboard(shape: SpawningShape): String =
if (shape !is SpawningShape.Sphere) Billboard.FIXED else Billboard.CENTER
val shapeDE: DisplayEntity? = if (shape !is SpawningShape.Point) { val shapeDE: DisplayEntity? = if (shape !is SpawningShape.Point) {
val position = eOrigin.add(
spawnPosOffset.x.toDouble(),
spawnPosOffset.y.toDouble(),
spawnPosOffset.z.toDouble()
)
DisplayEntity(DisplayEntityProperties( DisplayEntity(DisplayEntityProperties(
pos = eOrigin.add(spawnPosOffset.x.toDouble(), spawnPosOffset.y.toDouble(), spawnPosOffset.z.toDouble()), pos = position.add(spawnPosOffset.x.toDouble(), spawnPosOffset.y.toDouble(), spawnPosOffset.z.toDouble()),
rot = rot, model = determineModel(shape),
model = if (shape is SpawningShape.Circle || shape is SpawningShape.Sphere) { "minecraft:sunflower" } tags = arrayOf("DEBUG", "P_DEBUG", "shape_DEBUG", "BPS_UUID", SESSION_UUID.toString()),
else { "minecraft:yellow_stained_glass" },
tags = arrayOf("DEBUG", "P_DEBUG", "shape_DEBUG"),
brightnessOverride = 15, brightnessOverride = 15,
billboard = if (shape !is SpawningShape.Sphere) { Billboard.FIXED } else { Billboard.CENTER }, billboard = determineBillboard(shape),
translation = spawnPosOffset,
leftRotation = shapeRot, leftRotation = shapeRot,
scale = shapeScale!! scale = shapeScale ?: Vector3f(1f, 1f, 1f)
)) ))
} } else null
else { null }
fun spawn(world: ServerWorld) { fun spawn(world: ServerWorld) {
shapeDE?.spawn(world) shapeDE?.spawn(world)
@@ -104,4 +125,43 @@ class ShapeDebug(rot: Vector2f, eOrigin: Vec3d, spawnPosOffset: Vector3f, shape:
shapeDE?.updateProperties(newProperties!!) shapeDE?.updateProperties(newProperties!!)
} }
fun kill() {
shapeDE?.kill()
}
}
class ForceFieldDebug {
var FFDEArray: Array<DisplayEntity> = emptyArray()
fun makeArray(eOrigin: Vec3d, forceFieldArray: ParamClasses.ForceFieldArray, spawnPosOffset: Vector3f) {
forceFieldArray.array.forEach {
val shapeScale = when (it.shape) {
is ForceFieldShape.Sphere -> Vector3f(1.333f * it.shape.radius * 2, 1.333f * it.shape.radius * 2, 0.0001f)
is ForceFieldShape.Cube -> it.shape.size
}
val newOffset = Vector3f(spawnPosOffset).add(it.pos)
val displayEntity = DisplayEntity(
DisplayEntityProperties(
pos = eOrigin.add(newOffset.x.toDouble(), newOffset.y.toDouble(), newOffset.z.toDouble()),
model = if (it.shape is ForceFieldShape.Sphere) { "minecraft:snowball" } else { "minecraft:white_stained_glass" },
tags = arrayOf("DEBUG", "E_DEBUG", "eOrigin_DEBUG", "BPS_UUID", SESSION_UUID.toString()),
brightnessOverride = 15,
billboard = if (it.shape is ForceFieldShape.Sphere) { Billboard.CENTER } else { Billboard.FIXED },
scale = shapeScale
))
FFDEArray += displayEntity
}
}
fun spawn(world: ServerWorld) {
FFDEArray.forEach { it.spawn(world) }
}
fun kill() {
FFDEArray.forEach { it.kill() }
}
} }
@@ -126,7 +126,7 @@ data class EmitterParams (
ForceFieldShape.Sphere(15.0f, Pair(0.1f, 0.2f)) ForceFieldShape.Sphere(15.0f, Pair(0.1f, 0.2f))
)) ))
), ),
constVel = Vector3f(0.0f, 0.001f, 0.0f), constVel = Vector3f(0.0f, 0.00001f, 0.0f),
drag = 0.075f, drag = 0.075f,
minVel = 0.0f, minVel = 0.0f,
offsetCurve = ParamClasses.LerpValVec3f.Null, offsetCurve = ParamClasses.LerpValVec3f.Null,
@@ -43,14 +43,22 @@ class ParamClasses {
private val min: Int, private val min: Int,
private val max: Int, private val max: Int,
) { ) {
fun randomize() : Int { return randomIntBetween(min, max) } fun randomize() : Int {
return if (min == max) { max }
else if (min > max) { max }
else { randomIntBetween(min, max) }
}
} }
class PairFloat( class PairFloat(
private val min: Float, private val min: Float,
private val max: Float, private val max: Float,
) { ) {
fun randomize() : Float { return randomFloatBetween(min, max) } fun randomize() : Float {
return if (min == max) { max }
else if (min > max) { max }
else { randomFloatBetween(min, max) }
}
} }
sealed class LerpValVec3f { sealed class LerpValVec3f {
@@ -60,8 +60,10 @@ class Particle(
fun init() { fun init() {
val so = emitterParams.spawnPosOffset val so = Vector3f(emitterParams.spawnPosOffset)
if (so.x + so.y + so.z != 0.0f) {
entityPos = entityPos.add(so.x.toDouble(), so.y.toDouble(), so.z.toDouble()) entityPos = entityPos.add(so.x.toDouble(), so.y.toDouble(), so.z.toDouble())
}
val shape = emitterParams.shape val shape = emitterParams.shape
val newSpawnPos = shape.random(shape) val newSpawnPos = shape.random(shape)
@@ -222,7 +224,10 @@ class Particle(
private fun calcPos() : Pair<Vector3f, Vector3f> { private fun calcPos() : Pair<Vector3f, Vector3f> {
var vel = Vector3f(vel) var vel = Vector3f(vel)
val constVel = emitterParams.constVel
if (constVel.x + constVel.y + constVel.z != 0.0f) {
vel = vel.add(emitterParams.constVel) vel = vel.add(emitterParams.constVel)
}
vel = vel.mul(1.0f - emitterParams.drag) vel = vel.mul(1.0f - emitterParams.drag)
if (abs(vel.x) < emitterParams.minVel) { vel.x = 0.0f } if (abs(vel.x) < emitterParams.minVel) { vel.x = 0.0f }
@@ -49,7 +49,7 @@ class POrigin(rot: Vector2f, eOrigin: Vec3d, pOrigin: Vector3f) {
pos = eOrigin, pos = eOrigin,
rot = rot, rot = rot,
model = "minecraft:lime_concrete", model = "minecraft:lime_concrete",
tags = arrayOf("DEBUG", "P_DEBUG", "pOrigin_DEBUG"), tags = arrayOf("DEBUG", "P_DEBUG", "pOrigin_DEBUG", "BPS_UUID", SESSION_UUID.toString()),
brightnessOverride = 15, brightnessOverride = 15,
translation = Vector3f(pOrigin), translation = Vector3f(pOrigin),
scale = Vector3f(0.15f, 0.15f, 0.15f) scale = Vector3f(0.15f, 0.15f, 0.15f)
@@ -78,7 +78,7 @@ class PVel(rot: Vector2f, eOrigin: Vec3d, pOrigin: Vector3f, pVel: Vector3f, pSc
pos = eOrigin, pos = eOrigin,
rot = rot, rot = rot,
model = "minecraft:red_nether_brick_slab", //"minecraft:red_stained_glass", model = "minecraft:red_nether_brick_slab", //"minecraft:red_stained_glass",
tags = arrayOf("DEBUG", "P_DEBUG", "pVel_DEBUG"), tags = arrayOf("DEBUG", "P_DEBUG", "pVel_DEBUG", "BPS_UUID", SESSION_UUID.toString()),
brightnessOverride = 15, brightnessOverride = 15,
translation = Vector3f(pOrigin), translation = Vector3f(pOrigin),
scale = Vector3f(0.05f, max(max(pScale.x, pScale.y), pScale.z) + (pVel.x + pVel.y + pVel.z), 0.05f) scale = Vector3f(0.05f, max(max(pScale.x, pScale.y), pScale.z) + (pVel.x + pVel.y + pVel.z), 0.05f)
@@ -20,6 +20,7 @@ class ParticleEmitter(
private var allParticles: Array<Particle> = emptyArray() private var allParticles: Array<Particle> = emptyArray()
private var timeAlive = 0 private var timeAlive = 0
private var timeEmitted = 0
private var timePaused = 0 private var timePaused = 0
private var timesLooped = 0 private var timesLooped = 0
private var isPaused = false private var isPaused = false
@@ -28,13 +29,20 @@ class ParticleEmitter(
var isDead = false var isDead = false
private var debugTool = EmitterDebug(rot, pos, params.spawnPosOffset, params.shape)
suspend fun tick() { suspend fun tick() {
if (isTicking) { count() } if (isTicking) { count() }
if (isTicking) { addParticle() } if (isTicking) { addParticle() }
if (!isTicking && allParticles.isEmpty()) { isDead = true } if (!isTicking && allParticles.isEmpty()) { isDead = true; kill() }
processParticlesInBatches(100) processParticlesInBatches(100)
if (timeAlive == 0 && debug) { debugTool.spawn(world, pos, params.forceFields, params.spawnPosOffset) }
if (debug) { debugTool.update(rot, pos, params.spawnPosOffset) }
timeAlive += 1
} }
@@ -87,9 +95,9 @@ class ParticleEmitter(
private fun count() { private fun count() {
if (params.spawnDuration is ParamClasses.Duration.SingleBurst) { if (params.spawnDuration is ParamClasses.Duration.SingleBurst) {
val loopDur = (params.spawnDuration as ParamClasses.Duration.SingleBurst).loopDur val loopDur = (params.spawnDuration as ParamClasses.Duration.SingleBurst).loopDur
if (timeAlive >= loopDur) { isTicking = false; return } if (timeEmitted >= loopDur) { isTicking = false; return }
timeAlive += 1 timeEmitted += 1
} }
else if (params.spawnDuration is ParamClasses.Duration.MultiBurst) { else if (params.spawnDuration is ParamClasses.Duration.MultiBurst) {
@@ -97,19 +105,19 @@ class ParticleEmitter(
val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay
val loopCount = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopCount val loopCount = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopCount
if (timeAlive >= loopDur && timesLooped + 1 >= loopCount) { isTicking = false; return } if (timeEmitted >= loopDur && timesLooped + 1 >= loopCount) { isTicking = false; return }
if (timePaused >= loopDelay) { if (timePaused >= loopDelay) {
isPaused = false isPaused = false
timePaused = 0 timePaused = 0
timesLooped += 1 timesLooped += 1
} }
if (timeAlive >= loopDur && loopDelay > 0) { if (timeEmitted >= loopDur && loopDelay > 0) {
isPaused = true isPaused = true
timeAlive = 0 timeEmitted = 0
} }
if (!isPaused) { timeAlive += 1 } if (!isPaused) { timeEmitted += 1 }
else { timePaused += 1 } else { timePaused += 1 }
} }
@@ -121,12 +129,12 @@ class ParticleEmitter(
isPaused = false isPaused = false
timePaused = 0 timePaused = 0
} }
if (timeAlive >= loopDur && loopDelay > 0) { if (timeEmitted >= loopDur && loopDelay > 0) {
isPaused = true isPaused = true
timeAlive = 0 timeEmitted = 0
} }
if (!isPaused) { timeAlive += 1 } if (!isPaused) { timeEmitted += 1 }
else { timePaused += 1 } else { timePaused += 1 }
} }
} }
@@ -137,6 +145,7 @@ class ParticleEmitter(
particle.kill() particle.kill()
allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray() allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray()
} }
if (debug) { debugTool.kill() }
isTicking = false isTicking = false
isDead = true isDead = true
} }