diff --git a/src/main/java/de/bixilon/minosoft/data/Directions.kt b/src/main/java/de/bixilon/minosoft/data/Directions.kt index a103979f2..dfe43b316 100644 --- a/src/main/java/de/bixilon/minosoft/data/Directions.kt +++ b/src/main/java/de/bixilon/minosoft/data/Directions.kt @@ -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 { override val VALUES = values() override val NAME_MAP: Map = 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") diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/VoxelShape.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/VoxelShape.kt index 81bee2611..ba7935763 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/VoxelShape.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/VoxelShape.kt @@ -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 = 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 { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/AABB.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/AABB.kt index 064ed238c..c642ac992 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/AABB.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/AABB.kt @@ -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 + } + 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 } - return tMin - } - - 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 { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt index 08c0d9ba2..5142136c3 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt @@ -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 - return RaycastHit( - currentPosition, - getTotalDistance() + distance, - blockState = blockState, - hitDirection = currentPosition.nearestIntegerPositionDirection, - steps = i, - ) + 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, + 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 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/RaycastHit.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/RaycastHit.kt index f096bf167..82f924554 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/RaycastHit.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/RaycastHit.kt @@ -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) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt index 2d196fcd4..c21050c30 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt @@ -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 - } else { - position.floor.choose(axis) + 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[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,23 +262,40 @@ object VecUtil { return Vec3i(this.x.floor, this.y.floor, this.z.floor) } - fun Vec3i.choose(axis: Axes): Int { - return Axes.choose(axis, this) + 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 = if (direction[direction.axis] > 0f) { + getDistance(aabb.max, direction) + } else { + getDistance(aabb.min, direction) + } + if (distance < minDistance) { + minDistance = distance + minDistanceDirection = direction + } + } + return minDistanceDirection } - val Vec3.nearestIntegerPositionDirection: Directions - get() { - var minDistance = Float.MAX_VALUE - var minDistanceDirection = Directions.UP - for (direction in Directions.VALUES) { - val distance = (getTargetForNextIntegerAxisInDirection(direction.directionVector.toVec3, this, direction.axis) - this.choose(direction.axis)) / direction.directionVector.choose(direction.axis) - if (distance < minDistance) { - minDistance = distance - minDistanceDirection = direction - } - } - return minDistanceDirection + operator fun Vec3.get(axis: Axes): Float { + return when(axis) { + Axes.X -> this.x + Axes.Y -> this.y + Axes.Z -> this.z } + } - val Vec3i.toVec3: Vec3 get() = Vec3(this) + operator fun Vec3i.get(axis: Axes): Int { + return when(axis) { + Axes.X -> this.x + Axes.Y -> this.y + Axes.Z -> this.z + } + } } diff --git a/src/main/java/de/bixilon/minosoft/util/MMath.kt b/src/main/java/de/bixilon/minosoft/util/MMath.kt index f7ff2c91e..4a5ba0c66 100644 --- a/src/main/java/de/bixilon/minosoft/util/MMath.kt +++ b/src/main/java/de/bixilon/minosoft/util/MMath.kt @@ -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) {