mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 09:26:11 -04:00
physics: improve raycasting
This commit is contained in:
parent
21a7d6856c
commit
52f873c5f8
@ -15,6 +15,7 @@ package de.bixilon.minosoft.data
|
||||
import de.bixilon.minosoft.data.mappings.blocks.properties.serializer.BlockPropertiesSerializer
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.models.FaceSize
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.get
|
||||
import de.bixilon.minosoft.util.KUtil
|
||||
import de.bixilon.minosoft.util.enum.ValuesEnum
|
||||
import glm_.vec2.Vec2i
|
||||
@ -87,12 +88,15 @@ enum class Directions(val directionVector: Vec3i) {
|
||||
return false
|
||||
}
|
||||
|
||||
operator fun get(axis: Axes): Int {
|
||||
return directionVector[axis]
|
||||
}
|
||||
|
||||
|
||||
companion object : BlockPropertiesSerializer, ValuesEnum<Directions> {
|
||||
override val VALUES = values()
|
||||
override val NAME_MAP: Map<String, Directions> = KUtil.getEnumValues(VALUES)
|
||||
val SIDES = arrayOf(NORTH, SOUTH, WEST, EAST)
|
||||
const val SIDES_OFFSET = 2
|
||||
|
||||
override fun deserialize(value: Any): Directions {
|
||||
return NAME_MAP[value] ?: throw IllegalArgumentException("No such property: $value")
|
||||
|
@ -4,8 +4,10 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.data.Directions
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.models.AABB
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.getMinDistanceDirection
|
||||
import glm_.vec3.Vec3
|
||||
import glm_.vec3.Vec3i
|
||||
|
||||
@ -77,17 +79,22 @@ class VoxelShape(private val aabbs: MutableList<AABB> = mutableListOf()) : Itera
|
||||
return result
|
||||
}
|
||||
|
||||
fun raycast(position: Vec3, direction: Vec3): Float {
|
||||
data class VoxelShapeRaycastResult(val hit: Boolean, val distance: Float, val direction: Directions)
|
||||
|
||||
fun raycast(position: Vec3, direction: Vec3): VoxelShapeRaycastResult {
|
||||
var minDistance = Float.MAX_VALUE
|
||||
var minDistanceDirection = Directions.UP
|
||||
for (aabb in aabbs) {
|
||||
if (position in aabb) {
|
||||
return 0f
|
||||
return VoxelShapeRaycastResult(true, 0f, position.getMinDistanceDirection(aabb).inverted)
|
||||
}
|
||||
val current = aabb.raycast(position, direction)
|
||||
if (current >= -0.1f) {
|
||||
return 0f
|
||||
val currentDistance = aabb.raycast(position, direction)
|
||||
if (minDistance > currentDistance) {
|
||||
minDistance = currentDistance
|
||||
minDistanceDirection = (position + direction * currentDistance).getMinDistanceDirection(aabb)
|
||||
}
|
||||
}
|
||||
return -1f
|
||||
return VoxelShapeRaycastResult(minDistance != Float.MAX_VALUE, minDistance, minDistanceDirection.inverted)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -5,9 +5,7 @@ import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.choose
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.max
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.min
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.get
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
|
||||
import glm_.Java.Companion.glm
|
||||
import glm_.vec3.Vec3
|
||||
@ -36,9 +34,7 @@ class AABB {
|
||||
}
|
||||
|
||||
fun intersect(other: AABB): Boolean {
|
||||
return (min.x < other.max.x && max.x > other.min.x) &&
|
||||
(min.y < other.max.y && max.y > other.min.y) &&
|
||||
(min.z < other.max.z && max.z > other.min.z)
|
||||
return (min.x < other.max.x && max.x > other.min.x) && (min.y < other.max.y && max.y > other.min.y) && (min.z < other.max.z && max.z > other.min.z)
|
||||
}
|
||||
|
||||
operator fun plus(vec3: Vec3): AABB {
|
||||
@ -50,16 +46,8 @@ class AABB {
|
||||
}
|
||||
|
||||
operator fun plus(other: AABB): AABB {
|
||||
val newMin = Vec3(
|
||||
glm.min(min.x, other.min.x),
|
||||
glm.min(min.y, other.min.y),
|
||||
glm.min(min.z, other.min.z)
|
||||
)
|
||||
val newMax = Vec3(
|
||||
glm.max(max.x, other.max.x),
|
||||
glm.max(max.y, other.max.y),
|
||||
glm.max(max.z, other.max.z)
|
||||
)
|
||||
val newMin = Vec3(glm.min(min.x, other.min.x), glm.min(min.y, other.min.y), glm.min(min.z, other.min.z))
|
||||
val newMax = Vec3(glm.max(max.x, other.max.x), glm.max(max.y, other.max.y), glm.max(max.z, other.max.z))
|
||||
return AABB(newMin, newMax)
|
||||
}
|
||||
|
||||
@ -151,33 +139,30 @@ class AABB {
|
||||
}
|
||||
|
||||
fun raycast(position: Vec3, direction: Vec3): Float {
|
||||
val tMins = getLengthMultipliers(position, direction, min)
|
||||
val tMaxs = getLengthMultipliers(position, direction, max)
|
||||
val tMin = tMins.max
|
||||
val tMax = tMaxs.min
|
||||
if (tMax < 0 || tMin > tMax) {
|
||||
return -1f
|
||||
if (max - min == VecUtil.ONES_VEC3 || position in this) {
|
||||
return 0f
|
||||
}
|
||||
return tMin
|
||||
var tMin = 0f
|
||||
var tMax = +100f
|
||||
for (axis in Axes.VALUES) {
|
||||
val t1 = getLengthMultiplier(position, direction, min, axis)
|
||||
val t2 = getLengthMultiplier(position, direction, max, axis)
|
||||
tMin = glm.max(tMin, glm.min(t1, t2))
|
||||
tMax = glm.min(tMax, glm.max(t1, t2))
|
||||
}
|
||||
return if (tMax > tMin) {
|
||||
tMin
|
||||
} else {
|
||||
Float.MAX_VALUE
|
||||
}
|
||||
|
||||
private fun getLengthMultipliers(position: Vec3, direction: Vec3, target: Vec3): Vec3 {
|
||||
return Vec3(
|
||||
getLengthMultiplier(position, direction, target, Axes.X),
|
||||
getLengthMultiplier(position, direction, target, Axes.Y),
|
||||
getLengthMultiplier(position, direction, target, Axes.Z),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getLengthMultiplier(position: Vec3, direction: Vec3, target: Vec3, axis: Axes): Float {
|
||||
return (position.choose(axis) - target.choose(axis)) / direction.choose(axis)
|
||||
return (target[axis] - position[axis]) / direction[axis]
|
||||
}
|
||||
|
||||
operator fun contains(position: Vec3): Boolean {
|
||||
return (
|
||||
position.x in min.x..max.x &&
|
||||
position.y in min.y..max.y &&
|
||||
position.z in min.z..max.z )
|
||||
return (position.x in min.x..max.x && position.y in min.y..max.y && position.z in min.z..max.z)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -30,7 +30,6 @@ import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.floor
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.getWorldOffset
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkSectionPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.nearestIntegerPositionDirection
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
||||
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
|
||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||
@ -319,28 +318,26 @@ class Camera(
|
||||
|
||||
for (i in 0..RAYCAST_MAX_STEPS) {
|
||||
val blockPosition = currentPosition.floor
|
||||
val blockState = connection.world[blockPosition]
|
||||
val distance = blockState?.outlineShape?.let {
|
||||
val aabb = it + blockPosition + blockPosition.getWorldOffset(blockState.block)
|
||||
aabb.raycast(currentPosition, direction)
|
||||
} ?: -1.0f
|
||||
|
||||
if (distance >= 0.0f && blockState != null) {
|
||||
currentPosition += direction * distance
|
||||
val blockState = connection.world.getBlockState(blockPosition)
|
||||
if (blockState != null) {
|
||||
val voxelShapeRaycastResult = (blockState.outlineShape + blockPosition + blockPosition.getWorldOffset(blockState.block)).raycast(currentPosition, direction)
|
||||
if (voxelShapeRaycastResult.hit) {
|
||||
currentPosition += direction * voxelShapeRaycastResult.distance
|
||||
return RaycastHit(
|
||||
currentPosition,
|
||||
getTotalDistance() + distance,
|
||||
blockState = blockState,
|
||||
hitDirection = currentPosition.nearestIntegerPositionDirection,
|
||||
steps = i,
|
||||
blockPosition,
|
||||
getTotalDistance(),
|
||||
blockState,
|
||||
voxelShapeRaycastResult.direction,
|
||||
i,
|
||||
)
|
||||
}
|
||||
currentPosition += direction * (VecUtil.getDistanceToNextIntegerAxis(currentPosition, direction) + 0.001)
|
||||
}
|
||||
currentPosition += direction * (VecUtil.getDistanceToNextIntegerAxisInDirection(currentPosition, direction) + 0.001)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f)
|
||||
private const val PLAYER_EYE_HEIGHT = 1.3 // player is 1.8 blocks high, the camera is normally at 0.5. 1.8 - 0.5 = 1.3
|
||||
|
@ -15,16 +15,16 @@ package de.bixilon.minosoft.gui.rendering.input.camera
|
||||
|
||||
import de.bixilon.minosoft.data.Directions
|
||||
import de.bixilon.minosoft.data.mappings.blocks.BlockState
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.floor
|
||||
import glm_.vec3.Vec3
|
||||
import glm_.vec3.Vec3i
|
||||
|
||||
data class RaycastHit(
|
||||
val position: Vec3,
|
||||
val blockPosition: Vec3i,
|
||||
val distance: Float,
|
||||
val blockState: BlockState,
|
||||
val hitDirection: Directions,
|
||||
val steps: Int,
|
||||
) {
|
||||
val blockPosition = position.floor
|
||||
val hitPosition = position.minus(blockPosition)
|
||||
}
|
||||
|
@ -21,8 +21,11 @@ import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.data.Directions
|
||||
import de.bixilon.minosoft.data.mappings.blocks.RandomOffsetTypes
|
||||
import de.bixilon.minosoft.data.mappings.blocks.types.Block
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.models.AABB
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.MMath.ceilInt
|
||||
import de.bixilon.minosoft.util.MMath.floorInt
|
||||
import glm_.func.common.clamp
|
||||
import glm_.func.common.floor
|
||||
import glm_.func.cos
|
||||
@ -155,7 +158,11 @@ object VecUtil {
|
||||
}
|
||||
|
||||
fun Vec3i.Companion.of(chunkPosition: Vec2i, sectionHeight: Int, inChunkSectionPosition: Vec3i): Vec3i {
|
||||
return Vec3i(chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X + inChunkSectionPosition.x, sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y + inChunkSectionPosition.y, chunkPosition.y * ProtocolDefinition.SECTION_WIDTH_Z + inChunkSectionPosition.z) // ToDo
|
||||
return Vec3i(
|
||||
chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X + inChunkSectionPosition.x,
|
||||
sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y + inChunkSectionPosition.y,
|
||||
chunkPosition.y * ProtocolDefinition.SECTION_WIDTH_Z + inChunkSectionPosition.z
|
||||
) // ToDo
|
||||
}
|
||||
|
||||
infix operator fun Vec3i.plus(vec3: Vec3i?): Vec3i {
|
||||
@ -224,27 +231,21 @@ object VecUtil {
|
||||
return hash shr 16
|
||||
}
|
||||
|
||||
fun getDistanceToNextIntegerAxis(position: Vec3, direction: Vec3): Float {
|
||||
val directionXDistance = (direction * getLengthMultiplierToNextIntegerAxisInDirection(direction, position, Axes.X)).length()
|
||||
val directionYDistance = (direction * getLengthMultiplierToNextIntegerAxisInDirection(direction, position, Axes.Y)).length()
|
||||
val directionZDistance = (direction * getLengthMultiplierToNextIntegerAxisInDirection(direction, position, Axes.Z)).length()
|
||||
return glm.min(directionXDistance, directionYDistance, directionZDistance)
|
||||
}
|
||||
|
||||
private fun getLengthMultiplierToNextIntegerAxisInDirection(direction: Vec3, position: Vec3, axis: Axes): Float {
|
||||
return (getTargetForNextIntegerAxisInDirection(direction, position, axis) - position.choose(axis)) / direction.choose(axis)
|
||||
}
|
||||
|
||||
private fun getTargetForNextIntegerAxisInDirection(direction: Vec3, position: Vec3, axis: Axes): Int {
|
||||
return if (direction.choose(axis) > 0) {
|
||||
position.floor.choose(axis) + 1
|
||||
fun getDistanceToNextIntegerAxisInDirection(position: Vec3, direction: Vec3): Float {
|
||||
fun getTarget(direction: Vec3, position: Vec3, axis: Axes): Int {
|
||||
return if (direction[axis] > 0) {
|
||||
position[axis].floorInt + 1
|
||||
} else {
|
||||
position.floor.choose(axis)
|
||||
position[axis].ceilInt - 1
|
||||
}
|
||||
}
|
||||
|
||||
fun Vec3.choose(axis: Axes): Float {
|
||||
return Axes.choose(axis, this)
|
||||
fun getLengthMultiplier(direction: Vec3, position: Vec3, axis: Axes): Float {
|
||||
return (getTarget(direction, position, axis) - position[axis]) / direction[axis]
|
||||
}
|
||||
val directionXDistance = getLengthMultiplier(direction, position, Axes.X)
|
||||
val directionYDistance = getLengthMultiplier(direction, position, Axes.Y)
|
||||
val directionZDistance = getLengthMultiplier(direction, position, Axes.Z)
|
||||
return glm.min(directionXDistance, directionYDistance, directionZDistance)
|
||||
}
|
||||
|
||||
val Vec3.min: Float get() = glm.min(this.x, this.y, this.z)
|
||||
@ -261,16 +262,19 @@ object VecUtil {
|
||||
return Vec3i(this.x.floor, this.y.floor, this.z.floor)
|
||||
}
|
||||
|
||||
fun Vec3i.choose(axis: Axes): Int {
|
||||
return Axes.choose(axis, this)
|
||||
}
|
||||
|
||||
val Vec3.nearestIntegerPositionDirection: Directions
|
||||
get() {
|
||||
fun Vec3.getMinDistanceDirection(aabb: AABB): Directions {
|
||||
var minDistance = Float.MAX_VALUE
|
||||
var minDistanceDirection = Directions.UP
|
||||
fun getDistance(position: Vec3, direction: Directions): Float {
|
||||
val axis = direction.axis
|
||||
return (position[axis] - this[axis]) * -direction[axis]
|
||||
}
|
||||
for (direction in Directions.VALUES) {
|
||||
val distance = (getTargetForNextIntegerAxisInDirection(direction.directionVector.toVec3, this, direction.axis) - this.choose(direction.axis)) / direction.directionVector.choose(direction.axis)
|
||||
val distance = if (direction[direction.axis] > 0f) {
|
||||
getDistance(aabb.max, direction)
|
||||
} else {
|
||||
getDistance(aabb.min, direction)
|
||||
}
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance
|
||||
minDistanceDirection = direction
|
||||
@ -279,5 +283,19 @@ object VecUtil {
|
||||
return minDistanceDirection
|
||||
}
|
||||
|
||||
val Vec3i.toVec3: Vec3 get() = Vec3(this)
|
||||
operator fun Vec3.get(axis: Axes): Float {
|
||||
return when(axis) {
|
||||
Axes.X -> this.x
|
||||
Axes.Y -> this.y
|
||||
Axes.Z -> this.z
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Vec3i.get(axis: Axes): Int {
|
||||
return when(axis) {
|
||||
Axes.X -> this.x
|
||||
Axes.Y -> this.y
|
||||
Axes.Z -> this.z
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,13 +93,9 @@ object MMath {
|
||||
return value - floor(value)
|
||||
}
|
||||
|
||||
fun linearInterpolate(delta: Float, start: Float, end: Float): Float {
|
||||
return start + delta * (end - start)
|
||||
}
|
||||
val Float.floorInt: Int get() = glm.floor(this).toInt()
|
||||
|
||||
val Float.floor: Float get() = glm.floor(this)
|
||||
|
||||
val Float.fractionalPart: Float get() = this - floor
|
||||
val Float.ceilInt: Int get() = glm.ceil(this).toInt()
|
||||
|
||||
val Boolean.positiveNegative: Int get() =
|
||||
if (this) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user