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
## 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
import space.blokk.CoordinatePart
import space.blokk.Vector
import kotlin.math.roundToInt
sealed class AbstractWorldAndLocation<T : AbstractLocation> {
@ -23,14 +25,14 @@ sealed class AbstractLocation {
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].
*/
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
* [Double.roundToInt], in contrast to [asVoxelLocation] which uses [Double.toInt].
* Converts this Location to a VoxelLocation by converting [x], [y] and [z] to integers using [Double.roundToInt],
* in contrast to [toVoxelLocation] which uses [Double.toInt].
*/
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.
*/
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() {
override fun inside(world: World) = WorldAndLocation(world, this)
fun asVector() = Vector(x, y, z)
}
data class LocationWithRotation(
@ -62,4 +72,6 @@ data class LocationWithRotation(
val pitch: Float
) : AbstractLocation() {
override fun inside(world: World) = WorldAndLocationWithRotation(world, this)
fun toVector() = Vector(x, y, z)
}

View file

@ -1,44 +1,49 @@
package space.blokk.world
import space.blokk.CoordinatePart
data class VoxelLocation(val x: Int, val y: UByte, val z: Int) {
constructor(x: Int, y: Int, z: Int): this(x, y.toUByte(), z)
import space.blokk.Vector
/**
* 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 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]
* and then adding 0.5 to each value.
* Converts this VoxelLocation to a Location **and then adds 0.5 to x, y and z**.
*
* 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)
/**
* Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble]
* and then adding 0.5 to x and z, but not y.
* Converts this VoxelLocation to a Location **and then adds 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)`.
*/
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))
/**
* @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))
operator fun get(part: CoordinatePart) = when (part) {
operator fun get(part: CoordinatePart): Int = when (part) {
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
}
}

View file

@ -52,19 +52,23 @@ abstract class World(val uuid: UUID) {
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)
fun getVoxelsInCube(firstCorner: VoxelLocation, secondCorner: VoxelLocation): List<Voxel> {
val start = firstCorner.withLowestValues(secondCorner)
val end = firstCorner.withHighestValues(secondCorner)
fun getVoxelsInCube(cornerA: VoxelLocation, cornerB: VoxelLocation, hollow: Boolean = false): List<Voxel> {
val xList = if (hollow) listOf(cornerA.x, cornerB.x).distinct() else (cornerA.x..cornerB.x).toList()
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 {
for(x in start.x..end.x) {
for(y in start.y..end.y) {
for(z in start.z..end.z) {
for(x in xList) {
for(y in yList) {
for(z in zList) {
add(getVoxel(VoxelLocation(x, y.toUByte(), z)))
}
}
@ -77,9 +81,10 @@ abstract class World(val uuid: UUID) {
*/
fun getVoxelsInSphere(
center: VoxelLocation,
radius: Int
radius: Int,
hollow: Boolean
): 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()
}

View file

@ -12,7 +12,6 @@ import space.blokk.net.event.SessionAfterLoginEvent
import space.blokk.net.packet.login.*
import space.blokk.net.packet.play.*
import space.blokk.player.BlokkPlayer
import space.blokk.player.GameMode
import space.blokk.tag.TagRegistry
import space.blokk.util.AuthenticationHelper
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.scheduleKeepAlivePacket()

View file

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