Skip to content

Commit 46c5d48

Browse files
committed
feat: Velocity rescaling
1 parent ae256c7 commit 46c5d48

9 files changed

Lines changed: 70 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ development.
1111
### Added
1212

1313
- Started a changelog based on keep a changelog
14-
- Mean square velocity calculation via a compute shader that sums log_2 times; plan to extract into a general API in the
15-
future
14+
- **Mean square velocity** calculation via a compute shader that sums log_2 times; plan to extract into a general API in
15+
the future
16+
- **Support for recent projects in web** via local storage, plus "Save as" button to save projects back to disk
17+
- **Target velocity** via rescaling based on mean square velocity
1618
- Added more options to graphs displayed in GUI like toggling on/off and clearing
1719
- Added helper function for drawing graphs for values that update over time
1820
- Added banner and icon for the project
1921
- Grid toggle button under visual options
20-
- Support for recent projects in web via local storage, plus "Save as" button to save projects back to disk
2122

2223
### Changed
2324

particles-config/src/commonMain/kotlin/me/dvyy/particles/dsl/Simulation.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ data class Simulation(
1010
val dT: Double = 0.001,
1111
val maxVelocity: Double = 20.0,
1212
val maxForce: Double = 100000.0,
13+
val targetVelocity: Double = 0.0,
14+
val targetVelocityStrength: Double = 0.0,
1315
val threeDimensions: Boolean = false,
1416
val passesPerFrame: Int = 100,
1517
val size: Size = Size(),

particles-kool/src/commonMain/kotlin/me/dvyy/particles/ParticlesScene.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ class ParticlesScene(
7777
}
7878

7979
offsetsShader.addTo(computePass) // Calculate offsets (start index in particles array for each grid cell)
80-
val fieldsPasses = fieldsShader.addTo(computePass) // Run force computations based on particle interactions
80+
val fieldsPasses = fieldsShader.addTo(
81+
computePass,
82+
meanSquareDataShader
83+
) // Run force computations based on particle interactions
8184
convertShader.addTo(computePass) // Convert particles to different types as needed
8285

8386
// == DATA COLLECTION ==

particles-kool/src/commonMain/kotlin/me/dvyy/particles/compute/data/MeanSquareVelocities.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,32 @@ class MeanSquareVelocities(
4040
}
4141
}
4242
}
43+
44+
private val outputShader = KslComputeShader("MeanSquareVelocities_output") {
45+
// val struct = struct { SimulationStatisticsStruct() }
46+
// val total = storage("total", struct)
47+
val inputs = storage<KslFloat1>("inputs")
48+
val output = storage<KslFloat1>("outputStorage")
49+
computeStage(1) {
50+
main {
51+
output[0.const] = inputs[0.const]
52+
}
53+
}
54+
}
55+
56+
private var outputBind by outputShader.storage("outputStorage")//.uniformStruct("total", ::SimulationStatisticsStruct)
57+
4358
private var inputs by reduce.storage("inputs")
4459
private var outputs by reduce.storage("outputs")
4560
private var total by reduce.uniform1i("total")
4661

47-
val inputBuffer = Buffers.floats(buffers.count)
48-
val outputBuffer = Buffers.floats(buffers.count)
62+
private val inputBuffer = Buffers.floats(buffers.count)
63+
private val outputBuffer = Buffers.floats(buffers.count)
64+
val output = Buffers.floats(1)
4965

5066
private val roundedUp = 1 shl (32 - (buffers.count - 1).countLeadingZeroBits())
5167
private val iterations = roundedUp.countTrailingZeroBits()
52-
val readBack = if (iterations % 2 == 0) inputBuffer else outputBuffer
68+
private val readBack = if (iterations % 2 == 0) inputBuffer else outputBuffer
5369

5470
fun addTo(
5571
pass: ComputePass,
@@ -73,6 +89,10 @@ class MeanSquareVelocities(
7389
}
7490
}
7591
}
92+
addTask(outputShader.apply {
93+
storage("inputs").set(readBack)
94+
outputBind = output
95+
}, Vec3i(1, 1, 1))
7696
}
7797
}
7898
}

particles-kool/src/commonMain/kotlin/me/dvyy/particles/compute/simulation/FieldsMultiPasses.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package me.dvyy.particles.compute.simulation
22

33
import de.fabmax.kool.pipeline.ComputePass
44
import me.dvyy.particles.compute.ParticleBuffers
5+
import me.dvyy.particles.compute.data.MeanSquareVelocities
56
import me.dvyy.particles.compute.forces.ForcesDefinition
67
import me.dvyy.particles.config.ConfigRepository
78

@@ -32,10 +33,14 @@ class FieldsMultiPasses(
3233
halfStep.boxMax = configRepo.boxSize
3334
}
3435

