Lightmap support in uber shader

This commit is contained in:
DBotThePony 2023-09-22 14:20:50 +07:00
parent 339891b6e2
commit 8d32daa840
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 142 additions and 94 deletions

View File

@ -153,14 +153,14 @@ fun main() {
//ent.position += Vector2d(y = 14.0, x = -10.0)
ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0)
client.camera.pos = Vector2f(238f, 685f)
client.camera.pos = Vector2d(238.0, 685.0)
//client.camera.pos = Vector2f(0f, 0f)
client.onDrawGUI {
client.font.render("${ent.position}", y = 100f, scale = 0.25f)
client.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
client.font.render("Camera: ${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
client.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos.toDoubleVector())}", y = 160f, scale = 0.25f)
client.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos)}", y = 160f, scale = 0.25f)
}
client.onPreDrawWorld {
@ -241,9 +241,9 @@ fun main() {
//client.camera.pos.x = ent.position.x.toFloat()
//client.camera.pos.y = ent.position.y.toFloat()
client.camera.pos += Vector2f(
(if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f) + (if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f),
(if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f) + (if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f)
client.camera.pos += Vector2d(
(if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0),
(if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0)
)
//println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1)

View File

@ -9,7 +9,6 @@ import org.lwjgl.glfw.GLFW
import org.lwjgl.glfw.GLFWErrorCallback
import org.lwjgl.opengl.GL
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL15
import org.lwjgl.opengl.GL45.*
import org.lwjgl.opengl.GLCapabilities
import org.lwjgl.system.MemoryStack
@ -34,9 +33,8 @@ import ru.dbotthepony.kstarbound.client.gl.properties.GLStateSwitchTracker
import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
import ru.dbotthepony.kstarbound.client.gl.shader.UberShader
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
import ru.dbotthepony.kstarbound.client.input.UserInput
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
@ -62,6 +60,7 @@ import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2d
import ru.dbotthepony.kvector.vector.Vector2f
import ru.dbotthepony.kvector.vector.Vector2i
import ru.dbotthepony.kvector.vector.Vector4f
import java.io.Closeable
import java.io.File
import java.lang.ref.Cleaner
@ -91,6 +90,7 @@ class StarboundClient : Closeable {
var viewportHeight: Int = 0
private set
// potentially visible cells
var viewportCellX = 0
private set
var viewportCellY = 0
@ -102,7 +102,12 @@ class StarboundClient : Closeable {
var viewportRectangle = AABB.rectangle(Vector2d.ZERO, 0.0, 0.0)
private set
var fullbright = true
var viewportBottomLeft = Vector2d()
private set
var viewportTopRight = Vector2d()
private set
var fullbright = false
var clientTerminated = false
private set
@ -169,6 +174,7 @@ class StarboundClient : Closeable {
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4)
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5)
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE)
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE)
window = GLFW.glfwCreateWindow(800, 600, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL)
require(window != MemoryUtil.NULL) { "Unable to create GLFW window" }
@ -228,18 +234,19 @@ class StarboundClient : Closeable {
GLFW.glfwShowWindow(window)
putDebugLog("Initialized GLFW window")
val v = glGenBuffers()
glBindBuffer(GL_ARRAY_BUFFER, v)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
GL15.glDeleteBuffers(v)
}
val maxTextureBlocks = glGetInteger(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
val maxVertexAttribBindPoints = glGetInteger(GL_MAX_VERTEX_ATTRIB_BINDINGS)
init {
LOGGER.info("OpenGL Version: ${glGetString(GL_VERSION)}")
LOGGER.info("OpenGL Vendor: ${glGetString(GL_VENDOR)}")
LOGGER.info("OpenGL Renderer: ${glGetString(GL_RENDERER)}")
LOGGER.debug("Max supported texture image units: $maxTextureBlocks")
LOGGER.debug("Max supported vertex attribute bind points: $maxVertexAttribBindPoints")
}
val stack = Matrix3fStack()
// минимальное время хранения 5 минут и...
@ -261,8 +268,15 @@ class StarboundClient : Closeable {
private val missingTexturePath = "/assetmissing.png"
private val regularShaderPrograms = ArrayList<WeakReference<GLShaderProgram.Regular>>()
private val uberShaderPrograms = ArrayList<WeakReference<UberShader>>()
val lightMapLocation = maxTextureBlocks - 1
fun addShaderProgram(program: GLShaderProgram) {
if (program is UberShader) {
uberShaderPrograms.add(WeakReference(program))
}
if (program is GLShaderProgram.Regular) {
regularShaderPrograms.add(WeakReference(program))
}
@ -529,50 +543,20 @@ class StarboundClient : Closeable {
return Vector2f(xMousePos.get().toFloat(), yMousePos.get().toFloat())
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(x: Double, y: Double): Vector2d {
val relativeX = camera.pos.x - (viewportWidth / 2.0 - x) / PIXELS_IN_STARBOUND_UNIT / settings.zoom
val relativeY = (viewportHeight / 2.0 - y) / PIXELS_IN_STARBOUND_UNIT / settings.zoom + camera.pos.y
val relativeX = (-viewportWidth / 2.0 + x) / settings.zoom / PIXELS_IN_STARBOUND_UNIT + camera.pos.x
val relativeY = (-viewportHeight / 2.0 + y) / settings.zoom / PIXELS_IN_STARBOUND_UNIT + camera.pos.y
return Vector2d(relativeX, relativeY)
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(x: Int, y: Int): Vector2d {
return screenToWorld(x.toDouble(), y.toDouble())
}
fun screenToWorld(value: Vector2d): Vector2d {
return screenToWorld(value.x, value.y)
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(x: Float, y: Float): Vector2f {
val relativeX = camera.pos.x - (viewportWidth / 2f - x) / PIXELS_IN_STARBOUND_UNITf / settings.zoom
val relativeY = (viewportHeight / 2f - y) / PIXELS_IN_STARBOUND_UNITf / settings.zoom + camera.pos.y
return Vector2f(relativeX, relativeY)
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(value: Vector2f): Vector2f {
return screenToWorld(value.x, value.y)
}
/**
* Преобразует мировые координаты в экранные
*/
fun worldToScreen(x: Float, y: Float): Vector2f {
val relativeX = (x - camera.pos.x) * PIXELS_IN_STARBOUND_UNITf * settings.zoom + viewportWidth / 2f
val relativeY = (camera.pos.y - y) * PIXELS_IN_STARBOUND_UNITf * settings.zoom + viewportHeight / 2f
return Vector2f(relativeX, relativeY)
}
val tileRenderers = TileRenderers(this)
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
@ -641,7 +625,7 @@ class StarboundClient : Closeable {
fun updateViewportParams() {
viewportRectangle = AABB.rectangle(
camera.pos.toDoubleVector(),
camera.pos,
viewportWidth / settings.zoom / PIXELS_IN_STARBOUND_UNIT,
viewportHeight / settings.zoom / PIXELS_IN_STARBOUND_UNIT)
@ -650,6 +634,9 @@ class StarboundClient : Closeable {
viewportCellWidth = roundTowardsPositiveInfinity(viewportRectangle.width) + 32
viewportCellHeight = roundTowardsPositiveInfinity(viewportRectangle.height) + 32
viewportTopRight = screenToWorld(viewportWidth, viewportHeight)
viewportBottomLeft = screenToWorld(0, 0)
if (viewportLighting.width != viewportCellWidth || viewportLighting.height != viewportCellHeight) {
viewportLighting = LightCalculator(viewportCells, viewportCellWidth, viewportCellHeight)
viewportLighting.multithreaded = true
@ -720,6 +707,12 @@ class StarboundClient : Closeable {
return true
}
uberShaderPrograms.forEachValid {
if (it.flags.contains(UberShader.Flag.NEEDS_SCREEN_SIZE)) {
it.screenSize = Vector2f(viewportWidth.toFloat(), viewportHeight.toFloat())
}
}
if (world != null) {
updateViewportParams()
val layers = LayeredRenderer()
@ -734,7 +727,7 @@ class StarboundClient : Closeable {
val viewMatrix = viewportMatrixWorld.copy()
.translate(viewportWidth / 2f, viewportHeight / 2f) // центр экрана + координаты отрисовки мира
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
.translate(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
.translate(-camera.pos.x.toFloat(), -camera.pos.y.toFloat()) // перемещаем вид к камере
regularShaderPrograms.forEachValid { it.viewMatrix = viewMatrix }
@ -748,15 +741,12 @@ class StarboundClient : Closeable {
}
viewportLighting.clear()
val viewportLightingMem = viewportLightingMem
world.addLayers(
layers = layers,
size = viewportRectangle)
layers.render()
val viewportLightingMem = viewportLightingMem
if (viewportLightingMem != null && !fullbright) {
viewportLightingMem.position(0)
BufferUtils.zeroBuffer(viewportLightingMem)
@ -777,27 +767,29 @@ class StarboundClient : Closeable {
)
textureUnpackAlignment = old
viewportLightingTexture.textureMinFilter = GL_LINEAR
//viewportLightingTexture.textureMagFilter = GL_NEAREST
//viewportLightingTexture.generateMips()
blendFunc = BlendFunc.MULTIPLY_BY_SRC
/*quadTexture(viewportLightingTexture) {
it.quad(
(viewportCellX).toFloat(),
(viewportCellY).toFloat(),
(viewportCellX + viewportCellWidth).toFloat(),
(viewportCellY + viewportCellHeight).toFloat(),
QuadTransformers.uv()
)
}*/
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
} else {
viewportLightingTexture.upload(GL_RGBA, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, WHITE)
}
viewportLightingTexture.textureMinFilter = GL_LINEAR
activeTexture = lightMapLocation
texture2D = viewportLightingTexture
activeTexture = 0
val lightmapUV = Vector4f(
((viewportBottomLeft.x - viewportCellX) / viewportLighting.width).toFloat(),
((viewportBottomLeft.y - viewportCellY) / viewportLighting.height).toFloat(),
(1f - (viewportCellX + viewportCellWidth - viewportTopRight.x) / viewportLighting.width).toFloat(),
(1f - (viewportCellY + viewportCellHeight - viewportTopRight.y) / viewportLighting.height).toFloat())
uberShaderPrograms.forEachValid {
it.lightmapTexture = lightMapLocation
it.lightmapUV = lightmapUV
}
layers.render()
world.physics.debugDraw()
for (lambda in onPostDrawWorld) {
@ -898,6 +890,13 @@ class StarboundClient : Closeable {
companion object {
private val LOGGER = LogManager.getLogger(StarboundClient::class.java)
private val CLIENTS = ThreadLocal<StarboundClient>()
private val WHITE = ByteBuffer.allocateDirect(4).also {
it.put(0xFF.toByte())
it.put(0xFF.toByte())
it.put(0xFF.toByte())
it.put(0xFF.toByte())
it.position(0)
}
@JvmStatic
fun current() = checkNotNull(CLIENTS.get()) { "No client registered to current thread (${Thread.currentThread()})" }

View File

@ -43,8 +43,9 @@ class GLLiquidProgram : GLShaderProgram(shaders("liquid"), VertexAttributes.POSI
class GLPrograms {
val position = UberShader.Builder().build()
val positionTexture = UberShader.Builder().withTexture().build()
val positionTextureLightmap = UberShader.Builder().withTexture().withLightMap().build()
val positionColor = UberShader.Builder().withColor().build()
val tile = UberShader.Builder().withTexture().withHueShift().build()
val tile = UberShader.Builder().withTexture().withHueShift().withLightMap().build()
val font = FontProgram()
val liquid = GLLiquidProgram()
}

View File

@ -11,8 +11,20 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
import ru.dbotthepony.kvector.api.IStruct4f
import ru.dbotthepony.kvector.arrays.Matrix3f
import ru.dbotthepony.kvector.vector.RGBAColor
import java.util.Collections
import java.util.EnumSet
class UberShader private constructor(
fragment: GLShader,
vertex: GLShader,
attributes: VertexAttributes,
val flags: Set<Flag>,
) : GLShaderProgram(listOf(fragment, vertex), attributes), GLShaderProgram.Regular {
enum class Flag {
LIGHT_MAP,
NEEDS_SCREEN_SIZE;
}
class UberShader private constructor(fragment: GLShader, vertex: GLShader, attributes: VertexAttributes) : GLShaderProgram(listOf(fragment, vertex), attributes), GLShaderProgram.Regular {
override var viewMatrix: Matrix3f by F3x3Uniform("viewMatrix")
override var worldMatrix: Matrix3f by F3x3Uniform("worldMatrix")
override var modelMatrix: Matrix3f by F3x3Uniform("modelMatrix")
@ -20,6 +32,10 @@ class UberShader private constructor(fragment: GLShader, vertex: GLShader, attri
var texture0 by IUniform("texture0", VertexAttributeType.UV !in attributes)
var lightmapTexture by IUniform("lightmap", Flag.LIGHT_MAP !in flags)
var lightmapUV by F4Uniform("lightmapUV", Flag.LIGHT_MAP !in flags)
var screenSize by F2Uniform("screenSize", Flag.NEEDS_SCREEN_SIZE !in flags)
val builder = StreamVertexBuilder(attributes)
init {
@ -32,6 +48,15 @@ class UberShader private constructor(fragment: GLShader, vertex: GLShader, attri
class Builder {
private val directives = Object2ObjectArrayMap<String, String>()
private val attributes = ObjectArraySet<VertexAttributeType>()
private val flags = EnumSet.noneOf(Flag::class.java)
private fun addFlag(flag: Flag): Builder {
if (flags.add(flag)) {
directives[flag.name] = ""
}
return this
}
init {
attributes.add(VertexAttributeType.POSITION)
@ -60,9 +85,12 @@ class UberShader private constructor(fragment: GLShader, vertex: GLShader, attri
return attribute(VertexAttributeType.HUE_SHIFT)
}
fun build(): UberShader {
val client = StarboundClient.current()
fun withLightMap(): Builder {
addFlag(Flag.NEEDS_SCREEN_SIZE)
return addFlag(Flag.LIGHT_MAP)
}
fun build(): UberShader {
val fragment = GLShader("#version 450\n" +
directives.entries.joinToString("\n") { "#define ${it.key}" + (if (it.value != "") " " + it.value else "") } +
fragment, GL_FRAGMENT_SHADER)
@ -73,7 +101,7 @@ class UberShader private constructor(fragment: GLShader, vertex: GLShader, attri
val attributes = VertexAttributes.of(attributes)
return UberShader(fragment, vertex, attributes)
return UberShader(fragment, vertex, attributes, Collections.unmodifiableSet(EnumSet.copyOf(flags)))
}
}

View File

@ -2,13 +2,14 @@ package ru.dbotthepony.kstarbound.client.render
import org.lwjgl.glfw.GLFW.*
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kvector.vector.Vector2d
import ru.dbotthepony.kvector.vector.Vector2f
class Camera(val client: StarboundClient) {
/**
* Позиция этой камеры в Starbound Unit'ах
*/
var pos = Vector2f()
var pos = Vector2d()
var pressedLeft = false
private set
@ -31,31 +32,31 @@ class Camera(val client: StarboundClient) {
}
}
val velocity: Vector2f get() {
var x = 0f
var y = 0f
val velocity: Vector2d get() {
var x = 0.0
var y = 0.0
if (pressedLeft) {
x -= (FREEVIEW_SENS).toFloat()
x -= (FREEVIEW_SENS)
}
if (pressedRight) {
x += (FREEVIEW_SENS).toFloat()
x += (FREEVIEW_SENS)
}
if (pressedUp) {
y += (FREEVIEW_SENS).toFloat()
y += (FREEVIEW_SENS)
}
if (pressedDown) {
y -= (FREEVIEW_SENS).toFloat()
y -= (FREEVIEW_SENS)
}
return Vector2f(x, y)
return Vector2d(x, y)
}
fun think(delta: Double) {
pos += velocity * delta.toFloat()
pos += velocity * delta
}
companion object {

View File

@ -82,7 +82,7 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
override fun render(client: StarboundClient, x: Float, y: Float) {
val sprite = path.sprite ?: return
val texture = client.loadTexture(path.imagePath.value!!)
val program = client.programs.positionTexture
val program = if (fullbright) client.programs.positionTexture else client.programs.positionTextureLightmap
program.modelMatrix = transform.map({ it }, {
val mat = Matrix3f.identity()

View File

@ -6,7 +6,7 @@
</Console>
</Appenders>
<Loggers>
<Root level="info">
<Root level="debug">
<AppenderRef ref="Console"/>
</Root>
</Loggers>

View File

@ -15,6 +15,16 @@ in vec4 vertexColor;
in float hueShiftOut;
#endif
#ifdef NEEDS_SCREEN_SIZE
uniform vec2 screenSize;
#endif
#ifdef LIGHT_MAP
uniform sampler2D lightmap;
uniform vec4 lightmapUV;
in vec4 gl_FragCoord;
#endif
// https://gist.github.com/983/e170a24ae8eba2cd174f
// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
// https://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl
@ -56,4 +66,13 @@ void main() {
#endif
colorResult *= colorMultiplier;
#ifdef LIGHT_MAP
vec4 lightmapSample = texture(lightmap, vec2(
mix(lightmapUV.x, lightmapUV.z, gl_FragCoord.x / screenSize.x),
mix(lightmapUV.y, lightmapUV.w, gl_FragCoord.y / screenSize.y)
));
colorResult *= vec4(lightmapSample.xyz, 1.0);
#endif
}