Archived
1
0
Fork 0

Add Vector class for calculations

This commit is contained in:
Moritz Ruth 2020-12-27 23:26:00 +01:00
parent c0d21c8b5d
commit 1a69b3f3a1
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
8 changed files with 121 additions and 31 deletions

View file

@ -1 +1,15 @@
# Blokk # Blokk
## Conventions
### KDoc
1. If the name of the target is already sufficient for understanding what it does/represents, you should **not** add a comment. But if you want to provide additional information, you should start the comment with a short description nevertheless.
2. The name of the return type, property type or type of the enclosing class should **not** be wrapped in square
brackets.
3. If a comment only consists of the `@returns` block tag, the latter should be replaced with a sentence starting with
`Returns `.
### Code
1. If a member function of a class creates an instance of another class which represents the same value, the functions name should be `as<name of the other class>`. If the new instance does not exactly represent the value of the original instance (for example because it is rounded), the name should be `to<name of the other class>`.

View file

@ -0,0 +1,55 @@
package space.blokk
import space.blokk.util.clamp
import space.blokk.world.Location
import space.blokk.world.VoxelLocation
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.math.sqrt
data class Vector(val x: Double, val y: Double, val z: Double) {
/**
* Returns a VoxelLocation by converting [x], [y] and z to Int and clamping y between 0 and 255.
*/
fun toVoxelLocation() = VoxelLocation(x.toInt(), y.toInt().clamp(0..255).toUByte(), z.toInt())
fun roundToBlock() = VoxelLocation(x.roundToInt(), y.toInt().clamp(0..255).toUByte(), z.roundToInt())
fun asLocation() = Location(x, y, z)
operator fun plus(other: Vector) = Vector(x + other.x, y + other.y, z + other.z)
operator fun minus(other: Vector) = Vector(x - other.x, y - other.y, z - other.z)
operator fun div(other: Vector) = Vector(x / other.x, y / other.y, z / other.z)
operator fun times(other: Vector) = Vector(x * other.x, y * other.y, z * other.z)
operator fun get(part: CoordinatePart): Double = when (part) {
CoordinatePart.X -> x
CoordinatePart.Y -> y
CoordinatePart.Z -> z
}
companion object {
/**
* Returns a Vector with the maximum x, y and z values of [a] and [b].
*/
fun withHigherValuesOf(a: Vector, b: Vector) = Vector(maxOf(a.x, b.x), maxOf(a.y, b.y), maxOf(a.z, b.z))
/**
* Returns a Vector with the minimum x, y and z values of [a] and [b].
*/
fun withLowerValuesOf(a: Vector, b: Vector) = Vector(minOf(a.x, b.x), minOf(a.y, b.y), minOf(a.z, b.z))
/**
* Returns the (euclidean) distance between [a] and [b].
*/
fun distanceBetween(a: Vector, b: Vector): Double {
val (x, y, z) = abs(a - b)
return sqrt(sqrt(x.pow(2) + y.pow(2)) + z.pow(2))
}
}
}
/**
* Returns a Vector with the absolute values of x, y and z.
*/
fun abs(vector: Vector) = Vector(abs(vector.x), abs(vector.y), abs(vector.z))

View file

@ -0,0 +1,3 @@
package space.blokk.util
fun Int.clamp(range: IntRange) = maxOf(minOf(range.first, range.last), minOf(maxOf(range.first, range.last), this))

View file

