mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 03:15:35 -04:00
rendering: improve 3d line rendering, render entity hit boxes
This commit is contained in:
parent
da739cad79
commit
5b680ae066
@ -15,6 +15,7 @@ package de.bixilon.minosoft.config.config.game
|
||||
|
||||
import de.bixilon.minosoft.config.config.game.controls.ControlsGameConfig
|
||||
import de.bixilon.minosoft.config.config.game.elements.ElementsGameConfig
|
||||
import de.bixilon.minosoft.config.config.game.entities.EntitiesConfig
|
||||
import de.bixilon.minosoft.config.config.game.graphics.GraphicsGameConfig
|
||||
import de.bixilon.minosoft.config.config.game.sound.SoundConfig
|
||||
|
||||
@ -26,4 +27,5 @@ data class GameConfig(
|
||||
var elements: ElementsGameConfig = ElementsGameConfig(),
|
||||
var camera: CameraGameConfig = CameraGameConfig(),
|
||||
var sound: SoundConfig = SoundConfig(),
|
||||
var entities: EntitiesConfig = EntitiesConfig(),
|
||||
)
|
||||
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.config.config.game.entities
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
data class EntitiesConfig(
|
||||
@Json(name = "hit_box") val hitBox: EntityHitBoxConfig = EntityHitBoxConfig(),
|
||||
)
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.config.config.game.entities
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import de.bixilon.minosoft.data.text.ChatColors
|
||||
import de.bixilon.minosoft.data.text.RGBColor
|
||||
|
||||
data class EntityHitBoxConfig(
|
||||
@Json(name = "enabled") val enabled: Boolean = true,
|
||||
@Json(name = "disable_z_buffer") val disableZBuffer: Boolean = false,
|
||||
@Json(name = "hit_box_color") val hitBoxColor: RGBColor = ChatColors.WHITE,
|
||||
@Json(name = "eye_height_color") val eyeHeightColor: RGBColor = ChatColors.RED,
|
||||
@Json(name = "render_invisible_entities") val renderInvisibleEntities: Boolean = false,
|
||||
@Json(name = "invisible_entities_color") val invisibleEntitiesColor: RGBColor = ChatColors.GREEN,
|
||||
)
|
@ -19,10 +19,14 @@ import de.bixilon.minosoft.data.mappings.entities.EntityFactory
|
||||
import de.bixilon.minosoft.data.mappings.entities.EntityType
|
||||
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
|
||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||
import glm_.vec2.Vec2
|
||||
import glm_.vec3.Vec3d
|
||||
|
||||
class AreaEffectCloud(connection: PlayConnection, entityType: EntityType, position: Vec3d, rotation: EntityRotation) : Entity(connection, entityType, position, rotation) {
|
||||
|
||||
override val dimensions: Vec2
|
||||
get() = Vec2(radius * 2, super.dimensions.y)
|
||||
|
||||
@get:EntityMetaDataFunction(name = "Ignore radius")
|
||||
val ignoreRadius: Boolean
|
||||
get() = entityMetaData.sets.getBoolean(EntityMetaDataFields.AREA_EFFECT_CLOUD_IGNORE_RADIUS)
|
||||
|
@ -19,6 +19,7 @@ import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
|
||||
import de.bixilon.minosoft.data.mappings.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.WorldRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.block.outline.BlockOutlineRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.entities.EntityHitBoxRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.font.Font
|
||||
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.hud.atlas.TextureLike
|
||||
@ -115,6 +116,9 @@ class RenderWindow(
|
||||
if (Minosoft.config.config.game.graphics.particles.enabled) {
|
||||
registerRenderer(ParticleRenderer)
|
||||
}
|
||||
if (Minosoft.config.config.game.entities.hitBox.enabled) {
|
||||
registerRenderer(EntityHitBoxRenderer)
|
||||
}
|
||||
registerRenderer(HUDRenderer)
|
||||
}
|
||||
|
||||
|
@ -35,17 +35,16 @@ class BlockOutlineRenderer(
|
||||
private var currentOutlinePosition: Vec3i? = null
|
||||
private var currentOutlineBlockState: BlockState? = null
|
||||
|
||||
private var outlineMesh: LineMesh? = null
|
||||
private var collisionMesh: LineMesh? = null
|
||||
private var currentMesh: LineMesh? = null
|
||||
|
||||
private fun draw(outlineMesh: LineMesh, collisionMesh: LineMesh?) {
|
||||
private fun drawMesh() {
|
||||
val currentMesh = currentMesh ?: return
|
||||
glDisable(GL_CULL_FACE)
|
||||
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
|
||||
glDepthFunc(GL_ALWAYS)
|
||||
}
|
||||
renderWindow.shaderManager.genericColorShader.use()
|
||||
outlineMesh.draw()
|
||||
collisionMesh?.draw()
|
||||
currentMesh.draw()
|
||||
glEnable(GL_CULL_FACE)
|
||||
if (Minosoft.config.config.game.other.blockOutline.disableZBuffer) {
|
||||
glDepthFunc(GL_LESS)
|
||||
@ -53,11 +52,9 @@ class BlockOutlineRenderer(
|
||||
}
|
||||
|
||||
private fun unload() {
|
||||
outlineMesh ?: return
|
||||
outlineMesh?.unload()
|
||||
collisionMesh?.unload()
|
||||
this.outlineMesh = null
|
||||
this.collisionMesh = null
|
||||
currentMesh ?: return
|
||||
currentMesh?.unload()
|
||||
this.currentMesh = null
|
||||
this.currentOutlinePosition = null
|
||||
this.currentOutlineBlockState = null
|
||||
}
|
||||
@ -65,8 +62,7 @@ class BlockOutlineRenderer(
|
||||
override fun draw() {
|
||||
val raycastHit = renderWindow.inputHandler.camera.getTargetBlock()
|
||||
|
||||
var outlineMesh = outlineMesh
|
||||
var collisionMesh = collisionMesh
|
||||
var currentMesh = currentMesh
|
||||
|
||||
if (raycastHit == null) {
|
||||
unload()
|
||||
@ -86,33 +82,29 @@ class BlockOutlineRenderer(
|
||||
}
|
||||
|
||||
if (raycastHit.blockPosition == currentOutlinePosition && raycastHit.blockState == currentOutlineBlockState) {
|
||||
draw(outlineMesh!!, collisionMesh)
|
||||
drawMesh()
|
||||
return
|
||||
}
|
||||
|
||||
outlineMesh?.unload()
|
||||
collisionMesh?.unload()
|
||||
outlineMesh = LineMesh(Minosoft.config.config.game.other.blockOutline.outlineColor, LINE_WIDTH)
|
||||
currentMesh?.unload()
|
||||
currentMesh = LineMesh()
|
||||
|
||||
val blockOffset = raycastHit.blockPosition.toVec3d + raycastHit.blockPosition.getWorldOffset(raycastHit.blockState.block)
|
||||
|
||||
outlineMesh.drawVoxelShape(raycastHit.blockState.outlineShape, blockOffset, outlineMesh)
|
||||
outlineMesh.load()
|
||||
currentMesh.drawVoxelShape(raycastHit.blockState.outlineShape, blockOffset, LINE_WIDTH, Minosoft.config.config.game.other.blockOutline.outlineColor)
|
||||
|
||||
|
||||
if (Minosoft.config.config.game.other.blockOutline.collisionBoxes) {
|
||||
collisionMesh = LineMesh(Minosoft.config.config.game.other.blockOutline.collisionColor, LINE_WIDTH)
|
||||
|
||||
collisionMesh.drawVoxelShape(raycastHit.blockState.collisionShape, blockOffset, collisionMesh, 0.005f)
|
||||
collisionMesh.load()
|
||||
this.collisionMesh = collisionMesh
|
||||
currentMesh.drawVoxelShape(raycastHit.blockState.collisionShape, blockOffset, LINE_WIDTH, Minosoft.config.config.game.other.blockOutline.collisionColor, 0.005f)
|
||||
}
|
||||
|
||||
currentMesh.load()
|
||||
|
||||
|
||||
this.currentOutlinePosition = raycastHit.blockPosition
|
||||
this.currentOutlineBlockState = raycastHit.blockState
|
||||
this.outlineMesh = outlineMesh
|
||||
draw(outlineMesh, collisionMesh)
|
||||
this.currentMesh = currentMesh
|
||||
drawMesh()
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.entities
|
||||
|
||||
import de.bixilon.minosoft.Minosoft
|
||||
import de.bixilon.minosoft.data.entities.entities.Entity
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.models.AABB
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
|
||||
import de.bixilon.minosoft.gui.rendering.util.mesh.LineMesh
|
||||
import glm_.vec3.Vec3
|
||||
import glm_.vec3.Vec3d
|
||||
|
||||
class EntityHitBoxMesh(
|
||||
val entity: Entity,
|
||||
) : LineMesh() {
|
||||
val aabb = entity.aabb
|
||||
|
||||
init {
|
||||
val hitboxColor = when {
|
||||
entity.isInvisible -> Minosoft.config.config.game.entities.hitBox.invisibleEntitiesColor
|
||||
else -> Minosoft.config.config.game.entities.hitBox.hitBoxColor
|
||||
}
|
||||
drawAABB(entity.aabb, Vec3d.EMPTY, LINE_WIDTH, hitboxColor)
|
||||
|
||||
val halfWidth = entity.dimensions.x / 2
|
||||
val eyeAABB = AABB(Vec3(-halfWidth, entity.eyeHeight - LINE_WIDTH, -halfWidth), Vec3(halfWidth, entity.eyeHeight - LINE_WIDTH, halfWidth))
|
||||
drawAABB(eyeAABB, entity.position, LINE_WIDTH, Minosoft.config.config.game.entities.hitBox.eyeHeightColor)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val LINE_WIDTH = 1.0f / 128.0f
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.entities
|
||||
|
||||
import de.bixilon.minosoft.Minosoft
|
||||
import de.bixilon.minosoft.data.entities.entities.Entity
|
||||
import de.bixilon.minosoft.data.mappings.ResourceLocation
|
||||
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.modding.event.CallbackEventInvoker
|
||||
import de.bixilon.minosoft.modding.event.events.EntityDestroyEvent
|
||||
import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent
|
||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
|
||||
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
|
||||
import de.bixilon.minosoft.util.collections.SynchronizedMap
|
||||
import org.lwjgl.opengl.GL11.*
|
||||
|
||||
class EntityHitBoxRenderer(
|
||||
val connection: PlayConnection,
|
||||
val renderWindow: RenderWindow,
|
||||
) : Renderer {
|
||||
private val meshes: SynchronizedMap<Entity, EntityHitBoxMesh> = synchronizedMapOf()
|
||||
|
||||
|
||||
private fun createMesh(entity: Entity): EntityHitBoxMesh? {
|
||||
if (entity.isInvisible && !Minosoft.config.config.game.entities.hitBox.renderInvisibleEntities) {
|
||||
return null
|
||||
}
|
||||
val mesh = EntityHitBoxMesh(entity)
|
||||
mesh.load()
|
||||
this.meshes[entity] = mesh
|
||||
|
||||
return mesh
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
connection.registerEvent(CallbackEventInvoker.of<EntitySpawnEvent> {
|
||||
renderWindow.queue += {
|
||||
createMesh(it.entity)
|
||||
}
|
||||
})
|
||||
connection.registerEvent(CallbackEventInvoker.of<EntityDestroyEvent> {
|
||||
val meshes: MutableSet<EntityHitBoxMesh> = mutableSetOf()
|
||||
for (entity in it.entities) {
|
||||
val mesh = this.meshes.getAndRemove(entity) ?: continue
|
||||
meshes += mesh
|
||||
}
|
||||
|
||||
renderWindow.queue += {
|
||||
for (mesh in meshes) {
|
||||
mesh.unload(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun draw() {
|
||||
glDisable(GL_CULL_FACE)
|
||||
if (Minosoft.config.config.game.entities.hitBox.disableZBuffer) {
|
||||
glDepthFunc(GL_ALWAYS)
|
||||
}
|
||||
renderWindow.shaderManager.genericColorShader.use()
|
||||
|
||||
for ((entity, mesh) in meshes.toSynchronizedMap()) {
|
||||
val aabb = entity.aabb
|
||||
if (aabb != mesh.aabb) {
|
||||
this.meshes.remove(entity)
|
||||
mesh.unload()
|
||||
|
||||
createMesh(entity)?.draw()
|
||||
continue
|
||||
}
|
||||
|
||||
mesh.draw()
|
||||
}
|
||||
|
||||
|
||||
glEnable(GL_CULL_FACE)
|
||||
if (Minosoft.config.config.game.entities.hitBox.disableZBuffer) {
|
||||
glDepthFunc(GL_LESS)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object : RendererBuilder<EntityHitBoxRenderer> {
|
||||
override val RESOURCE_LOCATION = ResourceLocation("minosoft:entity_hitbox")
|
||||
|
||||
override fun build(connection: PlayConnection, renderWindow: RenderWindow): EntityHitBoxRenderer {
|
||||
return EntityHitBoxRenderer(connection, renderWindow)
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ 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.AABB
|
||||
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
|
||||
@ -22,14 +23,9 @@ 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
|
||||
open class LineMesh : GenericColorMesh() {
|
||||
|
||||
|
||||
fun drawLine(start: Vec3, end: Vec3, mesh: GenericColorMesh) {
|
||||
fun drawLine(start: Vec3, end: Vec3, lineWidth: Float, color: RGBColor) {
|
||||
val direction = (end - start).normalize()
|
||||
val normal1 = Vec3(direction.z, direction.z, direction.x - direction.y)
|
||||
if (normal1 == Vec3.EMPTY) {
|
||||
@ -39,11 +35,12 @@ class LineMesh(
|
||||
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))
|
||||
drawLineQuad(start, end, direction, normal1, normal2, i.isBit(0), i.isBit(1), lineWidth, color)
|
||||
}
|
||||
}
|
||||
|
||||
fun drawLineQuad(mesh: GenericColorMesh, start: Vec3, end: Vec3, direction: Vec3, normal1: Vec3, normal2: Vec3, invertNormal1: Boolean, invertNormal2: Boolean) {
|
||||
private fun drawLineQuad(start: Vec3, end: Vec3, direction: Vec3, normal1: Vec3, normal2: Vec3, invertNormal1: Boolean, invertNormal2: Boolean, lineWidth: Float, color: RGBColor) {
|
||||
val halfLineWidth = lineWidth / 2
|
||||
val normal1Multiplier = invertNormal1.positiveNegative
|
||||
val normal2Multiplier = invertNormal2.positiveNegative
|
||||
val positions = listOf(
|
||||
@ -53,33 +50,37 @@ class LineMesh(
|
||||
end + normal2 * normal2Multiplier * halfLineWidth + direction * halfLineWidth,
|
||||
)
|
||||
for ((_, positionIndex) in ElementRenderer.DRAW_ODER) {
|
||||
mesh.addVertex(positions[positionIndex], color)
|
||||
addVertex(positions[positionIndex], color)
|
||||
}
|
||||
}
|
||||
|
||||
fun drawVoxelShape(shape: VoxelShape, blockPosition: Vec3d, mesh: GenericColorMesh, margin: Float = 0.0f) {
|
||||
fun drawAABB(aabb: AABB, position: Vec3d, lineWidth: Float, color: RGBColor, margin: Float = 0.0f) {
|
||||
val min = position + aabb.min - margin
|
||||
val max = position + aabb.max + margin
|
||||
|
||||
fun drawSideQuad(x: Double) {
|
||||
drawLine(Vec3(x, min.y, min.z), Vec3(x, max.y, min.z), lineWidth, color)
|
||||
drawLine(Vec3(x, min.y, min.z), Vec3(x, min.y, max.z), lineWidth, color)
|
||||
drawLine(Vec3(x, max.y, min.z), Vec3(x, max.y, max.z), lineWidth, color)
|
||||
drawLine(Vec3(x, min.y, max.z), Vec3(x, max.y, max.z), lineWidth, color)
|
||||
}
|
||||
|
||||
// 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), lineWidth, color)
|
||||
drawLine(Vec3(min.x, max.y, min.z), Vec3(max.x, max.y, min.z), lineWidth, color)
|
||||
drawLine(Vec3(min.x, max.y, max.z), Vec3(max.x, max.y, max.z), lineWidth, color)
|
||||
drawLine(Vec3(min.x, min.y, max.z), Vec3(max.x, min.y, max.z), lineWidth, color)
|
||||
}
|
||||
|
||||
fun drawVoxelShape(shape: VoxelShape, position: Vec3d, lineWidth: Float, color: RGBColor, 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)
|
||||
drawAABB(aabb, position, lineWidth, color, margin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,4 +189,12 @@ class SynchronizedMap<K, V>(
|
||||
return original.replace(key, oldValue, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
fun getAndRemove(key: K): V? {
|
||||
synchronized(lock) {
|
||||
val value = this[key] ?: return null
|
||||
this.remove(key)
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user