improve raycasting block outline

This commit is contained in:
Bixilon 2021-05-16 13:59:06 +02:00
parent f1e3051f4e
commit 4311eaabaa
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 67 additions and 41 deletions

View File

@ -115,7 +115,7 @@ class World : BiomeAccessor {
return blocks.toMap() return blocks.toMap()
} }
data class RayCastHit( data class RaycastHit(
val position: Vec3, val position: Vec3,
val distance: Float, val distance: Float,
val blockState: BlockState, val blockState: BlockState,
@ -124,7 +124,7 @@ class World : BiomeAccessor {
val blockPosition = position.floor val blockPosition = position.floor
} }
fun raycast(origin: Vec3, direction: Vec3): RayCastHit? { fun raycast(origin: Vec3, direction: Vec3): RaycastHit? {
val currentPosition = Vec3(origin) val currentPosition = Vec3(origin)
fun getTotalDistance(): Float { fun getTotalDistance(): Float {
@ -140,7 +140,7 @@ class World : BiomeAccessor {
} ?: -1.0f } ?: -1.0f
if (distance >= 0.0f && blockState != null) { if (distance >= 0.0f && blockState != null) {
return RayCastHit( return RaycastHit(
currentPosition + direction * distance, currentPosition + direction * distance,
getTotalDistance() + distance, getTotalDistance() + distance,
blockState = blockState, blockState = blockState,

View File

@ -303,7 +303,7 @@ class Camera(
cameraPosition = getAbsoluteCameraPosition() cameraPosition = getAbsoluteCameraPosition()
} }
fun getTargetBlock(): World.RayCastHit? { fun getTargetBlock(): World.RaycastHit? {
return connection.world.raycast(cameraPosition, cameraFront) return connection.world.raycast(cameraPosition, cameraFront)
} }

View File

@ -76,4 +76,6 @@ object RenderConstants {
const val DOUBLE_PRESS_KEY_PRESS_MAX_DELAY = 200 const val DOUBLE_PRESS_KEY_PRESS_MAX_DELAY = 200
const val DOUBLE_PRESS_DELAY_BETWEEN_PRESSED = 500 const val DOUBLE_PRESS_DELAY_BETWEEN_PRESSED = 500
const val MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE = 5.0f
} }

View File

@ -111,7 +111,7 @@ class WorldRenderer(
} }
} }
if (meshCollection.transparentSectionArrayMesh!!.trianglesCount == 0) { if (meshCollection.transparentSectionArrayMesh!!.primitiveCount == 0) {
meshCollection.transparentSectionArrayMesh = null meshCollection.transparentSectionArrayMesh = null
} }
@ -321,24 +321,24 @@ class WorldRenderer(
sectionMap[index]?.let { sectionMap[index]?.let {
it.opaqueSectionArrayMesh.unload() it.opaqueSectionArrayMesh.unload()
meshes-- meshes--
triangles -= it.opaqueSectionArrayMesh.trianglesCount triangles -= it.opaqueSectionArrayMesh.primitiveCount
it.transparentSectionArrayMesh?.let { it.transparentSectionArrayMesh?.let {
it.unload() it.unload()
meshes-- meshes--
triangles -= it.trianglesCount triangles -= it.primitiveCount
} }
} }
meshCollection.opaqueSectionArrayMesh.let { meshCollection.opaqueSectionArrayMesh.let {
it.load() it.load()
meshes++ meshes++
triangles += it.trianglesCount triangles += it.primitiveCount
} }
meshCollection.transparentSectionArrayMesh?.let { meshCollection.transparentSectionArrayMesh?.let {
it.load() it.load()
meshes++ meshes++
triangles += it.trianglesCount triangles += it.primitiveCount
} }
@ -390,12 +390,12 @@ class WorldRenderer(
meshCollection.opaqueSectionArrayMesh.let { meshCollection.opaqueSectionArrayMesh.let {
it.unload() it.unload()
this.meshes-- this.meshes--
triangles -= it.trianglesCount triangles -= it.primitiveCount
} }
meshCollection.transparentSectionArrayMesh?.let { meshCollection.transparentSectionArrayMesh?.let {
it.unload() it.unload()
this.meshes-- this.meshes--
triangles -= it.trianglesCount triangles -= it.primitiveCount
} }
} }
} }

View File

@ -20,6 +20,7 @@ import org.lwjgl.opengl.GL20.glEnableVertexAttribArray
import org.lwjgl.opengl.GL20.glVertexAttribPointer import org.lwjgl.opengl.GL20.glVertexAttribPointer
class BlockOutlineMesh : Mesh() { class BlockOutlineMesh : Mesh() {
fun addVertex(position: Vec3) { fun addVertex(position: Vec3) {
data.addAll(floatArrayOf( data.addAll(floatArrayOf(
position.x, position.x,
@ -37,6 +38,7 @@ class BlockOutlineMesh : Mesh() {
super.unbind() super.unbind()
} }
companion object { companion object {
private const val FLOATS_PER_VERTEX = 3 private const val FLOATS_PER_VERTEX = 3
} }

View File

@ -13,9 +13,11 @@
package de.bixilon.minosoft.gui.rendering.chunk.block.outline 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.ResourceLocation
import de.bixilon.minosoft.data.mappings.blocks.BlockState import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.data.text.ChatColors 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.RenderWindow
import de.bixilon.minosoft.gui.rendering.Renderer import de.bixilon.minosoft.gui.rendering.Renderer
import de.bixilon.minosoft.gui.rendering.RendererBuilder 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 de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3 import glm_.vec3.Vec3
import glm_.vec3.Vec3i import glm_.vec3.Vec3i
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11.*
class BlockOutlineRenderer( class BlockOutlineRenderer(
val connection: PlayConnection, val connection: PlayConnection,
@ -61,17 +63,19 @@ class BlockOutlineRenderer(
for (aabb in shape) { for (aabb in shape) {
val min = blockPosition + aabb.min val min = blockPosition + aabb.min
val max = blockPosition + aabb.max 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 // left quad
drawLine(Vec3(min.x, min.y, min.z), Vec3(min.x, max.y, min.z), mesh) drawSideQuad(min.x)
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)
// right quad // right quad
drawLine(Vec3(max.x, min.y, min.z), Vec3(max.x, max.y, min.z), mesh) drawSideQuad(max.x)
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)
// connections between 2 quads // connections between 2 quads
drawLine(Vec3(min.x, min.y, min.z), Vec3(max.x, min.y, min.z), mesh) 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) { private fun draw(mesh: BlockOutlineMesh) {
GL11.glDisable(GL11.GL_CULL_FACE) glDisable(GL_CULL_FACE)
outlineShader.use() outlineShader.use()
mesh.draw() 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() { override fun draw() {
val rayCastHit = renderWindow.inputHandler.camera.getTargetBlock() val raycastHit = renderWindow.inputHandler.camera.getTargetBlock()
var outlineMesh = outlineMesh var outlineMesh = outlineMesh
if (rayCastHit == null) {
outlineMesh ?: return if (raycastHit == null) {
outlineMesh.unload() unload()
this.outlineMesh = null
this.currentOutlinePosition = null
this.currentOutlineBlockState = null
return 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!!) draw(outlineMesh!!)
return return
} }
@ -111,11 +132,11 @@ class BlockOutlineRenderer(
outlineMesh?.unload() outlineMesh?.unload()
outlineMesh = BlockOutlineMesh() 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() outlineMesh.load()
this.currentOutlinePosition = rayCastHit.blockPosition this.currentOutlinePosition = raycastHit.blockPosition
this.currentOutlineBlockState = rayCastHit.blockState this.currentOutlineBlockState = raycastHit.blockState
this.outlineMesh = outlineMesh this.outlineMesh = outlineMesh
draw(outlineMesh) draw(outlineMesh)
} }
@ -123,7 +144,7 @@ class BlockOutlineRenderer(
companion object : RendererBuilder<BlockOutlineRenderer> { companion object : RendererBuilder<BlockOutlineRenderer> {
override val RESOURCE_LOCATION = ResourceLocation("minosoft:block_outline") 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 private const val HALF_LINE_WIDTH = LINE_WIDTH / 2.0f
override fun build(connection: PlayConnection, renderWindow: RenderWindow): BlockOutlineRenderer { override fun build(connection: PlayConnection, renderWindow: RenderWindow): BlockOutlineRenderer {

View File

@ -87,10 +87,10 @@ class HUDSystemDebugNode(hudRenderer: HUDRenderer) : DebugScreenNode(hudRenderer
allocatedMemoryText.sText = "Allocated: ${getAllocatedMemoryPercent()}% ${getFormattedAllocatedMemory()}" allocatedMemoryText.sText = "Allocated: ${getAllocatedMemoryPercent()}% ${getFormattedAllocatedMemory()}"
val rayCastHit = hudRenderer.renderWindow.inputHandler.camera.getTargetBlock() val rayCastHit = hudRenderer.renderWindow.inputHandler.camera.getTargetBlock()
if (rayCastHit == null) { if (rayCastHit == null) {
targetPosition.sText = "No blocks in reach!" targetPosition.sText = ""
targetBlockState.sText = "" targetBlockState.sText = ""
} else { } else {
targetPosition.sText = "looking at ${rayCastHit.blockPosition}" targetPosition.sText = "Target block: ${rayCastHit.blockPosition}"
targetBlockState.sText = rayCastHit.blockState.toString() targetBlockState.sText = rayCastHit.blockState.toString()
} }

View File

@ -28,9 +28,10 @@ abstract class Mesh(
_data = value _data = value
} }
private var vao: Int = -1 protected var vao: Int = -1
private set
private var vbo: Int = -1 private var vbo: Int = -1
var trianglesCount: Int = -1 var primitiveCount: Int = -1
private set private set
var state = MeshStates.PREPARING var state = MeshStates.PREPARING
@ -42,7 +43,7 @@ abstract class Mesh(
protected fun initializeBuffers(floatsPerVertex: Int) { protected fun initializeBuffers(floatsPerVertex: Int) {
check(state == MeshStates.PREPARING) { "Mesh already loaded: $state" } check(state == MeshStates.PREPARING) { "Mesh already loaded: $state" }
trianglesCount = data.size / floatsPerVertex primitiveCount = data.size / floatsPerVertex
vao = glGenVertexArrays() vao = glGenVertexArrays()
vbo = glGenBuffers() vbo = glGenBuffers()
glBindVertexArray(vao) glBindVertexArray(vao)
@ -58,10 +59,10 @@ abstract class Mesh(
glBindBuffer(GL_ARRAY_BUFFER, 0) glBindBuffer(GL_ARRAY_BUFFER, 0)
} }
fun draw() { open fun draw() {
// check(state == MeshStates.LOADED) { "Mesh not loaded: $state" } // check(state == MeshStates.LOADED) { "Mesh not loaded: $state" }
glBindVertexArray(vao) glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, trianglesCount) glDrawArrays(GL_TRIANGLES, 0, primitiveCount)
} }
fun unload(checkLoaded: Boolean = true) { fun unload(checkLoaded: Boolean = true) {