mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-19 12:25:12 -04:00
rendering: generic 3d line drawing (Voxel Shapes, AABBs, etc)
This commit is contained in:
parent
48b761a772
commit
da739cad79
@ -86,6 +86,7 @@ class RenderWindow(
|
|||||||
|
|
||||||
|
|
||||||
val shaders: MutableList<Shader> = mutableListOf()
|
val shaders: MutableList<Shader> = mutableListOf()
|
||||||
|
val shaderManager = ShaderManager(this)
|
||||||
|
|
||||||
lateinit var WHITE_TEXTURE: TextureLike
|
lateinit var WHITE_TEXTURE: TextureLike
|
||||||
|
|
||||||
@ -201,6 +202,8 @@ class RenderWindow(
|
|||||||
|
|
||||||
font.load(connection.assetsManager, textures.allTextures)
|
font.load(connection.assetsManager, textures.allTextures)
|
||||||
|
|
||||||
|
shaderManager.init()
|
||||||
|
|
||||||
|
|
||||||
Log.log(LogMessageType.RENDERING_LOADING) { "Initializing renderer (${stopwatch.labTime()})..." }
|
Log.log(LogMessageType.RENDERING_LOADING) { "Initializing renderer (${stopwatch.labTime()})..." }
|
||||||
for (renderer in rendererMap.values) {
|
for (renderer in rendererMap.values) {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -21,18 +21,10 @@ 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
|
||||||
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.getWorldOffset
|
||||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3d
|
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.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 glm_.vec3.Vec3i
|
||||||
import org.lwjgl.opengl.GL11.*
|
import org.lwjgl.opengl.GL11.*
|
||||||
|
|
||||||
@ -43,82 +35,17 @@ class BlockOutlineRenderer(
|
|||||||
private var currentOutlinePosition: Vec3i? = null
|
private var currentOutlinePosition: Vec3i? = null
|
||||||
private var currentOutlineBlockState: BlockState? = null
|
private var currentOutlineBlockState: BlockState? = null
|
||||||
|
|
||||||
private var outlineMesh: BlockOutlineMesh? = null
|
private var outlineMesh: LineMesh? = null
|
||||||
private var collisionMesh: BlockOutlineMesh? = null
|
private var collisionMesh: LineMesh? = null
|
||||||
private val outlineShader = Shader(
|
|
||||||
resourceLocation = ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "chunk/block/outline"),
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun init() {
|
private fun draw(outlineMesh: LineMesh, collisionMesh: LineMesh?) {
|
||||||
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?) {
|
|
||||||
glDisable(GL_CULL_FACE)
|
glDisable(GL_CULL_FACE)
|
||||||
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
|
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
|
||||||
glDepthFunc(GL_ALWAYS)
|
glDepthFunc(GL_ALWAYS)
|
||||||
}
|
}
|
||||||
outlineShader.use()
|
renderWindow.shaderManager.genericColorShader.use()
|
||||||
outlineShader.setRGBColor("uTintColor", Minosoft.config.config.game.other.blockOutline.outlineColor)
|
|
||||||
outlineMesh.draw()
|
outlineMesh.draw()
|
||||||
collisionMesh?.let {
|
collisionMesh?.draw()
|
||||||
outlineShader.setRGBColor("uTintColor", Minosoft.config.config.game.other.blockOutline.collisionColor)
|
|
||||||
it.draw()
|
|
||||||
}
|
|
||||||
glEnable(GL_CULL_FACE)
|
glEnable(GL_CULL_FACE)
|
||||||
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
|
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
|
||||||
glDepthFunc(GL_LESS)
|
glDepthFunc(GL_LESS)
|
||||||
@ -165,18 +92,18 @@ class BlockOutlineRenderer(
|
|||||||
|
|
||||||
outlineMesh?.unload()
|
outlineMesh?.unload()
|
||||||
collisionMesh?.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)
|
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()
|
outlineMesh.load()
|
||||||
|
|
||||||
|
|
||||||
if (Minosoft.config.config.game.other.blockOutline.collisionBoxes) {
|
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()
|
collisionMesh.load()
|
||||||
this.collisionMesh = collisionMesh
|
this.collisionMesh = collisionMesh
|
||||||
}
|
}
|
||||||
@ -192,7 +119,6 @@ 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 / 128.0f
|
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 {
|
override fun build(connection: PlayConnection, renderWindow: RenderWindow): BlockOutlineRenderer {
|
||||||
return BlockOutlineRenderer(connection, renderWindow)
|
return BlockOutlineRenderer(connection, renderWindow)
|
||||||
|
@ -11,19 +11,27 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* 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.data.text.ChatColors
|
||||||
import de.bixilon.minosoft.gui.rendering.util.mesh.PositionOnlyMeshStruct
|
import de.bixilon.minosoft.data.text.RGBColor
|
||||||
import glm_.vec3.Vec3
|
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(
|
data.addAll(floatArrayOf(
|
||||||
position.x,
|
position.x,
|
||||||
position.y,
|
position.y,
|
||||||
position.z,
|
position.z,
|
||||||
|
Float.fromBits((color ?: ChatColors.WHITE).rgba)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class GenericColorMeshStruct(
|
||||||
|
val position: Vec3,
|
||||||
|
val color: RGBColor,
|
||||||
|
) {
|
||||||
|
companion object : MeshStruct(GenericColorMeshStruct::class)
|
||||||
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,10 +13,12 @@
|
|||||||
|
|
||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
|
in vec4 finTintColor;
|
||||||
|
|
||||||
|
|
||||||
out vec4 foutColor;
|
out vec4 foutColor;
|
||||||
|
|
||||||
uniform vec4 uTintColor;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
foutColor = uTintColor;
|
foutColor = finTintColor;
|
||||||
}
|
}
|
@ -14,9 +14,16 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
layout (location = 0) in vec3 vinPosition;
|
layout (location = 0) in vec3 vinPosition;
|
||||||
|
layout (location = 1) in uint vinTintColor;
|
||||||
|
|
||||||
uniform mat4 uViewProjectionMatrix;
|
uniform mat4 uViewProjectionMatrix;
|
||||||
|
|
||||||
|
out vec4 finTintColor;
|
||||||
|
|
||||||
|
#include "minosoft:color"
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = uViewProjectionMatrix * vec4(vinPosition, 1.0f);
|
gl_Position = uViewProjectionMatrix * vec4(vinPosition, 1.0f);
|
||||||
|
|
||||||
|
finTintColor = getRGBAColor(vinTintColor);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user