mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -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
|
package de.bixilon.minosoft.data.world
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.Directions
|
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) {
|
data class ChunkLocation(val x: Int, val z: Int) {
|
||||||
|
|
||||||
override fun toString(): String {
|
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")
|
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.cos
|
||||||
import kotlin.math.sin
|
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 mouseSensitivity = Minosoft.getConfig().config.game.camera.moseSensitivity
|
||||||
private var movementSpeed = 7
|
private var movementSpeed = 7
|
||||||
var cameraPosition = Vec3(0.0f, 0.0f, 0.0f)
|
var cameraPosition = Vec3(0.0f, 0.0f, 0.0f)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Minosoft
|
* 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.
|
* 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.KeyBinding
|
||||||
import de.bixilon.minosoft.config.key.KeyCodes
|
import de.bixilon.minosoft.config.key.KeyCodes
|
||||||
import de.bixilon.minosoft.data.mappings.ModIdentifier
|
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.HUDRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.hud.elements.RenderStats
|
import de.bixilon.minosoft.gui.rendering.hud.elements.RenderStats
|
||||||
import de.bixilon.minosoft.modding.event.EventInvokerCallback
|
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
|
private var mouseCatch = !StaticConfiguration.DEBUG_MODE
|
||||||
|
|
||||||
// all renderers
|
// 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 hudRenderer: HUDRenderer = HUDRenderer(connection, this)
|
||||||
|
|
||||||
val renderQueue = ConcurrentLinkedQueue<Runnable>()
|
val renderQueue = ConcurrentLinkedQueue<Runnable>()
|
||||||
@ -205,7 +205,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
// Make the OpenGL context current
|
// Make the OpenGL context current
|
||||||
glfwMakeContextCurrent(windowId)
|
glfwMakeContextCurrent(windowId)
|
||||||
// Enable v-sync
|
// Enable v-sync
|
||||||
glfwSwapInterval(1)
|
glfwSwapInterval(0)
|
||||||
|
|
||||||
|
|
||||||
// Make the window visible
|
// 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)
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
|
||||||
|
|
||||||
chunkRenderer.init()
|
worldRenderer.init()
|
||||||
hudRenderer.init()
|
hudRenderer.init()
|
||||||
|
|
||||||
chunkRenderer.postInit()
|
worldRenderer.postInit()
|
||||||
hudRenderer.postInit()
|
hudRenderer.postInit()
|
||||||
|
|
||||||
|
|
||||||
@ -275,7 +275,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
|
|
||||||
hudRenderer.screenChangeResizeCallback(screenWidth, screenHeight)
|
hudRenderer.screenChangeResizeCallback(screenWidth, screenHeight)
|
||||||
|
|
||||||
camera.addShaders(chunkRenderer.chunkShader)
|
camera.addShaders(worldRenderer.chunkShader)
|
||||||
|
|
||||||
camera.screenChangeResizeCallback(screenWidth, screenHeight)
|
camera.screenChangeResizeCallback(screenWidth, screenHeight)
|
||||||
|
|
||||||
@ -304,7 +304,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
chunkRenderer.draw()
|
worldRenderer.draw()
|
||||||
hudRenderer.draw()
|
hudRenderer.draw()
|
||||||
|
|
||||||
renderStats.endDraw()
|
renderStats.endDraw()
|
||||||
@ -351,7 +351,7 @@ class RenderWindow(private val connection: Connection, val rendering: Rendering)
|
|||||||
}
|
}
|
||||||
if (this.renderingStatus == RenderingStates.PAUSED) {
|
if (this.renderingStatus == RenderingStates.PAUSED) {
|
||||||
renderQueue.clear()
|
renderQueue.clear()
|
||||||
chunkRenderer.refreshChunkCache()
|
worldRenderer.refreshChunkCache()
|
||||||
}
|
}
|
||||||
this.renderingStatus = renderingStatus
|
this.renderingStatus = renderingStatus
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Minosoft
|
* 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.
|
* 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.GL20.glVertexAttribPointer
|
||||||
import org.lwjgl.opengl.GL30.*
|
import org.lwjgl.opengl.GL30.*
|
||||||
|
|
||||||
class WorldMesh(data: FloatArray) {
|
class ChunkSectionMesh(data: FloatArray) {
|
||||||
var vAO: Int = glGenVertexArrays()
|
var vAO: Int = glGenVertexArrays()
|
||||||
var vBO: Int = glGenBuffers()
|
var vBO: Int = glGenBuffers()
|
||||||
var trianglesCount: Int = data.size / FLOATS_PER_VERTEX
|
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
|
* 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.
|
* 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 org.lwjgl.opengl.GL13.glDisable
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
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
|
private lateinit var minecraftTextures: TextureArray
|
||||||
lateinit var chunkShader: Shader
|
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 currentTick = 0 // for animation usage
|
||||||
private var lastTickIncrementTime = 0L
|
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) {
|
for ((_, mesh) in map) {
|
||||||
mesh.draw()
|
mesh.draw()
|
||||||
}
|
}
|
||||||
@ -157,7 +158,7 @@ class ChunkRenderer(private val connection: Connection, private val world: World
|
|||||||
chunkSectionsToDraw[chunkLocation] = sectionMap
|
chunkSectionsToDraw[chunkLocation] = sectionMap
|
||||||
}
|
}
|
||||||
renderWindow.renderQueue.add {
|
renderWindow.renderQueue.add {
|
||||||
val newMesh = WorldMesh(data)
|
val newMesh = ChunkSectionMesh(data)
|
||||||
sectionMap[sectionHeight]?.unload()
|
sectionMap[sectionHeight]?.unload()
|
||||||
sectionMap[sectionHeight] = newMesh
|
sectionMap[sectionHeight] = newMesh
|
||||||
}
|
}
|
@ -31,7 +31,7 @@ import glm_.vec4.Vec4
|
|||||||
|
|
||||||
class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolean, rescale: Boolean) {
|
class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvlock: Boolean, rescale: Boolean) {
|
||||||
private val fullFaceDirections: MutableSet<Directions> = mutableSetOf()
|
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 var positions: Array<Vec3> = element.positions.clone()
|
||||||
private val directionMapping: MutableMap<Directions, Directions> = mutableMapOf()
|
private val directionMapping: MutableMap<Directions, Directions> = mutableMapOf()
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public class PacketBlockChange extends ClientboundPacket {
|
|||||||
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock());
|
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
|
@Override
|
||||||
|
@ -87,7 +87,7 @@ public class PacketChunkBulk extends ClientboundPacket {
|
|||||||
|
|
||||||
connection.getPlayer().getWorld().setChunks(getChunks());
|
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
|
@Override
|
||||||
|
@ -124,7 +124,7 @@ public class PacketChunkData extends ClientboundPacket {
|
|||||||
|
|
||||||
connection.getPlayer().getWorld().setChunk(getLocation(), getChunk());
|
connection.getPlayer().getWorld().setChunk(getLocation(), getChunk());
|
||||||
connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities());
|
connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities());
|
||||||
connection.getRenderer().getRenderWindow().getChunkRenderer().prepareChunk(this.location, this.chunk);
|
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(this.location, this.chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,7 +110,7 @@ public class PacketMultiBlockChange extends ClientboundPacket {
|
|||||||
|
|
||||||
for (var sectionHeight : sectionHeights) {
|
for (var sectionHeight : sectionHeights) {
|
||||||
ChunkSection section = chunk.getSectionOrCreate(sectionHeight);
|
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().setSpawnConfirmed(false);
|
||||||
connection.getPlayer().setGameMode(getGameMode());
|
connection.getPlayer().setGameMode(getGameMode());
|
||||||
|
|
||||||
connection.getRenderer().getRenderWindow().getChunkRenderer().clearChunkCache();
|
connection.getRenderer().getRenderWindow().getWorldRenderer().clearChunkCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,7 +31,7 @@ public class PacketUnloadChunk extends ClientboundPacket {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(Connection connection) {
|
public void handle(Connection connection) {
|
||||||
connection.getPlayer().getWorld().unloadChunk(getLocation());
|
connection.getPlayer().getWorld().unloadChunk(getLocation());
|
||||||
connection.getRenderer().getRenderWindow().getChunkRenderer().unloadChunk(this.location);
|
connection.getRenderer().getRenderWindow().getWorldRenderer().unloadChunk(this.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user