Add Vector class for calculations
This commit is contained in:
parent
c0d21c8b5d
commit
1a69b3f3a1
8 changed files with 121 additions and 31 deletions
14
README.md
14
README.md
|
@ -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 function’s 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>`.
|
||||
|
|
55
blokk-api/src/main/kotlin/space/blokk/Vector.kt
Normal file
55
blokk-api/src/main/kotlin/space/blokk/Vector.kt
Normal 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))
|
3
blokk-api/src/main/kotlin/space/blokk/util/Clamp.kt
Normal file
3
blokk-api/src/main/kotlin/space/blokk/util/Clamp.kt
Normal 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))
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Reference in a new issue