added emitter debug tools. More testing required before 1.0!
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user