mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
binopan adjustments, get actor head size from the sprite
This commit is contained in:
@@ -5,10 +5,9 @@ import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.terrarum.ItemCodex
|
||||
import net.torvald.terrarum.Second
|
||||
import net.torvald.terrarum.floorToFloat
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody.Companion.METER
|
||||
import net.torvald.terrarum.gameitems.GameItem
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
|
||||
import net.torvald.terrarum.savegame.ByteArray64Reader
|
||||
@@ -19,7 +18,6 @@ import net.torvald.terrarum.spriteassembler.ADProperties
|
||||
import net.torvald.terrarum.spriteassembler.ADPropertyObject
|
||||
import net.torvald.terrarum.spriteassembler.AssembleFrameBase
|
||||
import net.torvald.terrarum.spriteassembler.AssembleSheetPixmap
|
||||
import net.torvald.terrarum.tryDispose
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
|
||||
@@ -61,6 +59,9 @@ class AssembledSpriteAnimation(
|
||||
|
||||
@Transient private val res = HashMap<String, TextureRegion?>()
|
||||
|
||||
@Transient var headSprite: TextureRegion? = null
|
||||
@Transient var headSizeInMeter: Float? = null
|
||||
|
||||
init {
|
||||
// init = true
|
||||
|
||||
@@ -73,6 +74,25 @@ class AssembledSpriteAnimation(
|
||||
else AssembleSheetPixmap.getAssetsDirFileGetter(adp)
|
||||
|
||||
adp.bodyparts.forEach { res[it] = getPartTexture(fileGetter, it) }
|
||||
|
||||
val (mugPixmap, headSprite0) = if (disk != null)
|
||||
AssembleSheetPixmap.getMugshotFromVirtualDisk(disk, -1025L, adp)
|
||||
else
|
||||
AssembleSheetPixmap.getMugshotFromAssetsDir(adp)
|
||||
|
||||
headSprite = headSprite0
|
||||
|
||||
mugPixmap?.let { pixmap ->
|
||||
// measure width
|
||||
val m = (0 until pixmap.height).map { y ->
|
||||
(0 until pixmap.width).fold(0) { acc, x ->
|
||||
acc + (pixmap.getPixel(x, y).and(255) > 128).toInt()
|
||||
}
|
||||
}.filter { it > 0 }.average() / METER
|
||||
if (m > 0) headSizeInMeter = m.toFloat() * 0.36f
|
||||
}
|
||||
|
||||
mugPixmap?.dispose()
|
||||
}
|
||||
|
||||
private var delta = 0f
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.backends.lwjgl3.audio.Lwjgl3Audio
|
||||
import com.badlogic.gdx.utils.Disposable
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.spriteanimation.AssembledSpriteAnimation
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.App.THREAD_COUNT
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
@@ -13,11 +14,13 @@ import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RAT
|
||||
import net.torvald.terrarum.audio.dsp.*
|
||||
import net.torvald.terrarum.concurrent.ThreadExecutor
|
||||
import net.torvald.terrarum.concurrent.sliceEvenly
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||
import net.torvald.terrarum.modulebasegame.BuildingMaker
|
||||
import net.torvald.terrarum.modulebasegame.MusicContainer
|
||||
import java.lang.Thread.MAX_PRIORITY
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
@@ -114,11 +117,23 @@ object AudioMixer: Disposable {
|
||||
return dynamicTracks.filter { it.trackingTarget == null && !it.isPlaying }.minByOrNull { it.playStartedTime }
|
||||
}
|
||||
|
||||
var listenerHeadSize = BinoPan.EARDIST_DEFAULT; private set
|
||||
|
||||
private fun setHeadSize(actor: ActorWithBody?): Float {
|
||||
val scale = actor?.scale?.toFloat() ?: 1f
|
||||
val headSize0 = if (actor?.sprite is AssembledSpriteAnimation)
|
||||
(actor.sprite as AssembledSpriteAnimation).headSizeInMeter
|
||||
else null
|
||||
|
||||
return (headSize0 ?: 0f).times(scale).coerceAtLeast(BinoPan.EARDIST_DEFAULT)
|
||||
}
|
||||
|
||||
private val processingExecutor = ThreadExecutor()
|
||||
val processingThread = Thread {
|
||||
// serial precessing
|
||||
while (processing) {
|
||||
actorNowPlaying = Terrarum.ingame?.actorNowPlaying
|
||||
listenerHeadSize = setHeadSize(actorNowPlaying)
|
||||
|
||||
// process
|
||||
dynamicTracks.forEach {
|
||||
@@ -143,6 +158,7 @@ object AudioMixer: Disposable {
|
||||
// parallel processing, it seems even on the 7950X this is less efficient than serial processing...
|
||||
/*while (processing) {
|
||||
actorNowPlaying = Terrarum.ingame?.actorNowPlaying
|
||||
listenerHeadSize = setHeadSize(actorNowPlaying)
|
||||
|
||||
for (tracks in parallelProcessingSchedule) {
|
||||
if (!processing) break
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.torvald.terrarum.audio
|
||||
import com.badlogic.gdx.utils.Queue
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.reflection.forceInvoke
|
||||
import net.torvald.spriteanimation.AssembledSpriteAnimation
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATE
|
||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATED
|
||||
@@ -55,7 +56,7 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra
|
||||
private val distFalloff = 1600.0
|
||||
|
||||
private fun printdbg(msg: Any) {
|
||||
if (true) App.printdbg("AudioAdapter ${track.name}", msg)
|
||||
if (true) App.printdbg("MixerTrackProcessor ${track.name}", msg)
|
||||
}
|
||||
|
||||
private fun allocateStreamBuf(track: TerrarumAudioMixerTrack) {
|
||||
@@ -116,6 +117,8 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra
|
||||
|
||||
// update panning and shits
|
||||
if (track.trackType == TrackType.DYNAMIC_SOURCE && track.isPlaying) {
|
||||
(track.filters[0] as BinoPan).earDist = AudioMixer.listenerHeadSize
|
||||
|
||||
if (AudioMixer.actorNowPlaying != null) {
|
||||
if (track.trackingTarget == null || track.trackingTarget == AudioMixer.actorNowPlaying) {
|
||||
// "reset" the track
|
||||
@@ -128,10 +131,7 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra
|
||||
val distFromActor = distBetweenActors(AudioMixer.actorNowPlaying!!, track.trackingTarget as ActorWithBody)
|
||||
val vol = track.maxVolume * getVolFun(distFromActor / distFalloff).coerceAtLeast(0.0)
|
||||
track.volume = vol
|
||||
(track.filters[0] as BinoPan).pan =
|
||||
if (relativeXpos <= -distFalloff) -1f
|
||||
else if (relativeXpos >= distFalloff) 1f
|
||||
else ((2*asin(relativeXpos / distFalloff)) / Math.PI).toFloat()
|
||||
(track.filters[0] as BinoPan).pan = tanh(1.5 * relativeXpos / distFalloff).toFloat()
|
||||
(track.filters[1] as Lowpass).setCutoff(
|
||||
(SAMPLING_RATED*0.5) / (24.0 * (distFromActor / distFalloff).sqr() + 1.0)
|
||||
)
|
||||
@@ -268,13 +268,13 @@ class MixerTrackProcessor(val buffertaille: Int, val rate: Int, val track: Terra
|
||||
// val x2 = x.pow(q(x))
|
||||
|
||||
// method 1.
|
||||
//https://www.desmos.com/calculator/uzbjw10lna
|
||||
// https://www.desmos.com/calculator/uzbjw10lna
|
||||
val K = 512.0
|
||||
return K.pow(-sqrt(1.0+x.sqr())) * K
|
||||
|
||||
|
||||
// method 2.
|
||||
// https://www.desmos.com/calculator/xxp5ipp85w
|
||||
// https://www.desmos.com/calculator/3xsac66rsp
|
||||
}
|
||||
|
||||
private fun FloatArray.applyVolume(volume: Float) = FloatArray(this.size) { (this[it] * volume) }
|
||||
|
||||
@@ -7,7 +7,9 @@ import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.audio.AudioMixer
|
||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack
|
||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.AUDIO_BUFFER_SIZE
|
||||
import net.torvald.terrarum.audio.TerrarumAudioMixerTrack.Companion.SAMPLING_RATEF
|
||||
import net.torvald.terrarum.audio.decibelsToFullscale
|
||||
import net.torvald.terrarum.ceilToInt
|
||||
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.COL_METER_GRAD
|
||||
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.COL_METER_GRAD2
|
||||
import net.torvald.terrarum.ui.BasicDebugInfoWindow.Companion.FILTER_NAME_ACTIVE
|
||||
@@ -20,12 +22,14 @@ import kotlin.math.roundToInt
|
||||
/**
|
||||
* @param pan -1 for far-left, 0 for centre, 1 for far-right
|
||||
* @param soundSpeed speed of the sound in meters per seconds
|
||||
* @param earDist distance between ears in meters
|
||||
* @param earDist distance between ears in meters. Maximum: 16.0
|
||||
*/
|
||||
class BinoPan(var pan: Float, var earDist: Float = 0.18f): TerrarumAudioFilter() {
|
||||
class BinoPan(var pan: Float, var earDist: Float = EARDIST_DEFAULT): TerrarumAudioFilter() {
|
||||
|
||||
private val delayLineL = FloatArray(AUDIO_BUFFER_SIZE)
|
||||
private val delayLineR = FloatArray(AUDIO_BUFFER_SIZE)
|
||||
private val MAX_DELAY = (EARDIST_MAX / AudioMixer.SPEED_OF_SOUND * SAMPLING_RATEF).ceilToInt()
|
||||
|
||||
private val delayLineL = FloatArray(MAX_DELAY)
|
||||
private val delayLineR = FloatArray(MAX_DELAY)
|
||||
|
||||
|
||||
private fun getFrom(index: Float, buf0: FloatArray, buf1: FloatArray): Float {
|
||||
@@ -42,6 +46,9 @@ class BinoPan(var pan: Float, var earDist: Float = 0.18f): TerrarumAudioFilter()
|
||||
|
||||
|
||||
companion object {
|
||||
const val EARDIST_DEFAULT = 0.18f
|
||||
const val EARDIST_MAX = 16f
|
||||
|
||||
private val PANNING_CONST = 6.0
|
||||
private const val L = 0
|
||||
private const val R = 1
|
||||
@@ -50,11 +57,14 @@ class BinoPan(var pan: Float, var earDist: Float = 0.18f): TerrarumAudioFilter()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param angleOffset must be smaller than ±90 deg
|
||||
*/
|
||||
private fun thru(sym: String, angleOffset: Float, inbuf: FloatArray, sumbuf: Array<FloatArray>, delayLine: FloatArray) {
|
||||
val pan = (pan + angleOffset / 90f).coerceIn(-1f, 1f)
|
||||
val pan = (pan + angleOffset / (90f - angleOffset.absoluteValue)).coerceIn(-1f, 1f)
|
||||
|
||||
val timeDiffMax = earDist.coerceAtMost(EARDIST_MAX) / AudioMixer.SPEED_OF_SOUND * SAMPLING_RATEF
|
||||
val angle = pan * HALF_PI
|
||||
val timeDiffMax = earDist / AudioMixer.SPEED_OF_SOUND * TerrarumAudioMixerTrack.SAMPLING_RATEF
|
||||
val delayInSamples = (timeDiffMax * FastMath.sin(angle)).absoluteValue
|
||||
val volMultDbThis = PANNING_CONST * pan.absoluteValue
|
||||
val volMultFsThis = decibelsToFullscale(volMultDbThis).toFloat()
|
||||
@@ -87,8 +97,8 @@ class BinoPan(var pan: Float, var earDist: Float = 0.18f): TerrarumAudioFilter()
|
||||
// printdbg(this, "$sym\tpan=$pan, mults=${mults[L]}\t${mults[R]}")
|
||||
}
|
||||
override fun thru(inbuf: List<FloatArray>, outbuf: List<FloatArray>) {
|
||||
thru("L", -90f, inbuf[L], outLs, delayLineL)
|
||||
thru("R", +90f, inbuf[R], outRs, delayLineR)
|
||||
thru("L", -30f, inbuf[L], outLs, delayLineL)
|
||||
thru("R", +30f, inbuf[R], outRs, delayLineR)
|
||||
|
||||
for (i in 0 until AUDIO_BUFFER_SIZE) {
|
||||
val outL = (outLs[L][i] + outRs[L][i]) / 2f
|
||||
|
||||
@@ -19,6 +19,11 @@ abstract class TerrarumAudioFilter {
|
||||
|
||||
fun FloatArray.applyGain(gain: Float = 1f) = this.map { it * gain }.toFloatArray()
|
||||
fun push(samples: FloatArray, buf: FloatArray) {
|
||||
System.arraycopy(buf, samples.size, buf, 0, buf.size - samples.size)
|
||||
System.arraycopy(samples, 0, buf, buf.size - samples.size, samples.size)
|
||||
if (samples.size >= buf.size) {
|
||||
System.arraycopy(samples, samples.size - buf.size, buf, 0, buf.size)
|
||||
}
|
||||
else {
|
||||
System.arraycopy(buf, samples.size, buf, 0, buf.size - samples.size)
|
||||
System.arraycopy(samples, 0, buf, buf.size - samples.size, samples.size)
|
||||
}
|
||||
}
|
||||
@@ -80,19 +80,19 @@ object AssembleSheetPixmap {
|
||||
return null
|
||||
}
|
||||
|
||||
fun getMugshotFromAssetsDir(properties: ADProperties): TextureRegion? {
|
||||
// TODO assemble from HAIR_FORE (optional), HAIR (optional) then HEAD (mandatory)
|
||||
fun getMugshotFromAssetsDir(properties: ADProperties): Pair<Pixmap?, TextureRegion?> {
|
||||
// assemble from HAIR_FORE (optional), HAIR (optional) then HEAD (mandatory)
|
||||
val getter = getAssetsDirFileGetter(properties)
|
||||
val headPixmap = getPartPixmap(getter, "HEAD")
|
||||
val hairPixmap = getPartPixmap(getter, "HAIR")
|
||||
val hair2Pixmap = getPartPixmap(getter, "HAIR_FORE")
|
||||
|
||||
if (headPixmap == null) throw FileNotFoundException("Bodyparts file of HEAD is not found!")
|
||||
return composeMugshot(properties, headPixmap, hairPixmap, hair2Pixmap)
|
||||
if (headPixmap == null) return null to null//throw FileNotFoundException("Bodyparts file of HEAD is not found!")
|
||||
return headPixmap to composeMugshot(properties, headPixmap, hairPixmap, hair2Pixmap)
|
||||
}
|
||||
|
||||
fun getMugshotFromVirtualDisk(disk: SimpleFileSystem, entrynum: Long, properties: ADProperties): TextureRegion? {
|
||||
// TODO assemble from HAIR_FORE (optional), HAIR (optional) then HEAD (mandatory)
|
||||
fun getMugshotFromVirtualDisk(disk: SimpleFileSystem, entrynum: Long, properties: ADProperties): Pair<Pixmap?, TextureRegion?> {
|
||||
// assemble from HAIR_FORE (optional), HAIR (optional) then HEAD (mandatory)
|
||||
val bodypartMapping = Properties()
|
||||
bodypartMapping.load(ByteArray64Reader(disk.getFile(entrynum)!!.bytes, Common.CHARSET))
|
||||
val getter = getVirtualDiskFileGetter(bodypartMapping, disk)
|
||||
@@ -100,8 +100,8 @@ object AssembleSheetPixmap {
|
||||
val hairPixmap = getPartPixmap(getter, "HAIR")
|
||||
val hair2Pixmap = getPartPixmap(getter, "HAIR_FORE")
|
||||
|
||||
if (headPixmap == null) throw FileNotFoundException("Bodyparts file of HEAD is not found!")
|
||||
return composeMugshot(properties, headPixmap, hairPixmap, hair2Pixmap)
|
||||
if (headPixmap == null) return null to null//throw FileNotFoundException("Bodyparts file of HEAD is not found!")
|
||||
return headPixmap to composeMugshot(properties, headPixmap, hairPixmap, hair2Pixmap)
|
||||
}
|
||||
|
||||
private fun composeMugshot(properties: ADProperties, head: Pixmap, hair: Pixmap?, hair2: Pixmap?): TextureRegion {
|
||||
@@ -125,7 +125,7 @@ object AssembleSheetPixmap {
|
||||
val tr = TextureRegion(Texture(canvas))
|
||||
|
||||
canvas.dispose()
|
||||
head.dispose()
|
||||
// head.dispose()
|
||||
hair?.dispose()
|
||||
hair2?.dispose()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user