Remove gpu light rendering
This commit is contained in:
parent
7a69dee5ca
commit
dc72bf1b18
@ -1,16 +1,9 @@
|
|||||||
package ru.dbotthepony.kstarbound.client
|
package ru.dbotthepony.kstarbound.client
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
||||||
import org.lwjgl.opengl.GL11.GL_LINES
|
|
||||||
import org.lwjgl.opengl.GL11.GL_TRIANGLES
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLHardLightGeometryProgram
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLSoftLightGeometryProgram
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
||||||
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
@ -21,11 +14,7 @@ import ru.dbotthepony.kstarbound.world.api.CHUNK_SIZEf
|
|||||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
|
||||||
import ru.dbotthepony.kvector.util2d.intersectCircleRectangle
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector3f
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
@ -47,6 +36,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
var isDirty = true
|
var isDirty = true
|
||||||
|
|
||||||
fun bake() {
|
fun bake() {
|
||||||
|
if (!isDirty) return
|
||||||
isDirty = false
|
isDirty = false
|
||||||
|
|
||||||
if (state.isSameThread()) {
|
if (state.isSameThread()) {
|
||||||
@ -60,7 +50,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
layers.clear()
|
layers.clear()
|
||||||
|
|
||||||
for ((pos, tile) in view.iterateTiles()) {
|
for ((pos, tile) in view.iterateTiles()) {
|
||||||
if (!world.chunkMap.inBounds(this@ClientChunk.pos.firstTile + pos)) continue
|
if (!world.chunkMap.inBounds(this@ClientChunk.pos.tile + pos)) continue
|
||||||
|
|
||||||
val material = tile.material
|
val material = tile.material
|
||||||
|
|
||||||
@ -76,33 +66,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadRenderers() {
|
|
||||||
for ((pos, tile) in view.iterateTiles()) {
|
|
||||||
if (!world.chunkMap.inBounds(this@ClientChunk.pos.firstTile + pos)) continue
|
|
||||||
|
|
||||||
val material = tile.material
|
|
||||||
val modifier = tile.modifier
|
|
||||||
|
|
||||||
if (material != null) {
|
|
||||||
world.client.tileRenderers.getTileRenderer(material.materialName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modifier != null) {
|
|
||||||
world.client.tileRenderers.getModifierRenderer(modifier.modName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uploadStatic(clear: Boolean = true) {
|
|
||||||
for ((baked, builder, zLevel) in layers.buildSortedLayerList()) {
|
|
||||||
bakedMeshes.add(ConfiguredStaticMesh(baked, builder) to zLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clear) {
|
|
||||||
layers.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun render(stack: Matrix4fStack) {
|
fun render(stack: Matrix4fStack) {
|
||||||
val transform = stack.last()
|
val transform = stack.last()
|
||||||
|
|
||||||
@ -111,13 +74,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun autoBake() {
|
fun upload() {
|
||||||
if (isDirty) {
|
|
||||||
bake()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun autoUpload() {
|
|
||||||
if (layers.isNotEmpty) {
|
if (layers.isNotEmpty) {
|
||||||
for (mesh in bakedMeshes) {
|
for (mesh in bakedMeshes) {
|
||||||
mesh.first.close()
|
mesh.first.close()
|
||||||
@ -133,16 +90,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun autoBakeAndUpload() {
|
|
||||||
autoBake()
|
|
||||||
autoUpload()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bakeAndRender(transform: Matrix4fStack) {
|
|
||||||
autoBakeAndUpload()
|
|
||||||
render(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
for (mesh in bakedMeshes) {
|
for (mesh in bakedMeshes) {
|
||||||
mesh.first.close()
|
mesh.first.close()
|
||||||
@ -150,7 +97,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val debugCollisions get() = world.client.settings.debugCollisions
|
|
||||||
val posVector2d = Vector2d(x = pos.x * CHUNK_SIZEd, y = pos.y * CHUNK_SIZEd)
|
val posVector2d = Vector2d(x = pos.x * CHUNK_SIZEd, y = pos.y * CHUNK_SIZEd)
|
||||||
|
|
||||||
private val foregroundRenderer = TileLayerRenderer(worldForegroundView, isBackground = false)
|
private val foregroundRenderer = TileLayerRenderer(worldForegroundView, isBackground = false)
|
||||||
@ -176,16 +122,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Принудительно подгружает в GLStateTracker все необходимые рендереры (ибо им нужны текстуры и прочее)
|
|
||||||
*
|
|
||||||
* Вызывается перед tesselateStatic()
|
|
||||||
*/
|
|
||||||
fun loadRenderers() {
|
|
||||||
foregroundRenderer.loadRenderers()
|
|
||||||
backgroundRenderer.loadRenderers()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Тесселирует "статичную" геометрию в builders (к примеру тайлы), с проверкой, изменилось ли что либо,
|
* Тесселирует "статичную" геометрию в builders (к примеру тайлы), с проверкой, изменилось ли что либо,
|
||||||
* и загружает её в видеопамять.
|
* и загружает её в видеопамять.
|
||||||
@ -195,8 +131,8 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun bake() {
|
fun bake() {
|
||||||
backgroundRenderer.autoBake()
|
backgroundRenderer.bake()
|
||||||
foregroundRenderer.autoBake()
|
foregroundRenderer.bake()
|
||||||
|
|
||||||
if (state.isSameThread())
|
if (state.isSameThread())
|
||||||
upload()
|
upload()
|
||||||
@ -206,97 +142,8 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
* Загружает в видеопамять всю геометрию напрямую, если есть что загружать
|
* Загружает в видеопамять всю геометрию напрямую, если есть что загружать
|
||||||
*/
|
*/
|
||||||
fun upload() {
|
fun upload() {
|
||||||
backgroundRenderer.autoUpload()
|
backgroundRenderer.upload()
|
||||||
foregroundRenderer.autoUpload()
|
foregroundRenderer.upload()
|
||||||
}
|
|
||||||
|
|
||||||
private inner class ShadowGeometryTracker(val x: Int, val y: Int) : GPULightRenderer.ShadowGeometryRenderer {
|
|
||||||
private val hardShadowGeometry by lazy(LazyThreadSafetyMode.NONE) { StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT, GeometryType.LINES) }
|
|
||||||
private var hardShadowGeometryRev = -1
|
|
||||||
private val softShadowGeometry by lazy(LazyThreadSafetyMode.NONE) { StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_ALTERNATIVE) }
|
|
||||||
private var softShadowGeometryRev = -1
|
|
||||||
|
|
||||||
private fun buildGeometry(builder: StreamVertexBuilder, line: (StreamVertexBuilder, Float, Float, Float, Float) -> Unit) {
|
|
||||||
builder.builder.begin()
|
|
||||||
|
|
||||||
for (x in this.x * SHADOW_GEOMETRY_SQUARE_SIZE until (this.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) {
|
|
||||||
for (y in this.y * SHADOW_GEOMETRY_SQUARE_SIZE until (this.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) {
|
|
||||||
if (localForegroundView.getTile(x, y).material?.renderParameters?.lightTransparent == false) {
|
|
||||||
if (x == 0 || localForegroundView.getTile(x - 1, y).material?.renderParameters?.lightTransparent != true) {
|
|
||||||
line(builder, x.toFloat(), y + 1f, x.toFloat(), y.toFloat())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x == CHUNK_SIZE - 1 || localForegroundView.getTile(x + 1, y).material?.renderParameters?.lightTransparent != true) {
|
|
||||||
line(builder, x + 1f, y.toFloat(), x + 1f, y + 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y == 0 || localForegroundView.getTile(x, y - 1).material?.renderParameters?.lightTransparent != true) {
|
|
||||||
line(builder, x.toFloat(), y.toFloat(), x + 1f, y.toFloat())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y == CHUNK_SIZE - 1 || localForegroundView.getTile(x, y + 1).material?.renderParameters?.lightTransparent != true) {
|
|
||||||
line(builder, x + 1f, y + 1f, x.toFloat(), y + 1f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.upload()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildHardGeometry() {
|
|
||||||
hardShadowGeometryRev = tileChangeset
|
|
||||||
|
|
||||||
buildGeometry(hardShadowGeometry) { it, x0, y0, x1, y1 ->
|
|
||||||
it.builder.vertex().pushVec2f(x0, y0)
|
|
||||||
it.builder.vertex().pushVec2f(x1, y1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildSoftGeometry() {
|
|
||||||
softShadowGeometryRev = tileChangeset
|
|
||||||
buildGeometry(softShadowGeometry) { it, x0, y0, x1, y1 ->
|
|
||||||
it.builder.shadowLine(x0, y0, x1, y1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderHardGeometry(
|
|
||||||
renderer: GPULightRenderer,
|
|
||||||
lightPosition: Vector2f,
|
|
||||||
lightRadius: Float,
|
|
||||||
stack: Matrix4fStack,
|
|
||||||
program: GLHardLightGeometryProgram
|
|
||||||
) {
|
|
||||||
if (hardShadowGeometryRev != tileChangeset) {
|
|
||||||
buildHardGeometry()
|
|
||||||
}
|
|
||||||
|
|
||||||
hardShadowGeometry.draw(GL_LINES)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderSoftGeometry(
|
|
||||||
renderer: GPULightRenderer,
|
|
||||||
lightPosition: Vector2f,
|
|
||||||
lightRadius: Float,
|
|
||||||
stack: Matrix4fStack,
|
|
||||||
program: GLSoftLightGeometryProgram
|
|
||||||
) {
|
|
||||||
if (softShadowGeometryRev != tileChangeset) {
|
|
||||||
buildSoftGeometry()
|
|
||||||
}
|
|
||||||
|
|
||||||
softShadowGeometry.draw(GL_TRIANGLES)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val shadowGeometry = ArrayList<ShadowGeometryTracker>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
for (x in 0 until SHADOW_GEOMETRY_SUBDIVISION) {
|
|
||||||
for (y in 0 until SHADOW_GEOMETRY_SUBDIVISION) {
|
|
||||||
shadowGeometry.add(ShadowGeometryTracker(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val liquidTypes = ReferenceArraySet<LiquidDefinition>()
|
private val liquidTypes = ReferenceArraySet<LiquidDefinition>()
|
||||||
@ -317,81 +164,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
return liquidTypes
|
return liquidTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class Renderer(val renderOrigin: ChunkPos = pos) : GPULightRenderer.ShadowGeometryRenderer {
|
inner class Renderer(val renderOrigin: ChunkPos = pos) {
|
||||||
override fun renderHardGeometry(
|
|
||||||
renderer: GPULightRenderer,
|
|
||||||
lightPosition: Vector2f,
|
|
||||||
lightRadius: Float,
|
|
||||||
stack: Matrix4fStack,
|
|
||||||
program: GLHardLightGeometryProgram
|
|
||||||
) {
|
|
||||||
if (!intersectCircleRectangle(lightPosition, lightRadius, renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf, (renderOrigin.x + 1) * CHUNK_SIZEf, (renderOrigin.y + 1) * CHUNK_SIZEf)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var setOnce = false
|
|
||||||
|
|
||||||
for (geometry in shadowGeometry) {
|
|
||||||
if (intersectCircleRectangle(
|
|
||||||
lightPosition,
|
|
||||||
lightRadius,
|
|
||||||
renderOrigin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
|
|
||||||
renderOrigin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
|
|
||||||
renderOrigin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
|
|
||||||
renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
|
|
||||||
) {
|
|
||||||
if (!setOnce) {
|
|
||||||
program.localToWorldTransform =
|
|
||||||
Matrix4f.identity().translateWithMultiplication(
|
|
||||||
Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
|
|
||||||
y = renderOrigin.y * CHUNK_SIZEf
|
|
||||||
))
|
|
||||||
|
|
||||||
setOnce = true
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry.renderHardGeometry(renderer, lightPosition, lightRadius, stack, program)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderSoftGeometry(
|
|
||||||
renderer: GPULightRenderer,
|
|
||||||
lightPosition: Vector2f,
|
|
||||||
lightRadius: Float,
|
|
||||||
stack: Matrix4fStack,
|
|
||||||
program: GLSoftLightGeometryProgram
|
|
||||||
) {
|
|
||||||
if (!intersectCircleRectangle(lightPosition, lightRadius, renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf, (renderOrigin.x + 1) * CHUNK_SIZEf, (renderOrigin.y + 1) * CHUNK_SIZEf)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var setOnce = false
|
|
||||||
|
|
||||||
for (geometry in shadowGeometry) {
|
|
||||||
if (intersectCircleRectangle(
|
|
||||||
lightPosition,
|
|
||||||
lightRadius,
|
|
||||||
renderOrigin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
|
|
||||||
renderOrigin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
|
|
||||||
renderOrigin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
|
|
||||||
renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
|
|
||||||
) {
|
|
||||||
if (!setOnce) {
|
|
||||||
program.localToWorldTransform =
|
|
||||||
Matrix4f.identity().translateWithMultiplication(
|
|
||||||
Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
|
|
||||||
y = renderOrigin.y * CHUNK_SIZEf
|
|
||||||
))
|
|
||||||
|
|
||||||
setOnce = true
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry.renderSoftGeometry(renderer, lightPosition, lightRadius, stack, program)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addLayers(layers: LayeredRenderer) {
|
fun addLayers(layers: LayeredRenderer) {
|
||||||
for ((baked, zLevel) in backgroundRenderer.bakedMeshes) {
|
for ((baked, zLevel) in backgroundRenderer.bakedMeshes) {
|
||||||
layers.add(zLevel + Z_LEVEL_BACKGROUND) {
|
layers.add(zLevel + Z_LEVEL_BACKGROUND) {
|
||||||
@ -484,9 +257,4 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
renderer.close()
|
renderer.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SHADOW_GEOMETRY_SUBDIVISION = 4
|
|
||||||
const val SHADOW_GEOMETRY_SQUARE_SIZE = CHUNK_SIZE / SHADOW_GEOMETRY_SUBDIVISION
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,6 @@ class ClientWorld(
|
|||||||
size: AABB,
|
size: AABB,
|
||||||
layers: LayeredRenderer
|
layers: LayeredRenderer
|
||||||
) {
|
) {
|
||||||
client.lightRenderer.begin()
|
|
||||||
|
|
||||||
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
|
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
|
||||||
val renderer = chunk.second.Renderer(chunk.first)
|
val renderer = chunk.second.Renderer(chunk.first)
|
||||||
renderer.addLayers(layers)
|
renderer.addLayers(layers)
|
||||||
|
@ -14,7 +14,6 @@ import ru.dbotthepony.kstarbound.client.gl.BlendFunc
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.input.UserInput
|
import ru.dbotthepony.kstarbound.client.input.UserInput
|
||||||
import ru.dbotthepony.kstarbound.client.render.Camera
|
import ru.dbotthepony.kstarbound.client.render.Camera
|
||||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||||
@ -34,7 +33,6 @@ import java.nio.ByteOrder
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.locks.LockSupport
|
import java.util.concurrent.locks.LockSupport
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class StarboundClient(val starbound: Starbound) : Closeable {
|
class StarboundClient(val starbound: Starbound) : Closeable {
|
||||||
val time = PausableTimeSource(JVMTimeSource.INSTANCE)
|
val time = PausableTimeSource(JVMTimeSource.INSTANCE)
|
||||||
@ -188,8 +186,6 @@ class StarboundClient(val starbound: Starbound) : Closeable {
|
|||||||
viewportMatrixScreen = updateViewportMatrixScreen()
|
viewportMatrixScreen = updateViewportMatrixScreen()
|
||||||
viewportMatrixWorld = updateViewportMatrixWorld()
|
viewportMatrixWorld = updateViewportMatrixWorld()
|
||||||
|
|
||||||
lightRenderer.resizeFramebuffer(w, h)
|
|
||||||
|
|
||||||
for (callback in onViewportChanged) {
|
for (callback in onViewportChanged) {
|
||||||
callback.invoke(w, h)
|
callback.invoke(w, h)
|
||||||
}
|
}
|
||||||
@ -230,11 +226,6 @@ class StarboundClient(val starbound: Starbound) : Closeable {
|
|||||||
val viewportHeight by gl::viewportHeight
|
val viewportHeight by gl::viewportHeight
|
||||||
|
|
||||||
val tileRenderers = TileRenderers(this)
|
val tileRenderers = TileRenderers(this)
|
||||||
val lightRenderer = GPULightRenderer(gl)
|
|
||||||
|
|
||||||
init {
|
|
||||||
lightRenderer.resizeFramebuffer(viewportWidth, viewportHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
|
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
|
||||||
@ -49,32 +48,6 @@ class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLHardLightGeometryProgram(state: GLStateTracker) : GLShaderProgram(state, state.gshaders("hard_light_geometry")) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var localToWorldTransform by F4x4Uniform("localToWorldTransform")
|
|
||||||
var lightPosition by F2Uniform("lightPosition")
|
|
||||||
var lightPenetration by FUniform("lightPenetration")
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLSoftLightGeometryProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("soft_light_geometry2")) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var lightPenetration by FUniform("lightPenetration")
|
|
||||||
var localToWorldTransform by F4x4Uniform("localToWorldTransform")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vector3f(x, y, size)
|
|
||||||
*/
|
|
||||||
var lightPositionAndSize by F3Uniform("lightPositionAndSize")
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_AS_LINES, 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad")) {
|
class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad")) {
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
|
|
||||||
@ -239,8 +212,6 @@ class GLPrograms(val state: GLStateTracker) {
|
|||||||
val flatColor by lazy { GLFlatColorProgram(state) }
|
val flatColor by lazy { GLFlatColorProgram(state) }
|
||||||
val liquid by lazy { GLLiquidProgram(state) }
|
val liquid by lazy { GLLiquidProgram(state) }
|
||||||
val light by lazy { GLLightProgram(state) }
|
val light by lazy { GLLightProgram(state) }
|
||||||
val hardLightGeometry by lazy { GLHardLightGeometryProgram(state) }
|
|
||||||
val softLightGeometry by lazy { GLSoftLightGeometryProgram(state) }
|
|
||||||
val textured by lazy { GLTexturedProgram(state) }
|
val textured by lazy { GLTexturedProgram(state) }
|
||||||
val texturedColored by lazy { GLTexturedColoredProgram(state) }
|
val texturedColored by lazy { GLTexturedColoredProgram(state) }
|
||||||
|
|
||||||
|
@ -1,313 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT
|
|
||||||
import org.lwjgl.opengl.GL11.GL_FRONT
|
|
||||||
import org.lwjgl.opengl.GL11.GL_RGBA
|
|
||||||
import org.lwjgl.opengl.GL11.glClear
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLHardLightGeometryProgram
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLSoftLightGeometryProgram
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector3f
|
|
||||||
|
|
||||||
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
|
||||||
// https://slembcke.github.io/SuperFastHardShadows
|
|
||||||
// https://slembcke.github.io/SuperFastSoftShadows
|
|
||||||
class GPULightRenderer(val state: GLStateTracker) {
|
|
||||||
interface ShadowGeometryRenderer {
|
|
||||||
fun renderHardGeometry(renderer: GPULightRenderer, lightPosition: Vector2f, lightRadius: Float, stack: Matrix4fStack, program: GLHardLightGeometryProgram)
|
|
||||||
fun renderSoftGeometry(renderer: GPULightRenderer, lightPosition: Vector2f, lightRadius: Float, stack: Matrix4fStack, program: GLSoftLightGeometryProgram)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val geometry = ArrayList<ShadowGeometryRenderer>()
|
|
||||||
|
|
||||||
fun addShadowGeometry(geometry: ShadowGeometryRenderer): GPULightRenderer {
|
|
||||||
this.geometry.add(geometry)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeShadowGeometry(geometry: ShadowGeometryRenderer): Boolean {
|
|
||||||
return this.geometry.remove(geometry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearShadowGeometry() {
|
|
||||||
geometry.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сюда происходит рендер маски света и самого света
|
|
||||||
*/
|
|
||||||
private val framebufferRender = GLFrameBuffer(state)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сюда накапливается отрисованный свет
|
|
||||||
*/
|
|
||||||
val framebufferAccumulator = GLFrameBuffer(state)
|
|
||||||
|
|
||||||
val outputTexture: GLTexture2D? get() = framebufferAccumulator.texture
|
|
||||||
|
|
||||||
fun resizeFramebuffer(width: Int, height: Int) {
|
|
||||||
framebufferRender.reattachTexture(width, height, GL_RGBA)
|
|
||||||
framebufferAccumulator.reattachTexture(width, height, GL_RGBA)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun begin(clearGeometry: Boolean = true) {
|
|
||||||
state.ensureSameThread()
|
|
||||||
|
|
||||||
if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val old = state.clearColor
|
|
||||||
state.clearColor = RGBAColor.BLACK
|
|
||||||
|
|
||||||
framebufferRender.bind()
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT)
|
|
||||||
checkForGLError()
|
|
||||||
framebufferAccumulator.bind()
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT)
|
|
||||||
checkForGLError()
|
|
||||||
state.clearColor = old
|
|
||||||
|
|
||||||
framebufferAccumulator.unbind()
|
|
||||||
|
|
||||||
if (clearGeometry) {
|
|
||||||
geometry.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрисовывает результат на весь текущий framebuffer в режиме additive
|
|
||||||
*/
|
|
||||||
fun renderOutputAdditive() {
|
|
||||||
val oldFunc = state.blendFunc
|
|
||||||
|
|
||||||
try {
|
|
||||||
state.activeTexture = 0
|
|
||||||
state.texture2D = outputTexture
|
|
||||||
state.blendFunc = BlendFunc.ADDITIVE
|
|
||||||
state.programs.viewTextureQuad.run(0)
|
|
||||||
} finally {
|
|
||||||
state.blendFunc = oldFunc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрисовывает результат на весь текущий framebuffer в режиме additive
|
|
||||||
*/
|
|
||||||
fun renderOutputMultiplicative() {
|
|
||||||
val oldFunc = state.blendFunc
|
|
||||||
|
|
||||||
try {
|
|
||||||
state.activeTexture = 0
|
|
||||||
state.texture2D = outputTexture
|
|
||||||
state.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
|
||||||
state.programs.viewTextureQuad.run(0)
|
|
||||||
} finally {
|
|
||||||
state.blendFunc = oldFunc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun blendResult() {
|
|
||||||
val oldFunc = state.blendFunc
|
|
||||||
|
|
||||||
try {
|
|
||||||
framebufferAccumulator.bind()
|
|
||||||
|
|
||||||
state.blendFunc = BlendFunc.ADDITIVE
|
|
||||||
state.activeTexture = 0
|
|
||||||
state.texture2D = framebufferRender.texture
|
|
||||||
state.programs.viewTextureQuad.run(0)
|
|
||||||
} finally {
|
|
||||||
state.blendFunc = oldFunc
|
|
||||||
framebufferAccumulator.unbind()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun renderHardLight(
|
|
||||||
position: Vector2f,
|
|
||||||
color: RGBAColor = RGBAColor.WHITE,
|
|
||||||
radius: Float = 10f,
|
|
||||||
lightPenetration: Float = 0.1f,
|
|
||||||
stack: Matrix4fStack = state.matrixStack
|
|
||||||
) {
|
|
||||||
state.ensureSameThread()
|
|
||||||
|
|
||||||
if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val oldFunc = state.blendFunc
|
|
||||||
|
|
||||||
// отрисовка
|
|
||||||
try {
|
|
||||||
framebufferRender.bind()
|
|
||||||
|
|
||||||
// state.programs.colorQuad.clearAlpha()
|
|
||||||
|
|
||||||
// Геометрия
|
|
||||||
val old = state.clearColor
|
|
||||||
state.clearColor = CLEAR_COLOR_HARD
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT)
|
|
||||||
checkForGLError()
|
|
||||||
state.clearColor = old
|
|
||||||
|
|
||||||
state.programs.hardLightGeometry.use()
|
|
||||||
state.programs.hardLightGeometry.transform = (stack.last())
|
|
||||||
state.programs.hardLightGeometry.lightPosition = (position)
|
|
||||||
state.programs.hardLightGeometry.lightPenetration = (1f - lightPenetration)
|
|
||||||
|
|
||||||
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
|
|
||||||
|
|
||||||
for (renderer in geometry) {
|
|
||||||
renderer.renderHardGeometry(this, position, radius, stack, state.programs.hardLightGeometry)
|
|
||||||
}
|
|
||||||
|
|
||||||
state.programs.light.use()
|
|
||||||
state.programs.light.transform = (stack.last())
|
|
||||||
state.programs.light.baselineColor = (color)
|
|
||||||
|
|
||||||
// Свет
|
|
||||||
val builder = state.programs.light.builder
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
builder.builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv())
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
state.blendFunc = BLEND_MODE_INV
|
|
||||||
builder.draw()
|
|
||||||
} finally {
|
|
||||||
state.blendFunc = oldFunc
|
|
||||||
framebufferRender.unbind()
|
|
||||||
}
|
|
||||||
|
|
||||||
blendResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun renderSoftLight(
|
|
||||||
position: Vector2f,
|
|
||||||
color: RGBAColor = RGBAColor.WHITE,
|
|
||||||
radius: Float = 10f,
|
|
||||||
innerRadius: Float = radius / 3f,
|
|
||||||
lightPenetration: Float = 0.5f,
|
|
||||||
stack: Matrix4fStack = state.matrixStack
|
|
||||||
) {
|
|
||||||
state.ensureSameThread()
|
|
||||||
|
|
||||||
if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val oldFunc = state.blendFunc
|
|
||||||
|
|
||||||
// отрисовка
|
|
||||||
try {
|
|
||||||
framebufferRender.bind()
|
|
||||||
|
|
||||||
// state.programs.colorQuad.clearAlpha()
|
|
||||||
|
|
||||||
// Геометрия
|
|
||||||
val old = state.clearColor
|
|
||||||
state.clearColor = CLEAR_COLOR_SOFT
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT)
|
|
||||||
checkForGLError()
|
|
||||||
state.clearColor = old
|
|
||||||
|
|
||||||
state.programs.softLightGeometry.use()
|
|
||||||
state.programs.softLightGeometry.transform = (stack.last())
|
|
||||||
state.programs.softLightGeometry.lightPositionAndSize = (Vector3f(position, innerRadius))
|
|
||||||
state.programs.softLightGeometry.lightPenetration = (lightPenetration)
|
|
||||||
|
|
||||||
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
|
|
||||||
|
|
||||||
state.cull = true
|
|
||||||
state.cullMode = GL_FRONT
|
|
||||||
|
|
||||||
for (renderer in geometry) {
|
|
||||||
renderer.renderSoftGeometry(this, position, radius, stack, state.programs.softLightGeometry)
|
|
||||||
}
|
|
||||||
|
|
||||||
state.cull = false
|
|
||||||
|
|
||||||
state.programs.light.use()
|
|
||||||
state.programs.light.transform = (stack.last())
|
|
||||||
state.programs.light.baselineColor = (color)
|
|
||||||
|
|
||||||
// Свет
|
|
||||||
val builder = state.programs.light.builder
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
builder.builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv())
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
state.blendFunc = BLEND_MODE_SOFT
|
|
||||||
builder.draw()
|
|
||||||
} finally {
|
|
||||||
state.cull = false
|
|
||||||
state.blendFunc = oldFunc
|
|
||||||
framebufferRender.unbind()
|
|
||||||
}
|
|
||||||
|
|
||||||
blendResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 256)
|
|
||||||
}
|
|
||||||
|
|
||||||
val hugeBuilder by lazy {
|
|
||||||
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 16384)
|
|
||||||
}
|
|
||||||
|
|
||||||
val lineBuilder by lazy {
|
|
||||||
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.LINES, 256)
|
|
||||||
}
|
|
||||||
|
|
||||||
val builderSoft by lazy {
|
|
||||||
StreamVertexBuilder(state, SHADOW_FORMAT_SOFT, GeometryType.QUADS_ALTERNATIVE, 256)
|
|
||||||
}
|
|
||||||
|
|
||||||
val lineBuilderSoft by lazy {
|
|
||||||
StreamVertexBuilder(state, SHADOW_FORMAT_SOFT, GeometryType.LINES, 256)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val CLEAR_COLOR_HARD = RGBAColor(0f, 0f, 0f, 0f)
|
|
||||||
val CLEAR_COLOR_SOFT = RGBAColor(0f, 0f, 0f, 0f)
|
|
||||||
|
|
||||||
val BLEND_MODE = BlendFunc(
|
|
||||||
BlendFunc.Func.DST_ALPHA,
|
|
||||||
BlendFunc.Func.ZERO,
|
|
||||||
BlendFunc.Func.ZERO,
|
|
||||||
BlendFunc.Func.ONE,
|
|
||||||
)
|
|
||||||
|
|
||||||
val BLEND_MODE_INV = BlendFunc(
|
|
||||||
BlendFunc.Func.ONE_MINUS_DST_ALPHA,
|
|
||||||
BlendFunc.Func.ZERO,
|
|
||||||
BlendFunc.Func.ZERO,
|
|
||||||
BlendFunc.Func.ONE,
|
|
||||||
)
|
|
||||||
|
|
||||||
val BLEND_MODE_SOFT = BlendFunc(
|
|
||||||
BlendFunc.Func.ONE_MINUS_DST_ALPHA,
|
|
||||||
BlendFunc.Func.ZERO,
|
|
||||||
BlendFunc.Func.ZERO,
|
|
||||||
BlendFunc.Func.ONE,
|
|
||||||
)
|
|
||||||
|
|
||||||
val SHADOW_FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
|
|
||||||
val SHADOW_FORMAT_SOFT = GLAttributeList.Builder().push(GLType.VEC4F).push(GLType.VEC2F).build()
|
|
||||||
}
|
|
||||||
}
|
|
@ -165,12 +165,6 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class TileRenderTesselateResult {
|
|
||||||
NO_MATCH,
|
|
||||||
CONTINUE,
|
|
||||||
HALT
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun vertexTextureBuilder() = VertexBuilder(GLAttributeList.TILE, GeometryType.QUADS)
|
private fun vertexTextureBuilder() = VertexBuilder(GLAttributeList.TILE, GeometryType.QUADS)
|
||||||
|
|
||||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||||
@ -186,6 +180,12 @@ private class ModifierEqualityTester(val definition: MaterialModifier) : Equalit
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||||
|
private enum class TestResult {
|
||||||
|
NO_MATCH,
|
||||||
|
CONTINUE,
|
||||||
|
HALT
|
||||||
|
}
|
||||||
|
|
||||||
val state get() = renderers.state
|
val state get() = renderers.state
|
||||||
val texture = state.loadTexture(def.renderParameters.texture.imagePath.value!!).also {
|
val texture = state.loadTexture(def.renderParameters.texture.imagePath.value!!).also {
|
||||||
it.textureMagFilter = GL_NEAREST
|
it.textureMagFilter = GL_NEAREST
|
||||||
@ -251,7 +251,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
thisBuilder: VertexBuilder,
|
thisBuilder: VertexBuilder,
|
||||||
background: Boolean,
|
background: Boolean,
|
||||||
isModifier: Boolean,
|
isModifier: Boolean,
|
||||||
): TileRenderTesselateResult {
|
): TestResult {
|
||||||
if (matchPiece.test(getter, equalityTester, pos)) {
|
if (matchPiece.test(getter, equalityTester, pos)) {
|
||||||
for (renderPiece in matchPiece.pieces) {
|
for (renderPiece in matchPiece.pieces) {
|
||||||
if (renderPiece.piece.texture != null) {
|
if (renderPiece.piece.texture != null) {
|
||||||
@ -270,19 +270,19 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
for (subPiece in matchPiece.subMatches) {
|
for (subPiece in matchPiece.subMatches) {
|
||||||
val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier)
|
val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier)
|
||||||
|
|
||||||
if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
if (matched == TestResult.HALT || matched == TestResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
||||||
return TileRenderTesselateResult.HALT
|
return TestResult.HALT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchPiece.haltOnMatch) {
|
if (matchPiece.haltOnMatch) {
|
||||||
return TileRenderTesselateResult.HALT
|
return TestResult.HALT
|
||||||
}
|
}
|
||||||
|
|
||||||
return TileRenderTesselateResult.CONTINUE
|
return TestResult.CONTINUE
|
||||||
}
|
}
|
||||||
|
|
||||||
return TileRenderTesselateResult.NO_MATCH
|
return TestResult.NO_MATCH
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -305,7 +305,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
for (matchPiece in matcher) {
|
for (matchPiece in matcher) {
|
||||||
val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier)
|
val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier)
|
||||||
|
|
||||||
if (matched == TileRenderTesselateResult.HALT) {
|
if (matched == TestResult.HALT) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,13 +62,13 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// local cells' tile access
|
// local cells' tile access
|
||||||
val localBackgroundView = TileView.background(this)
|
val localBackgroundView = TileView.Background(this)
|
||||||
val localForegroundView = TileView.foreground(this)
|
val localForegroundView = TileView.Foreground(this)
|
||||||
|
|
||||||
// relative world cells access (accessing 0, 0 will lookup cell in world, relative to this chunk)
|
// relative world cells access (accessing 0, 0 will lookup cell in world, relative to this chunk)
|
||||||
val worldView = OffsetCellAccess(world.chunkMap, pos)
|
val worldView = OffsetCellAccess(world.chunkMap, pos)
|
||||||
val worldBackgroundView = TileView.background(worldView)
|
val worldBackgroundView = TileView.Background(worldView)
|
||||||
val worldForegroundView = TileView.foreground(worldView)
|
val worldForegroundView = TileView.Foreground(worldView)
|
||||||
|
|
||||||
val aabb = aabbBase + Vector2d(pos.x * CHUNK_SIZE.toDouble(), pos.y * CHUNK_SIZE.toDouble())
|
val aabb = aabbBase + Vector2d(pos.x * CHUNK_SIZE.toDouble(), pos.y * CHUNK_SIZE.toDouble())
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
|||||||
private val collisionCacheView = Collections.unmodifiableCollection(collisionCache)
|
private val collisionCacheView = Collections.unmodifiableCollection(collisionCache)
|
||||||
|
|
||||||
private val body = world.physics.createBody(BodyDef(
|
private val body = world.physics.createBody(BodyDef(
|
||||||
position = pos.firstTile.toDoubleVector(),
|
position = pos.tile.toDoubleVector(),
|
||||||
userData = this
|
userData = this
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -29,18 +29,10 @@ private fun circulate(value: Int, bounds: Int): Int {
|
|||||||
class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> {
|
class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> {
|
||||||
constructor(pos: IStruct2i) : this(pos.component1(), pos.component2())
|
constructor(pos: IStruct2i) : this(pos.component1(), pos.component2())
|
||||||
|
|
||||||
val firstTile get() = Vector2i(tileX, tileY)
|
// bottom left corner
|
||||||
val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SIZE_BITS) - 1, ((y + 1) shl CHUNK_SIZE_BITS) - 1)
|
val tileX: Int = x shl CHUNK_SIZE_BITS
|
||||||
|
val tileY: Int = y shl CHUNK_SIZE_BITS
|
||||||
/**
|
val tile = Vector2i(tileX, tileY)
|
||||||
* Координата тайла на 0 позиции по оси X внутри чанка в мире
|
|
||||||
*/
|
|
||||||
val tileX: Int get() = x shl CHUNK_SIZE_BITS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Координата тайла на 0 позиции по оси Y внутри чанка в мире
|
|
||||||
*/
|
|
||||||
val tileY: Int get() = y shl CHUNK_SIZE_BITS
|
|
||||||
|
|
||||||
val top: ChunkPos
|
val top: ChunkPos
|
||||||
get() {
|
get() {
|
||||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.world
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||||
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||||
import ru.dbotthepony.kbox2d.api.ContactImpulse
|
import ru.dbotthepony.kbox2d.api.ContactImpulse
|
||||||
import ru.dbotthepony.kbox2d.api.IContactFilter
|
import ru.dbotthepony.kbox2d.api.IContactFilter
|
||||||
import ru.dbotthepony.kbox2d.api.IContactListener
|
import ru.dbotthepony.kbox2d.api.IContactListener
|
||||||
@ -131,8 +132,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
return super.randomLongFor(x, y) xor seed
|
return super.randomLongFor(x, y) xor seed
|
||||||
}
|
}
|
||||||
|
|
||||||
val background = TileView.background(this)
|
val background = TileView.Background(this)
|
||||||
val foreground = TileView.foreground(this)
|
val foreground = TileView.Foreground(this)
|
||||||
|
|
||||||
abstract operator fun get(x: Int, y: Int): ChunkType?
|
abstract operator fun get(x: Int, y: Int): ChunkType?
|
||||||
operator fun get(pos: ChunkPos) = get(pos.x, pos.y)
|
operator fun get(pos: ChunkPos) = get(pos.x, pos.y)
|
||||||
@ -381,12 +382,12 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
/**
|
/**
|
||||||
* Сущности, у которых нет чанка, но они находятся в этом мире
|
* Сущности, у которых нет чанка, но они находятся в этом мире
|
||||||
*/
|
*/
|
||||||
val orphanedEntities = HashSet<Entity>()
|
val orphanedEntities = ReferenceOpenHashSet<Entity>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сущности, находящиеся в этом мире
|
* Сущности, находящиеся в этом мире
|
||||||
*/
|
*/
|
||||||
val entities = HashSet<Entity>()
|
val entities = ReferenceOpenHashSet<Entity>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Стандартное ускорение свободного падения в Starbound Units/секунда^2
|
* Стандартное ускорение свободного падения в Starbound Units/секунда^2
|
||||||
|
@ -38,7 +38,7 @@ interface ICellAccess {
|
|||||||
|
|
||||||
class OffsetCellAccess(private val parent: ICellAccess, private val x: Int, private val y: Int) : ICellAccess {
|
class OffsetCellAccess(private val parent: ICellAccess, private val x: Int, private val y: Int) : ICellAccess {
|
||||||
constructor(parent: ICellAccess, offset: IStruct2i) : this(parent, offset.component1(), offset.component2())
|
constructor(parent: ICellAccess, offset: IStruct2i) : this(parent, offset.component1(), offset.component2())
|
||||||
constructor(parent: ICellAccess, offset: ChunkPos) : this(parent, offset.firstTile)
|
constructor(parent: ICellAccess, offset: ChunkPos) : this(parent, offset.tile)
|
||||||
|
|
||||||
override fun getCell(x: Int, y: Int): IChunkCell {
|
override fun getCell(x: Int, y: Int): IChunkCell {
|
||||||
return parent.getCell(x + this.x, y + this.y)
|
return parent.getCell(x + this.x, y + this.y)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.api
|
package ru.dbotthepony.kstarbound.world.api
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
|
||||||
import ru.dbotthepony.kvector.api.IStruct2i
|
import ru.dbotthepony.kvector.api.IStruct2i
|
||||||
|
|
||||||
// for getting tiles directly, avoiding manual layer specification
|
// for getting tiles directly, avoiding manual layer specification
|
||||||
@ -11,49 +10,15 @@ interface ITileAccess : ICellAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent {
|
sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent {
|
||||||
private class Foreground(parent: ICellAccess) : TileView(parent) {
|
class Foreground(parent: ICellAccess) : TileView(parent) {
|
||||||
override fun getTile(x: Int, y: Int): ITileState {
|
override fun getTile(x: Int, y: Int): ITileState {
|
||||||
return getCell(x, y).foreground
|
return getCell(x, y).foreground
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Background(parent: ICellAccess) : TileView(parent) {
|
class Background(parent: ICellAccess) : TileView(parent) {
|
||||||
override fun getTile(x: Int, y: Int): ITileState {
|
override fun getTile(x: Int, y: Int): ITileState {
|
||||||
return getCell(x, y).background
|
return getCell(x, y).background
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun foreground(parent: ICellAccess, xOffset: Int = 0, yOffset: Int = 0): TileView {
|
|
||||||
if (xOffset == 0 && yOffset == 0) {
|
|
||||||
return Foreground(parent)
|
|
||||||
} else {
|
|
||||||
return Foreground(OffsetCellAccess(parent, xOffset, yOffset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun foreground(parent: ICellAccess, offset: IStruct2i): TileView {
|
|
||||||
return Foreground(OffsetCellAccess(parent, offset))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun foreground(parent: ICellAccess, offset: ChunkPos): TileView {
|
|
||||||
return Foreground(OffsetCellAccess(parent, offset))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun background(parent: ICellAccess, xOffset: Int = 0, yOffset: Int = 0): TileView {
|
|
||||||
if (xOffset == 0 && yOffset == 0) {
|
|
||||||
return Background(parent)
|
|
||||||
} else {
|
|
||||||
return Background(OffsetCellAccess(parent, xOffset, yOffset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun background(parent: ICellAccess, offset: IStruct2i): TileView {
|
|
||||||
return Background(OffsetCellAccess(parent, offset))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun background(parent: ICellAccess, offset: ChunkPos): TileView {
|
|
||||||
return Background(OffsetCellAccess(parent, offset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
uniform vec2 lightPosition;
|
|
||||||
|
|
||||||
out vec4 resultColor;
|
|
||||||
|
|
||||||
uniform float lightPenetration;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
resultColor = vec4(0.0, 0.0, 0.0, lightPenetration);
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
layout (lines) in;
|
|
||||||
|
|
||||||
layout (triangle_strip, max_vertices = 5) out;
|
|
||||||
|
|
||||||
uniform mat4 transform;
|
|
||||||
uniform vec2 lightPosition;
|
|
||||||
|
|
||||||
in vec2 originalPos[];
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 a = gl_in[0].gl_Position;
|
|
||||||
vec4 b = gl_in[1].gl_Position;
|
|
||||||
|
|
||||||
vec2 aInv = originalPos[0];
|
|
||||||
vec2 bInv = originalPos[1];
|
|
||||||
|
|
||||||
gl_Position = b;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = a;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = transform * vec4(aInv + (aInv - lightPosition) * 10000, 0, 1);
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = transform * vec4(bInv + (bInv - lightPosition) * 10000, 0, 1);
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = b;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
EndPrimitive();
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 vertexPos;
|
|
||||||
|
|
||||||
uniform mat4 transform;
|
|
||||||
uniform mat4 localToWorldTransform;
|
|
||||||
|
|
||||||
uniform float lightPenetration;
|
|
||||||
|
|
||||||
out vec2 originalPos;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
originalPos = (localToWorldTransform * vec4(vertexPos, 0.0, 1.0)).xy;
|
|
||||||
gl_Position = transform * localToWorldTransform * vec4(vertexPos, 0.0, 1.0);
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
out vec4 resultColor;
|
|
||||||
|
|
||||||
in vec4 penumbras;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
resultColor = vec4(1.0, 1.0, 1.0, 0.0);
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
layout (lines) in;
|
|
||||||
|
|
||||||
layout (triangle_strip, max_vertices = 5) out;
|
|
||||||
|
|
||||||
uniform mat4 transform;
|
|
||||||
uniform vec3 lightPositionAndSize;
|
|
||||||
|
|
||||||
in vec2 originalPos[];
|
|
||||||
|
|
||||||
out vec4 penumbras;
|
|
||||||
|
|
||||||
mat2 adjugate(mat2 m) {
|
|
||||||
return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void workWithSegmentForward(float x) {
|
|
||||||
vec2 endpoint_a = originalPos[0];
|
|
||||||
vec2 endpoint_b = originalPos[1];
|
|
||||||
|
|
||||||
vec2 endpoint = mix(endpoint_a, endpoint_b, x);
|
|
||||||
float light_radius = lightPositionAndSize.z;
|
|
||||||
|
|
||||||
// Deltas from the segment to the light center.
|
|
||||||
vec2 delta_a = endpoint_a - lightPositionAndSize.xy;
|
|
||||||
vec2 delta_b = endpoint_b - lightPositionAndSize.xy;
|
|
||||||
vec2 delta = endpoint - lightPositionAndSize.xy;
|
|
||||||
|
|
||||||
// Offsets from the light center to the edge of the light volume.
|
|
||||||
vec2 offset_a = vec2(-light_radius, light_radius) * normalize(delta_a).xy;
|
|
||||||
vec2 offset_b = vec2( light_radius, -light_radius) * normalize(delta_b).xy;
|
|
||||||
vec2 offset = mix(offset_a, offset_b, x);
|
|
||||||
|
|
||||||
vec2 penumbra_a = adjugate(mat2( offset_a, -delta_a))*(delta - mix(offset, delta_a, 0));
|
|
||||||
vec2 penumbra_b = adjugate(mat2(-offset_b, delta_b))*(delta - mix(offset, delta_b, 0));
|
|
||||||
|
|
||||||
// Vertex projection.
|
|
||||||
gl_Position = transform * vec4(delta - offset, 0.0, 1.0);
|
|
||||||
EmitVertex();
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 a = gl_in[0].gl_Position;
|
|
||||||
vec4 b = gl_in[1].gl_Position;
|
|
||||||
|
|
||||||
vec2 aInv = originalPos[0];
|
|
||||||
vec2 bInv = originalPos[1];
|
|
||||||
|
|
||||||
vec2 lightPosition = lightPositionAndSize.xy;
|
|
||||||
float lightSize = lightPositionAndSize.z;
|
|
||||||
|
|
||||||
gl_Position = b;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = a;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
workWithSegmentForward(0);
|
|
||||||
workWithSegmentForward(1);
|
|
||||||
|
|
||||||
EndPrimitive();
|
|
||||||
|
|
||||||
// hard shadow geometry (umbra)
|
|
||||||
//gl_Position = transform * vec4(aInv + (aInv - lightPositionAndSize.xy) * 10000, 0, 1);
|
|
||||||
//EmitVertex();
|
|
||||||
|
|
||||||
//gl_Position = transform * vec4(bInv + (bInv - lightPositionAndSize.xy) * 10000, 0, 1);
|
|
||||||
//EmitVertex();
|
|
||||||
|
|
||||||
// находим направления к вершинам линии
|
|
||||||
/*vec2 deltaA = normalize(aInv - lightPosition);
|
|
||||||
vec2 deltaB = normalize(bInv - lightPosition);
|
|
||||||
|
|
||||||
// находим наклон прямых из центра
|
|
||||||
float degA = acos(deltaA.x);
|
|
||||||
float degB = acos(deltaB.x);
|
|
||||||
|
|
||||||
// корректируем угол в зависимости, находимся ли мы в отрицательном Y
|
|
||||||
if (deltaA.y < 0) {
|
|
||||||
degA = -degA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deltaB.y < 0) {
|
|
||||||
degB = -degB;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if (degA < 0) {
|
|
||||||
degA = 360 + degA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (degB < 0) {
|
|
||||||
degB = 360 + degB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// определяем, находимся ли мы "сзади"
|
|
||||||
if (degA < degB) {
|
|
||||||
vec2 temp = aInv;
|
|
||||||
aInv = bInv;
|
|
||||||
bInv = temp;
|
|
||||||
|
|
||||||
// находим направления к вершинам линии
|
|
||||||
deltaA = normalize(aInv - lightPosition);
|
|
||||||
deltaB = normalize(bInv - lightPosition);
|
|
||||||
|
|
||||||
// находим наклон прямых из центра
|
|
||||||
degA = acos(deltaA.x);
|
|
||||||
degB = acos(deltaB.x);
|
|
||||||
|
|
||||||
// корректируем угол в зависимости, находимся ли мы в отрицательном Y
|
|
||||||
if (deltaA.y < 0) {
|
|
||||||
degA = -degA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deltaB.y < 0) {
|
|
||||||
degB = -degB;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (degA < 0) {
|
|
||||||
degA = 360 + degA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (degB < 0) {
|
|
||||||
degB = 360 + degB;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// поворачиваем наши углы на 90 градусов
|
|
||||||
/*degA -= 1.57079632;
|
|
||||||
degB += 1.57079632;
|
|
||||||
|
|
||||||
// Теперь для каждой вершины вычисляем касательную точку на окружности
|
|
||||||
vec2 pointA = vec2(cos(degA), sin(degA)) * lightSize + lightPosition;
|
|
||||||
vec2 pointB = vec2(cos(degB), sin(degB)) * lightSize + lightPosition;
|
|
||||||
|
|
||||||
// мы нашли касательные точки, теперь просто надо провести прямые начиная от вершин
|
|
||||||
|
|
||||||
// это у нас "hard shadows"
|
|
||||||
gl_Position = transform * vec4(aInv + (aInv - pointA) * 10000, 0, 1);
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = transform * vec4(bInv + (bInv - pointB) * 10000, 0, 1);
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
gl_Position = b;
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
EndPrimitive();*/
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 vertexPos;
|
|
||||||
|
|
||||||
uniform mat4 transform;
|
|
||||||
|
|
||||||
out vec2 originalPos;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = transform * vec4(vertexPos, 0.0, 1.0);
|
|
||||||
originalPos = vertexPos;
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
|
||||||
// https://slembcke.github.io/SuperFastHardShadows
|
|
||||||
// https://slembcke.github.io/SuperFastSoftShadows
|
|
||||||
|
|
||||||
out vec4 resultColor;
|
|
||||||
|
|
||||||
in vec4 v_penumbras;
|
|
||||||
in vec3 v_edges;
|
|
||||||
in vec3 v_proj_pos;
|
|
||||||
in vec4 v_endpoints;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Calculate the light intersection point, but clamp to endpoints to avoid artifacts.
|
|
||||||
float intersection_t = clamp(v_edges.x/abs(v_edges.y), -0.5, 0.5);
|
|
||||||
vec2 intersection_point = (0.5 - intersection_t)*v_endpoints.xy + (0.5 + intersection_t)*v_endpoints.zw;
|
|
||||||
// The delta from the intersection to the pixel.
|
|
||||||
vec2 penetration_delta = intersection_point - v_proj_pos.xy/v_proj_pos.z;
|
|
||||||
// Apply a simple falloff function.
|
|
||||||
float bleed = min(dot(penetration_delta, penetration_delta), 1.0);
|
|
||||||
|
|
||||||
// Penumbra mixing.
|
|
||||||
vec2 penumbras = smoothstep(-1.0, 1.0, v_penumbras.xz/v_penumbras.yw);
|
|
||||||
float penumbra = dot(penumbras, step(v_penumbras.yw, vec2(0.0)));
|
|
||||||
penumbra -= 1.0/64.0; // Numerical precision fudge factor.
|
|
||||||
|
|
||||||
resultColor = vec4(0.0, 0.0, 0.0, bleed * (1.0 - penumbra) * step(v_edges.z, 0.0));
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
|
||||||
// https://slembcke.github.io/SuperFastHardShadows
|
|
||||||
// https://slembcke.github.io/SuperFastSoftShadows
|
|
||||||
|
|
||||||
layout (location = 0) in vec4 packedVertexPos;
|
|
||||||
layout (location = 1) in vec2 edgeType;
|
|
||||||
|
|
||||||
uniform mat4 transform;
|
|
||||||
uniform mat4 localToWorldTransform;
|
|
||||||
|
|
||||||
uniform vec3 lightPositionAndSize;
|
|
||||||
uniform float lightPenetration;
|
|
||||||
|
|
||||||
varying vec4 v_penumbras;
|
|
||||||
varying vec3 v_edges;
|
|
||||||
varying vec3 v_proj_pos;
|
|
||||||
varying vec4 v_endpoints;
|
|
||||||
|
|
||||||
// Keep in mind GLSL is column major. You'll need to swap row/column for HLSL.
|
|
||||||
mat2 adjugate(mat2 m) {
|
|
||||||
return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 lightPosition = lightPositionAndSize.xy;
|
|
||||||
float lightSize = lightPositionAndSize.z;
|
|
||||||
|
|
||||||
// Unpack the vertex shader input.
|
|
||||||
vec2 endpoint_a = (localToWorldTransform * vec4(packedVertexPos.zw, 0.0, 1.0)).xy;
|
|
||||||
vec2 endpoint_b = (localToWorldTransform * vec4(packedVertexPos.xy, 0.0, 1.0)).xy;
|
|
||||||
vec2 endpoint = mix(endpoint_a, endpoint_b, edgeType.x);
|
|
||||||
|
|
||||||
// Deltas from the segment to the light center.
|
|
||||||
vec2 delta_a = endpoint_a - lightPosition;
|
|
||||||
vec2 delta_b = endpoint_b - lightPosition;
|
|
||||||
vec2 delta = endpoint - lightPosition;
|
|
||||||
|
|
||||||
// Offsets from the light center to the edge of the light volume.
|
|
||||||
vec2 offset_a = vec2(-lightSize, lightSize) * normalize(delta_a).yx;
|
|
||||||
vec2 offset_b = vec2( lightSize, -lightSize) * normalize(delta_b).yx;
|
|
||||||
vec2 offset = mix(offset_a, offset_b, edgeType.x);
|
|
||||||
|
|
||||||
// Vertex projection.
|
|
||||||
float w = edgeType.y;
|
|
||||||
vec4 proj_pos = vec4(mix(delta - offset, endpoint, w), 0.0, w);
|
|
||||||
|
|
||||||
gl_Position = transform * proj_pos;
|
|
||||||
|
|
||||||
vec2 penumbra_a = adjugate(mat2( offset_a, -delta_a))*(delta - mix(offset, delta_a, w));
|
|
||||||
vec2 penumbra_b = adjugate(mat2(-offset_b, delta_b))*(delta - mix(offset, delta_b, w));
|
|
||||||
|
|
||||||
v_penumbras = (lightSize > 0.0 ? vec4(penumbra_a, penumbra_b) : vec4(0, 1, 0, 1));
|
|
||||||
|
|
||||||
// Edge values for light penetration and clipping.
|
|
||||||
vec2 seg_delta = endpoint_b - endpoint_a;
|
|
||||||
vec2 seg_normal = seg_delta.yx*vec2(-1.0, 1.0);
|
|
||||||
// Calculate where the light -> pixel ray will intersect with the segment.
|
|
||||||
v_edges.xy = -adjugate(mat2(seg_delta, delta_a + delta_b))*(delta - offset*(1.0 - w));
|
|
||||||
v_edges.y *= 2.0; // Skip a multiply in the fragment shader.
|
|
||||||
// Calculate a clipping coordinate that is 0 at the near edge (when w = 1)...
|
|
||||||
// otherwise calculate the dot product with the projected coordinate.
|
|
||||||
v_edges.z = dot(seg_normal, delta - offset)*(1.0 - w);
|
|
||||||
|
|
||||||
// Light penetration values.
|
|
||||||
v_proj_pos = vec3(proj_pos.xy, w * lightPenetration);
|
|
||||||
v_endpoints = vec4(endpoint_a, endpoint_b) / lightPenetration;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user