231 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			6.6 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.from method.
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {*} imageId
 | 
						|
     * @param {*} crossorigin
 | 
						|
     * @param {*} scaleMode
 | 
						|
     * @returns
 | 
						|
     * @memberof Tile
 | 
						|
     */
 | 
						|
    static fromImage(imageId, crossorigin, scaleMode) {
 | 
						|
        return new Tile(PIXI.Texture.from(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 })
 | 
						|
    }
 | 
						|
}
 |