mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -04:00
physics: reduce memory allocations a lot
This should improve performance of physics when exploding millions of tnt. Memory is being reused with the temporary allocator. Shapes are extracted initially, which is still bad, because most of them are empty.
This commit is contained in:
parent
2340fc1a3d
commit
728ca1ce2f
@ -82,4 +82,32 @@ class CollisionIT {
|
||||
player.assertPosition(0.0, 1.5, 0.0)
|
||||
player.assertVelocity(0.0, -0.0784000015258789, 0.0)
|
||||
}
|
||||
|
||||
fun `not crashing when already exceeding negative y`() {
|
||||
val player = createPlayer(createSession(2))
|
||||
player.forceTeleport(Vec3d(0.0, -3000, 0.0))
|
||||
player.physics.velocity = Vec3d(0.0, -150.0, 0.0)
|
||||
player.runTicks(10)
|
||||
}
|
||||
|
||||
fun `not crashing when exceeding negative y`() {
|
||||
val player = createPlayer(createSession(2))
|
||||
player.forceTeleport(Vec3d(0.0, -2040, 0.0))
|
||||
player.physics.velocity = Vec3d(0.0, -150.0, 0.0)
|
||||
player.runTicks(10)
|
||||
}
|
||||
|
||||
fun `not crashing when already exceeding positive y`() {
|
||||
val player = createPlayer(createSession(2))
|
||||
player.forceTeleport(Vec3d(0.0, 3000, 0.0))
|
||||
player.physics.velocity = Vec3d(0.0, 150.0, 0.0)
|
||||
player.runTicks(10)
|
||||
}
|
||||
|
||||
fun `not crashing when exceeding positive y`() {
|
||||
val player = createPlayer(createSession(2))
|
||||
player.forceTeleport(Vec3d(0.0, 2040, 0.0))
|
||||
player.physics.velocity = Vec3d(0.0, 150.0, 0.0)
|
||||
player.runTicks(10)
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import de.bixilon.kotlinglm.func.common.clamp
|
||||
import de.bixilon.kotlinglm.vec3.Vec3
|
||||
import de.bixilon.kotlinglm.vec3.Vec3d
|
||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||
import de.bixilon.kotlinglm.vec3.Vec3t
|
||||
import de.bixilon.kutil.math.simple.DoubleMath.ceil
|
||||
import de.bixilon.kutil.math.simple.DoubleMath.floor
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
@ -59,12 +58,13 @@ class AABB {
|
||||
this.max = max
|
||||
}
|
||||
|
||||
fun intersect(other: AABB): Boolean {
|
||||
fun intersects(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)
|
||||
}
|
||||
|
||||
operator fun plus(other: Vec3t<out Number>): AABB = offset(other)
|
||||
fun offset(other: Vec3t<out Number>) = AABB(true, min + other, max + other)
|
||||
fun intersects(other: AABB, offset: BlockPosition): Boolean {
|
||||
return (min.x < (other.max.x + offset.x) && max.x > (other.min.x + offset.x)) && (min.y < (other.max.y + offset.y) && max.y > (other.min.y + offset.y)) && (min.z < (other.max.z + offset.z) && max.z > (other.min.z + offset.z))
|
||||
}
|
||||
|
||||
operator fun plus(other: Vec3d): AABB = offset(other)
|
||||
fun offset(other: Vec3d) = AABB(true, min + other, max + other)
|
||||
@ -128,15 +128,10 @@ class AABB {
|
||||
return AABB(true, newMin, newMax)
|
||||
}
|
||||
|
||||
fun extend(vec3i: Vec3i): AABB {
|
||||
return this.extend(Vec3d(vec3i))
|
||||
}
|
||||
|
||||
fun extend(direction: Directions): AABB {
|
||||
return this.extend(direction.vectord)
|
||||
}
|
||||
|
||||
|
||||
fun grow(size: Double = 1.0E-7): AABB {
|
||||
return AABB(min - size, max + size)
|
||||
}
|
||||
@ -149,37 +144,38 @@ class AABB {
|
||||
return this > min && this < max
|
||||
}
|
||||
|
||||
private fun intersects(axis: Axes, other: AABB): Boolean {
|
||||
private fun intersects(axis: Axes, other: AABB, offset: BlockPosition): Boolean {
|
||||
val min = min[axis]
|
||||
val max = max[axis]
|
||||
|
||||
val otherMin = other.min[axis]
|
||||
val otherMax = other.max[axis]
|
||||
val otherMin = other.min[axis] + offset[axis]
|
||||
val otherMax = other.max[axis] + offset[axis]
|
||||
|
||||
return min.isIn(otherMin, otherMax)
|
||||
|| max.isIn(otherMin, otherMax)
|
||||
|| otherMin.isIn(min, max)
|
||||
|| otherMax.isIn(min, max)
|
||||
|| (min == otherMin && max == otherMax)
|
||||
|| max.isIn(otherMin, otherMax)
|
||||
|| otherMin.isIn(min, max)
|
||||
|| otherMax.isIn(min, max)
|
||||
|| (min == otherMin && max == otherMax)
|
||||
}
|
||||
|
||||
fun calculateMaxOffset(other: AABB, offset: Double, axis: Axes): Double {
|
||||
if (!intersects(axis.next(), other) || !intersects(axis.previous(), other)) {
|
||||
return offset
|
||||
fun calculateMaxDistance(other: AABB, maxDistance: Double, axis: Axes) = calculateMaxDistance(other, BlockPosition(), maxDistance, axis)
|
||||
fun calculateMaxDistance(other: AABB, offset: BlockPosition, maxDistance: Double, axis: Axes): Double {
|
||||
if (!intersects(axis.next(), other, offset) || !intersects(axis.previous(), other, offset)) {
|
||||
return maxDistance
|
||||
}
|
||||
val min = min[axis]
|
||||
val max = max[axis]
|
||||
val otherMin = other.min[axis]
|
||||
val otherMax = other.max[axis]
|
||||
val otherMin = other.min[axis] + offset[axis]
|
||||
val otherMax = other.max[axis] + offset[axis]
|
||||
|
||||
if (offset > 0 && otherMax <= min && otherMax + offset > min) {
|
||||
return (min - otherMax).clamp(0.0, offset)
|
||||
if (maxDistance > 0 && otherMax <= min && otherMax + maxDistance > min) {
|
||||
return (min - otherMax).clamp(0.0, maxDistance)
|
||||
}
|
||||
if (offset < 0 && max <= otherMin && otherMin + offset < max) {
|
||||
return (max - otherMin).clamp(offset, 0.0)
|
||||
if (maxDistance < 0 && max <= otherMin && otherMin + maxDistance < max) {
|
||||
return (max - otherMin).clamp(maxDistance, 0.0)
|
||||
}
|
||||
|
||||
return offset
|
||||
return maxDistance
|
||||
}
|
||||
|
||||
@Deprecated("mutable")
|
||||
@ -188,12 +184,10 @@ class AABB {
|
||||
max.array[axis.ordinal] += value
|
||||
}
|
||||
|
||||
fun offset(axis: Axes, offset: Double): AABB {
|
||||
return when (axis) {
|
||||
Axes.X -> this + Vec3d(-offset, 0.0, 0.0)
|
||||
Axes.Y -> this + Vec3d(0.0, -offset, 0.0)
|
||||
Axes.Z -> this + Vec3d(0.0, 0.0, -offset)
|
||||
}
|
||||
fun offset(axis: Axes, offset: Double) = when (axis) {
|
||||
Axes.X -> this + Vec3d(-offset, 0.0, 0.0)
|
||||
Axes.Y -> this + Vec3d(0.0, -offset, 0.0)
|
||||
Axes.Z -> this + Vec3d(0.0, 0.0, -offset)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,19 +290,19 @@ class AABB {
|
||||
|
||||
fun checkSide(x: Double): Boolean {
|
||||
return (this.min == Vec3d(x, min.y, min.z) && this.max == Vec3d(x, max.y, min.z))
|
||||
|| (this.min == Vec3d(x, min.y, min.z) && this.max == Vec3d(x, min.y, max.z))
|
||||
|| (this.min == Vec3d(x, max.y, min.z) && this.max == Vec3d(x, max.y, max.z))
|
||||
|| (this.min == Vec3d(x, min.y, max.z) && this.max == Vec3d(x, max.y, max.z))
|
||||
|| (this.min == Vec3d(x, min.y, min.z) && this.max == Vec3d(x, min.y, max.z))
|
||||
|| (this.min == Vec3d(x, max.y, min.z) && this.max == Vec3d(x, max.y, max.z))
|
||||
|| (this.min == Vec3d(x, min.y, max.z) && this.max == Vec3d(x, max.y, max.z))
|
||||
}
|
||||
|
||||
|
||||
return checkSide(min.x) // left quad
|
||||
|| checkSide(max.x) // right quad
|
||||
// connections between 2 quads
|
||||
|| (this.min == min && this.max == Vec3d(max.x, min.y, min.z))
|
||||
|| (this.min == Vec3d(min.x, max.y, min.z) && this.max == Vec3d(max.x, max.y, min.z))
|
||||
|| (this.min == Vec3d(min.x, max.y, max.z) && this.max == max)
|
||||
|| (this.min == Vec3d(min.x, min.y, max.z) && this.max == Vec3d(max.x, min.y, max.z))
|
||||
|| checkSide(max.x) // right quad
|
||||
// connections between 2 quads
|
||||
|| (this.min == min && this.max == Vec3d(max.x, min.y, min.z))
|
||||
|| (this.min == Vec3d(min.x, max.y, min.z) && this.max == Vec3d(max.x, max.y, min.z))
|
||||
|| (this.min == Vec3d(min.x, max.y, max.z) && this.max == max)
|
||||
|| (this.min == Vec3d(min.x, min.y, max.z) && this.max == Vec3d(max.x, min.y, max.z))
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2025 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.registries.shapes.collision
|
||||
|
||||
import de.bixilon.kotlinglm.vec3.Vec3d
|
||||
import de.bixilon.kutil.array.ArrayUtil.cast
|
||||
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.data.registries.blocks.shapes.collision.CollisionPredicate
|
||||
import de.bixilon.minosoft.data.registries.blocks.shapes.collision.context.CollisionContext
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.entity.BlockWithEntity
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.properties.shape.collision.CollidableBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.properties.shape.collision.fixed.FixedCollidable
|
||||
import de.bixilon.minosoft.data.registries.shapes.aabb.AABB
|
||||
import de.bixilon.minosoft.data.registries.shapes.voxel.AbstractVoxelShape
|
||||
import de.bixilon.minosoft.data.world.World
|
||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||
import de.bixilon.minosoft.data.world.iterator.WorldIterator
|
||||
import de.bixilon.minosoft.data.world.positions.BlockPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.allocator.LongAllocator
|
||||
import de.bixilon.minosoft.gui.rendering.util.allocator.TemporaryAllocator
|
||||
|
||||
class CollisionShape(
|
||||
val world: World,
|
||||
context: CollisionContext,
|
||||
aabb: AABB,
|
||||
movement: Vec3d,
|
||||
chunk: Chunk?,
|
||||
predicate: CollisionPredicate? = null,
|
||||
) : AbstractVoxelShape {
|
||||
override val aabbs: Int
|
||||
|
||||
private var total: Int
|
||||
private val positions: LongArray
|
||||
private val shapes: Array<AbstractVoxelShape>
|
||||
|
||||
|
||||
init {
|
||||
val aabbs = aabb.extend(movement).grow(1.0).positions()
|
||||
val positions = POSITIONS.allocate(aabbs.size)
|
||||
val shapes: Array<AbstractVoxelShape?> = SHAPES.allocate(aabbs.size)
|
||||
|
||||
var index = 0
|
||||
var total = 0
|
||||
|
||||
// TODO: add entity collisions (boat, shulker)
|
||||
// TODO: add world border collision shape
|
||||
|
||||
for ((position, state, chunk) in WorldIterator(aabbs, world, chunk)) {
|
||||
if (state.block !is CollidableBlock) continue
|
||||
if (predicate != null && !predicate.invoke(state)) continue
|
||||
// TODO: filter blocks (e.g. moving piston), whatever that means
|
||||
|
||||
val shape = when (state.block) {
|
||||
is FixedCollidable -> state.block.getCollisionShape(state)
|
||||
is BlockWithEntity<*> -> state.block.getCollisionShape(world.session, context, position, state, chunk.getBlockEntity(position.inChunkPosition))
|
||||
else -> state.block.getCollisionShape(world.session, context, position, state, null)
|
||||
} ?: continue
|
||||
|
||||
if (position in aabb && shape.intersects(aabb, -position)) {
|
||||
continue
|
||||
}
|
||||
positions[index] = position.raw
|
||||
shapes[index] = shape
|
||||
index++
|
||||
total += shape.aabbs
|
||||
}
|
||||
this.shapes = shapes.cast()
|
||||
this.total = index
|
||||
this.positions = positions
|
||||
this.aabbs = total
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<AABB> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun intersects(other: AABB): Boolean {
|
||||
for (index in 0 until total) {
|
||||
val position = BlockPosition(this.positions[index])
|
||||
val shape = this.shapes[index]
|
||||
|
||||
if (shape.intersects(other, -position)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun calculateMaxDistance(other: AABB, maxDistance: Double, axis: Axes): Double {
|
||||
var distance = maxDistance
|
||||
|
||||
for (index in 0 until total) {
|
||||
val position = BlockPosition(this.positions[index])
|
||||
val shape = this.shapes[index]
|
||||
|
||||
distance = shape.calculateMaxDistance(other, -position, distance, axis)
|
||||
}
|
||||
|
||||
return distance
|
||||
}
|
||||
|
||||
fun free() {
|
||||
POSITIONS.free(this.positions)
|
||||
SHAPES.free(this.shapes.unsafeCast())
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val POSITIONS = LongAllocator()
|
||||
private val SHAPES = object : TemporaryAllocator<Array<AbstractVoxelShape?>>() {
|
||||
override fun getSize(value: Array<AbstractVoxelShape?>) = value.size
|
||||
override fun create(size: Int): Array<AbstractVoxelShape?> = arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ package de.bixilon.minosoft.data.registries.shapes.voxel
|
||||
import de.bixilon.kotlinglm.vec3.Vec3
|
||||
import de.bixilon.kotlinglm.vec3.Vec3d
|
||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||
import de.bixilon.kotlinglm.vec3.Vec3t
|
||||
import de.bixilon.kutil.primitive.IntUtil.toInt
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.data.registries.shapes.ShapeRegistry
|
||||
@ -30,14 +29,20 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.max
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.min
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||
|
||||
abstract class AbstractVoxelShape : Iterable<AABB> {
|
||||
abstract val aabbs: Int
|
||||
interface AbstractVoxelShape : Iterable<AABB> {
|
||||
val aabbs: Int
|
||||
|
||||
fun intersect(other: AABB): Boolean {
|
||||
fun intersects(other: AABB): Boolean {
|
||||
for (aabb in this) {
|
||||
if (!aabb.intersect(other)) {
|
||||
continue
|
||||
}
|
||||
if (!aabb.intersects(other)) continue
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun intersects(other: AABB, offset: BlockPosition): Boolean {
|
||||
for (aabb in this) {
|
||||
if (!aabb.intersects(other, offset)) continue
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -51,18 +56,14 @@ abstract class AbstractVoxelShape : Iterable<AABB> {
|
||||
return VoxelShape(result)
|
||||
}
|
||||
|
||||
operator fun plus(offset: Vec3t<out Number>) = modify { it + offset }
|
||||
operator fun plus(offset: Vec3d) = modify { it + offset }
|
||||
operator fun plus(offset: Vec3) = modify { it + offset }
|
||||
operator fun plus(offset: Vec3i) = modify { it + offset }
|
||||
|
||||
@JvmName("plusBlockPosition")
|
||||
operator fun plus(offset: BlockPosition) = modify { it + offset }
|
||||
|
||||
@JvmName("plusInChunkPosition")
|
||||
operator fun plus(offset: InChunkPosition) = modify { it + offset }
|
||||
|
||||
@JvmName("plusInSectionPosition")
|
||||
operator fun plus(offset: InSectionPosition) = modify { it + offset }
|
||||
|
||||
fun add(other: AbstractVoxelShape): AbstractVoxelShape {
|
||||
@ -75,7 +76,15 @@ abstract class AbstractVoxelShape : Iterable<AABB> {
|
||||
fun calculateMaxDistance(other: AABB, maxDistance: Double, axis: Axes): Double {
|
||||
var distance = maxDistance
|
||||
for (aabb in this) {
|
||||
distance = aabb.calculateMaxOffset(other, distance, axis)
|
||||
distance = aabb.calculateMaxDistance(other, distance, axis)
|
||||
}
|
||||
return distance
|
||||
}
|
||||
|
||||
fun calculateMaxDistance(other: AABB, offset: BlockPosition, maxDistance: Double, axis: Axes): Double {
|
||||
var distance = maxDistance
|
||||
for (aabb in this) {
|
||||
distance = aabb.calculateMaxDistance(other, offset, distance, axis)
|
||||
}
|
||||
return distance
|
||||
}
|
||||
@ -110,7 +119,6 @@ abstract class AbstractVoxelShape : Iterable<AABB> {
|
||||
return shouldDrawLine(start.toVec3d, end.toVec3d)
|
||||
}
|
||||
|
||||
|
||||
fun getMax(axis: Axes): Double {
|
||||
if (aabbs == 0) return Double.NaN
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2023 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.registries.shapes.voxel
|
||||
|
||||
import de.bixilon.minosoft.data.registries.shapes.aabb.AABB
|
||||
|
||||
class MutableVoxelShape(
|
||||
val aabb: MutableSet<AABB>,
|
||||
) : AbstractVoxelShape() {
|
||||
override val aabbs: Int = aabb.size
|
||||
|
||||
constructor(vararg aabbs: AABB) : this(aabbs.toMutableSet())
|
||||
constructor(shape: AbstractVoxelShape) : this(shape.toMutableSet())
|
||||
|
||||
operator fun plusAssign(aabb: AABB) {
|
||||
this.aabb += aabb
|
||||
}
|
||||
|
||||
operator fun plusAssign(shape: AbstractVoxelShape) {
|
||||
this.aabb += shape
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<AABB> {
|
||||
return aabb.iterator()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
if (aabbs == 0) {
|
||||
return "VoxelShape{EMPTY}"
|
||||
}
|
||||
return "VoxelShape{$aabb}"
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return aabb.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null && aabbs == 0) return true
|
||||
if (other !is AbstractVoxelShape) return false
|
||||
if (other is VoxelShape) return aabb == other.aabb
|
||||
if (other is MutableVoxelShape) return aabb == other.aabb
|
||||
TODO("Can not compare $this with $other")
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2023 Moritz Zwerger
|
||||
* Copyright (C) 2020-2025 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
@ -19,7 +19,7 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||
|
||||
class VoxelShape(
|
||||
val aabb: Collection<AABB>,
|
||||
) : AbstractVoxelShape() {
|
||||
) : AbstractVoxelShape {
|
||||
override val aabbs: Int = aabb.size
|
||||
|
||||
constructor(vararg aabbs: AABB) : this(ObjectOpenHashSet(aabbs))
|
||||
@ -47,7 +47,6 @@ class VoxelShape(
|
||||
if (other == null && aabbs == 0) return true
|
||||
if (other !is AbstractVoxelShape) return false
|
||||
if (other is VoxelShape) return aabb == other.aabb
|
||||
if (other is MutableVoxelShape) return aabb == other.aabb
|
||||
TODO("Can not compare $this with $other")
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ class WorldEntities : Iterable<Entity> {
|
||||
}
|
||||
val aabb = entity.physics.aabb
|
||||
|
||||
if (shape.intersect(aabb)) {
|
||||
if (shape.intersects(aabb)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ class WorldIterator(
|
||||
if (this.chunk !== chunk) {
|
||||
this.chunk = chunk
|
||||
}
|
||||
// TODO: some fast skip? (if section is empty, can not be in section or chunk is null)
|
||||
|
||||
val state = chunk[position.inChunkPosition] ?: continue
|
||||
|
||||
@ -116,7 +117,7 @@ class WorldIterator(
|
||||
if (predicate != null && !predicate.invoke(state)) continue
|
||||
|
||||
val shape = state.block.getCollisionShape(world.session, context, position, state, null) ?: continue
|
||||
if ((shape + position).intersect(aabb)) {
|
||||
if ((shape + position).intersects(aabb)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
package de.bixilon.minosoft.data.world.positions
|
||||
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.data.direction.Directions
|
||||
import de.bixilon.minosoft.data.text.formatting.TextFormattable
|
||||
import de.bixilon.minosoft.data.world.positions.BlockPositionUtil.assertPosition
|
||||
@ -102,6 +103,12 @@ value class BlockPosition(
|
||||
return modifyZ(-Z * 1)
|
||||
}
|
||||
|
||||
operator fun get(axis: Axes) = when (axis) {
|
||||
Axes.X -> x
|
||||
Axes.Y -> y
|
||||
Axes.Z -> z
|
||||
}
|
||||
|
||||
inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = BlockPosition(x, y, z)
|
||||
|
||||
inline operator fun plus(value: Int) = BlockPosition(this.x + value, this.y + value, this.z + value)
|
||||
|
@ -44,7 +44,7 @@ class WallOverlay(context: RenderContext) : SimpleOverlay(context) {
|
||||
}
|
||||
val camera = context.session.camera.entity
|
||||
val shape = blockState.block.getCollisionShape(context.session, EntityCollisionContext(camera), position, blockState, null) ?: return false // TODO: block entity
|
||||
if (!shape.intersect(player.physics.aabb)) {
|
||||
if (!shape.intersects(player.physics.aabb)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -20,6 +20,7 @@ import de.bixilon.minosoft.data.physics.PhysicsEntity
|
||||
import de.bixilon.minosoft.data.registries.blocks.shapes.collision.context.ParticleCollisionContext
|
||||
import de.bixilon.minosoft.data.registries.particle.data.ParticleData
|
||||
import de.bixilon.minosoft.data.registries.shapes.aabb.AABB
|
||||
import de.bixilon.minosoft.data.registries.shapes.collision.CollisionShape
|
||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||
import de.bixilon.minosoft.gui.rendering.particle.ParticleFactory
|
||||
import de.bixilon.minosoft.gui.rendering.particle.ParticleMesh
|
||||
@ -28,7 +29,6 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.EMPTY
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.blockPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.interpolateLinear
|
||||
import de.bixilon.minosoft.physics.parts.CollisionMovementPhysics.collectCollisions
|
||||
import de.bixilon.minosoft.physics.parts.CollisionMovementPhysics.collide
|
||||
import de.bixilon.minosoft.protocol.network.session.play.PlaySession
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
@ -138,7 +138,7 @@ abstract class Particle(
|
||||
private fun collide(movement: Vec3d): Vec3d {
|
||||
val aabb = aabb + movement
|
||||
val context = ParticleCollisionContext(this)
|
||||
val collisions = session.world.collectCollisions(context, movement, aabb, getChunk())
|
||||
val collisions = CollisionShape(session.world, context, aabb, movement, getChunk())
|
||||
val adjusted = collide(movement, aabb, collisions)
|
||||
if (adjusted.y != movement.y) {
|
||||
onGround = true
|
||||
|
@ -108,7 +108,9 @@ class LocalPlayerPhysics(entity: LocalPlayerEntity) : PlayerPhysics<LocalPlayerE
|
||||
val aabb = aabb
|
||||
val offset = AABB(position.x + 0.0, aabb.min.y, position.z + 0.0, position.x + 1.0, aabb.max.y, position.z + 1.0).shrink(1.0E-7)
|
||||
val collisions = collectCollisions(Vec3d.EMPTY, offset, predicate = predicate)
|
||||
return collisions.intersect(aabb)
|
||||
val intersects = collisions.intersects(aabb)
|
||||
collisions.free()
|
||||
return intersects
|
||||
}
|
||||
|
||||
private fun shouldSprint(): Boolean {
|
||||
|
@ -17,59 +17,19 @@ import de.bixilon.kotlinglm.vec3.Vec3d
|
||||
import de.bixilon.kotlinglm.vec3.swizzle.xz
|
||||
import de.bixilon.minosoft.data.Axes
|
||||
import de.bixilon.minosoft.data.registries.blocks.shapes.collision.CollisionPredicate
|
||||
import de.bixilon.minosoft.data.registries.blocks.shapes.collision.context.CollisionContext
|
||||
import de.bixilon.minosoft.data.registries.blocks.shapes.collision.context.EntityCollisionContext
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.entity.BlockWithEntity
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.properties.shape.collision.CollidableBlock
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.properties.shape.collision.fixed.FixedCollidable
|
||||
import de.bixilon.minosoft.data.registries.shapes.aabb.AABB
|
||||
import de.bixilon.minosoft.data.registries.shapes.collision.CollisionShape
|
||||
import de.bixilon.minosoft.data.registries.shapes.voxel.AbstractVoxelShape
|
||||
import de.bixilon.minosoft.data.registries.shapes.voxel.VoxelShape
|
||||
import de.bixilon.minosoft.data.world.World
|
||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||
import de.bixilon.minosoft.data.world.iterator.WorldIterator
|
||||
import de.bixilon.minosoft.data.world.positions.BlockPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.EMPTY
|
||||
import de.bixilon.minosoft.physics.entities.EntityPhysics
|
||||
import kotlin.math.abs
|
||||
|
||||
object CollisionMovementPhysics {
|
||||
|
||||
fun World.collectCollisions(context: CollisionContext, movement: Vec3d, aabb: AABB, chunk: Chunk?, predicate: CollisionPredicate? = null): VoxelShape {
|
||||
val shapes: ArrayList<AABB> = ArrayList()
|
||||
// TODO: add entity collisions (boat, shulker)
|
||||
// TODO: add world border collision shape
|
||||
|
||||
|
||||
for ((position, state, chunk) in WorldIterator(aabb.extend(movement).grow(1.0).positions(), this, chunk)) {
|
||||
if (state.block !is CollidableBlock) continue
|
||||
|
||||
if (predicate != null && !predicate.invoke(state)) continue
|
||||
// TODO: filter blocks (e.g. moving piston), whatever that means
|
||||
|
||||
var shape = when (state.block) {
|
||||
is FixedCollidable -> state.block.getCollisionShape(state)
|
||||
is BlockWithEntity<*> -> {
|
||||
|
||||
state.block.getCollisionShape(session, context, position, state, chunk.getBlockEntity(position.inChunkPosition))
|
||||
}
|
||||
|
||||
else -> {
|
||||
state.block.getCollisionShape(session, context, position, state, null)
|
||||
}
|
||||
} ?: continue
|
||||
shape += position
|
||||
|
||||
if (position in aabb && shape.intersect(aabb)) {
|
||||
continue
|
||||
}
|
||||
shapes += shape
|
||||
}
|
||||
|
||||
return VoxelShape(shapes)
|
||||
}
|
||||
|
||||
fun EntityPhysics<*>.collectCollisions(movement: Vec3d, aabb: AABB, predicate: CollisionPredicate? = null): VoxelShape {
|
||||
return this.entity.session.world.collectCollisions(EntityCollisionContext(entity, this, aabb), movement, aabb, positionInfo.chunk, predicate)
|
||||
fun EntityPhysics<*>.collectCollisions(movement: Vec3d, aabb: AABB, predicate: CollisionPredicate? = null): CollisionShape {
|
||||
return CollisionShape(this.entity.session.world, EntityCollisionContext(entity, this, aabb), aabb, movement, positionInfo.chunk, predicate)
|
||||
}
|
||||
|
||||
private fun checkMovement(axis: Axes, originalValue: Double, offsetAABB: Boolean, aabb: AABB, collisions: AbstractVoxelShape): Double {
|
||||
@ -114,13 +74,19 @@ object CollisionMovementPhysics {
|
||||
|
||||
fun EntityPhysics<*>.collide(movement: Vec3d): Vec3d {
|
||||
val aabb = aabb
|
||||
val collisions = collectCollisions(movement, aabb)
|
||||
val collision = collide(movement, aabb, collisions)
|
||||
if (stepHeight <= 0.0) {
|
||||
return collision
|
||||
}
|
||||
if (aabb.min.y <= BlockPosition.MIN_Y || aabb.max.y >= BlockPosition.MAX_Y) return movement // TODO: also check movement
|
||||
|
||||
return collideStepping(movement, collision, collisions)
|
||||
val collisions = collectCollisions(movement, aabb)
|
||||
try {
|
||||
val collision = collide(movement, aabb, collisions)
|
||||
if (stepHeight <= 0.0) {
|
||||
return collision
|
||||
}
|
||||
|
||||
return collideStepping(movement, collision, collisions)
|
||||
} finally {
|
||||
collisions.free()
|
||||
}
|
||||
}
|
||||
|
||||
private fun EntityPhysics<*>.collideStepping(movement: Vec3d, collision: Vec3d, collisions: AbstractVoxelShape): Vec3d {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2023 Moritz Zwerger
|
||||
* Copyright (C) 2020-2025 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
@ -33,7 +33,7 @@ internal class AABBTest {
|
||||
val a = AABB(Vec3d(5.0, 0.0, 7.0), Vec3d(6.0, 1.0, 8.0))
|
||||
val b = AABB(Vec3d(5.7, 1.0, 6.3), Vec3d(6.3, 3, 6.9))
|
||||
|
||||
assertEquals(1239312.0, a.calculateMaxOffset(b, 1239312.0, Axes.Z))
|
||||
assertEquals(1239312.0, a.calculateMaxDistance(b, 1239312.0, Axes.Z))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -41,7 +41,7 @@ internal class AABBTest {
|
||||
val a = AABB(Vec3d(5.0, 0.0, 7.0), Vec3d(6.0, 1.0, 8.0))
|
||||
val b = AABB(Vec3d(5.699999988079071, 0.5358406250445555, 6.373910529638632), Vec3d(6.300000011920929, 2.3358405773608397, 6.97391055348049))
|
||||
|
||||
assertNotEquals(0.1, a.calculateMaxOffset(b, 0.1, Axes.Z))
|
||||
assertNotEquals(0.1, a.calculateMaxDistance(b, 0.1, Axes.Z))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -49,7 +49,7 @@ internal class AABBTest {
|
||||
val a = AABB(Vec3d(5.0, 0.0, 6.0), Vec3d(5.8, 1.0, 7.0))
|
||||
val b = AABB(Vec3d(5.7, 1.0, 6.0), Vec3d(6.3, 2.8, 6.6))
|
||||
|
||||
assertEquals(0.0, a.calculateMaxOffset(b, -0.0784000015258789, Axes.Y))
|
||||
assertEquals(0.0, a.calculateMaxDistance(b, -0.0784000015258789, Axes.Y))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -57,7 +57,7 @@ internal class AABBTest {
|
||||
val a = AABB(Vec3d(5.0, 0.0, 6.0), Vec3d(5.8, 1.0, 7.0))
|
||||
val b = AABB(Vec3d(5.0, 1.0, 6.0), Vec3d(5.8, 2.8, 6.6))
|
||||
|
||||
assertEquals(0.0, a.calculateMaxOffset(b, -0.0784000015258789, Axes.Y))
|
||||
assertEquals(0.0, a.calculateMaxDistance(b, -0.0784000015258789, Axes.Y))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -65,7 +65,7 @@ internal class AABBTest {
|
||||
val a = AABB(Vec3d(5.0, 0.0, 6.0), Vec3d(5.8, 1.0, 7.0))
|
||||
val b = AABB(Vec3d(5.1, 1.0, 5.9), Vec3d(5.5, 2.8, 7.1))
|
||||
|
||||
assertEquals(0.0, a.calculateMaxOffset(b, -0.0784000015258789, Axes.Y))
|
||||
assertEquals(0.0, a.calculateMaxDistance(b, -0.0784000015258789, Axes.Y))
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user