@ -1,5 +1,7 @@
package space.blokk.world package space.blokk.world
import space.blokk.CoordinatePart
import space.blokk.Vector
import kotlin.math.roundToInt import kotlin.math.roundToInt
sealed class AbstractWorldAndLocation<T : AbstractLocation> { sealed class AbstractWorldAndLocation<T : AbstractLocation> {
@ -23,14 +25,14 @@ sealed class AbstractLocation {
abstract val z: Double abstract val z: Double
/** /**
* Converts this [Location] to a [VoxelLocation] by converting [x], [y] and [z] to an integer using [Double.toInt], * Converts this Location to a VoxelLocation by converting [x], [y] and [z] to integers using [Double.toInt],
* in contrast to [roundToBlock] which uses [Double.roundToInt]. * in contrast to [roundToBlock] which uses [Double.roundToInt].
*/ */
fun asVoxelLocation(): VoxelLocation = VoxelLocation(x.toInt(), y.toInt().toUByte(), z.toInt()) fun toVoxelLocation(): VoxelLocation = VoxelLocation(x.toInt(), y.toInt().toUByte(), z.toInt())
/** /**
* Converts this [Location] to a [VoxelLocation] by rounding [x], [y] and [z] to an integer using * Converts this Location to a VoxelLocation by converting [x], [y] and [z] to integers using [Double.roundToInt],
* [Double.roundToInt], in contrast to [asVoxelLocation] which uses [Double.toInt]. * in contrast to [toVoxelLocation] which uses [Double.toInt].
*/ */
fun roundToBlock(): VoxelLocation = VoxelLocation(x.roundToInt(), y.roundToInt().toUByte(), z.roundToInt()) fun roundToBlock(): VoxelLocation = VoxelLocation(x.roundToInt(), y.roundToInt().toUByte(), z.roundToInt())
@ -43,6 +45,12 @@ sealed class AbstractLocation {
* Returns a pair of [world] and this location. * Returns a pair of [world] and this location.
*/ */
abstract infix fun inside(world: World): AbstractWorldAndLocation<*> abstract infix fun inside(world: World): AbstractWorldAndLocation<*>
operator fun get(part: CoordinatePart): Double = when (part) {
CoordinatePart.X -> x
CoordinatePart.Y -> y
CoordinatePart.Z -> z
}
} }
/** /**
@ -52,6 +60,8 @@ sealed class AbstractLocation {
*/ */
data class Location(override val x: Double, override val y: Double, override val z: Double) : AbstractLocation() { data class Location(override val x: Double, override val y: Double, override val z: Double) : AbstractLocation() {
override fun inside(world: World) = WorldAndLocation(world, this) override fun inside(world: World) = WorldAndLocation(world, this)
fun asVector() = Vector(x, y, z)
} }
data class LocationWithRotation( data class LocationWithRotation(
@ -62,4 +72,6 @@ data class LocationWithRotation(
val pitch: Float val pitch: Float
) : AbstractLocation() { ) : AbstractLocation() {
override fun inside(world: World) = WorldAndLocationWithRotation(world, this) override fun inside(world: World) = WorldAndLocationWithRotation(world, this)
fun toVector() = Vector(x, y, z)
} }

View file

@ -1,44 +1,49 @@
package space.blokk.world package space.blokk.world
import space.blokk.CoordinatePart import space.blokk.CoordinatePart
import space.blokk.Vector
data class VoxelLocation(val x: Int, val y: UByte, val z: Int) {
constructor(x: Int, y: Int, z: Int): this(x, y.toUByte(), z)
/** /**
* Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble]. * A location consisting of an x, y and z coordinate.
*
* This class does not contain any functions for calculations, but [Vector] does.
* You can convert a VoxelLocation to a Vector using [asVector].
*/
data class VoxelLocation(val x: Int, val y: UByte, val z: Int) {
/**
* Converts this VoxelLocation to a
*/ */
fun asLocation(): Location = Location(x.toDouble(), y.toDouble(), z.toDouble()) fun asLocation(): Location = Location(x.toDouble(), y.toDouble(), z.toDouble())
fun asVector(): Vector = Vector(x.toDouble(), y.toDouble(), z.toDouble())
/** /**
* Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble] * Converts this VoxelLocation to a Location **and then adds 0.5 to x, y and z**.
* and then adding 0.5 to each value.
* *
* Example: `VoxelLocation(x = 1, y = 2, z = 3)` becomes `Location(x = 1.5, y = 2.5, z = 3.5)`. * Example: `VoxelLocation(x = 1, y = 2, z = 3)` becomes `Location(x = 1.5, y = 2.5, z = 3.5)`.
*/ */
fun atCenter(): Location = Location(x.toDouble() + 0.5, y.toDouble() + 0.5, z.toDouble() + 0.5) fun atCenter(): Location = Location(x.toDouble() + 0.5, y.toDouble() + 0.5, z.toDouble() + 0.5)
/** /**
* Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble] * Converts this VoxelLocation to a Location **and then adds 0.5 to x and z, but not y**.
* and then adding 0.5 to x and z, but not y.
* *
* Example: `VoxelLocation(x = 1, y = 2, z = 3)` becomes `Location(x = 1.5, y = 2, z = 3.5)`. * Example: `VoxelLocation(x = 1, y = 2, z = 3)` becomes `Location(x = 1.5, y = 2, z = 3.5)`.
*/ */
fun atTopCenter(): Location = Location(x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5) fun atTopCenter(): Location = Location(x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5)
/** /**
* @return A new [VoxelLocation] with the maximum x, y and z values of a and b. * Returns a VoxelLocation with the maximum x, y and z values of this and [other].
*/ */
fun withHighestValues(other: VoxelLocation) = VoxelLocation(maxOf(x, other.x), maxOf(y, other.y), maxOf(z, other.z)) fun withHighestValues(other: VoxelLocation) = VoxelLocation(maxOf(x, other.x), maxOf(y, other.y), maxOf(z, other.z))
/** /**
* @return A new [VoxelLocation] with the minimum x, y and z values of a and b. * Returns a VoxelLocation with the minimum x, y and z values of this and [other].
*/ */
fun withLowestValues(other: VoxelLocation) = VoxelLocation(minOf(x, other.x), minOf(y, other.y), minOf(z, other.z)) fun withLowestValues(other: VoxelLocation) = VoxelLocation(minOf(x, other.x), minOf(y, other.y), minOf(z, other.z))
operator fun get(part: CoordinatePart) = when (part) { operator fun get(part: CoordinatePart): Int = when (part) {
CoordinatePart.X -> x CoordinatePart.X -> x
CoordinatePart.Y -> y.toInt() // Because otherwise the return type of this function would be Any CoordinatePart.Y -> y.toInt()
CoordinatePart.Z -> z CoordinatePart.Z -> z
} }
} }

View file

@ -52,19 +52,23 @@ abstract class World(val uuid: UUID) {
fun getVoxel(location: VoxelLocation): Voxel = getChunk(location).getVoxel(location) fun getVoxel(location: VoxelLocation): Voxel = getChunk(location).getVoxel(location)
/** /**
* Returns all voxels in the cube with the corner points [firstCorner] and [secondCorner]. * Returns all voxels in the cube with the corner points [cornerA] and [cornerB].
* *
* @param order The nesting order of the arrays. * @param hollow Whether the cube is hollow
*/ */
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
fun getVoxelsInCube(firstCorner: VoxelLocation, secondCorner: VoxelLocation): List<Voxel> { fun getVoxelsInCube(cornerA: VoxelLocation, cornerB: VoxelLocation, hollow: Boolean = false): List<Voxel> {
val start = firstCorner.withLowestValues(secondCorner) val xList = if (hollow) listOf(cornerA.x, cornerB.x).distinct() else (cornerA.x..cornerB.x).toList()
val end = firstCorner.withHighestValues(secondCorner) val zList = if (hollow) listOf(cornerA.z, cornerB.z).distinct() else (cornerA.z..cornerB.z).toList()
val yList =
if (hollow) listOf(cornerA.y.toInt(), cornerB.y.toInt()).distinct()
else (cornerA.y.toInt()..cornerB.y.toInt()).toList()
return buildList { return buildList {
for(x in start.x..end.x) { for(x in xList) {
for(y in start.y..end.y) { for(y in yList) {
for(z in start.z..end.z) { for(z in zList) {
add(getVoxel(VoxelLocation(x, y.toUByte(), z))) add(getVoxel(VoxelLocation(x, y.toUByte(), z)))
} }
} }
@ -77,9 +81,10 @@ abstract class World(val uuid: UUID) {
*/ */
fun getVoxelsInSphere( fun getVoxelsInSphere(
center: VoxelLocation, center: VoxelLocation,
radius: Int radius: Int,
hollow: Boolean
): List<Voxel> { ): List<Voxel> {
// https://www.reddit.com/r/VoxelGameDev/comments/2cttnt/how_to_create_a_sphere_out_of_voxels/ // Implementation example https://git.io/JLysq
TODO() TODO()
} }

View file

@ -12,7 +12,6 @@ import space.blokk.net.event.SessionAfterLoginEvent
import space.blokk.net.packet.login.* import space.blokk.net.packet.login.*
import space.blokk.net.packet.play.* import space.blokk.net.packet.play.*
import space.blokk.player.BlokkPlayer import space.blokk.player.BlokkPlayer
import space.blokk.player.GameMode
import space.blokk.tag.TagRegistry import space.blokk.tag.TagRegistry
import space.blokk.util.AuthenticationHelper import space.blokk.util.AuthenticationHelper
import space.blokk.util.EncryptionUtils import space.blokk.util.EncryptionUtils
@ -199,7 +198,7 @@ class LoginAndJoinProcedure(val session: BlokkSession) {
) )
) )
session.send(UpdateViewPositionPacket(Chunk.Key.from(player.location.asVoxelLocation()))) session.send(UpdateViewPositionPacket(Chunk.Key.from(player.location.toVoxelLocation())))
session.lastKeepAlivePacketTimestamp = System.currentTimeMillis() session.lastKeepAlivePacketTimestamp = System.currentTimeMillis()
session.scheduleKeepAlivePacket() session.scheduleKeepAlivePacket()

View file

@ -1,12 +1,9 @@
package space.blokk.player package space.blokk.player
import space.blokk.chat.TextComponent import space.blokk.chat.TextComponent
import space.blokk.event.EventBus
import space.blokk.logging.Logger
import space.blokk.net.Session import space.blokk.net.Session
import space.blokk.net.packet.play.ChunkDataPacket import space.blokk.net.packet.play.ChunkDataPacket
import space.blokk.net.packet.play.ChunkLightDataPacket import space.blokk.net.packet.play.ChunkLightDataPacket
import space.blokk.player.event.PlayerEvent
import space.blokk.world.Chunk import space.blokk.world.Chunk
import space.blokk.world.LocationWithRotation import space.blokk.world.LocationWithRotation
import space.blokk.world.VoxelLocation import space.blokk.world.VoxelLocation
@ -53,7 +50,7 @@ class BlokkPlayer(
*/ */
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
fun updateCurrentlyViewedChunks() { fun updateCurrentlyViewedChunks() {
val (centerX, centerZ) = Chunk.Key.from(location.asVoxelLocation()) val (centerX, centerZ) = Chunk.Key.from(location.toVoxelLocation())
val edgeLength = settings.viewDistance + 1 val edgeLength = settings.viewDistance + 1