From 4311eaabaac68b2246f9b530021cc6c02ddeefc0 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sun, 16 May 2021 13:59:06 +0200 Subject: [PATCH] improve raycasting block outline --- .../de/bixilon/minosoft/data/world/World.kt | 6 +- .../minosoft/gui/input/camera/Camera.kt | 2 +- .../minosoft/gui/rendering/RenderConstants.kt | 2 + .../gui/rendering/chunk/WorldRenderer.kt | 14 ++-- .../chunk/block/outline/BlockOutlineMesh.kt | 2 + .../block/outline/BlockOutlineRenderer.kt | 67 ++++++++++++------- .../hud/nodes/debug/HUDSystemDebugNode.kt | 4 +- .../minosoft/gui/rendering/util/Mesh.kt | 11 +-- 8 files changed, 67 insertions(+), 41 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/data/world/World.kt b/src/main/java/de/bixilon/minosoft/data/world/World.kt index 7309f79c9..85ed372f3 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/World.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/World.kt @@ -115,7 +115,7 @@ class World : BiomeAccessor { return blocks.toMap() } - data class RayCastHit( + data class RaycastHit( val position: Vec3, val distance: Float, val blockState: BlockState, @@ -124,7 +124,7 @@ class World : BiomeAccessor { val blockPosition = position.floor } - fun raycast(origin: Vec3, direction: Vec3): RayCastHit? { + fun raycast(origin: Vec3, direction: Vec3): RaycastHit? { val currentPosition = Vec3(origin) fun getTotalDistance(): Float { @@ -140,7 +140,7 @@ class World : BiomeAccessor { } ?: -1.0f if (distance >= 0.0f && blockState != null) { - return RayCastHit( + return RaycastHit( currentPosition + direction * distance, getTotalDistance() + distance, blockState = blockState, diff --git a/src/main/java/de/bixilon/minosoft/gui/input/camera/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/input/camera/Camera.kt index c7ad59788..361d3cc3a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/input/camera/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/input/camera/Camera.kt @@ -303,7 +303,7 @@ class Camera( cameraPosition = getAbsoluteCameraPosition() } - fun getTargetBlock(): World.RayCastHit? { + fun getTargetBlock(): World.RaycastHit? { return connection.world.raycast(cameraPosition, cameraFront) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt index 9c7103e0f..a4453f946 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt @@ -76,4 +76,6 @@ object RenderConstants { const val DOUBLE_PRESS_KEY_PRESS_MAX_DELAY = 200 const val DOUBLE_PRESS_DELAY_BETWEEN_PRESSED = 500 + + const val MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE = 5.0f } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt index 443e67626..e9e87b811 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt @@ -111,7 +111,7 @@ class WorldRenderer( } } - if (meshCollection.transparentSectionArrayMesh!!.trianglesCount == 0) { + if (meshCollection.transparentSectionArrayMesh!!.primitiveCount == 0) { meshCollection.transparentSectionArrayMesh = null } @@ -321,24 +321,24 @@ class WorldRenderer( sectionMap[index]?.let { it.opaqueSectionArrayMesh.unload() meshes-- - triangles -= it.opaqueSectionArrayMesh.trianglesCount + triangles -= it.opaqueSectionArrayMesh.primitiveCount it.transparentSectionArrayMesh?.let { it.unload() meshes-- - triangles -= it.trianglesCount + triangles -= it.primitiveCount } } meshCollection.opaqueSectionArrayMesh.let { it.load() meshes++ - triangles += it.trianglesCount + triangles += it.primitiveCount } meshCollection.transparentSectionArrayMesh?.let { it.load() meshes++ - triangles += it.trianglesCount + triangles += it.primitiveCount } @@ -390,12 +390,12 @@ class WorldRenderer( meshCollection.opaqueSectionArrayMesh.let { it.unload() this.meshes-- - triangles -= it.trianglesCount + triangles -= it.primitiveCount } meshCollection.transparentSectionArrayMesh?.let { it.unload() this.meshes-- - triangles -= it.trianglesCount + triangles -= it.primitiveCount } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineMesh.kt index 57524c204..27a8b5f98 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineMesh.kt @@ -20,6 +20,7 @@ import org.lwjgl.opengl.GL20.glEnableVertexAttribArray import org.lwjgl.opengl.GL20.glVertexAttribPointer class BlockOutlineMesh : Mesh() { + fun addVertex(position: Vec3) { data.addAll(floatArrayOf( position.x, @@ -37,6 +38,7 @@ class BlockOutlineMesh : Mesh() { super.unbind() } + companion object { private const val FLOATS_PER_VERTEX = 3 } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineRenderer.kt index 547da79b5..f9f6f1a80 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/block/outline/BlockOutlineRenderer.kt @@ -13,9 +13,11 @@ package de.bixilon.minosoft.gui.rendering.chunk.block.outline +import de.bixilon.minosoft.data.Gamemodes import de.bixilon.minosoft.data.mappings.ResourceLocation import de.bixilon.minosoft.data.mappings.blocks.BlockState 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.Renderer import de.bixilon.minosoft.gui.rendering.RendererBuilder @@ -26,7 +28,7 @@ import de.bixilon.minosoft.protocol.network.connection.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import glm_.vec3.Vec3 import glm_.vec3.Vec3i -import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL11.* class BlockOutlineRenderer( val connection: PlayConnection, @@ -61,17 +63,19 @@ class BlockOutlineRenderer( for (aabb in shape) { val min = blockPosition + aabb.min val max = blockPosition + aabb.max + + fun drawSideQuad(x: Float) { + drawLine(Vec3(x, min.y, min.z), Vec3(x, max.y, min.z), mesh) + drawLine(Vec3(x, min.y, min.z), Vec3(x, min.y, max.z), mesh) + drawLine(Vec3(x, max.y, min.z), Vec3(x, max.y, max.z), mesh) + drawLine(Vec3(x, min.y, max.z), Vec3(x, max.y, max.z), mesh) + } + // left quad - drawLine(Vec3(min.x, min.y, min.z), Vec3(min.x, max.y, min.z), mesh) - drawLine(Vec3(min.x, min.y, min.z), Vec3(min.x, min.y, max.z), mesh) - drawLine(Vec3(min.x, max.y, min.z), Vec3(min.x, max.y, max.z), mesh) - drawLine(Vec3(min.x, min.y, max.z), Vec3(min.x, max.y, max.z), mesh) + drawSideQuad(min.x) // right quad - drawLine(Vec3(max.x, min.y, min.z), Vec3(max.x, max.y, min.z), mesh) - drawLine(Vec3(max.x, min.y, min.z), Vec3(max.x, min.y, max.z), mesh) - drawLine(Vec3(max.x, max.y, min.z), Vec3(max.x, max.y, max.z), mesh) - drawLine(Vec3(max.x, min.y, max.z), Vec3(max.x, max.y, max.z), mesh) + drawSideQuad(max.x) // connections between 2 quads drawLine(Vec3(min.x, min.y, min.z), Vec3(max.x, min.y, min.z), mesh) @@ -82,28 +86,45 @@ class BlockOutlineRenderer( } private fun draw(mesh: BlockOutlineMesh) { - GL11.glDisable(GL11.GL_CULL_FACE) + glDisable(GL_CULL_FACE) outlineShader.use() mesh.draw() - GL11.glEnable(GL11.GL_CULL_FACE) + glEnable(GL_CULL_FACE) + } + + private fun unload() { + outlineMesh ?: return + outlineMesh?.unload() + this.outlineMesh = null + this.currentOutlinePosition = null + this.currentOutlineBlockState = null } override fun draw() { - val rayCastHit = renderWindow.inputHandler.camera.getTargetBlock() + val raycastHit = renderWindow.inputHandler.camera.getTargetBlock() var outlineMesh = outlineMesh - if (rayCastHit == null) { - outlineMesh ?: return - outlineMesh.unload() - this.outlineMesh = null - this.currentOutlinePosition = null - this.currentOutlineBlockState = null + + if (raycastHit == null) { + unload() return } - if (rayCastHit.blockPosition == currentOutlinePosition && rayCastHit.blockState == currentOutlineBlockState) { + if (raycastHit.distance >= RenderConstants.MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE) { + unload() + return + } + + if (connection.player.entity.gamemode == Gamemodes.ADVENTURE || connection.player.entity.gamemode == Gamemodes.SPECTATOR) { + if (connection.mapping.blockEntityRegistry.get(raycastHit.blockState.block.resourceLocation) == null) { + unload() + return + } + } + + if (raycastHit.blockPosition == currentOutlinePosition && raycastHit.blockState == currentOutlineBlockState) { draw(outlineMesh!!) return } @@ -111,11 +132,11 @@ class BlockOutlineRenderer( outlineMesh?.unload() outlineMesh = BlockOutlineMesh() - drawVoxelShape(rayCastHit.blockState.outlineShape, rayCastHit.blockPosition.getWorldOffset(rayCastHit.blockState.block).plus(rayCastHit.blockPosition), outlineMesh) + drawVoxelShape(raycastHit.blockState.outlineShape, raycastHit.blockPosition.getWorldOffset(raycastHit.blockState.block).plus(raycastHit.blockPosition), outlineMesh) outlineMesh.load() - this.currentOutlinePosition = rayCastHit.blockPosition - this.currentOutlineBlockState = rayCastHit.blockState + this.currentOutlinePosition = raycastHit.blockPosition + this.currentOutlineBlockState = raycastHit.blockState this.outlineMesh = outlineMesh draw(outlineMesh) } @@ -123,7 +144,7 @@ class BlockOutlineRenderer( companion object : RendererBuilder { override val RESOURCE_LOCATION = ResourceLocation("minosoft:block_outline") - private const val LINE_WIDTH = 1.0f / 16.0f + private const val LINE_WIDTH = 1.0f / 64.0f private const val HALF_LINE_WIDTH = LINE_WIDTH / 2.0f override fun build(connection: PlayConnection, renderWindow: RenderWindow): BlockOutlineRenderer { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/nodes/debug/HUDSystemDebugNode.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/nodes/debug/HUDSystemDebugNode.kt index 3aa114adb..a55b612d5 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/hud/nodes/debug/HUDSystemDebugNode.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/hud/nodes/debug/HUDSystemDebugNode.kt @@ -87,10 +87,10 @@ class HUDSystemDebugNode(hudRenderer: HUDRenderer) : DebugScreenNode(hudRenderer allocatedMemoryText.sText = "Allocated: ${getAllocatedMemoryPercent()}% ${getFormattedAllocatedMemory()}" val rayCastHit = hudRenderer.renderWindow.inputHandler.camera.getTargetBlock() if (rayCastHit == null) { - targetPosition.sText = "No blocks in reach!" + targetPosition.sText = "" targetBlockState.sText = "" } else { - targetPosition.sText = "looking at ${rayCastHit.blockPosition}" + targetPosition.sText = "Target block: ${rayCastHit.blockPosition}" targetBlockState.sText = rayCastHit.blockState.toString() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/Mesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/Mesh.kt index 739565969..5af9b7a0f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/Mesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/Mesh.kt @@ -28,9 +28,10 @@ abstract class Mesh( _data = value } - private var vao: Int = -1 + protected var vao: Int = -1 + private set private var vbo: Int = -1 - var trianglesCount: Int = -1 + var primitiveCount: Int = -1 private set var state = MeshStates.PREPARING @@ -42,7 +43,7 @@ abstract class Mesh( protected fun initializeBuffers(floatsPerVertex: Int) { check(state == MeshStates.PREPARING) { "Mesh already loaded: $state" } - trianglesCount = data.size / floatsPerVertex + primitiveCount = data.size / floatsPerVertex vao = glGenVertexArrays() vbo = glGenBuffers() glBindVertexArray(vao) @@ -58,10 +59,10 @@ abstract class Mesh( glBindBuffer(GL_ARRAY_BUFFER, 0) } - fun draw() { + open fun draw() { // check(state == MeshStates.LOADED) { "Mesh not loaded: $state" } glBindVertexArray(vao) - glDrawArrays(GL_TRIANGLES, 0, trianglesCount) + glDrawArrays(GL_TRIANGLES, 0, primitiveCount) } fun unload(checkLoaded: Boolean = true) {