commonMain.ru.casperix.spine.renderer.SkeletonRenderer.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spine-jvm Show documentation
Show all versions of spine-jvm Show documentation
Signals for all occasions
The newest version!
package ru.casperix.spine.renderer
import ru.casperix.math.angle.float32.RadianFloat
import ru.casperix.math.axis_aligned.float32.Box2f
import ru.casperix.math.color.Colors
import ru.casperix.math.geometry.builder.UniformArrowMode
import ru.casperix.math.geometry.toSegment
import ru.casperix.math.quad_matrix.float32.Matrix3f
import ru.casperix.math.straight_line.float32.LineSegment2f
import ru.casperix.math.vector.float32.Vector2f
import ru.casperix.math.vector.toQuad
import ru.casperix.math.vector.vectorOf
import ru.casperix.multiplatform.font.FontReference
import ru.casperix.multiplatform.text.drawText
import ru.casperix.renderer.Renderer2D
import ru.casperix.renderer.material.SimpleMaterial
import ru.casperix.renderer.material.Texture2D
import ru.casperix.renderer.material.TextureConfig
import ru.casperix.renderer.material.TextureFilter
import ru.casperix.renderer.vector.GeometryBuilder.texturedQuad
import ru.casperix.renderer.vector.VectorGraphic
import ru.casperix.renderer.vector.VectorShape
import ru.casperix.spine.RegionAttachment
import ru.casperix.spine.Skeleton
object SkeletonRenderer {
var debugFont = FontReference("Serif", 24)
var fontScale = 0.5f
var textureConfig = TextureConfig(TextureFilter.LINEAR, TextureFilter.LINEAR)
fun Renderer2D.drawSkeleton(skeleton: Skeleton, worldMatrix: Matrix3f) {
drawGraphic(assemble(skeleton), worldMatrix)
}
fun assemble(skeleton: Skeleton) = VectorGraphic(skeleton.drawOrder.mapNotNull { slot ->
val bone = slot.bone
val boneSummaryMatrix = bone.world
val attachment = slot.attachment
when (attachment) {
is RegionAttachment -> {
val summaryMatrix = attachment.transform.value * boneSummaryMatrix.toMatrix()
val pixels = attachment.region.bounds.toBox2f()
val dimension = pixels.dimension
val positions =
Box2f.byDimension(
-Vector2f(dimension.x, dimension.y) / 2f,
Vector2f(dimension.x, dimension.y)
).toQuad().convert { summaryMatrix.transform(it) }
val textures = Box2f(
pixels.min / attachment.region.pixels.dimension.toVector2f(),
pixels.max / attachment.region.pixels.dimension.toVector2f()
).toQuad()
VectorShape(SimpleMaterial(slot.color, Texture2D(attachment.region.pixels, textureConfig)), texturedQuad(positions, textures))
}
else -> {
null
}
}
})
fun Renderer2D.drawSkeletonDebug(skeleton: Skeleton, worldMatrix: Matrix3f) {
drawQuadContour(SimpleMaterial(Colors.BLACK), skeleton.data.area.toQuad().convert { worldMatrix.transform(it) }, 2f)
drawQuad(SimpleMaterial(Colors.RED), Box2f.byRadius(Vector2f.ZERO, Vector2f(2f, 16f)).toQuad(), Box2f.ONE.toQuad(), worldMatrix)
drawQuad(SimpleMaterial(Colors.RED), Box2f.byRadius(Vector2f.ZERO, Vector2f(16f, 2f)).toQuad(), Box2f.ONE.toQuad(), worldMatrix)
skeleton.bones.forEach { bone ->
val boneSummaryMatrix = bone.world.toMatrix() * worldMatrix
val length = bone.data.length
val center = boneSummaryMatrix.transform(Vector2f.ZERO)
if (length <= 0.1f) {
drawCircle(Colors.RED, center, 10f, 12f, 64)
} else {
val line = LineSegment2f.byDelta(Vector2f.ZERO, Vector2f(bone.data.length, 0f)).toLine().convert { boneSummaryMatrix.transform(it) }
drawArrow(Colors.RED, line.toSegment(), 2f, UniformArrowMode(0.5f, 20f, 10f))
}
drawText(bone.data.name, debugFont, createUniformTransform(boneSummaryMatrix))
}
}
private fun createUniformTransform(matrix: Matrix3f): Matrix3f {
val pivot = matrix.transform(Vector2f.ZERO)
val xAxis = matrix.transform(Vector2f.X)
return Matrix3f.scale(vectorOf(fontScale, -fontScale)) * Matrix3f.rotate(-RadianFloat.byDirection(xAxis - pivot)) * Matrix3f.translate(pivot)
}
}