KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/defs/Drawable.kt

176 lines
6.2 KiB
Kotlin

package ru.dbotthepony.kstarbound.defs
import com.google.common.collect.ImmutableList
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.defs.image.ImageReference
import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.math.LineF
import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.util.contains
import ru.dbotthepony.kvector.arrays.Matrix3f
import ru.dbotthepony.kvector.arrays.Matrix4fStack
import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2f
import ru.dbotthepony.kvector.vector.Vector3f
sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) {
class Line(
val line: LineF,
val width: Float,
position: Vector2f = Vector2f.ZERO,
color: RGBAColor = RGBAColor.WHITE,
fullbright: Boolean = false
) : Drawable(position, color, fullbright) {
override fun render(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {
TODO("Not yet implemented")
}
}
class Poly(
val vertices: ImmutableList<Vector2f>,
position: Vector2f = Vector2f.ZERO,
color: RGBAColor = RGBAColor.WHITE,
fullbright: Boolean = false
) : Drawable(position, color, fullbright) {
override fun render(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {
TODO("Not yet implemented")
}
}
class Image(
val path: ImageReference,
val transform: Matrix3f,
val centered: Boolean,
position: Vector2f = Vector2f.ZERO,
color: RGBAColor = RGBAColor.WHITE,
fullbright: Boolean = false
) : Drawable(position, color, fullbright) {
override fun with(values: (String) -> String?): Image {
val newPath = path.with(values)
if (newPath == path) {
return this
}
return Image(newPath, transform, centered, position, color, fullbright)
}
override fun render(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {
val sprite = path.sprite ?: return
val texture = gl.loadTexture(path.imagePath.value!!)
if (centered) {
gl.quadTexture(texture) {
it.quad(x - (sprite.width(texture.width) / PIXELS_IN_STARBOUND_UNITf) * 0.5f, y - (sprite.height(texture.height) / PIXELS_IN_STARBOUND_UNITf) * 0.5f, x + sprite.width(texture.width) / PIXELS_IN_STARBOUND_UNITf * 0.5f, y + sprite.height(texture.height) / PIXELS_IN_STARBOUND_UNITf * 0.5f, QuadTransformers.uv(sprite.compute(texture)))
}
} else {
gl.quadTexture(texture) {
it.quad(x, y, x + sprite.width(texture.width) / PIXELS_IN_STARBOUND_UNITf, y + sprite.height(texture.height) / PIXELS_IN_STARBOUND_UNITf, QuadTransformers.uv(sprite.compute(texture)))
}
}
}
}
class Empty(position: Vector2f = Vector2f.ZERO, color: RGBAColor = RGBAColor.WHITE, fullbright: Boolean = false) : Drawable(position, color, fullbright) {
override fun render(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {}
}
open fun with(values: (String) -> String?): Drawable {
return this
}
abstract fun render(gl: GLStateTracker = GLStateTracker.current(), stack: Matrix4fStack = gl.matrixStack, x: Float = 0f, y: Float = 0f)
companion object {
val EMPTY = Empty()
private val LOGGER = LogManager.getLogger()
}
class Adapter(gson: Gson) : TypeAdapter<Drawable>() {
private val lines = gson.getAdapter(LineF::class.java)
private val objects = gson.getAdapter(JsonObject::class.java)
private val vectors = gson.getAdapter(Vector2f::class.java)
private val vectors3 = gson.getAdapter(Vector3f::class.java)
private val colors = gson.getAdapter(RGBAColor::class.java)
private val images = gson.getAdapter(ImageReference::class.java)
private val vertices = gson.getAdapter(TypeToken.getParameterized(ImmutableList::class.java, Vector2f::class.java)) as TypeAdapter<ImmutableList<Vector2f>>
override fun write(out: JsonWriter?, value: Drawable?) {
TODO("Not yet implemented")
}
override fun read(`in`: JsonReader): Drawable {
if (`in`.consumeNull()) {
return EMPTY
} else {
val value = objects.read(`in`)!!
val position = value["position"]?.let { vectors.fromJsonTree(it) } ?: Vector2f.ZERO
val color = value["color"]?.let { colors.fromJsonTree(it) } ?: RGBAColor.WHITE
val fullbright = value["fullbright"]?.asBoolean ?: false
if ("line" in value) {
return Line(lines.fromJsonTree(value["line"]), value["width"].asFloat, position, color, fullbright)
} else if ("poly" in value) {
return Poly(vertices.fromJsonTree(value["poly"]), position, color, fullbright)
} else if ("image" in value) {
val image = images.fromJsonTree(value["image"])
val mat = Matrix3f.identity()
if ("transformation" in value) {
val array = value["transformation"].asJsonArray
// original starbound use GLM, which reflects OpenGL, which in turn make matrices row-major
val row0 = vectors3.fromJsonTree(array[0])
val row1 = vectors3.fromJsonTree(array[1])
val row2 = vectors3.fromJsonTree(array[2])
mat.r00 = row0.x
mat.r01 = row0.y
mat.r02 = row0.z
mat.r10 = row1.x
mat.r11 = row1.y
mat.r12 = row1.z
mat.r20 = row2.x
mat.r21 = row2.y
mat.r22 = row2.z
} else {
if ("rotation" in value) {
LOGGER.warn("Rotation is not supported yet (required by ${image.raw})")
}
if ("mirrored" in value && value["mirrored"].asBoolean) {
mat.scale(-1f, -1f)
}
if ("scale" in value) {
if (value["scale"].isJsonArray) {
mat.scale(vectors.fromJsonTree(value["scale"]))
} else {
val scale = value["scale"].asFloat
mat.scale(scale, scale)
}
}
}
return Image(image, mat, value["centered"]?.asBoolean ?: false, position, color, fullbright)
} else {
return Empty(position, color, fullbright)
}
}
}
}
}