integrate world renderer with new chunk format (performance optimizations)

This commit is contained in:
Bixilon 2021-11-15 11:08:43 +01:00
parent 6032900b42
commit a147fb3c02
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
6 changed files with 173 additions and 163 deletions

View File

@ -153,7 +153,7 @@ class Chunk(
val neighbours: Array<Chunk> = world.getChunkNeighbours(chunkPosition).unsafeCast()
val cacheBiomeAccessor = world.cacheBiomeAccessor
if (cacheBiomeAccessor != null && biomesInitialized && neighboursLoaded) {
section.buildBiomeCache(chunkPosition, sectionHeight, this, neighbours, cacheBiomeAccessor, world)
section.buildBiomeCache(chunkPosition, sectionHeight, this, neighbours, cacheBiomeAccessor)
}
sections[sectionIndex] = section
}
@ -195,7 +195,7 @@ class Chunk(
for ((sectionIndex, section) in sections!!.withIndex()) {
section ?: continue
val sectionHeight = sectionIndex + lowestSection
section.buildBiomeCache(chunkPosition, sectionHeight, this, neighbours, cacheBiomeAccessor, world)
section.buildBiomeCache(chunkPosition, sectionHeight, this, neighbours, cacheBiomeAccessor)
}
biomesInitialized = true
}
@ -211,7 +211,7 @@ class Chunk(
if (section == null) {
// ToDo: Faster
val chunkPosition = Vec3i(x, y, z).chunkPosition
return connection.world.cacheBiomeAccessor?.getBiome(x, y, z, chunkPosition.x, chunkPosition.y, this, null, world)
return connection.world.cacheBiomeAccessor?.getBiome(x, y, z, chunkPosition.x, chunkPosition.y, this, null)
}
return section.biomes[x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z]
}

View File

