Files
Firmament/src/main/kotlin/util/render/RenderInWorldContext.kt
2024-09-06 20:23:01 +02:00

304 lines
13 KiB
Kotlin

package moe.nea.firmament.util.render
import com.mojang.blaze3d.systems.RenderSystem
import io.github.notenoughupdates.moulconfig.platform.next
import java.lang.Math.pow
import org.joml.Matrix4f
import org.joml.Vector3f
import net.minecraft.client.gl.VertexBuffer
import net.minecraft.client.render.BufferBuilder
import net.minecraft.client.render.BufferRenderer
import net.minecraft.client.render.Camera
import net.minecraft.client.render.GameRenderer
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.render.RenderPhase
import net.minecraft.client.render.RenderTickCounter
import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.client.texture.Sprite
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.MC
@RenderContextDSL
class RenderInWorldContext private constructor(
private val tesselator: Tessellator,
val matrixStack: MatrixStack,
private val camera: Camera,
private val tickCounter: RenderTickCounter,
val vertexConsumers: VertexConsumerProvider.Immediate,
) {
object RenderLayers {
val TRANSLUCENT_TRIS = RenderLayer.of("firmament_translucent_tris",
VertexFormats.POSITION_COLOR,
VertexFormat.DrawMode.TRIANGLES,
RenderLayer.DEFAULT_BUFFER_SIZE,
false, true,
RenderLayer.MultiPhaseParameters.builder()
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
.program(RenderPhase.COLOR_PROGRAM)
.build(false))
val LINES = RenderLayer.of("firmament_rendertype_lines",
VertexFormats.LINES,
VertexFormat.DrawMode.LINES,
RenderLayer.DEFAULT_BUFFER_SIZE,
false, false, // do we need translucent? i dont think so
RenderLayer.MultiPhaseParameters.builder()
.depthTest(RenderPhase.ALWAYS_DEPTH_TEST)
.program(RenderPhase.LINES_PROGRAM)
.build(false)
)
}
fun color(color: me.shedaniel.math.Color) {
color(color.red / 255F, color.green / 255f, color.blue / 255f, color.alpha / 255f)
}
fun color(red: Float, green: Float, blue: Float, alpha: Float) {
RenderSystem.setShaderColor(red, green, blue, alpha)
}
fun block(blockPos: BlockPos) {
matrixStack.push()
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
buildCube(matrixStack.peek().positionMatrix, tesselator)
matrixStack.pop()
}
enum class VerticalAlign {
TOP, BOTTOM, CENTER;
fun align(index: Int, count: Int): Float {
return when (this) {
CENTER -> (index - count / 2F) * (1 + MC.font.fontHeight.toFloat())
BOTTOM -> (index - count) * (1 + MC.font.fontHeight.toFloat())
TOP -> (index) * (1 + MC.font.fontHeight.toFloat())
}
}
}
fun waypoint(position: BlockPos, vararg label: Text) {
text(
position.toCenterPos(),
*label,
Text.literal("§e${FirmFormatters.formatDistance(MC.player?.pos?.distanceTo(position.toCenterPos()) ?: 42069.0)}"),
background = 0xAA202020.toInt()
)
}
fun withFacingThePlayer(position: Vec3d, block: FacingThePlayerContext.() -> Unit) {
matrixStack.push()
matrixStack.translate(position.x, position.y, position.z)
val actualCameraDistance = position.distanceTo(camera.pos)
val distanceToMoveTowardsCamera = if (actualCameraDistance < 10) 0.0 else -(actualCameraDistance - 10.0)
val vec = position.subtract(camera.pos).multiply(distanceToMoveTowardsCamera / actualCameraDistance)
matrixStack.translate(vec.x, vec.y, vec.z)
matrixStack.multiply(camera.rotation)
matrixStack.scale(0.025F, -0.025F, 1F)
FacingThePlayerContext(this).run(block)
matrixStack.pop()
vertexConsumers.drawCurrentLayer()
}
fun sprite(position: Vec3d, sprite: Sprite, width: Int, height: Int) {
texture(
position, sprite.atlasId, width, height, sprite.minU, sprite.minV, sprite.maxU, sprite.maxV
)
}
fun texture(
position: Vec3d, texture: Identifier, width: Int, height: Int,
u1: Float, v1: Float,
u2: Float, v2: Float,
) {
withFacingThePlayer(position) {
texture(texture, width, height, u1, v1, u2, v2)
}
}
fun text(position: Vec3d, vararg texts: Text, verticalAlign: VerticalAlign = VerticalAlign.CENTER, background: Int = 0x70808080) {
withFacingThePlayer(position) {
text(*texts, verticalAlign = verticalAlign, background = background)
}
}
fun tinyBlock(vec3d: Vec3d, size: Float) {
RenderSystem.setShader(GameRenderer::getPositionColorProgram)
matrixStack.push()
matrixStack.translate(vec3d.x, vec3d.y, vec3d.z)
matrixStack.scale(size, size, size)
matrixStack.translate(-.5, -.5, -.5)
buildCube(matrixStack.peek().positionMatrix, tesselator)
matrixStack.pop()
}
fun wireframeCube(blockPos: BlockPos, lineWidth: Float = 10F) {
RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram)
matrixStack.push()
RenderSystem.lineWidth(lineWidth / pow(camera.pos.squaredDistanceTo(blockPos.toCenterPos()), 0.25).toFloat())
matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat())
buildWireFrameCube(matrixStack.peek(), tesselator)
matrixStack.pop()
}
fun line(vararg points: Vec3d, lineWidth: Float = 10F) {
line(points.toList(), lineWidth)
}
fun tracer(toWhere: Vec3d, lineWidth: Float = 3f) {
val cameraForward = Vector3f(0f, 0f, -1f).rotate(camera.rotation)
line(camera.pos.add(Vec3d(cameraForward)), toWhere, lineWidth = lineWidth)
}
fun line(points: List<Vec3d>, lineWidth: Float = 10F) {
RenderSystem.lineWidth(lineWidth)
val buffer = tesselator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
val matrix = matrixStack.peek()
var lastNormal: Vector3f? = null
points.zipWithNext().forEach { (a, b) ->
val normal = Vector3f(b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
.sub(a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
.normalize()
val lastNormal0 = lastNormal ?: normal
lastNormal = normal
buffer.vertex(matrix.positionMatrix, a.x.toFloat(), a.y.toFloat(), a.z.toFloat())
.color(-1)
.normal(matrix, lastNormal0.x, lastNormal0.y, lastNormal0.z)
.next()
buffer.vertex(matrix.positionMatrix, b.x.toFloat(), b.y.toFloat(), b.z.toFloat())
.color(-1)
.normal(matrix, normal.x, normal.y, normal.z)
.next()
}
RenderLayers.LINES.draw(buffer.end())
}
companion object {
private fun doLine(
matrix: MatrixStack.Entry,
buf: BufferBuilder,
i: Float,
j: Float,
k: Float,
x: Float,
y: Float,
z: Float
) {
val normal = Vector3f(x, y, z)
.sub(i, j, k)
.normalize()
buf.vertex(matrix.positionMatrix, i, j, k)
.normal(matrix, normal.x, normal.y, normal.z)
.color(-1)
.next()
buf.vertex(matrix.positionMatrix, x, y, z)
.normal(matrix, normal.x, normal.y, normal.z)
.color(-1)
.next()
}
private fun buildWireFrameCube(matrix: MatrixStack.Entry, tessellator: Tessellator) {
val buf = tessellator.begin(VertexFormat.DrawMode.LINES, VertexFormats.LINES)
for (i in 0..1) {
for (j in 0..1) {
val i = i.toFloat()
val j = j.toFloat()
doLine(matrix, buf, 0F, i, j, 1F, i, j)
doLine(matrix, buf, i, 0F, j, i, 1F, j)
doLine(matrix, buf, i, j, 0F, i, j, 1F)
}
}
BufferRenderer.drawWithGlobalProgram(buf.end())
}
private fun buildCube(matrix: Matrix4f, tessellator: Tessellator) {
val buf = tessellator.begin(VertexFormat.DrawMode.TRIANGLES, VertexFormats.POSITION_COLOR)
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 0.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 0.0F, 1.0F, 1.0F).color(-1).next()
buf.vertex(matrix, 1.0F, 0.0F, 1.0F).color(-1).next()
RenderLayers.TRANSLUCENT_TRIS.draw(buf.end())
}
fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) {
RenderSystem.disableDepthTest()
RenderSystem.enableBlend()
RenderSystem.defaultBlendFunc()
RenderSystem.disableCull()
event.matrices.push()
event.matrices.translate(-event.camera.pos.x, -event.camera.pos.y, -event.camera.pos.z)
val ctx = RenderInWorldContext(
RenderSystem.renderThreadTesselator(),
event.matrices,
event.camera,
event.tickCounter,
event.vertexConsumers
)
block(ctx)
event.matrices.pop()
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
VertexBuffer.unbind()
RenderSystem.enableDepthTest()
RenderSystem.enableCull()
RenderSystem.disableBlend()
}
}
}