improve particle, physics performance

This commit is contained in:
Bixilon 2021-09-15 23:30:15 +02:00
parent 8fe880caec
commit 357d40af4c
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 79 additions and 48 deletions

View File

@ -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

View File

@ -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<out Number>): AABB {
return AABB(min + other, max + other)
operator fun plus(other: Vec3t<out Number>): AABB = offset(other)
fun offset(other: Vec3t<out Number>): 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<Vec3i>
@ -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)

View File

@ -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<AABB> = 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<AABB> = 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<AABB> {

View File

@ -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) {

View File

@ -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<Particle> = synchronizedSetOf()
private var particles: MutableSet<Particle> = mutableSetOf()
private var particleQueue: MutableSet<Particle> = mutableSetOf()
val size: Int
get() = particles.size
get() = particles.size + particleQueue.size
override fun init() {
connection.registerEvent(CallbackEventInvoker.of<CameraMatrixChangeEvent> {
@ -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<Particle> = 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()