Remove gpu light rendering
This commit is contained in:
parent
7a69dee5ca
commit
dc72bf1b18
@ -1,16 +1,9 @@
|
||||
package ru.dbotthepony.kstarbound.client
|
||||
|
||||
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.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.entity.EntityRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
||||
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.entities.Entity
|
||||
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.Vector2f
|
||||
import ru.dbotthepony.kvector.vector.Vector3f
|
||||
import java.io.Closeable
|
||||
import java.util.LinkedList
|
||||
|
||||
@ -47,6 +36,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
var isDirty = true
|
||||
|
||||
fun bake() {
|
||||
if (!isDirty) return
|
||||
isDirty = false
|
||||
|
||||
if (state.isSameThread()) {
|
||||
@ -60,7 +50,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
layers.clear()
|
||||
|
||||
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
|
||||
|
||||
@ -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) {
|
||||
val transform = stack.last()
|
||||
|
||||
@ -111,13 +74,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
}
|
||||
}
|
||||
|
||||
fun autoBake() {
|
||||
if (isDirty) {
|
||||
bake()
|
||||
}
|
||||
}
|
||||
|
||||
fun autoUpload() {
|
||||
fun upload() {
|
||||
if (layers.isNotEmpty) {
|
||||
for (mesh in bakedMeshes) {
|
||||
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() {
|
||||
for (mesh in bakedMeshes) {
|
||||
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)
|
||||
|
||||
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 (к примеру тайлы), с проверкой, изменилось ли что либо,
|
||||
* и загружает её в видеопамять.
|
||||
@ -195,8 +131,8 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
*
|
||||
*/
|
||||
fun bake() {
|
||||
backgroundRenderer.autoBake()
|
||||
foregroundRenderer.autoBake()
|
||||
backgroundRenderer.bake()
|
||||
foregroundRenderer.bake()
|
||||
|
||||
if (state.isSameThread())
|
||||
upload()
|
||||
@ -206,97 +142,8 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
* Загружает в видеопамять всю геометрию напрямую, если есть что загружать
|
||||
*/
|
||||
fun upload() {
|
||||
backgroundRenderer.autoUpload()
|
||||
foregroundRenderer.autoUpload()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
backgroundRenderer.upload()
|
||||
foregroundRenderer.upload()
|
||||
}
|
||||
|
||||
private val liquidTypes = ReferenceArraySet<LiquidDefinition>()
|
||||
@ -317,81 +164,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
return liquidTypes
|
||||
}
|
||||
|
||||
inner class Renderer(val renderOrigin: ChunkPos = pos) : GPULightRenderer.ShadowGeometryRenderer {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class Renderer(val renderOrigin: ChunkPos = pos) {
|
||||
fun addLayers(layers: LayeredRenderer) {
|
||||
for ((baked, zLevel) in backgroundRenderer.bakedMeshes) {
|
||||
layers.add(zLevel + Z_LEVEL_BACKGROUND) {
|
||||
@ -484,9 +257,4 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
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,
|
||||
layers: LayeredRenderer
|
||||
) {
|
||||
client.lightRenderer.begin()
|
||||
|
||||
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
|
||||
val renderer = chunk.second.Renderer(chunk.first)
|
||||
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.input.UserInput
|
||||
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.TextAlignY
|
||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||
@ -34,7 +33,6 @@ import java.nio.ByteOrder
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class StarboundClient(val starbound: Starbound) : Closeable {
|
||||
val time = PausableTimeSource(JVMTimeSource.INSTANCE)
|
||||
@ -188,8 +186,6 @@ class StarboundClient(val starbound: Starbound) : Closeable {
|
||||
viewportMatrixScreen = updateViewportMatrixScreen()
|
||||
viewportMatrixWorld = updateViewportMatrixWorld()
|
||||
|
||||
lightRenderer.resizeFramebuffer(w, h)
|
||||
|
||||
for (callback in onViewportChanged) {
|
||||
callback.invoke(w, h)
|
||||
}
|
||||
@ -230,11 +226,6 @@ class StarboundClient(val starbound: Starbound) : Closeable {
|
||||
val viewportHeight by gl::viewportHeight
|
||||
|
||||
val tileRenderers = TileRenderers(this)
|
||||
val lightRenderer = GPULightRenderer(gl)
|
||||
|
||||
init {
|
||||
lightRenderer.resizeFramebuffer(viewportWidth, viewportHeight)
|
||||
}
|
||||
|
||||
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.GeometryType
|
||||
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.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")) {
|
||||
var color by F4Uniform("color")
|
||||
|
||||
@ -239,8 +212,6 @@ class GLPrograms(val state: GLStateTracker) {
|
||||
val flatColor by lazy { GLFlatColorProgram(state) }
|
||||
val liquid by lazy { GLLiquidProgram(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 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 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) {
|
||||
private enum class TestResult {
|
||||
NO_MATCH,
|
||||
CONTINUE,
|
||||
HALT
|
||||
}
|
||||
|
||||
val state get() = renderers.state
|
||||
val texture = state.loadTexture(def.renderParameters.texture.imagePath.value!!).also {
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
@ -251,7 +251,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
thisBuilder: VertexBuilder,
|
||||
background: Boolean,
|
||||
isModifier: Boolean,
|
||||
): TileRenderTesselateResult {
|
||||
): TestResult {
|
||||
if (matchPiece.test(getter, equalityTester, pos)) {
|
||||
for (renderPiece in matchPiece.pieces) {
|
||||
if (renderPiece.piece.texture != null) {
|
||||
@ -270,19 +270,19 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
for (subPiece in matchPiece.subMatches) {
|
||||
val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier)
|
||||
|
||||
if (matched == TileRenderTesselateResult.HALT || matched == TileRenderTesselateResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
||||
return TileRenderTesselateResult.HALT
|
||||
if (matched == TestResult.HALT || matched == TestResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
||||
return TestResult.HALT
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier)
|
||||
|
||||
if (matched == TileRenderTesselateResult.HALT) {
|
||||
if (matched == TestResult.HALT) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -62,13 +62,13 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
}
|
||||
|
||||
// local cells' tile access
|
||||
val localBackgroundView = TileView.background(this)
|
||||
val localForegroundView = TileView.foreground(this)
|
||||
val localBackgroundView = TileView.Background(this)
|
||||
val localForegroundView = TileView.Foreground(this)
|
||||
|
||||
// relative world cells access (accessing 0, 0 will lookup cell in world, relative to this chunk)
|
||||
val worldView = OffsetCellAccess(world.chunkMap, pos)
|
||||
val worldBackgroundView = TileView.background(worldView)
|
||||
val worldForegroundView = TileView.foreground(worldView)
|
||||
val worldBackgroundView = TileView.Background(worldView)
|
||||
val worldForegroundView = TileView.Foreground(worldView)
|
||||
|
||||
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 body = world.physics.createBody(BodyDef(
|
||||
position = pos.firstTile.toDoubleVector(),
|
||||
position = pos.tile.toDoubleVector(),
|
||||
userData = this
|
||||
))
|
||||
|
||||
|
@ -29,18 +29,10 @@ private fun circulate(value: Int, bounds: Int): Int {
|
||||
class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> {
|
||||
constructor(pos: IStruct2i) : this(pos.component1(), pos.component2())
|
||||
|
||||
val firstTile get() = Vector2i(tileX, tileY)
|
||||
val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SIZE_BITS) - 1, ((y + 1) shl CHUNK_SIZE_BITS) - 1)
|
||||
|
||||
/**
|
||||
* Координата тайла на 0 позиции по оси X внутри чанка в мире
|
||||
*/
|
||||
val tileX: Int get() = x shl CHUNK_SIZE_BITS
|
||||
|
||||
/**
|
||||
* Координата тайла на 0 позиции по оси Y внутри чанка в мире
|
||||
*/
|
||||
val tileY: Int get() = y shl CHUNK_SIZE_BITS
|
||||
// bottom left corner
|
||||
val tileX: Int = x shl CHUNK_SIZE_BITS
|
||||
val tileY: Int = y shl CHUNK_SIZE_BITS
|
||||
val tile = Vector2i(tileX, tileY)
|
||||
|
||||
val top: ChunkPos
|
||||
get() {
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.world
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||
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.IContactFilter
|
||||
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
|
||||
}
|
||||
|
||||
val background = TileView.background(this)
|
||||
val foreground = TileView.foreground(this)
|
||||
val background = TileView.Background(this)
|
||||
val foreground = TileView.Foreground(this)
|
||||
|
||||
abstract operator fun get(x: Int, y: Int): ChunkType?
|
||||
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
|
||||
|
@ -38,7 +38,7 @@ interface 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: ChunkPos) : this(parent, offset.firstTile)
|
||||
constructor(parent: ICellAccess, offset: ChunkPos) : this(parent, offset.tile)
|
||||
|
||||
override fun getCell(x: Int, y: Int): IChunkCell {
|
||||
return parent.getCell(x + this.x, y + this.y)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package ru.dbotthepony.kstarbound.world.api
|
||||
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
|
||||
// for getting tiles directly, avoiding manual layer specification
|
||||
@ -11,49 +10,15 @@ interface ITileAccess : ICellAccess {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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