231 lines
6.7 KiB
JavaScript
231 lines
6.7 KiB
JavaScript
/* ES Lint */
|
|
/* globals PIXI, console*/
|
|
|
|
const registeredTiles = new Map()
|
|
/** Implements a baseTexture cache. The last textures are kept for reuse */
|
|
let keepTextures = 0
|
|
const keptTextures = []
|
|
const lateTextures = new Map()
|
|
let lastSweepTime = 0
|
|
let sweepInterval = 2000.0
|
|
|
|
/** The current Tile implementation simply uses PIXI.Sprites.
|
|
*
|
|
* BTW: PIXI.extras.TilingSprite is not appropriate. It should be used for
|
|
* repeating patterns.
|
|
**/
|
|
export default class Tile extends PIXI.Sprite {
|
|
constructor(texture, url) {
|
|
super(texture)
|
|
this.url = url
|
|
this.register(url)
|
|
}
|
|
|
|
/**
|
|
* Static method to enable keeping of base textures
|
|
*
|
|
* @static
|
|
* @param {*} value
|
|
* @memberof Tile
|
|
*/
|
|
static enableKeepTextures(value = 1000) {
|
|
keepTextures = value
|
|
}
|
|
|
|
/**
|
|
* Returns true iff the url is pending
|
|
*
|
|
* @static
|
|
* @param {*} url
|
|
* @returns
|
|
* @memberof Tile
|
|
*/
|
|
/*static isPending(url) {
|
|
return pendingTiles.has(url) && pendingTiles.get(url) > 0
|
|
} */
|
|
|
|
static isObsolete(url) {
|
|
if (registeredTiles.has(url) && registeredTiles.get(url) > 0) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Loads a tile from image using the PIXI.Texture.fromImage method.
|
|
*
|
|
* @static
|
|
* @param {*} imageId
|
|
* @param {*} crossorigin
|
|
* @param {*} scaleMode
|
|
* @returns
|
|
* @memberof Tile
|
|
*/
|
|
static fromImage(imageId, crossorigin, scaleMode) {
|
|
return new Tile(PIXI.Texture.fromImage(imageId, crossorigin, scaleMode), imageId)
|
|
}
|
|
|
|
/**
|
|
* Registers the tile in the global reference counter for textures
|
|
*
|
|
* @param {*} url
|
|
* @param {boolean} [debug=false]
|
|
* @memberof Tile
|
|
*/
|
|
register(url, debug = false) {
|
|
//Tile.unschedule(url)
|
|
if (registeredTiles.has(url)) {
|
|
let tiles = registeredTiles.get(url)
|
|
tiles.add(this)
|
|
if (debug) console.log('Tile.register', url, tiles.size)
|
|
} else {
|
|
registeredTiles.set(url, new Set([this]))
|
|
if (debug) console.log('Tile.register', url, 1)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregisters the rile in the global reference counter for textures
|
|
*
|
|
* @returns {number} The number of how often a texture is used.
|
|
* @memberof Tile
|
|
*/
|
|
unregister() {
|
|
let tiles = registeredTiles.get(this.url)
|
|
tiles.delete(this)
|
|
if (tiles.size == 0) {
|
|
registeredTiles.delete(this.url)
|
|
return 0
|
|
}
|
|
return tiles.size
|
|
}
|
|
|
|
/**
|
|
* Destroys this sprite and optionally its texture and children
|
|
*
|
|
* @param {*} options Part of the PIXI API, but ignored in the implementation
|
|
* @memberof Tile
|
|
*/
|
|
destroy(options, debug = false) {
|
|
let count = this.unregister()
|
|
|
|
if (keepTextures > 0) {
|
|
keptTextures.push({ url: this.url, texture: this.texture })
|
|
|
|
let opts = { children: true, texture: false, baseTexture: false }
|
|
if (debug) console.log('Tile.destroy', registeredTiles.size, opts)
|
|
super.destroy(opts)
|
|
|
|
while (keptTextures.length > keepTextures) {
|
|
let { url, texture } = keptTextures.shift()
|
|
if (Tile.isObsolete(url)) {
|
|
texture.destroy(true) // Destroy base as well
|
|
if (debug) console.log('Destroying texture and baseTexture', url)
|
|
}
|
|
}
|
|
} else {
|
|
// No longer registered and not pending
|
|
if (count <= 0) {
|
|
// && !Tile.isPending(this.url)
|
|
let opts = { children: true, texture: true, baseTexture: true }
|
|
super.destroy(opts)
|
|
if (debug) console.log('Tile.destroy', registeredTiles.size, opts)
|
|
} else {
|
|
let opts = { children: true, texture: false, baseTexture: false }
|
|
if (debug) console.log('Tile.destroy', registeredTiles.size, opts)
|
|
super.destroy(opts)
|
|
}
|
|
if (this.parent != null) {
|
|
// UO: Emit warning and remove
|
|
console.warn('Destroying tile with parent. Hiding instead')
|
|
this.visible = false
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an available texture that can be reused
|
|
*
|
|
* @param {*} url
|
|
* @returns
|
|
* @memberof Tile
|
|
*/
|
|
static textureAvailable(url) {
|
|
if (registeredTiles.has(url)) {
|
|
let tiles = registeredTiles.get(url)
|
|
for (let tile of tiles.values()) {
|
|
//console.log("Reusing cached texture", tile.parent)
|
|
return tile.texture
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Specialized renderWebGL to avoid freezing system
|
|
*
|
|
* @param {*} renderer
|
|
* @memberof Tile
|
|
*/
|
|
renderWebGL(renderer) {
|
|
try {
|
|
super.renderWebGL(renderer)
|
|
} catch (e) {
|
|
// We want persistent logging here
|
|
Logging.error('Error in Tile.renderWebGL: ' + e.message)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes lately arrived texture if they have not been touched in the meanwhile.
|
|
*
|
|
* @static
|
|
* @memberof Tile
|
|
*/
|
|
static sweepLateTextures() {
|
|
let now = performance.now()
|
|
if (now > lastSweepTime + sweepInterval) {
|
|
lastSweepTime = now
|
|
let count = 0
|
|
for (let [url, texture] of lateTextures.entries()) {
|
|
if (texture) {
|
|
let base = texture.baseTexture
|
|
if (base != null && base.touched == 0) {
|
|
texture.destroy(true)
|
|
//console.info("Sweeping ", url)
|
|
count += 1
|
|
lateTextures.delete(url)
|
|
}
|
|
}
|
|
}
|
|
if (count > 0) console.log('Sweeping textures', count)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Texture received too late. We do not need it.
|
|
* @param {*} url
|
|
* @param {*} texture
|
|
*/
|
|
static lateTexture(url, texture) {
|
|
lateTextures.set(url, texture)
|
|
//console.warn("Tile.lateTexture")
|
|
// We cannot destroy the texture since we got errors in t.renderWebGL.
|
|
// Perhaps we can destroy unsed textures later on
|
|
Tile.sweepLateTextures()
|
|
}
|
|
|
|
static printInfos() {
|
|
let references = new Map()
|
|
let multiples = 0
|
|
for (let [url, tiles] of registeredTiles.entries()) {
|
|
let count = tiles.size
|
|
references.set(url, count)
|
|
if (count > 1) {
|
|
multiples += 1
|
|
}
|
|
}
|
|
console.log({ multiples, references })
|
|
}
|
|
}
|