From 3ead1c998d607c224d66efebe9a8fafaa48c8ea7 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 15 Oct 2022 23:34:06 +0700 Subject: [PATCH] Android glitch effect or something Fixes #116 --- .../otm/capability/MatteryPlayerCapability.kt | 10 +- .../mc/otm/client/ClientEventHandler.kt | 14 +- .../mc/otm/client/render/GlitchRenderer.kt | 505 ++++++++++++++++++ .../mc/otm/client/render/UVStuff.kt | 6 + .../network/MatteryPlayerNetworkChannel.kt | 20 + src/main/resources/coremods/code_injector.js | 143 +---- 6 files changed, 552 insertions(+), 146 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GlitchRenderer.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt index 9aaa953da..cdeedbd5d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability.kt @@ -360,8 +360,14 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial } fun onHurt(event: LivingHurtEvent) { - for (feature in featureMap.values) { - feature.onHurt(event) + if (isAndroid) { + for (feature in featureMap.values) { + feature.onHurt(event) + } + + if (!event.isCanceled) { + sendNetwork(GlitchPacket((event.amount * 300).toLong().coerceAtLeast(200L).coerceAtMost(800L))) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt index b482a941d..3ec1bd18e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientEventHandler.kt @@ -1,5 +1,8 @@ package ru.dbotthepony.mc.otm.client +import com.mojang.blaze3d.pipeline.MainTarget +import com.mojang.blaze3d.platform.GlConst.GL_COLOR_BUFFER_BIT +import com.mojang.blaze3d.platform.GlStateManager import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.BufferUploader @@ -7,6 +10,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.VertexFormat import com.mojang.math.Matrix4f +import net.minecraft.client.Minecraft import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.client.gui.screens.inventory.InventoryScreen import net.minecraft.client.renderer.GameRenderer @@ -16,6 +20,7 @@ import net.minecraftforge.client.event.ScreenEvent import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature import ru.dbotthepony.mc.otm.android.feature.JumpBoostFeature import ru.dbotthepony.mc.otm.capability.matteryPlayer +import ru.dbotthepony.mc.otm.client.render.GlitchRenderer import ru.dbotthepony.mc.otm.client.render.UVWindingOrder import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.tesselator @@ -27,6 +32,7 @@ import ru.dbotthepony.mc.otm.compat.cos.isCosmeticArmorScreen import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.identityFast import ru.dbotthepony.mc.otm.registry.AndroidFeatures +import kotlin.math.min fun onMovementInputUpdate(event: MovementInputUpdateEvent) { val ply = event.entity @@ -111,11 +117,3 @@ fun onScreenOpen(event: ScreenEvent.Opening) { event.newScreen = ExoSuitInventoryScreen(player.exoSuitMenu) } } - -fun postLevelDrawHook(poseStack: PoseStack) { - -} - -fun lastLevelDrawHook(poseStack: PoseStack) { - -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GlitchRenderer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GlitchRenderer.kt new file mode 100644 index 000000000..f09a6b6c2 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/GlitchRenderer.kt @@ -0,0 +1,505 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.google.common.collect.ImmutableList +import com.mojang.blaze3d.pipeline.MainTarget +import com.mojang.blaze3d.platform.GlConst +import com.mojang.blaze3d.platform.GlStateManager +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.BufferBuilder +import com.mojang.blaze3d.vertex.BufferUploader +import com.mojang.blaze3d.vertex.DefaultVertexFormat +import com.mojang.blaze3d.vertex.VertexFormat +import com.mojang.math.Matrix4f +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap +import it.unimi.dsi.fastutil.ints.Int2ObjectFunction +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.GameRenderer +import net.minecraft.core.Vec3i +import net.minecraft.world.level.levelgen.XoroshiroRandomSource +import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.core.component1 +import ru.dbotthepony.mc.otm.core.component2 +import ru.dbotthepony.mc.otm.core.component3 +import ru.dbotthepony.mc.otm.core.linearInterpolation +import java.util.stream.Collectors +import kotlin.math.absoluteValue +import kotlin.math.ceil +import kotlin.math.pow + +@Suppress("SameParameterValue") +object GlitchRenderer { + private abstract class VideoGlitchType { + abstract fun upload(builder: BufferBuilder, x: Double, y: Double, width: Double, height: Double, u0: Float, v0: Float, u1: Float, v1: Float) + + protected fun uploadVertices(faces: Int, builder: BufferBuilder, x: Double, y: Double, width: Double, height: Double, u0: Float, v0: Float, u1: Float, v1: Float, red: Int, green: Int, blue: Int, alpha: Int) { + if (faces and BOTTOM_LEFT != 0) { + builder.vertex(x, y + height, 0.0).uv(u0, v1).color(red, green, blue, alpha).endVertex() + } + + if (faces and BOTTOM_RIGHT != 0) { + builder.vertex(x + width, y + height, 0.0).uv(u1, v1).color(red, green, blue, alpha).endVertex() + } + + if (faces and TOP_RIGHT != 0) { + builder.vertex(x + width, y, 0.0).uv(u1, v0).color(red, green, blue, alpha).endVertex() + } + + if (faces and TOP_LEFT != 0) { + builder.vertex(x, y, 0.0).uv(u0, v0).color(red, green, blue, alpha).endVertex() + } + } + + protected fun uploadQuad(builder: BufferBuilder, x: Double, y: Double, width: Double, height: Double, u0: Float, v0: Float, u1: Float, v1: Float, red: Int, green: Int, blue: Int, alpha: Int) { + uploadVertices(TOP_LEFT or TOP_RIGHT or BOTTOM_RIGHT, builder, x, y, width, height, u0, v0, u1, v1, red, green, blue, alpha) + uploadVertices(TOP_LEFT or BOTTOM_RIGHT or BOTTOM_LEFT, builder, x, y, width, height, u0, v0, u1, v1, red, green, blue, alpha) + } + + companion object { + const val TOP_LEFT = 1 + const val TOP_RIGHT = 2 + const val BOTTOM_LEFT = 4 + const val BOTTOM_RIGHT = 8 + + val vertices = intArrayOf( + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT, + ) + } + } + + private val glitchesSorted: List> + private val glitchesTrisSorted: List> + private val glitchesQuadsSorted: List> + + private fun selectGlitch(): List { + for (list in glitchesSorted) { + if (random.nextFloat() < 0.35f) { + return list + } + } + + return glitchesSorted[glitchesSorted.size - 1] + } + + private fun selectGlitchQuad(): List { + for (list in glitchesQuadsSorted) { + if (random.nextFloat() < 0.35f) { + return list + } + } + + return glitchesQuadsSorted[glitchesQuadsSorted.size - 1] + } + + private fun selectGlitchTriangle(): List { + for (list in glitchesTrisSorted) { + if (random.nextFloat() < 0.35f) { + return list + } + } + + return glitchesTrisSorted[glitchesTrisSorted.size - 1] + } + + init { + val glitchesMapQuads = Int2ObjectArrayMap>() + val glitchesMapTris = Int2ObjectArrayMap>() + val glitchesMap = Int2ObjectArrayMap>() + + val colorVariants = ArrayList() + + val flippersFloppers = listOf( + { u0: Float, v0: Float, u1: Float, v1: Float -> UVCoords(u0, v0, u1, v1) }, + { u0: Float, v0: Float, u1: Float, v1: Float -> UVCoords(1f - u0, v0, 1f - u1, v1) }, + { u0: Float, v0: Float, u1: Float, v1: Float -> UVCoords(u0, 1f - v0, u1, 1f - v1) }, + { u0: Float, v0: Float, u1: Float, v1: Float -> UVCoords(1f - u0, 1f - v0, 1f - u1, 1f - v1) }, + ) + + val colors = intArrayOf(0, 10, 30, 50, 80, 100, 120, 150, 180, 220, 230, 235, 245, 249, 255) + + for (red in colors) + for (green in colors) + for (blue in colors) + if (red != 255 || green != 255 || blue != 255) + colorVariants.add(Vec3i(red, green, blue)) + + for ((r, g, b) in colorVariants) { + for (winding in UVWindingOrder.distinct) { + for ((i, flipflop) in flippersFloppers.withIndex()) { + object : VideoGlitchType() { + @Suppress("name_shadowing") + override fun upload(builder: BufferBuilder, x: Double, y: Double, width: Double, height: Double, u0: Float, v0: Float, u1: Float, v1: Float) { + val (u0_, v0_, u1_, v1_) = winding.translate(u0, v0, u1, v1) + val (u0, v0, u1, v1) = flipflop.invoke(u0_, v0_, u1_, v1_) + uploadQuad(builder, x, y, width, height, u0, v0, u1, v1, r, g, b, 255) + } + }.also { + if (i == 0) { + glitchesMapQuads + .computeIfAbsent(255 - r + 255 - g + 255 - b, Int2ObjectFunction { ArrayList() }) + .add(it) + } + + glitchesMap + .computeIfAbsent(255 - r + 255 - g + 255 - b, Int2ObjectFunction { ArrayList() }) + .add(it) + } + } + } + } + + for (va in VideoGlitchType.vertices) { + for (vb in VideoGlitchType.vertices) { + if (va == vb) continue + + for (cb in VideoGlitchType.vertices) { + if (cb == va || cb == vb) continue + + for ((r, g, b) in colorVariants) { + for (winding in UVWindingOrder.distinct) { + for ((i, flipflop) in flippersFloppers.withIndex()) { + @Suppress("name_shadowing") + object : VideoGlitchType() { + val vertices = va or vb or cb + + override fun upload(builder: BufferBuilder, x: Double, y: Double, width: Double, height: Double, u0: Float, v0: Float, u1: Float, v1: Float) { + val (u0_, v0_, u1_, v1_) = winding.translate(u0, v0, u1, v1) + val (u0, v0, u1, v1) = flipflop.invoke(u0_, v0_, u1_, v1_) + uploadVertices(vertices, builder, x, y, width, height, u0, v0, u1, v1, r, g, b, 255) + } + }.also { + if (i == 0) { + glitchesMapTris + .computeIfAbsent(255 - r + 255 - g + 255 - b, Int2ObjectFunction { ArrayList() }) + .add(it) + } + + glitchesMap + .computeIfAbsent(255 - r + 255 - g + 255 - b, Int2ObjectFunction { ArrayList() }) + .add(it) + } + } + } + } + } + } + } + + val toSort = glitchesMapQuads.entries.stream().collect(Collectors.toList()) + toSort.sortBy { it.key } + glitchesQuadsSorted = toSort.stream().map { ImmutableList.copyOf(it.value) }.collect(ImmutableList.toImmutableList()) + + val toSort2 = glitchesMapTris.entries.stream().collect(Collectors.toList()) + toSort2.sortBy { it.key } + glitchesTrisSorted = toSort2.stream().map { ImmutableList.copyOf(it.value) }.collect(ImmutableList.toImmutableList()) + + val toSort3 = glitchesMap.entries.stream().collect(Collectors.toList()) + toSort3.sortBy { it.key } + glitchesSorted = toSort3.stream().map { ImmutableList.copyOf(it.value) }.collect(ImmutableList.toImmutableList()) + } + + private val random = XoroshiroRandomSource(System.nanoTime(), System.currentTimeMillis()) + + var redShiftX = 0.0 + private set + var redShiftY = 0.0 + private set + var greenShiftX = 0.0 + private set + var greenShiftY = 0.0 + private set + var blueShiftX = 0.0 + private set + var blueShiftY = 0.0 + private set + + var lastGlitch = System.nanoTime() + private set + + var nextGlitch = 0L + private set + + var lastEncodingGlitch = System.nanoTime() + private set + + var nextEncodingGlitch = 0L + private set + + + + private val glitchBuffer by lazy(LazyThreadSafetyMode.NONE) { + MainTarget(minecraft.window.width, minecraft.window.height) + } + + private fun upload(builder: BufferBuilder, offsetX: Double, offsetY: Double, u0: Float, v0: Float, u1: Float, v1: Float) { + builder.vertex(offsetX - 1.0, offsetY + 1.0, 0.0).uv(u0, v1).endVertex() + builder.vertex(offsetX + 1.0, offsetY + 1.0, 0.0).uv(u1, v1).endVertex() + builder.vertex(offsetX + 1.0, offsetY - 1.0, 0.0).uv(u1, v0).endVertex() + builder.vertex(offsetX - 1.0, offsetY - 1.0, 0.0).uv(u0, v0).endVertex() + } + + private fun uploadEmpty(builder: BufferBuilder, offsetX: Double, offsetY: Double) { + builder.vertex(offsetX - 1.0, offsetY + 1.0, 0.0).endVertex() + builder.vertex(offsetX + 1.0, offsetY + 1.0, 0.0).endVertex() + builder.vertex(offsetX + 1.0, offsetY - 1.0, 0.0).endVertex() + builder.vertex(offsetX - 1.0, offsetY - 1.0, 0.0).endVertex() + } + + private fun uploadLine(builder: BufferBuilder, offsetX: Double, offsetY: Double, height: Double, u0: Float, v0: Float, u1: Float, v1: Float) { + builder.vertex(offsetX - 1.0, offsetY - height, 0.0).uv(u0, v1).endVertex() + builder.vertex(offsetX + 1.0, offsetY - height, 0.0).uv(u1, v1).endVertex() + builder.vertex(offsetX + 1.0, offsetY, 0.0).uv(u1, v0).endVertex() + builder.vertex(offsetX - 1.0, offsetY, 0.0).uv(u0, v0).endVertex() + } + + private inline fun makeMirrors(handler: (x: Double, y: Double, u0: Float, v0: Float, u1: Float, v1: Float) -> Unit) { + handler(0.0 - 2f, 0.0, 1f, 0f, 0f, 1f) + handler(0.0 + 2f, 0.0, 1f, 0f, 0f, 1f) + + handler(0.0, 2.0, 0f, 1f, 1f, 0f) + handler(0.0, -2.0, 0f, 1f, 1f, 0f) + + handler(0.0 - 2f, -2.0, 1f, 1f, 0f, 0f) + handler(0.0 + 2f, -2.0, 1f, 1f, 0f, 0f) + + handler(0.0 - 2f, +2.0, 1f, 1f, 0f, 0f) + handler(0.0 + 2f, +2.0, 1f, 1f, 0f, 0f) + } + + private fun draw(offsetX: Double, offsetY: Double) { + val builder = tesselator.builder + + builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX) + + upload(builder, offsetX, offsetY, 0f, 0f, 1f, 1f) + + if (offsetX != 0.0 || offsetY != 0.0) { + makeMirrors { x: Double, y: Double, u0: Float, v0: Float, u1: Float, v1: Float -> + upload(builder, offsetX + x, offsetY + y, u0, v0, u1, v1) + } + } + + BufferUploader.drawWithShader(builder.end()) + } + + private fun pixel2ViewX(value: Double) = linearInterpolation(value / glitchBuffer.width, 1.0, -1.0) + private fun pixel2ViewY(value: Double) = linearInterpolation(value / glitchBuffer.height, 1.0, -1.0) + private fun pixel2TextureX(value: Double) = (value / glitchBuffer.width).toFloat() + private fun pixel2TextureY(value: Double) = 1f - (value / glitchBuffer.height).toFloat() + + private fun drawVHSLineGap(y: Double, height: Double) { + val builder = tesselator.builder + + val v = pixel2TextureY(y) + + builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX) + uploadLine(builder, 0.0, pixel2ViewY(y), (height / glitchBuffer.height) * 2.0, 0f, v, 1f, v) + BufferUploader.drawWithShader(builder.end()) + } + + private var colorGlitchBuff = arrayOfNulls(0) + private var colorGlitchWidth = 0 + private var colorGlitchHeight = 0 + + private fun putMacroGlitch(x: Int, y: Int, width: Int, height: Int, value: VideoGlitchType?) { + for (offsetX in 0 until width) { + for (offsetY in 0 until height) { + val index = (x + offsetX) + (y + offsetY) * colorGlitchWidth + + if (colorGlitchBuff.size > index) { + colorGlitchBuff[index] = value + } + } + } + } + + private fun colorGlitchChanceMultiplier(x: Int, y: Int): Float { + return (1f - ((x.toDouble() - colorGlitchWidth.toDouble() / 2.0).pow(2.0) / colorGlitchWidth.toDouble().pow(2.0) + + (y.toDouble() - colorGlitchHeight.toDouble() / 2.0).pow(2.0) / colorGlitchHeight.toDouble().pow(2.0)) + .toFloat() + .coerceAtLeast(0f) + .coerceAtMost(1f)) + .pow(1f / 8f) + } + + private fun makeColorGlitch() { + lastEncodingGlitch = System.nanoTime() + nextEncodingGlitch = random.nextIntBetweenInclusive(10_000_000, 40_000_000).toLong() + colorGlitchWidth = ceil(glitchBuffer.width / GLITCH_BLOCK_SIZE).toInt() + colorGlitchHeight = ceil(glitchBuffer.height / GLITCH_BLOCK_SIZE).toInt() + + colorGlitchBuff = arrayOfNulls((colorGlitchWidth + 1) * (colorGlitchHeight + 1)) + + for (xBlock in 0 .. colorGlitchWidth) + for (yBlock in 0 .. colorGlitchHeight) + if (colorGlitchChanceMultiplier(xBlock, yBlock) < 0.99f && random.nextFloat() > colorGlitchChanceMultiplier(xBlock, yBlock) * 0.999f) + colorGlitchBuff[xBlock + yBlock * colorGlitchWidth] = selectGlitch().let { it[random.nextInt(0, it.size)] } + + for (xBlock in 0 .. colorGlitchWidth) + for (yBlock in 0 .. colorGlitchHeight) + if (colorGlitchChanceMultiplier(xBlock, yBlock) < 0.99f && random.nextFloat() > colorGlitchChanceMultiplier(xBlock, yBlock) * 0.9999f) + putMacroGlitch(xBlock, yBlock, random.nextInt(1, 5), random.nextInt(1, 5), selectGlitchQuad().let { it[random.nextInt(0, it.size)] }) + + for (xBlock in 0 .. colorGlitchWidth) + for (yBlock in 0 .. colorGlitchHeight) + if (colorGlitchChanceMultiplier(xBlock, yBlock) < 0.99f && random.nextFloat() > colorGlitchChanceMultiplier(xBlock, yBlock) * 0.99999f) + putMacroGlitch(xBlock, yBlock, random.nextInt(1, 9), random.nextInt(1, 9), selectGlitchQuad().let { it[random.nextInt(0, it.size)] }) + + for (xBlock in 0 .. colorGlitchWidth) + for (yBlock in 0 .. colorGlitchHeight) + if (colorGlitchChanceMultiplier(xBlock, yBlock) < 0.99f && random.nextFloat() > colorGlitchChanceMultiplier(xBlock, yBlock) * 0.999999f) + putMacroGlitch(xBlock, yBlock, random.nextInt(1, 14), random.nextInt(1, 14), selectGlitchQuad().let { it[random.nextInt(0, it.size)] }) + } + + private fun makeGlitch() { + redShiftX = random.nextDouble() * 0.05 - 0.025 + redShiftY = random.nextDouble() * 0.05 - 0.025 + greenShiftX = random.nextDouble() * 0.05 - 0.025 + greenShiftY = random.nextDouble() * 0.05 - 0.025 + blueShiftX = random.nextDouble() * 0.05 - 0.025 + blueShiftY = random.nextDouble() * 0.05 - 0.025 + lastGlitch = System.nanoTime() + nextGlitch = random.nextIntBetweenInclusive(75_000_000, 400_000_000).toLong() + } + + var glitchUntil = 0L + private set + var glitchSince = System.nanoTime() + private set + + fun glitchFor(millis: Long) { + if (glitchUntil - (System.nanoTime() - glitchSince) < millis) { + glitchSince = System.nanoTime() + glitchUntil = millis * 1_000_000L + } + } + + private const val GLITCH_BLOCK_SIZE = 8f + + @JvmStatic + fun render() { + if (System.nanoTime() - glitchSince >= glitchUntil) { + return + } + + if (System.nanoTime() - lastGlitch >= nextGlitch) { + makeGlitch() + } + + if (System.nanoTime() - lastEncodingGlitch >= nextEncodingGlitch) { + makeColorGlitch() + } + + val glitchBuffer = glitchBuffer + val projection = RenderSystem.getProjectionMatrix() + RenderSystem.setProjectionMatrix(Matrix4f().also { it.setIdentity() }) + + RenderSystem.getModelViewStack().also { + it.pushPose() + it.setIdentity() + } + + RenderSystem.applyModelViewMatrix() + + RenderSystem.disableCull() + RenderSystem.disableDepthTest() + RenderSystem.enableBlend() + RenderSystem.enableTexture() + RenderSystem.clearColor(0f, 0f, 0f, 1f) + + if (glitchBuffer.width != minecraft.window.width || glitchBuffer.height != minecraft.window.height) { + glitchBuffer.resize(minecraft.window.width, minecraft.window.height, Minecraft.ON_OSX) + glitchBuffer.bindWrite(true) + } else { + glitchBuffer.bindWrite(true) + RenderSystem.clear(GlConst.GL_COLOR_BUFFER_BIT, Minecraft.ON_OSX) + } + + // distort colors by sampling main frame buffer (raw stage) + RenderSystem.setShaderTexture(0, minecraft.mainRenderTarget.colorTextureId) + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_DST_ALPHA) + + RenderSystem.setShader(GameRenderer::getPositionTexShader) + + RenderSystem.setShaderColor(1f, 0.1f, 0f, 1f) + draw(redShiftX, redShiftY) + + RenderSystem.setShaderColor(0f, 1f, 0f, 1f) + draw(greenShiftX, greenShiftY) + + RenderSystem.setShaderColor(0f, 0f, 1f, 1f) + draw(blueShiftX, blueShiftY) + + minecraft.mainRenderTarget.bindWrite(true) + + // draw back to main frame buffer (prepare for post process) + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO) + RenderSystem.setShaderColor(1f, 1f, 1f, 1f) + RenderSystem.setShaderTexture(0, glitchBuffer.colorTextureId) + draw(0.0, 0.0) + + // return to our frame buffer (post process stage) + glitchBuffer.bindWrite(true) + RenderSystem.setShaderTexture(0, minecraft.mainRenderTarget.colorTextureId) + + // color perception errors (eye-camera glitch) + drawVHSLineGap((System.currentTimeMillis() % glitchBuffer.height).toDouble(), glitchBuffer.height * 0.025) + drawVHSLineGap(((System.currentTimeMillis() + glitchBuffer.height / 2) % glitchBuffer.height).toDouble(), glitchBuffer.height * 0.075) + drawVHSLineGap(((System.currentTimeMillis() + glitchBuffer.height / 3) % glitchBuffer.height).toDouble(), glitchBuffer.height * 0.04) + drawVHSLineGap(((-System.currentTimeMillis() - glitchBuffer.height / 3) % glitchBuffer.height).toDouble().absoluteValue, glitchBuffer.height * 0.07) + + // color encoding errors (encoder/transmission glitch) + val blocksWidth = ceil(glitchBuffer.width / GLITCH_BLOCK_SIZE).toInt() + val blocksHeight = ceil(glitchBuffer.height / GLITCH_BLOCK_SIZE).toInt() + + if (colorGlitchWidth != blocksWidth || colorGlitchHeight != blocksHeight) { + makeColorGlitch() + } + + val blockStepWidth = (GLITCH_BLOCK_SIZE / glitchBuffer.width) * 2.0 + val blockStepHeight = (GLITCH_BLOCK_SIZE / glitchBuffer.height) * 2.0 + + val blockStepWidthUV = GLITCH_BLOCK_SIZE / glitchBuffer.width + val blockStepHeightUV = GLITCH_BLOCK_SIZE / glitchBuffer.height + + val builder = tesselator.builder + + builder.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_TEX_COLOR) + + for (xBlock in 0 .. blocksWidth) { + for (yBlock in 0 .. blocksHeight) { + colorGlitchBuff[xBlock + yBlock * blocksWidth]?.upload( + builder = builder, + x = -1.0 + xBlock * blockStepWidth, + y = -1.0 + yBlock * blockStepHeight, + width = blockStepWidth, + height = blockStepHeight, + u0 = xBlock * blockStepWidthUV, + v0 = yBlock * blockStepHeightUV, + u1 = (xBlock + 1) * blockStepWidthUV, + v1 = (yBlock + 1) * blockStepHeightUV, + ) + } + } + + RenderSystem.setShaderColor(1f, 1f, 1f, 1f) + RenderSystem.setShader(GameRenderer::getPositionTexColorShader) + BufferUploader.drawWithShader(builder.end()) + + // upload final result to main frame buffer + minecraft.mainRenderTarget.bindWrite(true) + + RenderSystem.setShader(GameRenderer::getPositionTexShader) + + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO) + RenderSystem.setShaderColor(1f, 1f, 1f, 1f) + RenderSystem.setShaderTexture(0, glitchBuffer.colorTextureId) + draw(0.0, 0.0) + + RenderSystem.setProjectionMatrix(projection) + RenderSystem.getModelViewStack().popPose() + RenderSystem.applyModelViewMatrix() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/UVStuff.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/UVStuff.kt index 746729ae6..406e8fec9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/UVStuff.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/UVStuff.kt @@ -1,5 +1,7 @@ package ru.dbotthepony.mc.otm.client.render +import com.google.common.collect.ImmutableList + sealed interface IUVCoords { val u0: Float val v0: Float @@ -80,6 +82,10 @@ enum class UVWindingOrder( U1_V1_U0_V0(2, 3, 0, 1), // mirror both FLIP_FLOP(2, 3, 0, 1); // mirror both + companion object { + val distinct: List = ImmutableList.of(NORMAL, FLIP, FLOP, FLIP_FLOP) + } + val isIdentity: Boolean = u0 == 0 && v0 == 1 && u1 == 2 && v1 == 3 operator fun component1() = u0 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt index fde821a2a..834c53ddb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerNetworkChannel.kt @@ -23,6 +23,7 @@ import ru.dbotthepony.mc.otm.android.feature.TriggerShockwavePacket import ru.dbotthepony.mc.otm.capability.matteryPlayer import ru.dbotthepony.mc.otm.client.MatteryGUI import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.render.GlitchRenderer import ru.dbotthepony.mc.otm.container.get import ru.dbotthepony.mc.otm.container.set import ru.dbotthepony.mc.otm.menu.AndroidStationMenu @@ -430,6 +431,23 @@ class PickItemFromInventoryPacket( } } +class GlitchPacket(val millis: Long) : MatteryPacket { + override fun write(buff: FriendlyByteBuf) { + buff.writeVarLong(millis) + } + + override fun play(context: Supplier) { + context.packetHandled = true + GlitchRenderer.glitchFor(millis) + } + + companion object { + fun read(buff: FriendlyByteBuf): GlitchPacket { + return GlitchPacket(buff.readVarLong()) + } + } +} + object MatteryPlayerNetworkChannel : MatteryNetworkChannel( version = "1", name = "player" @@ -457,5 +475,7 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel( add(TriggerJumpBoostPacket::class, { TriggerJumpBoostPacket }, PLAY_TO_SERVER) add(PickItemFromInventoryPacket::class, PickItemFromInventoryPacket.Companion::read, PLAY_TO_SERVER) + + add(GlitchPacket::class, GlitchPacket.Companion::read, PLAY_TO_CLIENT) } } diff --git a/src/main/resources/coremods/code_injector.js b/src/main/resources/coremods/code_injector.js index ed0f8acc3..e474e4095 100644 --- a/src/main/resources/coremods/code_injector.js +++ b/src/main/resources/coremods/code_injector.js @@ -890,33 +890,6 @@ function initializeCoreMod() { } }, - 'big GameRenderer#renderItemInHand fuck off': - method('net.minecraft.client.renderer.GameRenderer.m_109120_(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/Camera;F)V', function(node) { - putInstructions(node, undefined, [ - new VarInsnNode(opcodesRemapped.aload, 1), - new MethodInsnNode( - opcodesRemapped.invokevirtual, - 'com/mojang/blaze3d/vertex/PoseStack', - ASMAPI.mapMethod('m_85836_'), // pushPose - '()V', - false - ) - ]) - - injectInstructionsAtTail(node, [ - new VarInsnNode(opcodesRemapped.aload, 1), - new MethodInsnNode( - opcodesRemapped.invokevirtual, - 'com/mojang/blaze3d/vertex/PoseStack', - ASMAPI.mapMethod('m_85849_'), // popPose - '()V', - false - ) - ]) - - return node - }), - 'GameRenderer#render hook': { 'target': { 'type': 'METHOD', @@ -934,138 +907,36 @@ function initializeCoreMod() { false )) - // 233: new #26 // class com/mojang/blaze3d/vertex/PoseStack - // 236: dup - // 237: invokespecial #1425 // Method com/mojang/blaze3d/vertex/PoseStack."":()V - // <-- our target for local variable - // 240: invokevirtual #3398 // Method renderLevel:(FJLcom/mojang/blaze3d/vertex/PoseStack;)V - // 243: aload_0 - // 244: invokevirtual #3540 // Method tryTakeScreenshotIfNeeded:()V - // 247: aload_0 - // 248: getfield #2773 // Field minecraft:Lnet/minecraft/client/Minecraft; - // 251: getfield #3207 // Field net/minecraft/client/Minecraft.levelRenderer:Lnet/minecraft/client/renderer/LevelRenderer; - // 254: invokevirtual #3543 // Method net/minecraft/client/renderer/LevelRenderer.doEntityOutline:()V - // <-- our target for post - // 257: aload_0 - // 258: getfield #2788 // Field postEffect:Lnet/minecraft/client/renderer/PostChain; - // 261: ifnull 291 - // 264: aload_0 - // 265: getfield #2805 // Field effectActive:Z - // 268: ifeq 291 - // 271: invokestatic #3546 // Method com/mojang/blaze3d/systems/RenderSystem.disableBlend:()V - // 274: invokestatic #3549 // Method com/mojang/blaze3d/systems/RenderSystem.disableDepthTest:()V - // 277: invokestatic #3552 // Method com/mojang/blaze3d/systems/RenderSystem.enableTexture:()V - // 280: invokestatic #3555 // Method com/mojang/blaze3d/systems/RenderSystem.resetTextureMatrix:()V - // 283: aload_0 - // 284: getfield #2788 // Field postEffect:Lnet/minecraft/client/renderer/PostChain; - // 287: fload_1 // 288: invokevirtual #3558 // Method net/minecraft/client/renderer/PostChain.process:(F)V // 291: aload_0 // 292: getfield #2773 // Field minecraft:Lnet/minecraft/client/Minecraft; // 295: invokevirtual #2818 // Method net/minecraft/client/Minecraft.getMainRenderTarget:()Lcom/mojang/blaze3d/pipeline/RenderTarget; // 298: iconst_1 // 299: invokevirtual #3561 // Method com/mojang/blaze3d/pipeline/RenderTarget.bindWrite:(Z)V - // <-- our target for last + // <-- our target // 302: aload_0 // 303: getfield #2773 // Field minecraft:Lnet/minecraft/client/Minecraft; // 306: invokevirtual #2821 // Method net/minecraft/client/Minecraft.getWindow:()Lcom/mojang/blaze3d/platform/Window; // 309: astore 7 - var ourmethod = ASMAPI.mapMethod('m_83947_') - var lastLabel = undefined - var beforeInvoke = undefined - var slot = undefined + var ourmethod = ASMAPI.mapMethod('m_83947_') // bindWrite for (var i = 0; i < node.instructions.size(); i++) { var instruction = node.instructions.get(i) if (instruction.getOpcode() == opcodesRemapped.invokevirtual && instruction.name == ourmethod && instruction.desc == '(Z)V') { - beforeInvoke = instruction - - var invoke = new MethodInsnNode( + node.instructions.insert(instruction, new MethodInsnNode( opcodesRemapped.invokestatic, - 'ru/dbotthepony/mc/otm/client/ClientEventHandlerKt', - 'lastLevelDrawHook', - '(Lcom/mojang/blaze3d/vertex/PoseStack;)V', + 'ru/dbotthepony/mc/otm/client/render/GlitchRenderer', + 'render', + '()V', false - ) - - node.instructions.insert(instruction, invoke) - - lastLabel = new LabelNode(new Label()) - node.instructions.insert(invoke, lastLabel) + )) break } } - if (lastLabel !== undefined && beforeInvoke !== undefined) { - var renderLevel = ASMAPI.mapMethod('m_109089_') - - node.maxLocals++ - - for (var i = 0; i < node.instructions.size(); i++) { - var instruction = node.instructions.get(i) - - if (instruction.getOpcode() == opcodesRemapped.invokevirtual && instruction.name == renderLevel && instruction.desc == '(FJLcom/mojang/blaze3d/vertex/PoseStack;)V') { - var startLabel = new Label() - var prev = node.instructions.get(i - 1) - - var labelNode = new LabelNode(startLabel) - node.instructions.insert(prev, labelNode) - prev = labelNode - - var slot = node.localVariables.size() + 1 - - var local = new LocalVariableNode( - 'otm_capturePoseStack', - 'Lcom/mojang/blaze3d/vertex/PoseStack;', - null, - labelNode, - lastLabel, - slot - ) - - node.localVariables.add(local) - - var next = new InsnNode(opcodesRemapped.dup) - node.instructions.insert(prev, next) - prev = next - next = new VarInsnNode(opcodesRemapped.astore, slot) - node.instructions.insert(prev, next) - - node.instructions.insert(beforeInvoke, new VarInsnNode(opcodesRemapped.aload, slot)) - - break - } - } - } - - if (slot !== undefined) { - var doEntityOutline = ASMAPI.mapMethod('m_109769_') - - for (var i = 0; i < node.instructions.size(); i++) { - var instruction = node.instructions.get(i) - - if (instruction.getOpcode() == opcodesRemapped.invokevirtual && instruction.name == doEntityOutline && instruction.desc == '()V') { - var prev = instruction - var next = new VarInsnNode(opcodesRemapped.aload, slot) - node.instructions.insert(prev, next) - prev = next - - node.instructions.insert(prev, new MethodInsnNode( - opcodesRemapped.invokestatic, - 'ru/dbotthepony/mc/otm/client/ClientEventHandlerKt', - 'postLevelDrawHook', - '(Lcom/mojang/blaze3d/vertex/PoseStack;)V', - false - )) - - break - } - } - } - return node } },