rendering: generic 3d line drawing (Voxel Shapes, AABBs, etc)

This commit is contained in:
Bixilon 2021-06-09 20:37:56 +02:00
parent 48b761a772
commit da739cad79
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
7 changed files with 153 additions and 91 deletions

View File

@ -86,6 +86,7 @@ class RenderWindow(
val shaders: MutableList<Shader> = mutableListOf()
val shaderManager = ShaderManager(this)
lateinit var WHITE_TEXTURE: TextureLike
@ -201,6 +202,8 @@ class RenderWindow(
font.load(connection.assetsManager, textures.allTextures)
shaderManager.init()
Log.log(LogMessageType.RENDERING_LOADING) { "Initializing renderer (${stopwatch.labTime()})..." }
for (renderer in rendererMap.values) {

View File

@ -0,0 +1,31 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
class ShaderManager(
val renderWindow: RenderWindow,
) {
val genericColorShader = Shader(
resourceLocation = ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "generic/color"),
)
fun init() {
genericColorShader.load(renderWindow.connection.assetsManager)
}
}

View File

@ -21,18 +21,10 @@ 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
import de.bixilon.minosoft.gui.rendering.chunk.VoxelShape
import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.ElementRenderer
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.VecUtil.getWorldOffset
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3d
import de.bixilon.minosoft.gui.rendering.util.mesh.LineMesh
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.BitByte.isBit
import de.bixilon.minosoft.util.MMath.positiveNegative
import glm_.vec3.Vec3
import glm_.vec3.Vec3d
import glm_.vec3.Vec3i
import org.lwjgl.opengl.GL11.*
@ -43,82 +35,17 @@ class BlockOutlineRenderer(
private var currentOutlinePosition: Vec3i? = null
private var currentOutlineBlockState: BlockState? = null
private var outlineMesh: BlockOutlineMesh? = null
private var collisionMesh: BlockOutlineMesh? = null
private val outlineShader = Shader(
resourceLocation = ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "chunk/block/outline"),
)
private var outlineMesh: LineMesh? = null
private var collisionMesh: LineMesh? = null
override fun init() {
outlineShader.load(connection.assetsManager)
}
private fun drawLine(start: Vec3, end: Vec3, mesh: BlockOutlineMesh) {
val direction = (end - start).normalize()
val normal1 = Vec3(direction.z, direction.z, direction.x - direction.y)
if (normal1 == Vec3.EMPTY) {
normal1.x = normal1.z
normal1.z = direction.z
}
normal1.normalizeAssign()
val normal2 = (direction cross normal1).normalize()
for (i in 0..4) {
drawLineQuad(mesh, start, end, direction, normal1, normal2, i.isBit(0), i.isBit(1))
}
}
private fun drawLineQuad(mesh: BlockOutlineMesh, start: Vec3, end: Vec3, direction: Vec3, normal1: Vec3, normal2: Vec3, invertNormal1: Boolean, invertNormal2: Boolean) {
val normal1Multiplier = invertNormal1.positiveNegative
val normal2Multiplier = invertNormal2.positiveNegative
val positions = listOf(
start + normal2 * normal2Multiplier * HALF_LINE_WIDTH - direction * HALF_LINE_WIDTH,
start + normal1 * normal1Multiplier * HALF_LINE_WIDTH - direction * HALF_LINE_WIDTH,
end + normal1 * normal1Multiplier * HALF_LINE_WIDTH + direction * HALF_LINE_WIDTH,
end + normal2 * normal2Multiplier * HALF_LINE_WIDTH + direction * HALF_LINE_WIDTH,
)
for ((_, positionIndex) in ElementRenderer.DRAW_ODER) {
mesh.addVertex(positions[positionIndex])
}
}
private fun drawVoxelShape(shape: VoxelShape, blockPosition: Vec3d, mesh: BlockOutlineMesh, margin: Float = 0.0f) {
for (aabb in shape) {
val min = blockPosition + aabb.min - margin
val max = blockPosition + aabb.max + margin
fun drawSideQuad(x: Double) {
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
drawSideQuad(min.x)
// right quad
drawSideQuad(max.x)
// connections between 2 quads
drawLine(Vec3(min.x, min.y, min.z), Vec3(max.x, min.y, min.z), mesh)
drawLine(Vec3(min.x, max.y, min.z), Vec3(max.x, max.y, min.z), mesh)
drawLine(Vec3(min.x, max.y, max.z), Vec3(max.x, max.y, max.z), mesh)
drawLine(Vec3(min.x, min.y, max.z), Vec3(max.x, min.y, max.z), mesh)
}
}
private fun draw(outlineMesh: BlockOutlineMesh, collisionMesh: BlockOutlineMesh?) {
private fun draw(outlineMesh: LineMesh, collisionMesh: LineMesh?) {
glDisable(GL_CULL_FACE)
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
glDepthFunc(GL_ALWAYS)
}
outlineShader.use()
outlineShader.setRGBColor("uTintColor", Minosoft.config.config.game.other.blockOutline.outlineColor)
renderWindow.shaderManager.genericColorShader.use()
outlineMesh.draw()
collisionMesh?.let {
outlineShader.setRGBColor("uTintColor", Minosoft.config.config.game.other.blockOutline.collisionColor)
it.draw()
}
collisionMesh?.draw()
glEnable(GL_CULL_FACE)
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
glDepthFunc(GL_LESS)
@ -165,18 +92,18 @@ class BlockOutlineRenderer(
outlineMesh?.unload()
collisionMesh?.unload()
outlineMesh = BlockOutlineMesh()
outlineMesh = LineMesh(Minosoft.config.config.game.other.blockOutline.outlineColor, LINE_WIDTH)
val blockOffset = raycastHit.blockPosition.toVec3d + raycastHit.blockPosition.getWorldOffset(raycastHit.blockState.block)
drawVoxelShape(raycastHit.blockState.outlineShape, blockOffset, outlineMesh)
outlineMesh.drawVoxelShape(raycastHit.blockState.outlineShape, blockOffset, outlineMesh)
outlineMesh.load()
if (Minosoft.config.config.game.other.blockOutline.collisionBoxes) {
collisionMesh = BlockOutlineMesh()
collisionMesh = LineMesh(Minosoft.config.config.game.other.blockOutline.collisionColor, LINE_WIDTH)
drawVoxelShape(raycastHit.blockState.collisionShape, blockOffset, collisionMesh, 0.005f)
collisionMesh.drawVoxelShape(raycastHit.blockState.collisionShape, blockOffset, collisionMesh, 0.005f)
collisionMesh.load()
this.collisionMesh = collisionMesh
}
@ -192,7 +119,6 @@ class BlockOutlineRenderer(
companion object : RendererBuilder<BlockOutlineRenderer> {
override val RESOURCE_LOCATION = ResourceLocation("minosoft:block_outline")
private const val LINE_WIDTH = 1.0f / 128.0f
private const val HALF_LINE_WIDTH = LINE_WIDTH / 2.0f
override fun build(connection: PlayConnection, renderWindow: RenderWindow): BlockOutlineRenderer {
return BlockOutlineRenderer(connection, renderWindow)

View File

@ -11,19 +11,27 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.chunk.block.outline
package de.bixilon.minosoft.gui.rendering.util.mesh
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
import de.bixilon.minosoft.gui.rendering.util.mesh.PositionOnlyMeshStruct
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.RGBColor
import glm_.vec3.Vec3
class BlockOutlineMesh : Mesh(PositionOnlyMeshStruct::class) {
open class GenericColorMesh : Mesh(GenericColorMeshStruct::class) {
fun addVertex(position: Vec3) {
fun addVertex(position: Vec3, color: RGBColor?) {
data.addAll(floatArrayOf(
position.x,
position.y,
position.z,
Float.fromBits((color ?: ChatColors.WHITE).rgba)
))
}
data class GenericColorMeshStruct(
val position: Vec3,
val color: RGBColor,
) {
companion object : MeshStruct(GenericColorMeshStruct::class)
}
}

View File

@ -0,0 +1,85 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.util.mesh
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.chunk.VoxelShape
import de.bixilon.minosoft.gui.rendering.chunk.models.renderable.ElementRenderer
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import de.bixilon.minosoft.util.BitByte.isBit
import de.bixilon.minosoft.util.MMath.positiveNegative
import glm_.vec3.Vec3
import glm_.vec3.Vec3d
class LineMesh(
var color: RGBColor,
lineWidth: Float = 0.1f,
) : GenericColorMesh() {
private var halfLineWidth = lineWidth / 2.0f
fun drawLine(start: Vec3, end: Vec3, mesh: GenericColorMesh) {
val direction = (end - start).normalize()
val normal1 = Vec3(direction.z, direction.z, direction.x - direction.y)
if (normal1 == Vec3.EMPTY) {
normal1.x = normal1.z
normal1.z = direction.z
}
normal1.normalizeAssign()
val normal2 = (direction cross normal1).normalize()
for (i in 0..4) {
drawLineQuad(mesh, start, end, direction, normal1, normal2, i.isBit(0), i.isBit(1))
}
}
fun drawLineQuad(mesh: GenericColorMesh, start: Vec3, end: Vec3, direction: Vec3, normal1: Vec3, normal2: Vec3, invertNormal1: Boolean, invertNormal2: Boolean) {
val normal1Multiplier = invertNormal1.positiveNegative
val normal2Multiplier = invertNormal2.positiveNegative
val positions = listOf(
start + normal2 * normal2Multiplier * halfLineWidth - direction * halfLineWidth,
start + normal1 * normal1Multiplier * halfLineWidth - direction * halfLineWidth,
end + normal1 * normal1Multiplier * halfLineWidth + direction * halfLineWidth,
end + normal2 * normal2Multiplier * halfLineWidth + direction * halfLineWidth,
)
for ((_, positionIndex) in ElementRenderer.DRAW_ODER) {
mesh.addVertex(positions[positionIndex], color)
}
}
fun drawVoxelShape(shape: VoxelShape, blockPosition: Vec3d, mesh: GenericColorMesh, margin: Float = 0.0f) {
for (aabb in shape) {
val min = blockPosition + aabb.min - margin
val max = blockPosition + aabb.max + margin
fun drawSideQuad(x: Double) {
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
drawSideQuad(min.x)
// right quad
drawSideQuad(max.x)
// connections between 2 quads
drawLine(Vec3(min.x, min.y, min.z), Vec3(max.x, min.y, min.z), mesh)
drawLine(Vec3(min.x, max.y, min.z), Vec3(max.x, max.y, min.z), mesh)
drawLine(Vec3(min.x, max.y, max.z), Vec3(max.x, max.y, max.z), mesh)
drawLine(Vec3(min.x, min.y, max.z), Vec3(max.x, min.y, max.z), mesh)
}
}
}

View File

@ -13,10 +13,12 @@
#version 330 core
in vec4 finTintColor;
out vec4 foutColor;
uniform vec4 uTintColor;
void main() {
foutColor = uTintColor;
foutColor = finTintColor;
}

View File

@ -14,9 +14,16 @@
#version 330 core
layout (location = 0) in vec3 vinPosition;
layout (location = 1) in uint vinTintColor;
uniform mat4 uViewProjectionMatrix;
out vec4 finTintColor;
#include "minosoft:color"
void main() {
gl_Position = uViewProjectionMatrix * vec4(vinPosition, 1.0f);
finTintColor = getRGBAColor(vinTintColor);
}