significantly changed emitter params. STILL A LOT OF ERRORS.

This commit is contained in:
2024-11-29 19:38:43 +01:00
parent 6728445280
commit 939867934b
9 changed files with 260 additions and 270 deletions
@@ -24,6 +24,7 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
import org.joml.Vector2f
import java.util.*
@@ -34,22 +35,19 @@ import java.util.*
// blockCurve array not needing "minecraft:"
// Adding a parameter for an initial velocity from an origin point (you currently have to use force fields)
// Pair<Pair<Vec3f, Vec3f>, Pair<Vec3f, Vec3f>>
// "min center vel", "max center vel", "min edge vel", "max edge vel"
// Parameters
// localOffset (Vec3f)
// velocity based, relative coordinate based, or rotation based
// localOffsetCurve (curve)
// spawnOffset (Vec3f)
// 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)
// Move most (if not all) particle params to emitters.
// 3D curves (curves for all X Y Z)
// Custom curve equation command args (parse from strings)
// Cylinder and cone force field shape.
// Better particle performance.
@@ -109,7 +107,7 @@ class Bde_particles : ModInitializer {
fun init() {
addToEmitterRegister("DEFAULT", EmitterParams.DEFAULT)
addToRegister("DEFAULT", EmitterParams.DEFAULT)
}
@@ -139,7 +137,7 @@ fun onRightClick(player: PlayerEntity, world: ServerWorld, hand: Hand) : TypedAc
val handItem = player.getStackInHand(hand)
val (isEmitterTool, emitterId) = ParticleEmitterTool.getToolDetails(handItem)
val hitResult = raycastFromPlayer(player as ServerPlayerEntity, 200.0)
val emitterParams = getEmitterParams(emitterId)
val emitterParams = getParamsById(emitterId)
if (
!isEmitterTool
@@ -149,7 +147,7 @@ fun onRightClick(player: PlayerEntity, world: ServerWorld, hand: Hand) : TypedAc
return TypedActionResult(ActionResult.PASS, handItem)
}
val emitter = ParticleEmitter(hitResult.pos, world, emitterParams)
val emitter = ParticleEmitter(hitResult.pos, Vector2f(0.0f, 0.0f), world, emitterParams)
ALL_PARTICLE_EMITTERS += emitter
return TypedActionResult(ActionResult.PASS, handItem)
@@ -157,35 +155,16 @@ fun onRightClick(player: PlayerEntity, world: ServerWorld, hand: Hand) : TypedAc
fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { // TODO: map to new params
val allParticleParamsJSON: MutableList<Map<String, Any?>> = mutableListOf()
val particle = params.particle
val allParamsJson: MutableList<Map<String, Any?>> = mutableListOf()
val particleParamsJSON = mapOf(
"shape" to particle.shape,
"blockCurve" to particle.blockCurve,
"rotRandom" to particle.rotRandom,
"rotVelRandom" to particle.rotVelRandom,
"rotVelCurve" to particle.rotVelCurve,
"sizeRandom" to particle.sizeRandom,
"uniformSize" to particle.uniformSize,
"sizeCurve" to particle.sizeCurve,
"velRandom" to particle.velRandom,
"forceFields" to particle.forceFields,
"gravity" to particle.gravity,
"drag" to particle.drag,
"minVel" to particle.minVel,
"lifeTime" to particle.lifeTime
val paramsJson = mapOf(
"shape" to params.shape
)
allParticleParamsJSON.add(particleParamsJSON)
allParamsJson.add(paramsJson)
val emitterParamsJSON = mapOf(
"maxCount" to params.maxCount,
"spawnsPerTick" to params.spawnsPerTick,
"loopDur" to params.loopDur,
"loopDelay" to params.loopDelay,
"loopCount" to params.loopCount,
"particleTypes" to allParticleParamsJSON
)
return emitterParamsJSON
@@ -137,45 +137,6 @@ fun quatToEuler(quat: Vector4f): Vector3f {
}
fun randomInCircle(r: Float) : Vector2f {
val randRadius = r * sqrt(randomFloatBetween(0.0f, 1.0f))
val randAngle = randomFloatBetween(0.0f, Math.PI.toFloat() * 2)
val randomPos = Vector2f(randRadius * cos(randAngle), randRadius * sin(randAngle))
return randomPos
}
fun randomInSphere(r: Float) : Vector3f {
val randRadius = r * Random.nextDouble().pow(1.0 / 3.0)
val phi = Random.nextDouble(0.0, 2 * Math.PI)
val cosTheta = Random.nextDouble(-1.0, 1.0)
val theta = acos(cosTheta)
val x = randRadius * sin(theta) * cos(phi)
val y = randRadius * sin(theta) * sin(phi)
val z = randRadius * cos(theta)
return Vector3f(x.toFloat(), y.toFloat(), z.toFloat())
}
fun randomInRect(size: Vector2f) : Vector2f {
return Vector2f(randomFloatBetween(0.0f, size.x), randomFloatBetween(0.0f, size.y))
}
fun randomInCube(size: Vector3f) : Vector3f {
return Vector3f(
randomFloatBetween(0.0f, size.x),
randomFloatBetween(0.0f, size.y),
randomFloatBetween(0.0f, size.z)
)
}
fun transformOffsetByQuat(offset: Vector3f, rotation: Vector4f): Vector3f {
val x = offset.x
val y = offset.y
@@ -1,7 +1,7 @@
package com.bytedice.bde_particles.commands
import com.bytedice.bde_particles.items.ParticleEmitterTool
import com.bytedice.bde_particles.particles.emitterIdRegister
import com.bytedice.bde_particles.particles.idRegister
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType
@@ -20,7 +20,7 @@ object GiveEmitterTool {
val command = CommandManager.literal("GiveEmitterTool")
.requires { source -> source.hasPermissionLevel(4) }
val allEmitterIds = emitterIdRegister.keys
val allEmitterIds = idRegister.keys
val itemNameArg = CommandManager.argument("Item Name", StringArgumentType.string())
val itemTypeArg = CommandManager.argument("Item Type", ItemStackArgumentType.itemStack(registryAccess))
@@ -4,7 +4,6 @@ import com.bytedice.bde_particles.emitterParamsToJson
import com.bytedice.bde_particles.particles.*
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.builder.RequiredArgumentBuilder
@@ -31,30 +31,63 @@ import org.joml.Vector3f
* @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 (
var maxCount: Int = 200,
var spawnRate: Int = 1,
var spawnChance: Float = 1.0f,
var loopDur: Int = 25,
var loopDelay: Int = 0,
var loopCount: Int = 0,
var shape: SpawningShape = SpawningShape.Circle(3.0f),
var initRot: ParamClasses.PairVec3f = ParamClasses.PairVec3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f),
var rotVel: ParamClasses.PairVec3f = ParamClasses.PairVec3f(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f),
var rotTowardVel: Boolean = true,
var initVel: ParamClasses.PairVec3f = ParamClasses.PairVec3f(-0.1f, 0.3f, -0.1f, 0.1f, 0.6f, 0.1f),
var initScale: ParamClasses.RandScale = ParamClasses.RandScale.Uniform(),
var scaleTowardVel: Boolean = true,
var forceFields: Array<ForceField> = emptyArray(),
var constVel: Vector3f = Vector3f(0.0f, -0.01f, 0.0f),
var drag: Float = 0.075f,
var minVel: Float = 0.0f,
var lifeTime: ParamClasses.PairInt = ParamClasses.PairInt(25, 40),
var rotVelCurve: ParamClasses.LerpVal = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
var scaleCurve: ParamClasses.LerpVal = ParamClasses.LerpVal.LerpUniform(1.0f, 0.5f, LerpCurves.Linear),
var blockCurve: Pair<Array<String>, LerpCurves> = Pair(arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"), LerpCurves.Sqrt),
// spawning
var maxCount: Int,
var spawnRate: Int,
var spawnChance: Float,
var spawnDuration: ParamClasses.Duration,
var spawnPosOffset: Vector3f,
var lifeTime: ParamClasses.PairInt,
var shape: SpawningShape,
// init transforms
var offset: ParamClasses.PairVec3f,
var initRot: ParamClasses.PairVec3f,
var rotVel: ParamClasses.PairVec3f,
var rotWithVel: Boolean,
var initVel: ParamClasses.PairVec3f,
var initCenterVel: ParamClasses.PairFloat,
var initScale: ParamClasses.PairVec3f,
var scaleWithVel: Boolean,
// velocity
var forceFields: Array<ForceField>,
var constVel: Vector3f,
var drag: Float,
var minVel: Float,
// curves
var offsetCurve: ParamClasses.LerpVal,
var rotVelCurve: ParamClasses.LerpVal,
var scaleCurve: ParamClasses.LerpVal,
var blockCurve: Pair<Array<String>, LerpCurves>,
)
{
companion object Presets {
val DEFAULT = EmitterParams()
val DEFAULT = EmitterParams(
maxCount = 200,
spawnRate = 1,
spawnChance = 1.0f,
spawnDuration = ParamClasses.Duration.SingleBurst(25),
spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f),
lifeTime = ParamClasses.PairInt(25, 40),
shape = SpawningShape.Circle(3.0f),
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),
rotWithVel = true,
initVel = ParamClasses.PairVec3f.Null,
initCenterVel = ParamClasses.PairFloat(0.1f, 0.3f),
initScale = ParamClasses.PairVec3f.Uniform(1.0f, 0.2f),
scaleWithVel = true,
forceFields = emptyArray(),
constVel = Vector3f(0.0f, -0.01f, 0.0f),
drag = 0.075f,
minVel = 0.0f,
offsetCurve = ParamClasses.LerpVal.NoLerpVec3f(Vector3f(-0.5f, -0.5f, -0.5f)),
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.5f, LerpCurves.Linear),
blockCurve = Pair(
arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"),
LerpCurves.Sqrt
),
)
}
}
@@ -6,40 +6,24 @@ import com.bytedice.bde_particles.randomIntBetween
import org.joml.Vector3f
class ParamClasses {
class PairVec3f (
private val minX: Float = 0.0f,
private val minY: Float = 0.0f,
private val minZ: Float = 0.0f,
private val maxX: Float = 1.0f,
private val maxY: Float = 1.0f,
private val maxZ: Float = 1.0f,
) {
fun randomize() : Vector3f {
return Vector3f(
randomFloatBetween(minX, maxX),
randomFloatBetween(minY, maxY),
randomFloatBetween(minZ, maxZ),
)
}
}
sealed class RandScale {
sealed class PairVec3f {
class Uniform (
private val min: Float = 0.0f,
private val max: Float = 1.0f,
) : RandScale() {
private val min: Float,
private val max: Float,
) : PairVec3f() {
fun randomize() : Vector3f {
val x = randomFloatBetween(min, max)
return Vector3f(x, x, x)
}
}
class NonUniform (
private val minX: Float = 0.0f,
private val minY: Float = 0.0f,
private val minZ: Float = 0.0f,
private val maxX: Float = 1.0f,
private val maxY: Float = 1.0f,
private val maxZ: Float = 1.0f
) : RandScale() {
private val minX: Float,
private val minY: Float,
private val minZ: Float,
private val maxX: Float,
private val maxY: Float,
private val maxZ: Float
) : PairVec3f() {
fun randomize(): Vector3f {
return Vector3f(
randomFloatBetween(minX, maxX),
@@ -48,35 +32,29 @@ class ParamClasses {
)
}
}
}
class PairVec2i (
private val minX: Int = 0,
private val minY: Int = 0,
private val maxX: Int = 1,
private val maxY: Int = 1,
) {
fun randomize() : Pair<Int, Int> {
return Pair(
randomIntBetween(minX, maxX),
randomIntBetween(minY, maxY),
)
}
data object Null : PairVec3f()
}
class PairInt (
private val min: Int = 0,
private val max: Int = 1,
private val min: Int,
private val max: Int,
) {
fun randomize() : Int { return randomIntBetween(min, max) }
}
class PairFloat(
private val min: Float,
private val max: Float,
) {
fun randomize() : Float { return randomFloatBetween(min, max) }
}
sealed class LerpVal {
class LerpVec3f(
private val fromX: Float = 0.0f,
private val fromY: Float = 0.0f,
private val fromZ: Float = 0.0f,
private val toX: Float = 1.0f,
private val toY: Float = 1.0f,
private val toZ: Float = 1.0f,
private val curve: LerpCurves = LerpCurves.Linear,
private val fromX: Float,
private val fromY: Float,
private val fromZ: Float,
private val toX: Float,
private val toY: Float,
private val toZ: Float,
private val curve: LerpCurves,
) : LerpVal() {
fun lerp(t: Float): Vector3f {
val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curve.function(t)
@@ -86,15 +64,15 @@ class ParamClasses {
}
}
class MultiLerpVec3f(
private val fromX: Float = 0.0f,
private val fromY: Float = 0.0f,
private val fromZ: Float = 0.0f,
private val toX: Float = 1.0f,
private val toY: Float = 1.0f,
private val toZ: Float = 1.0f,
private val curveX: LerpCurves = LerpCurves.Linear,
private val curveY: LerpCurves = LerpCurves.Linear,
private val curveZ: LerpCurves = LerpCurves.Linear
private val fromX: Float,
private val fromY: Float,
private val fromZ: Float,
private val toX: Float,
private val toY: Float,
private val toZ: Float,
private val curveX: LerpCurves,
private val curveY: LerpCurves,
private val curveZ: LerpCurves
) : LerpVal() {
fun lerp(t: Float): Vector3f {
val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curveX.function(t)
@@ -104,14 +82,15 @@ class ParamClasses {
}
}
class LerpUniform(
private val from: Float = 0.0f,
private val to: Float = 1.0f,
private val curve: LerpCurves = LerpCurves.Linear,
private val from: Float,
private val to: Float,
private val curve: LerpCurves,
) : LerpVal() {
fun lerp(t: Float): Float {
return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t)
}
}
data class NoLerpVec3f(val value: Vector3f) : LerpVal()
fun lerpToVector3f(t: Float) : Vector3f {
when (this) {
@@ -121,7 +100,22 @@ class ParamClasses {
val x = this.lerp(t)
return Vector3f(x, x, x)
}
is NoLerpVec3f -> return this.value
}
}
}
sealed class Duration {
data class SingleBurst(
val loopDur: Int
) : Duration()
data class MultiBurst(
val loopDur: Int,
val loopDelay: Int,
val loopCount: Int,
) : Duration()
data class InfiniteLoop(
val loopDur: Int,
val loopDelay: Int
) : Duration()
}
}
@@ -2,8 +2,10 @@ package com.bytedice.bde_particles.particles
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.Vec3d
import org.joml.Vector2f
import org.joml.Vector3f
class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: ServerWorld, private val emitterParams: EmitterParams) {
class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private val world: ServerWorld, private val params: EmitterParams) {
private var allParticles: Array<Particle> = emptyArray()
@@ -33,12 +35,12 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S
private fun addParticle() {
if (loopDelayActive) { return }
repeat(this.emitterParams.spawnsPerTick) {
if (allParticles.size >= emitterParams.maxCount) { return }
repeat(params.spawnRate) {
if (allParticles.size >= params.maxCount) { return }
val particle = Particle(emitterParams.particle)
particle.init(this.emitterPos)
particle.spawn(emitterWorld)
val particle = Particle(params)
particle.init(pos, rot)
particle.spawn(world)
allParticles += particle
}
@@ -50,30 +52,30 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S
// because im so damn good at cooking spaghetti
// if max duration is 0 or less, skip counting, its infinite
if (emitterParams.loopDur <= 0) { return }
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 >= emitterParams.loopDur) {
if (loopDur >= params.loopDur) {
if (loopCount == 0) {
isTicking = false
return
}
loopCount += 1
loopDur = 0
if (emitterParams.loopDelay > 0) { loopDelayActive = true }
if (params.loopDelay > 0) { loopDelayActive = true }
}
// if the delay is greater than max delay, disable the delay
else if (loopDelay >= emitterParams.loopDelay) {
else if (loopDelay >= params.loopDelay) {
loopDelay = 0
loopDelayActive = false
}
// 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 >= emitterParams.loopCount) {
if (loopCount > 0 && loopCount >= params.loopCount) {
isTicking = false
return
}
@@ -2,133 +2,60 @@
package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.LerpCurves
import org.joml.Vector3f
val idRegister: MutableMap<String, EmitterParams> = mutableMapOf()
val forbiddenIds = arrayOf("", "NULL")
val emitterIdRegister: MutableMap<String, EmitterParams> = mutableMapOf()
val forbiddenIds = arrayOf("", "DEFAULT", "NULL")
fun addToRegister(id: String, params: EmitterParams) : Pair<String, Boolean> {
val newId = replaceForbiddenChars(id.replace(" ", "_"))
if (idRegister.containsKey(newId)) { return Pair(newId, false) }
fun addToEmitterRegister(id: String, params: EmitterParams) : Pair<String, Boolean> {
val newId = id.replace(" ", "_")
if (newId in forbiddenIds && newId != "DEFAULT") { return Pair(newId, false) }
if (emitterIdRegister.containsKey(newId)) { return Pair(newId, false) }
emitterIdRegister[id] = params
idRegister[id] = params
return Pair(newId, true)
}
fun removeFromEmitterRegister(id: String) : Boolean {
fun removeFromRegister(id: String) : Boolean {
if (id in forbiddenIds) { return false }
if (!emitterIdRegister.containsKey(id)) { return false }
if (!idRegister.containsKey(id)) { return false }
emitterIdRegister.remove(id)
idRegister.remove(id)
return true
}
fun getEmitterParams(id: String) : EmitterParams? {
return emitterIdRegister[id]
fun getParamsById(id: String) : EmitterParams? {
return idRegister[id]
}
fun updateEmitterParams(id: String, newParams: EmitterParams) {
emitterIdRegister[id] = newParams
fun updateRegistered(id: String, newParams: EmitterParams) {
idRegister[id] = newParams
}
fun updateSingleEmitterParam(id: String, paramName: String, paramValue: Any) : Boolean {
val emitter = getEmitterParams(id) ?: return false
val updatedValues = updateSingleEmitterParam(emitter, paramName, paramValue)
updateEmitterParams(id, updatedValues.first)
fun updateParam(id: String, paramName: String, paramValue: Any) : Boolean {
val emitter = getParamsById(id) ?: return false
val updatedValues = updateParams(emitter, paramName, paramValue)
return updatedValues.second
}
fun updateSingleParticleParam(emitterId: String, paramName: String, paramValue: Any) : Boolean { // TODO: map to new params
val emitter = getEmitterParams(emitterId) ?: return false
val particle = emitter.particle
val updatedValues = updateSingleParticleParam(particle, paramName, paramValue)
emitter.particle = updatedValues.first
updateEmitterParams(emitterId, emitter)
return updatedValues.second
}
fun updateSingleEmitterParam(params: EmitterParams, paramName: String, paramValue: Any) : Pair<EmitterParams, Boolean> { // TODO: map to new params
val value: Any? = when (paramName) {
"maxCount" -> paramValue as Int
"spawnsPerTick" -> paramValue as Int
"loopDur" -> paramValue as Int
"loopDelay" -> paramValue as Int
"loopCount" -> paramValue as Int
else -> null
}
if (value == null) { return Pair(params, false) }
fun updateParams(params: EmitterParams, paramName: String, paramValue: Any) : Pair<EmitterParams, Boolean> { // TODO: map to new params
val newParams = params.copy()
when (paramName) {
"maxCount" -> newParams.maxCount = value as Int
"spawnsPerTick" -> newParams.spawnsPerTick = value as Int
"loopDur" -> newParams.loopDur = value as Int
"loopDelay" -> newParams.loopDelay = value as Int
"loopCount" -> newParams.loopCount = value as Int
else -> return Pair(params, false)
"maxCount" -> newParams.maxCount = paramValue as Int
}
return Pair(newParams, true)
}
fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramValue: Any) : Pair<ParticleParams, Boolean> { // TODO: map to new params
val value: Any? = when (paramName) {
"shape" -> paramValue as SpawningShape?
"blockCurve" -> paramValue as Array<String>
"rotRandom" -> paramValue as Pair<Vector3f, Vector3f>
"rotVelRandom" -> paramValue as Pair<Vector3f, Vector3f>
"sizeRandom" -> paramValue as Pair<Vector3f, Vector3f>
"uniformSize" -> paramValue as Boolean
"velRandom" -> paramValue as Pair<Vector3f, Vector3f>
"forceFields" -> paramValue as Array<ForceField>
"gravity" -> paramValue as Vector3f
"drag" -> paramValue as Float
"minVel" -> paramValue as Float
"lifeTime" -> paramValue as Pair<Int, Int>
"rotVelCurve" -> paramValue as Triple<Vector3f, Vector3f, LerpCurves>
"sizeCurve" -> paramValue as Triple<Vector3f, Vector3f, LerpCurves>
else -> null
}
if (value == null) { return Pair(params, false) }
val newParams = params.copy()
when (paramName) {
"shape" -> newParams.shape = value as SpawningShape
"blockCurve" -> newParams.blockCurve = value as Array<String>
"rotRandom" -> newParams.rotRandom = value as Pair<Vector3f, Vector3f>
"rotVelRandom" -> newParams.rotVelRandom = value as Pair<Vector3f, Vector3f>
"sizeRandom" -> newParams.sizeRandom = value as Pair<Vector3f, Vector3f>
"uniformSize" -> newParams.uniformSize = value as Boolean
"velRandom" -> newParams.velRandom = value as Pair<Vector3f, Vector3f>
"forceFields" -> newParams.forceFields = value as Array<ForceField>
"gravity" -> newParams.gravity = value as Vector3f
"drag" -> newParams.drag = value as Float
"minVel" -> newParams.minVel = value as Float
"lifeTime" -> newParams.lifeTime = value as Pair<Int, Int>
"rotVelCurve" -> newParams.rotVelCurve = value as Triple<Vector3f, Vector3f, LerpCurves>
"sizeCurve" -> newParams.sizeCurve = value as Triple<Vector3f, Vector3f, LerpCurves>
else -> return Pair(params, false)
}
return Pair(newParams, true)
fun replaceForbiddenChars(input: String): String {
val regex = "[^a-zA-Z0-9_-]".toRegex()
return input.replace(regex, "-")
}
@@ -1,16 +1,111 @@
package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.randomFloatBetween
import com.bytedice.bde_particles.randomIntBetween
import org.joml.Vector2f
import org.joml.Vector3f
import kotlin.math.*
import kotlin.random.Random
sealed class SpawningShape {
data class Circle(val radius: Float = 1.0f) : SpawningShape()
data class Rect (val size: Vector2f = Vector2f(1.0f, 1.0f)) : SpawningShape()
data class Cube (val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f)) : SpawningShape()
data class Sphere(val radius: Float = 1.0f) : SpawningShape()
class Circle(val radius: Float = 1.0f) : SpawningShape() {
fun randomWithin(): Vector3f {
val randRadius = radius * sqrt(randomFloatBetween(0.0f, 1.0f))
val randAngle = randomFloatBetween(0.0f, Math.PI.toFloat() * 2)
val randomPos = Vector3f(randRadius * cos(randAngle), 0.0f, randRadius * sin(randAngle))
return randomPos
}
fun randomOnEdge(): Vector3f {
val randAngle = randomFloatBetween(0.0f, Math.PI.toFloat() * 2)
return Vector3f(radius * cos(randAngle), 0.0f, radius * sin(randAngle))
}
}
class Rect(val size: Vector2f = Vector2f(1.0f, 1.0f)) : SpawningShape() {
fun randomWithin(): Vector3f {
return Vector3f(randomFloatBetween(0.0f, size.x), 0.0f, randomFloatBetween(0.0f, size.y))
}
fun randomOnEdge(): Vector3f {
val perimeter = 2 * (size.x + size.y)
val randPos = randomFloatBetween(0.0f, perimeter)
return when {
randPos <= size.x -> Vector3f(randPos, 0.0f, 0.0f)
randPos <= size.x + size.y -> Vector3f(size.x, 0.0f, randPos - size.x)
randPos <= 2 * size.x + size.y -> Vector3f(size.x - (randPos - size.x - size.y), 0.0f, size.y)
else -> Vector3f(0.0f, 0.0f, size.y - (randPos - 2 * size.x - size.y))
}
}
}
class Cube(val size: Vector3f = Vector3f(1.0f, 1.0f, 1.0f)) : SpawningShape() {
fun randomWithin(): Vector3f {
return Vector3f(
randomFloatBetween(0.0f, size.x),
randomFloatBetween(0.0f, size.y),
randomFloatBetween(0.0f, size.z)
)
}
fun randomOnEdge(): Vector3f {
val edgeIndex = randomIntBetween(0, 11) // 12 edges
val randPos = randomFloatBetween(0.0f, 1.0f)
return when (edgeIndex) {
0 -> Vector3f(randPos * size.x, 0.0f, 0.0f)
1 -> Vector3f(size.x, 0.0f, randPos * size.z)
2 -> Vector3f(size.x - randPos * size.x, 0.0f, size.z)
3 -> Vector3f(0.0f, 0.0f, size.z - randPos * size.z)
4 -> Vector3f(randPos * size.x, size.y, 0.0f)
5 -> Vector3f(size.x, size.y, randPos * size.z)
6 -> Vector3f(size.x - randPos * size.x, size.y, size.z)
7 -> Vector3f(0.0f, size.y, size.z - randPos * size.z)
8 -> Vector3f(0.0f, randPos * size.y, 0.0f)
9 -> Vector3f(size.x, randPos * size.y, 0.0f)
10 -> Vector3f(size.x, randPos * size.y, size.z)
11 -> Vector3f(0.0f, randPos * size.y, size.z)
else -> Vector3f(0.0f, 0.0f, 0.0f)
}
}
}
class Sphere(val radius: Float = 1.0f) : SpawningShape() {
fun randomWithin(): Vector3f {
val randRadius = radius * Random.nextDouble().pow(1.0 / 3.0)
val phi = Random.nextDouble(0.0, 2 * Math.PI)
val cosTheta = Random.nextDouble(-1.0, 1.0)
val theta = acos(cosTheta)
val x = randRadius * sin(theta) * cos(phi)
val y = randRadius * sin(theta) * sin(phi)
val z = randRadius * cos(theta)
return Vector3f(x.toFloat(), y.toFloat(), z.toFloat())
}
fun randomOnEdge(): Vector3f {
val phi = randomFloatBetween(0.0f, Math.PI.toFloat() * 2)
val cosTheta = randomFloatBetween(-1.0f, 1.0f)
val sinTheta = sqrt(1 - cosTheta * cosTheta)
val x = radius * sinTheta * cos(phi)
val y = radius * sinTheta * sin(phi)
val z = radius * cosTheta
return Vector3f(x, y, z)
}
}
data object Point : SpawningShape()
}
sealed class ForceFieldShape {
data class Sphere(
val radius: Float = 3.0f,