|
| 1 | +/** |
| 2 | + * Animatium |
| 3 | + * The all-you-could-want legacy animations mod for modern minecraft versions. |
| 4 | + * Brings back animations from the 1.7/1.8 era and more. |
| 5 | + * <p> |
| 6 | + * Copyright (C) 2024-2025 lowercasebtw |
| 7 | + * Copyright (C) 2024-2025 mixces |
| 8 | + * Copyright (C) 2024-2025 Contributors to the project retain their copyright |
| 9 | + * <p> |
| 10 | + * This program is free software: you can redistribute it and/or modify |
| 11 | + * it under the terms of the GNU General Public License as published by |
| 12 | + * the Free Software Foundation, either version 3 of the License, or |
| 13 | + * (at your option) any later version. |
| 14 | + * <p> |
| 15 | + * This program is distributed in the hope that it will be useful, |
| 16 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | + * GNU General Public License for more details. |
| 19 | + * <p> |
| 20 | + * You should have received a copy of the GNU General Public License |
| 21 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 22 | + * <p> |
| 23 | + * "MINECRAFT" LINKING EXCEPTION TO THE GPL |
| 24 | + */ |
| 25 | + |
| 26 | +package org.visuals.legacy.animatium.util.rendering; |
| 27 | + |
| 28 | +import com.mojang.blaze3d.ProjectionType; |
| 29 | +import com.mojang.blaze3d.pipeline.BlendFunction; |
| 30 | +import com.mojang.blaze3d.pipeline.MainTarget; |
| 31 | +import com.mojang.blaze3d.pipeline.RenderPipeline; |
| 32 | +import com.mojang.blaze3d.platform.DestFactor; |
| 33 | +import com.mojang.blaze3d.platform.SourceFactor; |
| 34 | +import com.mojang.blaze3d.systems.RenderSystem; |
| 35 | +import com.mojang.blaze3d.textures.FilterMode; |
| 36 | +import com.mojang.blaze3d.textures.GpuTexture; |
| 37 | +import com.mojang.blaze3d.textures.GpuTextureView; |
| 38 | +import com.mojang.blaze3d.vertex.DefaultVertexFormat; |
| 39 | +import com.mojang.blaze3d.vertex.VertexConsumer; |
| 40 | +import com.mojang.blaze3d.vertex.VertexFormat; |
| 41 | +import lombok.experimental.UtilityClass; |
| 42 | +import net.minecraft.client.gui.GuiGraphics; |
| 43 | +import net.minecraft.client.gui.navigation.ScreenRectangle; |
| 44 | +import net.minecraft.client.gui.render.TextureSetup; |
| 45 | +import net.minecraft.client.gui.render.state.GuiElementRenderState; |
| 46 | +import net.minecraft.client.renderer.CachedPerspectiveProjectionMatrixBuffer; |
| 47 | +import net.minecraft.client.renderer.RenderPipelines; |
| 48 | +import net.minecraft.client.renderer.texture.DynamicTexture; |
| 49 | +import net.minecraft.resources.ResourceLocation; |
| 50 | +import net.minecraft.util.ARGB; |
| 51 | +import net.minecraft.util.Mth; |
| 52 | +import org.jetbrains.annotations.NotNull; |
| 53 | +import org.jetbrains.annotations.Nullable; |
| 54 | +import org.joml.Matrix3x2f; |
| 55 | +import org.joml.Matrix4f; |
| 56 | +import org.joml.Vector4i; |
| 57 | +import org.visuals.legacy.animatium.Animatium; |
| 58 | +import org.visuals.legacy.animatium.util.Utils; |
| 59 | + |
| 60 | +@UtilityClass |
| 61 | +// Ported code of the old <=1.12.2 panorama renderer (w/ blur) |
| 62 | +public class LegacyPanoramaRenderer { |
| 63 | + private final BlendFunction PANORAMA_BLEND = new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO); |
| 64 | + |
| 65 | + private final RenderPipeline LEGACY_PANORAMA = |
| 66 | + RenderPipeline.builder(RenderPipelines.MATRICES_PROJECTION_SNIPPET) |
| 67 | + .withLocation(Animatium.location("pipeline/legacy_panorama")) |
| 68 | + .withVertexShader(Animatium.location("core/legacy_panorama")) |
| 69 | + .withFragmentShader(Animatium.location("core/legacy_panorama")) |
| 70 | + .withCull(false) |
| 71 | + .withDepthWrite(false) |
| 72 | + .withBlend(PANORAMA_BLEND) |
| 73 | + // .withColorWrite(true, false) // TODO/NOTE: Causes it to not render (alpha becomes 0.0?!??!?!) |
| 74 | + .withSampler("Sampler0") |
| 75 | + .withVertexFormat(DefaultVertexFormat.POSITION_TEX_COLOR, VertexFormat.Mode.QUADS) |
| 76 | + .build(); |
| 77 | + |
| 78 | + private final RenderPipeline LEGACY_PANORAMA_BLUR = |
| 79 | + RenderPipeline.builder(RenderPipelines.GUI_TEXTURED_SNIPPET) |
| 80 | + .withLocation(Animatium.location("pipeline/legacy_panorama_blur")) |
| 81 | + .withVertexShader(Animatium.location("core/legacy_panorama_blur")) |
| 82 | + .withFragmentShader(Animatium.location("core/legacy_panorama_blur")) |
| 83 | + .withBlend(PANORAMA_BLEND) |
| 84 | + .withColorWrite(true, false) |
| 85 | + .build(); |
| 86 | + |
| 87 | + private final Vector4i VIEWPORT = new Vector4i(0, 0, 256, 256); |
| 88 | + |
| 89 | + private final CachedPerspectiveProjectionMatrixBuffer projectionMatrixBuffer = new CachedPerspectiveProjectionMatrixBuffer("panorama", 0.05F, 10.0F); |
| 90 | + private final MainTarget panoramaTarget = new MainTarget(256, 256); |
| 91 | + private final GpuTextureView backgroundTextureView; |
| 92 | + private float spin = 0.0F; |
| 93 | + |
| 94 | + static { |
| 95 | + final DynamicTexture dynamicTexture = new DynamicTexture(() -> "background", 256, 256, true); |
| 96 | + backgroundTextureView = dynamicTexture.getTextureView(); |
| 97 | + RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(panoramaTarget.getDepthTexture(), 1.0F); |
| 98 | + } |
| 99 | + |
| 100 | + public void render(final GuiGraphics guiGraphics, final int width, final int height) { |
| 101 | + renderPanorama(width, height); |
| 102 | + for (int pass = 0; pass < 7; ++pass) { |
| 103 | + panoramaBlurPass(guiGraphics.pose(), width, height); |
| 104 | + } |
| 105 | + |
| 106 | + guiGraphics.guiRenderState.submitGuiElement(new BlitFinalTexture(guiGraphics.pose(), backgroundTextureView, width, height, ARGB.white(1.0F))); |
| 107 | + } |
| 108 | + |
| 109 | + private void renderPanorama(final int width, final int height) { |
| 110 | + RenderSystem.setProjectionMatrix(projectionMatrixBuffer.getBuffer(width, height, 120.0F), ProjectionType.PERSPECTIVE); |
| 111 | + final Matrix4f rootMatrix = new Matrix4f().identity().rotateX(Utils.toRadians(180.0F)).rotateZ(Utils.toRadians(90.0F)); |
| 112 | + for (int layer = 0; layer < 64; layer++) { |
| 113 | + float x = (layer % 8 / 8.0F - 0.5F) / 64.0F; |
| 114 | + float y = ((float) layer / 8 / 8.0F - 0.5F) / 64.0F; |
| 115 | + final Matrix4f layerMatrix = new Matrix4f(rootMatrix).translate(x, y, 0.0F).rotateX(Utils.toRadians(getXRot())).rotateY(Utils.toRadians(getYRot())); |
| 116 | + for (int panoramaIdx = 0; panoramaIdx < 6; panoramaIdx++) { |
| 117 | + final Matrix4f faceMatrix = new Matrix4f(layerMatrix); |
| 118 | + if (panoramaIdx == 1) { |
| 119 | + faceMatrix.rotateY(Utils.toRadians(90.0F)); |
| 120 | + } else if (panoramaIdx == 2) { |
| 121 | + faceMatrix.rotateY(Utils.toRadians(180.0F)); |
| 122 | + } else if (panoramaIdx == 3) { |
| 123 | + faceMatrix.rotateY(Utils.toRadians(-90.0F)); |
| 124 | + } else if (panoramaIdx == 4) { |
| 125 | + faceMatrix.rotateX(Utils.toRadians(90.0F)); |
| 126 | + } else if (panoramaIdx == 5) { |
| 127 | + faceMatrix.rotateX(Utils.toRadians(-90.0F)); |
| 128 | + } |
| 129 | + |
| 130 | + try (final Renderer renderer = Renderer.of("Panorama")) { |
| 131 | + renderer.setPipeline(LEGACY_PANORAMA); |
| 132 | + renderer.setViewport(VIEWPORT); |
| 133 | + renderer.setFramebuffer(panoramaTarget); |
| 134 | + renderer.setDynamicTransforms(renderer.getDynamicTransforms().withModelViewMatrix(faceMatrix)); |
| 135 | + |
| 136 | + final int currentLayer = layer; |
| 137 | + renderer.setup((vertexConsumer) -> { |
| 138 | + final int color = ARGB.white(255.0F / (currentLayer + 1.0F)); |
| 139 | + vertexConsumer.addVertex(-1.0F, -1.0F, 1.0F).setUv(0.0F, 0.0F).setColor(color); |
| 140 | + vertexConsumer.addVertex(1.0F, -1.0F, 1.0F).setUv(1.0F, 0.0F).setColor(color); |
| 141 | + vertexConsumer.addVertex(1.0F, 1.0F, 1.0F).setUv(1.0F, 1.0F).setColor(color); |
| 142 | + vertexConsumer.addVertex(-1.0F, 1.0F, 1.0F).setUv(0.0F, 1.0F).setColor(color); |
| 143 | + }, 4); |
| 144 | + |
| 145 | + renderer.setTexture(0, getPanoramaTexture(panoramaIdx)); |
| 146 | + renderer.draw(); |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + private void panoramaBlurPass(final Matrix3x2f pose, final int width, final int height) { |
| 153 | + final GpuTexture texture = backgroundTextureView.texture(); |
| 154 | + texture.setTextureFilter(FilterMode.LINEAR, FilterMode.LINEAR, false); |
| 155 | + RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( |
| 156 | + panoramaTarget.getColorTexture(), // source |
| 157 | + texture, // destination |
| 158 | + 0, // mipLevel |
| 159 | + 0, // destX |
| 160 | + 0, // destY |
| 161 | + 0, // srcX |
| 162 | + 0, // srcY |
| 163 | + 256, // width |
| 164 | + 256 // height |
| 165 | + ); |
| 166 | + try (final Renderer renderer = Renderer.of("Panorama Blur Pass")) { |
| 167 | + renderer.setPipeline(LEGACY_PANORAMA_BLUR); |
| 168 | + renderer.setViewport(VIEWPORT); |
| 169 | + renderer.setFramebuffer(panoramaTarget); |
| 170 | + renderer.setup((vertexConsumer) -> { |
| 171 | + for (int cycle = 0; cycle < 3; cycle++) { |
| 172 | + final int color = ARGB.white(1.0F / (cycle + 1.0F)); |
| 173 | + final float growth = (cycle - 1.0F) / 256.0F; |
| 174 | + vertexConsumer.addVertexWith2DPose(pose, width, height).setUv(0.0F + growth, 1.0F).setColor(color); |
| 175 | + vertexConsumer.addVertexWith2DPose(pose, width, 0.0F).setUv(1.0F + growth, 1.0F).setColor(color); |
| 176 | + vertexConsumer.addVertexWith2DPose(pose, 0.0F, 0.0F).setUv(1.0F + growth, 0.0F).setColor(color); |
| 177 | + vertexConsumer.addVertexWith2DPose(pose, 0.0F, height).setUv(0.0F + growth, 0.0F).setColor(color); |
| 178 | + } |
| 179 | + }, 12); |
| 180 | + renderer.setTexture(0, backgroundTextureView); |
| 181 | + renderer.drawInGui(); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + public void update(float tickDelta) { |
| 186 | + spin += tickDelta; |
| 187 | + } |
| 188 | + |
| 189 | + public float getXRot() { |
| 190 | + return Mth.sin(spin / 400.0F) * 25.0F + 20.0F; |
| 191 | + } |
| 192 | + |
| 193 | + public float getYRot() { |
| 194 | + return -spin * 0.1F; |
| 195 | + } |
| 196 | + |
| 197 | + public ResourceLocation getPanoramaTexture(int side) { |
| 198 | + return ResourceLocation.withDefaultNamespace("textures/gui/title/background/panorama_" + side + ".png"); |
| 199 | + } |
| 200 | + |
| 201 | + private record BlitFinalTexture( |
| 202 | + Matrix3x2f pose, |
| 203 | + GpuTextureView texture, |
| 204 | + int width, int height, |
| 205 | + int color |
| 206 | + ) implements GuiElementRenderState { |
| 207 | + @Override |
| 208 | + public void buildVertices(VertexConsumer consumer) { |
| 209 | + final float aspect = 120.0F / Math.max(this.width, this.height); |
| 210 | + final float sw = this.width * aspect / 256.0F; |
| 211 | + final float sh = this.height * aspect / 256.0F; |
| 212 | + consumer.addVertexWith2DPose(this.pose, 0.0F, this.height).setUv(0.5F - sh, 0.5F + sw).setColor(this.color); |
| 213 | + consumer.addVertexWith2DPose(this.pose, this.width, this.height).setUv(0.5F - sh, 0.5F - sw).setColor(this.color); |
| 214 | + consumer.addVertexWith2DPose(this.pose, this.width, 0.0F).setUv(0.5F + sh, 0.5F - sw).setColor(this.color); |
| 215 | + consumer.addVertexWith2DPose(this.pose, 0.0F, 0.0F).setUv(0.5F + sh, 0.5F + sw).setColor(this.color); |
| 216 | + } |
| 217 | + |
| 218 | + @Override |
| 219 | + public @NotNull RenderPipeline pipeline() { |
| 220 | + return RenderPipelines.GUI_TEXTURED; |
| 221 | + } |
| 222 | + |
| 223 | + @Override |
| 224 | + public @NotNull TextureSetup textureSetup() { |
| 225 | + return TextureSetup.singleTexture(this.texture); |
| 226 | + } |
| 227 | + |
| 228 | + @Override |
| 229 | + public @Nullable ScreenRectangle scissorArea() { |
| 230 | + return null; |
| 231 | + } |
| 232 | + |
| 233 | + @Override |
| 234 | + public @NotNull ScreenRectangle bounds() { |
| 235 | + return new ScreenRectangle(0, 0, this.width, this.height).transformMaxBounds(this.pose); |
| 236 | + } |
| 237 | + } |
| 238 | +} |
0 commit comments