diff --git a/doc/rendering/world_renderer.md b/doc/rendering/world_renderer.md index 9919528b2..5b0df37c6 100644 --- a/doc/rendering/world_renderer.md +++ b/doc/rendering/world_renderer.md @@ -43,7 +43,6 @@ - ToDo: - - Thread safety - Build biome cache only in render distance - Update neighbour chunks if needed - Reduce memory usage diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/crash/ErosCrashReport.kt b/src/main/java/de/bixilon/minosoft/gui/eros/crash/ErosCrashReport.kt index 51415fb7e..1e42b9e40 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/crash/ErosCrashReport.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/crash/ErosCrashReport.kt @@ -105,6 +105,7 @@ class ErosCrashReport : JavaFXWindowController() { "Written while driving in a FlixBus", "Coded while traveling in the ICE 272 towards Hamburg-Altona", "Sorry, the ICE 693 drive towards Munich was really long", + "Coded while playing bedwars" ) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt index 46754bb77..2d5ee69be 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt @@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.block import de.bixilon.minosoft.data.assets.AssetsUtil import de.bixilon.minosoft.data.assets.Resources +import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.world.Chunk import de.bixilon.minosoft.data.world.ChunkSection @@ -35,17 +36,15 @@ import de.bixilon.minosoft.gui.rendering.system.base.phases.TransparentDrawable import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition import de.bixilon.minosoft.gui.rendering.util.VecUtil.empty import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkSectionPosition +import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight import de.bixilon.minosoft.gui.rendering.util.VecUtil.of import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight -import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3 import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY +import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.toVec3 import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.toVec3 -import de.bixilon.minosoft.modding.event.events.BlockSetEvent -import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent -import de.bixilon.minosoft.modding.event.events.ChunkUnloadEvent -import de.bixilon.minosoft.modding.event.events.RespawnEvent +import de.bixilon.minosoft.modding.event.events.* import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -169,28 +168,56 @@ class WorldRenderer( queueSection(Vec2i(chunkPosition.x + 1, chunkPosition.y), sectionHeight) } }) - /* + connection.registerEvent(CallbackEventInvoker.of { - val chunk = world[it.chunkPosition] ?: return@of - if (!chunk.isFullyLoaded || it.chunkPosition in incomplete) { + val chunk = world[it.chunkPosition] ?: return@of // should not happen + if (!chunk.isFullyLoaded) { return@of } - val neighbourChunks = world.getChunkNeighbours(it.chunkPosition) - if (!neighbourChunks.fullyLoaded) { - return@of - } - val sectionHeights: MutableSet = mutableSetOf() + val sectionHeights: MutableMap = mutableMapOf() for (blockPosition in it.blocks.keys) { - sectionHeights += blockPosition.sectionHeight + val neighbours = sectionHeights.getOrPut(blockPosition.sectionHeight) { BooleanArray(Directions.SIZE) } + val inSectionHeight = blockPosition.y.inSectionHeight + if (inSectionHeight == 0) { + neighbours[0] = true + } else if (inSectionHeight == ProtocolDefinition.SECTION_MAX_Y) { + neighbours[1] = true + } + if (blockPosition.z == 0) { + neighbours[2] = true + } else if (blockPosition.z == ProtocolDefinition.SECTION_MAX_Z) { + neighbours[3] = true + } + if (blockPosition.x == 0) { + neighbours[4] = true + } else if (blockPosition.x == ProtocolDefinition.SECTION_MAX_X) { + neighbours[5] = true + } } - renderWindow.queue += { - val meshes = loadedMeshes.getOrPut(it.chunkPosition) { mutableMapOf() } - for (sectionHeight in sectionHeights) { - // updateSection(it.chunkPosition, sectionHeight, chunk, neighbourChunks.unsafeCast(), meshes) + for ((sectionHeight, neighbourUpdates) in sectionHeights) { + queueSection(it.chunkPosition, sectionHeight, chunk) + + if (neighbourUpdates[0]) { + queueSection(it.chunkPosition, sectionHeight - 1, chunk) + } + if (neighbourUpdates[1]) { + queueSection(it.chunkPosition, sectionHeight + 1, chunk) + } + if (neighbourUpdates[2]) { + queueSection(Vec2i(it.chunkPosition.x, it.chunkPosition.y - 1), sectionHeight) + } + if (neighbourUpdates[3]) { + queueSection(Vec2i(it.chunkPosition.x, it.chunkPosition.y + 1), sectionHeight) + } + if (neighbourUpdates[4]) { + queueSection(Vec2i(it.chunkPosition.x - 1, it.chunkPosition.y), sectionHeight) + } + if (neighbourUpdates[5]) { + queueSection(Vec2i(it.chunkPosition.x + 1, it.chunkPosition.y), sectionHeight) } } }) - */ + connection.registerEvent(CallbackEventInvoker.of { unloadChunk(it.chunkPosition) }) } @@ -253,6 +280,28 @@ class WorldRenderer( meshesToUnloadLock.unlock() } + private fun addMesh(mesh: ChunkSectionMeshes, visibleOpaque: MutableList = this.visibleOpaque, visibleTranslucent: MutableList = this.visibleTranslucent, visibleTransparent: MutableList = this.visibleTransparent) { + val distance = (cameraPosition - mesh.center).length2() + mesh.opaqueMesh?.let { + it.distance = distance + visibleOpaque += it + } + mesh.translucentMesh?.let { + it.distance = distance + visibleTranslucent += it + } + mesh.transparentMesh?.let { + it.distance = distance + visibleTransparent += it + } + } + + private fun sortVisible(visibleOpaque: MutableList = this.visibleOpaque, visibleTranslucent: MutableList = this.visibleTranslucent, visibleTransparent: MutableList = this.visibleTransparent) { + visibleOpaque.sortBy { it.distance } + visibleTranslucent.sortBy { -it.distance } + visibleTransparent.sortBy { it.distance } + } + private fun removeMesh(mesh: ChunkSectionMeshes) { mesh.opaqueMesh?.let { visibleOpaque -= it } mesh.translucentMesh?.let { visibleTranslucent -= it } @@ -324,13 +373,13 @@ class WorldRenderer( } private fun internalQueueSection(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, section: ChunkSection, ignoreFrustum: Boolean): Boolean { - if (!chunk.isFullyLoaded || section.blocks.isEmpty) { + if (!chunk.isFullyLoaded || section.blocks.isEmpty) { // ToDo: Unload if empty return false } - val visible = ignoreFrustum || isChunkVisible(chunkPosition, sectionHeight, section.blocks.minPosition, section.blocks.maxPosition) + val visible = ignoreFrustum || isSectionVisible(chunkPosition, sectionHeight, section.blocks.minPosition, section.blocks.maxPosition, true) if (visible) { - val item = WorldQueueItem(chunkPosition, sectionHeight, chunk, section, Vec3i.of(chunkPosition, sectionHeight, Vec3i(8, 8, 8)).toVec3(), null) + val item = WorldQueueItem(chunkPosition, sectionHeight, chunk, section, Vec3i.of(chunkPosition, sectionHeight).toVec3() + CHUNK_CENTER, null) queueLock.lock() queue.removeAll { it == item } // Prevent duplicated entries (to not prepare the same chunk twice (if it changed and was not prepared yet or ...) if (chunkPosition == cameraChunkPosition) { @@ -365,6 +414,7 @@ class WorldRenderer( return } + // ToDo: Check if chunk is visible (not section, chunk) var queueChanges = 0 for (sectionHeight in chunk.lowestSection until chunk.highestSection) { val section = chunk[sectionHeight] ?: continue @@ -386,6 +436,7 @@ class WorldRenderer( return } + var addedMeshes = 0 val time = System.currentTimeMillis() val maxTime = if (connection.player.velocity.empty) 50L else 20L // If the player is still, then we can load more chunks (to not cause lags) @@ -393,12 +444,12 @@ class WorldRenderer( val item = meshesToLoad.removeAt(0) val mesh = item.mesh ?: throw IllegalStateException("Mesh of queued item is null!") mesh.load() - val visible = isChunkVisible(item.chunkPosition, item.sectionHeight, mesh.minPosition, mesh.maxPosition) + val visible = isSectionVisible(item.chunkPosition, item.sectionHeight, mesh.minPosition, mesh.maxPosition, true) if (visible) { - mesh.opaqueMesh?.let { visibleOpaque += it } - mesh.translucentMesh?.let { visibleTranslucent += it } - mesh.transparentMesh?.let { visibleTransparent += it } + addMesh(mesh) + addedMeshes++ } + loadedMeshesLock.lock() val meshes = loadedMeshes.getOrPut(item.chunkPosition) { mutableMapOf() } @@ -409,6 +460,10 @@ class WorldRenderer( loadedMeshesLock.unlock() } meshesToLoadLock.release() + + if (addedMeshes > 0) { + sortVisible() + } } private fun unloadMeshes() { @@ -473,8 +528,12 @@ class WorldRenderer( } } - private fun isChunkVisible(chunkPosition: Vec2i, sectionHeight: Int, minPosition: Vec3i, maxPosition: Vec3i): Boolean { - if (!chunkPosition.isInRenderDistance(cameraChunkPosition)) { + private fun isChunkVisible(chunkPosition: Vec2i): Boolean { + return chunkPosition.isInRenderDistance(cameraChunkPosition) + } + + private fun isSectionVisible(chunkPosition: Vec2i, sectionHeight: Int, minPosition: Vec3i, maxPosition: Vec3i, checkChunk: Boolean): Boolean { + if (checkChunk && !isChunkVisible(chunkPosition)) { return false } // ToDo: Cave culling, frustum clipping, improve performance @@ -482,19 +541,28 @@ class WorldRenderer( } private fun onFrustumChange() { + var sortQueue = false + val cameraPosition = connection.player.cameraPosition.toVec3() + if (this.cameraPosition != cameraPosition) { + this.cameraPosition = cameraPosition + this.cameraChunkPosition = connection.player.positionInfo.chunkPosition + sortQueue = true + } + val visibleOpaque: MutableList = mutableListOf() val visibleTranslucent: MutableList = mutableListOf() val visibleTransparent: MutableList = mutableListOf() loadedMeshesLock.acquire() for ((chunkPosition, meshes) in this.loadedMeshes) { + if (!isChunkVisible(chunkPosition)) { + continue + } for ((sectionHeight, mesh) in meshes) { - if (!isChunkVisible(chunkPosition, sectionHeight, mesh.minPosition, mesh.maxPosition)) { + if (!isSectionVisible(chunkPosition, sectionHeight, mesh.minPosition, mesh.maxPosition, false)) { continue } - mesh.opaqueMesh?.let { visibleOpaque += it } - mesh.translucentMesh?.let { visibleTranslucent += it } - mesh.transparentMesh?.let { visibleTransparent += it } + addMesh(mesh, visibleOpaque, visibleTranslucent, visibleTransparent) } } loadedMeshesLock.release() @@ -502,9 +570,12 @@ class WorldRenderer( culledQueueLock.acquire() val queue: MutableMap> = mutableMapOf() // The queue method needs the full lock of the culledQueue for ((chunkPosition, sectionHeights) in this.culledQueue) { + if (!isChunkVisible(chunkPosition)) { + continue + } var chunkQueue: MutableSet? = null for (sectionHeight in sectionHeights) { - if (!isChunkVisible(chunkPosition, sectionHeight, Vec3i.EMPTY, Vec3i(16))) { + if (!isSectionVisible(chunkPosition, sectionHeight, Vec3i.EMPTY, Vec3i(16), false)) { continue } if (chunkQueue == null) { @@ -513,6 +584,7 @@ class WorldRenderer( chunkQueue += sectionHeight } } + culledQueueLock.release() @@ -537,20 +609,13 @@ class WorldRenderer( culledQueueLock.release() - val cameraPosition = connection.player.cameraPosition.toVec3 + sortVisible(visibleOpaque, visibleTranslucent, visibleTransparent) - visibleOpaque.sortBy { (it.center - cameraPosition).length2() } this.visibleOpaque = visibleOpaque - - visibleTranslucent.sortBy { -(it.center - cameraPosition).length2() } this.visibleTranslucent = visibleTranslucent - - visibleTransparent.sortBy { (it.center - cameraPosition).length2() } this.visibleTransparent = visibleTransparent - if (this.cameraPosition != cameraPosition) { - this.cameraPosition = cameraPosition - this.cameraChunkPosition = connection.player.positionInfo.chunkPosition + if (sortQueue) { sortQueue() } } @@ -558,6 +623,7 @@ class WorldRenderer( companion object : RendererBuilder { override val RESOURCE_LOCATION = ResourceLocation("minosoft:world_renderer") + private val CHUNK_CENTER = Vec3(8.0f) override fun build(connection: PlayConnection, renderWindow: RenderWindow): WorldRenderer { return WorldRenderer(connection, renderWindow) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMesh.kt index a5e071fb1..d8520f4a0 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMesh.kt @@ -21,7 +21,8 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct import glm_.vec2.Vec2 import glm_.vec3.Vec3 -class ChunkSectionMesh(renderWindow: RenderWindow, initialCacheSize: Int, val center: Vec3) : Mesh(renderWindow, SectionArrayMeshStruct, initialCacheSize = initialCacheSize) { +class ChunkSectionMesh(renderWindow: RenderWindow, initialCacheSize: Int) : Mesh(renderWindow, SectionArrayMeshStruct, initialCacheSize = initialCacheSize) { + var distance: Float = 0.0f // Used for sorting fun addVertex(position: FloatArray, uv: Vec2, texture: AbstractTexture, tintColor: Int, light: Int) { val transformedUV = texture.renderData?.transformUV(uv) ?: uv diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshes.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshes.kt index f1649e037..494622db1 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshes.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshes.kt @@ -24,11 +24,12 @@ class ChunkSectionMeshes( val chunkPosition: Vec2i, val sectionHeight: Int, ) { - var opaqueMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 100000, Vec3(Vec3i.of(chunkPosition, sectionHeight, Vec3i(8, 8, 8)))) + val center: Vec3 = Vec3(Vec3i.of(chunkPosition, sectionHeight, Vec3i(8, 8, 8))) + var opaqueMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 100000) private set - var translucentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 10000, Vec3(Vec3i.of(chunkPosition, sectionHeight, Vec3i(8, 8, 8)))) + var translucentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 10000) private set - var transparentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 20000, Vec3(Vec3i.of(chunkPosition, sectionHeight, Vec3i(8, 8, 8)))) + var transparentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 20000) private set // used for frustum culling diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt index c0c7e30ad..4e1003aae 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt @@ -23,7 +23,6 @@ import de.bixilon.minosoft.gui.rendering.models.baked.block.GreedyBakedBlockMode import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.SECTION_SIZE import de.bixilon.minosoft.util.KUtil.decide import glm_.vec2.Vec2i -import glm_.vec3.Vec3 import glm_.vec3.Vec3i import java.util.* @@ -41,7 +40,7 @@ class GreedySectionPreparer( // base taken from https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/ @Deprecated("TODO") override fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array): ChunkSectionMeshes { - val mesh = ChunkSectionMesh(renderWindow, 20000, Vec3()) + val mesh = ChunkSectionMesh(renderWindow, 20000) val random = Random(0L) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt index ab519faff..767e52a95 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt @@ -25,6 +25,7 @@ import de.bixilon.minosoft.data.registries.fluid.DefaultFluids import de.bixilon.minosoft.data.text.ChatColors import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderWindow +import de.bixilon.minosoft.gui.rendering.input.camera.frustum.Frustum import de.bixilon.minosoft.gui.rendering.input.camera.hit.BlockRaycastHit import de.bixilon.minosoft.gui.rendering.input.camera.hit.EntityRaycastHit import de.bixilon.minosoft.gui.rendering.input.camera.hit.FluidRaycastHit diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Frustum.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/frustum/Frustum.kt similarity index 75% rename from src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Frustum.kt rename to src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/frustum/Frustum.kt index cbe6a5489..932e880bc 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Frustum.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/frustum/Frustum.kt @@ -11,33 +11,35 @@ * This software is not affiliated with Mojang AB, the original developer of Minecraft. */ -package de.bixilon.minosoft.gui.rendering.input.camera +package de.bixilon.minosoft.gui.rendering.input.camera.frustum import de.bixilon.minosoft.data.registries.AABB import de.bixilon.minosoft.gui.rendering.RenderConstants +import de.bixilon.minosoft.gui.rendering.input.camera.Camera import de.bixilon.minosoft.gui.rendering.util.VecUtil.of +import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4Util.dot import de.bixilon.minosoft.util.KUtil import de.bixilon.minosoft.util.KUtil.get import de.bixilon.minosoft.util.enum.ValuesEnum import glm_.mat3x3.Mat3 +import glm_.mat4x4.Mat4 import glm_.vec2.Vec2i import glm_.vec3.Vec3 import glm_.vec3.Vec3i -import glm_.vec4.Vec4d +import glm_.vec4.Vec4 -// Bit thanks to: https://gist.github.com/podgorskiy/e698d18879588ada9014768e3e82a644 +// Big thanks to: https://gist.github.com/podgorskiy/e698d18879588ada9014768e3e82a644 class Frustum(private val camera: Camera) { - private var normals: List = listOf() - private var planes: List = listOf() + private lateinit var data: FrustumData init { recalculate() } fun recalculate() { - val matrix = camera.viewProjectionMatrix.transpose() - val planes = listOf( + val matrix = Mat4(camera.viewProjectionMatrix).transpose() + val planes = arrayOf( matrix[3] + matrix[0], matrix[3] - matrix[0], @@ -48,7 +50,7 @@ class Frustum(private val camera: Camera) { matrix[3] - matrix[2], ) - val crosses = listOf( + val crosses = arrayOf( Vec3(planes[Planes.LEFT]) cross Vec3(planes[Planes.RIGHT]), Vec3(planes[Planes.LEFT]) cross Vec3(planes[Planes.BOTTOM]), Vec3(planes[Planes.LEFT]) cross Vec3(planes[Planes.TOP]), @@ -80,7 +82,7 @@ class Frustum(private val camera: Camera) { return res * (-1.0f / d) } - val normals: List = listOf( + val normals = arrayOf( intersections(Planes.LEFT, Planes.BOTTOM, Planes.NEAR), intersections(Planes.LEFT, Planes.TOP, Planes.NEAR), intersections(Planes.RIGHT, Planes.BOTTOM, Planes.NEAR), @@ -92,13 +94,7 @@ class Frustum(private val camera: Camera) { intersections(Planes.RIGHT, Planes.TOP, Planes.FAR), ) - synchronized(this.normals) { - this.normals = normals - } - - synchronized(this.planes) { - this.planes = planes - } + this.data = FrustumData(normals, planes) } @@ -106,42 +102,36 @@ class Frustum(private val camera: Camera) { if (!RenderConstants.FRUSTUM_CULLING_ENABLED) { return true } + val (normals, planes) = this.data + val minArray = min.array + val maxArray = max.array - val normals: List - synchronized(this.normals) { - normals = this.normals - } - val planes: List - synchronized(this.planes) { - planes = this.planes - } - - for (i in 0 until Planes.VALUES.size) { - if ( - (planes[i] dot Vec4d(min.x, min.y, min.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(max.x, min.y, min.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(min.x, max.y, min.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(max.x, max.y, min.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(min.x, min.y, max.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(max.x, min.y, max.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(min.x, max.y, max.z, 1.0f)) < 0.0f - && (planes[i] dot Vec4d(max.x, max.y, max.z, 1.0f)) < 0.0f + for (i in 0 until Planes.SIZE) { + val plane = planes[i].array + if (plane.dot(minArray[0], minArray[1], minArray[2]) < 0.0f + && plane.dot(maxArray[0], minArray[1], minArray[2]) < 0.0f + && plane.dot(minArray[0], maxArray[1], minArray[2]) < 0.0f + && plane.dot(maxArray[0], maxArray[1], minArray[2]) < 0.0f + && plane.dot(minArray[0], minArray[1], maxArray[2]) < 0.0f + && plane.dot(maxArray[0], minArray[1], maxArray[2]) < 0.0f + && plane.dot(minArray[0], maxArray[1], maxArray[2]) < 0.0f + && plane.dot(maxArray[0], maxArray[1], maxArray[2]) < 0.0f ) { return false } } fun checkPoint(check: (Vec3) -> Boolean): Boolean { - var out = 0 + var successFullChecks = 0 for (i in 0 until 8) { if (check(normals[i])) { - out++ + successFullChecks++ } } - return out == 8 + return successFullChecks == 8 } - val checks: List<(Vec3) -> Boolean> = listOf( + val checks: Array<(Vec3) -> Boolean> = arrayOf( { it.x > max.x }, { it.x < min.x }, @@ -171,6 +161,8 @@ class Frustum(private val camera: Camera) { return containsRegion(Vec3(aabb.min), Vec3(aabb.max)) } + private data class FrustumData(val normals: Array, val planes: Array) + private enum class Planes { LEFT, RIGHT, @@ -181,6 +173,7 @@ class Frustum(private val camera: Camera) { ; companion object : ValuesEnum { + const val SIZE = 6 override val VALUES: Array = values() override val NAME_MAP: Map = KUtil.getEnumValues(VALUES) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/FrustumChangeEvent.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/FrustumChangeEvent.kt index 349ce7777..25f9d5480 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/FrustumChangeEvent.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/FrustumChangeEvent.kt @@ -15,7 +15,7 @@ package de.bixilon.minosoft.gui.rendering.modding.events import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.Rendering -import de.bixilon.minosoft.gui.rendering.input.camera.Frustum +import de.bixilon.minosoft.gui.rendering.input.camera.frustum.Frustum class FrustumChangeEvent( renderWindow: RenderWindow = Rendering.currentContext!!, diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt index 790d62234..fc5151bc2 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.gui.rendering.util.vec.vec3 +import glm_.vec3.Vec3 import glm_.vec3.Vec3d object Vec3dUtil { @@ -29,4 +30,9 @@ object Vec3dUtil { val Vec3d.Companion.MAX: Vec3d get() = Vec3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE) + + fun Vec3d.toVec3(): Vec3 { + val array = array + return Vec3(floatArrayOf(array[0].toFloat(), array[1].toFloat(), array[2].toFloat())) + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt index a83230ae8..e1d58fbb4 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec4/Vec4Util.kt @@ -25,4 +25,6 @@ object Vec4Util { val Vec4.Companion.MAX: Vec4 get() = Vec4(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE) + + fun FloatArray.dot(x: Float, y: Float, z: Float) = this[0] * x + this[1] * y + this[2] * z + this[3] } diff --git a/src/main/java/de/bixilon/minosoft/util/KUtil.kt b/src/main/java/de/bixilon/minosoft/util/KUtil.kt index 9598fdefa..3779954e9 100644 --- a/src/main/java/de/bixilon/minosoft/util/KUtil.kt +++ b/src/main/java/de/bixilon/minosoft/util/KUtil.kt @@ -259,6 +259,10 @@ object KUtil { return this[enum.ordinal] } + operator fun Array.get(enum: Enum<*>): T { + return this[enum.ordinal] + } + fun Map.extend(vararg pairs: Pair): Map { val map: MutableMap = mutableMapOf()