35-
fun addTo(computePass: ComputePass): List<Pair<ComputePass.Task, ComputePass.Task>> {
36+
fun addTo(
37+
computePass: ComputePass,
38+
velocitiesShader: MeanSquareVelocities,
39+
): List<Pair<ComputePass.Task, ComputePass.Task>> {
3640
val config = configRepo.config.value
3741

3842
initBuffers()
43+
fields.velocityData = velocitiesShader.output
3944
val passes = buildList {
4045
repeat(config.simulation.passesPerFrame) { passIndex ->
4146
val halfStep = computePass.addTask(halfStep.shader, numGroups = configRepo.numGroups).apply {
@@ -53,6 +58,8 @@ class FieldsMultiPasses(
5358
fields.params.set {
5459
maxVelocity.set(simulation.maxVelocity.toFloat())
5560
maxForce.set(simulation.maxForce.toFloat())
61+
targetVelocity.set(simulation.targetVelocity.toFloat())
62+
targetVelocityFixStrength.set(simulation.targetVelocityStrength.toFloat())
5663
}
5764
val count = configRepo.count
5865
fields.count = count

particles-kool/src/commonMain/kotlin/me/dvyy/particles/compute/simulation/FieldsShader.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class FieldsShader(
3636
val velocities = storage<KslFloat4>("velocities")
3737
val forces = storage<KslFloat4>("forces")
3838
val localNeighbours = storage<KslFloat1>("localNeighbours")
39+
val velocityData = storage<KslFloat1>("velocityData")
3940

4041
val particleTypes = storage<KslInt1>("particleTypes")
4142

@@ -188,10 +189,23 @@ class FieldsShader(
188189
`if`(length(nextVelocity) gt params.maxVelocity.ksl) {
189190
nextVelocity set normalize(nextVelocity) * params.maxVelocity.ksl
190191
}
192+
val target = params.targetVelocity.ksl
193+
val totalSqrtVelocities = float1Var(velocityData[0.const])
194+
val average = totalSqrtVelocities / count.toFloat1()
195+
// val halved = totalSqrtVelocities/2f.const
196+
// val degreesOfFreedom = 2f.const
197+
val strength = params.targetVelocityFixStrength.ksl
198+
// nudge particles towards target velocity
199+
nextVelocity set nextVelocity * sqrt(
200+
1f.const + (dT * strength) * ((target) / max(
201+
average,
202+
0.1f.const
203+
) - 1f.const)
204+
)
191205

192206
forces[id] = float4Value(nextForce, 0f)
193207
velocities[id] = float4Value(nextVelocity, 0f)
194-
localNeighbours[id] = localCount
208+
localNeighbours[id] = totalSqrtVelocities
195209
}
196210
}
197211

@@ -214,6 +228,7 @@ class FieldsShader(
214228
var localNeighbours by shader.storage("localNeighbours")
215229
var forces by shader.storage("forces")
216230
var particleTypes by shader.storage("particleTypes")
231+
var velocityData by shader.storage("velocityData")
217232
}
218233
//
219234
//context(scope: KslScopeBuilder)

particles-kool/src/commonMain/kotlin/me/dvyy/particles/compute/simulation/SimulationParametersStruct.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ import de.fabmax.kool.util.Struct
66
class SimulationParametersStruct : Struct("SimulationParametersStruct", MemoryLayout.Std140) {
77
val maxVelocity = float1()
88
val maxForce = float1()
9+
val targetVelocity = float1()
10+
val targetVelocityFixStrength = float1()
911
}

particles-kool/src/commonMain/kotlin/me/dvyy/particles/helpers/Buffers.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ object Buffers {
1717
}
1818

1919
fun randomPosition(size: Vec3f) = Vec4f(
20-
Random.Default.nextDouble(size.x.toDouble()).toFloat(),
21-
Random.Default.nextDouble(size.y.toDouble()).toFloat(),
22-
if (size.z == 0f) 0f else Random.Default.nextDouble(size.z.toDouble()).toFloat(),
20+
Random.nextDouble(size.x.toDouble()).toFloat(),
21+
Random.nextDouble(size.y.toDouble()).toFloat(),
22+
if (size.z == 0f) 0f else Random.nextDouble(size.z.toDouble()).toFloat(),
2323
0f
2424
)
2525

particles-kool/src/commonMain/kotlin/me/dvyy/particles/ui/viewmodels/ParticlesViewModel.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ class ParticlesViewModel(
5252
UiConfigurable.Slider("dT", state.dT, 0f, 0.01f, precision = 4) {
5353
updateState { copy(dT = it.toDouble()) }
5454
},
55+
UiConfigurable.Slider("Target Velocity", state.targetVelocity, 0f, 100f) {
56+
updateState { copy(targetVelocity = it.toDouble()) }
57+
},
58+
UiConfigurable.Slider("Targetting Strength", state.targetVelocityStrength, 0f, 100f) {
59+
updateState { copy(targetVelocityStrength = it.toDouble()) }
60+
},
5561
UiConfigurable.Slider("Max Velocity", state.maxVelocity, 0f, 100f) {
5662
updateState { copy(maxVelocity = it.toDouble()) }
5763
},
@@ -89,8 +95,8 @@ class ParticlesViewModel(
8995
}
9096

9197
suspend fun readbackMeanSquareVelocity() {
92-
val result = Float32Buffer(buffers.count)
93-
meanSquareData.readBack.downloadData(result)
98+
val result = Float32Buffer(1)
99+
meanSquareData.output.downloadData(result)
94100
val msqV = result[0] / buffers.count
95101
msqvOverTime.pushNewValueRight(msqV)
96102
meanSquareVelocity.update { msqV }

0 commit comments

Comments
 (0)