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

222 lines
7.0 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.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<Float, Vector2f> = 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<Vector2f>,
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<Matrix3f, Transformations>,
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<Drawable>() {
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<ImmutableList<Vector2f>>
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)
}
}
}
}
}