nageyqma·git
HTTPS https://git.nageyqma.icu/nageyqma/odin_next.git SSH git@git.nageyqma.icu:nageyqma/odin_next.git

HTTPS 链接可直接 git clone;推送代码请使用 SSH。

7.4 KB · 172 行raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
package com.odtheking.odin.utils.render

import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.renderpearl.api.GpuFormat
import com.mojang.renderpearl.api.pipeline.RenderPipeline
import com.mojang.renderpearl.api.textures.FilterMode
import com.mojang.renderpearl.api.vertex.VertexFormat
import com.odtheking.odin.OdinMod.mc
import com.odtheking.odin.utils.render.CustomRenderPipelines.PIPELINE_ROUND_RECT
import com.odtheking.odin.utils.render.CustomRenderPipelines.PIPELINE_ROUND_RECT_SHADOW
import com.odtheking.odin.utils.render.CustomRenderPipelines.PIPELINE_ROUND_RECT_TEXTURED
import net.minecraft.client.gui.GuiGraphicsExtractor
import net.minecraft.client.gui.navigation.ScreenRectangle
import net.minecraft.client.gui.render.TextureSetup
import net.minecraft.client.renderer.state.gui.GuiElementRenderState
import net.minecraft.resources.Identifier
import org.joml.Matrix3x2f
import org.joml.Matrix3x2fc
import kotlin.math.*

object RoundedRectRenderer {

    private const val AA_PADDING = 2f
    private const val HALF_EXTENT_SCALE = 8f
    private const val RADIUS_SCALE = 4f
    private const val MAX_RADIUS = 255f / RADIUS_SCALE

    val FORMAT: VertexFormat = VertexFormat.builder(0)
        .addAttribute(DefaultVertexFormat.POSITION_SEMANTIC_NAME, GpuFormat.RGB32_FLOAT)
        .addAttribute(DefaultVertexFormat.COLOR_SEMANTIC_NAME, GpuFormat.RGBA8_UNORM)
        .addAttribute(DefaultVertexFormat.UV0_SEMANTIC_NAME, GpuFormat.RG32_FLOAT)
        .addAttribute(DefaultVertexFormat.UV1_SEMANTIC_NAME, GpuFormat.RG16_SINT)
        .addAttribute(DefaultVertexFormat.UV2_SEMANTIC_NAME, GpuFormat.RG16_SINT)
        .addAttribute(DefaultVertexFormat.LINE_WIDTH_SEMANTIC_NAME, GpuFormat.R32_FLOAT)
        .build()

    fun submit(
        context: GuiGraphicsExtractor,
        x0: Float, y0: Float, x1: Float, y1: Float,
        topLeftColor: Int, topRightColor: Int, bottomRightColor: Int, bottomLeftColor: Int,
        corners: Corners, outlineWidth: Float,
        texture: Identifier? = null,
        clip: ScreenRectangle? = null
    ) = emit(
        context, x0, y0, x1, y1,
        topLeftColor, topRightColor, bottomRightColor, bottomLeftColor,
        corners, outlineWidth, AA_PADDING,
        if (texture != null) PIPELINE_ROUND_RECT_TEXTURED else PIPELINE_ROUND_RECT,
        textureSetup(texture), clip
    )

    fun submitShadow(
        context: GuiGraphicsExtractor,
        x0: Float, y0: Float, x1: Float, y1: Float,
        color: Int, corners: Corners, blur: Float
    ) = emit(
        context, x0, y0, x1, y1, color, color, color, color,
        corners, blur, AA_PADDING + blur,
        PIPELINE_ROUND_RECT_SHADOW, TextureSetup.noTexture(), null
    )