@ -89,7 +89,7 @@ class ChunkSection(
}
}
fun buildBiomeCache(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, neighbours: Array<Chunk>, biomeAccessor: NoiseBiomeAccessor, world: World) {
fun buildBiomeCache(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, neighbours: Array<Chunk>, biomeAccessor: NoiseBiomeAccessor) {
val chunkPositionX = chunkPosition.x
val chunkPositionZ = chunkPosition.y
val blockOffset = Vec3i.of(chunkPosition, sectionHeight)
@ -98,7 +98,7 @@ class ChunkSection(
val z = blockOffset.z
val biomes: Array<Biome?> = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION)
for (index in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) {
biomes[index] = biomeAccessor.getBiome(x + (index and 0x0F), y + ((index shr 8) and 0x0F), z + ((index shr 4) and 0x0F), chunkPositionX, chunkPositionZ, chunk, neighbours, world) //!!
biomes[index] = biomeAccessor.getBiome(x + (index and 0x0F), y + ((index shr 8) and 0x0F), z + ((index shr 4) and 0x0F), chunkPositionX, chunkPositionZ, chunk, neighbours) //!!
}
this.biomes.setData(biomes.unsafeCast())
}

View File

@ -17,12 +17,13 @@ import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.Chunk
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.biome.noise.FuzzyNoiseBiomeCalculator
import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
import de.bixilon.minosoft.util.MMath
import glm_.vec2.Vec2i
class NoiseBiomeAccessor(private val world: World) {
fun getBiome(x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?, world: World): Biome? {
fun getBiome(x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?): Biome? {
val biomeY = if (world.dimension?.supports3DBiomes == true) {
y
} else {
@ -40,6 +41,139 @@ class NoiseBiomeAccessor(private val world: World) {
return null
}
return FuzzyNoiseBiomeCalculator.getBiome(world.hashedSeed, x, biomeY, z, chunkPositionX, chunkPositionZ, chunk, neighbours, world)
return getBiome(world.hashedSeed, x, biomeY, z, chunkPositionX, chunkPositionZ, chunk, neighbours)
}
private fun getBiome(seed: Long, x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?): Biome? {
val m = x - 2
val n = y - 2
val o = z - 2
val p = m shr 2
val q = n shr 2
val r = o shr 2
val d = (m and 0x03) / 4.0
val e = (n and 0x03) / 4.0
val f = (o and 0x03) / 4.0
var s = 0
var g = Double.POSITIVE_INFINITY
for (i in 0 until 8) {
var u = p
var xFraction = d
if (i and 0x04 != 0) {
u++
xFraction -= 1.0
}
var v = q
var yFraction = e
if (i and 0x02 != 0) {
v++
yFraction -= 1.0
}
var w = r
var zFraction = f
if (i and 0x01 != 0) {
w++
zFraction -= 1.0
}
val d3 = calculateFiddle(seed, u, v, w, xFraction, yFraction, zFraction)
if (g > d3) {
s = i
g = d3
}
}
var biomeX = p
if (s and 0x04 != 0) {
biomeX++
}
var biomeY = q
if (s and 0x02 != 0) {
biomeY++
}
var biomeZ = r
if (s and 0x01 != 0) {
biomeZ++
}
var biomeChunk: Chunk? = null
val biomeChunkX = biomeX shr 2
val biomeChunkZ = biomeZ shr 2
if (neighbours == null) {
return world[Vec2i(biomeChunkX, biomeChunkZ)]?.biomeSource?.getBiome(biomeX, biomeY, biomeZ)
}
val deltaChunkX = biomeChunkX - chunkPositionX
val deltaChunkZ = biomeChunkZ - chunkPositionZ
when (deltaChunkX) {
0 -> when (deltaChunkZ) {
0 -> biomeChunk = chunk
-1 -> biomeChunk = neighbours[3]
1 -> biomeChunk = neighbours[4]
}
-1 -> when (deltaChunkZ) {
0 -> biomeChunk = neighbours[1]
-1 -> biomeChunk = neighbours[0]
1 -> biomeChunk = neighbours[2]
}
1 -> when (deltaChunkZ) {
0 -> biomeChunk = neighbours[6]
-1 -> biomeChunk = neighbours[5]
1 -> biomeChunk = neighbours[7]
}
}
return biomeChunk?.biomeSource?.getBiome(biomeX, biomeY, biomeZ)
}
private fun calculateFiddle(seed: Long, x: Int, y: Int, z: Int, xFraction: Double, yFraction: Double, zFraction: Double): Double {
var ret = seed
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
val xFractionSalt = distribute(ret)
ret = next(ret, seed)
val yFractionSalt = distribute(ret)
ret = next(ret, seed)
val zFractionSalt = distribute(ret)
return MMath.square(xFraction + xFractionSalt) + MMath.square(yFraction + yFractionSalt) + MMath.square(zFraction + zFractionSalt)
}
private fun distribute(seed: Long): Double {
val d = Math.floorMod(seed shr 24, 1024L).toInt() / 1024.0
return (d - 0.5) * 0.9
}
// https://en.wikipedia.org/wiki/Linear_congruential_generator
private fun next(seed: Long): Long {
return seed * (seed * 6364136223846793005L + 1442695040888963407L)
}
private fun next(seed: Long, salt: Int): Long {
return next(seed) + salt
}
private fun next(seed: Long, salt: Long): Long {
return next(seed) + salt
}
}

View File

@ -1,142 +0,0 @@
package de.bixilon.minosoft.data.world.biome.noise
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.Chunk
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.util.MMath.square
import glm_.vec2.Vec2i
object FuzzyNoiseBiomeCalculator {
fun getBiome(seed: Long, x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?, world: World): Biome? {
val m = x - 2
val n = y - 2
val o = z - 2
val p = m shr 2
val q = n shr 2
val r = o shr 2
val d = (m and 0x03) / 4.0
val e = (n and 0x03) / 4.0
val f = (o and 0x03) / 4.0
var s = 0
var g = Double.POSITIVE_INFINITY
for (i in 0 until 8) {
var u = p
var xFraction = d
if (i and 0x04 != 0) {
u++
xFraction -= 1.0
}
var v = q
var yFraction = e
if (i and 0x02 != 0) {
v++
yFraction -= 1.0
}
var w = r
var zFraction = f
if (i and 0x01 != 0) {
w++
zFraction -= 1.0
}
val d3 = calculateFiddle(seed, u, v, w, xFraction, yFraction, zFraction)
if (g > d3) {
s = i
g = d3
}
}
var biomeX = p
if (s and 0x04 != 0) {
biomeX++
}
var biomeY = q
if (s and 0x02 != 0) {
biomeY++
}
var biomeZ = r
if (s and 0x01 != 0) {
biomeZ++
}
var biomeChunk: Chunk? = null
val biomeChunkX = biomeX shr 2
val biomeChunkZ = biomeZ shr 2
if (neighbours == null) {
return world[Vec2i(biomeChunkX, biomeChunkZ)]?.biomeSource?.getBiome(biomeX, biomeY, biomeZ)
}
val deltaChunkX = biomeChunkX - chunkPositionX
val deltaChunkZ = biomeChunkZ - chunkPositionZ
when (deltaChunkX) {
0 -> when (deltaChunkZ) {
0 -> biomeChunk = chunk
-1 -> biomeChunk = neighbours[3]
1 -> biomeChunk = neighbours[4]
}
-1 -> when (deltaChunkZ) {
0 -> biomeChunk = neighbours[1]
-1 -> biomeChunk = neighbours[0]
1 -> biomeChunk = neighbours[2]
}
1 -> when (deltaChunkZ) {
0 -> biomeChunk = neighbours[6]
-1 -> biomeChunk = neighbours[5]
1 -> biomeChunk = neighbours[7]
}
}
return biomeChunk?.biomeSource?.getBiome(biomeX, biomeY, biomeZ)
}
private fun calculateFiddle(seed: Long, x: Int, y: Int, z: Int, xFraction: Double, yFraction: Double, zFraction: Double): Double {
var ret = seed
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
ret = next(ret, x)
ret = next(ret, y)
ret = next(ret, z)
val xFractionSalt = distribute(ret)
ret = next(ret, seed)
val yFractionSalt = distribute(ret)
ret = next(ret, seed)
val zFractionSalt = distribute(ret)
return square(xFraction + xFractionSalt) + square(yFraction + yFractionSalt) + square(zFraction + zFractionSalt)
}
private fun distribute(seed: Long): Double {
val d = Math.floorMod(seed shr 24, 1024L).toInt() / 1024.0
return (d - 0.5) * 0.9
}
// https://en.wikipedia.org/wiki/Linear_congruential_generator
private fun next(seed: Long): Long {
return seed * (seed * 6364136223846793005L + 1442695040888963407L)
}
private fun next(seed: Long, salt: Int): Long {
return next(seed) + salt
}
private fun next(seed: Long, salt: Long): Long {
return next(seed) + salt
}
}

View File

@ -7,6 +7,8 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMeshes
import de.bixilon.minosoft.gui.rendering.util.VecUtil
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.chunk.ChunkUtil.acquire
import de.bixilon.minosoft.util.chunk.ChunkUtil.release
import glm_.vec2.Vec2i
import glm_.vec3.Vec3i
import java.util.*
@ -21,6 +23,8 @@ class CullSectionPreparer(
val random = Random(0L)
val blocks = section.blocks
section.acquire()
neighbours.acquire()
var block: BlockState?
val neighbourBlocks: Array<BlockState?> = arrayOfNulls(Directions.SIZE)
@ -31,41 +35,41 @@ class CullSectionPreparer(
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
block = blocks[ChunkSection.getIndex(x, y, z)]
block = blocks.unsafeGet(x, y, z)
val model = block?.model ?: continue
// ToDo: Chunk borders
neighbourBlocks[Directions.DOWN.ordinal] = if (y == 0) {
neighbours[Directions.DOWN.ordinal]?.blocks?.get(ChunkSection.getIndex(x, ProtocolDefinition.SECTION_MAX_Y, z))
neighbours[Directions.DOWN.ordinal]?.blocks?.unsafeGet(x, ProtocolDefinition.SECTION_MAX_Y, z)
} else {
blocks[ChunkSection.getIndex(x, y - 1, z)]
blocks.unsafeGet(x, y - 1, z)
}
neighbourBlocks[Directions.UP.ordinal] = if (y == ProtocolDefinition.SECTION_MAX_Y) {
neighbours[Directions.UP.ordinal]?.blocks?.get(ChunkSection.getIndex(x, 0, z))
neighbours[Directions.UP.ordinal]?.blocks?.unsafeGet(x, 0, z)
} else {
blocks[ChunkSection.getIndex(x, y + 1, z)]
blocks.unsafeGet(x, y + 1, z)
}
neighbourBlocks[Directions.NORTH.ordinal] = if (z == 0) {
neighbours[Directions.NORTH.ordinal]?.blocks?.get(ChunkSection.getIndex(x, y, ProtocolDefinition.SECTION_MAX_Z))
neighbours[Directions.NORTH.ordinal]?.blocks?.unsafeGet(x, y, ProtocolDefinition.SECTION_MAX_Z)
} else {
blocks[ChunkSection.getIndex(x, y, z - 1)]
blocks.unsafeGet(x, y, z - 1)
}
neighbourBlocks[Directions.SOUTH.ordinal] = if (z == ProtocolDefinition.SECTION_MAX_Z) {
neighbours[Directions.SOUTH.ordinal]?.blocks?.get(ChunkSection.getIndex(x, y, 0))
neighbours[Directions.SOUTH.ordinal]?.blocks?.unsafeGet(x, y, 0)
} else {
blocks[ChunkSection.getIndex(x, y, z + 1)]
blocks.unsafeGet(x, y, z + 1)
}
neighbourBlocks[Directions.WEST.ordinal] = if (x == 0) {
neighbours[Directions.WEST.ordinal]?.blocks?.get(ChunkSection.getIndex(ProtocolDefinition.SECTION_MAX_X, y, z))
neighbours[Directions.WEST.ordinal]?.blocks?.unsafeGet(ProtocolDefinition.SECTION_MAX_X, y, z)
} else {
blocks[ChunkSection.getIndex(x - 1, y, z)]
blocks.unsafeGet(x - 1, y, z)
}
neighbourBlocks[Directions.EAST.ordinal] = if (x == ProtocolDefinition.SECTION_MAX_X) {
neighbours[Directions.EAST.ordinal]?.blocks?.get(ChunkSection.getIndex(0, y, z))
neighbours[Directions.EAST.ordinal]?.blocks?.unsafeGet(0, y, z)
} else {
blocks[ChunkSection.getIndex(x + 1, y, z)]
blocks.unsafeGet(x + 1, y, z)
}
val position = Vec3i(offsetX + x, offsetY + y, offsetZ + z)
@ -77,6 +81,8 @@ class CullSectionPreparer(
}
}
}
section.release()
neighbours.release()
return mesh

View File

@ -272,4 +272,16 @@ object ChunkUtil {
neighbourChunks[6][sectionHeight],
)
}
fun Array<ChunkSection?>.acquire() {
for (section in this) {
section?.acquire()
}
}
fun Array<ChunkSection?>.release() {
for (section in this) {
section?.release()
}
}
}