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).
// 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 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.DisplayEntity
import com.bytedice.bde_particles.DisplayEntityProperties
import com.bytedice.bde_particles.SESSION_UUID
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Vec3d
import org.joml.Vector2f
@@ -14,26 +15,31 @@ class EmitterDebug(
eOrigin: Vec3d,
spawnPosOffset: Vector3f,
shape: SpawningShape,
forceFields: ParamClasses.ForceFieldArray
) {
private val eOrigin = EOrigin(rot, eOrigin)
private val shapeDebug = ShapeDebug(rot, eOrigin, spawnPosOffset, shape)
private val eOriginDebug = EOrigin(rot, eOrigin)
private val shapeDebug = ShapeDebug(eOrigin, spawnPosOffset, shape)
private val forceFieldDebug = ForceFieldDebug()
fun spawn(world: ServerWorld) {
eOrigin.spawn(world)
fun spawn(world: ServerWorld, eOrigin: Vec3d, forceFields: ParamClasses.ForceFieldArray, spawnPosOffset: Vector3f) {
eOriginDebug.spawn(world)
shapeDebug.spawn(world)
forceFieldDebug.makeArray(eOrigin, forceFields, spawnPosOffset)
forceFieldDebug.spawn(world)
}
fun update(
rot: Vector2f,
eOrigin: Vec3d,
pOrigin: Vector3f,
pVel: Vector3f,
pScale: Vector3f,
spawnPosOffset: Vector3f,
shape: SpawningShape,
spawnPosOffset: Vector3f
) {
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,
rot = rot,
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,
scale = Vector3f(0.25f, 0.25f, 0.25f)
)
)
))
fun spawn(world: ServerWorld) {
eOriginDE.spawn(world)
@@ -61,36 +66,52 @@ class EOrigin(rot: Vector2f, eOrigin: Vec3d) {
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) {
is SpawningShape.Circle -> Vector3f(shape.radius * 1.75f, shape.radius * 1.75f, 0.0f)
is SpawningShape.Sphere -> Vector3f(shape.radius * 1.75f, shape.radius * 1.75f, 0.0f)
is SpawningShape.Rect -> Vector3f(shape.size.x, 0.0f, shape.size.y)
is SpawningShape.Circle -> Vector3f(shape.radius * 1.75f * 2, shape.radius * 1.75f * 2, 0.0001f)
is SpawningShape.Sphere -> Vector3f(shape.radius * 1.75f * 2, shape.radius * 1.75f * 2, 0.0001f)
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)
else -> null
}
val shapeRot = if (shape is SpawningShape.Circle) { Vector4f(-0.707f, 0.0f, 0.0f, 0.707f) }
else { Vector4f(0.0f, 0.0f, 0.0f, 1.0f) }
val shapeRot = when (shape) {
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 position = eOrigin.add(
spawnPosOffset.x.toDouble(),
spawnPosOffset.y.toDouble(),
spawnPosOffset.z.toDouble()
)
DisplayEntity(DisplayEntityProperties(
pos = eOrigin.add(spawnPosOffset.x.toDouble(), spawnPosOffset.y.toDouble(), spawnPosOffset.z.toDouble()),
rot = rot,
model = if (shape is SpawningShape.Circle || shape is SpawningShape.Sphere) { "minecraft:sunflower" }
else { "minecraft:yellow_stained_glass" },
tags = arrayOf("DEBUG", "P_DEBUG", "shape_DEBUG"),
pos = position.add(spawnPosOffset.x.toDouble(), spawnPosOffset.y.toDouble(), spawnPosOffset.z.toDouble()),
model = determineModel(shape),
tags = arrayOf("DEBUG", "P_DEBUG", "shape_DEBUG", "BPS_UUID", SESSION_UUID.toString()),
brightnessOverride = 15,
billboard = if (shape !is SpawningShape.Sphere) { Billboard.FIXED } else { Billboard.CENTER },
translation = spawnPosOffset,
billboard = determineBillboard(shape),
leftRotation = shapeRot,
scale = shapeScale!!
scale = shapeScale ?: Vector3f(1f, 1f, 1f)
))
}
else { null }
} else null
fun spawn(world: ServerWorld) {
shapeDE?.spawn(world)
@@ -104,4 +125,43 @@ class ShapeDebug(rot: Vector2f, eOrigin: Vec3d, spawnPosOffset: Vector3f, shape:
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))
))
),
constVel = Vector3f(0.0f, 0.001f, 0.0f),
constVel = Vector3f(0.0f, 0.00001f, 0.0f),
drag = 0.075f,
minVel = 0.0f,
offsetCurve = ParamClasses.LerpValVec3f.Null,
@@ -43,14 +43,22 @@ class ParamClasses {
private val min: 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(
private val min: 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 {
@@ -60,8 +60,10 @@ class Particle(
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())
}
val shape = emitterParams.shape
val newSpawnPos = shape.random(shape)
@@ -222,7 +224,10 @@ class Particle(
private fun calcPos() : Pair<Vector3f, Vector3f> {
var vel = Vector3f(vel)
val constVel = emitterParams.constVel
if (constVel.x + constVel.y + constVel.z != 0.0f) {
vel = vel.add(emitterParams.constVel)
}
vel = vel.mul(1.0f - emitterParams.drag)
if (abs(vel.x) < emitterParams.minVel) { vel.x = 0.0f }
@@ -49,7 +49,7 @@ class POrigin(rot: Vector2f, eOrigin: Vec3d, pOrigin: Vector3f) {
pos = eOrigin,
rot = rot,
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,
translation = Vector3f(pOrigin),
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,
rot = rot,
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,
translation = Vector3f(pOrigin),
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 timeAlive = 0
private var timeEmitted = 0
private var timePaused = 0
private var timesLooped = 0
private var isPaused = false
@@ -28,13 +29,20 @@ class ParticleEmitter(
var isDead = false
private var debugTool = EmitterDebug(rot, pos, params.spawnPosOffset, params.shape)
suspend fun tick() {
if (isTicking) { count() }
if (isTicking) { addParticle() }
if (!isTicking && allParticles.isEmpty()) { isDead = true }
if (!isTicking && allParticles.isEmpty()) { isDead = true; kill() }
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() {
if (params.spawnDuration is ParamClasses.Duration.SingleBurst) {
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) {
@@ -97,19 +105,19 @@ class ParticleEmitter(
val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay
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) {
isPaused = false
timePaused = 0
timesLooped += 1
}
if (timeAlive >= loopDur && loopDelay > 0) {
if (timeEmitted >= loopDur && loopDelay > 0) {
isPaused = true
timeAlive = 0
timeEmitted = 0
}
if (!isPaused) { timeAlive += 1 }
if (!isPaused) { timeEmitted += 1 }
else { timePaused += 1 }
}
@@ -121,12 +129,12 @@ class ParticleEmitter(
isPaused = false
timePaused = 0
}
if (timeAlive >= loopDur && loopDelay > 0) {
if (timeEmitted >= loopDur && loopDelay > 0) {
isPaused = true
timeAlive = 0
timeEmitted = 0
}
if (!isPaused) { timeAlive += 1 }
if (!isPaused) { timeEmitted += 1 }
else { timePaused += 1 }
}
}
@@ -137,6 +145,7 @@ class ParticleEmitter(
particle.kill()
allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray()
}
if (debug) { debugTool.kill() }
isTicking = false
isDead = true
}