All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.earth.worldwind.render.Texture.kt Maven / Gradle / Ivy

Go to download

The WorldWind Kotlin SDK (WWK) includes the library, examples and tutorials for building multiplatform 3D virtual globe applications for Android, Web and Java.

The newest version!
package earth.worldwind.render

import earth.worldwind.draw.DrawContext
import earth.worldwind.geom.Matrix3
import earth.worldwind.util.Logger.ERROR
import earth.worldwind.util.Logger.logMessage
import earth.worldwind.util.kgl.*
import earth.worldwind.util.math.powerOfTwoCeiling

open class Texture(val width: Int, val height: Int, protected val format: Int, protected val type: Int, protected val isRT: Boolean = false) : RenderResource {
    companion object {
        protected fun estimateByteCount(width: Int, height: Int, format: Int, type: Int, hasMipMap: Boolean): Int {
            require(width >= 0 && height >= 0) {
                logMessage(ERROR, "Texture", "estimateByteCount", "invalidWidthOrHeight")
            }
            // Compute the number of bytes per row of texture image level 0. Use a default of 32 bits per pixel when either
            // of the bitmap's type or internal format are unrecognized. Adjust the width to the next highest power-of-two
            // to better estimate the memory consumed by non-power-of-two images.
            val widthPow2 = powerOfTwoCeiling(width)
            val bytesPerRow = when (type) {
                GL_UNSIGNED_BYTE -> when (format) {
                    GL_ALPHA, GL_LUMINANCE -> widthPow2 // 8 bits per pixel
                    GL_LUMINANCE_ALPHA -> widthPow2 * 2 // 16 bits per pixel
                    GL_RGB -> widthPow2 * 3 // 24 bits per pixel
                    GL_RGBA -> widthPow2 * 4 // 32 bits per pixel
                    else -> widthPow2 * 4 // 32 bits per pixel
                }
                GL_UNSIGNED_SHORT, GL_UNSIGNED_SHORT_5_6_5,
                GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1 -> widthPow2 * 2 // 16 bits per pixel
                GL_UNSIGNED_INT -> widthPow2 * 4 // 32 bits per pixel
                else -> widthPow2 * 4 // 32 bits per pixel
            }

            // Compute the number of bytes for the entire texture image level 0 (i.e. bytePerRow * numRows). Adjust the
            // height to the next highest power-of-two to better estimate the memory consumed by non power-of-two images.
            val heightPow2 = powerOfTwoCeiling(height)
            var byteCount = bytesPerRow * heightPow2

            // If the texture will have mipmaps, add 1/3 to account for the bytes used by texture image level 1 through
            // texture image level N.
            if (hasMipMap) byteCount += byteCount / 3
            return byteCount
        }
    }

    val coordTransform = Matrix3()
    val byteCount get() = estimateByteCount(width, height, format, type, hasMipMap)
    protected var name = KglTexture.NONE
    protected var parameters: MutableMap? = null
    protected open val hasMipMap = false
    private var pickMode = false

    fun getTexParameter(name: Int) = parameters?.get(name)?:0

    fun setTexParameter(name: Int, param: Int) {
        val parameters = parameters ?: mutableMapOf().also { parameters = it }
        parameters[name] = param
    }

    override fun release(dc: DrawContext) {
        if (name.isValid()) deleteTexture(dc)
    }

    fun getTextureName(dc: DrawContext): KglTexture {
        if (!name.isValid()) createTexture(dc)
        return name
    }

    fun bindTexture(dc: DrawContext): Boolean {
        if (!name.isValid()) createTexture(dc)
        if (name.isValid()) dc.bindTexture(name)
        if (name.isValid() && pickMode != dc.isPickMode) {
            setTexParameters(dc)
            pickMode = dc.isPickMode
        }
        return name.isValid()
    }

    protected open fun createTexture(dc: DrawContext) {
        val currentTexture = dc.currentTexture
        try {
            // Create the OpenGL texture 2D object.
            name = dc.gl.createTexture()
            dc.gl.bindTexture(GL_TEXTURE_2D, name)

            // Specify the texture object's image data
            allocTexImage(dc)

            // Configure the texture object's parameters.
            setTexParameters(dc)
        } finally {
            // Restore the current OpenGL texture object binding.
            dc.gl.bindTexture(GL_TEXTURE_2D, currentTexture)
        }
    }

    protected open fun deleteTexture(dc: DrawContext) {
        dc.gl.deleteTexture(name)
        name = KglTexture.NONE
    }

    protected open fun allocTexImage(dc: DrawContext) {
        // Following line of code is a dirty hack to disable AFBC compression on Mali GPU driver,
        // which cause huge memory leak during surface shapes drawing on terrain textures.
        if (isRT and dc.gl.hasMaliOOMBug) dc.gl.texImage2D(GL_TEXTURE_2D, 0, format, 1, 1, 0, format, type, null)

        // Allocate texture memory for the OpenGL texture 2D object. The texture memory is initialized with 0.
        dc.gl.texImage2D(
            GL_TEXTURE_2D, 0 /*level*/, format, width, height, 0 /*border*/, format, type, null /*pixels*/
        )
    }

    // TODO refactor setTexParameters to apply all configured tex parameters
    // TODO apply defaults only when no parameter is configured
    // TODO consider simplifying the defaults and requiring that layers/shapes specify what they want
    protected open fun setTexParameters(dc: DrawContext) {
        var param: Int

        // Configure the OpenGL texture minification function. Always use the nearest filtering function in picking mode.
        when {
            dc.isPickMode -> dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
            getTexParameter(GL_TEXTURE_MIN_FILTER).also { param = it } != 0 ->
                dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param)
            else -> dc.gl.texParameteri(
                GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, if (hasMipMap) GL_LINEAR_MIPMAP_LINEAR else GL_LINEAR
            )
        }

        // Configure the OpenGL texture magnification function. Always use the nearest filtering function in picking mode.
        when {
            dc.isPickMode -> dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
            getTexParameter(GL_TEXTURE_MAG_FILTER).also { param = it } != 0 ->
                dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param)
            else -> dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        }

        // Configure the OpenGL texture wrapping function for texture coordinate S. Default to the edge clamping
        // function to render image tiles without seams.
        if (getTexParameter(GL_TEXTURE_WRAP_S).also { param = it } != 0)
            dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, param)
        else dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)

        // Configure the OpenGL texture wrapping function for texture coordinate T. Default to the edge clamping
        // function to render image tiles without seams.
        if (getTexParameter(GL_TEXTURE_WRAP_T).also { param = it } != 0)
            dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, param)
        else dc.gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy