Have textures be logically loaded from Image itself

This commit is contained in:
DBotThePony 2023-10-12 08:10:37 +07:00
parent c8488c3565
commit 0b163481e7
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 75 additions and 51 deletions

View File

@ -190,6 +190,8 @@ object Starbound : ISBFileLocator {
registerTypeAdapterFactory(ItemReference.Factory(STRINGS))
registerTypeAdapterFactory(TreasurePoolDefinition.Companion)
registerTypeAdapter(Image.Companion)
registerTypeAdapterFactory(with(RegistryReferenceFactory()) {
add(Registries.tiles)
add(Registries.tileModifiers)

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.client
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import org.apache.logging.log4j.LogManager
import org.lwjgl.BufferUtils
import org.lwjgl.glfw.Callbacks
@ -73,6 +74,7 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.time.Duration
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.ForkJoinWorkerThread
import java.util.concurrent.atomic.AtomicInteger
@ -282,13 +284,15 @@ class StarboundClient : Closeable {
val stack = Matrix3fStack()
// минимальное время хранения 5 минут и...
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
val named2DTextures0: Cache<Image, GLTexture2D> = Caffeine.newBuilder()
.expireAfterAccess(Duration.ofMinutes(1))
.scheduler(Scheduler.systemScheduler())
.build()
// ...бесконечное хранение пока кто-то все ещё использует текстуру
private val named2DTextures1: Cache<String, GLTexture2D> = Caffeine.newBuilder()
val named2DTextures1: Cache<Image, GLTexture2D> = Caffeine.newBuilder()
.weakValues()
.weakKeys()
.build()
private val fontShaderPrograms = ArrayList<WeakReference<FontProgram>>()
@ -502,44 +506,6 @@ class StarboundClient : Closeable {
fun isSameThread() = thread === Thread.currentThread()
fun loadTexture(path: String): GLTexture2D {
ensureSameThread()
return named2DTextures0.get(path) {
named2DTextures1.get(it) {
val data = Image.get(it)
if (data == null) {
LOGGER.error("Texture $it is not found!")
missingTexture
} else {
val tex = GLTexture2D(data.width, data.height, when (data.amountOfChannels) {
1 -> GL_R8
3 -> GL_RGB8
4 -> GL_RGBA8
else -> throw IllegalArgumentException("Unknown amount of channels in $it: ${data.amountOfChannels}")
})
val fileFormat = when (data.amountOfChannels) {
1 -> GL_RED
3 -> GL_RGB
4 -> GL_RGBA
else -> throw IllegalArgumentException("Unknown amount of channels in $it: ${data.amountOfChannels}")
}
data.data.thenApplyAsync({
tex.upload(fileFormat, GL_UNSIGNED_BYTE, it)
tex.textureMinFilter = GL_NEAREST
tex.textureMagFilter = GL_NEAREST
}, foregroundExecutor)
tex
}
}
}
}
fun newEBO() = BufferObject.EBO()
fun newVBO() = BufferObject.VBO()
fun newVAO() = VertexArrayObject()

View File

@ -7,7 +7,6 @@ import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL45.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.*
import ru.dbotthepony.kstarbound.client.gl.shader.UberShader
@ -16,12 +15,10 @@ import ru.dbotthepony.kstarbound.defs.tile.*
import ru.dbotthepony.kstarbound.world.api.ITileAccess
import ru.dbotthepony.kstarbound.world.api.ITileState
import ru.dbotthepony.kstarbound.world.api.TileColor
import ru.dbotthepony.kvector.arrays.Matrix3f
import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2i
import java.time.Duration
import java.util.concurrent.Callable
import kotlin.collections.HashMap
/**
* Хранит в себе программы для отрисовки определённых [TileDefinition]
@ -116,7 +113,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
}
val client get() = renderers.client
val texture = def.renderParameters.texture?.imagePath?.value?.let { client.loadTexture(it).also { it.textureMagFilter = GL_NEAREST }}
val texture = def.renderParameters.texture?.image?.texture?.also { it.textureMagFilter = GL_NEAREST }
val equalityTester: EqualityRuleTester = when (def) {
is TileDefinition -> TileEqualityTester(def)
@ -152,7 +149,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
var mins = piece.texturePosition
var maxs = piece.texturePosition + piece.textureSize
if (def.renderParameters.variants != 0 && piece.variantStride != null && piece.texture == null) {
if (def.renderParameters.variants != 0 && piece.variantStride != null && piece.image == null) {
val variant = (getter.randomDoubleFor(pos) * def.renderParameters.variants).toInt()
mins += piece.variantStride * variant
maxs += piece.variantStride * variant
@ -187,11 +184,11 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
): TestResult {
if (matchPiece.test(getter, equalityTester, pos)) {
for (renderPiece in matchPiece.pieces) {
if (renderPiece.piece.texture != null) {
if (renderPiece.piece.image != null) {
val program = if (isBackground) {
renderers.background(client.loadTexture(renderPiece.piece.texture!!))
renderers.background(renderPiece.piece.image!!.texture)
} else {
renderers.foreground(client.loadTexture(renderPiece.piece.texture!!))
renderers.foreground(renderPiece.piece.image!!.texture)
}
tesselateAt(

View File

@ -82,7 +82,7 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
override fun render(client: StarboundClient, layer: IGeometryLayer, x: Float, y: Float) {
val sprite = path.sprite ?: return
val texture = client.loadTexture(path.imagePath.value!!)
val texture = path.image!!.texture
val program = if (fullbright) client.programs.positionTexture else client.programs.positionTextureLightmap
val mat = transform.map({ it.copy() }, {

View File

@ -11,16 +11,22 @@ import com.google.gson.JsonArray
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL45
import org.lwjgl.stb.STBImage
import org.lwjgl.system.MemoryUtil
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITi
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.api.IStarboundFile
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.io.stream2STBIO
import ru.dbotthepony.kstarbound.util.contains
import ru.dbotthepony.kstarbound.util.get
@ -56,6 +62,7 @@ class Image private constructor(
private val spritesInternal = Object2ObjectLinkedOpenHashMap<String, Sprite>()
private var dataRef: WeakReference<ByteBuffer>? = null
private val lock = ReentrantLock()
//private val _texture = ThreadLocal<WeakReference<GLTexture2D>>()
init {
if (sprites == null) {
@ -97,6 +104,43 @@ class Image private constructor(
}
}
val texture: GLTexture2D get() {
//val get = _texture.get()?.get()
val client = StarboundClient.current()
/*if (get != null) {
// update access time
client.named2DTextures0.getIfPresent(this)
client.named2DTextures1.getIfPresent(this)
return get
}*/
val value = client.named2DTextures0.get(this) {
client.named2DTextures1.get(this) {
val (memFormat, fileFormat) = when (amountOfChannels) {
1 -> GL45.GL_R8 to GL45.GL_RED
3 -> GL45.GL_RGB8 to GL45.GL_RGB
4 -> GL45.GL_RGBA8 to GL45.GL_RGBA
else -> throw IllegalArgumentException("Unknown amount of channels in $it: $amountOfChannels")
}
val tex = GLTexture2D(width, height, memFormat)
data.thenApplyAsync({
tex.upload(fileFormat, GL45.GL_UNSIGNED_BYTE, it)
tex.textureMinFilter = GL45.GL_NEAREST
tex.textureMagFilter = GL45.GL_NEAREST
}, client.foregroundExecutor)
tex
}
}
//_texture.set(WeakReference(value))
return value
}
val size = Vector2i(width, height)
val sprites: Map<String, Sprite> = Collections.unmodifiableMap(this.spritesInternal)
val first: Sprite = this.spritesInternal.values.first()
@ -250,7 +294,7 @@ class Image private constructor(
}
companion object {
companion object : TypeAdapter<Image>() {
const val FILL_RATIO = 1 / (PIXELS_IN_STARBOUND_UNIT * PIXELS_IN_STARBOUND_UNIT)
private val objects by lazy { Starbound.gson.getAdapter(JsonObject::class.java) }
@ -317,6 +361,20 @@ class Image private constructor(
}.orElse(null)
}
override fun write(out: JsonWriter, value: Image?) {
if (value == null)
out.nullValue()
else
out.value(value.path)
}
override fun read(`in`: JsonReader): Image? {
if (`in`.consumeNull())
return null
else
return get(`in`.nextString())
}
private fun generateFakeNames(dimensions: Vector2i): JsonArray {
return JsonArray(dimensions.y).also {
var stripElem = 0

View File

@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.defs.image.Image
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.util.WriteOnce
import ru.dbotthepony.kstarbound.world.api.ITileAccess
@ -12,7 +13,7 @@ import ru.dbotthepony.kvector.vector.Vector2i
@JsonFactory
data class RenderPiece(
val texture: String? = null,
val image: Image? = null,
val textureSize: Vector2i,
val texturePosition: Vector2i,
val colorStride: Vector2i? = null,