implementing water sim but not actually working

This commit is contained in:
Minjae Song
2018-12-13 04:45:09 +09:00
parent 1f1d6f1eda
commit 05a8f47006
6 changed files with 230 additions and 51 deletions

View File

@@ -598,7 +598,7 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
}
actorNowPlaying = newActor
WorldSimulator(actorNowPlaying, Terrarum.deltaTime)
//WorldSimulator(actorNowPlaying, Terrarum.deltaTime)
}
private fun changePossession(refid: Int) {

View File

@@ -1,17 +1,13 @@
package net.torvald.terrarum.modulebasegame.gameworld
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.roundInt
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.roundInt
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.gameworld.FluidCodex
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
/**
@@ -25,15 +21,20 @@ object WorldSimulator {
* In tiles;
* square width/height = field * 2
*/
const val FLUID_UPDATING_SQUARE_RADIUS = 64 // larger value will have dramatic impact on performance
const val FLUID_UPDATING_SQUARE_RADIUS = 80 // larger value will have dramatic impact on performance
const private val DOUBLE_RADIUS = FLUID_UPDATING_SQUARE_RADIUS * 2
// maps are separated as old-new for obvious reason, also it'll allow concurrent modification
private val fluidMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
private val fluidTypeMap = Array(DOUBLE_RADIUS, { IntArray(DOUBLE_RADIUS) })
private val fluidTypeMap = Array(DOUBLE_RADIUS, { Array<FluidType>(DOUBLE_RADIUS) { Fluid.NULL } })
private val fluidNewMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
private val fluidNewTypeMap = Array(DOUBLE_RADIUS, { Array<FluidType>(DOUBLE_RADIUS) { Fluid.NULL } })
const val FLUID_MAX_MASS = 1f // The normal, un-pressurized mass of a full water cell
const val FLUID_MAX_COMP = 0.02f // How much excess water a cell can store, compared to the cell above it
const val FLUID_MAX_COMP = 0.02f // How much excess water a cell can store, compared to the cell above it. A tile of fluid can contain more than MaxMass water.
const val FLUID_MIN_MASS = 0.0001f //Ignore cells that are almost dry
const val minFlow = 0.01f
const val maxSpeed = 1f // max units of water moved out of one block to another, per timestamp
// END OF FLUID-RELATED STUFFS
@@ -68,11 +69,160 @@ object WorldSimulator {
* TODO multithread
*/
fun moveFluids(delta: Float) {
////////////////////
// build fluidmap //
////////////////////
purgeFluidMap()
makeFluidMapFromWorld()
// before data: fluidMap/fluidTypeMap
// after data: fluidNewMap/fluidNewTypeMap
var flow = 0f
var remainingMass = 0f
for (y in 1 until fluidMap.size - 1) {
for (x in 1 until fluidMap[0].size - 1) {
val worldX = x + updateXFrom
val worldY = y + updateYFrom
// check solidity
if (isSolid(worldX, worldY)) continue
// check if the fluid is a same kind
//if (!isFlowable(type, worldX, worldY))) continue
// Custom push-only flow
flow = 0f
remainingMass = fluidMap[y][x]
if (remainingMass <= 0) continue
// The block below this one
if (!isSolid(worldX, worldY + 1)) { // TODO use isFlowable
flow = getStableStateB(remainingMass + fluidMap[y + 1][x]) - fluidMap[y + 1][x]
if (flow > minFlow) {
flow *= 0.5f // leads to smoother flow
}
flow.coerceIn(0f, minOf(maxSpeed, remainingMass))
fluidNewMap[y][x] -= flow
fluidNewMap[y + 1][x] += flow
remainingMass -= flow
}
if (remainingMass <= 0) continue
// Left
if (!isSolid(worldX - 1, worldY)) { // TODO use isFlowable
// Equalise the amount fo water in this block and its neighbour
flow = (fluidMap[y][x] - fluidMap[y][x - 1]) / 4f
if (flow > minFlow) {
flow *= 0.5f
}
flow.coerceIn(0f, remainingMass)
fluidNewMap[y][x] -= flow
fluidNewMap[y][x - 1] += flow
remainingMass -= flow
}
if (remainingMass <= 0) continue
// Right
if (!isSolid(worldX + 1, worldY)) { // TODO use isFlowable
// Equalise the amount fo water in this block and its neighbour
flow = (fluidMap[y][x] - fluidMap[y][x + 1]) / 4f
if (flow > minFlow) {
flow *= 0.5f
}
flow.coerceIn(0f, remainingMass)
fluidNewMap[y][x] -= flow
fluidNewMap[y][x + 1] += flow
remainingMass -= flow
}
if (remainingMass <= 0) continue
// Up; only compressed water flows upwards
if (!isSolid(worldX, worldY - 1)) { // TODO use isFlowable
flow = remainingMass - getStableStateB(remainingMass + fluidMap[y - 1][x])
if (flow > minFlow) {
flow *= 0.5f
}
flow.coerceIn(0f, minOf(maxSpeed, remainingMass))
fluidNewMap[y][x] -= flow
fluidNewMap[y - 1][x] += flow
remainingMass -= flow
}
}
}
fluidmapToWorld()
}
fun isFlowable(type: FluidType, worldX: Int, worldY: Int): Boolean {
val targetFluid = world.getFluid(worldX, worldY)
// true if target's type is the same as mine, or it's NULL (air)
return (targetFluid.type sameAs type || targetFluid.type sameAs Fluid.NULL)
}
fun isSolid(worldX: Int, worldY: Int): Boolean {
val tile = world.getTileFromTerrain(worldX, worldY)
if (tile != Block.FLUID_MARKER) {
// check for block properties isSolid
return BlockCodex[tile].isSolid
}
else {
// check for fluid
// no STATIC is implement yet, just return true
return true
}
}
/*
Explanation of get_stable_state_b (well, kind-of) :
if x <= 1, all water goes to the lower cell
* a = 0
* b = 1
if x > 1 & x < 2*MaxMass + MaxCompress, the lower cell should have MaxMass + (upper_cell/MaxMass) * MaxCompress
b = MaxMass + (a/MaxMass)*MaxCompress
a = x - b
->
b = MaxMass + ((x - b)/MaxMass)*MaxCompress ->
b = MaxMass + (x*MaxCompress - b*MaxCompress)/MaxMass
b*MaxMass = MaxMass^2 + (x*MaxCompress - b*MaxCompress)
b*(MaxMass + MaxCompress) = MaxMass*MaxMass + x*MaxCompress
* b = (MaxMass*MaxMass + x*MaxCompress)/(MaxMass + MaxCompress)
* a = x - b;
if x >= 2 * MaxMass + MaxCompress, the lower cell should have upper+MaxCompress
b = a + MaxCompress
a = x - b
->
b = x - b + MaxCompress ->
2b = x + MaxCompress ->
* b = (x + MaxCompress)/2
* a = x - b
*/
private fun getStableStateB(totalMass: Float): Float {
if (totalMass <= 1)
return 1f
else if (totalMass < 2f * FLUID_MAX_MASS + FLUID_MAX_COMP)
return (FLUID_MAX_MASS * FLUID_MAX_MASS + totalMass * FLUID_MAX_COMP) / (FLUID_MAX_MASS + FLUID_MAX_COMP)
else
return (totalMass + FLUID_MAX_COMP) / 2f
}
/**
@@ -109,11 +259,22 @@ object WorldSimulator {
}
private fun purgeFluidMap() {
for (y in 1..DOUBLE_RADIUS) {
for (x in 1..DOUBLE_RADIUS) {
fluidMap[y - 1][x - 1] = 0f
fluidTypeMap[y - 1][x - 1] = Fluid.NULL
private fun makeFluidMapFromWorld() {
for (y in 0 until fluidMap.size) {
for (x in 0 until fluidMap[0].size) {
val fluidData = world.getFluid(x + updateXFrom, y + updateYFrom)
fluidMap[y][x] = fluidData.amount
fluidTypeMap[y][x] = fluidData.type
fluidNewMap[y][x] = fluidData.amount
fluidNewTypeMap[y][x] = fluidData.type
}
}
}
private fun fluidmapToWorld() {
for (y in 0 until fluidMap.size) {
for (x in 0 until fluidMap[0].size) {
world.setFluid(x + updateXFrom, y + updateYFrom, fluidNewTypeMap[y][x], fluidNewMap[y][x])
}
}
}