mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 18:34:56 -04:00
rendering: WIP: chunk culling
This commit is contained in:
parent
c67965f40b
commit
f30d8feb4b
@ -13,10 +13,11 @@
|
||||
package de.bixilon.minosoft.data.world
|
||||
|
||||
import de.bixilon.minosoft.data.Directions
|
||||
import de.bixilon.minosoft.gui.rendering.Camera
|
||||
import glm_.Java.Companion.glm
|
||||
import glm_.vec2.Vec2
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Chunk X and Z location (block position / 16, rounded down)
|
||||
*/
|
||||
data class ChunkLocation(val x: Int, val z: Int) {
|
||||
|
||||
override fun toString(): String {
|
||||
@ -32,4 +33,76 @@ data class ChunkLocation(val x: Int, val z: Int) {
|
||||
else -> throw IllegalArgumentException("Chunk location is just 2d")
|
||||
}
|
||||
}
|
||||
|
||||
fun isVisibleFrom(camera: Camera): Boolean {
|
||||
val from = Vec2(x * 16, z * 16)
|
||||
val to = from + Vec2(16, 16)
|
||||
val frustrum: Frustrum
|
||||
// val origin = Vec2(camera.cameraPosition.x, camera.cameraPosition.z)
|
||||
// if (isInCone(from, origin, camera.yaw, camera.fov)) {
|
||||
// return true
|
||||
// }
|
||||
// if (isInCone(to, origin, camera.yaw, camera.fov)) {
|
||||
// return true
|
||||
// }
|
||||
// if (intersectsQuad(from, to, origin, -glm.radians(camera.yaw + camera.fov / 2))) {
|
||||
// return true
|
||||
// }
|
||||
// if (intersectsQuad(from, to, origin, -glm.radians(camera.yaw - camera.fov / 2))) {
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
}
|
||||
|
||||
private fun isInCone(point: Vec2, origin: Vec2, yaw: Double, fov: Float): Boolean {
|
||||
val difference = (point - origin).normalize()
|
||||
val angle = Math.toDegrees(glm.asin(difference.y).toDouble())
|
||||
val realYaw = if (yaw > 0) {
|
||||
yaw
|
||||
} else {
|
||||
yaw + 360
|
||||
}
|
||||
val realAngle = if (angle > 0) {
|
||||
angle
|
||||
} else {
|
||||
angle + 180
|
||||
}
|
||||
return abs(angle) < fov
|
||||
}
|
||||
|
||||
private fun intersectsQuad(from: Vec2, to: Vec2, origin: Vec2, angle: Double): Boolean {
|
||||
val direction = Vec2(glm.cos(angle), glm.sin(angle))
|
||||
if (intersect(origin, direction, from, Vec2(from.x, to.y))) {
|
||||
return true
|
||||
}
|
||||
if (intersect(origin, direction, from, Vec2(to.x, from.y))) {
|
||||
return true
|
||||
}
|
||||
if (intersect(origin, direction, to, Vec2(from.x, to.y))) {
|
||||
return true
|
||||
}
|
||||
if (intersect(origin, direction, to, Vec2(to.x, from.y))) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun intersectLines(v1: Vec2, v2: Vec2, v3: Vec2, v4: Vec2): Vec2 {
|
||||
// formula from https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
|
||||
val d = (v1.x - v2.x) * (v3.y - v4.y) - (v1.y - v2.y) * (v3.x - v4.x)
|
||||
val x = (v1.x * v2.y - v1.y * v2.x) * (v3.x - v4.x) - (v1.x - v2.x) * (v3.x * v4.y - v3.y * v4.x)
|
||||
val y = (v1.x * v2.y - v1.y * v2.x) * (v4.x - v3.x) - (v1.y - v2.y) * (v3.x * v4.y - v3.y * v4.x)
|
||||
return Vec2(x / d, y / d)
|
||||
}
|
||||
|
||||
private fun intersect(origin: Vec2, direction: Vec2, p1: Vec2, p2: Vec2): Boolean {
|
||||
val normal = Vec2(direction.yx)
|
||||
val first = dotProduct(normal, p1-origin)
|
||||
val second = dotProduct(normal, p2-origin)
|
||||
return (first.toBits() and 0x80000000.toInt() != second.toBits() and 0x80000000.toInt())
|
||||
}
|
||||
|
||||
private fun dotProduct(v1: Vec2, v2: Vec2): Float {
|
||||
return v1.x * v2.x + v1.y * v2.y
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import glm_.vec3.Vec3
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class Camera(private val connection: Connection, private var fov: Float) {
|
||||
class Camera(private val connection: Connection, var fov: Float) {
|
||||
private var mouseSensitivity = Minosoft.getConfig().config.game.camera.moseSensitivity
|
||||
private var movementSpeed = 7
|
||||
var cameraPosition = Vec3(0.0f, 0.0f, 0.0f)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
* Copyright (C) 2020 Moritz Zwerger, Lukas Eisenhauer
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
@ -20,7 +20,7 @@ import de.bixilon.minosoft.config.key.KeyAction
|
||||
import de.bixilon.minosoft.config.key.KeyBinding
|
||||
import de.bixilon.minosoft.config.key.KeyCodes
|
||||
import de.bixilon.minosoft.data.mappings.ModIdentifier
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.chunk.WorldRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.hud.elements.RenderStats
|
||||
import de.bixilon.minosoft.modding.event.EventInvokerCallback
|
||||
@ -59,7 +59,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
||||
private var mouseCatch = !StaticConfiguration.DEBUG_MODE
|
||||
|
||||
// all renderers
|
||||
val chunkRenderer: ChunkRenderer = ChunkRenderer(connection, connection.player.world, this)
|
||||
val worldRenderer: WorldRenderer = WorldRenderer(connection, connection.player.world, this)
|
||||
val hudRenderer: HUDRenderer = HUDRenderer(connection, this)
|
||||
|
||||
val renderQueue = ConcurrentLinkedQueue<Runnable>()
|
||||
@ -205,7 +205,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
||||
// Make the OpenGL context current
|
||||
glfwMakeContextCurrent(windowId)
|
||||
// Enable v-sync
|
||||
glfwSwapInterval(1)
|
||||
glfwSwapInterval(0)
|
||||
|
||||
|
||||
// Make the window visible
|
||||
@ -216,10 +216,10 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
|
||||
chunkRenderer.init()
|
||||
worldRenderer.init()
|
||||
hudRenderer.init()
|
||||
|
||||
chunkRenderer.postInit()
|
||||
worldRenderer.postInit()
|
||||
hudRenderer.postInit()
|
||||
|
||||
|
||||
@ -275,7 +275,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
||||
|
||||
hudRenderer.screenChangeResizeCallback(screenWidth, screenHeight)
|
||||
|
||||
camera.addShaders(chunkRenderer.chunkShader)
|
||||
camera.addShaders(worldRenderer.chunkShader)
|
||||
|
||||
camera.screenChangeResizeCallback(screenWidth, screenHeight)
|
||||
|
||||
@ -304,7 +304,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
||||
|
||||
|
||||
|
||||
chunkRenderer.draw()
|
||||
worldRenderer.draw()
|
||||
hudRenderer.draw()
|
||||
|
||||
renderStats.endDraw()
|
||||
@ -351,7 +351,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
||||
}
|
||||
if (this.renderingStatus == RenderingStates.PAUSED) {
|
||||
renderQueue.clear()
|
||||
chunkRenderer.refreshChunkCache()
|
||||
worldRenderer.refreshChunkCache()
|
||||
}
|
||||
this.renderingStatus = renderingStatus
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
* Copyright (C) 2020 Moritz Zwerger, Lukas Eisenhauer
|
||||
*
|
||||
* 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 org.lwjgl.opengl.GL20.glEnableVertexAttribArray
|
||||
import org.lwjgl.opengl.GL20.glVertexAttribPointer
|
||||
import org.lwjgl.opengl.GL30.*
|
||||
|
||||
class WorldMesh(data: FloatArray) {
|
||||
class ChunkSectionMesh(data: FloatArray) {
|
||||
var vAO: Int = glGenVertexArrays()
|
||||
var vBO: Int = glGenBuffers()
|
||||
var trianglesCount: Int = data.size / FLOATS_PER_VERTEX
|
@ -0,0 +1,15 @@
|
||||
package de.bixilon.minosoft.gui.rendering.chunk
|
||||
|
||||
import glm_.mat4x4.Mat4
|
||||
import glm_.vec3.Vec3
|
||||
|
||||
class Frustum(matrix: Mat4) {
|
||||
val normals =
|
||||
arrayOf(
|
||||
Vec3(
|
||||
matrix.a3 + matrix.a0,
|
||||
matrix.b3 + matrix.b0,
|
||||
matrix.c3 + matrix.c0).normalize(),
|
||||
Vec3()
|
||||
)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package de.bixilon.minosoft.gui.rendering.chunk
|
||||
|
||||
class Plane {
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 Moritz Zwerger
|
||||
* Copyright (C) 2020 Moritz Zwerger, Lukas Eisenhauer
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
@ -30,10 +30,10 @@ import org.lwjgl.opengl.GL11.glEnable
|
||||
import org.lwjgl.opengl.GL13.glDisable
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class ChunkRenderer(private val connection: Connection, private val world: World, val renderWindow: RenderWindow) : Renderer {
|
||||
class WorldRenderer(private val connection: Connection, private val world: World, val renderWindow: RenderWindow) : Renderer {
|
||||
private lateinit var minecraftTextures: TextureArray
|
||||
lateinit var chunkShader: Shader
|
||||
private val chunkSectionsToDraw = ConcurrentHashMap<ChunkLocation, ConcurrentHashMap<Int, WorldMesh>>()
|
||||
private val chunkSectionsToDraw = ConcurrentHashMap<ChunkLocation, ConcurrentHashMap<Int, ChunkSectionMesh>>()
|
||||
private var currentTick = 0 // for animation usage
|
||||
private var lastTickIncrementTime = 0L
|
||||
|
||||
@ -118,7 +118,8 @@ class ChunkRenderer(private val connection: Connection, private val world: World
|
||||
}
|
||||
}
|
||||
|
||||
for ((_, map) in chunkSectionsToDraw) {
|
||||
for ((chunkLocation, map) in chunkSectionsToDraw) {
|
||||
if (chunkLocation.isVisibleFrom(connection.renderer.renderWindow.camera))
|
||||
for ((_, mesh) in map) {
|
||||
mesh.draw()
|
||||
}
|
||||
@ -157,7 +158,7 @@ class ChunkRenderer(private val connection: Connection, private val world: World
|
||||
chunkSectionsToDraw[chunkLocation] = sectionMap
|
||||
}
|
||||
renderWindow.renderQueue.add {
|
||||
val newMesh = WorldMesh(data)
|
||||
val newMesh = ChunkSectionMesh(data)
|
||||
sectionMap[sectionHeight]?.unload()
|
||||
sectionMap[sectionHeight] = newMesh
|
||||
}
|
@ -31,7 +31,7 @@ import glm_.vec4.Vec4
|
||||
|
||||
class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolean, rescale: Boolean) {
|
||||
private val fullFaceDirections: MutableSet<Directions> = mutableSetOf()
|
||||
private val faces: MutableMap<Directions, BlockModelFace> = HashMap(element.faces)
|
||||
private val faces: MutableMap<Directions, BlockModelFace> = element.faces.toMutableMap()
|
||||
private var positions: Array<Vec3> = element.positions.clone()
|
||||
private val directionMapping: MutableMap<Directions, Directions> = mutableMapOf()
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class PacketBlockChange extends ClientboundPacket {
|
||||
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock());
|
||||
}
|
||||
|
||||
connection.getRenderer().getRenderWindow().getChunkRenderer().prepareChunkSection(getPosition().getChunkLocation(), sectionHeight, section);
|
||||
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunkSection(getPosition().getChunkLocation(), sectionHeight, section);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,7 +87,7 @@ public class PacketChunkBulk extends ClientboundPacket {
|
||||
|
||||
connection.getPlayer().getWorld().setChunks(getChunks());
|
||||
|
||||
getChunks().forEach(((location, chunk) -> connection.getRenderer().getRenderWindow().getChunkRenderer().prepareChunk(location, chunk)));
|
||||
getChunks().forEach(((location, chunk) -> connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(location, chunk)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -124,7 +124,7 @@ public class PacketChunkData extends ClientboundPacket {
|
||||
|
||||
connection.getPlayer().getWorld().setChunk(getLocation(), getChunk());
|
||||
connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities());
|
||||
connection.getRenderer().getRenderWindow().getChunkRenderer().prepareChunk(this.location, this.chunk);
|
||||
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(this.location, this.chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,7 +110,7 @@ public class PacketMultiBlockChange extends ClientboundPacket {
|
||||
|
||||
for (var sectionHeight : sectionHeights) {
|
||||
ChunkSection section = chunk.getSectionOrCreate(sectionHeight);
|
||||
connection.getRenderer().getRenderWindow().getChunkRenderer().prepareChunkSection(getLocation(), sectionHeight, section);
|
||||
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunkSection(getLocation(), sectionHeight, section);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ public class PacketRespawn extends ClientboundPacket {
|
||||
connection.getPlayer().setSpawnConfirmed(false);
|
||||
connection.getPlayer().setGameMode(getGameMode());
|
||||
|
||||
connection.getRenderer().getRenderWindow().getChunkRenderer().clearChunkCache();
|
||||
connection.getRenderer().getRenderWindow().getWorldRenderer().clearChunkCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,7 +31,7 @@ public class PacketUnloadChunk extends ClientboundPacket {
|
||||
@Override
|
||||
public void handle(Connection connection) {
|
||||
connection.getPlayer().getWorld().unloadChunk(getLocation());
|
||||
connection.getRenderer().getRenderWindow().getChunkRenderer().unloadChunk(this.location);
|
||||
connection.getRenderer().getRenderWindow().getWorldRenderer().unloadChunk(this.location);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user