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, 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() { 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> 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) } } } } }