From 357d40af4c6ef1d3dba578893a84edbc60f4a69e Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 15 Sep 2021 23:30:15 +0200 Subject: [PATCH] improve particle, physics performance --- .../data/physics/CollisionDetector.kt | 31 +++++++----- .../bixilon/minosoft/data/registries/AABB.kt | 49 +++++++++++-------- .../minosoft/data/registries/VoxelShape.kt | 11 +++-- .../gui/rendering/particle/ParticleMesh.kt | 2 +- .../rendering/particle/ParticleRenderer.kt | 34 ++++++++----- 5 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/data/physics/CollisionDetector.kt b/src/main/java/de/bixilon/minosoft/data/physics/CollisionDetector.kt index 2a5780a9d..77b82bde3 100644 --- a/src/main/java/de/bixilon/minosoft/data/physics/CollisionDetector.kt +++ b/src/main/java/de/bixilon/minosoft/data/physics/CollisionDetector.kt @@ -14,13 +14,16 @@ package de.bixilon.minosoft.data.physics import de.bixilon.minosoft.data.Axes +import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.player.LocalPlayerEntity import de.bixilon.minosoft.data.registries.AABB import de.bixilon.minosoft.data.registries.VoxelShape +import de.bixilon.minosoft.data.world.Chunk import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkPosition import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection +import glm_.vec2.Vec2i import glm_.vec3.Vec3 import glm_.vec3.Vec3bool import glm_.vec3.Vec3d @@ -30,26 +33,32 @@ class CollisionDetector(val connection: PlayConnection) { private fun getCollisionsToCheck(deltaPosition: Vec3d, aabb: AABB, ignoreUnloadedChunks: Boolean = true): VoxelShape { // also look at blocks further down to also cover blocks with a higher than normal hitbox (for example fences) - val blockPositions = ((aabb extend deltaPosition).grow(1.0 + COLLISION_BOX_MARGIN)).blockPositions.toMutableList() + val blockPositions = aabb.offset(deltaPosition).grow(COLLISION_BOX_MARGIN).extend(Directions.DOWN).blockPositions val aabbBlockPositions = aabb.shrink().blockPositions val result = VoxelShape() + var chunk: Chunk? = null + var lastChunkPosition: Vec2i? = null for (blockPosition in blockPositions) { - val chunk = connection.world[blockPosition.chunkPosition] - if ((chunk == null || !chunk.isFullyLoaded) && !ignoreUnloadedChunks) { + val chunkPosition = blockPosition.chunkPosition + if (lastChunkPosition != chunkPosition) { + lastChunkPosition = chunkPosition + chunk = connection.world[blockPosition.chunkPosition] + } + if (chunk == null || !chunk.isFullyLoaded) { // chunk is not loaded - result.add(VoxelShape.FULL + blockPosition) + if (!ignoreUnloadedChunks) { + result += AABB.BLOCK + blockPosition + } continue } - val blockState = chunk?.get(blockPosition.inChunkPosition) ?: continue + val blockState = chunk[blockPosition.inChunkPosition] ?: continue val blockShape = blockState.collisionShape + blockPosition // remove position if not already in it and not colliding with it - if (blockPosition in aabbBlockPositions) { - if (blockShape.intersect(aabb)) { - continue - } + if (blockPosition in aabbBlockPositions && blockShape.intersect(aabb)) { + continue } result.add(blockShape) } @@ -112,7 +121,7 @@ class CollisionDetector(val connection: PlayConnection) { if (stepping && onGround && (blocked.x || blocked.z)) { var stepMovement = adjustMovementForCollisions(Vec3d(movement.x, PhysicsConstants.STEP_HEIGHT, movement.z), aabb) - val verticalStepMovement = adjustMovementForCollisions(Vec3d(0.0, PhysicsConstants.STEP_HEIGHT, 0.0), aabb extend Vec3d(movement.x, 0.0, movement.z)) + val verticalStepMovement = adjustMovementForCollisions(Vec3d(0.0, PhysicsConstants.STEP_HEIGHT, 0.0), aabb.extend(Vec3d(movement.x, 0.0, movement.z))) if (verticalStepMovement.y < PhysicsConstants.STEP_HEIGHT) { val horizontalStepMovement = adjustMovementForCollisions(Vec3d(movement.x, 0.0, movement.z), aabb + verticalStepMovement) + verticalStepMovement @@ -124,8 +133,8 @@ class CollisionDetector(val connection: PlayConnection) { if (stepMovement.length() > collisionMovement.length()) { returnMovement = stepMovement + adjustMovementForCollisions(Vec3d(0.0, -stepMovement.y + movement.y, 0.0), aabb + stepMovement) } + checkBlocked() } - checkBlocked() physicsEntity?.let { if (blocked.x) { it.velocity.x = 0.0 diff --git a/src/main/java/de/bixilon/minosoft/data/registries/AABB.kt b/src/main/java/de/bixilon/minosoft/data/registries/AABB.kt index fb4e6cc0a..c6cbe3894 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/AABB.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/AABB.kt @@ -29,12 +29,9 @@ import glm_.vec3.Vec3i import glm_.vec3.Vec3t -class AABB( - min: Vec3d, - max: Vec3d, -) { - val min = Vec3d(glm.min(min.x, max.x), glm.min(min.y, max.y), glm.min(min.z, max.z)) - val max = Vec3d(glm.max(min.x, max.x), glm.max(min.y, max.y), glm.max(min.z, max.z)) +class AABB { + val min: Vec3d + val max: Vec3d constructor(jsonData: JsonObject) : this(jsonData["from"].toVec3(Vec3.EMPTY), jsonData["to"].toVec3(Vec3.ONE)) @@ -44,19 +41,30 @@ class AABB( constructor(min: Vec3, max: Vec3) : this(Vec3d(min), Vec3d(max)) + constructor(min: Vec3d, max: Vec3d) { + this.min = Vec3d(glm.min(min.x, max.x), glm.min(min.y, max.y), glm.min(min.z, max.z)) + this.max = Vec3d(glm.max(min.x, max.x), glm.max(min.y, max.y), glm.max(min.z, max.z)) + } + + private constructor(unsafe: Boolean, min: Vec3d, max: Vec3d) { + this.min = min + this.max = max + } 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) } - operator fun plus(other: Vec3t): AABB { - return AABB(min + other, max + other) + operator fun plus(other: Vec3t): AABB = offset(other) + + fun offset(other: Vec3t): AABB { + return AABB(true, min + other, max + other) } 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)) - return AABB(newMin, newMax) + val newMin = Vec3d(glm.min(min.x, other.min.x), glm.min(min.y, other.min.y), glm.min(min.z, other.min.z)) + val newMax = Vec3d(glm.max(max.x, other.max.x), glm.max(max.y, other.max.y), glm.max(max.z, other.max.z)) + return AABB(true, newMin, newMax) } val blockPositions: List @@ -84,7 +92,8 @@ class AABB( return axis.choose(max) } - infix fun extend(vec3: Vec3d): AABB { + + fun extend(vec3: Vec3d): AABB { val newMin = Vec3d(min) val newMax = Vec3d(max) @@ -106,22 +115,22 @@ class AABB( newMax.z += vec3.z } - return AABB(newMin, newMax) + return AABB(true, newMin, newMax) } - infix fun extend(vec3i: Vec3i): AABB { - return this extend Vec3d(vec3i) + fun extend(vec3i: Vec3i): AABB { + return this.extend(Vec3d(vec3i)) } - infix fun extend(direction: Directions): AABB { - return this extend direction.vector + fun extend(direction: Directions): AABB { + return this.extend(direction.vectord) } fun grow(size: Double = 1.0E-7): AABB { return AABB(min - size, max + size) } - infix fun grow(size: Float): AABB { + fun grow(size: Float): AABB { return AABB(min - size, max + size) } @@ -230,8 +239,8 @@ class AABB( } companion object { - val EMPTY: AABB - get() = AABB(Vec3.EMPTY, Vec3.EMPTY) + val BLOCK: AABB = AABB(Vec3.EMPTY, Vec3.ONE) + val EMPTY: AABB = AABB(Vec3.EMPTY, Vec3.EMPTY) private fun getRange(min: Double, max: Double): IntRange { return IntRange(min.floor, max.ceil - 1) diff --git a/src/main/java/de/bixilon/minosoft/data/registries/VoxelShape.kt b/src/main/java/de/bixilon/minosoft/data/registries/VoxelShape.kt index 87c2d4ee2..29e46bce9 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/VoxelShape.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/VoxelShape.kt @@ -17,11 +17,8 @@ import com.google.gson.JsonArray import com.google.gson.JsonPrimitive import de.bixilon.minosoft.data.Axes import de.bixilon.minosoft.data.direction.Directions -import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY -import de.bixilon.minosoft.gui.rendering.util.VecUtil.ONE import de.bixilon.minosoft.gui.rendering.util.VecUtil.getMinDistanceDirection import de.bixilon.minosoft.util.KUtil.toInt -import glm_.vec3.Vec3 import glm_.vec3.Vec3d import glm_.vec3.Vec3t @@ -88,6 +85,12 @@ class VoxelShape(private val aabbs: MutableList = mutableListOf()) : Itera } } + operator fun plusAssign(voxelShape: VoxelShape) = add(voxelShape) + + operator fun plusAssign(aabb: AABB) { + aabbs += aabb + } + fun remove(voxelShape: VoxelShape) { for (newAABB in voxelShape.aabbs) { aabbs.remove(newAABB) @@ -122,7 +125,7 @@ class VoxelShape(private val aabbs: MutableList = mutableListOf()) : Itera companion object { val EMPTY = VoxelShape() - val FULL = VoxelShape(mutableListOf(AABB(Vec3.EMPTY, Vec3.ONE))) + val FULL = VoxelShape(mutableListOf(AABB.BLOCK)) } override fun iterator(): Iterator { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt index 1529bd8f3..b167d6749 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleMesh.kt @@ -24,7 +24,7 @@ import glm_.vec2.Vec2 import glm_.vec3.Vec3 import glm_.vec3.Vec3d -class ParticleMesh(renderWindow: RenderWindow) : Mesh(renderWindow, ParticleMeshStruct, PrimitiveTypes.POINT) { +class ParticleMesh(renderWindow: RenderWindow, initialCacheSize: Int) : Mesh(renderWindow, ParticleMeshStruct, PrimitiveTypes.POINT, initialCacheSize) { fun addVertex(position: Vec3d, scale: Float, texture: AbstractTexture, tintColor: RGBColor, uvMin: Vec2 = Vec2(0.0f, 0.0f), uvMax: Vec2 = Vec2(1.0f, 1.0f)) { val textureLayer = if (RenderConstants.FORCE_DEBUG_TEXTURE) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt index 947327d5b..89dd4fa4c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt @@ -24,8 +24,6 @@ import de.bixilon.minosoft.gui.rendering.system.base.shader.Shader import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition -import de.bixilon.minosoft.util.KUtil.synchronizedSetOf -import de.bixilon.minosoft.util.KUtil.toSynchronizedSet import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 @@ -35,13 +33,14 @@ class ParticleRenderer( val renderWindow: RenderWindow, ) : Renderer { private val particleShader: Shader = renderWindow.renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "particle")) - private var particleMesh = ParticleMesh(renderWindow) - private var transparentParticleMesh = ParticleMesh(renderWindow) + private var particleMesh = ParticleMesh(renderWindow, 0) + private var transparentParticleMesh = ParticleMesh(renderWindow, 0) - private var particles: MutableSet = synchronizedSetOf() + private var particles: MutableSet = mutableSetOf() + private var particleQueue: MutableSet = mutableSetOf() val size: Int - get() = particles.size + get() = particles.size + particleQueue.size override fun init() { connection.registerEvent(CallbackEventInvoker.of { @@ -71,8 +70,11 @@ class ParticleRenderer( } fun add(particle: Particle) { - check(particles.size < RenderConstants.MAXIMUM_PARTICLE_AMOUNT) { "Can not add particle: Limit reached (${particles.size} > ${RenderConstants.MAXIMUM_PARTICLE_AMOUNT}" } - particles += particle + val particleCount = particles.size + particleQueue.size + check(particleCount < RenderConstants.MAXIMUM_PARTICLE_AMOUNT) { "Can not add particle: Limit reached (${particleCount} > ${RenderConstants.MAXIMUM_PARTICLE_AMOUNT}" } + synchronized(particleQueue) { + particleQueue += particle + } } operator fun plusAssign(particle: Particle) { @@ -82,18 +84,26 @@ class ParticleRenderer( override fun update() { particleMesh.unload() transparentParticleMesh.unload() - particleMesh = ParticleMesh(renderWindow) - transparentParticleMesh = ParticleMesh(renderWindow) + particleMesh = ParticleMesh(renderWindow, particles.size) + transparentParticleMesh = ParticleMesh(renderWindow, 500) + + val toRemove: MutableSet = mutableSetOf() - for (particle in particles.toSynchronizedSet()) { + synchronized(particleQueue) { + particles += particleQueue + particleQueue.clear() + } + + for (particle in particles) { particle.tryTick() if (particle.dead) { - this.particles -= particle + toRemove += particle continue } particle.addVertex(transparentParticleMesh, particleMesh) } + particles -= toRemove particleMesh.load() transparentParticleMesh.load()