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.kommons.util.Either import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.matrix.Matrix3f import ru.dbotthepony.kommons.vector.Vector2f import ru.dbotthepony.kommons.vector.Vector3f import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.render.IGeometryLayer import ru.dbotthepony.kstarbound.defs.image.SpriteReference import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kommons.gson.consumeNull import ru.dbotthepony.kommons.gson.contains import ru.dbotthepony.kstarbound.math.Line2d sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) { @JsonFactory data class Transformations(val centered: Boolean = false, val rotation: Float = 0f, val mirrored: Boolean = false, val scale: Either = Either.left(1f)) class Line( val line: Line2d, val width: Float, position: Vector2f = Vector2f.ZERO, color: RGBAColor = RGBAColor.WHITE, fullbright: Boolean = false ) : Drawable(position, color, fullbright) { override fun render(client: StarboundClient, layer: IGeometryLayer, x: Float, y: Float) { TODO("Not yet implemented") } override fun flop(): Drawable { TODO("Not yet implemented") } } class Poly( val vertices: ImmutableList, position: Vector2f = Vector2f.ZERO, color: RGBAColor = RGBAColor.WHITE, fullbright: Boolean = false ) : Drawable(position, color, fullbright) { override fun render(client: StarboundClient, layer: IGeometryLayer, x: Float, y: Float) { TODO("Not yet implemented") } override fun flop(): Drawable { TODO("Not yet implemented") } } class Image( val path: SpriteReference, val transform: Either, 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, position, color, fullbright) } override fun flop(): Drawable { return Image(path, transform.flatMap({ it.copy().scale(-1f, 1f) }, { it.copy(mirrored = !it.mirrored) }), position, color, fullbright) } override fun render(client: StarboundClient, layer: IGeometryLayer, x: Float, y: Float) { val sprite = path.sprite ?: return val texture = path.image!!.texture val program = if (fullbright) client.programs.positionTexture else client.programs.positionTextureLightmap val mat = transform.map({ it.copy() }, { val mat = Matrix3f.identity() it.scale.map({ mat.scale(it / PIXELS_IN_STARBOUND_UNITf, it / PIXELS_IN_STARBOUND_UNITf) }, { mat.scale(it / PIXELS_IN_STARBOUND_UNITf) }) if (it.centered) { mat.translate(sprite.width / -2f, sprite.height / -2f) } if (it.rotation != 0f) { mat.rotateAroundZ(it.rotation) } if (it.mirrored) { mat.translate(sprite.width.toFloat(), 0f) mat.scale(-1f, 1f) } mat }) val builder = layer.getBuilder(program.config(texture)) mat.preTranslate(x, y) client.stack.last().mulIntoOther(mat) builder.mode(GeometryType.QUADS) builder.vertex(mat, 0f, 0f).uv(sprite.u0, sprite.v0) builder.vertex(mat, 0f + sprite.width, 0f).uv(sprite.u1, sprite.v0) builder.vertex(mat, 0f + sprite.width, 0f + sprite.height).uv(sprite.u1, sprite.v1) builder.vertex(mat, 0f, 0f + sprite.height).uv(sprite.u0, sprite.v1) } } class Empty(position: Vector2f = Vector2f.ZERO, color: RGBAColor = RGBAColor.WHITE, fullbright: Boolean = false) : Drawable(position, color, fullbright) { override fun render(client: StarboundClient, layer: IGeometryLayer, x: Float, y: Float) {} override fun flop(): Drawable { return this } } open fun with(values: (String) -> String?): Drawable { return this } abstract fun render(client: StarboundClient = StarboundClient.current(), layer: IGeometryLayer, x: Float = 0f, y: Float = 0f) /** * mirror along X axis */ abstract fun flop(): Drawable companion object { val EMPTY = Empty() private val LOGGER = LogManager.getLogger() } class Adapter(gson: Gson) : TypeAdapter() { private val lines = gson.getAdapter(Line2d::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(SpriteReference::class.java) private val vertices = gson.getAdapter(TypeToken.getParameterized(ImmutableList::class.java, Vector2f::class.java)) as TypeAdapter> private val transformations = gson.getAdapter(Transformations::class.java) 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"]) if ("transformation" in value) { val mat = Matrix3f.identity() 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 return Image( image, Either.left(mat), position, color, fullbright ) } else { return Image( image, Either.right(transformations.fromJsonTree(value)), position, color, fullbright ) } } else { return Empty(position, color, fullbright) } } } } }