added some new params to Particle
This commit is contained in:
@@ -5,12 +5,17 @@ import com.bytedice.bde_particles.commands.KillAllEmitters
|
||||
import com.bytedice.bde_particles.commands.ManageEmitters
|
||||
import com.bytedice.bde_particles.items.ParticleEmitterTool
|
||||
import com.bytedice.bde_particles.particles.*
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.ModInitializer
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents
|
||||
import net.fabricmc.fabric.api.event.player.UseItemCallback
|
||||
import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory
|
||||
import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.decoration.DisplayEntity.BlockDisplayEntity
|
||||
@@ -23,6 +28,7 @@ import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.TypedActionResult
|
||||
import net.minecraft.world.GameRules
|
||||
import net.minecraft.world.World
|
||||
import org.joml.Vector2f
|
||||
import java.util.*
|
||||
@@ -34,19 +40,9 @@ import java.util.*
|
||||
// force field "config" option
|
||||
// blockCurve array not needing "minecraft:"
|
||||
|
||||
// Adding a parameter for an initial velocity from an origin point (you currently have to use force fields)
|
||||
|
||||
// Parameters
|
||||
// originOffset (Vec3f)
|
||||
// velocity based, relative coordinate based, or rotation based
|
||||
|
||||
// originOffsetCurve (curve)
|
||||
// spawnOffset (Vec3f)
|
||||
// velRot (bool)
|
||||
// rotates the particle to face the velocity
|
||||
|
||||
// spawnChance (Float)
|
||||
// shape: spawnOnlyOnEdge (Boolean)
|
||||
// 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.
|
||||
@@ -59,7 +55,12 @@ import java.util.*
|
||||
|
||||
|
||||
var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray()
|
||||
val sessionUuid: UUID = UUID.randomUUID()
|
||||
val SESSION_UUID: UUID = UUID.randomUUID()
|
||||
val GLOBAL_MAX_PARTICLES = GameRuleRegistry.register(
|
||||
"GlobalMaxParticles",
|
||||
GameRules.Category.UPDATES,
|
||||
GameRuleFactory.createIntRule(3000, 0)
|
||||
)
|
||||
|
||||
|
||||
class Bde_particles : ModInitializer {
|
||||
@@ -112,18 +113,23 @@ fun init() {
|
||||
|
||||
|
||||
fun tick(server: MinecraftServer) {
|
||||
for (world in server.worlds) {
|
||||
for (entity in world.iterateEntities()) {
|
||||
if (entity.type == EntityType.BLOCK_DISPLAY) {
|
||||
if (displayEntityContainsTag(entity as BlockDisplayEntity, "BPS_UUID")
|
||||
&& !displayEntityContainsTag(entity, sessionUuid.toString())) {
|
||||
runBlocking {
|
||||
server.worlds.map { world ->
|
||||
async {
|
||||
val blockDisplayEntities = world.iterateEntities()
|
||||
.filter { it.type == EntityType.BLOCK_DISPLAY }
|
||||
for (entity in blockDisplayEntities) {
|
||||
if (displayEntityContainsTag(entity as BlockDisplayEntity, "BPS_UUID")
|
||||
&& !displayEntityContainsTag(entity, SESSION_UUID.toString())) {
|
||||
|
||||
entity.kill()
|
||||
entity.kill()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
|
||||
|
||||
for (emitter in ALL_PARTICLE_EMITTERS) {
|
||||
if (emitter.isDead) {
|
||||
ALL_PARTICLE_EMITTERS = ALL_PARTICLE_EMITTERS.toMutableList().apply { remove(emitter) }.toTypedArray()
|
||||
@@ -154,6 +160,7 @@ fun onRightClick(player: PlayerEntity, world: ServerWorld, hand: Hand) : TypedAc
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { // TODO: map to new params
|
||||
val allParamsJson: MutableList<Map<String, Any?>> = mutableListOf()
|
||||
|
||||
@@ -161,7 +168,7 @@ fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { // TODO: map
|
||||
"shape" to params.shape
|
||||
)
|
||||
|
||||
allParamsJson.add(paramsJson)
|
||||
allParamsJson.add(paramsJson)
|
||||
|
||||
val emitterParamsJSON = mapOf(
|
||||
"maxCount" to params.maxCount,
|
||||
@@ -169,6 +176,7 @@ fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { // TODO: map
|
||||
|
||||
return emitterParamsJSON
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
fun displayEntityContainsTag(entity: BlockDisplayEntity, tag: String) : Boolean {
|
||||
|
||||
@@ -170,18 +170,9 @@ fun lerp(x: Float, y: Float, t: Float) : Float {
|
||||
}
|
||||
|
||||
|
||||
fun interpolateCurve(start: Vector3f, end: Vector3f, t: Float, curve: LerpCurves): Vector3f {
|
||||
val clampedT = t.coerceIn(0.0f, 1.0f)
|
||||
|
||||
val curvedT = curve.function(clampedT)
|
||||
|
||||
val interpolatedVec = Vector3f(
|
||||
lerp(start.x, end.x, curvedT),
|
||||
lerp(start.y, end.y, curvedT),
|
||||
lerp(start.z, end.z, curvedT)
|
||||
)
|
||||
|
||||
return interpolatedVec
|
||||
fun lerpArray(array: Array<Any>, t: Float, curve: LerpCurves = LerpCurves.Linear) : Any {
|
||||
val idx = round(lerp(0.0f, array.lastIndex.toFloat(), t) * curve.function(t)).toInt()
|
||||
return array[idx]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,37 @@ import java.util.concurrent.CompletableFuture
|
||||
// TODO: completely redesign
|
||||
|
||||
|
||||
/*
|
||||
// spawning
|
||||
maxCount: Int,
|
||||
spawnRate: Int,
|
||||
spawnChance: Float,
|
||||
spawnDuration: ParamClasses.Duration,
|
||||
spawnPosOffset: Vector3f,
|
||||
lifeTime: ParamClasses.PairInt,
|
||||
shape: SpawningShape,
|
||||
// init transforms
|
||||
offset: ParamClasses.PairVec3f,
|
||||
initRot: ParamClasses.PairVec3f,
|
||||
rotVel: ParamClasses.PairVec3f,
|
||||
rotWithVel: Boolean,
|
||||
initVel: ParamClasses.PairVec3f,
|
||||
initCenterVel: ParamClasses.PairFloat,
|
||||
initScale: ParamClasses.PairVec3f,
|
||||
scaleWithVel: Boolean,
|
||||
// velocity
|
||||
forceFields: Array<ForceField>,
|
||||
constVel: Vector3f,
|
||||
drag: Float,
|
||||
minVel: Float,
|
||||
// curves
|
||||
offsetCurve: ParamClasses.LerpVal,
|
||||
rotVelCurve: ParamClasses.LerpVal,
|
||||
scaleCurve: ParamClasses.LerpVal,
|
||||
blockCurve: Pair<Array<String>, LerpCurves>,
|
||||
*/
|
||||
|
||||
|
||||
// ManageEmitters
|
||||
// <create / remove / config / list>
|
||||
// create
|
||||
|
||||
@@ -4,32 +4,6 @@ import com.bytedice.bde_particles.LerpCurves
|
||||
import org.joml.Vector3f
|
||||
|
||||
|
||||
/**
|
||||
* EmitterParams defines the parameters for controlling how particles are emitted,
|
||||
* including settings for looping, spawn frequency, and maximum particle count.
|
||||
*
|
||||
* @param maxCount The maximum number of particles that can exist at any time. If set to 0 or below, no particles will spawn.
|
||||
* @param spawnRate The number of particles to spawn each tick. If set to 0 or below, no particles will spawn.
|
||||
* @param spawnChance The chance for a particle to spawn. 0.5 means a particle has a 50% chance of spawning. Values are between 0.0 and 1.0.
|
||||
* @param loopDur The duration, in ticks, that each emission loop lasts. A value of 0 or below makes the loop infinite, ignoring `loopDelay` and `loopCount`.
|
||||
* @param loopDelay The delay, in ticks, between consecutive loops. Negative values are treated as 0 (no delay).
|
||||
* @param loopCount The number of times the loop repeats. A value of 0 results in a single burst of particles. Values below 0 make the loop repeat indefinitely.
|
||||
* @param shape The spawning shape for the particles, such as `SpawningShape.Sphere()`.
|
||||
* @param initRot Specifies the initial random rotation range for the particles, in degrees, for the X, Y, and Z axes.
|
||||
* @param rotVel Specifies the random range for rotational velocity, in degrees per tick, for the X, Y, and Z axes.
|
||||
* @param rotTowardVel If true, particles will face the direction of velocity. It uses `initRot` as an offset rotation and ignores `rotVel`.
|
||||
* @param initVel Specifies the random range for the initial velocity, in units per tick, for the X, Y, and Z axes.
|
||||
* @param initScale Defines the random range for the initial particle size.
|
||||
* @param scaleTowardVel If true, particles will scale to the direction of velocity.
|
||||
* @param forceFields An array of force fields applied to particles, affecting their motion during their lifetime.
|
||||
* @param constVel A constant velocity vector applied to particles each tick, useful for effects like gravity.
|
||||
* @param drag A drag coefficient that slows down particle velocity over time. Calculated as `velocity * (1.0 - drag)`.
|
||||
* @param minVel The minimum speed a particle must maintain. Particles slower than this value are stopped. Values <= 0 disable this check.
|
||||
* @param lifeTime Specifies the range for particle lifetime, in ticks. Values below 1 prevent particles from spawning.
|
||||
* @param rotVelCurve A curve defining how rotational velocity evolves during the particle's lifetime. Multiplies the `rotVel` values.
|
||||
* @param scaleCurve A curve defining how the particle's size evolves over its lifetime. Multiplies the `initScale` values.
|
||||
* @param blockCurve Specifies an array of block names that the particle transitions through during its lifetime, combined with a curve to control transitions. Defaults to "minecraft:air" if empty.
|
||||
*/
|
||||
data class EmitterParams (
|
||||
// spawning
|
||||
var maxCount: Int,
|
||||
@@ -68,7 +42,7 @@ data class EmitterParams (
|
||||
spawnDuration = ParamClasses.Duration.SingleBurst(25),
|
||||
spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f),
|
||||
lifeTime = ParamClasses.PairInt(25, 40),
|
||||
shape = SpawningShape.Circle(3.0f),
|
||||
shape = SpawningShape.Circle(3.0f, false),
|
||||
offset = ParamClasses.PairVec3f.Uniform(-0.5f, -0.5f),
|
||||
initRot = ParamClasses.PairVec3f.Null,
|
||||
rotVel = ParamClasses.PairVec3f.NonUniform(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f),
|
||||
|
||||
@@ -9,61 +9,77 @@ import org.joml.Vector4f
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
// TODO: consider moving most params to emitter (such as shape, and forceFields) to store less data
|
||||
// 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 val initOffset = Vector3f(-0.5f, -0.5f, -0.5f)
|
||||
private var offset = Vector3f(0.0f, 0.0f, 0.0f)
|
||||
private var pos = Vec3d(0.0, 0.0, 0.0)
|
||||
private var entityRot = Vector2f(0.0f, 0.0f)
|
||||
class Particle(private val emitterParams: EmitterParams, private var entityPos: Vec3d, private val entityRot: Vector2f) {
|
||||
private var pos = Vector3f(0.0f, 0.0f, 0.0f)
|
||||
private var blockDisplay: DisplayEntity = DisplayEntity(DisplayEntityProperties())
|
||||
private var timeAlive = 0
|
||||
private var isInit = false
|
||||
var isDead = false
|
||||
|
||||
private var rot = emitterParams.initRot.randomize()
|
||||
private val rotVel = emitterParams.rotVel.randomize()
|
||||
private var vel = emitterParams.initVel.randomize()
|
||||
|
||||
private val scaleAny: Any = when (emitterParams.initScale) {
|
||||
is ParamClasses.RandScale.Uniform -> { (emitterParams.initScale as ParamClasses.RandScale.Uniform).randomize() }
|
||||
is ParamClasses.RandScale.NonUniform -> { (emitterParams.initScale as ParamClasses.RandScale.NonUniform).randomize() }
|
||||
// params
|
||||
private val originOffset = when (val offset = emitterParams.offset) {
|
||||
is ParamClasses.PairVec3f.Uniform -> offset.randomize()
|
||||
is ParamClasses.PairVec3f.NonUniform -> offset.randomize()
|
||||
ParamClasses.PairVec3f.Null -> Vector3f(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
|
||||
private var rot = when (val initRot = emitterParams.initRot) {
|
||||
is ParamClasses.PairVec3f.Uniform -> initRot.randomize()
|
||||
is ParamClasses.PairVec3f.NonUniform -> initRot.randomize()
|
||||
ParamClasses.PairVec3f.Null -> Vector3f(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
|
||||
private var rotVel = when (val initRotVel = emitterParams.rotVel) {
|
||||
is ParamClasses.PairVec3f.Uniform -> initRotVel.randomize()
|
||||
is ParamClasses.PairVec3f.NonUniform -> initRotVel.randomize()
|
||||
ParamClasses.PairVec3f.Null -> Vector3f(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
|
||||
private var vel = when (val initVel = emitterParams.initVel) {
|
||||
is ParamClasses.PairVec3f.Uniform -> initVel.randomize()
|
||||
is ParamClasses.PairVec3f.NonUniform -> initVel.randomize()
|
||||
ParamClasses.PairVec3f.Null -> Vector3f(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
|
||||
private var scale = when (val initScale = emitterParams.initScale) {
|
||||
is ParamClasses.PairVec3f.Uniform -> initScale.randomize()
|
||||
is ParamClasses.PairVec3f.NonUniform -> initScale.randomize()
|
||||
ParamClasses.PairVec3f.Null -> Vector3f(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
private var scale = if (scaleAny is Vector3f) { scaleAny } else { Vector3f(scaleAny as Float, scaleAny, scaleAny) }
|
||||
|
||||
private var blockDisplay: DisplayEntity? = null
|
||||
private var lifeTime = emitterParams.lifeTime.randomize()
|
||||
|
||||
var isDead = false
|
||||
private var timeAlive = 0
|
||||
|
||||
private var isInit = false
|
||||
|
||||
|
||||
fun init(pos: Vec3d, rot: Vector2f) { // TODO: replace initOffset with originOffset param
|
||||
this.pos = pos
|
||||
this.entityRot = rot
|
||||
fun init() {
|
||||
val so = emitterParams.spawnPosOffset
|
||||
entityPos = entityPos.add(so.x.toDouble(), so.y.toDouble(), so.z.toDouble())
|
||||
|
||||
val shape = emitterParams.shape
|
||||
val newSpawnPos: Vector3f
|
||||
val newSpawnPos = shape.random(shape)
|
||||
|
||||
when (shape) {
|
||||
is SpawningShape.Circle -> { val posInCircle = randomInCircle(shape.radius); newSpawnPos = Vector3f(posInCircle.x, 0.0f, posInCircle.y) }
|
||||
is SpawningShape.Sphere -> { newSpawnPos = randomInSphere(shape.radius) }
|
||||
is SpawningShape.Rect -> { val posInRect = randomInRect(shape.size); newSpawnPos = Vector3f(posInRect.x, 0.0f, posInRect.y) }
|
||||
is SpawningShape.Cube -> { newSpawnPos = randomInCube(shape.size) }
|
||||
is SpawningShape.Point -> { newSpawnPos = Vector3f(0.0f, 0.0f, 0.0f) }
|
||||
}
|
||||
pos = newSpawnPos
|
||||
|
||||
offset = newSpawnPos
|
||||
val dirFromCenter = entityPos.subtract(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()).normalize()
|
||||
val forceFromCenter = dirFromCenter.multiply(emitterParams.initCenterVel.randomize().toDouble())
|
||||
vel.add(forceFromCenter.x.toFloat(), forceFromCenter.y.toFloat(), forceFromCenter.z.toFloat())
|
||||
|
||||
val (quatRot, newOffset) = calcTransformOffset(this.rot, initOffset, scale)
|
||||
val (quatRot, newPos) = calcTransformOffset(this.rot, originOffset, scale)
|
||||
|
||||
val properties = DisplayEntityProperties(
|
||||
pos = this.pos,
|
||||
rot = rot,
|
||||
blockType = emitterParams.blockCurve.first[0], // TODO: curve this
|
||||
translation = newOffset.add(offset),
|
||||
pos = entityPos,
|
||||
rot = entityRot,
|
||||
blockType = lerpArray(emitterParams.blockCurve.first as Array<Any>, 0.0f, emitterParams.blockCurve.second) as String,
|
||||
translation = newPos.add(pos),
|
||||
leftRotation = quatRot,
|
||||
scale = scale,
|
||||
tags = arrayOf("BPS_Particle", "BPS_UUID", sessionUuid.toString())
|
||||
tags = arrayOf("BPS_Particle", "BPS_UUID", SESSION_UUID.toString())
|
||||
)
|
||||
|
||||
blockDisplay = DisplayEntity(properties)
|
||||
@@ -73,7 +89,7 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
|
||||
|
||||
fun spawn(world: ServerWorld) {
|
||||
if (isInit) { blockDisplay?.spawn(world) }
|
||||
if (isInit) { blockDisplay.spawn(world) }
|
||||
else { error("Particle is not initialized before being spawned. Please use Particle.init() to initialize it.") }
|
||||
}
|
||||
|
||||
@@ -83,9 +99,9 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
|
||||
val newProperties = calcNewProperties()
|
||||
|
||||
blockDisplay?.updateProperties(newProperties)
|
||||
blockDisplay.updateProperties(newProperties)
|
||||
|
||||
if (timeAlive > lifeTime) { isDead = true; blockDisplay?.kill() }
|
||||
if (timeAlive > lifeTime) { isDead = true; blockDisplay.kill() }
|
||||
|
||||
timeAlive += 1
|
||||
}
|
||||
@@ -105,52 +121,46 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
|
||||
|
||||
private fun calcNewProperties() : DisplayEntityProperties {
|
||||
|
||||
val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f)
|
||||
|
||||
val newBlock = calcBlockCurve(timeAliveClamped)
|
||||
val (newVel, newOffset) = calcOffset()
|
||||
val newRot = calcRot(timeAliveClamped)
|
||||
val newScale = calcSize(timeAliveClamped)
|
||||
val newBlock = lerpArray(emitterParams.blockCurve.first as Array<Any>, timeAliveClamped, emitterParams.blockCurve.second) as String
|
||||
val (newVel, newPos) = calcPos()
|
||||
val newRot = calcRot(timeAliveClamped)
|
||||
val newScale = calcScale(timeAliveClamped)
|
||||
val newOriginOffset = Vector3f(originOffset.mul(emitterParams.offsetCurve.lerpToVector3f(timeAliveClamped)))
|
||||
|
||||
vel = newVel
|
||||
offset = newOffset
|
||||
rot = newRot
|
||||
vel = newVel
|
||||
pos = newPos
|
||||
rot = newRot
|
||||
|
||||
for (forceField in emitterParams.forceFields) {
|
||||
if (forceField.shape is ForceFieldShape.Sphere) { vel.add(sphereSdfVel(forceField)) }
|
||||
else if (forceField.shape is ForceFieldShape.Cube) { vel.add(cubeSdfVel(forceField)) }
|
||||
}
|
||||
|
||||
val (quatRot, transformOffset) = calcTransformOffset(newRot, initOffset, newScale)
|
||||
val combinedOffset = transformOffset.add(newOffset)
|
||||
val (quatRot, transformOffset) = calcTransformOffset(newRot, originOffset, newScale)
|
||||
val combinedOffset = transformOffset.add(newPos)
|
||||
|
||||
val newProperties = DisplayEntityProperties(
|
||||
pos = pos,
|
||||
pos = entityPos,
|
||||
blockType = newBlock,
|
||||
translation = combinedOffset,
|
||||
leftRotation = quatRot,
|
||||
scale = newScale,
|
||||
tags = arrayOf("BPS_Particle", "BPS_UUID", sessionUuid.toString())
|
||||
tags = arrayOf("BPS_Particle", "BPS_UUID", SESSION_UUID.toString())
|
||||
)
|
||||
|
||||
return newProperties
|
||||
}
|
||||
|
||||
|
||||
private fun calcBlockCurve(t: Float) : String {
|
||||
val newBlockIdx = round(lerp(0.0f, emitterParams.blockCurve.first.lastIndex.toFloat(), t)).toInt()
|
||||
return emitterParams.blockCurve.first[newBlockIdx]
|
||||
}
|
||||
|
||||
|
||||
private fun sphereSdfVel(forceField: ForceField) : Vector3f {
|
||||
if (forceField.shape !is ForceFieldShape.Sphere) { return Vector3f(0.0f, 0.0f, 0.0f) }
|
||||
val shape = forceField.shape
|
||||
|
||||
val sdfVal = sdfSphere(forceField.pos, shape.radius, offset)
|
||||
val sdfVal = sdfSphere(forceField.pos, shape.radius, pos)
|
||||
val normalizedSdf = normalizeSdf(sdfVal, shape.radius)
|
||||
val velDir = Vector3f(offset).sub(forceField.pos).normalize()
|
||||
val velDir = Vector3f(pos).sub(forceField.pos).normalize()
|
||||
val velMul = if (sdfVal > 0) { 0.0f }
|
||||
else { lerp(shape.force.second, shape.force.second, 1 - normalizedSdf) }
|
||||
|
||||
@@ -162,7 +172,7 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
if (forceField.shape !is ForceFieldShape.Cube) { return Vector3f(0.0f, 0.0f, 0.0f) }
|
||||
val shape = forceField.shape
|
||||
|
||||
val sdfVal = sdfCube(forceField.pos, shape.size, offset)
|
||||
val sdfVal = sdfCube(forceField.pos, shape.size, pos)
|
||||
val velDir = if (sdfVal < 0) { shape.dir.mul(shape.force) }
|
||||
else { Vector3f(0.0f, 0.0f, 0.0f) }
|
||||
|
||||
@@ -181,7 +191,7 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
}
|
||||
|
||||
|
||||
private fun calcOffset() : Pair<Vector3f, Vector3f> {
|
||||
private fun calcPos() : Pair<Vector3f, Vector3f> {
|
||||
var vel = Vector3f(vel)
|
||||
vel = vel.add(emitterParams.constVel)
|
||||
vel = vel.mul(1.0f - emitterParams.drag)
|
||||
@@ -190,13 +200,13 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
if (abs(vel.y) < emitterParams.minVel) { vel.y = 0.0f }
|
||||
if (abs(vel.z) < emitterParams.minVel) { vel.z = 0.0f }
|
||||
|
||||
var newOffset = Vector3f(offset)
|
||||
newOffset = newOffset.add(vel)
|
||||
return Pair(vel, newOffset)
|
||||
var newPos = Vector3f(pos)
|
||||
newPos = newPos.add(vel)
|
||||
return Pair(vel, newPos)
|
||||
}
|
||||
|
||||
|
||||
private fun calcSize(t: Float) : Vector3f {
|
||||
private fun calcScale(t: Float) : Vector3f {
|
||||
val newScale = Vector3f(scale)
|
||||
newScale.mul(emitterParams.scaleCurve.lerpToVector3f(t))
|
||||
return scale
|
||||
@@ -204,6 +214,6 @@ class Particle(private val emitterParams: EmitterParams) {
|
||||
|
||||
|
||||
fun kill() {
|
||||
if (!isDead) { blockDisplay?.kill() }
|
||||
if (!isDead) { blockDisplay.kill() }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
package com.bytedice.bde_particles.particles
|
||||
|
||||
import com.bytedice.bde_particles.randomFloatBetween
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import org.joml.Vector2f
|
||||
import org.joml.Vector3f
|
||||
|
||||
class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private val world: ServerWorld, private val params: EmitterParams) {
|
||||
|
||||
private var allParticles: Array<Particle> = emptyArray()
|
||||
|
||||
private var timeAlive = 0
|
||||
private var loopCount = 0
|
||||
private var loopDelay = 0
|
||||
private var loopDur = 0
|
||||
private var loopDelayActive = false
|
||||
private var timePaused = 0
|
||||
private var timesLooped = 0
|
||||
private var isPaused = false
|
||||
|
||||
private var isTicking = true
|
||||
|
||||
@@ -33,13 +32,16 @@ class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private
|
||||
|
||||
|
||||
private fun addParticle() {
|
||||
if (loopDelayActive) { return }
|
||||
if (isPaused) { return }
|
||||
|
||||
repeat(params.spawnRate) {
|
||||
if (allParticles.size >= params.maxCount) { return }
|
||||
|
||||
val particle = Particle(params)
|
||||
particle.init(pos, rot)
|
||||
val randomSpawnChance = randomFloatBetween(0.0f, params.spawnChance.coerceIn(0.0f, 1.0f))
|
||||
if (randomSpawnChance > params.spawnChance) { return }
|
||||
|
||||
val particle = Particle(params, pos, rot)
|
||||
particle.init()
|
||||
particle.spawn(world)
|
||||
|
||||
allParticles += particle
|
||||
@@ -48,44 +50,30 @@ class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private
|
||||
|
||||
|
||||
private fun count() {
|
||||
// this is the only section I have comments
|
||||
// because im so damn good at cooking spaghetti
|
||||
if (params.spawnDuration is ParamClasses.Duration.SingleBurst) {
|
||||
val loopDur = (params.spawnDuration as ParamClasses.Duration.SingleBurst).loopDur
|
||||
if (timeAlive >= loopDur) { isTicking = false; return }
|
||||
|
||||
// if max duration is 0 or less, skip counting, its infinite
|
||||
if (params.loopDur <= 0) { return }
|
||||
|
||||
// if the duration is greater than max duration, add 1 loopCount
|
||||
// return if max loopCount is 0
|
||||
// if the delay is greater than 0 then enable it
|
||||
if (loopDur >= params.loopDur) {
|
||||
if (loopCount == 0) {
|
||||
isTicking = false
|
||||
return
|
||||
}
|
||||
loopCount += 1
|
||||
loopDur = 0
|
||||
if (params.loopDelay > 0) { loopDelayActive = true }
|
||||
timeAlive += 1
|
||||
}
|
||||
|
||||
// if the delay is greater than max delay, disable the delay
|
||||
else if (loopDelay >= params.loopDelay) {
|
||||
loopDelay = 0
|
||||
loopDelayActive = false
|
||||
else if (params.spawnDuration is ParamClasses.Duration.MultiBurst) {
|
||||
val loopDur = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDur
|
||||
val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay
|
||||
val loopCount = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopCount
|
||||
|
||||
if (timeAlive >= loopDur && timesLooped >= loopCount) { isTicking = false; return }
|
||||
|
||||
if (timePaused >= loopDelay) { isPaused = false; timePaused = 0 }
|
||||
if (timeAlive >= loopDur && loopDelay > 0) { isPaused = true; timeAlive = 0 }
|
||||
|
||||
if (!isPaused) { timeAlive += 1 }
|
||||
else { timePaused += 1 }
|
||||
}
|
||||
|
||||
// if loopCount is greater than max loopCount then stop immediately
|
||||
// if max loopCount is below 0 then don't do anything, loop infinitely
|
||||
if (loopCount > 0 && loopCount >= params.loopCount) {
|
||||
isTicking = false
|
||||
else if (params.spawnDuration is ParamClasses.Duration.InfiniteLoop) {
|
||||
return
|
||||
}
|
||||
|
||||
// if delay isn't active, add 1 to duration
|
||||
// if delay is active, add 1 to delay
|
||||
if (!loopDelayActive) { loopDur += 1 }
|
||||
else { loopDelay += 1 }
|
||||
|
||||
timeAlive += 1
|
||||
}
|
||||
|
||||
|
||||
@@ -97,4 +85,7 @@ class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private
|
||||
isTicking = false
|
||||
isDead = true
|
||||
}
|
||||
fun stopTicking() {
|
||||
isTicking = false
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.bytedice.bde_particles.particles
|
||||
|
||||
|
||||
@@ -10,9 +8,13 @@ val forbiddenIds = arrayOf("", "NULL")
|
||||
fun addToRegister(id: String, params: EmitterParams) : Pair<String, Boolean> {
|
||||
val newId = replaceForbiddenChars(id.replace(" ", "_"))
|
||||
|
||||
if (idRegister.containsKey(newId)) { return Pair(newId, false) }
|
||||
if (idRegister.containsKey(newId)) {
|
||||
println("BPS - Failed to register Emitter ID \"$newId\" as it already exists!")
|
||||
return Pair(newId, false)
|
||||
}
|
||||
|
||||
idRegister[id] = params
|
||||
println("BPS - Registered Emitter ID \"$newId\".")
|
||||
return Pair(newId, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
import kotlin.random.Random
|
||||
|
||||
sealed class SpawningShape {
|
||||
class Circle(val radius: Float = 1.0f) : SpawningShape() {
|
||||
sealed class SpawningShape() {
|
||||
class Circle(private val radius: Float, val spawnOnEdge: Boolean) : SpawningShape() {
|
||||
fun randomWithin(): Vector3f {
|
||||
val randRadius = radius * sqrt(randomFloatBetween(0.0f, 1.0f))
|
||||
val randAngle = randomFloatBetween(0.0f, Math.PI.toFloat() * 2)
|
||||
@@ -23,7 +23,7 @@ sealed class SpawningShape {
|
||||
}
|
||||
}
|
||||
|
||||
class Rect(val size: Vector2f = Vector2f(1.0f, 1.0f)) : SpawningShape() {
|
||||
class Rect(private val size: Vector2f, val spawnOnEdge: Boolean) : SpawningShape() {
|
||||
fun randomWithin(): Vector3f {
|
||||
return Vector3f(randomFloatBetween(0.0f, size.x), 0.0f, randomFloatBetween(0.0f, size.y))
|
||||
}
|
||||
@@ -41,7 +41,7 @@ sealed class SpawningShape {
|
||||
}
|
||||
}
|
||||
|
||||
class Cube(val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f)) : SpawningShape() {
|
||||
class Cube(private val size: Vector3f, val spawnOnEdge: Boolean) : SpawningShape() {
|
||||
fun randomWithin(): Vector3f {
|
||||
return Vector3f(
|
||||
randomFloatBetween(0.0f, size.x),
|
||||
@@ -73,7 +73,7 @@ sealed class SpawningShape {
|
||||
}
|
||||
}
|
||||
|
||||
class Sphere(val radius: Float = 1.0f) : SpawningShape() {
|
||||
class Sphere(private val radius: Float, val spawnOnEdge: Boolean) : SpawningShape() {
|
||||
fun randomWithin(): Vector3f {
|
||||
val randRadius = radius * Random.nextDouble().pow(1.0 / 3.0)
|
||||
|
||||
@@ -103,6 +103,16 @@ sealed class SpawningShape {
|
||||
}
|
||||
|
||||
data object Point : SpawningShape()
|
||||
|
||||
fun random(shape: SpawningShape) : Vector3f {
|
||||
return when (shape) {
|
||||
is Circle -> if (!shape.spawnOnEdge) { shape.randomWithin() } else { shape.randomOnEdge() }
|
||||
is Sphere -> if (!shape.spawnOnEdge) { shape.randomWithin() } else { shape.randomOnEdge() }
|
||||
is Rect -> if (!shape.spawnOnEdge) { shape.randomWithin() } else { shape.randomOnEdge() }
|
||||
is Cube -> if (!shape.spawnOnEdge) { shape.randomWithin() } else { shape.randomOnEdge() }
|
||||
is Point -> Vector3f(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user