All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.ru.casperix.spine.renderer.SkeletonRenderer.kt Maven / Gradle / Ivy

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.Color
import ru.casperix.math.color.rgb.toRGBColor
import ru.casperix.math.color.rgba.toRGBAColor
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.VectorGraphic
import ru.casperix.renderer.vector.VectorShape
import ru.casperix.renderer.vector.builder.VectorGraphicBuilder
import ru.casperix.renderer.vector.builder.VertexDataBuilder
import ru.casperix.renderer.vector.vertex.ColorFormat
import ru.casperix.spine.RegionAttachment
import ru.casperix.spine.Skeleton

object SkeletonRenderer {
    data class Config(
        val debugFont: FontReference = FontReference("Serif", 24),
        val debugFontScale: Float = 0.5f,
        val textureConfig: TextureConfig = TextureConfig(TextureFilter.LINEAR, TextureFilter.LINEAR),
    )

    var defaultConfig = Config()

    fun Renderer2D.drawSkeleton(skeleton: Skeleton, worldMatrix: Matrix3f, custom: Config? = null) {
        drawGraphic(assemble(skeleton, custom), worldMatrix)
    }

    fun assemble(skeleton: Skeleton, custom: Config? = null) = VectorGraphicBuilder.build {
        skeleton.drawOrder.mapNotNull { slot ->
            val config = custom ?: defaultConfig
            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()

                    add(SimpleMaterial(Texture2D(attachment.region.pixels, config.textureConfig)), ColorFormat.RGB, true) {
                        addQuad { index->
                            val color = slot.color.toRGB()
                            val alpha = slot.color.toAlpha()
                            val result = (color.toVector3f() * alpha).toRGBColor().toRGBA(alpha)


                            setPosition2(positions.getVertex(index))
                            setTextureCoord(textures.getVertex(index))
                            setColor(result)
                        }
                    }
                }

                else -> {
                    null
                }
            }
        }
    }

    fun Renderer2D.drawSkeletonDebug(skeleton: Skeleton, worldMatrix: Matrix3f, custom: Config? = null) = drawGraphic {
        val config = custom ?: defaultConfig
        addQuadContour(SimpleMaterial(Color.BLACK), skeleton.data.area.toQuad().convert { worldMatrix.transform(it) }, 2f)
        addQuad(SimpleMaterial(Color.RED), Box2f.byRadius(Vector2f.ZERO, Vector2f(2f, 16f)).toQuad(), Box2f.ONE.toQuad(), worldMatrix)
        addQuad(SimpleMaterial(Color.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) {
                addCircle(Color.RED, center, 10f, 12f, 64)
            } else {
                val line = LineSegment2f.byDelta(Vector2f.ZERO, Vector2f(bone.data.length, 0f)).toLine().convert { boneSummaryMatrix.transform(it) }
                addArrow(Color.RED, line.toSegment(), 2f, UniformArrowMode(0.5f, 20f, 10f))
            }

            drawText(bone.data.name, createUniformTransform(boneSummaryMatrix, config), config.debugFont)
        }
    }

    private fun createUniformTransform(matrix: Matrix3f, config: Config): Matrix3f {
        val pivot = matrix.transform(Vector2f.ZERO)
        val xAxis = matrix.transform(Vector2f.X)
        return Matrix3f.scale(
            vectorOf(
                config.debugFontScale,
                -config.debugFontScale
            )
        ) * Matrix3f.rotate(-RadianFloat.byDirection(xAxis - pivot)) * Matrix3f.translate(pivot)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy