diff --git a/assets/mods/basegame/sprites/npc_template_anim_prototype.tga b/assets/mods/basegame/sprites/npc_template_anim_prototype.tga index 7b5c2129f..ce1f4e4a8 100644 --- a/assets/mods/basegame/sprites/npc_template_anim_prototype.tga +++ b/assets/mods/basegame/sprites/npc_template_anim_prototype.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b5a7d665a9e682f2fca8f52e6103dd545246dc4ae6048460b2161a4924ffcde -size 79890 +oid sha256:f990d2d1f89a6bc6467dd8f7e1f551b8fc93bc36fe75d90096dd25d0b8b2dd4b +size 79916 diff --git a/src/net/torvald/colourutil/CIEXYZUtil.kt b/src/net/torvald/colourutil/CIEXYZUtil.kt index 935b8fb90..ec9637bb3 100644 --- a/src/net/torvald/colourutil/CIEXYZUtil.kt +++ b/src/net/torvald/colourutil/CIEXYZUtil.kt @@ -1,7 +1,7 @@ package net.torvald.colourutil -import com.jme3.math.FastMath import com.badlogic.gdx.graphics.Color +import com.jme3.math.FastMath /** * Created by minjaesong on 2017-01-12. @@ -11,25 +11,27 @@ object CIEXYZUtil { /** * 0..255 -> 0.0..1.0 */ - private val rgbLineariseLUT = Array(257, { - val step = minOf(it, 255) / 255f + private val rgbLinLUT = FloatArray(256) { + val step = it / 255f if (step > 0.04045f) ((step + 0.055f) / 1.055f).powerOf(2.4f) else step / 12.92f - }) + } /** * 0..255 -> 0.0..1.0 */ - private val rgbUnLineariseLUT = Array(257, { - val step = minOf(it, 255) / 255f + private val rgbUnLinLUT = FloatArray(256) { + val step = it / 255f if (step > 0.0031308f) 1.055f * step.powerOf(1f / 2.4f) - 0.055f else step * 12.92f - }) + } + + private val rgbToXyzLut_XR = FloatArray(256) { 0.4124564f * (it / 255f) } @@ -65,8 +67,35 @@ object CIEXYZUtil { fun Color.toXYZ(): CIEXYZ = RGB(this).toXYZ() + /** + * "Linearise" the sRGB triads. This use lookup table to speed up calculation. + * Integer values (1/255, 2/255, .. , 254/255, 255/255) are accurate but any values in between are + * linearly interpolated and thus slightly less accurate. Visually there's little-to-no difference, + * but may not optimal for rigorous maths. + */ fun RGB.linearise(): RGB { - /*val newR = if (r > 0.04045f) + val out = floatArrayOf(0f, 0f, 0f) + for (i in 0..2) { + val value = when (i) { + 0 -> this.r + 1 -> this.g + 2 -> this.b + else -> throw InternalError("Fuck you") + } + val step = value.clampOne() * 255f // 0.0 .. 255.0 + val intStep = step.toInt() // 0 .. 255 + val NeXTSTEP = minOf(intStep + 1, 255) // 1 .. 255 + + out[i] = interpolateLinear(step - intStep, rgbLinLUT[intStep], rgbLinLUT[NeXTSTEP]) + } + + + return RGB(out[0], out[1], out[2], alpha) + } + + /** Suitable for rigorous maths but slower */ + fun RGB.lineariseSuper(): RGB { + val newR = if (r > 0.04045f) ((r + 0.055f) / 1.055f).powerOf(2.4f) else r / 12.92f val newG = if (g > 0.04045f) @@ -77,28 +106,38 @@ object CIEXYZUtil { else b / 12.92f - return RGB(newR, newG, newB, alpha)*/ + return RGB(newR, newG, newB, alpha) + } + /** + * "Un-linearise" the RGB triads. That is, codes the linear RGB into sRGB. This use lookup table to speed up calculation. + * Integer values (1/255, 2/255, .. , 254/255, 255/255) are accurate but any values in between are + * linearly interpolated and thus slightly less accurate. Visually there's little-to-no difference, + * but may not optimal for rigorous maths. + */ + fun RGB.unLinearise(): RGB { val out = floatArrayOf(0f, 0f, 0f) for (i in 0..2) { val value = when (i) { 0 -> this.r 1 -> this.g 2 -> this.b - else -> throw Exception("Fuck you") + else -> throw InternalError("Fuck you") } - val step = value.clampOne() * 255f - val intStep = step.toInt() + val step = value.clampOne() * 255f // 0.0 .. 255.0 + val intStep = step.toInt() // 0 .. 255 + val NeXTSTEP = minOf(intStep + 1, 255) // 1 .. 255 - out[i] = interpolateLinear(step - intStep, rgbLineariseLUT[intStep], rgbLineariseLUT[intStep + 1]) + out[i] = interpolateLinear(step - intStep, rgbUnLinLUT[intStep], rgbUnLinLUT[NeXTSTEP]) } return RGB(out[0], out[1], out[2], alpha) } - fun RGB.unLinearise(): RGB { - /*val newR = if (r > 0.0031308f) + /** Suitable for rigorous maths but slower */ + fun RGB.unLineariseSuper(): RGB { + val newR = if (r > 0.0031308f) 1.055f * r.powerOf(1f / 2.4f) - 0.055f else r * 12.92f @@ -112,24 +151,7 @@ object CIEXYZUtil { b * 12.92f - return RGB(newR, newG, newB, alpha)*/ - - val out = floatArrayOf(0f, 0f, 0f) - for (i in 0..2) { - val value = when (i) { - 0 -> this.r - 1 -> this.g - 2 -> this.b - else -> throw Exception("Fuck you") - } - val step = value.clampOne() * 255f - val intStep = step.toInt() - - out[i] = interpolateLinear(step - intStep, rgbUnLineariseLUT[intStep], rgbUnLineariseLUT[intStep + 1]) - } - - - return RGB(out[0], out[1], out[2], alpha) + return RGB(newR, newG, newB, alpha) } fun RGB.toXYZ(): CIEXYZ { diff --git a/src/net/torvald/colourutil/ColourUtil.kt b/src/net/torvald/colourutil/ColourUtil.kt index 0d5cd27a9..4aa45bf7e 100644 --- a/src/net/torvald/colourutil/ColourUtil.kt +++ b/src/net/torvald/colourutil/ColourUtil.kt @@ -1,7 +1,8 @@ package net.torvald.colourutil -import com.jme3.math.FastMath import com.badlogic.gdx.graphics.Color +import com.jme3.math.FastMath +import net.torvald.colourutil.CIEXYZUtil.linearise /** * Created by minjaesong on 2016-07-26. @@ -20,4 +21,17 @@ object ColourUtil { return Color(r, g, b, a) } + + /** Get luminosity level using CIEXYZ colour space. Slow but accurate. */ + fun RGB.getLuminosity(): Float { + val new = this.linearise() + return 0.2126729f * new.r + 0.7151522f * new.g + 0.0721750f * new.b // from RGB.toXYZ + } + /** Get luminosity level using CIEXYZ colour space. Slow but accurate. */ + fun Color.getLuminosity() = RGB(this).getLuminosity() + + /** Get luminosity level using NTSC standard. Fast, less accurate but should be good enough. */ + fun RGB.getLuminosityQuick() = 0.3f * this.r + 0.59f * this.g + 0.11f * this.b // NTSC standard + /** Get luminosity level using NTSC standard. Fast, less accurate but should be good enough. */ + fun Color.getLuminosityQuick() = 0.3f * this.r + 0.59f * this.g + 0.11f * this.b // NTSC standard } \ No newline at end of file diff --git a/src/net/torvald/spriteanimation/SpriteAnimation.kt b/src/net/torvald/spriteanimation/SpriteAnimation.kt index 093a9a88d..0c71f29fc 100644 --- a/src/net/torvald/spriteanimation/SpriteAnimation.kt +++ b/src/net/torvald/spriteanimation/SpriteAnimation.kt @@ -7,6 +7,7 @@ package net.torvald.spriteanimation import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.jme3.math.FastMath +import net.torvald.terrarum.Second import net.torvald.terrarum.gameactors.ActorWBMovable import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack @@ -16,12 +17,29 @@ class SpriteAnimation(val parentActor: ActorWBMovable) { var currentFrame = 0 var currentRow = 0 + var nFrames: Int = 1 private set var nRows: Int = 1 private set - var delay = 200f + + private val currentDelay: Second + get() = delays[currentRow] + + /** + * Sets delays for each rows. Array size must be the same as the rows of the sheet + */ + var delays: FloatArray = floatArrayOf(0.2f) + set(value) { + if (value.filter { it <= 0f }.isNotEmpty()) { + throw IllegalArgumentException("Delay array contains zero or negative value: $delays") + } + + field = value + } + private var delta = 0f + val looping = true private var animationRunning = true var flipHorizontal = false @@ -61,17 +79,23 @@ class SpriteAnimation(val parentActor: ActorWBMovable) { //skip this if animation is stopped this.delta += delta + //println("delta accumulation: $delta, currentDelay: $currentDelay") + //check if it's time to advance the frame - if (this.delta >= this.delay) { - //if set to not loop, keep the frame at the last frame - if (this.currentFrame == this.nFrames && !this.looping) { - this.currentFrame = this.nFrames - 1 + while (this.delta >= currentDelay) { + // advance frame + if (looping) { // looping, wrap around + currentFrame = (currentFrame + 1) % nFrames + } + else if (currentFrame < nFrames - 1) { // not looping and haven't reached the end + currentFrame += 1 } - //advance one frame, then reset delta counter - this.currentFrame = this.currentFrame % this.nFrames - this.delta = 0f + // discount counter + this.delta -= currentDelay } + + //println("row, frame: $currentRow, $currentFrame") } } @@ -92,7 +116,7 @@ class SpriteAnimation(val parentActor: ActorWBMovable) { } if (visible) { - val region = textureRegion.get(currentRow, currentFrame) + val region = textureRegion.get(currentFrame, currentRow) batch.color = colorFilter if (flipHorizontal && flipVertical) { @@ -139,15 +163,6 @@ class SpriteAnimation(val parentActor: ActorWBMovable) { } } - fun setSpriteDelay(newDelay: Float) { - if (newDelay > 0) { - delay = newDelay - } - else { - throw IllegalArgumentException("Delay equal or less than zero") - } - } - fun reset() { currentFrame = 1 } diff --git a/src/net/torvald/terrarum/debuggerapp/CSVEditor.java b/src/net/torvald/terrarum/debuggerapp/CSVEditor.java index c6c8ea86a..a2f64b447 100644 --- a/src/net/torvald/terrarum/debuggerapp/CSVEditor.java +++ b/src/net/torvald/terrarum/debuggerapp/CSVEditor.java @@ -17,7 +17,6 @@ import java.io.StringReader; import java.nio.file.Files; import java.util.List; import java.util.Properties; -import java.util.Vector; /** * Should be made into its own artifact to build. @@ -129,7 +128,7 @@ public class CSVEditor extends JFrame { // then work on the file for (CSVRecord record : records) { - Vector newRow = new Vector(columns.length); + String[] newRow = new String[columns.length]; // construct newRow for (String column : columns) { @@ -138,7 +137,7 @@ public class CSVEditor extends JFrame { value = csvFormat.getNullString(); } - newRow.add(spreadsheet.getColumnModel().getColumnIndex(column), value); + newRow[spreadsheet.getColumnModel().getColumnIndex(column)] = value; } ((DefaultTableModel) spreadsheet.getModel()).addRow(newRow); @@ -273,6 +272,9 @@ public class CSVEditor extends JFrame { statBar.setText(lang.getProperty("STAT_INIT")); + + this.revalidate(); + this.repaint(); } public static void main(String[] args) { diff --git a/src/net/torvald/terrarum/modulebasegame/Ingame.kt b/src/net/torvald/terrarum/modulebasegame/Ingame.kt index 9a527fe6e..a79495f63 100644 --- a/src/net/torvald/terrarum/modulebasegame/Ingame.kt +++ b/src/net/torvald/terrarum/modulebasegame/Ingame.kt @@ -274,7 +274,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) { /** Load rest of the game with GL context */ fun postInit() { - setTheRealGamerFirstTime(PlayerBuilderSigrid()) + //setTheRealGamerFirstTime(PlayerBuilderSigrid()) + setTheRealGamerFirstTime(PlayerBuilderTestSubject1()) diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt index a00d5ee52..5210e49a2 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorHumanoid.kt @@ -3,9 +3,7 @@ package net.torvald.terrarum.modulebasegame.gameactors import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.jme3.math.FastMath -import net.torvald.terrarum.AppLoader -import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.bipolarClamp +import net.torvald.terrarum.* import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.terrarum.gameworld.GameWorld @@ -612,14 +610,15 @@ open class ActorHumanoid( sprite?.update(delta) spriteGlow?.update(delta) - //println("$this\tsprite current frame: ${sprite!!.currentFrame}") + if (walledBottom && controllerMoveDelta?.x != 0.0) { + //switch row + sprite?.switchRow(SPRITE_ROW_WALK) + spriteGlow?.switchRow(SPRITE_ROW_WALK) - if (walledBottom) { - // set anim row - if (controllerMoveDelta?.x != 0.0) { - sprite?.switchRow(SPRITE_ROW_WALK) - spriteGlow?.switchRow(SPRITE_ROW_WALK) - } + // set anim frame delay + // 4f of the divider is a magic number, empirically decided + sprite?.delays?.set(SPRITE_ROW_WALK, scale.sqrt().toFloat() / (4f * (controllerMoveDelta?.x ?: 0.0001).abs().toFloat())) // FIXME empirical value + spriteGlow?.delays?.set(SPRITE_ROW_WALK, scale.sqrt().toFloat() / (4f * (controllerMoveDelta?.x ?: 0.0001).abs().toFloat())) // FIXME empirical value // flipping the sprite if (walkHeading == LEFT) { diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTikiTorch.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTikiTorch.kt index 664760179..703fdfd2d 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTikiTorch.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTikiTorch.kt @@ -34,7 +34,6 @@ internal class FixtureTikiTorch : FixtureBase( lightBoxList.add(Hitbox(3.0, 0.0, 4.0, 3.0)) makeNewSprite(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/fixtures/tiki_torch.tga"), 10, 27)) - sprite!!.delay = 0.2f sprite!!.setRowsAndFrames(1, 1) actorValue[AVKey.BASEMASS] = 1.0 diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderCynthia.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderCynthia.kt index c608b2348..8b71b02e6 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderCynthia.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderCynthia.kt @@ -1,7 +1,6 @@ package net.torvald.terrarum.modulebasegame.gameactors import net.torvald.terrarum.ModMgr -import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.ActorWBMovable import net.torvald.terrarum.gameactors.ai.NullAI @@ -28,7 +27,6 @@ object PlayerBuilderCynthia { p.makeNewSprite(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/test_player_2.tga"), 26, 42)) - p.sprite!!.delay = 0.2f p.sprite!!.setRowsAndFrames(1, 1) p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 9, 0) diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderSigrid.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderSigrid.kt index 8692bc837..76496470d 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderSigrid.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderSigrid.kt @@ -20,11 +20,9 @@ object PlayerBuilderSigrid { p.makeNewSprite(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/test_player.tga"), 28, 51)) - p.sprite!!.delay = 0.2f p.sprite!!.setRowsAndFrames(1, 1) p.makeNewSpriteGlow(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/test_player_glow.tga"), 28, 51)) - p.spriteGlow!!.delay = 0.2f p.spriteGlow!!.setRowsAndFrames(1, 1) p.actorValue[AVKey.SCALE] = 1.0 diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt index 8750d34e5..034a5da66 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt @@ -1,9 +1,7 @@ package net.torvald.terrarum.modulebasegame.gameactors import net.torvald.terrarum.ModMgr -import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameactors.AVKey -import net.torvald.terrarum.modulebasegame.Ingame import net.torvald.terrarum.worlddrawer.FeaturesDrawer import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack @@ -23,12 +21,12 @@ object PlayerBuilderTestSubject1 { p.makeNewSprite(TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/npc_template_anim_prototype.tga"), 48, 52)) - p.sprite!!.delay = 0.2f + p.sprite!!.delays = floatArrayOf(2f, 1f/12f) // second value does nothing -- overridden by ActorHumanoid.updateSprite(float) p.sprite!!.setRowsAndFrames(2, 4) p.setHitboxDimension(15, p.actorValue.getAsInt(AVKey.BASEHEIGHT) ?: ActorHumanoid.BASE_HEIGHT, 21, 0) - p.setPosition(4096.0 * FeaturesDrawer.TILE_SIZE, 300.0 * FeaturesDrawer.TILE_SIZE) + p.setPosition(3.0 * FeaturesDrawer.TILE_SIZE, 3.0 * FeaturesDrawer.TILE_SIZE) diff --git a/src/net/torvald/terrarum/tests/RGBtoXYZBenchmark.kt b/src/net/torvald/terrarum/tests/RGBtoXYZBenchmark.kt new file mode 100644 index 000000000..0256609d8 --- /dev/null +++ b/src/net/torvald/terrarum/tests/RGBtoXYZBenchmark.kt @@ -0,0 +1,65 @@ +package net.torvald.terrarum.tests + +import net.torvald.colourutil.CIEXYZUtil.linearise +import net.torvald.colourutil.CIEXYZUtil.unLinearise +import net.torvald.colourutil.ColourUtil.getLuminosity +import net.torvald.colourutil.ColourUtil.getLuminosityQuick +import net.torvald.colourutil.RGB +import net.torvald.random.HQRNG +import kotlin.system.measureNanoTime + +/** + * Created by minjaesong on 2019-01-03. + */ +class RGBtoXYZBenchmark { + + private val TEST_SIZE = 100000 + private val TEST_CHUNK = 1000 + + operator fun invoke() { + val rng = HQRNG() + // prepare test sets + val testSets = Array(TEST_SIZE) { + RGB(rng.nextFloat(), rng.nextFloat(), rng.nextFloat()) + } + // make sure to initialise Util's LUT + testSets[rng.nextInt(0, testSets.size)].linearise() + testSets[rng.nextInt(0, testSets.size)].unLinearise() + + + // conduct the experiment + val timer1 = ArrayList() + val timer2 = ArrayList() + + + for (i in 0 until TEST_SIZE step TEST_CHUNK) { + + val time1 = measureNanoTime { + for (c in i until i + TEST_CHUNK) { + testSets[c].getLuminosity() + } + } + + val time2 = measureNanoTime { + for (c in i until i + TEST_CHUNK) { + testSets[c].getLuminosityQuick() + } + } + + timer1.add(time1) + timer2.add(time2) + } + + + // print out captured data + println("with LUT\tno LUT\tmult") + for (i in 0 until timer1.size) { + println("${timer1[i]}\t${timer2[i]}\t${timer1[i].toFloat() / timer2[i]}") + } + } + +} + +fun main(args: Array) { + RGBtoXYZBenchmark().invoke() +} \ No newline at end of file diff --git a/work_files/graphics/sprites/npc_template.psd b/work_files/graphics/sprites/npc_template.psd index e5e7b3a09..30ec240a1 100644 --- a/work_files/graphics/sprites/npc_template.psd +++ b/work_files/graphics/sprites/npc_template.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b93615b90fc9f3705184723b88232bdcb5db116ca1d5da37aa46a96f8e1a4780 -size 79386 +oid sha256:d7be906967bab8fc6d813d09b17eafcfde52af75134f19fd97fad54b8b8c975d +size 90740 diff --git a/work_files/graphics/sprites/npc_template_anim_prototype.psd b/work_files/graphics/sprites/npc_template_anim_prototype.psd index d29fd21f8..c61a9f9a6 100644 --- a/work_files/graphics/sprites/npc_template_anim_prototype.psd +++ b/work_files/graphics/sprites/npc_template_anim_prototype.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ece2a959438edb588b1a5a3a9230c320fe209fd0fd1d0ee0abe5f64817a73bb8 -size 78578 +oid sha256:9a7bce76119d59d13b7a2c56ddb60d81cb8984c28407b69de06ec5e67524cfe2 +size 77414