mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 01:16:46 -04:00
make opengl texture array implementation lighter
Most of the functionality is actually not opengl specific, so abstract it
This commit is contained in:
parent
31225196ee
commit
35a9a08880
@ -34,7 +34,7 @@ class BakedFaceTest {
|
||||
|
||||
private fun texture(): Texture {
|
||||
val manager = BakedModelTestUtil.createTextureManager(texture)
|
||||
return manager.staticTextures.createTexture(texture.toResourceLocation())
|
||||
return manager.staticTextures.create(texture.toResourceLocation())
|
||||
}
|
||||
|
||||
private fun singleMesh(): ChunkMesh {
|
||||
|
@ -13,17 +13,22 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.dummy.texture
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kutil.latch.AbstractLatch
|
||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTextureArray
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class DummyStaticTextureArray(context: RenderContext) : StaticTextureArray(context, false, 0) {
|
||||
|
||||
override fun load(latch: AbstractLatch) {
|
||||
for (texture in this.named.values) {
|
||||
(texture as DummyTexture).state = TextureStates.LOADED
|
||||
override fun load(animationIndex: AtomicInteger, textures: Collection<Texture>) {
|
||||
for (texture in textures) {
|
||||
if (texture !is DummyTexture) continue
|
||||
texture.state = TextureStates.LOADED
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +36,12 @@ class DummyStaticTextureArray(context: RenderContext) : StaticTextureArray(conte
|
||||
animator.init()
|
||||
}
|
||||
|
||||
override fun create(resourceLocation: ResourceLocation, mipmaps: Boolean, properties: Boolean, factory: (mipmaps: Int) -> Texture): Texture {
|
||||
return super.create(resourceLocation, mipmaps, properties) { DummyTexture() }
|
||||
}
|
||||
|
||||
override fun findResolution(size: Vec2i) = size
|
||||
|
||||
override fun activate() = Unit
|
||||
override fun use(shader: NativeShader, name: String) = Unit
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.TextureRend
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
|
||||
|
||||
class DummyTexture : Texture {
|
||||
override var array = TextureArrayProperties(Vec2(), 1, Vec2())
|
||||
override var array = TextureArrayProperties(Vec2(), 1, 1.0f)
|
||||
override var state: TextureStates = TextureStates.DECLARED
|
||||
override var size: Vec2i = Vec2i(1, 1)
|
||||
override val transparency: TextureTransparencies get() = TextureTransparencies.OPAQUE
|
||||
|
@ -59,7 +59,7 @@ class WorldBorderRenderer(
|
||||
shader.native.defines["MAX_DISTANCE"] = MAX_DISTANCE
|
||||
shader.load()
|
||||
|
||||
texture = context.textures.staticTextures.createTexture(TEXTURE)
|
||||
texture = context.textures.staticTextures.create(TEXTURE)
|
||||
context.camera.offset::offset.observe(this) { reload = true }
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ class DoubleChestRenderer(
|
||||
|
||||
private fun register(loader: ModelLoader, name: ResourceLocation, texture: ResourceLocation) {
|
||||
val static = loader.context.textures.staticTextures
|
||||
val override = mapOf(TEXTURE to static.createTexture(texture))
|
||||
val override = mapOf(TEXTURE to static.create(texture))
|
||||
loader.skeletal.register(name, MODEL, override)
|
||||
}
|
||||
|
||||
@ -52,8 +52,8 @@ class DoubleChestRenderer(
|
||||
if (textures.size != 2) throw IllegalStateException("Textures must be left and right!")
|
||||
val static = loader.context.textures.staticTextures
|
||||
val override = mapOf(
|
||||
TEXTURE_5[0] to static.createTexture(textures[0]),
|
||||
TEXTURE_5[1] to static.createTexture(textures[1]),
|
||||
TEXTURE_5[0] to static.create(textures[0]),
|
||||
TEXTURE_5[1] to static.create(textures[1]),
|
||||
)
|
||||
loader.skeletal.register(name, MODEL_5, override)
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class SingleChestRenderer(
|
||||
private val named = minecraft("chest")
|
||||
|
||||
fun register(loader: ModelLoader, name: ResourceLocation, texture: ResourceLocation) {
|
||||
val texture = loader.context.textures.staticTextures.createTexture(texture)
|
||||
val texture = loader.context.textures.staticTextures.create(texture)
|
||||
val model = if (loader.packFormat < 5) MODEL else MODEL_5
|
||||
loader.skeletal.register(name, model, mapOf(named to texture))
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class ShulkerBoxRenderer(
|
||||
}
|
||||
|
||||
private fun load(name: ResourceLocation, texture: ResourceLocation, loader: ModelLoader) {
|
||||
val texture = loader.context.textures.staticTextures.createTexture(texture)
|
||||
val texture = loader.context.textures.staticTextures.create(texture)
|
||||
loader.skeletal.register(name, TEMPLATE, override = mapOf(this.named to texture))
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ class BitmapCodeRenderer(
|
||||
) : AscentedCodePointRenderer {
|
||||
|
||||
fun updateArray() {
|
||||
uvStart *= texture.array.uvEnd
|
||||
uvEnd *= texture.array.uvEnd
|
||||
val end = texture.array.uvEnd ?: return
|
||||
uvStart *= end
|
||||
uvEnd *= end
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class BitmapFontType(
|
||||
|
||||
private fun load(file: ResourceLocation, height: Int, ascent: Int, chars: List<String>, context: RenderContext): BitmapFontType? {
|
||||
if (chars.isEmpty() || height <= 0) return null
|
||||
val texture = context.textures.staticTextures.createTexture(file, mipmaps = false, properties = false)
|
||||
val texture = context.textures.staticTextures.create(file, mipmaps = false, properties = false)
|
||||
texture.load(context) // force load it, we need to calculate the width of every char
|
||||
|
||||
return load(texture, texture.size.y / chars.size, ascent, chars.codePoints())
|
||||
|
@ -19,14 +19,15 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
||||
|
||||
class UnicodeCodeRenderer(
|
||||
override val texture: Texture,
|
||||
override var uvStart: Vec2,
|
||||
override var uvEnd: Vec2,
|
||||
override val uvStart: Vec2,
|
||||
override val uvEnd: Vec2,
|
||||
override val width: Float,
|
||||
) : RasterizedCodePointRenderer {
|
||||
|
||||
|
||||
fun updateArray() {
|
||||
uvStart = uvStart * texture.array.uvEnd
|
||||
uvEnd = uvEnd * texture.array.uvEnd
|
||||
val end = texture.array.uvEnd ?: return
|
||||
uvStart *= end
|
||||
uvEnd *= end
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class LegacyUnicodeFontType(
|
||||
sizes.skip(PAGE_SIZE.toLong())
|
||||
return
|
||||
}
|
||||
val texture = textures.createTexture(textureFile, mipmaps = false, properties = false)
|
||||
val texture = textures.create(textureFile, mipmaps = false, properties = false)
|
||||
|
||||
loadPage(pageId, texture, chars, sizes)
|
||||
}
|
||||
|
@ -13,11 +13,11 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.font.types.unicode.unihex
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.font.types.empty.EmptyCodeRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.font.types.unicode.unihex.UnifontTexture.Companion.isPixelSet
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTextureArray
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureArray.Companion.TEXTURE_RESOLUTION_ID_MAP
|
||||
|
||||
class UnifontRasterizer(
|
||||
private val array: StaticTextureArray,
|
||||
@ -54,15 +54,8 @@ class UnifontRasterizer(
|
||||
}
|
||||
|
||||
private fun calculateRows(width: Int): Int {
|
||||
var previous = TEXTURE_RESOLUTION_ID_MAP.last()
|
||||
for (index in TEXTURE_RESOLUTION_ID_MAP.size - 1 downTo 0) {
|
||||
val resolution = TEXTURE_RESOLUTION_ID_MAP[index]
|
||||
val size = resolution * resolution / HEIGHT
|
||||
if (width >= size) return previous / HEIGHT
|
||||
previous = resolution
|
||||
}
|
||||
|
||||
return 1
|
||||
val size = array.findResolution(Vec2i(width, HEIGHT))
|
||||
return size.y / HEIGHT
|
||||
}
|
||||
|
||||
private fun createTexture(): UnifontTexture {
|
||||
|
@ -31,7 +31,7 @@ class FireOverlay(
|
||||
private val config = context.connection.profiles.rendering.overlay.fire
|
||||
private val player = context.connection.player
|
||||
private val shader = context.shaders.genericTexture2dShader
|
||||
private var texture: Texture = context.textures.staticTextures.createTexture("block/fire_1".toResourceLocation().texture())
|
||||
private var texture: Texture = context.textures.staticTextures.create("block/fire_1".toResourceLocation().texture())
|
||||
private val lava = context.connection.registries.fluid[LavaFluid]
|
||||
override val render: Boolean
|
||||
get() {
|
||||
|
@ -23,7 +23,7 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
|
||||
class PowderSnowOverlay(context: RenderContext) : SimpleOverlay(context) {
|
||||
private val config = context.connection.profiles.rendering.overlay
|
||||
override val texture: Texture = context.textures.staticTextures.createTexture(OVERLAY_TEXTURE)
|
||||
override val texture: Texture = context.textures.staticTextures.create(OVERLAY_TEXTURE)
|
||||
private val strength = FloatAverage(1L * 1000000000L, 0.0f)
|
||||
override var render: Boolean = false
|
||||
get() = config.powderSnow && field
|
||||
|
@ -23,7 +23,7 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
|
||||
class PumpkinOverlay(context: RenderContext) : FirstPersonOverlay(context) {
|
||||
private val config = context.connection.profiles.rendering.overlay
|
||||
override val texture: Texture = context.textures.staticTextures.createTexture(OVERLAY_TEXTURE)
|
||||
override val texture: Texture = context.textures.staticTextures.create(OVERLAY_TEXTURE)
|
||||
override val render: Boolean
|
||||
get() {
|
||||
if (!config.pumpkin) {
|
||||
|
@ -24,7 +24,7 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
|
||||
class WaterOverlay(context: RenderContext) : SimpleOverlay(context) {
|
||||
private val player = context.connection.player
|
||||
override val texture: Texture = context.textures.staticTextures.createTexture("minecraft:misc/underwater".toResourceLocation().texture())
|
||||
override val texture: Texture = context.textures.staticTextures.create("minecraft:misc/underwater".toResourceLocation().texture())
|
||||
override val render: Boolean
|
||||
get() = player.gamemode != Gamemodes.SPECTATOR && player.physics.submersion.eye is WaterFluid
|
||||
|
||||
|
@ -22,7 +22,7 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
|
||||
class WorldBorderOverlay(context: RenderContext) : SimpleOverlay(context) {
|
||||
private val config = context.connection.profiles.rendering.overlay
|
||||
override val texture: Texture = context.textures.staticTextures.createTexture(OVERLAY_TEXTURE)
|
||||
override val texture: Texture = context.textures.staticTextures.create(OVERLAY_TEXTURE)
|
||||
override val render: Boolean
|
||||
get() = config.worldBorder && context.connection.world.border.isOutside(context.connection.player.physics.position)
|
||||
|
||||
|
@ -33,8 +33,8 @@ import java.util.*
|
||||
class WeatherOverlay(private val context: RenderContext) : Overlay {
|
||||
private val world = context.connection.world
|
||||
private val config = context.connection.profiles.rendering.overlay.weather
|
||||
private val rain = context.textures.staticTextures.createTexture(RAIN)
|
||||
private val snow = context.textures.staticTextures.createTexture(SNOW)
|
||||
private val rain = context.textures.staticTextures.create(RAIN)
|
||||
private val snow = context.textures.staticTextures.create(SNOW)
|
||||
private val precipitation get() = context.connection.player.physics.positionInfo.biome?.precipitation
|
||||
override val render: Boolean
|
||||
get() = world.dimension.effects.weather && world.weather.raining && when (precipitation) { // ToDo: Check if exposed to the sky
|
||||
@ -70,7 +70,7 @@ class WeatherOverlay(private val context: RenderContext) : Overlay {
|
||||
val offsetMultiplicator = random.nextFloat(0.8f, 1.2f)
|
||||
val alpha = random.nextFloat(0.8f, 1.0f)
|
||||
mesh.addZQuad(
|
||||
Vec2(offset, 0), OVERLAY_Z, Vec2(offset + step, windowSize.y), Vec2(0.0f), texture.array.uvEnd
|
||||
Vec2(offset, 0), OVERLAY_Z, Vec2(offset + step, windowSize.y), Vec2(0.0f), texture.array.uvEnd ?: Vec2(1.0f)
|
||||
) { position, uv ->
|
||||
val transformed = Vec2()
|
||||
transformed.x = position.x / (windowSize.x / 2) - 1.0f
|
||||
|
@ -44,14 +44,14 @@ data class BlockModel(
|
||||
|
||||
fun createTexture(name: String, textures: TextureManager): Texture? {
|
||||
if (!name.startsWith("#")) {
|
||||
return textures.staticTextures.createTexture(name.toResourceLocation())
|
||||
return textures.staticTextures.create(name.toResourceLocation())
|
||||
}
|
||||
val texture = this.textures?.get(name.substring(1))
|
||||
if (texture == null || texture !is ResourceLocation) {
|
||||
return null
|
||||
}
|
||||
|
||||
return textures.staticTextures.createTexture(texture)
|
||||
return textures.staticTextures.create(texture)
|
||||
}
|
||||
|
||||
fun getOrNullTexture(name: String, textures: TextureManager): Texture? {
|
||||
|
@ -27,8 +27,8 @@ class LavaFluidModel : FluidModel {
|
||||
override val transparency = TextureTransparencies.OPAQUE// TODO: from texture
|
||||
|
||||
override fun load(context: RenderContext) {
|
||||
still = context.textures.staticTextures.createTexture(context.models.block.fixTexturePath(STILL).texture())
|
||||
flowing = context.textures.staticTextures.createTexture(context.models.block.fixTexturePath(FLOWING).texture())
|
||||
still = context.textures.staticTextures.create(context.models.block.fixTexturePath(STILL).texture())
|
||||
flowing = context.textures.staticTextures.create(context.models.block.fixTexturePath(FLOWING).texture())
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -31,8 +31,8 @@ class WaterFluidModel : FluidModel {
|
||||
override val transparency = TextureTransparencies.TRANSLUCENT// TODO: from texture
|
||||
|
||||
override fun load(context: RenderContext) {
|
||||
still = context.textures.staticTextures.createTexture(context.models.block.fixTexturePath(STILL).texture())
|
||||
flowing = context.textures.staticTextures.createTexture(context.models.block.fixTexturePath(FLOWING).texture())
|
||||
still = context.textures.staticTextures.create(context.models.block.fixTexturePath(STILL).texture())
|
||||
flowing = context.textures.staticTextures.create(context.models.block.fixTexturePath(FLOWING).texture())
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -32,7 +32,7 @@ class ItemModel(
|
||||
if (this.textures == null) return null
|
||||
val texture = this.textures["layer0", "particle"]?.toResourceLocation()?.texture() ?: return null
|
||||
|
||||
return ItemModelPrototype(textures.staticTextures.createTexture(texture))
|
||||
return ItemModelPrototype(textures.staticTextures.create(texture))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -119,7 +119,7 @@ class ParticleRenderer(
|
||||
translucentMesh.load()
|
||||
for (particle in connection.registries.particleType) {
|
||||
for (resourceLocation in particle.textures) {
|
||||
context.textures.staticTextures.createTexture(resourceLocation)
|
||||
context.textures.staticTextures.create(resourceLocation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ data class SkeletalModel(
|
||||
if (name in skip) continue
|
||||
val file = name.texture()
|
||||
if (file in skip) continue
|
||||
val texture = context.textures.staticTextures.createTexture(file)
|
||||
val texture = context.textures.staticTextures.create(file)
|
||||
this.loadedTextures[name] = SkeletalTextureInstance(properties, texture)
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class SkyboxRenderer(
|
||||
override fun init() {
|
||||
for (properties in DefaultDimensionEffects) {
|
||||
val texture = properties.fixedTexture ?: continue
|
||||
textureCache[texture] = sky.context.textures.staticTextures.createTexture(texture)
|
||||
textureCache[texture] = sky.context.textures.staticTextures.create(texture)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,14 @@ import java.util.*
|
||||
class MoonRenderer(
|
||||
sky: SkyRenderer,
|
||||
) : PlanetRenderer(sky) {
|
||||
override val texture = sky.context.textures.staticTextures.createTexture(MOON_PHASES)
|
||||
override val texture = sky.context.textures.staticTextures.create(MOON_PHASES)
|
||||
private var phase = MoonPhases.FULL_MOON
|
||||
|
||||
private fun updateUV(phases: MoonPhases) {
|
||||
val coordinates = PHASE_UV[phases.ordinal]
|
||||
uvStart = Vec2(1.0f / 4 * coordinates.x, 1.0f / 2 * coordinates.y) * texture.array.uvEnd
|
||||
uvEnd = Vec2(1.0f / 4 * (coordinates.x + 1), 1.0f / 2 * (coordinates.y + 1)) * texture.array.uvEnd
|
||||
val end = texture.array.uvEnd ?: Vec2i(1.0f)
|
||||
uvStart = Vec2(1.0f / 4 * coordinates.x, 1.0f / 2 * coordinates.y) * end
|
||||
uvEnd = Vec2(1.0f / 4 * (coordinates.x + 1), 1.0f / 2 * (coordinates.y + 1)) * end
|
||||
meshInvalid = true
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ import kotlin.math.pow
|
||||
class SunRenderer(
|
||||
sky: SkyRenderer,
|
||||
) : PlanetRenderer(sky) {
|
||||
override val texture = sky.context.textures.staticTextures.createTexture(SUN)
|
||||
override val texture = sky.context.textures.staticTextures.create(SUN)
|
||||
|
||||
public override fun calculateAngle(): Float {
|
||||
val time = sky.context.connection.world.time
|
||||
|
@ -42,8 +42,8 @@ abstract class TextureManager {
|
||||
if (this::debugTexture.isInitialized) {
|
||||
throw IllegalStateException("Already initialized!")
|
||||
}
|
||||
debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
|
||||
whiteTexture = CodeTexturePart(texture = staticTextures.createTexture(minosoft("white").texture(), mipmaps = false), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
|
||||
debugTexture = staticTextures.create(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
|
||||
whiteTexture = CodeTexturePart(texture = staticTextures.create(minosoft("white").texture(), mipmaps = false), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
|
||||
}
|
||||
|
||||
fun initializeSkins(connection: PlayConnection) {
|
||||
|
@ -13,10 +13,14 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.base.texture.array
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.ThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.runnable.ForcePooledRunnable
|
||||
import de.bixilon.kutil.concurrent.pool.runnable.SimplePoolRunnable
|
||||
import de.bixilon.kutil.latch.AbstractLatch
|
||||
import de.bixilon.kutil.latch.AbstractLatch.Companion.child
|
||||
import de.bixilon.minosoft.assets.util.InputStreamUtil.readAsString
|
||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
@ -28,6 +32,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file.PNGTex
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import de.bixilon.minosoft.util.json.Jackson
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
abstract class StaticTextureArray(
|
||||
val context: RenderContext,
|
||||
@ -50,10 +55,9 @@ abstract class StaticTextureArray(
|
||||
return texture
|
||||
}
|
||||
|
||||
operator fun plusAssign(texture: Texture) = pushTexture(texture)
|
||||
operator fun plusAssign(texture: Texture) = push(texture)
|
||||
|
||||
|
||||
fun pushTexture(texture: Texture) {
|
||||
fun push(texture: Texture) {
|
||||
lock.lock()
|
||||
other += texture
|
||||
lock.unlock()
|
||||
@ -62,7 +66,7 @@ abstract class StaticTextureArray(
|
||||
}
|
||||
}
|
||||
|
||||
fun createTexture(resourceLocation: ResourceLocation, mipmaps: Boolean = true, properties: Boolean = true, factory: (mipmaps: Int) -> Texture = { PNGTexture(resourceLocation, mipmaps = it) }): Texture {
|
||||
open fun create(resourceLocation: ResourceLocation, mipmaps: Boolean = true, properties: Boolean = true, factory: (mipmaps: Int) -> Texture = { PNGTexture(resourceLocation, mipmaps = it) }): Texture {
|
||||
lock.lock()
|
||||
named[resourceLocation]?.let { lock.unlock(); return it }
|
||||
|
||||
@ -92,6 +96,30 @@ abstract class StaticTextureArray(
|
||||
return null
|
||||
}
|
||||
|
||||
abstract fun findResolution(size: Vec2i): Vec2i
|
||||
|
||||
abstract fun load(latch: AbstractLatch)
|
||||
|
||||
private fun load(latch: AbstractLatch, textures: Collection<Texture>) {
|
||||
for (texture in textures) {
|
||||
if (texture.state != TextureStates.DECLARED) continue
|
||||
|
||||
latch.inc()
|
||||
DefaultThreadPool += SimplePoolRunnable(ThreadPool.HIGH) { texture.load(context); latch.dec() }
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun load(animationIndex: AtomicInteger, textures: Collection<Texture>)
|
||||
|
||||
fun load(latch: AbstractLatch) {
|
||||
val latch = latch.child(0)
|
||||
load(latch, named.values)
|
||||
load(latch, other)
|
||||
latch.await()
|
||||
|
||||
val animationIndex = AtomicInteger()
|
||||
load(animationIndex, named.values)
|
||||
load(animationIndex, other)
|
||||
|
||||
state = TextureArrayStates.LOADED
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.array
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
|
||||
data class TextureArrayProperties(
|
||||
val uvEnd: Vec2,
|
||||
val uvEnd: Vec2?,
|
||||
val size: Int,
|
||||
val pixel: Vec2,
|
||||
val pixel: Float,
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
* Copyright (C) 2020-2023 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
@ -15,8 +15,8 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.array
|
||||
|
||||
enum class TextureArrayStates {
|
||||
DECLARED,
|
||||
PRE_LOADED,
|
||||
LOADED,
|
||||
UPLOADED,
|
||||
UNLOADED,
|
||||
;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.memory.Memo
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
|
||||
|
||||
@Deprecated("Slow, because of synchronized loading of mcmeta")
|
||||
class SpriteTexture(private val original: Texture) : Texture {
|
||||
override var array: TextureArrayProperties by original::array
|
||||
override var properties: ImageProperties by original::properties
|
||||
|
@ -14,15 +14,11 @@
|
||||
package de.bixilon.minosoft.gui.rendering.system.opengl.texture
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.ThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.runnable.SimplePoolRunnable
|
||||
import de.bixilon.kutil.latch.AbstractLatch
|
||||
import de.bixilon.kutil.latch.SimpleLatch
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.StaticTextureArray
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayProperties
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayStates
|
||||
@ -46,92 +42,17 @@ class OpenGLTextureArray(
|
||||
async: Boolean,
|
||||
mipmaps: Int,
|
||||
) : StaticTextureArray(context, async, mipmaps) {
|
||||
private var textureIds = IntArray(TEXTURE_RESOLUTION_ID_MAP.size) { -1 }
|
||||
private var handles = IntArray(RESOLUTIONS.size) { -1 }
|
||||
|
||||
private val texturesByResolution = Array<MutableList<Texture>>(TEXTURE_RESOLUTION_ID_MAP.size) { mutableListOf() }
|
||||
private val lastTextureId = IntArray(TEXTURE_RESOLUTION_ID_MAP.size)
|
||||
private val resolution = Array<MutableList<Texture>>(RESOLUTIONS.size) { mutableListOf() }
|
||||
private val lastTextureId = IntArray(RESOLUTIONS.size)
|
||||
|
||||
init {
|
||||
context.system.unsafeCast<OpenGLRenderSystem>().textureBindingIndex += TEXTURE_RESOLUTION_ID_MAP.size
|
||||
context.system.unsafeCast<OpenGLRenderSystem>().textureBindingIndex += RESOLUTIONS.size
|
||||
}
|
||||
|
||||
|
||||
private fun preLoad(latch: AbstractLatch, textures: Collection<Texture>) {
|
||||
for (texture in textures) {
|
||||
if (texture.state != TextureStates.DECLARED) {
|
||||
latch.dec()
|
||||
continue
|
||||
}
|
||||
DefaultThreadPool += SimplePoolRunnable(ThreadPool.HIGH) { texture.load(context); latch.dec() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun preLoad(animationIndex: AtomicInteger, textures: Collection<Texture>) {
|
||||
for (texture in textures) {
|
||||
check(texture.size.x <= TEXTURE_MAX_RESOLUTION) { "Texture's width exceeds $TEXTURE_MAX_RESOLUTION (${texture.size.x})" }
|
||||
check(texture.size.y <= TEXTURE_MAX_RESOLUTION) { "Texture's height exceeds $TEXTURE_MAX_RESOLUTION (${texture.size.y})" }
|
||||
|
||||
|
||||
var arrayId = -1
|
||||
var arrayResolution = -1
|
||||
|
||||
for (i in TEXTURE_RESOLUTION_ID_MAP.indices) {
|
||||
arrayResolution = TEXTURE_RESOLUTION_ID_MAP[i]
|
||||
if (texture.size.x <= arrayResolution && texture.size.y <= arrayResolution) {
|
||||
arrayId = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val uvEnd: Vec2? = if (texture.size.x == arrayResolution && texture.size.y == arrayResolution) {
|
||||
null
|
||||
} else {
|
||||
Vec2(texture.size) / arrayResolution
|
||||
}
|
||||
val singlePixelSize = Vec2(1.0f) / arrayResolution
|
||||
val array = TextureArrayProperties(uvEnd ?: Vec2(1.0f, 1.0f), arrayResolution, singlePixelSize)
|
||||
|
||||
if (texture is SpriteTexture) {
|
||||
val animationIndex = animationIndex.getAndIncrement()
|
||||
val animation = TextureAnimation(texture)
|
||||
animator.animations += animation
|
||||
texture.renderData = OpenGLTextureData(-1, -1, uvEnd, animationIndex)
|
||||
for (split in texture.splitTextures) {
|
||||
split.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, animationIndex)
|
||||
split.array = array
|
||||
texturesByResolution[arrayId] += split
|
||||
}
|
||||
for (frame in texture.properties.animation!!.frames) {
|
||||
frame.texture = texture.splitTextures[frame.index]
|
||||
}
|
||||
} else {
|
||||
texturesByResolution[arrayId] += texture
|
||||
texture.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, -1)
|
||||
texture.array = array
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun load(latch: AbstractLatch) {
|
||||
if (state == TextureArrayStates.LOADED || state == TextureArrayStates.PRE_LOADED) {
|
||||
return
|
||||
}
|
||||
val preLoadLatch = SimpleLatch(named.size + other.size)
|
||||
preLoad(preLoadLatch, named.values)
|
||||
preLoad(preLoadLatch, other)
|
||||
preLoadLatch.await()
|
||||
|
||||
val animationIndex = AtomicInteger()
|
||||
preLoad(animationIndex, named.values)
|
||||
preLoad(animationIndex, other)
|
||||
|
||||
state = TextureArrayStates.PRE_LOADED
|
||||
}
|
||||
|
||||
|
||||
private fun loadSingleArray(resolution: Int, textures: List<Texture>): Int {
|
||||
private fun upload(resolution: Int, textures: List<Texture>): Int {
|
||||
val textureId = OpenGLTextureUtil.createTextureArray(mipmaps)
|
||||
|
||||
for (level in 0..mipmaps) {
|
||||
@ -154,22 +75,20 @@ class OpenGLTextureArray(
|
||||
|
||||
|
||||
override fun upload(latch: AbstractLatch?) {
|
||||
var totalLayers = 0
|
||||
for ((index, textures) in texturesByResolution.withIndex()) {
|
||||
if (textures.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
textureIds[index] = loadSingleArray(TEXTURE_RESOLUTION_ID_MAP[index], textures)
|
||||
totalLayers += textures.size
|
||||
var total = 0
|
||||
for ((index, textures) in resolution.withIndex()) {
|
||||
if (textures.isEmpty()) continue
|
||||
handles[index] = upload(RESOLUTIONS[index], textures)
|
||||
total += textures.size
|
||||
}
|
||||
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { "Loaded ${named.size} textures containing ${animator.animations.size} animated ones, split into $totalLayers layers!" }
|
||||
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { "Loaded ${named.size} textures containing ${animator.animations.size} animated ones, split into $total layers!" }
|
||||
|
||||
animator.init()
|
||||
state = TextureArrayStates.LOADED
|
||||
state = TextureArrayStates.UPLOADED
|
||||
}
|
||||
|
||||
override fun activate() {
|
||||
for ((index, textureId) in textureIds.withIndex()) {
|
||||
for ((index, textureId) in handles.withIndex()) {
|
||||
if (textureId == -1) {
|
||||
continue
|
||||
}
|
||||
@ -182,7 +101,7 @@ class OpenGLTextureArray(
|
||||
shader.use()
|
||||
activate()
|
||||
|
||||
for ((index, textureId) in textureIds.withIndex()) {
|
||||
for ((index, textureId) in handles.withIndex()) {
|
||||
if (textureId == -1) {
|
||||
continue
|
||||
}
|
||||
@ -192,8 +111,71 @@ class OpenGLTextureArray(
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEXTURE_MAX_RESOLUTION = 2048
|
||||
val TEXTURE_RESOLUTION_ID_MAP = intArrayOf(16, 32, 64, 128, 256, 512, 1024, TEXTURE_MAX_RESOLUTION) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split
|
||||
override fun findResolution(size: Vec2i): Vec2i {
|
||||
if (size.x >= MAX_RESOLUTION || size.y >= MAX_RESOLUTION) return Vec2i(MAX_RESOLUTION)
|
||||
val array = findArray(size)
|
||||
if (array < 0) return Vec2i(0, 0)
|
||||
return Vec2i(RESOLUTIONS[array])
|
||||
}
|
||||
|
||||
private fun findArray(size: Vec2i): Int {
|
||||
for ((index, resolution) in RESOLUTIONS.withIndex()) {
|
||||
if (size.x > resolution || size.y > resolution) continue
|
||||
return index
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun load(animationIndex: AtomicInteger, arrayId: Int, texture: Texture) {
|
||||
val resolution = RESOLUTIONS[arrayId]
|
||||
val pixel = PIXEL[arrayId]
|
||||
val size = texture.size
|
||||
|
||||
val uvEnd = if (size.x == resolution && size.y == resolution) null else Vec2(size) / resolution
|
||||
val array = TextureArrayProperties(uvEnd, resolution, pixel)
|
||||
|
||||
if (texture !is SpriteTexture) {
|
||||
this.resolution[arrayId] += texture
|
||||
texture.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, -1)
|
||||
texture.array = array
|
||||
return
|
||||
}
|
||||
|
||||
val animationIndex = animationIndex.getAndIncrement()
|
||||
val animation = TextureAnimation(texture)
|
||||
animator.animations += animation
|
||||
|
||||
texture.renderData = OpenGLTextureData(-1, -1, uvEnd, animationIndex)
|
||||
for (split in texture.splitTextures) {
|
||||
split.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, animationIndex)
|
||||
split.array = array
|
||||
this.resolution[arrayId] += split
|
||||
}
|
||||
for (frame in texture.properties.animation!!.frames) {
|
||||
frame.texture = texture.splitTextures[frame.index]
|
||||
}
|
||||
}
|
||||
|
||||
override fun load(animationIndex: AtomicInteger, textures: Collection<Texture>) {
|
||||
for (texture in textures) {
|
||||
if (texture.size.x > MAX_RESOLUTION || texture.size.y > MAX_RESOLUTION) {
|
||||
Log.log(LogMessageType.LOADING, LogLevels.WARN) { "Texture $texture exceeds max resolution ($MAX_RESOLUTION): ${texture.size}" }
|
||||
continue
|
||||
}
|
||||
|
||||
val arrayId = findArray(texture.size)
|
||||
if (arrayId == -1) {
|
||||
Log.log(LogMessageType.LOADING, LogLevels.WARN) { "Can not find texture array for $arrayId" }
|
||||
continue
|
||||
}
|
||||
load(animationIndex, arrayId, texture)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_RESOLUTION = 2048
|
||||
val RESOLUTIONS = intArrayOf(16, 32, 64, 128, 256, 512, 1024, MAX_RESOLUTION) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split
|
||||
val PIXEL = FloatArray(RESOLUTIONS.size) { 1.0f / RESOLUTIONS[it] }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user