    private fun emit(
        context: GuiGraphicsExtractor,
        x0: Float, y0: Float, x1: Float, y1: Float,
        topLeftColor: Int, topRightColor: Int, bottomRightColor: Int, bottomLeftColor: Int,
        corners: Corners, edgeWidth: Float, padding: Float,
        pipeline: RenderPipeline, textureSetup: TextureSetup,
        clip: ScreenRectangle?
    ) {
        if ((topLeftColor or topRightColor or bottomRightColor or bottomLeftColor) and ALPHA_MASK == 0) return

        val left = min(x0, x1)
        val top = min(y0, y1)
        val right = max(x0, x1)
        val bottom = max(y0, y1)

        val halfWidth = (right - left) * 0.5f
        val halfHeight = (bottom - top) * 0.5f
        if (halfWidth <= 0f || halfHeight <= 0f) return

        val pose = snapshotPose(context.pose())
        val scissor = context.scissorStack.peek()

        val quadX0 = max(floor(left - padding).toInt(), clip?.left() ?: Int.MIN_VALUE)
        val quadY0 = max(floor(top - padding).toInt(), clip?.top() ?: Int.MIN_VALUE)
        val quadX1 = min(ceil(right + padding).toInt(), clip?.right() ?: Int.MAX_VALUE)
        val quadY1 = min(ceil(bottom + padding).toInt(), clip?.bottom() ?: Int.MAX_VALUE)
        if (quadX0 >= quadX1 || quadY0 >= quadY1) return

        val quad = ScreenRectangle(quadX0, quadY0, quadX1 - quadX0, quadY1 - quadY0).transformOutward(pose)
        val bounds = if (scissor != null) scissor.intersection(quad) else quad

        context.guiRenderState.addGuiElement(
            Element(
                pose,
                quadX0.toFloat(), quadY0.toFloat(), quadX1.toFloat(), quadY1.toFloat(),
                (left + right) * 0.5f, (top + bottom) * 0.5f,
                topLeftColor, topRightColor, bottomRightColor, bottomLeftColor,
                fixedPoint(halfWidth), fixedPoint(halfHeight),
                packRadius(corners.topLeft) or (packRadius(corners.topRight) shl 8),
                packRadius(corners.bottomRight) or (packRadius(corners.bottomLeft) shl 8),
                edgeWidth, pipeline, textureSetup,
                scissor, bounds
            )
        )
    }

    private val NO_TEXTURE: TextureSetup = TextureSetup.noTexture()

    private fun textureSetup(texture: Identifier?): TextureSetup {
        if (texture == null) return NO_TEXTURE

        val view = mc.textureManager.getTexture(texture).textureView
        return TextureSetup.singleTexture(view, RenderSystem.getSamplerCache().getClampToEdge(FilterMode.LINEAR))
    }

    private var cachedPose: Matrix3x2f? = null

    private fun snapshotPose(current: Matrix3x2fc): Matrix3x2f {
        val cached = cachedPose
        if (cached != null && cached.equals(current, 0f)) return cached
        return Matrix3x2f(current).also { cachedPose = it }
    }

    private const val ALPHA_MASK = 0xFF shl 24

    private fun fixedPoint(value: Float): Int =
        (value * HALF_EXTENT_SCALE).roundToInt().coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt())

    private fun packRadius(radius: Float): Int =
        (radius.coerceIn(0f, MAX_RADIUS) * RADIUS_SCALE).roundToInt().coerceIn(0, 255)

    private class Element(
        private val pose: Matrix3x2fc,
        private val x0: Float, private val y0: Float,
        private val x1: Float, private val y1: Float,
        private val centerX: Float, private val centerY: Float,
        private val topLeftColor: Int, private val topRightColor: Int,
        private val bottomRightColor: Int, private val bottomLeftColor: Int,
        private val packedHalfWidth: Int, private val packedHalfHeight: Int,
        private val packedRadiiTop: Int, private val packedRadiiBottom: Int,
        private val edgeWidth: Float,
        private val pipeline: RenderPipeline,
        private val textureSetup: TextureSetup,
        private val scissorArea: ScreenRectangle?,
        private val bounds: ScreenRectangle?
    ) : GuiElementRenderState {

        override fun pipeline() = pipeline
        override fun textureSetup(): TextureSetup = textureSetup
        override fun scissorArea() = scissorArea
        override fun bounds() = bounds

        override fun buildVertices(consumer: VertexConsumer) {
            vertex(consumer, x0, y0, topLeftColor)
            vertex(consumer, x0, y1, bottomLeftColor)
            vertex(consumer, x1, y1, bottomRightColor)
            vertex(consumer, x1, y0, topRightColor)
        }

        private fun vertex(consumer: VertexConsumer, x: Float, y: Float, color: Int) {
            consumer.addVertexWith2DPose(pose, x, y)
                .setColor(color)
                .setUv(x - centerX, y - centerY)
                .setUv1(packedHalfWidth, packedHalfHeight)
                .setUv2(packedRadiiTop, packedRadiiBottom)
                .setLineWidth(edgeWidth)
        }
    }
}