added ticking logic to particles
This commit is contained in:
@@ -11,17 +11,15 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents
|
|||||||
import net.fabricmc.fabric.api.event.player.UseItemCallback
|
import net.fabricmc.fabric.api.event.player.UseItemCallback
|
||||||
import net.minecraft.entity.player.PlayerEntity
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.server.MinecraftServer
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity
|
import net.minecraft.server.network.ServerPlayerEntity
|
||||||
import net.minecraft.server.world.ServerWorld
|
import net.minecraft.server.world.ServerWorld
|
||||||
import net.minecraft.text.Text
|
|
||||||
import net.minecraft.util.ActionResult
|
import net.minecraft.util.ActionResult
|
||||||
import net.minecraft.util.Hand
|
import net.minecraft.util.Hand
|
||||||
import net.minecraft.util.TypedActionResult
|
import net.minecraft.util.TypedActionResult
|
||||||
import net.minecraft.world.World
|
import net.minecraft.world.World
|
||||||
|
|
||||||
|
|
||||||
// const val ALL_PARTICLE_EMITTERS: Array<> // ParticleEmitter
|
var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = emptyArray()
|
||||||
|
|
||||||
data class Config(
|
data class Config(
|
||||||
val cooldown: Int = 1
|
val cooldown: Int = 1
|
||||||
@@ -56,12 +54,17 @@ class Bde_particles : ModInitializer {
|
|||||||
|
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
addToParticleRegister("DEBUG", ParticleEmitterParams())
|
addToParticleRegister("DEBUG", ParticleEmitterParams(loopDur = 20))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun tick() {
|
fun tick() {
|
||||||
|
for (emitter in ALL_PARTICLE_EMITTERS) {
|
||||||
|
if (emitter.isDead) {
|
||||||
|
ALL_PARTICLE_EMITTERS = ALL_PARTICLE_EMITTERS.toMutableList().apply { remove(emitter) }.toTypedArray()
|
||||||
|
}
|
||||||
|
else { emitter.tick() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +83,7 @@ fun onRightClick(player: PlayerEntity, world: ServerWorld, hand: Hand) : TypedAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
val emitter = ParticleEmitter(hitResult.pos, world, emitterParams)
|
val emitter = ParticleEmitter(hitResult.pos, world, emitterParams)
|
||||||
emitter.tick()
|
ALL_PARTICLE_EMITTERS += emitter
|
||||||
|
|
||||||
return TypedActionResult(ActionResult.PASS, handItem)
|
return TypedActionResult(ActionResult.PASS, handItem)
|
||||||
}
|
}
|
||||||
@@ -5,15 +5,17 @@ import net.minecraft.entity.decoration.DisplayEntity.BlockDisplayEntity
|
|||||||
import net.minecraft.nbt.NbtCompound
|
import net.minecraft.nbt.NbtCompound
|
||||||
import net.minecraft.nbt.NbtFloat
|
import net.minecraft.nbt.NbtFloat
|
||||||
import net.minecraft.nbt.NbtList
|
import net.minecraft.nbt.NbtList
|
||||||
|
import net.minecraft.nbt.NbtString
|
||||||
import net.minecraft.server.world.ServerWorld
|
import net.minecraft.server.world.ServerWorld
|
||||||
|
|
||||||
class DisplayEntity(val properties: DisplayEntityProperties?) {
|
class DisplayEntity(val properties: DisplayEntityProperties?) {
|
||||||
private var hasSpawned = false
|
private var hasSpawned = false
|
||||||
|
private var entity: BlockDisplayEntity? = null
|
||||||
|
|
||||||
fun spawn(world: ServerWorld) : BlockDisplayEntity? {
|
fun spawn(world: ServerWorld) : BlockDisplayEntity? {
|
||||||
if (this.hasSpawned || properties == null) { return null }
|
if (hasSpawned || properties == null) { return null }
|
||||||
|
|
||||||
val e = updateProperties(world, this.properties)
|
val e = updateProperties(world, properties)
|
||||||
world.spawnEntity(e)
|
world.spawnEntity(e)
|
||||||
|
|
||||||
this.hasSpawned = true
|
this.hasSpawned = true
|
||||||
@@ -23,8 +25,18 @@ class DisplayEntity(val properties: DisplayEntityProperties?) {
|
|||||||
fun updateProperties(world: ServerWorld, properties: DisplayEntityProperties) : BlockDisplayEntity {
|
fun updateProperties(world: ServerWorld, properties: DisplayEntityProperties) : BlockDisplayEntity {
|
||||||
val e = BlockDisplayEntity(EntityType.BLOCK_DISPLAY, world)
|
val e = BlockDisplayEntity(EntityType.BLOCK_DISPLAY, world)
|
||||||
|
|
||||||
|
// TODO: add tag nbt and kill all particles when server starts
|
||||||
val nbt = NbtCompound().apply {
|
val nbt = NbtCompound().apply {
|
||||||
|
val tagList = NbtList()
|
||||||
|
|
||||||
|
properties.tags.forEach { tag ->
|
||||||
|
tagList.add(NbtString.of(tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
put("Tags", tagList)
|
||||||
|
|
||||||
put("block_state", NbtCompound().apply { putString("Name", properties.blockType) })
|
put("block_state", NbtCompound().apply { putString("Name", properties.blockType) })
|
||||||
|
|
||||||
put("transformation", NbtCompound().apply {
|
put("transformation", NbtCompound().apply {
|
||||||
val offsetList = NbtList().apply {
|
val offsetList = NbtList().apply {
|
||||||
add(NbtFloat.of(properties.translation.x))
|
add(NbtFloat.of(properties.translation.x))
|
||||||
@@ -60,6 +72,11 @@ class DisplayEntity(val properties: DisplayEntityProperties?) {
|
|||||||
|
|
||||||
e.refreshPositionAndAngles(properties.pos.x, properties.pos.y, properties.pos.z, properties.rot.x, properties.rot.y)
|
e.refreshPositionAndAngles(properties.pos.x, properties.pos.y, properties.pos.z, properties.rot.x, properties.rot.y)
|
||||||
|
|
||||||
|
entity = e
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun kill() {
|
||||||
|
entity?.kill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ data class DisplayEntityProperties (
|
|||||||
val rot: Vector2f = Vector2f(0.0f, 0.0f),
|
val rot: Vector2f = Vector2f(0.0f, 0.0f),
|
||||||
|
|
||||||
val blockType: String = "minecraft:purple_concrete",
|
val blockType: String = "minecraft:purple_concrete",
|
||||||
|
val tags: Array<String> = emptyArray(),
|
||||||
|
|
||||||
val translation: Vector3f = Vector3f(-0.5f, -0.5f, -0.5f),
|
val translation: Vector3f = Vector3f(-0.5f, -0.5f, -0.5f),
|
||||||
val leftRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
val leftRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||||
|
|||||||
@@ -67,24 +67,20 @@ fun quatToEuler(quat: Vector4f): Vector3f {
|
|||||||
val z = quat.z
|
val z = quat.z
|
||||||
val w = quat.w
|
val w = quat.w
|
||||||
|
|
||||||
// Roll (X-axis rotation)
|
val sinRCosP = 2 * (w * x + y * z)
|
||||||
val sinr_cosp = 2 * (w * x + y * z)
|
val cosRCosP = 1 - 2 * (x * x + y * y)
|
||||||
val cosr_cosp = 1 - 2 * (x * x + y * y)
|
val roll = atan2(sinRCosP, cosRCosP)
|
||||||
val roll = atan2(sinr_cosp, cosr_cosp)
|
|
||||||
|
|
||||||
// Pitch (Y-axis rotation)
|
val sinP = 2 * (w * y - z * x)
|
||||||
val sinp = 2 * (w * y - z * x)
|
val pitch = if (abs(sinP) >= 1) {
|
||||||
val pitch = if (kotlin.math.abs(sinp) >= 1) {
|
sign(sinP) * (Math.PI.toFloat() / 2)
|
||||||
// Use 90 degrees if out of range
|
|
||||||
kotlin.math.sign(sinp) * (Math.PI.toFloat() / 2)
|
|
||||||
} else {
|
} else {
|
||||||
asin(sinp)
|
asin(sinP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yaw (Z-axis rotation)
|
val sinYCosP = 2 * (w * z + x * y)
|
||||||
val siny_cosp = 2 * (w * z + x * y)
|
val cosYCosP = 1 - 2 * (y * y + z * z)
|
||||||
val cosy_cosp = 1 - 2 * (y * y + z * z)
|
val yaw = atan2(sinYCosP, cosYCosP)
|
||||||
val yaw = atan2(siny_cosp, cosy_cosp)
|
|
||||||
|
|
||||||
return Vector3f(roll, pitch, yaw)
|
return Vector3f(roll, pitch, yaw)
|
||||||
}
|
}
|
||||||
@@ -146,3 +142,8 @@ fun transformOffsetByQuat(offset: Vector3f, rotation: Vector4f): Vector3f {
|
|||||||
fun transformOffsetByScale(offset: Vector3f, scale: Vector3f): Vector3f {
|
fun transformOffsetByScale(offset: Vector3f, scale: Vector3f): Vector3f {
|
||||||
return offset.mul(scale)
|
return offset.mul(scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun lerp(x: Float, y: Float, t: Float) : Float {
|
||||||
|
return x + (y - x) * t
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.bytedice.bde_particles.particle
|
||||||
|
|
||||||
|
data class ForceField (
|
||||||
|
val x: Float = 0.0f,
|
||||||
|
val y: Float = 0.0f,
|
||||||
|
val z: Float = 0.0f,
|
||||||
|
val minForce: Float = 0.0f,
|
||||||
|
val maxForce: Float = 0.0f,
|
||||||
|
val radius: Float = -1.0f
|
||||||
|
)
|
||||||
@@ -6,57 +6,89 @@ import com.bytedice.bde_particles.math.*
|
|||||||
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.Vector3f
|
import org.joml.Vector3f
|
||||||
|
import kotlin.math.round
|
||||||
|
|
||||||
|
|
||||||
class Particle(particleParams: ParticleParams) {
|
class Particle(private val particleParams: ParticleParams) {
|
||||||
val rot = Vector3f(
|
private val rot = Vector3f(
|
||||||
randomFloatBetween(particleParams.rotRandom.first.x, particleParams.rotRandom.second.x),
|
randomFloatBetween(particleParams.rotRandom.first.x, particleParams.rotRandom.second.x),
|
||||||
randomFloatBetween(particleParams.rotRandom.first.y, particleParams.rotRandom.second.x),
|
randomFloatBetween(particleParams.rotRandom.first.y, particleParams.rotRandom.second.x),
|
||||||
randomFloatBetween(particleParams.rotRandom.first.z, particleParams.rotRandom.second.z)
|
randomFloatBetween(particleParams.rotRandom.first.z, particleParams.rotRandom.second.z)
|
||||||
)
|
)
|
||||||
val rotVel = Vector3f(
|
private val rotVel = Vector3f(
|
||||||
randomFloatBetween(particleParams.rotVelRandom.first.x, particleParams.rotVelRandom.second.x),
|
randomFloatBetween(particleParams.rotVelRandom.first.x, particleParams.rotVelRandom.second.x),
|
||||||
randomFloatBetween(particleParams.rotVelRandom.first.y, particleParams.rotVelRandom.second.x),
|
randomFloatBetween(particleParams.rotVelRandom.first.y, particleParams.rotVelRandom.second.x),
|
||||||
randomFloatBetween(particleParams.rotVelRandom.first.z, particleParams.rotVelRandom.second.z)
|
randomFloatBetween(particleParams.rotVelRandom.first.z, particleParams.rotVelRandom.second.z)
|
||||||
)
|
)
|
||||||
val scale = Vector3f(
|
private val scale = Vector3f(
|
||||||
randomFloatBetween(particleParams.sizeRandom.first.x, particleParams.sizeRandom.second.x),
|
randomFloatBetween(particleParams.sizeRandom.first.x, particleParams.sizeRandom.second.x),
|
||||||
randomFloatBetween(particleParams.sizeRandom.first.y, particleParams.sizeRandom.second.x),
|
randomFloatBetween(particleParams.sizeRandom.first.y, particleParams.sizeRandom.second.x),
|
||||||
randomFloatBetween(particleParams.sizeRandom.first.z, particleParams.sizeRandom.second.z)
|
randomFloatBetween(particleParams.sizeRandom.first.z, particleParams.sizeRandom.second.z)
|
||||||
)
|
)
|
||||||
var blockDisplay: DisplayEntity? = null
|
private var blockDisplay: DisplayEntity? = null
|
||||||
val params = particleParams
|
|
||||||
|
|
||||||
var isInit = false
|
var isDead = false
|
||||||
|
private var timeAlive = 0
|
||||||
|
|
||||||
|
private var isInit = false
|
||||||
|
|
||||||
fun init(pos: Vec3d) {
|
fun init(pos: Vec3d) {
|
||||||
val offset = Vector3f(-0.5f, -0.5f, -0.5f)
|
val offset = Vector3f(-0.5f, -0.5f, -0.5f)
|
||||||
val quatRot = eulerToQuat(this.rot)
|
val quatRot = eulerToQuat(rot)
|
||||||
|
|
||||||
val scaleOffset = transformOffsetByScale(offset, this.scale)
|
val scaleOffset = transformOffsetByScale(offset, scale)
|
||||||
val rotOffset = transformOffsetByQuat(scaleOffset, quatRot)
|
val rotOffset = transformOffsetByQuat(scaleOffset, quatRot)
|
||||||
|
|
||||||
val properties = DisplayEntityProperties(
|
val properties = DisplayEntityProperties(
|
||||||
pos = pos,
|
pos = pos,
|
||||||
blockType = this.params.blockCurve[0],
|
blockType = particleParams.blockCurve[0],
|
||||||
translation = rotOffset,
|
translation = rotOffset,
|
||||||
leftRotation = quatRot,
|
leftRotation = quatRot,
|
||||||
scale = this.scale
|
scale = scale,
|
||||||
|
tags = arrayOf("BPS_Particle")
|
||||||
)
|
)
|
||||||
|
|
||||||
this.blockDisplay = DisplayEntity(properties)
|
blockDisplay = DisplayEntity(properties)
|
||||||
|
|
||||||
this.isInit = true
|
isInit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun spawn(world: ServerWorld) {
|
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") }
|
else { error("Particle is not initialized before being spawned. Please use Particle.init() to initialize it.") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun tick() {
|
fun tick(world: ServerWorld) {
|
||||||
|
if (!isInit) { error("Particle is not initialized before being ticked. Please use Particle.init() to initialize it.") }
|
||||||
|
|
||||||
|
val properties = blockDisplay?.properties ?: return
|
||||||
|
|
||||||
|
val newProperties = calculateNewProperties(properties, particleParams)
|
||||||
|
|
||||||
|
blockDisplay?.updateProperties(world, newProperties)
|
||||||
|
|
||||||
|
if (timeAlive > particleParams.lifeTime) { isDead = true; blockDisplay?.kill() }
|
||||||
|
|
||||||
|
timeAlive += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun calculateNewProperties(propetries: DisplayEntityProperties, params: ParticleParams) : DisplayEntityProperties {
|
||||||
|
val newBlockIdx = round(lerp(
|
||||||
|
0.0f,
|
||||||
|
params.blockCurve.lastIndex.toFloat(),
|
||||||
|
(timeAlive.toFloat() / params.lifeTime.toFloat()).coerceIn(0.0f, 1.0f)
|
||||||
|
)).toInt()
|
||||||
|
|
||||||
|
val newBlock = params.blockCurve[newBlockIdx]
|
||||||
|
|
||||||
|
|
||||||
|
val newProperties = DisplayEntityProperties(
|
||||||
|
blockType = newBlock
|
||||||
|
)
|
||||||
|
|
||||||
|
return newProperties
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,18 +4,54 @@ import net.minecraft.server.world.ServerWorld
|
|||||||
import net.minecraft.util.math.Vec3d
|
import net.minecraft.util.math.Vec3d
|
||||||
|
|
||||||
class ParticleEmitter(emitterPos: Vec3d, emitterWorld: ServerWorld, emitterParams: ParticleEmitterParams) {
|
class ParticleEmitter(emitterPos: Vec3d, emitterWorld: ServerWorld, emitterParams: ParticleEmitterParams) {
|
||||||
val world = emitterWorld
|
private val world = emitterWorld
|
||||||
val pos = emitterPos
|
private val pos = emitterPos
|
||||||
val params = emitterParams
|
private val params = emitterParams
|
||||||
val allParticles: Array<Particle> = emptyArray()
|
|
||||||
val timeAlive = 0
|
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
|
||||||
|
|
||||||
|
var isDead = false
|
||||||
|
|
||||||
|
|
||||||
fun tick() {
|
fun tick() {
|
||||||
|
count()
|
||||||
|
|
||||||
|
if (!loopDelayActive) {
|
||||||
repeat(this.params.spawnsPerTick) {
|
repeat(this.params.spawnsPerTick) {
|
||||||
val particle = Particle(ParticleParams())
|
val particle = Particle(ParticleParams())
|
||||||
particle.init(this.pos)
|
particle.init(this.pos)
|
||||||
particle.spawn(world)
|
particle.spawn(world)
|
||||||
|
allParticles += particle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (particle in allParticles) {
|
||||||
|
if (particle.isDead) { allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray() }
|
||||||
|
else { particle.tick(world) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun count() {
|
||||||
|
if (loopDur >= params.loopDur) {
|
||||||
|
loopCount += 1
|
||||||
|
loopDur = 0
|
||||||
|
loopDelayActive = true
|
||||||
|
if (!params.loop) { isDead = true }
|
||||||
|
}
|
||||||
|
else if (loopDelay >= params.loopDelay) {
|
||||||
|
loopDelay = 0
|
||||||
|
loopDelayActive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loopDelayActive) { loopDur += 1 }
|
||||||
|
else { loopDelay += 1 }
|
||||||
|
timeAlive += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,24 +5,16 @@ import org.joml.Vector3f
|
|||||||
|
|
||||||
data class ParticleParams (
|
data class ParticleParams (
|
||||||
//val shape: Shape, // add later, start with single point
|
//val shape: Shape, // add later, start with single point
|
||||||
val blockCurve: Array<String> = arrayOf("minecraft:purple_concrete"),
|
val blockCurve: Array<String> = arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"),
|
||||||
val rotRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)),
|
val rotRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)),
|
||||||
val rotVelRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(5.0f, 5.0f, 5.0f)),
|
val rotVelRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(5.0f, 5.0f, 5.0f)),
|
||||||
val rotVelCurve: Array<Vector3f> = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)),
|
val rotVelCurve: Array<Vector3f> = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)),
|
||||||
val sizeRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)),
|
val sizeRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)),
|
||||||
val sizeCurve: Array<Vector3f> = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)),
|
val sizeCurve: Array<Vector3f> = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)),
|
||||||
|
val velRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(5.0f, 5.0f, 5.0f)),
|
||||||
val forceFields: ForceField = ForceField(),
|
val forceFields: ForceField = ForceField(),
|
||||||
val gravity: Vec3d = Vec3d(0.0, -9.0, 0.0),
|
val gravity: Vec3d = Vec3d(0.0, -9.0, 0.0),
|
||||||
val drag: Float = 0.2f,
|
val drag: Float = 0.2f,
|
||||||
val minVel: Float = 0.003f
|
val minVel: Float = 0.003f,
|
||||||
)
|
val lifeTime: Int = 20
|
||||||
|
|
||||||
|
|
||||||
data class ForceField (
|
|
||||||
val x: Float = 0.0f,
|
|
||||||
val y: Float = 0.0f,
|
|
||||||
val z: Float = 0.0f,
|
|
||||||
val minForce: Float = 0.0f,
|
|
||||||
val maxForce: Float = 0.0f,
|
|
||||||
val radius: Float = -1.0f,
|
|
||||||
)
|
)
|
||||||
Reference in New Issue
Block a user