mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
at least now I know which part is to be patched...
This commit is contained in:
@@ -16,7 +16,7 @@ uniform vec2 screenDimension;
|
||||
uniform vec2 tilesInAxes; // vec2(tiles_in_horizontal, tiles_in_vertical)
|
||||
|
||||
uniform ivec2 tilemapDimension;
|
||||
uniform sampler2D tilemap; // MUST be RGBA8888
|
||||
uniform sampler2D tilemap; // RGB888, A is optional and will be completely ignored
|
||||
|
||||
uniform sampler2D tilesAtlas;
|
||||
uniform sampler2D backgroundTexture;
|
||||
@@ -72,6 +72,9 @@ void main() {
|
||||
highp vec2 finalUVCoordForTile = uvCoordForTile + uvCoordOffset;// where we should be actually looking for in atlas, using UV coord (0..1)
|
||||
|
||||
|
||||
// TODO blend a breakage (0xrrggbb where 0xr0 -- upper 4 bits of int_red component)
|
||||
|
||||
|
||||
if (tileXY.x == 0 && tileXY.y == 0)
|
||||
gl_FragColor = nocolour;
|
||||
else
|
||||
|
||||
533
src/net/torvald/terrarum/FuckingWorldRenderer.kt
Normal file
533
src/net/torvald/terrarum/FuckingWorldRenderer.kt
Normal file
@@ -0,0 +1,533 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.InputAdapter
|
||||
import com.badlogic.gdx.Screen
|
||||
import com.badlogic.gdx.graphics.*
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||
import net.torvald.terrarum.gameactors.*
|
||||
import net.torvald.terrarum.gameactors.ai.ActorAI
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.gameworld.fmod
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.serialise.ReadLayerData
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ui.UITitleRemoConRoot
|
||||
import net.torvald.terrarum.weather.WeatherMixer
|
||||
import net.torvald.terrarum.worlddrawer.*
|
||||
import java.io.FileInputStream
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2017-09-02.
|
||||
*/
|
||||
class FuckingWorldRenderer(val batch: SpriteBatch) : Screen {
|
||||
|
||||
var camera = OrthographicCamera(Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat())
|
||||
|
||||
// invert Y
|
||||
fun initViewPort(width: Int, height: Int) {
|
||||
// Set Y to point downwards
|
||||
camera.setToOrtho(true, width.toFloat(), height.toFloat())
|
||||
|
||||
// Update camera matrix
|
||||
camera.update()
|
||||
|
||||
// Set viewport to restrict drawing
|
||||
Gdx.gl20.glViewport(0, 0, width, height)
|
||||
}
|
||||
|
||||
|
||||
private var loadDone = false
|
||||
|
||||
private lateinit var demoWorld: GameWorld
|
||||
private lateinit var cameraNodes: FloatArray // camera Y-pos
|
||||
private val cameraAI = object : ActorAI {
|
||||
private val axisMax = 1f
|
||||
|
||||
private var firstTime = true
|
||||
|
||||
override fun update(actor: HumanoidNPC, delta: Float) {
|
||||
// fuck
|
||||
val avSpeed = 1.0 // FIXME camera goes faster when FPS is high
|
||||
actor.actorValue[AVKey.SPEED] = avSpeed
|
||||
actor.actorValue[AVKey.ACCEL] = avSpeed / 6.0
|
||||
// end fuck
|
||||
|
||||
|
||||
|
||||
val tileSize = FeaturesDrawer.TILE_SIZE.toFloat()
|
||||
val catmullRomTension = 0f
|
||||
|
||||
// pan camera
|
||||
actor.moveRight(axisMax)
|
||||
|
||||
|
||||
val domainSize = demoWorld.width * tileSize
|
||||
val codomainSize = cameraNodes.size
|
||||
val x = actor.hitbox.canonicalX.toFloat()
|
||||
|
||||
val p1 = (x / (domainSize / codomainSize)).floorInt()
|
||||
val p0 = (p1 - 1) fmod codomainSize
|
||||
val p2 = (p1 + 1) fmod codomainSize
|
||||
val p3 = (p1 + 2) fmod codomainSize
|
||||
val u: Float = 1f - (p2 - (x / (domainSize / codomainSize))) / (p2 - p1)
|
||||
|
||||
//val targetYPos = FastMath.interpolateCatmullRom(u, catmullRomTension, cameraNodes[p0], cameraNodes[p1], cameraNodes[p2], cameraNodes[p3])
|
||||
val targetYPos = FastMath.interpolateLinear(u, cameraNodes[p1], cameraNodes[p2])
|
||||
val yDiff = targetYPos - actor.hitbox.canonicalY
|
||||
|
||||
/*if (!firstTime) {
|
||||
actor.moveDown(yDiff.bipolarClamp(axisMax.toDouble()).toFloat())
|
||||
}
|
||||
else {
|
||||
actor.hitbox.setPosition(actor.hitbox.canonicalX, targetYPos.toDouble())
|
||||
firstTime = false
|
||||
}*/
|
||||
actor.hitbox.setPosition(actor.hitbox.canonicalX, targetYPos.toDouble()) // just move the cameraY to interpolated path
|
||||
|
||||
|
||||
//println("${actor.hitbox.canonicalX}, ${actor.hitbox.canonicalY}")
|
||||
}
|
||||
}
|
||||
private lateinit var cameraPlayer: HumanoidNPC
|
||||
|
||||
private val gradWhiteTop = Color(0xf8f8f8ff.toInt())
|
||||
private val gradWhiteBottom = Color(0xd8d8d8ff.toInt())
|
||||
|
||||
private val lightFBOformat = Pixmap.Format.RGB888
|
||||
var lightmapFboA = FrameBuffer(lightFBOformat, Terrarum.WIDTH.div(Ingame.lightmapDownsample.toInt()), Terrarum.HEIGHT.div(Ingame.lightmapDownsample.toInt()), false)
|
||||
var lightmapFboB = FrameBuffer(lightFBOformat, Terrarum.WIDTH.div(Ingame.lightmapDownsample.toInt()), Terrarum.HEIGHT.div(Ingame.lightmapDownsample.toInt()), false)
|
||||
|
||||
lateinit var logo: TextureRegion
|
||||
|
||||
val uiContainer = ArrayList<UICanvas>()
|
||||
private lateinit var uiMenu: UICanvas
|
||||
|
||||
private lateinit var worldFBO: FrameBuffer
|
||||
|
||||
private fun loadThingsWhileIntroIsVisible() {
|
||||
demoWorld = ReadLayerData(FileInputStream(ModMgr.getFile("basegame", "demoworld")))
|
||||
|
||||
|
||||
// construct camera nodes
|
||||
val nodeCount = 100
|
||||
cameraNodes = kotlin.FloatArray(nodeCount, { it ->
|
||||
val tileXPos = (demoWorld.width.toFloat() * it / nodeCount).floorInt()
|
||||
var travelDownCounter = 0
|
||||
while (!BlockCodex[demoWorld.getTileFromTerrain(tileXPos, travelDownCounter)].isSolid) {
|
||||
travelDownCounter += 4
|
||||
}
|
||||
travelDownCounter * FeaturesDrawer.TILE_SIZE.toFloat()
|
||||
})
|
||||
|
||||
|
||||
cameraPlayer = object : HumanoidNPC(demoWorld, cameraAI, GameDate(1, 1), usePhysics = false) {
|
||||
init {
|
||||
setHitboxDimension(2, 2, 0, 0)
|
||||
hitbox.setPosition(
|
||||
HQRNG().nextInt(demoWorld.width) * FeaturesDrawer.TILE_SIZE.toDouble(),
|
||||
0.0 // placeholder; camera AI will take it over
|
||||
)
|
||||
noClip = true
|
||||
}
|
||||
}
|
||||
|
||||
demoWorld.time.timeDelta = 150
|
||||
|
||||
|
||||
LightmapRendererNew.world = demoWorld
|
||||
BlocksDrawer.world = demoWorld
|
||||
FeaturesDrawer.world = demoWorld
|
||||
|
||||
|
||||
uiMenu = UITitleRemoConRoot()
|
||||
uiMenu.setPosition(0, 0)
|
||||
uiMenu.setAsOpen()
|
||||
|
||||
|
||||
uiContainer.add(uiMenu)
|
||||
|
||||
loadDone = true
|
||||
}
|
||||
|
||||
|
||||
override fun hide() {
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT)
|
||||
|
||||
logo = TextureRegion(Texture(Gdx.files.internal("assets/graphics/logo_placeholder.tga")))
|
||||
logo.flip(false, true)
|
||||
|
||||
|
||||
Gdx.input.inputProcessor = TitleScreenController(this)
|
||||
|
||||
|
||||
worldFBO = FrameBuffer(Pixmap.Format.RGBA8888, Terrarum.WIDTH, Terrarum.HEIGHT, false)
|
||||
}
|
||||
|
||||
private var blurWriteBuffer = lightmapFboA
|
||||
private var blurReadBuffer = lightmapFboB
|
||||
|
||||
private val introUncoverTime: Second = 0.3f
|
||||
private var introUncoverDeltaCounter = 0f
|
||||
private var updateDeltaCounter = 0.0
|
||||
protected val updateRate = 1.0 / Terrarum.TARGET_INTERNAL_FPS
|
||||
|
||||
override fun render(delta: Float) {
|
||||
if (!loadDone) {
|
||||
loadThingsWhileIntroIsVisible()
|
||||
}
|
||||
else {
|
||||
// async update
|
||||
updateDeltaCounter += delta
|
||||
while (updateDeltaCounter >= updateRate) {
|
||||
updateScreen(delta)
|
||||
updateDeltaCounter -= updateRate
|
||||
}
|
||||
|
||||
// render? just do it anyway
|
||||
renderScreen()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun updateScreen(delta: Float) {
|
||||
Gdx.graphics.setTitle(TerrarumAppLoader.GAME_NAME +
|
||||
" — F: ${Gdx.graphics.framesPerSecond} (${Terrarum.TARGET_INTERNAL_FPS})" +
|
||||
" — M: ${Terrarum.memInUse}M / ${Terrarum.memTotal}M / ${Terrarum.memXmx}M"
|
||||
)
|
||||
|
||||
demoWorld.globalLight = WeatherMixer.globalLightNow
|
||||
//demoWorld.updateWorldTime(delta)
|
||||
WeatherMixer.update(delta, cameraPlayer)
|
||||
cameraPlayer.update(delta)
|
||||
// worldcamera update AFTER cameraplayer in this case; the other way is just an exception for actual ingame SFX
|
||||
WorldCamera.update(demoWorld, cameraPlayer)
|
||||
|
||||
|
||||
// update UIs //
|
||||
uiContainer.forEach { it.update(delta) }
|
||||
|
||||
|
||||
if (Terrarum.GLOBAL_RENDER_TIMER % 2 == 1) {
|
||||
LightmapRendererNew.fireRecalculateEvent()
|
||||
}
|
||||
}
|
||||
|
||||
fun renderScreen() {
|
||||
|
||||
// render and blur lightmap
|
||||
processBlur(LightmapRendererNew.DRAW_FOR_RGB)
|
||||
//camera.setToOrtho(true, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat())
|
||||
|
||||
// render world
|
||||
Gdx.gl.glClearColor(.64f, .754f, .84f, 1f)
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
|
||||
//BlocksDrawer.renderWall(batch)
|
||||
//BlocksDrawer.renderTerrain(batch)
|
||||
//BlocksDrawer.renderFront(batch, false)
|
||||
|
||||
|
||||
batch.inUse {
|
||||
setCameraPosition(0f, 0f)
|
||||
batch.shader = null
|
||||
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
renderDemoWorld()
|
||||
|
||||
batch.shader = null
|
||||
batch.color = Color.WHITE
|
||||
renderMenus()
|
||||
//renderOverlayTexts()
|
||||
}
|
||||
|
||||
|
||||
/*batch.inUse {
|
||||
setCameraPosition(0f, 0f)
|
||||
batch.color = Color.WHITE
|
||||
batch.shader = null
|
||||
camera.position.set(WorldCamera.gdxCamX, WorldCamera.gdxCamY, 0f) // make camara work
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
batch.color = Color.WHITE
|
||||
blendNormal()
|
||||
|
||||
|
||||
|
||||
renderDemoWorld()
|
||||
|
||||
renderMenus()
|
||||
|
||||
renderOverlayTexts()
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun renderDemoWorld() {
|
||||
// draw skybox //
|
||||
|
||||
setCameraPosition(0f, 0f)
|
||||
batch.color = Color.WHITE
|
||||
blendNormal()
|
||||
WeatherMixer.render(camera, demoWorld)
|
||||
|
||||
|
||||
// draw tiles //
|
||||
|
||||
blendNormal()
|
||||
|
||||
|
||||
BlocksDrawer.renderWall(batch)
|
||||
BlocksDrawer.renderTerrain(batch)
|
||||
|
||||
|
||||
FeaturesDrawer.drawEnvOverlay(batch)
|
||||
|
||||
|
||||
// draw lightmap //
|
||||
setCameraPosition(0f, 0f)
|
||||
batch.shader = Terrarum.shaderBayer
|
||||
batch.shader.setUniformf("rcount", 64f)
|
||||
batch.shader.setUniformf("gcount", 64f)
|
||||
batch.shader.setUniformf("bcount", 64f) // de-banding
|
||||
val lightTex = blurWriteBuffer.colorBufferTexture
|
||||
lightTex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
|
||||
//blendMul()
|
||||
blendNormal()
|
||||
batch.color = Color.WHITE
|
||||
batch.draw(logo.texture,//lightTex,
|
||||
0f, 0f,
|
||||
//lightTex.width * Ingame.lightmapDownsample, lightTex.height * Ingame.lightmapDownsample
|
||||
lightTex.width.toFloat(), lightTex.height.toFloat()
|
||||
)
|
||||
|
||||
batch.shader = null
|
||||
|
||||
|
||||
// move camera back to its former position
|
||||
// using custom code for camera; this is obscure and tricky
|
||||
camera.position.set(WorldCamera.gdxCamX, WorldCamera.gdxCamY, 0f) // make camara work
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
|
||||
|
||||
|
||||
// FIXME failed test tells that something is interfering with SpriteBatch, which makes every draw call within
|
||||
// the spritebatch all go black
|
||||
}
|
||||
|
||||
private fun renderMenus() {
|
||||
setCameraPosition(0f, 0f)
|
||||
blendNormal()
|
||||
batch.shader = null
|
||||
|
||||
|
||||
uiContainer.forEach { it.render(batch, camera) }
|
||||
}
|
||||
|
||||
private fun renderOverlayTexts() {
|
||||
setCameraPosition(0f, 0f)
|
||||
blendNormal()
|
||||
batch.shader = null
|
||||
|
||||
batch.color = Color.LIGHT_GRAY
|
||||
|
||||
val COPYTING = arrayOf(
|
||||
TerrarumAppLoader.COPYRIGHT_DATE_NAME,
|
||||
Lang["COPYRIGHT_GNU_GPL_3"]
|
||||
)
|
||||
|
||||
COPYTING.forEachIndexed { index, s ->
|
||||
val textWidth = Terrarum.fontGame.getWidth(s)
|
||||
Terrarum.fontGame.draw(batch, s,
|
||||
Terrarum.WIDTH - textWidth - 1f - 0.2f,
|
||||
Terrarum.HEIGHT - Terrarum.fontGame.lineHeight * (COPYTING.size - index) - 1f
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pause() {
|
||||
}
|
||||
|
||||
override fun resume() {
|
||||
}
|
||||
|
||||
override fun resize(width: Int, height: Int) {
|
||||
// Set up viewport when window is resized
|
||||
initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT)
|
||||
|
||||
BlocksDrawer.resize(Terrarum.WIDTH, Terrarum.HEIGHT)
|
||||
LightmapRendererNew.resize(Terrarum.WIDTH, Terrarum.HEIGHT)
|
||||
|
||||
if (loadDone) {
|
||||
// resize UI by re-creating it (!!)
|
||||
uiMenu.resize(Terrarum.WIDTH, Terrarum.HEIGHT)
|
||||
uiMenu.setPosition(0, UITitleRemoConRoot.menubarOffY)
|
||||
}
|
||||
|
||||
lightmapFboA.dispose()
|
||||
lightmapFboA = FrameBuffer(lightFBOformat, Terrarum.WIDTH.div(Ingame.lightmapDownsample.toInt()), Terrarum.HEIGHT.div(Ingame.lightmapDownsample.toInt()), false)
|
||||
lightmapFboB.dispose()
|
||||
lightmapFboB = FrameBuffer(lightFBOformat, Terrarum.WIDTH.div(Ingame.lightmapDownsample.toInt()), Terrarum.HEIGHT.div(Ingame.lightmapDownsample.toInt()), false)
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
logo.texture.dispose()
|
||||
lightmapFboA.dispose()
|
||||
lightmapFboB.dispose()
|
||||
|
||||
uiMenu.dispose()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun setCameraPosition(newX: Float, newY: Float) {
|
||||
Ingame.setCameraPosition(batch, camera, newX, newY)
|
||||
}
|
||||
|
||||
|
||||
fun processBlur(mode: Int) {
|
||||
val blurIterations = 5 // ideally, 4 * radius; must be even/odd number -- odd/even number will flip the image
|
||||
val blurRadius = 4f / Ingame.lightmapDownsample // (5, 4f); using low numbers for pixel-y aesthetics
|
||||
|
||||
blurWriteBuffer = lightmapFboA
|
||||
blurReadBuffer = lightmapFboB
|
||||
|
||||
|
||||
lightmapFboA.inAction(null, null) {
|
||||
Gdx.gl.glClearColor(0f, 0f, 0f, 0f)
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
}
|
||||
lightmapFboB.inAction(null, null) {
|
||||
Gdx.gl.glClearColor(0f, 0f, 0f, 0f)
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
}
|
||||
|
||||
|
||||
if (mode == LightmapRendererNew.DRAW_FOR_RGB) {
|
||||
// initialise readBuffer with untreated lightmap
|
||||
blurReadBuffer.inAction(camera, batch) {
|
||||
batch.inUse {
|
||||
// using custom code for camera; this is obscure and tricky
|
||||
camera.position.set(
|
||||
(WorldCamera.gdxCamX / Ingame.lightmapDownsample).round(),
|
||||
(WorldCamera.gdxCamY / Ingame.lightmapDownsample).round(),
|
||||
0f
|
||||
) // make camara work
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
|
||||
|
||||
blendNormal()
|
||||
batch.color = Color.WHITE
|
||||
LightmapRendererNew.draw(batch, LightmapRendererNew.DRAW_FOR_RGB)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// initialise readBuffer with untreated lightmap
|
||||
blurReadBuffer.inAction(camera, batch) {
|
||||
batch.inUse {
|
||||
// using custom code for camera; this is obscure and tricky
|
||||
camera.position.set(
|
||||
(WorldCamera.gdxCamX / Ingame.lightmapDownsample).round(),
|
||||
(WorldCamera.gdxCamY / Ingame.lightmapDownsample).round(),
|
||||
0f
|
||||
) // make camara work
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
|
||||
|
||||
blendNormal()
|
||||
batch.color = Color.WHITE
|
||||
LightmapRendererNew.draw(batch, LightmapRendererNew.DRAW_FOR_ALPHA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (i in 0 until blurIterations) {
|
||||
blurWriteBuffer.inAction(camera, batch) {
|
||||
|
||||
batch.inUse {
|
||||
val texture = blurReadBuffer.colorBufferTexture
|
||||
|
||||
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
|
||||
|
||||
|
||||
batch.shader = Terrarum.shaderBlur
|
||||
batch.shader.setUniformf("iResolution",
|
||||
blurWriteBuffer.width.toFloat(), blurWriteBuffer.height.toFloat())
|
||||
batch.shader.setUniformf("flip", 1f)
|
||||
if (i % 2 == 0)
|
||||
batch.shader.setUniformf("direction", blurRadius, 0f)
|
||||
else
|
||||
batch.shader.setUniformf("direction", 0f, blurRadius)
|
||||
|
||||
|
||||
batch.color = Color.WHITE
|
||||
batch.draw(texture, 0f, 0f)
|
||||
|
||||
|
||||
// swap
|
||||
val t = blurWriteBuffer
|
||||
blurWriteBuffer = blurReadBuffer
|
||||
blurReadBuffer = t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class TitleScreenController(val screen: FuckingWorldRenderer) : InputAdapter() {
|
||||
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun mouseMoved(screenX: Int, screenY: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.mouseMoved(screenX, screenY) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun keyTyped(character: Char): Boolean {
|
||||
screen.uiContainer.forEach { it.keyTyped(character) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun scrolled(amount: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.scrolled(amount) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun keyUp(keycode: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.keyUp(keycode) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.touchDragged(screenX, screenY, pointer) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun keyDown(keycode: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.keyDown(keycode) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
screen.uiContainer.forEach { it.touchDown(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,6 +252,8 @@ object Terrarum : Screen {
|
||||
lateinit var shaderBayer: ShaderProgram
|
||||
lateinit var shaderBayerSkyboxFill: ShaderProgram
|
||||
lateinit var shaderBlendGlow: ShaderProgram
|
||||
lateinit var shaderRGBOnly: ShaderProgram
|
||||
lateinit var shaderAtoGrey: ShaderProgram
|
||||
|
||||
|
||||
lateinit var textureWhiteSquare: Texture
|
||||
@@ -399,9 +401,12 @@ object Terrarum : Screen {
|
||||
|
||||
shaderBayerSkyboxFill = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/4096_bayer_skyboxfill.frag"))
|
||||
|
||||
|
||||
shaderBlendGlow = ShaderProgram(Gdx.files.internal("assets/blendGlow.vert"), Gdx.files.internal("assets/blendGlow.frag"))
|
||||
|
||||
shaderRGBOnly = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/rgbonly.frag"))
|
||||
shaderAtoGrey = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/aonly.frag"))
|
||||
|
||||
|
||||
if (!shaderBlendGlow.isCompiled) {
|
||||
Gdx.app.log("shaderBlendGlow", shaderBlendGlow.log)
|
||||
System.exit(1)
|
||||
@@ -419,6 +424,9 @@ object Terrarum : Screen {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
gameLocale = getConfigString("language")
|
||||
println("[Terrarum] locale = $gameLocale")
|
||||
|
||||
@@ -440,7 +448,8 @@ object Terrarum : Screen {
|
||||
|
||||
|
||||
// title screen
|
||||
appLoader.setScreen(TitleScreen(batch))
|
||||
//appLoader.setScreen(TitleScreen(batch))
|
||||
appLoader.setScreen(FuckingWorldRenderer(batch))
|
||||
}
|
||||
|
||||
internal fun setScreen(screen: Screen) {
|
||||
|
||||
@@ -109,6 +109,8 @@ class TitleScreen(val batch: SpriteBatch) : Screen {
|
||||
val uiContainer = ArrayList<UICanvas>()
|
||||
private lateinit var uiMenu: UICanvas
|
||||
|
||||
private lateinit var worldFBO: FrameBuffer
|
||||
|
||||
private fun loadThingsWhileIntroIsVisible() {
|
||||
demoWorld = ReadLayerData(FileInputStream(ModMgr.getFile("basegame", "demoworld")))
|
||||
|
||||
@@ -166,6 +168,9 @@ class TitleScreen(val batch: SpriteBatch) : Screen {
|
||||
|
||||
|
||||
Gdx.input.inputProcessor = TitleScreenController(this)
|
||||
|
||||
|
||||
worldFBO = FrameBuffer(Pixmap.Format.RGBA8888, Terrarum.WIDTH, Terrarum.HEIGHT, false)
|
||||
}
|
||||
|
||||
private var blurWriteBuffer = lightmapFboA
|
||||
@@ -224,12 +229,28 @@ class TitleScreen(val batch: SpriteBatch) : Screen {
|
||||
//camera.setToOrtho(true, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat())
|
||||
|
||||
// render world
|
||||
Gdx.gl.glClearColor(.64f, .754f, .84f, 0f)
|
||||
Gdx.gl.glClearColor(.64f, .754f, .84f, 1f)
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
BlocksDrawer.renderWall(batch)
|
||||
BlocksDrawer.renderTerrain(batch)
|
||||
|
||||
//BlocksDrawer.renderWall(batch)
|
||||
//BlocksDrawer.renderTerrain(batch)
|
||||
//BlocksDrawer.renderFront(batch, false)
|
||||
|
||||
|
||||
batch.inUse {
|
||||
setCameraPosition(0f, 0f)
|
||||
batch.shader = null
|
||||
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
renderDemoWorld()
|
||||
|
||||
batch.shader = null
|
||||
batch.color = Color.WHITE
|
||||
renderMenus()
|
||||
//renderOverlayTexts()
|
||||
}
|
||||
|
||||
|
||||
/*batch.inUse {
|
||||
setCameraPosition(0f, 0f)
|
||||
batch.color = Color.WHITE
|
||||
@@ -261,26 +282,13 @@ class TitleScreen(val batch: SpriteBatch) : Screen {
|
||||
|
||||
// draw tiles //
|
||||
|
||||
// using custom code for camera; this is obscure and tricky
|
||||
camera.position.set(WorldCamera.gdxCamX, WorldCamera.gdxCamY, 0f) // make camara work
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
batch.shader = null
|
||||
|
||||
blendNormal()
|
||||
|
||||
batch.end()
|
||||
|
||||
|
||||
|
||||
BlocksDrawer.renderWall(batch)
|
||||
BlocksDrawer.renderTerrain(batch)
|
||||
BlocksDrawer.renderFront(batch, false)
|
||||
|
||||
|
||||
|
||||
batch.begin()
|
||||
|
||||
FeaturesDrawer.drawEnvOverlay(batch)
|
||||
|
||||
|
||||
|
||||
@@ -381,30 +381,31 @@ object BlocksDrawer {
|
||||
///////////////////////////////////////////
|
||||
|
||||
fun renderWall(batch: SpriteBatch) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
blendNormal()
|
||||
// blend normal
|
||||
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
drawTiles(WALL, false)
|
||||
renderUsingBuffer(WALL, batch.projectionMatrix)
|
||||
}
|
||||
|
||||
fun renderTerrain(batch: SpriteBatch) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
blendNormal()
|
||||
// blend normal
|
||||
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
drawTiles(TERRAIN, false) // regular tiles
|
||||
renderUsingBuffer(TERRAIN, batch.projectionMatrix)
|
||||
}
|
||||
|
||||
@Deprecated("It's broken right now; don't use it")
|
||||
fun renderFront(batch: SpriteBatch, drawWires: Boolean) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
blendMul()
|
||||
// blend mul
|
||||
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
drawTiles(TERRAIN, true) // blendmul tiles
|
||||
renderUsingBuffer(TERRAIN, batch.projectionMatrix)
|
||||
@@ -414,7 +415,10 @@ object BlocksDrawer {
|
||||
renderUsingBuffer(WIRE, batch.projectionMatrix)
|
||||
}
|
||||
|
||||
blendNormal()
|
||||
// blend normal
|
||||
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
|
||||
private val tileDrawLightThreshold = 2f / LightmapRenderer.MUL
|
||||
@@ -698,12 +702,9 @@ object BlocksDrawer {
|
||||
//Gdx.gl.glClearColor(.094f, .094f, .094f, 0f)
|
||||
//Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
|
||||
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
||||
Gdx.gl.glEnable(GL20.GL_BLEND)
|
||||
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
val tilesInHorizontal = tilesBuffer.width
|
||||
val tilesInVertical = tilesBuffer.height
|
||||
//val tilesInHorizontal = tilesBuffer.width
|
||||
//val tilesInVertical = tilesBuffer.height
|
||||
|
||||
|
||||
val tileAtlas = when (mode) {
|
||||
@@ -759,9 +760,12 @@ object BlocksDrawer {
|
||||
private var oldScreenW = 0
|
||||
private var oldScreenH = 0
|
||||
|
||||
var tilesInHorizontal = -1; private set
|
||||
var tilesInVertical = -1; private set
|
||||
|
||||
fun resize(screenW: Int, screenH: Int) {
|
||||
val tilesInHorizontal = (screenW.toFloat() / TILE_SIZE).ceilInt() + 1
|
||||
val tilesInVertical = (screenH.toFloat() / TILE_SIZE).ceilInt() + 1
|
||||
tilesInHorizontal = (screenW.toFloat() / TILE_SIZE).ceilInt() + 1
|
||||
tilesInVertical = (screenH.toFloat() / TILE_SIZE).ceilInt() + 1
|
||||
|
||||
val oldTH = (oldScreenW.toFloat() / TILE_SIZE).ceilInt() + 1
|
||||
val oldTV = (oldScreenH.toFloat() / TILE_SIZE).ceilInt() + 1
|
||||
@@ -772,7 +776,7 @@ object BlocksDrawer {
|
||||
wallTilesBuffer = Array<IntArray>(tilesInVertical, { kotlin.IntArray(tilesInHorizontal) })
|
||||
wireTilesBuffer = Array<IntArray>(tilesInVertical, { kotlin.IntArray(tilesInHorizontal) })
|
||||
|
||||
tilesBuffer = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGBA8888)
|
||||
tilesBuffer = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGB888)
|
||||
}
|
||||
|
||||
if (oldScreenW != screenW || oldScreenH != screenH) {
|
||||
|
||||
706
src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt
Normal file
706
src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt
Normal file
@@ -0,0 +1,706 @@
|
||||
package net.torvald.terrarum.worlddrawer
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.Ingame
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.blockproperties.Block
|
||||
import net.torvald.terrarum.gameactors.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Warning: you are not going to store float value to the lightmap -- see RGB_HDR_LUT (beziér)
|
||||
*
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
|
||||
//typealias RGB10 = Int
|
||||
|
||||
// NOTE: no Float16 on this thing: 67 kB of memory footage is totally acceptable
|
||||
|
||||
object LightmapRendererNew {
|
||||
lateinit var world: GameWorld
|
||||
|
||||
|
||||
// TODO if (VBO works on BlocksDrawer) THEN overscan of 256, utilise same technique in here
|
||||
|
||||
val overscan_open: Int = 32
|
||||
val overscan_opaque: Int = 8
|
||||
|
||||
init {
|
||||
println("[LightmapRenderer] Overscan open: $overscan_open; opaque: $overscan_opaque")
|
||||
}
|
||||
|
||||
// TODO resize(int, int) -aware
|
||||
|
||||
val LIGHTMAP_WIDTH = (Terrarum.ingame?.ZOOM_MINIMUM ?: 1f).inv().times(Terrarum.WIDTH)
|
||||
.div(FeaturesDrawer.TILE_SIZE).ceil() + overscan_open * 2 + 3
|
||||
val LIGHTMAP_HEIGHT = (Terrarum.ingame?.ZOOM_MINIMUM ?: 1f).inv().times(Terrarum.HEIGHT)
|
||||
.div(FeaturesDrawer.TILE_SIZE).ceil() + overscan_open * 2 + 3
|
||||
|
||||
/**
|
||||
* Float value, 1.0 for 1023
|
||||
*/
|
||||
// it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light)
|
||||
private val lightmap: Array<Array<Color>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH, { Color(0f,0f,0f,0f) }) } // TODO framebuffer?
|
||||
private val lanternMap = ArrayList<Lantern>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
|
||||
|
||||
private val AIR = Block.AIR
|
||||
|
||||
private const val TILE_SIZE = FeaturesDrawer.TILE_SIZE
|
||||
private val DRAW_TILE_SIZE: Float = FeaturesDrawer.TILE_SIZE / Ingame.lightmapDownsample
|
||||
|
||||
// color model related constants
|
||||
const val MUL = 1024 // modify this to 1024 to implement 30-bit RGB
|
||||
const val CHANNEL_MAX_DECIMAL = 1f
|
||||
const val MUL_2 = MUL * MUL
|
||||
const val CHANNEL_MAX = MUL - 1
|
||||
const val CHANNEL_MAX_FLOAT = CHANNEL_MAX.toFloat()
|
||||
const val COLOUR_RANGE_SIZE = MUL * MUL_2
|
||||
const val MUL_FLOAT = MUL / 256f
|
||||
const val DIV_FLOAT = 256f / MUL
|
||||
|
||||
internal var for_x_start: Int = 0
|
||||
internal var for_y_start: Int = 0
|
||||
internal var for_x_end: Int = 0
|
||||
internal var for_y_end: Int = 0
|
||||
|
||||
|
||||
//inline fun getLightRawPos(x: Int, y: Int) = lightmap[y][x]
|
||||
|
||||
|
||||
/**
|
||||
* Conventional level (multiplied by four)
|
||||
*
|
||||
* @param x world tile coord
|
||||
* @param y world tile coord
|
||||
*/
|
||||
fun getLight(x: Int, y: Int): Color? {
|
||||
val col = getLightInternal(x, y)
|
||||
if (col == null) {
|
||||
return null
|
||||
}
|
||||
else {
|
||||
return Color(col.r * MUL_FLOAT, col.g * MUL_FLOAT, col.b * MUL_FLOAT, col.a * MUL_FLOAT)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal level (0..1)
|
||||
*
|
||||
* @param x world tile coord
|
||||
* @param y world tile coord
|
||||
*/
|
||||
private fun getLightInternal(x: Int, y: Int): Color? {
|
||||
if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
|
||||
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) {
|
||||
|
||||
return lightmap[y - for_y_start + overscan_open][x - for_x_start + overscan_open]
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun setLight(x: Int, y: Int, colour: Color) {
|
||||
if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
|
||||
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) {
|
||||
|
||||
lightmap[y - for_y_start + overscan_open][x - for_x_start + overscan_open] = colour
|
||||
}
|
||||
}
|
||||
|
||||
fun fireRecalculateEvent() {
|
||||
for_x_start = WorldCamera.x / TILE_SIZE - 1 // fix for premature lightmap rendering
|
||||
for_y_start = WorldCamera.y / TILE_SIZE - 1 // on topmost/leftmost side
|
||||
|
||||
for_x_end = for_x_start + WorldCamera.width / TILE_SIZE + 3
|
||||
for_y_end = for_y_start + WorldCamera.height / TILE_SIZE + 2 // same fix as above
|
||||
|
||||
/**
|
||||
* Updating order:
|
||||
* +--------+ +--+-----+ +-----+--+ +--------+ -
|
||||
* |↘ | | | 3| |3 | | | ↙| ↕︎ overscan_open / overscan_opaque
|
||||
* | +-----+ | | 2 | | 2 | | +-----+ | - depending on the noop_mask
|
||||
* | |1 | → | |1 | → | 1| | → | 1| |
|
||||
* | | 2 | | +-----+ +-----+ | | 2 | |
|
||||
* | | 3| |↗ | | ↖| |3 | |
|
||||
* +--+-----+ +--------+ +--------+ +-----+--+
|
||||
* round: 1 2 3 4
|
||||
* for all lightmap[y][x]
|
||||
*/
|
||||
|
||||
buildLanternmap()
|
||||
|
||||
// O(36n) == O(n) where n is a size of the map.
|
||||
// Because of inevitable overlaps on the area, it only works with ADDITIVE blend (aka maxblend)
|
||||
|
||||
|
||||
// Round 1
|
||||
for (y in for_y_start - overscan_open..for_y_end) {
|
||||
for (x in for_x_start - overscan_open..for_x_end) {
|
||||
setLight(x, y, calculate(x, y, 1))
|
||||
}
|
||||
}
|
||||
|
||||
// Round 2
|
||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||
for (x in for_x_start - overscan_open..for_x_end) {
|
||||
setLight(x, y, calculate(x, y, 2))
|
||||
}
|
||||
}
|
||||
|
||||
// Round 3
|
||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||
for (x in for_x_end + overscan_open downTo for_x_start) {
|
||||
setLight(x, y, calculate(x, y, 3))
|
||||
}
|
||||
}
|
||||
|
||||
// Round 4
|
||||
for (y in for_y_start - overscan_open..for_y_end) {
|
||||
for (x in for_x_end + overscan_open downTo for_x_start) {
|
||||
setLight(x, y, calculate(x, y, 4))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLanternmap() {
|
||||
lanternMap.clear()
|
||||
Terrarum.ingame?.let {
|
||||
it.actorContainer.forEach { it ->
|
||||
if (it is Luminous && it is ActorWithPhysics) {
|
||||
// put lanterns to the area the luminantBox is occupying
|
||||
for (lightBox in it.lightBoxList) {
|
||||
val lightBoxX = it.hitbox.startX + lightBox.startX
|
||||
val lightBoxY = it.hitbox.startY + lightBox.startY
|
||||
val lightBoxW = lightBox.width
|
||||
val lightBoxH = lightBox.height
|
||||
for (y in lightBoxY.div(TILE_SIZE).floorInt()
|
||||
..lightBoxY.plus(lightBoxH).div(TILE_SIZE).floorInt()) {
|
||||
for (x in lightBoxX.div(TILE_SIZE).floorInt()
|
||||
..lightBoxX.plus(lightBoxW).div(TILE_SIZE).floorInt()) {
|
||||
|
||||
val normalisedColor = it.color.cpy().mul(DIV_FLOAT)
|
||||
|
||||
lanternMap.add(Lantern(x, y, normalisedColor))
|
||||
// Q&D fix for Roundworld anomaly
|
||||
lanternMap.add(Lantern(x + world.width, y, normalisedColor))
|
||||
lanternMap.add(Lantern(x - world.width, y, normalisedColor))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private var ambientAccumulator = Color(0f,0f,0f,0f)
|
||||
private var lightLevelThis = Color(0f,0f,0f,0f)
|
||||
private var thisTerrain = 0
|
||||
private var thisWall = 0
|
||||
private var thisTileLuminosity = Color(0f,0f,0f,0f)
|
||||
private var thisTileOpacity = Color(0f,0f,0f,0f)
|
||||
private var sunLight = Color(0f,0f,0f,0f)
|
||||
|
||||
|
||||
private fun calculate(x: Int, y: Int, pass: Int): Color = calculate(x, y, pass, false)
|
||||
|
||||
private fun calculate(x: Int, y: Int, pass: Int, doNotCalculateAmbient: Boolean): Color {
|
||||
// O(9n) == O(n) where n is a size of the map
|
||||
// TODO devise multithreading on this
|
||||
|
||||
ambientAccumulator = Color(0f,0f,0f,0f)
|
||||
|
||||
lightLevelThis = Color(0f,0f,0f,0f)
|
||||
thisTerrain = world.getTileFromTerrain(x, y) ?: Block.STONE
|
||||
thisWall = world.getTileFromWall(x, y) ?: Block.STONE
|
||||
thisTileLuminosity = BlockCodex[thisTerrain].luminosity // already been div by four
|
||||
thisTileOpacity = BlockCodex[thisTerrain].opacity // already been div by four
|
||||
sunLight = world.globalLight.cpy().mul(DIV_FLOAT)
|
||||
|
||||
|
||||
// MIX TILE
|
||||
// open air
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightLevelThis = sunLight
|
||||
}
|
||||
// luminous tile on top of air
|
||||
else if (thisWall == AIR && thisTileLuminosity.nonZero()) {
|
||||
lightLevelThis = sunLight maxBlend thisTileLuminosity // maximise to not exceed 1.0 with normal (<= 1.0) light
|
||||
}
|
||||
// opaque wall and luminous tile
|
||||
else if (thisWall != AIR && thisTileLuminosity.nonZero()) {
|
||||
lightLevelThis = thisTileLuminosity
|
||||
}
|
||||
// END MIX TILE
|
||||
|
||||
for (i in 0 until lanternMap.size) {
|
||||
val lmap = lanternMap[i]
|
||||
if (lmap.posX == x && lmap.posY == y)
|
||||
lightLevelThis = lightLevelThis maxBlend lmap.color // maximise to not exceed 1.0 with normal (<= 1.0) light
|
||||
}
|
||||
|
||||
if (!doNotCalculateAmbient) {
|
||||
// calculate ambient
|
||||
/* + * +
|
||||
* * @ *
|
||||
* + * +
|
||||
* sample ambient for eight points and apply attenuation for those
|
||||
* maxblend eight values and use it
|
||||
*/
|
||||
|
||||
// will "overwrite" what's there in the lightmap if it's the first pass
|
||||
if (pass > 1) {
|
||||
/* + */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x - 1, y - 1) ?: Color(0f, 0f, 0f, 0f), scaleSqrt2(thisTileOpacity))
|
||||
/* + */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x + 1, y - 1) ?: Color(0f, 0f, 0f, 0f), scaleSqrt2(thisTileOpacity))
|
||||
/* + */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x - 1, y + 1) ?: Color(0f, 0f, 0f, 0f), scaleSqrt2(thisTileOpacity))
|
||||
/* + */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x + 1, y + 1) ?: Color(0f, 0f, 0f, 0f), scaleSqrt2(thisTileOpacity))
|
||||
|
||||
/* * */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x, y - 1) ?: Color(0f, 0f, 0f, 0f), thisTileOpacity)
|
||||
/* * */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x, y + 1) ?: Color(0f, 0f, 0f, 0f), thisTileOpacity)
|
||||
/* * */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x - 1, y) ?: Color(0f, 0f, 0f, 0f), thisTileOpacity)
|
||||
/* * */ambientAccumulator = ambientAccumulator maxBlend darkenColoured(getLightInternal(x + 1, y) ?: Color(0f, 0f, 0f, 0f), thisTileOpacity)
|
||||
}
|
||||
|
||||
val ret = lightLevelThis maxBlend ambientAccumulator
|
||||
|
||||
|
||||
|
||||
return ret
|
||||
}
|
||||
else {
|
||||
val ret = lightLevelThis
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getLightForOpaque(x: Int, y: Int): Color? { // ...so that they wouldn't appear too dark
|
||||
val l = getLightInternal(x, y)
|
||||
if (l == null) return null
|
||||
|
||||
if (BlockCodex[world.getTileFromTerrain(x, y)].isSolid) {
|
||||
return Color(
|
||||
(l.r * 1.25f),//.clampOne(),
|
||||
(l.g * 1.25f),//.clampOne(),
|
||||
(l.b * 1.25f),//.clampOne()
|
||||
(l.a * 1.25f)
|
||||
)
|
||||
}
|
||||
else {
|
||||
return l
|
||||
}
|
||||
}
|
||||
|
||||
const val DRAW_FOR_RGB = 0xFFF0
|
||||
const val DRAW_FOR_ALPHA = 0x000F
|
||||
|
||||
lateinit var lightBuffer: Pixmap
|
||||
|
||||
fun draw(batch: SpriteBatch, drawMode: Int) {
|
||||
val this_x_start = for_x_start// + overscan_open
|
||||
val this_x_end = for_x_end// + overscan_open
|
||||
val this_y_start = for_y_start// + overscan_open
|
||||
val this_y_end = for_y_end// + overscan_open
|
||||
|
||||
|
||||
|
||||
// write to colour buffer
|
||||
for (y in this_y_start..this_y_end) {
|
||||
for (x in this_x_start..this_x_end) {
|
||||
|
||||
val color = if (drawMode == DRAW_FOR_RGB) {
|
||||
(getLightForOpaque(x, y) ?: Color(0f,0f,0f,0f)).normaliseToColourHDR()
|
||||
}
|
||||
else if (drawMode == DRAW_FOR_ALPHA) {
|
||||
(getLightForOpaque(x, y) ?: Color(0f,0f,0f,0f)).normaliseToAlphaHDR()
|
||||
}
|
||||
else {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
|
||||
//lightBuffer.setColor(color)
|
||||
lightBuffer.setColor(Color.ORANGE)
|
||||
lightBuffer.drawPixel(x - this_x_start, y - this_y_start)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// draw to the batch
|
||||
val lightBufferAsTex = Texture(lightBuffer)
|
||||
lightBufferAsTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
||||
|
||||
// we might not need shader here...
|
||||
batch.draw(lightBufferAsTex, 0f, 0f, lightBufferAsTex.width * DRAW_TILE_SIZE, lightBufferAsTex.height * DRAW_TILE_SIZE)
|
||||
|
||||
lightBufferAsTex.dispose()
|
||||
|
||||
|
||||
|
||||
// TODO: set lightmap BACK TO array
|
||||
// TODO: re-iterate over the lightmap, applying filters like getLightForOpaque(x, y) and save it to new buffer (pixmap)
|
||||
// TODO: and THEN draw the map using SHADER
|
||||
|
||||
}
|
||||
|
||||
val lightScalingMagic = 8f
|
||||
|
||||
/**
|
||||
* Subtract each channel's RGB value.
|
||||
*
|
||||
* @param data Raw channel value (0-255) per channel
|
||||
* @param darken (0-255) per channel
|
||||
* @return darkened data (0-255) per channel
|
||||
*/
|
||||
fun darkenColoured(data: Color, darken: Color): Color {
|
||||
// use equation with magic number 8.0
|
||||
// should draw somewhat exponential curve when you plot the propagation of light in-game
|
||||
|
||||
return Color(
|
||||
data.r * (1f - darken.r * lightScalingMagic),//.clampZero(),
|
||||
data.g * (1f - darken.g * lightScalingMagic),//.clampZero(),
|
||||
data.b * (1f - darken.b * lightScalingMagic),//.clampZero(),
|
||||
data.a * (1f - darken.a * lightScalingMagic))
|
||||
}
|
||||
|
||||
private fun scaleSqrt2(data: Color): Color {
|
||||
return Color(
|
||||
data.r * 1.41421356f,
|
||||
data.g * 1.41421356f,
|
||||
data.b * 1.41421356f,
|
||||
data.a * 1.41421356f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add each channel's RGB value.
|
||||
*
|
||||
* @param data Raw channel value (0-255) per channel
|
||||
* @param brighten (0-255) per channel
|
||||
* @return brightened data (0-255) per channel
|
||||
*/
|
||||
fun brightenColoured(data: Color, brighten: Color): Color {
|
||||
return Color(
|
||||
data.r * (1f + brighten.r * lightScalingMagic),
|
||||
data.g * (1f + brighten.g * lightScalingMagic),
|
||||
data.b * (1f + brighten.b * lightScalingMagic),
|
||||
data.a * (1f + brighten.a * lightScalingMagic)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken each channel by 'darken' argument
|
||||
*
|
||||
* @param data Raw channel value (0-255) per channel
|
||||
* @param darken (0-255)
|
||||
* @return
|
||||
*/
|
||||
fun darkenUniformInt(data: Color, darken: Float): Color {
|
||||
if (darken < 0 || darken > CHANNEL_MAX)
|
||||
throw IllegalArgumentException("darken: out of range ($darken)")
|
||||
|
||||
val darkenColoured = Color(darken, darken, darken, darken)
|
||||
return darkenColoured(data, darkenColoured)
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken or brighten colour by 'brighten' argument
|
||||
*
|
||||
* @param data Raw channel value (0-255) per channel
|
||||
* @param brighten (-1.0 - 1.0) negative means darkening
|
||||
* @return processed colour
|
||||
*/
|
||||
fun alterBrightnessUniform(data: Color, brighten: Float): Color {
|
||||
return Color(
|
||||
data.r + brighten,
|
||||
data.g + brighten,
|
||||
data.b + brighten,
|
||||
data.a + brighten
|
||||
)
|
||||
}
|
||||
|
||||
/** Get each channel from two RGB values, return new RGB that has max value of each channel
|
||||
* @param rgb
|
||||
* @param rgb2
|
||||
* @return
|
||||
*/
|
||||
infix fun Color.maxBlend(other: Color): Color {
|
||||
return Color(
|
||||
if (this.r > other.r) this.r else other.r,
|
||||
if (this.g > other.g) this.g else other.g,
|
||||
if (this.b > other.b) this.b else other.b,
|
||||
if (this.a > other.a) this.a else other.a
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/*inline fun RGB10.rawR() = this.ushr(20) and 1023
|
||||
inline fun RGB10.rawG() = this.ushr(10) and 1023
|
||||
inline fun RGB10.rawB() = this and 1023
|
||||
|
||||
/** 0.0 - 1.0 for 0-1023 (0.0 - 0.25 for 0-255) */
|
||||
inline fun RGB10.r(): Float = this.rawR() / CHANNEL_MAX_FLOAT
|
||||
inline fun RGB10.g(): Float = this.rawG() / CHANNEL_MAX_FLOAT
|
||||
inline fun RGB10.b(): Float = this.rawB() / CHANNEL_MAX_FLOAT*/
|
||||
|
||||
|
||||
/*inline fun constructRGBFromInt(r: Int, g: Int, b: Int): RGB10 {
|
||||
//if (r !in 0..CHANNEL_MAX) throw IllegalArgumentException("Red: out of range ($r)")
|
||||
//if (g !in 0..CHANNEL_MAX) throw IllegalArgumentException("Green: out of range ($g)")
|
||||
//if (b !in 0..CHANNEL_MAX) throw IllegalArgumentException("Blue: out of range ($b)")
|
||||
|
||||
return r.shl(20) or
|
||||
g.shl(10) or
|
||||
b
|
||||
}*/
|
||||
|
||||
/*inline fun constructRGBFromFloat(r: Float, g: Float, b: Float): RGB10 {
|
||||
//if (r < 0 || r > CHANNEL_MAX_DECIMAL) throw IllegalArgumentException("Red: out of range ($r)")
|
||||
//if (g < 0 || g > CHANNEL_MAX_DECIMAL) throw IllegalArgumentException("Green: out of range ($g)")
|
||||
//if (b < 0 || b > CHANNEL_MAX_DECIMAL) throw IllegalArgumentException("Blue: out of range ($b)")
|
||||
|
||||
return (r * CHANNEL_MAX).round().shl(20) or
|
||||
(g * CHANNEL_MAX).round().shl(10) or
|
||||
(b * CHANNEL_MAX).round()
|
||||
}*/
|
||||
|
||||
fun Int.clampZero() = if (this < 0) 0 else this
|
||||
fun Float.clampZero() = if (this < 0) 0f else this
|
||||
fun Int.clampChannel() = if (this < 0) 0 else if (this > CHANNEL_MAX) CHANNEL_MAX else this
|
||||
fun Float.clampOne() = if (this < 0) 0f else if (this > 1) 1f else this
|
||||
fun Float.clampChannel() = if (this > CHANNEL_MAX_DECIMAL) CHANNEL_MAX_DECIMAL else this
|
||||
|
||||
fun getHighestRGB(x: Int, y: Int): Float? {
|
||||
val value = getLightInternal(x, y)
|
||||
if (value == null)
|
||||
return null
|
||||
else
|
||||
return FastMath.max(value.r, value.g, value.b)
|
||||
}
|
||||
|
||||
fun getHighestRGBA(x: Int, y: Int): Float? {
|
||||
val value = getLightInternal(x, y)
|
||||
if (value == null)
|
||||
return null
|
||||
else
|
||||
return FastMath.max(value.r, value.g, value.b, value.a)
|
||||
}
|
||||
|
||||
/*private fun purgeLightmap() {
|
||||
for (y in 0..LIGHTMAP_HEIGHT - 1) {
|
||||
for (x in 0..LIGHTMAP_WIDTH - 1) {
|
||||
lightmap.setColor(0)
|
||||
lightmap.fillRectangle(0, 0, lightmap.width, lightmap.height)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
inline infix fun Float.powerOf(f: Float) = FastMath.pow(this, f)
|
||||
private inline fun Float.sqr() = this * this
|
||||
private inline fun Float.sqrt() = FastMath.sqrt(this)
|
||||
private inline fun Float.inv() = 1f / this
|
||||
inline fun Float.floor() = FastMath.floor(this)
|
||||
inline fun Double.floorInt() = Math.floor(this).toInt()
|
||||
inline fun Float.round(): Int = Math.round(this)
|
||||
inline fun Double.round(): Int = Math.round(this).toInt()
|
||||
inline fun Float.ceil() = FastMath.ceil(this)
|
||||
inline fun Int.even(): Boolean = this and 1 == 0
|
||||
inline fun Int.odd(): Boolean = this and 1 == 1
|
||||
|
||||
// TODO: float LUT lookup using linear interpolation
|
||||
|
||||
// input: 0..1 for int 0..1023
|
||||
fun hdr(intensity: Float): Float {
|
||||
val intervalStart = (intensity * MUL).floorInt()
|
||||
val intervalEnd = minOf(rgbHDRLookupTable.lastIndex, (intensity * MUL).floorInt() + 1)
|
||||
|
||||
if (intervalStart == intervalEnd) return rgbHDRLookupTable[intervalStart]
|
||||
|
||||
val intervalPos = (intensity * MUL) - (intensity * MUL).toInt()
|
||||
|
||||
return interpolateLinear(
|
||||
intervalPos,
|
||||
rgbHDRLookupTable[intervalStart],
|
||||
rgbHDRLookupTable[intervalEnd]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun resize(width: Int, height: Int) {
|
||||
// make sure the BlocksDrawer is resized first!
|
||||
|
||||
lightBuffer = Pixmap(BlocksDrawer.tilesInHorizontal, BlocksDrawer.tilesInVertical, Pixmap.Format.RGB888)
|
||||
}
|
||||
|
||||
|
||||
val rgbHDRLookupTable = floatArrayOf( // polynomial of 6.0 please refer to work_files/HDRcurveBezierLinIntp.kts
|
||||
0.0000f,0.0000f,0.0020f,0.0060f,0.0100f,0.0139f,0.0179f,0.0219f,0.0259f,0.0299f,0.0338f,0.0378f,0.0418f,0.0458f,0.0497f,0.0537f,
|
||||
0.0577f,0.0617f,0.0656f,0.0696f,0.0736f,0.0776f,0.0816f,0.0855f,0.0895f,0.0935f,0.0975f,0.1014f,0.1054f,0.1094f,0.1134f,0.1173f,
|
||||
0.1213f,0.1253f,0.1293f,0.1332f,0.1372f,0.1412f,0.1451f,0.1491f,0.1531f,0.1571f,0.1610f,0.1650f,0.1690f,0.1730f,0.1769f,0.1809f,
|
||||
0.1849f,0.1888f,0.1928f,0.1968f,0.2007f,0.2047f,0.2087f,0.2127f,0.2166f,0.2206f,0.2246f,0.2285f,0.2325f,0.2365f,0.2404f,0.2444f,
|
||||
0.2484f,0.2523f,0.2563f,0.2602f,0.2642f,0.2682f,0.2721f,0.2761f,0.2800f,0.2840f,0.2880f,0.2919f,0.2959f,0.2998f,0.3038f,0.3078f,
|
||||
0.3117f,0.3157f,0.3196f,0.3236f,0.3275f,0.3315f,0.3354f,0.3394f,0.3433f,0.3472f,0.3512f,0.3551f,0.3591f,0.3630f,0.3669f,0.3709f,
|
||||
0.3748f,0.3788f,0.3827f,0.3866f,0.3905f,0.3945f,0.3984f,0.4023f,0.4062f,0.4101f,0.4141f,0.4180f,0.4219f,0.4258f,0.4297f,0.4336f,
|
||||
0.4375f,0.4414f,0.4453f,0.4491f,0.4530f,0.4569f,0.4608f,0.4647f,0.4685f,0.4724f,0.4762f,0.4801f,0.4839f,0.4878f,0.4916f,0.4954f,
|
||||
0.4993f,0.5031f,0.5069f,0.5107f,0.5145f,0.5183f,0.5220f,0.5258f,0.5296f,0.5333f,0.5371f,0.5408f,0.5445f,0.5482f,0.5520f,0.5556f,
|
||||
0.5593f,0.5630f,0.5667f,0.5703f,0.5739f,0.5776f,0.5812f,0.5848f,0.5883f,0.5919f,0.5955f,0.5990f,0.6025f,0.6060f,0.6095f,0.6130f,
|
||||
0.6164f,0.6199f,0.6233f,0.6267f,0.6300f,0.6334f,0.6367f,0.6401f,0.6433f,0.6466f,0.6499f,0.6531f,0.6563f,0.6595f,0.6627f,0.6658f,
|
||||
0.6689f,0.6720f,0.6751f,0.6781f,0.6811f,0.6841f,0.6871f,0.6901f,0.6930f,0.6959f,0.6987f,0.7016f,0.7044f,0.7072f,0.7100f,0.7127f,
|
||||
0.7154f,0.7181f,0.7208f,0.7234f,0.7260f,0.7286f,0.7311f,0.7337f,0.7362f,0.7386f,0.7411f,0.7435f,0.7459f,0.7483f,0.7506f,0.7530f,
|
||||
0.7553f,0.7575f,0.7598f,0.7620f,0.7642f,0.7664f,0.7685f,0.7706f,0.7727f,0.7748f,0.7769f,0.7789f,0.7809f,0.7829f,0.7849f,0.7868f,
|
||||
0.7887f,0.7906f,0.7925f,0.7944f,0.7962f,0.7980f,0.7998f,0.8016f,0.8033f,0.8051f,0.8068f,0.8085f,0.8101f,0.8118f,0.8134f,0.8150f,
|
||||
0.8166f,0.8182f,0.8198f,0.8213f,0.8229f,0.8244f,0.8259f,0.8274f,0.8288f,0.8303f,0.8317f,0.8331f,0.8345f,0.8359f,0.8373f,0.8386f,
|
||||
0.8400f,0.8413f,0.8426f,0.8439f,0.8452f,0.8465f,0.8477f,0.8490f,0.8502f,0.8514f,0.8526f,0.8538f,0.8550f,0.8562f,0.8573f,0.8585f,
|
||||
0.8596f,0.8608f,0.8619f,0.8630f,0.8641f,0.8651f,0.8662f,0.8673f,0.8683f,0.8693f,0.8704f,0.8714f,0.8724f,0.8734f,0.8744f,0.8754f,
|
||||
0.8763f,0.8773f,0.8782f,0.8792f,0.8801f,0.8811f,0.8820f,0.8829f,0.8838f,0.8847f,0.8856f,0.8864f,0.8873f,0.8882f,0.8890f,0.8899f,
|
||||
0.8907f,0.8915f,0.8923f,0.8932f,0.8940f,0.8948f,0.8956f,0.8963f,0.8971f,0.8979f,0.8987f,0.8994f,0.9002f,0.9009f,0.9017f,0.9024f,
|
||||
0.9031f,0.9039f,0.9046f,0.9053f,0.9060f,0.9067f,0.9074f,0.9081f,0.9087f,0.9094f,0.9101f,0.9108f,0.9114f,0.9121f,0.9127f,0.9134f,
|
||||
0.9140f,0.9146f,0.9153f,0.9159f,0.9165f,0.9171f,0.9177f,0.9184f,0.9190f,0.9195f,0.9201f,0.9207f,0.9213f,0.9219f,0.9225f,0.9230f,
|
||||
0.9236f,0.9242f,0.9247f,0.9253f,0.9258f,0.9264f,0.9269f,0.9274f,0.9280f,0.9285f,0.9290f,0.9296f,0.9301f,0.9306f,0.9311f,0.9316f,
|
||||
0.9321f,0.9326f,0.9331f,0.9336f,0.9341f,0.9346f,0.9351f,0.9355f,0.9360f,0.9365f,0.9370f,0.9374f,0.9379f,0.9383f,0.9388f,0.9393f,
|
||||
0.9397f,0.9402f,0.9406f,0.9410f,0.9415f,0.9419f,0.9423f,0.9428f,0.9432f,0.9436f,0.9440f,0.9445f,0.9449f,0.9453f,0.9457f,0.9461f,
|
||||
0.9465f,0.9469f,0.9473f,0.9477f,0.9481f,0.9485f,0.9489f,0.9493f,0.9497f,0.9501f,0.9504f,0.9508f,0.9512f,0.9516f,0.9519f,0.9523f,
|
||||
0.9527f,0.9530f,0.9534f,0.9537f,0.9541f,0.9545f,0.9548f,0.9552f,0.9555f,0.9559f,0.9562f,0.9565f,0.9569f,0.9572f,0.9576f,0.9579f,
|
||||
0.9582f,0.9586f,0.9589f,0.9592f,0.9595f,0.9599f,0.9602f,0.9605f,0.9608f,0.9611f,0.9614f,0.9617f,0.9621f,0.9624f,0.9627f,0.9630f,
|
||||
0.9633f,0.9636f,0.9639f,0.9642f,0.9645f,0.9648f,0.9650f,0.9653f,0.9656f,0.9659f,0.9662f,0.9665f,0.9668f,0.9670f,0.9673f,0.9676f,
|
||||
0.9679f,0.9681f,0.9684f,0.9687f,0.9690f,0.9692f,0.9695f,0.9697f,0.9700f,0.9703f,0.9705f,0.9708f,0.9711f,0.9713f,0.9716f,0.9718f,
|
||||
0.9721f,0.9723f,0.9726f,0.9728f,0.9731f,0.9733f,0.9735f,0.9738f,0.9740f,0.9743f,0.9745f,0.9747f,0.9750f,0.9752f,0.9754f,0.9757f,
|
||||
0.9759f,0.9761f,0.9764f,0.9766f,0.9768f,0.9770f,0.9773f,0.9775f,0.9777f,0.9779f,0.9781f,0.9784f,0.9786f,0.9788f,0.9790f,0.9792f,
|
||||
0.9794f,0.9796f,0.9799f,0.9801f,0.9803f,0.9805f,0.9807f,0.9809f,0.9811f,0.9813f,0.9815f,0.9817f,0.9819f,0.9821f,0.9823f,0.9825f,
|
||||
0.9827f,0.9829f,0.9831f,0.9832f,0.9834f,0.9836f,0.9838f,0.9840f,0.9842f,0.9844f,0.9846f,0.9847f,0.9849f,0.9851f,0.9853f,0.9855f,
|
||||
0.9856f,0.9858f,0.9860f,0.9862f,0.9864f,0.9865f,0.9867f,0.9869f,0.9870f,0.9872f,0.9874f,0.9876f,0.9877f,0.9879f,0.9881f,0.9882f,
|
||||
0.9884f,0.9886f,0.9887f,0.9889f,0.9890f,0.9892f,0.9894f,0.9895f,0.9897f,0.9898f,0.9900f,0.9901f,0.9903f,0.9905f,0.9906f,0.9908f,
|
||||
0.9909f,0.9911f,0.9912f,0.9914f,0.9915f,0.9917f,0.9918f,0.9920f,0.9921f,0.9922f,0.9924f,0.9925f,0.9927f,0.9928f,0.9930f,0.9931f,
|
||||
0.9932f,0.9934f,0.9935f,0.9937f,0.9938f,0.9939f,0.9941f,0.9942f,0.9943f,0.9945f,0.9946f,0.9947f,0.9949f,0.9950f,0.9951f,0.9953f,
|
||||
0.9954f,0.9955f,0.9957f,0.9958f,0.9959f,0.9960f,0.9962f,0.9963f,0.9964f,0.9965f,0.9967f,0.9968f,0.9969f,0.9970f,0.9971f,0.9973f,
|
||||
0.9974f,0.9975f,0.9976f,0.9977f,0.9978f,0.9980f,0.9981f,0.9982f,0.9983f,0.9984f,0.9985f,0.9987f,0.9988f,0.9989f,0.9990f,0.9991f,
|
||||
0.9992f,0.9993f,0.9994f,0.9995f,0.9996f,0.9997f,0.9999f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,
|
||||
1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f,1.0000f // isn't it beautiful?
|
||||
)
|
||||
/** To eliminated visible edge on the gradient when 255/1023 is exceeded */
|
||||
inline fun Color.normaliseToColourHDR() = Color(
|
||||
hdr(this.r),
|
||||
hdr(this.g),
|
||||
hdr(this.b),
|
||||
1f
|
||||
)
|
||||
|
||||
inline fun Color.normaliseToAlphaHDR() = Color(
|
||||
hdr(this.a),
|
||||
hdr(this.a),
|
||||
hdr(this.a),
|
||||
1f
|
||||
)
|
||||
|
||||
/**
|
||||
* color values are normalised -- 0.0 to 1.0 for 0..1023
|
||||
*/
|
||||
data class Lantern(val posX: Int, val posY: Int, val color: Color)
|
||||
|
||||
private fun Color.nonZero() = this.r != 0f || this.g != 0f || this.b != 0f || this.a != 0f
|
||||
|
||||
val histogram: Histogram
|
||||
get() {
|
||||
var reds = IntArray(MUL) // reds[intensity] ← counts
|
||||
var greens = IntArray(MUL) // do.
|
||||
var blues = IntArray(MUL) // do.
|
||||
val render_width = for_x_end - for_x_start
|
||||
val render_height = for_y_end - for_y_start
|
||||
// excluiding overscans; only reckon echo lights
|
||||
for (y in overscan_open..render_height + overscan_open + 1) {
|
||||
for (x in overscan_open..render_width + overscan_open + 1) {
|
||||
val colour = lightmap[y][x]
|
||||
reds [minOf(CHANNEL_MAX, colour.r.times(MUL).floorInt())] += 1
|
||||
greens[minOf(CHANNEL_MAX, colour.g.times(MUL).floorInt())] += 1
|
||||
blues [minOf(CHANNEL_MAX, colour.b.times(MUL).floorInt())] += 1
|
||||
}
|
||||
}
|
||||
return Histogram(reds, greens, blues)
|
||||
}
|
||||
|
||||
class Histogram(val reds: IntArray, val greens: IntArray, val blues: IntArray) {
|
||||
|
||||
val RED = 0
|
||||
val GREEN = 1
|
||||
val BLUE = 2
|
||||
|
||||
val screen_tiles: Int = (for_x_end - for_x_start + 2) * (for_y_end - for_y_start + 2)
|
||||
|
||||
val brightest: Int
|
||||
get() {
|
||||
for (i in CHANNEL_MAX downTo 1) {
|
||||
if (reds[i] > 0 || greens[i] > 0 || blues[i] > 0)
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
val brightest8Bit: Int
|
||||
get() { val b = brightest
|
||||
return if (brightest > 255) 255 else b
|
||||
}
|
||||
|
||||
val dimmest: Int
|
||||
get() {
|
||||
for (i in 0..CHANNEL_MAX) {
|
||||
if (reds[i] > 0 || greens[i] > 0 || blues[i] > 0)
|
||||
return i
|
||||
}
|
||||
return CHANNEL_MAX
|
||||
}
|
||||
|
||||
val range: Int = CHANNEL_MAX
|
||||
|
||||
fun get(index: Int): IntArray {
|
||||
return when (index) {
|
||||
RED -> reds
|
||||
GREEN -> greens
|
||||
BLUE -> blues
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interpolateLinear(scale: Float, startValue: Float, endValue: Float): Float {
|
||||
if (startValue == endValue) {
|
||||
return startValue
|
||||
}
|
||||
if (scale <= 0f) {
|
||||
return startValue
|
||||
}
|
||||
if (scale >= 1f) {
|
||||
return endValue
|
||||
}
|
||||
return (1f - scale) * startValue + scale * endValue
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user