From f25647a869707c622fc640b93c74385a0c2cc851 Mon Sep 17 00:00:00 2001 From: Zopiclone Date: Wed, 10 Jun 2026 00:19:53 +0800 Subject: [PATCH] Bruteforce port from minecraft version 1.21 to 26.2-snapshot-5 using Codex #5 --- common/build.gradle.kts | 1 + .../mixin/LevelRendererStandaloneMixin.java | 73 +++++ .../matrix/client/MatrixClient.kt | 29 ++ .../heckerpowered/matrix/client/MatrixHud.kt | 17 +- .../client/render/MatrixGraphicsBackend.kt | 22 ++ .../client/render/MatrixGuiPipelines.kt | 175 ++++++++++ .../render/MatrixPointSpriteParticles.kt | 304 ++++++++++++++++++ .../client/render/MatrixPostUniforms.kt | 65 ++++ .../client/render/MatrixShaderPipelines.kt | 88 ++++- .../client/render/MatrixShaderTextures.kt | 30 ++ .../client/render/PostProcessRenderer.kt | 90 ++++-- .../client/render/ScreenEffectRenderer.kt | 65 ++-- .../particle/system/ExplosionParticle.kt | 45 ++- .../post/OverlayPostProcessEffectRenderer.kt | 38 ++- .../render/post/TextureDissolveShader.kt | 6 +- .../render/shader/VelocityMapRenderer.kt | 8 +- .../client/render/shader/VolumeDistortion.kt | 42 +-- .../matrix/client/shader/BlurRenderer.kt | 61 +++- .../shader/TexturePixelDissolveProgram.kt | 8 +- .../core/extension/LivingEntityExtension.kt | 2 +- .../renderer/rendertype/MatrixRenderTypes.kt | 28 ++ .../assets/matrix/shaders/blit/blit.fsh | 2 +- .../matrix/shaders/blit/blit_no_depth.fsh | 2 +- .../resources/assets/matrix/shaders/debug.fsh | 2 +- .../shaders/gaussian_blur_horizontal.fsh | 4 +- .../matrix/shaders/gaussian_blur_vertical.fsh | 4 +- .../matrix/shaders/gui/dissolve_rect.fsh | 48 +++ .../matrix/shaders/gui/dissolve_rect.vsh | 20 ++ .../particle_render/world_point_sprite.fsh | 17 + .../particle_render/world_point_sprite.vsh | 20 ++ .../scale_sprite_size_by_speed.vsh | 2 +- .../shaders/point_sprite/point_sprite.vsh | 14 +- .../assets/matrix/shaders/post/aura.fsh | 15 +- .../post/bloom/bloom_brightness_pass.fsh | 12 +- .../matrix/shaders/post/blur/blur_mask.fsh | 14 + .../assets/matrix/shaders/post/circle.fsh | 20 +- .../matrix/shaders/post/dissolve/dissolve.fsh | 29 +- .../post/dissolve/texture_dissolve.fsh | 22 +- .../post/dissolve/texture_pixel_dissolve.fsh | 54 ++++ .../shaders/post/grain/background_grain.fsh | 11 +- .../matrix/shaders/post/hud/progress_ring.fsh | 2 +- .../matrix/shaders/post/opacity_blend.fsh | 2 +- .../matrix/shaders/post/opacity_mask.fsh | 2 +- .../shaders/post/refraction/refraction.fsh | 6 +- .../matrix/shaders/post/sdf/sdf_eval.fsh | 2 +- .../matrix/shaders/post/sdf/seed_gen.fsh | 2 +- .../post/velocity_map/velocity_map.fsh | 2 +- .../matrix/shaders/post/volume_distortion.fsh | 150 +++++++++ .../shaders/post/vortex/inverse_vortex.fsh | 17 +- .../matrix/shaders/post/vortex/vortex.fsh | 15 +- common/src/main/resources/matrix.mixins.json | 3 +- 51 files changed, 1523 insertions(+), 189 deletions(-) create mode 100644 common/src/main/java/heckerpowered/matrix/mixin/LevelRendererStandaloneMixin.java create mode 100644 common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGraphicsBackend.kt create mode 100644 common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGuiPipelines.kt create mode 100644 common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPointSpriteParticles.kt create mode 100644 common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderTextures.kt create mode 100644 common/src/main/kotlin/net/minecraft/client/renderer/rendertype/MatrixRenderTypes.kt create mode 100644 common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.fsh create mode 100644 common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.vsh create mode 100644 common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.fsh create mode 100644 common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.vsh create mode 100644 common/src/main/resources/assets/matrix/shaders/post/blur/blur_mask.fsh create mode 100644 common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_pixel_dissolve.fsh create mode 100644 common/src/main/resources/assets/matrix/shaders/post/volume_distortion.fsh diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 4b77a040..d102e6f9 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -84,6 +84,7 @@ sourceSets { "heckerpowered/matrix/mixin/GameRendererStandaloneMixin.java", "heckerpowered/matrix/mixin/KeyboardMixin.java", "heckerpowered/matrix/mixin/LevelMixin.java", + "heckerpowered/matrix/mixin/LevelRendererStandaloneMixin.java", "heckerpowered/matrix/mixin/LivingEntityMixin.java", "heckerpowered/matrix/mixin/MinecraftMixin.java", "heckerpowered/matrix/mixin/MinecraftServerMixin.java", diff --git a/common/src/main/java/heckerpowered/matrix/mixin/LevelRendererStandaloneMixin.java b/common/src/main/java/heckerpowered/matrix/mixin/LevelRendererStandaloneMixin.java new file mode 100644 index 00000000..c4fba967 --- /dev/null +++ b/common/src/main/java/heckerpowered/matrix/mixin/LevelRendererStandaloneMixin.java @@ -0,0 +1,73 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (c) 2026 heckerpowered + */ + +package heckerpowered.matrix.mixin; + +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.resource.ResourceHandle; +import heckerpowered.matrix.client.render.ScreenEffectRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; +import net.minecraft.client.renderer.feature.FeatureRenderDispatcher.PreparedFrame; +import net.minecraft.client.renderer.state.level.LevelRenderState; +import net.minecraft.util.profiling.ProfilerFiller; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LevelRenderer.class) +class LevelRendererStandaloneMixin { + private LevelRendererStandaloneMixin() { + } + + @Inject( + method = "lambda$addMainPass$0", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/feature/FeatureRenderDispatcher$PreparedFrame;executeSolid()V", + shift = At.Shift.BEFORE + ) + ) + private void matrix$beginRenderEntityEffects( + GpuBufferSlice gpuBufferSlice, + LevelRenderState levelRenderState, + ProfilerFiller profiler, + ChunkSectionsToRender sectionsToRender, + ResourceHandle resourceHandle, + PreparedFrame preparedFrame, + ResourceHandle resourceHandle2, + ResourceHandle resourceHandle3, + ResourceHandle resourceHandle4, + ResourceHandle resourceHandle5, + CallbackInfo ci + ) { + ScreenEffectRenderer.beginRenderEntity(); + } + + @Inject( + method = "lambda$addMainPass$0", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/feature/FeatureRenderDispatcher$PreparedFrame;executeTranslucentAfterTerrain()V", + shift = At.Shift.AFTER + ) + ) + private void matrix$endRenderEntityEffects( + GpuBufferSlice gpuBufferSlice, + LevelRenderState levelRenderState, + ProfilerFiller profiler, + ChunkSectionsToRender sectionsToRender, + ResourceHandle resourceHandle, + PreparedFrame preparedFrame, + ResourceHandle resourceHandle2, + ResourceHandle resourceHandle3, + ResourceHandle resourceHandle4, + ResourceHandle resourceHandle5, + CallbackInfo ci + ) { + ScreenEffectRenderer.endRenderEntity(); + } +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/MatrixClient.kt b/common/src/main/kotlin/heckerpowered/matrix/client/MatrixClient.kt index a3027f15..9da45758 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/MatrixClient.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/MatrixClient.kt @@ -8,7 +8,10 @@ package heckerpowered.matrix.client import heckerpowered.matrix.Matrix import heckerpowered.matrix.client.network.MatrixClientPlayNetworking import heckerpowered.matrix.client.render.ChannelSequenceRenderer +import heckerpowered.matrix.client.render.MatrixGuiPipelines +import heckerpowered.matrix.client.render.MatrixPointSpriteParticles import heckerpowered.matrix.client.render.MatrixRenderSystem +import heckerpowered.matrix.client.render.MatrixShaderPipelines import heckerpowered.matrix.client.render.ScreenEffectRenderer import heckerpowered.matrix.client.render.TargetGuideRenderer import heckerpowered.matrix.client.render.entity.DevEntityRenderer @@ -30,8 +33,13 @@ import heckerpowered.matrix.common.magic.core.Magic import heckerpowered.matrix.common.magic.system.Magics import heckerpowered.matrix.core.isInfiniteMana import net.fabricmc.api.ClientModInitializer +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry +import net.fabricmc.fabric.api.resource.ResourceManagerHelper +import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener import org.joml.Matrix4f +import net.minecraft.server.packs.PackType +import net.minecraft.server.packs.resources.ResourceManager import java.time.Duration val projectionMatrix: Matrix4f @@ -72,10 +80,12 @@ class MatrixClient : ClientModInitializer { MatrixClientPlayNetworking.onInitialize() DamageNumberHud.onInitialize() ScreenEffectRenderer.onInitialize() + MatrixPointSpriteParticles.onInitialize() ChannelSequenceRenderer.onInitialize() TargetGuideRenderer.onInitialize() ShockwaveRenderer.onInitialize() MatrixKeyBindings.onInitialize() + registerShaderReloadListener() registerEntityRenderers() onWindowInitialization() } @@ -87,6 +97,23 @@ class MatrixClient : ClientModInitializer { EntityRendererRegistry.register(ModEntityTypes.devEntity) { context -> DevEntityRenderer(context) } } + private fun registerShaderReloadListener() { + ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener( + object : SimpleSynchronousResourceReloadListener { + override fun getFabricId() = Matrix.identifier("shader_pipeline_reload") + + override fun onResourceManagerReload(resourceManager: ResourceManager) { + MatrixShaderPipelines.requestPostPipelinePrecompile() + MatrixGuiPipelines.requestGuiPipelinePrecompile() + } + } + ) + ClientTickEvents.END_CLIENT_TICK.register { + MatrixShaderPipelines.runRequestedPrecompileIfPossible() + MatrixGuiPipelines.runRequestedPrecompileIfPossible() + } + } + companion object { private var lastNonEmptyMagicList: List? = null private var shaderPipelinesInitialized = false @@ -112,6 +139,8 @@ class MatrixClient : ClientModInitializer { shaderPipelinesInitialized = true ShaderStageStore.Default.discoverFiles() ShaderStageStore.Default.precompileAll() + MatrixShaderPipelines.registerKnownPostPipelines() + MatrixGuiPipelines.registerKnownGuiPipelines() } } } diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/MatrixHud.kt b/common/src/main/kotlin/heckerpowered/matrix/client/MatrixHud.kt index 84f7c15f..bab16c87 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/MatrixHud.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/MatrixHud.kt @@ -13,6 +13,7 @@ import heckerpowered.matrix.client.core.ClientOptions.aimAssistMaxDistance import heckerpowered.matrix.client.event.KeyEvent import heckerpowered.matrix.client.event.MouseButtonEvent import heckerpowered.matrix.client.render.PostProcessRenderer +import heckerpowered.matrix.client.render.MatrixGuiPipelines import heckerpowered.matrix.client.shader.BlitProgram import heckerpowered.matrix.client.shader.ResourceShader import heckerpowered.matrix.client.ui.element.ManaBar @@ -158,6 +159,7 @@ object MatrixHud { private val manaOverclockAnimation = SimpleDoubleAnimation(initValue = 1.0) private val magicOverclockAnimation = SimpleDoubleAnimation(initValue = 1.0) private val grayscaleIntensityAnimation = SimpleDoubleAnimation(initValue = .0) + private val dissolveAnimation = SimpleDoubleAnimation(initValue = 1.0) private val crosshairX = SimpleDoubleAnimation(initValue = .0) private val crosshairY = SimpleDoubleAnimation(initValue = .0) private val magicHudTimeScale = TimeController.allocateTimeController() @@ -666,6 +668,9 @@ object MatrixHud { magicShownOpacityAnimation.value = 1.0 manaShownAnimation.value = .0 manaOpacityAnimation.value = 1.0 + dissolveAnimation.duration = Duration.ofMillis(1000) + dissolveAnimation.startTime = Duration.ZERO + dissolveAnimation.value = .0 entityDescriptionOpacityAnimation.value = if (targetedEntity != null) 1.0 else .0 descriptionYOffsetAnimation.value = if (targetedEntity != null) .0 else -35.0 AimAssist.resetAnimation() @@ -687,6 +692,8 @@ object MatrixHud { entityDescriptionOpacityAnimation.value = .0 descriptionYOffsetAnimation.value = -35.0 fovAnimation.value = 1.0 + dissolveAnimation.duration = Duration.ofMillis(300) + dissolveAnimation.value = 1.0 // takeScreenShot = true manaBar.onHudVisibilityChanged(false) @@ -990,7 +997,15 @@ object MatrixHud { val top = (height / 2.0 - 100.0 - offsetY / 2.0).roundToInt() val bottom = (height / 2.0 + 100.0 + offsetY / 2.0).roundToInt() - drawContext.fill(left, top, right, bottom, color((alpha * 127.5).roundToInt(), 255, 255, 255)) + MatrixGuiPipelines.drawDissolveRect( + drawContext, + left, + top, + right, + bottom, + color((alpha * 127.5).roundToInt(), 255, 255, 255), + dissolveAnimation.animatedValue.toFloat(), + ) val target = cachedTargetedEntity val descriptionAlpha = min(alpha, entityDescriptionOpacityAnimation.animatedValue) diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGraphicsBackend.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGraphicsBackend.kt new file mode 100644 index 00000000..e0a8e742 --- /dev/null +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGraphicsBackend.kt @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (c) 2026 heckerpowered + */ + +package heckerpowered.matrix.client.render + +import com.mojang.blaze3d.systems.RenderSystem + +object MatrixGraphicsBackend { + private val deviceClassName: String? + get() = RenderSystem.tryGetDevice()?.javaClass?.name + + fun isVulkan(): Boolean { + return deviceClassName?.contains("vulkan", ignoreCase = true) == true + } + + fun isOpenGl(): Boolean { + val name = deviceClassName ?: return false + return name.contains("opengl", ignoreCase = true) || name.contains(".Gl", ignoreCase = false) + } +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGuiPipelines.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGuiPipelines.kt new file mode 100644 index 00000000..8e5a4d66 --- /dev/null +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixGuiPipelines.kt @@ -0,0 +1,175 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (c) 2026 heckerpowered + */ + +package heckerpowered.matrix.client.render + +import com.mojang.blaze3d.pipeline.BlendFunction +import com.mojang.blaze3d.pipeline.ColorTargetState +import com.mojang.blaze3d.pipeline.RenderPipeline +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.textures.FilterMode +import com.mojang.blaze3d.vertex.DefaultVertexFormat +import com.mojang.blaze3d.vertex.VertexConsumer +import com.mojang.blaze3d.vertex.VertexFormat +import heckerpowered.matrix.Matrix +import net.minecraft.client.gui.navigation.ScreenRectangle +import net.minecraft.client.gui.GuiGraphicsExtractor +import net.minecraft.client.gui.render.TextureSetup +import net.minecraft.client.renderer.BindGroupLayouts +import net.minecraft.client.renderer.RenderPipelines +import net.minecraft.client.renderer.state.gui.GuiElementRenderState +import org.joml.Matrix3x2f +import org.joml.Matrix3x2fc +import kotlin.math.roundToInt + +object MatrixGuiPipelines { + private var dissolveRectPipeline: RenderPipeline? = null + @Volatile + private var precompileRequested = false + + fun dissolveRectPipeline(): RenderPipeline { + return dissolveRectPipeline ?: RenderPipelines.register( + RenderPipeline.builder() + .withLocation(Matrix.identifier("pipeline/gui_dissolve_rect")) + .withVertexShader(Matrix.identifier("gui/dissolve_rect")) + .withFragmentShader(Matrix.identifier("gui/dissolve_rect")) + .withBindGroupLayout(BindGroupLayouts.MATRICES_PROJECTION) + .withBindGroupLayout(BindGroupLayouts.SAMPLER0) + .withVertexFormat(DefaultVertexFormat.POSITION_TEX_COLOR_NORMAL, VertexFormat.Mode.QUADS) + .withCull(false) + .withColorTargetState(ColorTargetState(BlendFunction.TRANSLUCENT)) + .build() + ).also { + dissolveRectPipeline = it + } + } + + fun registerKnownGuiPipelines() { + dissolveRectPipeline() + } + + fun requestGuiPipelinePrecompile() { + precompileRequested = true + } + + fun runRequestedPrecompileIfPossible() { + if (!precompileRequested || RenderSystem.tryGetDevice() == null || !RenderSystem.isOnRenderThread()) { + return + } + precompileRequested = false + val pipeline = dissolveRectPipeline() + val result = runCatching { + RenderSystem.getDevice().precompilePipeline(pipeline).isValid + } + result.exceptionOrNull()?.let { + Matrix.LOGGER.warn("Matrix GUI shader pipeline failed to precompile: gui/dissolve_rect", it) + } + if (result.getOrDefault(false)) { + Matrix.LOGGER.info("Matrix GUI shader pipelines precompiled: 1/1") + } else if (result.exceptionOrNull() == null) { + Matrix.LOGGER.warn("Matrix GUI shader pipeline is invalid after precompile: gui/dissolve_rect") + } + } + + fun drawDissolveRect( + drawContext: GuiGraphicsExtractor, + left: Int, + top: Int, + right: Int, + bottom: Int, + color: Int, + dissolveFactor: Float = 1.0F, + ) { + val textureSetup = dissolveTextureSetup() ?: run { + drawContext.fill(left, top, right, bottom, color) + return + } + drawContext.guiRenderState.addGuiElement( + DissolveRectRenderState( + dissolveRectPipeline(), + textureSetup, + Matrix3x2f(drawContext.pose()), + left, + top, + right, + bottom, + color, + dissolveFactor.coerceIn(.0F, 1.0F), + (right - left).toFloat().coerceAtLeast(1.0F), + (bottom - top).toFloat().coerceAtLeast(1.0F), + drawContext.scissorStack.peek(), + ) + ) + } + + private fun dissolveTextureSetup(): TextureSetup? { + val noiseTexture = MatrixShaderTextures.perlinNoiseTextureView() ?: return null + val sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST) + return TextureSetup.singleTexture(noiseTexture, sampler) + } + + private data class DissolveRectRenderState( + private val pipeline: RenderPipeline, + private val textureSetup: TextureSetup, + private val pose: Matrix3x2fc, + private val x0: Int, + private val y0: Int, + private val x1: Int, + private val y1: Int, + private val color: Int, + private val dissolveFactor: Float, + private val width: Float, + private val height: Float, + private val scissorArea: ScreenRectangle?, + ) : GuiElementRenderState { + private val bounds = ScreenRectangle(x0, y0, x1 - x0, y1 - y0) + .transformMaxBounds(pose) + .let { transformed -> + scissorArea?.let(transformed::intersection) ?: transformed + } + + override fun pipeline() = pipeline + + override fun textureSetup() = textureSetup + + override fun scissorArea() = scissorArea + + override fun bounds() = bounds + + override fun buildVertices(vertexConsumer: VertexConsumer) { + val aspect = (height / width).coerceIn(.0F, 1.0F) + val time = ((System.nanoTime() / 1_000_000_000.0) % 10.0 / 10.0).toFloat() + vertex(vertexConsumer, x0.toFloat(), y0.toFloat(), .0F, .0F, dissolveFactor, aspect, time) + vertex(vertexConsumer, x0.toFloat(), y1.toFloat(), .0F, 1.0F, dissolveFactor, aspect, time) + vertex(vertexConsumer, x1.toFloat(), y1.toFloat(), 1.0F, 1.0F, dissolveFactor, aspect, time) + vertex(vertexConsumer, x1.toFloat(), y0.toFloat(), 1.0F, .0F, dissolveFactor, aspect, time) + } + + private fun vertex( + vertexConsumer: VertexConsumer, + x: Float, + y: Float, + u: Float, + v: Float, + dissolveFactor: Float, + aspect: Float, + time: Float, + ) { + vertexConsumer + .addVertexWith2DPose(pose, x, y) + .setUv(u, v) + .setColor(color) + .setNormal( + encodeSignedNormal(dissolveFactor), + encodeSignedNormal(aspect), + encodeSignedNormal(time), + ) + } + + private fun encodeSignedNormal(value: Float): Float { + return ((value.coerceIn(.0F, 1.0F) * 2.0F - 1.0F) * 127.0F).roundToInt() / 127.0F + } + } +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPointSpriteParticles.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPointSpriteParticles.kt new file mode 100644 index 00000000..2befcbdc --- /dev/null +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPointSpriteParticles.kt @@ -0,0 +1,304 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (c) 2026 heckerpowered + */ + +package heckerpowered.matrix.client.render + +import com.mojang.blaze3d.vertex.PoseStack +import com.mojang.blaze3d.vertex.VertexConsumer +import heckerpowered.matrix.client.TimeController +import heckerpowered.matrix.client.render.particle.module.particle_spawn.RandomLifetimeModule +import heckerpowered.matrix.client.render.particle.module.particle_update.DragModule +import heckerpowered.matrix.client.render.particle.module.particle_update.ScaleSpriteSizeBySpeedModule +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents +import net.minecraft.client.renderer.rendertype.MatrixRenderTypes +import net.minecraft.util.ARGB +import net.minecraft.world.phys.Vec3 +import org.joml.Vector2f +import org.joml.Vector3f +import kotlin.math.PI +import kotlin.math.acos +import kotlin.math.cos +import kotlin.math.floor +import kotlin.math.max +import kotlin.math.sin +import kotlin.math.sqrt +import kotlin.time.DurationUnit + +object MatrixPointSpriteParticles { + private const val PARTICLE_CAPACITY = 10000 + private const val SPRITE_SIZE = 80.0F + private const val COLOR_INTENSITY = 4.0F + + private val trailSystem = CpuPointSpriteSystem( + capacity = PARTICLE_CAPACITY, + baseColor = color(1.0F, 0.5F, 1.0F, 1.0F), + colorIntensity = COLOR_INTENSITY, + speedRange = Vector2f(.0F, 1.0F), + velocityMultiplier = Vector3f(1.0F, 1.0F, 1.0F), + ) + private val explosionSystem = CpuPointSpriteSystem( + capacity = PARTICLE_CAPACITY, + baseColor = color(0.1F, 0.5F, 1.0F, 1.0F), + colorIntensity = COLOR_INTENSITY, + speedRange = Vector2f(.0F, 20.0F), + velocityMultiplier = Vector3f(10.0F, 10.0F, 10.0F), + ) + private var initialized = false + + @JvmStatic + fun onInitialize() { + if (initialized) { + return + } + initialized = true + LevelRenderEvents.COLLECT_SUBMITS.register(::collectSubmits) + } + + fun spawnTrailParticles(position: Vec3, count: Int) { + trailSystem.spawnPartial(position, count) + } + + fun spawnExplosionParticles(position: Vec3, speedRange: Vector2f, velocityMultiplier: Vector3f) { + explosionSystem.speedRange.set(speedRange) + explosionSystem.velocityMultiplier.set(velocityMultiplier) + explosionSystem.spawnAll(position) + } + + private fun collectSubmits(context: LevelRenderContext) { + updateParticles() + if (!trailSystem.hasActiveParticles && !explosionSystem.hasActiveParticles) { + return + } + + val cameraPosition = context.levelState().cameraRenderState.pos + val poseStack = PoseStack() + poseStack.translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z) + + context.submitNodeCollector().submitCustomGeometry( + poseStack, + MatrixRenderTypes.matrixPointSprite(MatrixShaderPipelines.worldPointSpritePipeline()), + ) { pose, vertexConsumer -> + trailSystem.writeVertices(pose, vertexConsumer) + explosionSystem.writeVertices(pose, vertexConsumer) + } + } + + private fun updateParticles() { + val deltaSeconds = TimeController.strictDeltaTime.toDouble(DurationUnit.SECONDS).toFloat() + if (deltaSeconds <= .0F) { + return + } + trailSystem.update(deltaSeconds) + explosionSystem.update(deltaSeconds) + } + + private class CpuPointSpriteSystem( + private val capacity: Int, + private val baseColor: Int, + private val colorIntensity: Float, + val speedRange: Vector2f, + val velocityMultiplier: Vector3f, + ) { + private val particles = Array(capacity) { Particle() } + private var spawnCursor = 0 + private var activeParticleCount = 0 + + val hasActiveParticles: Boolean + get() = activeParticleCount > 0 + + fun spawnPartial(position: Vec3, count: Int) { + val particleCount = count.coerceAtLeast(0) + if (particleCount == 0) { + return + } + + spawnCursor += particleCount + if (spawnCursor > capacity) { + spawnCursor = 0 + } + spawnRange(position, spawnCursor, particleCount) + } + + fun spawnAll(position: Vec3) { + clear() + spawnCursor = 0 + spawnRange(position, 0, capacity) + } + + fun update(deltaSeconds: Float) { + if (activeParticleCount == 0) { + return + } + + for (index in particles.indices) { + val particle = particles[index] + if (!particle.active) { + continue + } + if (particle.age > particle.lifetime) { + particle.active = false + activeParticleCount-- + continue + } + + val newVelocityX = particle.velocityX + particle.accelerationX * deltaSeconds + val newVelocityY = particle.velocityY + particle.accelerationY * deltaSeconds + val newVelocityZ = particle.velocityZ + particle.accelerationZ * deltaSeconds + particle.x += newVelocityX * deltaSeconds + particle.y += newVelocityY * deltaSeconds + particle.z += newVelocityZ * deltaSeconds + particle.velocityX = newVelocityX + particle.velocityY = newVelocityY + particle.velocityZ = newVelocityZ + particle.age += deltaSeconds + + val drag = randomRangeFloat(index, DragModule.minDrag, DragModule.maxDrag) + val dragFactor = max(.0F, 1.0F - drag * deltaSeconds) + particle.velocityX *= dragFactor + particle.velocityY *= dragFactor + particle.velocityZ *= dragFactor + + val speed = sqrt( + particle.velocityX * particle.velocityX + + particle.velocityY * particle.velocityY + + particle.velocityZ * particle.velocityZ, + ) + val normalizedSpeed = (speed / ScaleSpriteSizeBySpeedModule.velocityThreshold).coerceIn(.0F, 1.0F) + particle.scale = lerp( + ScaleSpriteSizeBySpeedModule.minScaleFactor, + ScaleSpriteSizeBySpeedModule.maxScaleFactor, + normalizedSpeed, + ) + } + } + + fun writeVertices(pose: PoseStack.Pose, vertexConsumer: VertexConsumer) { + if (activeParticleCount == 0) { + return + } + + for (particle in particles) { + if (!particle.active) { + continue + } + vertexConsumer.addVertex(pose, particle.x, particle.y, particle.z) + .setUv(particle.spriteSize * particle.scale, colorIntensity) + .setColor(baseColor) + } + } + + private fun clear() { + for (particle in particles) { + particle.active = false + } + activeParticleCount = 0 + } + + private fun spawnRange(position: Vec3, first: Int, count: Int) { + val start = first.coerceIn(0, capacity) + val actualCount = count.coerceIn(0, capacity - start) + if (actualCount <= 0) { + return + } + + val time = ((System.currentTimeMillis() % 10000L) / 1000.0).toFloat() + for (offset in 0 until actualCount) { + val index = start + offset + val particle = particles[index] + if (!particle.active) { + activeParticleCount++ + } + initializeParticle(particle, index, position, time) + } + } + + private fun initializeParticle(particle: Particle, index: Int, position: Vec3, time: Float) { + val velocityDirection = randomDirection(index + time) + val randomSpeed = randomScalarInRange(index + 114.514F, speedRange.x, speedRange.y) + + particle.active = true + particle.x = position.x.toFloat() + particle.y = position.y.toFloat() + particle.z = position.z.toFloat() + particle.velocityX = velocityDirection.x * randomSpeed * velocityMultiplier.x + particle.velocityY = velocityDirection.y * randomSpeed * velocityMultiplier.y + particle.velocityZ = velocityDirection.z * randomSpeed * velocityMultiplier.z + particle.accelerationX = .0F + particle.accelerationY = .0F + particle.accelerationZ = .0F + particle.spriteSize = SPRITE_SIZE + particle.scale = 1.0F + particle.age = .0F + particle.lifetime = randomRangeFloat(index, RandomLifetimeModule.minLifetime, RandomLifetimeModule.maxLifetime) + } + } + + private class Particle { + var active = false + var x = .0F + var y = .0F + var z = .0F + var velocityX = .0F + var velocityY = .0F + var velocityZ = .0F + var accelerationX = .0F + var accelerationY = .0F + var accelerationZ = .0F + var spriteSize = SPRITE_SIZE + var scale = 1.0F + var age = .0F + var lifetime = .0F + } + + private fun randomDirection(seed: Float): Vector3f { + val u = hash(seed) + val v = hash(seed + 1.0F) + val theta = u * 2.0F * PI.toFloat() + val phi = acos((2.0F * v - 1.0F).coerceIn(-1.0F, 1.0F)) + val sinPhi = sin(phi) + return Vector3f( + sinPhi * cos(theta), + sinPhi * sin(theta), + cos(phi), + ) + } + + private fun randomScalarInRange(seed: Float, minValue: Float, maxValue: Float): Float { + return lerp(minValue, maxValue, hash(seed)) + } + + private fun hash(value: Float): Float { + return fract(sin(value) * 43758.5453F) + } + + private fun randomRangeFloat(vertexId: Int, minValue: Float, maxValue: Float): Float { + var seed = vertexId + seed = (seed xor 61) xor (seed ushr 16) + seed *= 9 + seed = seed xor (seed ushr 4) + seed *= 0x27d4eb2d + seed = seed xor (seed ushr 15) + val normalized = (seed and 0x00FFFFFF).toFloat() / 0x01000000.toFloat() + return lerp(minValue, maxValue, normalized) + } + + private fun lerp(from: Float, to: Float, amount: Float): Float { + return from + (to - from) * amount + } + + private fun fract(value: Float): Float { + return value - floor(value) + } + + private fun color(red: Float, green: Float, blue: Float, alpha: Float): Int { + return ARGB.color( + (alpha.coerceIn(.0F, 1.0F) * 255.0F).toInt(), + (red.coerceIn(.0F, 1.0F) * 255.0F).toInt(), + (green.coerceIn(.0F, 1.0F) * 255.0F).toInt(), + (blue.coerceIn(.0F, 1.0F) * 255.0F).toInt(), + ) + } +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPostUniforms.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPostUniforms.kt index 2615fccd..e2d42d4a 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPostUniforms.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixPostUniforms.kt @@ -20,9 +20,12 @@ import heckerpowered.matrix.client.render.post.ToneMapping import heckerpowered.matrix.client.render.shader.GaussianBlurRenderer import heckerpowered.matrix.client.render.shader.RadialBlurRenderer import heckerpowered.matrix.client.render.shader.TentShader +import heckerpowered.matrix.client.render.shader.VolumeDistortion import heckerpowered.matrix.client.render.shader.hud.ProgressRingRenderer import heckerpowered.matrix.client.render.shader.sdf.DropShadowRenderer import heckerpowered.matrix.client.render.shader.sdf.SignedDistanceField +import heckerpowered.matrix.client.render.post.TextureDissolveShader +import heckerpowered.matrix.client.shader.TexturePixelDissolveProgram import heckerpowered.matrix.client.viewMatrix import net.minecraft.client.gl.Framebuffer import org.joml.Matrix4f @@ -37,6 +40,9 @@ object MatrixPostUniforms { var colorFilterColor = Vector4f(1.0F, 1.0F, 1.0F, 1.0F) var edgeHighlightThreshold = 1.0F var edgeHighlightColor = Vector4f(0.7F, 0.1F, 0.1F, 1.0F) + var auraAlpha = .0F + var auraColor = Vector4f(.0F, .0F, .0F, .0F) + var grainStrength = 0.05F private val buffers = mutableMapOf() @@ -167,6 +173,28 @@ object MatrixPostUniforms { } } + "post/dissolve/dissolve" -> { + val dissolveFactor = TexturePixelDissolveProgram.dissolveFactor.takeIf { it != .0F } ?: 0.5F + data.putVec4(0, dissolveFactor, 0.05F, 15.0F, 16.0F) + data.putVec4(1, 1.0F, currentTimeSeconds(), input.textureWidth.toFloat(), input.textureHeight.toFloat()) + data.putVec4(2, 0.1F, 0.5F, 1.0F, 1.0F) + } + + "post/dissolve/texture_dissolve" -> { + val dissolveFactor = TextureDissolveShader.dissolveFactor + .takeIf { it != .0F } + ?: TexturePixelDissolveProgram.dissolveFactor.takeIf { it != .0F } + ?: 0.5F + data.putVec4(0, dissolveFactor, 0.05F, 16.0F, 1.0F) + data.putVec4(1, .0F, 0.5F, 1.0F, 1.0F) + } + + "post/dissolve/texture_pixel_dissolve" -> { + val dissolveFactor = TexturePixelDissolveProgram.dissolveFactor.takeIf { it != .0F } ?: 0.5F + data.putVec4(0, dissolveFactor, 0.05F, 100.0F, 1.0F) + data.putVec4(1, .0F, 0.5F, 1.0F, 1.0F) + } + "post/color_fusion" -> { data.putVec4(0, colorMultiplier.x, colorMultiplier.y, colorMultiplier.z, colorMultiplier.w) } @@ -178,14 +206,39 @@ object MatrixPostUniforms { } } + "post/aura" -> { + data.putVec4(0, currentTimeSeconds(), auraAlpha, .0F, .0F) + auraColor.also { color -> data.putVec4(1, color.x, color.y, color.z, color.w) } + } + + "post/circle" -> { + data.putMatrix4f(0, viewMatrix) + data.putMatrix4f(64, projectionMatrix) + data.putVec4(8, 600.0F, 600.0F, 600.0F, 300.0F) + data.putVec4(9, input.textureWidth.toFloat(), input.textureHeight.toFloat(), 1.0F, .0F) + } + "post/ghost" -> { data.putVec4(0, 1.0F, .0F, .0F, .0F) } + "post/grain/background_grain" -> { + data.putVec4(0, grainStrength, .0F, .0F, .0F) + } + "post/the_world" -> { data.putVec4(0, MatrixHud.grayscaleIntensity, .0F, .0F, .0F) } + "post/volume_distortion" -> { + data.putMatrix4f(0, viewMatrix.invert(Matrix4f())) + data.putMatrix4f(64, projectionMatrix.invert(Matrix4f())) + VolumeDistortion.volumePosition.also { position -> + data.putVec4(8, position.x, position.y, position.z, VolumeDistortion.volumeRadius) + } + data.putVec4(9, VolumeDistortion.grayscaleIntensity, VolumeDistortion.emissiveStrength, .0F, .0F) + } + "post/shockwave" -> { data.putMatrix4f(0, projectionMatrix.invert(Matrix4f())) data.putMatrix4f(64, viewMatrix.invert(Matrix4f())) @@ -211,9 +264,21 @@ object MatrixPostUniforms { ) data.putVec4(9, input.textureWidth.toFloat(), input.textureHeight.toFloat(), .0F, .0F) } + + "post/vortex/vortex" -> { + data.putVec4(0, currentTimeSeconds(), 0.4F, 0.5F, .0F) + } + + "post/vortex/inverse_vortex" -> { + data.putVec4(0, currentTimeSeconds(), 1.0F, 1.0F, 0.1F) + } } } + private fun currentTimeSeconds(): Float { + return (System.currentTimeMillis().toDouble() / 1000.0 % 1000.0).toFloat() + } + private fun ByteBuffer.putMatrix4f(offset: Int, matrix: Matrix4f) { putFloat(offset, matrix.m00()) putFloat(offset + 4, matrix.m01()) diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderPipelines.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderPipelines.kt index 96d6b354..442052be 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderPipelines.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderPipelines.kt @@ -9,7 +9,9 @@ import com.mojang.blaze3d.pipeline.BindGroupLayout import com.mojang.blaze3d.pipeline.CompiledRenderPipeline import com.mojang.blaze3d.pipeline.BlendFunction import com.mojang.blaze3d.pipeline.ColorTargetState +import com.mojang.blaze3d.pipeline.DepthStencilState import com.mojang.blaze3d.pipeline.RenderPipeline +import com.mojang.blaze3d.platform.CompareOp import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.DefaultVertexFormat import com.mojang.blaze3d.vertex.VertexFormat @@ -43,12 +45,16 @@ object MatrixShaderPipelines { private val postProcessPipelines = linkedMapOf() private val compiledPipelines = linkedMapOf() private var pointSpritePipeline: RenderPipeline? = null + private var worldPointSpritePipeline: RenderPipeline? = null + @Volatile + private var precompileRequested = false private val fallbackPostFragments = listOf( - "post/dissolve/dissolve", "post/dissolve/texture_dissolve", + "post/dissolve/texture_pixel_dissolve", "post/aura", "post/blend_screen", + "post/blur/blur_mask", "post/blur/gaussian_blur", "post/blur/kawase_blur", "post/blur/radial_blur", @@ -78,11 +84,15 @@ object MatrixShaderPipelines { "post/shockwave", "post/the_world", "post/tone_mapping/aces_filmic", - "post/velocity_map/velocity_map", "post/the_world", + "post/volume_distortion", "post/vortex/vortex", "post/vortex/inverse_vortex", ) + private val nonPostProcessFragments = setOf( + "post/dissolve/dissolve", + "post/velocity_map/velocity_map", + ) fun postProcessPipeline(fragmentShader: String): RenderPipeline { return postProcessPass(fragmentShader).pipeline @@ -109,17 +119,40 @@ object MatrixShaderPipelines { } fun precompileKnownPostPipelines() { + compiledPipelines.clear() val postFragments = discoverPostFragments() val compiledPostPipelines = postFragments.count(::precompilePostPipeline) val compiledPointSprite = precompilePointSpritePipeline() + val compiledWorldPointSprite = precompileWorldPointSpritePipeline() Matrix.LOGGER.info( - "Matrix shader pipelines precompiled: {}/{} post effects, point sprite={}", + "Matrix shader pipelines precompiled: {}/{} post effects, point sprite={}, world point sprite={}", compiledPostPipelines, postFragments.size, compiledPointSprite, + compiledWorldPointSprite, ) } + fun requestPostPipelinePrecompile() { + precompileRequested = true + } + + fun runRequestedPrecompileIfPossible() { + if (!precompileRequested || RenderSystem.tryGetDevice() == null || !RenderSystem.isOnRenderThread()) { + return + } + precompileRequested = false + precompileKnownPostPipelines() + } + + fun registerKnownPostPipelines() { + val postFragments = discoverPostFragments() + postFragments.forEach(::postProcessPass) + pointSpritePipeline() + worldPointSpritePipeline() + Matrix.LOGGER.info("Matrix shader pipelines registered: {} post effects, point sprite=true, world point sprite=true", postFragments.size) + } + fun precompilePostPipeline(fragmentShader: String): Boolean { val normalizedFragment = normalizeShaderId(fragmentShader) val pipeline = postProcessPipeline(normalizedFragment) @@ -159,6 +192,11 @@ object MatrixShaderPipelines { .withVertexShader(Matrix.identifier("point_sprite/point_sprite")) .withFragmentShader(Matrix.identifier("point_sprite/point_sprite")) .withVertexFormat(DefaultVertexFormat.POSITION, VertexFormat.Mode.POINTS) + .withBindGroupLayout( + BindGroupLayout.builder() + .withUniform("MatrixPointSpriteUniforms", com.mojang.blaze3d.shaders.UniformType.UNIFORM_BUFFER) + .build() + ) .withCull(false) .withColorTargetState(ColorTargetState(BlendFunction.ADDITIVE)) .build() @@ -167,6 +205,22 @@ object MatrixShaderPipelines { } } + fun worldPointSpritePipeline(): RenderPipeline { + return worldPointSpritePipeline ?: RenderPipelines.register( + RenderPipeline.builder() + .withLocation(Matrix.identifier("pipeline/world_point_sprite")) + .withVertexShader(Matrix.identifier("particle/particle_render/world_point_sprite")) + .withFragmentShader(Matrix.identifier("particle/particle_render/world_point_sprite")) + .withVertexFormat(DefaultVertexFormat.POSITION_TEX_COLOR, VertexFormat.Mode.POINTS) + .withBindGroupLayout(BindGroupLayouts.MATRICES_PROJECTION) + .withCull(false) + .withDepthStencilState(DepthStencilState(CompareOp.LESS_THAN_OR_EQUAL, true)) + .build() + ).also { + worldPointSpritePipeline = it + } + } + fun precompilePointSpritePipeline(): Boolean { val pipeline = pointSpritePipeline() val device = RenderSystem.tryGetDevice() ?: return false @@ -185,6 +239,24 @@ object MatrixShaderPipelines { return valid } + fun precompileWorldPointSpritePipeline(): Boolean { + val pipeline = worldPointSpritePipeline() + val device = RenderSystem.tryGetDevice() ?: return false + val result = runCatching { + val compiled = device.precompilePipeline(pipeline) + compiledPipelines["particle/particle_render/world_point_sprite"] = compiled + compiled.isValid + } + result.exceptionOrNull()?.let { + Matrix.LOGGER.warn("Matrix world point sprite pipeline failed to precompile", it) + } + val valid = result.getOrDefault(false) + if (!valid && result.exceptionOrNull() == null) { + Matrix.LOGGER.warn("Matrix world point sprite pipeline is invalid after precompile") + } + return valid + } + private fun normalizeShaderId(path: String): String { return path .removePrefix("/") @@ -203,7 +275,9 @@ object MatrixShaderPipelines { .map(::normalizeShaderId) .toList() }.getOrDefault(emptyList()) - return (fallbackPostFragments + discovered).distinct() + return (fallbackPostFragments + discovered) + .distinct() + .filterNot(nonPostProcessFragments::contains) } private fun parseSamplerNames(fragmentShader: String): List { @@ -248,5 +322,11 @@ object MatrixShaderPipelines { "framebuffer", "colorAttachment", "hdrScene", + "depthAttachment", + "noiseTexture", + "noiseColorAttachment", + "normalTexture", + "opacityMask", + "sceneColorTexture", ) } diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderTextures.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderTextures.kt new file mode 100644 index 00000000..6d8dda1f --- /dev/null +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/MatrixShaderTextures.kt @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (c) 2026 heckerpowered + */ + +package heckerpowered.matrix.client.render + +import com.mojang.blaze3d.textures.GpuTextureView +import heckerpowered.matrix.Matrix +import heckerpowered.matrix.client.minecraft +import net.minecraft.resources.Identifier + +object MatrixShaderTextures { + private val perlinNoiseId = Matrix.identifier("textures/noise/perlin_noise.png") + private val vortexNoiseId = Matrix.identifier("textures/noise/vortex_noise.png") + + fun perlinNoiseTextureView(): GpuTextureView? { + return textureView(perlinNoiseId) + } + + fun vortexNoiseTextureView(): GpuTextureView? { + return textureView(vortexNoiseId) ?: perlinNoiseTextureView() + } + + private fun textureView(identifier: Identifier): GpuTextureView? { + return runCatching { + minecraft.textureManager.getTexture(identifier).textureView + }.getOrNull() + } +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/PostProcessRenderer.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/PostProcessRenderer.kt index e418867a..8e4861ac 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/PostProcessRenderer.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/PostProcessRenderer.kt @@ -13,11 +13,14 @@ import heckerpowered.matrix.client.MatrixHud import heckerpowered.matrix.client.event.PostProcessCallback import heckerpowered.matrix.client.minecraft import heckerpowered.matrix.client.shader.BlitProgram +import heckerpowered.matrix.client.shader.ResourceShader import heckerpowered.matrix.client.shader.UniformProvider import net.minecraft.client.Minecraft import net.minecraft.client.gl.Framebuffer import net.minecraft.client.gl.SimpleFramebuffer import net.minecraft.client.renderer.RenderPipelines +import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER +import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER import java.util.* val framebufferProvider: UniformProvider @@ -40,6 +43,12 @@ object PostProcessRenderer { private val managedFramebuffers = mutableListOf() private val framebuffers = mutableListOf(createFramebuffer(), createFramebuffer()) private var currentFramebufferIndex = 0 + private val additiveBlendShader by lazy { + BlitProgram( + ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), + ResourceShader("/assets/matrix/shaders/post/color_fusion.fsh", GL_FRAGMENT_SHADER), + ) + } fun currentFramebuffer(): Framebuffer { return framebuffers[currentFramebufferIndex] @@ -128,7 +137,9 @@ object PostProcessRenderer { } else { renderToFramebuffer(sourceFramebuffer) } - PostProcessCallback.EVENT.invoker().onPostProcess() + if (minecraft.level != null || postProcessShaders.isNotEmpty()) { + PostProcessCallback.EVENT.invoker().onPostProcess() + } } private fun syncMinecraftFramebufferSize() { @@ -239,6 +250,20 @@ object PostProcessRenderer { copyDepthTexture(from, to) } + @JvmStatic + fun blendAdditiveFramebuffer(overlay: Framebuffer, target: Framebuffer) { + val output = temporaryFramebufferExcluding(overlay, target) + renderShaderToFramebuffer( + additiveBlendShader, + output, + linkedMapOf( + "primaryFramebuffer" to target, + "secondaryFramebuffer" to overlay, + ), + ) + copyFramebuffer(output, target) + } + fun useFramebuffer(framebuffer: Framebuffer, action: () -> Unit) { val previousFramebuffer = sourceFramebuffer val previousBoundFramebuffer = boundFramebuffer @@ -260,17 +285,17 @@ object PostProcessRenderer { ?.toMatrixFragmentId() ?.let(::compiledSingleInputPass) if (pass == null) { - blitFramebuffer( - input, - output, - RenderPipelines.ENTITY_OUTLINE_BLIT, - mapOf("InSampler" to input.colorTextureView) - ) - + copyFramebuffer(input, output) return } - val textureBindings = pass.samplers.associateWith { input.colorTextureView } + val textureBindings = pass.samplers.mapNotNull { sampler -> + defaultTextureView(sampler, input)?.let { sampler to it } + }.toMap() + if (textureBindings.keys != pass.samplers.toSet()) { + copyFramebuffer(input, output) + return + } blitFramebuffer(input, output, pass.pipeline, textureBindings, pass) } @@ -282,30 +307,45 @@ object PostProcessRenderer { requireNoReadWriteAlias(output, textureBindings) val fragmentShader = shader.fragmentResourcePath()?.toMatrixFragmentId() val pass = fragmentShader?.let(::compiledPostPass) - if (pass == null || !pass.samplers.all(textureBindings::containsKey)) { + if (pass == null || !pass.samplers.all { textureBindings.containsKey(it) || defaultTextureView(it, textureBindings.values.firstOrNull() ?: boundFramebuffer) != null }) { val fallbackInput = textureBindings.values.firstOrNull() ?: boundFramebuffer requireDifferentFramebuffers(fallbackInput, output) - blitFramebuffer( - fallbackInput, - output, - RenderPipelines.ENTITY_OUTLINE_BLIT, - mapOf("InSampler" to fallbackInput.colorTextureView) - ) + copyFramebuffer(fallbackInput, output) return } val firstInput = textureBindings.values.firstOrNull() ?: boundFramebuffer requireDifferentFramebuffers(firstInput, output) val textureViews = pass.samplers.associateWith { sampler -> - val framebuffer = textureBindings.getValue(sampler) - if (sampler.equals("depthAttachment", ignoreCase = true)) { - framebuffer.depthTextureView ?: framebuffer.colorTextureView + val framebuffer = textureBindings[sampler] + if (framebuffer != null) { + framebufferTextureView(sampler, framebuffer) } else { - framebuffer.colorTextureView + defaultTextureView(sampler, firstInput) ?: firstInput.colorTextureView } } blitFramebuffer(firstInput, output, pass.pipeline, textureViews, pass) } + private fun framebufferTextureView(sampler: String, framebuffer: Framebuffer): GpuTextureView { + return if (sampler.equals("depthAttachment", ignoreCase = true) || + sampler.endsWith("DepthAttachment", ignoreCase = true) + ) { + framebuffer.depthTextureView ?: framebuffer.colorTextureView + } else { + framebuffer.colorTextureView + } + } + + private fun defaultTextureView(sampler: String, input: Framebuffer): GpuTextureView? { + return when { + sampler.equals("noiseTexture", ignoreCase = true) -> MatrixShaderTextures.perlinNoiseTextureView() + sampler.equals("noiseColorAttachment", ignoreCase = true) -> MatrixShaderTextures.perlinNoiseTextureView() + sampler.equals("depthAttachment", ignoreCase = true) -> input.depthTextureView ?: input.colorTextureView + sampler.endsWith("DepthAttachment", ignoreCase = true) -> input.depthTextureView ?: input.colorTextureView + else -> input.colorTextureView + } + } + private fun compiledPostPass(fragmentShader: String): MatrixShaderPipelines.PostProcessPass? { val pass = MatrixShaderPipelines.postProcessPass(fragmentShader) if (!MatrixShaderPipelines.isPostPipelineCompiled(fragmentShader)) { @@ -377,6 +417,16 @@ object PostProcessRenderer { ) } + private fun temporaryFramebufferExcluding(vararg excluded: Framebuffer): Framebuffer { + return framebuffers.firstOrNull { candidate -> + excluded.none { excludedFramebuffer -> + candidate === excludedFramebuffer || + candidate.colorTexture === excludedFramebuffer.colorTexture || + candidate.colorTextureView === excludedFramebuffer.colorTextureView + } + } ?: createManagedFramebuffer() + } + private fun String.toMatrixFragmentId(): String? { val normalized = replace('\\', '/') .removePrefix("/assets/matrix/shaders/") diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/ScreenEffectRenderer.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/ScreenEffectRenderer.kt index 41c96b20..977508ab 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/ScreenEffectRenderer.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/ScreenEffectRenderer.kt @@ -11,6 +11,8 @@ import heckerpowered.matrix.client.player import heckerpowered.matrix.client.render.effect.SculkCatalystEffectRenderer import heckerpowered.matrix.client.render.post.CollapseEffectRenderer import heckerpowered.matrix.client.render.post.ShockwaveRenderer +import heckerpowered.matrix.client.render.post.BloomEffect +import heckerpowered.matrix.client.render.shader.RadialBlurRenderer import heckerpowered.matrix.client.shader.BlitProgram import heckerpowered.matrix.client.shader.ResourceShader import heckerpowered.matrix.client.ui.foundation.animation.ColorAnimation @@ -19,13 +21,11 @@ import heckerpowered.matrix.common.effect.ModMobEffects.ANGERED_EFFECT import heckerpowered.matrix.common.effect.ModMobEffects.WITHER_ARMOR_EFFECT import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.minecraft.client.Minecraft -import net.minecraft.core.particles.ParticleTypes import org.joml.Vector4f import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER import java.time.Duration import kotlin.math.abs -import kotlin.random.Random import net.minecraft.world.phys.Vec3 object ScreenEffectRenderer { @@ -40,6 +40,7 @@ object ScreenEffectRenderer { blue = SimpleDoubleAnimation(from = 1.0, to = 1.0, initValue = 1.0, duration = Duration.ofMillis(1000)), ) private val edgeThresholdAnimation = SimpleDoubleAnimation(initValue = 1.0, duration = Duration.ofMillis(1000)) + private val ghostStrengthAnimation = SimpleDoubleAnimation(initValue = .0, duration = Duration.ofMillis(1000)) val bloomThresholdAnimation = SimpleDoubleAnimation(initValue = 1.0, duration = Duration.ofMillis(1000)) private val colorFilterShader by lazy { @@ -56,6 +57,10 @@ object ScreenEffectRenderer { ) } + private val radialBlurShader by lazy { + RadialBlurRenderer.radialBlurShader + } + fun onInitialize() { if (initialized) { return @@ -68,13 +73,16 @@ object ScreenEffectRenderer { colorAnimation.green.start() colorAnimation.blue.start() edgeThresholdAnimation.start() + ghostStrengthAnimation.start() bloomThresholdAnimation.start() } + @JvmStatic fun beginRenderEntity() { // Entity post effects are currently driven by the 26.2 post-process and tick paths. } + @JvmStatic fun endRenderEntity() { if (SculkCatalystEffectRenderer.entity?.isAlive == false) { SculkCatalystEffectRenderer.entity = null @@ -82,19 +90,7 @@ object ScreenEffectRenderer { } fun spawnParticleAt(position: Vec3, count: Int = 1) { - val level = minecraft.level ?: return - repeat(count.coerceAtLeast(0)) { - val velocity = randomVelocity(0.045) - level.addParticle( - ParticleTypes.WITCH, - position.x, - position.y, - position.z, - velocity.x, - velocity.y, - velocity.z, - ) - } + MatrixPointSpriteParticles.spawnTrailParticles(position, count) } fun onWitherArmorEffectApplied() { @@ -102,6 +98,15 @@ object ScreenEffectRenderer { previousWitherArmorState = true previousWitherArmorDuration = player.getEffect(WITHER_ARMOR_EFFECT)?.duration ?: 0 + PostProcessRenderer.postProcessShaders.add(colorFilterShader) + PostProcessRenderer.postProcessShaders.add(edgeHighlightShader) + PostProcessRenderer.postProcessShaders.add(radialBlurShader) + + ghostStrengthAnimation.from = 5.0 + ghostStrengthAnimation.to = ghostStrengthAnimation.value + ghostStrengthAnimation.duration = Duration.ofMillis(1000) + ghostStrengthAnimation.start() + colorAnimation.red.from = 2.0 colorAnimation.red.to = if (previousAngryState) 2.0 else 1.0 colorAnimation.red.duration = Duration.ofMillis(10000) @@ -170,6 +175,12 @@ object ScreenEffectRenderer { } private fun onPostProcess() { + bloomThresholdAnimation.animatedValue = 1.0 + BloomEffect.brightnessThreshold = bloomThresholdAnimation.animatedValue.toFloat() + BloomEffect.brightnessPassFramebuffer = PostProcessRenderer.sourceFramebuffer + BloomEffect.renderBloom() + PostProcessRenderer.blendAdditiveFramebuffer(BloomEffect.bloomUpFramebuffer, PostProcessRenderer.sourceFramebuffer) + if (!CollapseEffectRenderer.dissolveFactor.isAnimating && CollapseEffectRenderer.dissolveFactor.animatedValue <= 0.001 ) { @@ -187,12 +198,19 @@ object ScreenEffectRenderer { } private fun onAngeredEffectApplied() { + PostProcessRenderer.postProcessShaders.add(colorFilterShader) + PostProcessRenderer.postProcessShaders.add(edgeHighlightShader) + PostProcessRenderer.postProcessShaders.add(radialBlurShader) + colorAnimation.red.value = 2.0 colorAnimation.red.duration = Duration.ofMillis(1000) colorAnimation.green.value = 1.0 colorAnimation.green.duration = Duration.ofMillis(1000) colorAnimation.blue.value = 1.0 edgeThresholdAnimation.value = 0.3 + edgeThresholdAnimation.duration = Duration.ofMillis(1000) + ghostStrengthAnimation.duration = Duration.ofMillis(1000) + ghostStrengthAnimation.value = 1.0 } private fun onAngeredEffectRemoved() { @@ -200,6 +218,7 @@ object ScreenEffectRenderer { colorAnimation.green.value = 1.0 colorAnimation.blue.value = 1.0 edgeThresholdAnimation.value = 1.0 + ghostStrengthAnimation.value = .0 } private fun applyPostProcessState() { @@ -211,6 +230,7 @@ object ScreenEffectRenderer { ) MatrixPostUniforms.edgeHighlightThreshold = edgeThresholdAnimation.animatedValue.toFloat() MatrixPostUniforms.edgeHighlightColor = Vector4f(0.7F, 0.1F, 0.1F, 1.0F) + RadialBlurRenderer.strength = ghostStrengthAnimation.animatedValue.toFloat() if (isColorFilterActive()) { PostProcessRenderer.postProcessShaders.add(colorFilterShader) @@ -223,6 +243,12 @@ object ScreenEffectRenderer { } else { PostProcessRenderer.postProcessShaders.remove(edgeHighlightShader) } + + if (isRadialBlurActive()) { + PostProcessRenderer.postProcessShaders.add(radialBlurShader) + } else { + PostProcessRenderer.postProcessShaders.remove(radialBlurShader) + } } private fun resetPostProcessState() { @@ -230,6 +256,7 @@ object ScreenEffectRenderer { colorAnimation.green.animatedValue = 1.0 colorAnimation.blue.animatedValue = 1.0 edgeThresholdAnimation.animatedValue = 1.0 + ghostStrengthAnimation.animatedValue = .0 applyPostProcessState() } @@ -246,11 +273,7 @@ object ScreenEffectRenderer { return edgeThresholdAnimation.isAnimating || edgeThresholdAnimation.animatedValue < 0.999 } - private fun randomVelocity(scale: Double): Vec3 { - return Vec3( - (Random.nextDouble() - 0.5) * scale, - Random.nextDouble() * scale, - (Random.nextDouble() - 0.5) * scale, - ) + private fun isRadialBlurActive(): Boolean { + return ghostStrengthAnimation.isAnimating || ghostStrengthAnimation.animatedValue > 0.001 } } diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/particle/system/ExplosionParticle.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/particle/system/ExplosionParticle.kt index 993521dc..8d6636c1 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/particle/system/ExplosionParticle.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/particle/system/ExplosionParticle.kt @@ -5,7 +5,8 @@ package heckerpowered.matrix.client.render.particle.system -import heckerpowered.matrix.client.minecraft +import heckerpowered.matrix.client.render.MatrixGraphicsBackend +import heckerpowered.matrix.client.render.MatrixPointSpriteParticles import heckerpowered.matrix.client.render.particle.ParticleSystem import heckerpowered.matrix.client.render.particle.memory.MemoryLayout import heckerpowered.matrix.client.render.particle.module.particle_render.ParticleSpriteRendererModule @@ -16,12 +17,12 @@ import heckerpowered.matrix.client.render.particle.module.particle_update.DragMo import heckerpowered.matrix.client.render.particle.module.particle_update.KillParticleModule import heckerpowered.matrix.client.render.particle.module.particle_update.ParticleStateModule import heckerpowered.matrix.client.render.particle.module.particle_update.ScaleSpriteSizeBySpeedModule -import net.minecraft.core.particles.ParticleTypes import net.minecraft.world.phys.Vec3 import org.joml.Vector3f -import kotlin.random.Random object ExplosionParticle { + private const val ENABLE_LEGACY_OPENGL_PARTICLES = false + val randomVelocityModule = RandomVelocityModule() init { @@ -50,8 +51,24 @@ object ExplosionParticle { } fun spawnParticleAt(position: Vec3) { - spawnVanillaFallback(position) + MatrixPointSpriteParticles.spawnExplosionParticles( + position, + randomVelocityModule.speedRange, + randomVelocityModule.multiplier, + ) + if (!ENABLE_LEGACY_OPENGL_PARTICLES || !MatrixGraphicsBackend.isOpenGl()) { + return + } + // The legacy GPU particle path is OpenGL transform-feedback based. It is + // kept for OpenGL only until the point-sprite renderer is fully rebuilt + // on Minecraft's cross-backend RenderPipeline API. + runCatching { + spawnLegacyGpuParticles(position) + } + } + + private fun spawnLegacyGpuParticles(position: Vec3) { val particleState = (particleSystem.particleSpawnModules.first { it is InitializeParticleModule } as InitializeParticleModule).particleState particleState.x = position.x.toFloat() particleState.y = position.y.toFloat() @@ -69,24 +86,4 @@ object ExplosionParticle { particleSystem.spawnParticles() } - - private fun spawnVanillaFallback(position: Vec3) { - val level = minecraft.level ?: return - repeat(80) { - val velocity = Vec3( - (Random.nextDouble() - 0.5) * 0.7, - Random.nextDouble() * 0.7, - (Random.nextDouble() - 0.5) * 0.7, - ) - level.addParticle( - ParticleTypes.END_ROD, - position.x, - position.y, - position.z, - velocity.x, - velocity.y, - velocity.z, - ) - } - } } diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/post/OverlayPostProcessEffectRenderer.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/post/OverlayPostProcessEffectRenderer.kt index 8a1b5ff9..8429bbc7 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/post/OverlayPostProcessEffectRenderer.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/post/OverlayPostProcessEffectRenderer.kt @@ -5,8 +5,44 @@ package heckerpowered.matrix.client.render.post +import heckerpowered.matrix.client.minecraft +import heckerpowered.matrix.client.render.PostProcessRenderer import net.minecraft.client.DeltaTracker +import java.time.Duration object OverlayPostProcessEffectRenderer { - fun render(tickCounter: DeltaTracker) = Unit + private val framebuffer = PostProcessRenderer.createManagedFramebuffer() + private val dissolveAnimation = heckerpowered.matrix.client.ui.foundation.animation.SimpleDoubleAnimation( + initValue = 1.0, + duration = Duration.ofMillis(1000), + ) + private var previousState = false + + fun render(tickCounter: DeltaTracker) { + if (minecraft.gui.screen() == null) { + dissolveAnimation.value = 1.0 + previousState = false + } else if (!previousState) { + previousState = true + dissolveAnimation.value = .0 + } + } + + @JvmStatic + fun beginRenderOverlay() { + framebuffer.clear(false) + } + + @JvmStatic + fun endRenderOverlay() { + TextureDissolveShader.dissolveFactor = dissolveAnimation.animatedValue.toFloat() + val output = PostProcessRenderer.currentFramebuffer() + PostProcessRenderer.renderShaderToFramebuffer( + TextureDissolveShader.program, + output, + mapOf("colorAttachment" to framebuffer), + ) + PostProcessRenderer.copyFramebuffer(output, PostProcessRenderer.sourceFramebuffer) + PostProcessRenderer.nextFramebuffer() + } } diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/post/TextureDissolveShader.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/post/TextureDissolveShader.kt index e37831f4..f446a767 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/post/TextureDissolveShader.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/post/TextureDissolveShader.kt @@ -6,7 +6,7 @@ package heckerpowered.matrix.client.render.post import heckerpowered.matrix.client.shader.DissolveShader -import heckerpowered.matrix.client.shader.Program +import heckerpowered.matrix.client.shader.BlitProgram import heckerpowered.matrix.client.shader.ResourceShader import heckerpowered.matrix.client.shader.UniformProvider import org.lwjgl.opengl.GL46.* @@ -15,7 +15,7 @@ object TextureDissolveShader { var colorAttachment: Int = 0 var dissolveFactor: Float = 0F - val program = Program( + val program = BlitProgram( ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), ResourceShader("/assets/matrix/shaders/post/dissolve/texture_dissolve.fsh", GL_FRAGMENT_SHADER), uniforms = arrayOf( @@ -34,4 +34,4 @@ object TextureDissolveShader { } ) ) -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VelocityMapRenderer.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VelocityMapRenderer.kt index 1321cf29..a6b4b10c 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VelocityMapRenderer.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VelocityMapRenderer.kt @@ -7,8 +7,14 @@ package heckerpowered.matrix.client.render.shader import heckerpowered.matrix.client.render.PostProcessRenderer import heckerpowered.matrix.client.shader.BlitProgram +import heckerpowered.matrix.client.shader.ResourceShader +import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER +import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER object VelocityMapRenderer { val velocityMap = PostProcessRenderer.createManagedFramebuffer() - val velocityMapShader = BlitProgram() + val velocityMapShader = BlitProgram( + ResourceShader("/assets/matrix/shaders/post/velocity_map/velocity_map.vsh", GL_VERTEX_SHADER), + ResourceShader("/assets/matrix/shaders/post/velocity_map/velocity_map.fsh", GL_FRAGMENT_SHADER), + ) } diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VolumeDistortion.kt b/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VolumeDistortion.kt index 4baebe25..6aebc465 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VolumeDistortion.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/render/shader/VolumeDistortion.kt @@ -5,13 +5,11 @@ package heckerpowered.matrix.client.render.shader -import heckerpowered.matrix.client.shader.* +import heckerpowered.matrix.client.shader.BlitProgram +import heckerpowered.matrix.client.shader.ResourceShader import org.joml.Vector3f -import org.lwjgl.opengl.GL11.GL_TEXTURE_2D -import org.lwjgl.opengl.GL11.glBindTexture -import org.lwjgl.opengl.GL13.GL_TEXTURE0 -import org.lwjgl.opengl.GL13.glActiveTexture -import org.lwjgl.opengl.GL20.* +import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER +import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER object VolumeDistortion { var sceneColorTexture: Int = 0 @@ -25,34 +23,6 @@ object VolumeDistortion { val Shader = BlitProgram( ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), - ResourceShader("/assets/matrix/shaders/volume_distortion.frag", GL_FRAGMENT_SHADER), - uniforms = arrayOf( - UniformProvider("sceneColorTexture") { pointer -> - glActiveTexture(GL_TEXTURE0) - glBindTexture(GL_TEXTURE_2D, sceneColorTexture) - glUniform1i(pointer, 0) - }, - UniformProvider("depthAttachment") { pointer -> - glActiveTexture(GL_TEXTURE1) - glBindTexture(GL_TEXTURE_2D, depthAttachment) - glUniform1i(pointer, 1) - }, - // projectionMatrixProvider, - // viewProjectionMatrixProvider, - inverseViewMatrixProvider, - inverseProjectionMatrixProvider, - UniformProvider("volumePosition") { pointer -> - glUniform3f(pointer, volumePosition.x, volumePosition.y, volumePosition.z) - }, - UniformProvider("volumeRadius") { pointer -> - glUniform1f(pointer, volumeRadius) - }, - UniformProvider("grayscaleIntensity") { pointer -> - glUniform1f(pointer, grayscaleIntensity) - }, - UniformProvider("emissiveStrength") { pointer -> - glUniform1f(pointer, emissiveStrength) - } - ) + ResourceShader("/assets/matrix/shaders/post/volume_distortion.fsh", GL_FRAGMENT_SHADER), ) -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/shader/BlurRenderer.kt b/common/src/main/kotlin/heckerpowered/matrix/client/shader/BlurRenderer.kt index b38ce3c1..e6b61eb4 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/shader/BlurRenderer.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/shader/BlurRenderer.kt @@ -6,7 +6,12 @@ package heckerpowered.matrix.client.shader import heckerpowered.matrix.client.render.PostProcessRenderer +import heckerpowered.matrix.client.render.post.ScaleSampling +import heckerpowered.matrix.client.render.shader.GaussianBlurRenderer import net.minecraft.client.gl.Framebuffer +import org.joml.Vector2f +import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER +import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER object BlurRenderer { var initialFramebuffer: Framebuffer = PostProcessRenderer.sourceFramebuffer @@ -20,11 +25,20 @@ object BlurRenderer { PostProcessRenderer.createManagedFramebuffer() } - val horizontalBlurShader = BlitProgram() - val verticalBlurShader = BlitProgram() - val kawaseBlurShader = BlitProgram() - val tentBlurShader = BlitProgram() - val blurTextureRenderProgram = Program() + val horizontalBlurShader = GaussianBlurRenderer.gaussianBlurShader + val verticalBlurShader = GaussianBlurRenderer.gaussianBlurShader + val kawaseBlurShader = BlitProgram( + ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), + ResourceShader("/assets/matrix/shaders/post/blur/kawase_blur.fsh", GL_FRAGMENT_SHADER), + ) + val tentBlurShader = BlitProgram( + ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), + ResourceShader("/assets/matrix/shaders/post/blur/tent.fsh", GL_FRAGMENT_SHADER), + ) + val blurTextureRenderProgram = BlitProgram( + ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), + ResourceShader("/assets/matrix/shaders/post/blur/blur_mask.fsh", GL_FRAGMENT_SHADER), + ) fun dumpFrameBuffer(framebuffer: Framebuffer) { } @@ -33,12 +47,49 @@ object BlurRenderer { } fun renderGaussianBlurFullResolution(source: Framebuffer = PostProcessRenderer.sourceFramebuffer) { + blurFramebuffer.clear(false) + GaussianBlurRenderer.fullPing.clear(false) + GaussianBlurRenderer.fullPong.clear(false) + + GaussianBlurRenderer.direction = Vector2f(1F, 0F) + PostProcessRenderer.renderShaderToFramebuffer(GaussianBlurRenderer.gaussianBlurShader, source, GaussianBlurRenderer.fullPing) + + GaussianBlurRenderer.direction = Vector2f(0F, 1F) + PostProcessRenderer.renderShaderToFramebuffer(GaussianBlurRenderer.gaussianBlurShader, GaussianBlurRenderer.fullPing, GaussianBlurRenderer.fullPong) + + PostProcessRenderer.copyFramebuffer(GaussianBlurRenderer.fullPong, blurFramebuffer) } fun renderGaussianBlur(source: Framebuffer = PostProcessRenderer.sourceFramebuffer, target: Framebuffer = blurFramebuffer) { + val halfFramebuffer = ScaleSampling.getDownScalingFramebuffer(0.5) + val quarterFramebuffer = ScaleSampling.getDownScalingFramebuffer(0.25) + + target.clear(false) + GaussianBlurRenderer.ping.clear(false) + GaussianBlurRenderer.pong.clear(false) + + ScaleSampling.sample(source, halfFramebuffer, ScaleSampling.bilinearSample) + ScaleSampling.sample(halfFramebuffer, quarterFramebuffer, ScaleSampling.bilinearSample) + + GaussianBlurRenderer.direction = Vector2f(1F, 0F) + PostProcessRenderer.renderShaderToFramebuffer(GaussianBlurRenderer.gaussianBlurShader, quarterFramebuffer, GaussianBlurRenderer.ping) + + GaussianBlurRenderer.direction = Vector2f(0F, 1F) + PostProcessRenderer.renderShaderToFramebuffer(GaussianBlurRenderer.gaussianBlurShader, GaussianBlurRenderer.ping, GaussianBlurRenderer.pong) + + ScaleSampling.sample(GaussianBlurRenderer.pong, halfFramebuffer, ScaleSampling.bilinearSample) + ScaleSampling.sample(halfFramebuffer, target, ScaleSampling.bilinearSample) } fun renderKawaseBlur() { + val halfFramebuffer = ScaleSampling.getDownScalingFramebuffer(0.5) + val quarterFramebuffer = ScaleSampling.getDownScalingFramebuffer(0.25) + ScaleSampling.sample(PostProcessRenderer.sourceFramebuffer, halfFramebuffer, ScaleSampling.bilinearSample) + ScaleSampling.sample(halfFramebuffer, quarterFramebuffer, ScaleSampling.bilinearSample) + kawaseOffset = .0F + PostProcessRenderer.useFramebuffer(quarterFramebuffer) { + PostProcessRenderer.renderShadersToFramebuffer(List(5) { kawaseBlurShader }, blurFramebuffer) + } } fun renderBlur() { diff --git a/common/src/main/kotlin/heckerpowered/matrix/client/shader/TexturePixelDissolveProgram.kt b/common/src/main/kotlin/heckerpowered/matrix/client/shader/TexturePixelDissolveProgram.kt index ac3a3e15..7132f082 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/client/shader/TexturePixelDissolveProgram.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/client/shader/TexturePixelDissolveProgram.kt @@ -5,7 +5,13 @@ package heckerpowered.matrix.client.shader -object TexturePixelDissolveProgram : BlitProgram() { +import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER +import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER + +object TexturePixelDissolveProgram : BlitProgram( + ResourceShader("/assets/matrix/shaders/sobel.vert", GL_VERTEX_SHADER), + ResourceShader("/assets/matrix/shaders/post/dissolve/texture_pixel_dissolve.fsh", GL_FRAGMENT_SHADER), +) { var noiseTextureId: Int = 0 var normalTextureId: Int = 0 var dissolveFactor: Float = 0.0F diff --git a/common/src/main/kotlin/heckerpowered/matrix/core/extension/LivingEntityExtension.kt b/common/src/main/kotlin/heckerpowered/matrix/core/extension/LivingEntityExtension.kt index 9b6e8797..f070c78d 100644 --- a/common/src/main/kotlin/heckerpowered/matrix/core/extension/LivingEntityExtension.kt +++ b/common/src/main/kotlin/heckerpowered/matrix/core/extension/LivingEntityExtension.kt @@ -16,7 +16,7 @@ import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.entity.player.Player val LivingEntity.attackDamage: Double - get() = getAttributeValue(Attributes.ATTACK_DAMAGE) + get() = getAttribute(Attributes.ATTACK_DAMAGE)?.value ?: 0.0 fun LivingEntity.healOverflow(amount: Float) { val actualAmount = amount * healingMultiplier.toFloat() diff --git a/common/src/main/kotlin/net/minecraft/client/renderer/rendertype/MatrixRenderTypes.kt b/common/src/main/kotlin/net/minecraft/client/renderer/rendertype/MatrixRenderTypes.kt new file mode 100644 index 00000000..8b8debec --- /dev/null +++ b/common/src/main/kotlin/net/minecraft/client/renderer/rendertype/MatrixRenderTypes.kt @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: MIT + * Copyright (c) 2026 heckerpowered + */ + +package net.minecraft.client.renderer.rendertype + +import com.mojang.blaze3d.pipeline.RenderPipeline + +object MatrixRenderTypes { + private var pointSprite: RenderType? = null + private var pointSpritePipeline: RenderPipeline? = null + + fun matrixPointSprite(pipeline: RenderPipeline): RenderType { + val current = pointSprite + if (current != null && pointSpritePipeline === pipeline) { + return current + } + + pointSpritePipeline = pipeline + return RenderType.create( + "matrix_point_sprite", + RenderSetup.builder(pipeline).createRenderSetup(), + ).also { + pointSprite = it + } + } +} diff --git a/common/src/main/resources/assets/matrix/shaders/blit/blit.fsh b/common/src/main/resources/assets/matrix/shaders/blit/blit.fsh index 50e8e7f1..63887f35 100644 --- a/common/src/main/resources/assets/matrix/shaders/blit/blit.fsh +++ b/common/src/main/resources/assets/matrix/shaders/blit/blit.fsh @@ -3,7 +3,7 @@ in vec2 fragTexCoord; uniform sampler2D framebuffer; -uniform float lod = .0; +uniform float lod = 0.0; uniform sampler2D depthAttachment; out vec4 fragColor; diff --git a/common/src/main/resources/assets/matrix/shaders/blit/blit_no_depth.fsh b/common/src/main/resources/assets/matrix/shaders/blit/blit_no_depth.fsh index 389fb722..699dfe60 100644 --- a/common/src/main/resources/assets/matrix/shaders/blit/blit_no_depth.fsh +++ b/common/src/main/resources/assets/matrix/shaders/blit/blit_no_depth.fsh @@ -3,7 +3,7 @@ in vec2 fragTexCoord; uniform sampler2D framebuffer; -uniform float lod = .0; +uniform float lod = 0.0; out vec4 fragColor; diff --git a/common/src/main/resources/assets/matrix/shaders/debug.fsh b/common/src/main/resources/assets/matrix/shaders/debug.fsh index 1e046ffb..7873f116 100644 --- a/common/src/main/resources/assets/matrix/shaders/debug.fsh +++ b/common/src/main/resources/assets/matrix/shaders/debug.fsh @@ -3,5 +3,5 @@ out vec4 fragColor; void main() { - fragColor = vec4(1.0, .0, .0, 1.0); + fragColor = vec4(1.0, 0.0, 0.0, 1.0); } \ No newline at end of file diff --git a/common/src/main/resources/assets/matrix/shaders/gaussian_blur_horizontal.fsh b/common/src/main/resources/assets/matrix/shaders/gaussian_blur_horizontal.fsh index 1f6c271c..c45887e4 100644 --- a/common/src/main/resources/assets/matrix/shaders/gaussian_blur_horizontal.fsh +++ b/common/src/main/resources/assets/matrix/shaders/gaussian_blur_horizontal.fsh @@ -12,8 +12,8 @@ void main() { vec3 result = texture(framebuffer, fragTexCoord).rgb * weight[0]; for (int i = 1; i < 5; ++i) { - result += texture(framebuffer, fragTexCoord + vec2(.0, tex_offset.y * i)).rgb * weight[i]; - result += texture(framebuffer, fragTexCoord - vec2(.0, tex_offset.y * i)).rgb * weight[i]; + result += texture(framebuffer, fragTexCoord + vec2(0.0, tex_offset.y * i)).rgb * weight[i]; + result += texture(framebuffer, fragTexCoord - vec2(0.0, tex_offset.y * i)).rgb * weight[i]; } fragColor = vec4(result, 1.0); diff --git a/common/src/main/resources/assets/matrix/shaders/gaussian_blur_vertical.fsh b/common/src/main/resources/assets/matrix/shaders/gaussian_blur_vertical.fsh index e7ea713b..100b985b 100644 --- a/common/src/main/resources/assets/matrix/shaders/gaussian_blur_vertical.fsh +++ b/common/src/main/resources/assets/matrix/shaders/gaussian_blur_vertical.fsh @@ -12,8 +12,8 @@ void main() { vec3 result = texture(framebuffer, fragTexCoord).rgb * weight[0]; for (int i = 1; i < 5; ++i) { - result += texture(framebuffer, fragTexCoord + vec2(tex_offset.x * i, .0)).rgb * weight[i]; - result += texture(framebuffer, fragTexCoord - vec2(tex_offset.x * i, .0)).rgb * weight[i]; + result += texture(framebuffer, fragTexCoord + vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; + result += texture(framebuffer, fragTexCoord - vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; } fragColor = vec4(result, 1.0); diff --git a/common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.fsh b/common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.fsh new file mode 100644 index 00000000..763b6efd --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.fsh @@ -0,0 +1,48 @@ +#version 330 + +#moj_import + +uniform sampler2D Sampler0; + +in vec2 texCoord0; +in vec4 vertexColor; +in vec3 dissolveParams; + +out vec4 fragColor; + +const float emissiveStrength = 15.0; +const float pixelStrength = 16.0; +const float detialStrength = 1.0; +const vec4 emissiveColor = vec4(0.1, 0.5, 1.0, 1.0); + +vec2 dissolveTexCoord() { + vec2 uv = texCoord0 - 0.5; + uv.y *= max(dissolveParams.y, 0.0001); + return uv + 0.5; +} + +float pixelColor() { + vec2 pixelTexCoord = dissolveTexCoord(); + float time = dissolveParams.z * 10.0; + return texture(Sampler0, pixelTexCoord + vec2(time * 0.1, time * 0.1)).b; +} + +float pixelAnimation() { + float pixelNoise = dissolveTexCoord().r; + return pixelNoise - mix(1.5, -1.5, dissolveParams.x); +} + +float border() { + vec4 normalColor = texture(Sampler0, ceil(dissolveTexCoord() * pixelStrength) / pixelStrength); + vec4 offsetColor = texture(Sampler0, ceil((dissolveTexCoord() + 0.01) * pixelStrength) / pixelStrength); + return (normalColor.b - offsetColor.b) * detialStrength; +} + +void main() { + float opacityMask = clamp((pixelColor() + border()) - pixelAnimation(), 0.0, 1.0); + + vec4 color = vertexColor; + color.a *= ceil(opacityMask); + color.rgb = pow(1.0 - opacityMask, 10.0) * (emissiveColor.rgb * emissiveStrength); + fragColor = color * ColorModulator; +} diff --git a/common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.vsh b/common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.vsh new file mode 100644 index 00000000..c28aa98d --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/gui/dissolve_rect.vsh @@ -0,0 +1,20 @@ +#version 330 + +#moj_import +#moj_import + +in vec3 Position; +in vec2 UV0; +in vec4 Color; +in vec3 Normal; + +out vec2 texCoord0; +out vec4 vertexColor; +out vec3 dissolveParams; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); + texCoord0 = UV0; + vertexColor = Color; + dissolveParams = Normal * 0.5 + 0.5; +} diff --git a/common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.fsh b/common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.fsh new file mode 100644 index 00000000..99135617 --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.fsh @@ -0,0 +1,17 @@ +#version 330 + +#moj_import + +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + vec2 coord = gl_PointCoord * 2.0 - 1.0; + float squaredDistance = dot(coord, coord); + if (squaredDistance > 1.0) { + discard; + } + + fragColor = vertexColor * ColorModulator; +} diff --git a/common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.vsh b/common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.vsh new file mode 100644 index 00000000..c8c47f92 --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/particle/particle_render/world_point_sprite.vsh @@ -0,0 +1,20 @@ +#version 330 + +#moj_import +#moj_import + +in vec3 Position; +in vec2 UV0; +in vec4 Color; + +out vec4 vertexColor; + +void main() { + vec4 viewPosition = ModelViewMat * vec4(Position, 1.0); + gl_Position = ProjMat * viewPosition; + + float distance = max(length(viewPosition.xyz), 0.01); + gl_PointSize = UV0.x / distance; + + vertexColor = Color * UV0.y; +} diff --git a/common/src/main/resources/assets/matrix/shaders/particle/particle_update/scale_sprite_size_by_speed.vsh b/common/src/main/resources/assets/matrix/shaders/particle/particle_update/scale_sprite_size_by_speed.vsh index 035c9cc7..7552532f 100644 --- a/common/src/main/resources/assets/matrix/shaders/particle/particle_update/scale_sprite_size_by_speed.vsh +++ b/common/src/main/resources/assets/matrix/shaders/particle/particle_update/scale_sprite_size_by_speed.vsh @@ -11,7 +11,7 @@ layout (location = 7) in vec4 InColor; layout (location = 8) in vec4 InOrientation; layout (location = 9) in vec3 InAngularVelocity; -uniform float MinScaleFactor = .0; +uniform float MinScaleFactor = 0.0; uniform float MaxScaleFactor = 2.0; uniform float VelocityThreshold = 1.0; diff --git a/common/src/main/resources/assets/matrix/shaders/point_sprite/point_sprite.vsh b/common/src/main/resources/assets/matrix/shaders/point_sprite/point_sprite.vsh index 08f392fc..a38dfd58 100644 --- a/common/src/main/resources/assets/matrix/shaders/point_sprite/point_sprite.vsh +++ b/common/src/main/resources/assets/matrix/shaders/point_sprite/point_sprite.vsh @@ -2,9 +2,14 @@ layout (location = 0) in vec2 Position; -uniform float time; +layout(std140) uniform MatrixPointSpriteUniforms { + vec4 pointSpriteParams; +}; + +#define time pointSpriteParams.x out vec2 OutPosition; +out vec4 InColor; vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; @@ -124,12 +129,13 @@ void main() vec3 windForce = vec3(0.01, 0.0, 0.0); float r = snoise3(vec3(Position * 3.0, time)) * 0.01; - vec3 velocity = curlNoise(position + vec3(.0, .0, time * 0.2)) * 0.001 + windForce; + vec3 velocity = curlNoise(position + vec3(0.0, 0.0, time * 0.2)) * 0.001 + windForce; OutPosition = (position + velocity).xy; if (OutPosition.x > 1 || OutPosition.y < -1 || OutPosition.y > 1){ OutPosition = vec2(-1.0, OutPosition.y); } - gl_Position = vec4(OutPosition, .0, 1.0); + gl_Position = vec4(OutPosition, 0.0, 1.0); gl_PointSize = 6.0; -} \ No newline at end of file + InColor = vec4(1.0, 0.5, 1.0, 1.0); +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/aura.fsh b/common/src/main/resources/assets/matrix/shaders/post/aura.fsh index f96b57d9..5321dcea 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/aura.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/aura.fsh @@ -7,9 +7,14 @@ uniform sampler2D entityColorAttachment; uniform sampler2D sceneDepthAttachment; uniform sampler2D sceneColorAttachment; uniform sampler2D noiseColorAttachment; -uniform float time; -uniform float alpha; -uniform vec4 auraColor = vec4(0, 0, 0, 0); + +layout(std140) uniform MatrixPostUniforms { + vec4 auraParams; + vec4 auraColor; +}; + +#define time auraParams.x +#define alpha auraParams.y out vec4 fragColor; @@ -23,7 +28,7 @@ void main() { float sceneDepth = texture(sceneDepthAttachment, fragTexCoord).r; vec4 sceneColor = texture(sceneColorAttachment, fragTexCoord); - if (entityColor.a <= .0 || (entityColor.r <= 0 && entityColor.g <= 0 && entityColor.b <= 0)) { + if (entityColor.a <= 0.0 || (entityColor.r <= 0 && entityColor.g <= 0 && entityColor.b <= 0)) { fragColor = sceneColor; return; } @@ -35,4 +40,4 @@ void main() { } else { fragColor = entityColor; } -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/bloom/bloom_brightness_pass.fsh b/common/src/main/resources/assets/matrix/shaders/post/bloom/bloom_brightness_pass.fsh index 80f59741..f2d56862 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/bloom/bloom_brightness_pass.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/bloom/bloom_brightness_pass.fsh @@ -9,8 +9,8 @@ layout(std140) uniform MatrixPostUniforms { vec4 MatrixPostData3; }; -#define threshold MatrixPostData0.x -#define intensity MatrixPostData0.y +#define bloomThreshold MatrixPostData0.x +#define bloomIntensity MatrixPostData0.y in vec2 fragTexCoord;// Texture coordinates from vertex shader @@ -46,14 +46,14 @@ void main() { // Determine the factor based on the threshold // step(edge, x) returns 0.0 if x < edge, and 1.0 if x >= edge - float factor = step(threshold, brightness); + float factor = step(bloomThreshold, brightness); // Output the final color: // If brightness is above threshold (factor = 1.0), keep the original color. // If brightness is below threshold (factor = 0.0), output black. - if (brightness < threshold) { - fragColor = vec4(0.0, 0.0, 0.0, .0); + if (brightness < bloomThreshold) { + fragColor = vec4(0.0, 0.0, 0.0, 0.0); } else { - fragColor = vec4(extractBloomSoft(color.rgb, threshold, 1.0), color.a); + fragColor = vec4(extractBloomSoft(color.rgb, bloomThreshold, 1.0), color.a); } } diff --git a/common/src/main/resources/assets/matrix/shaders/post/blur/blur_mask.fsh b/common/src/main/resources/assets/matrix/shaders/post/blur/blur_mask.fsh new file mode 100644 index 00000000..60e1fd8b --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/post/blur/blur_mask.fsh @@ -0,0 +1,14 @@ +#version 330 core + +in vec2 fragTexCoord; + +uniform sampler2D framebuffer; + +out vec4 fragColor; + +void main() { + fragColor = texture(framebuffer, fragTexCoord); + if (fragColor.a < 0.1) { + discard; + } +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/circle.fsh b/common/src/main/resources/assets/matrix/shaders/post/circle.fsh index 91b15a23..a609eb6b 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/circle.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/circle.fsh @@ -2,13 +2,19 @@ in vec2 fragTexCoord; -uniform mat4 modelViewMatrix; -uniform mat4 projectionMatrix; uniform sampler2D framebuffer; -uniform vec3 center = vec3(600.0); -uniform float radius = 300; -uniform vec2 resolution; // 必须正确设置为屏幕分辨率 -uniform float grayscaleIntensity = 1.0; + +layout(std140) uniform MatrixPostUniforms { + mat4 modelViewMatrix; + mat4 projectionMatrix; + vec4 circleParams0; + vec4 circleParams1; +}; + +#define center circleParams0.xyz +#define radius circleParams0.w +#define resolution circleParams1.xy +#define grayscaleIntensity circleParams1.z out vec4 fragColor; @@ -39,4 +45,4 @@ void main() { vec3 color = mix(framebufferColor.rgb, vec3(grayscale), grayscaleIntensity); fragColor = vec4(color, framebufferColor.a); } -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/dissolve/dissolve.fsh b/common/src/main/resources/assets/matrix/shaders/post/dissolve/dissolve.fsh index f7338b0b..12a51f37 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/dissolve/dissolve.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/dissolve/dissolve.fsh @@ -1,14 +1,21 @@ #version 410 core uniform sampler2D noiseTexture; -uniform float dissolveFactor = 0.5; -uniform float emissiveRange = 0.05; -uniform vec4 emissiveColor = vec4(0.1, 0.5, 1.0, 1.0); -uniform float emissiveStrength = 15.0; -uniform float pixelStrength = 16.0; -uniform float detialStrength = 1; -uniform float time; -uniform vec2 resolution = vec2(1.0, 1.0); + +layout(std140) uniform MatrixPostUniforms { + vec4 dissolveParams0; + vec4 dissolveParams1; + vec4 dissolveEmissiveColor; +}; + +#define dissolveFactor dissolveParams0.x +#define emissiveRange dissolveParams0.y +#define emissiveStrength dissolveParams0.z +#define pixelStrength dissolveParams0.w +#define detialStrength dissolveParams1.x +#define time dissolveParams1.y +#define resolution dissolveParams1.zw +#define emissiveColor dissolveEmissiveColor layout (location = 0) out vec4 fragColor; @@ -37,14 +44,14 @@ float border() { return (normalColor.b - offsetColor.b) * detialStrength; } -float clamp(float minValue, float maxValue, float value) { +float clampRange(float minValue, float maxValue, float value) { return min(max(value, minValue), maxValue); } void main() { fragColor = vertexColor; - float opacityMask = clamp(0, 1, pixelColor() - pixelAnimation()); + float opacityMask = clampRange(0.0, 1.0, pixelColor() - pixelAnimation()); fragColor.a *= ceil(opacityMask); fragColor.rgb = pow(1 - opacityMask, 10) * (emissiveColor.rgb * emissiveStrength); -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_dissolve.fsh b/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_dissolve.fsh index 25dd99e7..29ced1cc 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_dissolve.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_dissolve.fsh @@ -4,11 +4,17 @@ layout (location = 0) in vec2 fragTexCoord; uniform sampler2D noiseTexture; uniform sampler2D colorAttachment; -uniform float dissolveFactor = 0.5; -uniform float emissiveRange = 0.05; -uniform vec4 emissiveColor = vec4(0, 0.5, 1.0, 1.0); -uniform float pixelStrength = 16.0; -uniform float detialStrength = 1; + +layout(std140) uniform MatrixPostUniforms { + vec4 dissolveParams0; + vec4 dissolveEmissiveColor; +}; + +#define dissolveFactor dissolveParams0.x +#define emissiveRange dissolveParams0.y +#define pixelStrength dissolveParams0.z +#define detialStrength dissolveParams0.w +#define emissiveColor dissolveEmissiveColor out vec4 fragColor; @@ -32,14 +38,14 @@ float border() { return (normalColor.b - offsetColor.b) * detialStrength; } -float clamp(float minValue, float maxValue, float value) { +float clampRange(float minValue, float maxValue, float value) { return min(max(value, minValue), maxValue); } void main() { fragColor = texture(colorAttachment, texCoord()); - float opacityMask = clamp(0, 1, (pixelColor() + border()) - pixelAnimation()); + float opacityMask = clampRange(0.0, 1.0, (pixelColor() + border()) - pixelAnimation()); fragColor.a *= ceil(opacityMask); fragColor.rgb = pow(1 - opacityMask, 10) * emissiveColor.rgb; -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_pixel_dissolve.fsh b/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_pixel_dissolve.fsh new file mode 100644 index 00000000..89e80775 --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/post/dissolve/texture_pixel_dissolve.fsh @@ -0,0 +1,54 @@ +#version 330 core + +in vec2 fragTexCoord; + +uniform sampler2D noiseTexture; +uniform sampler2D normalTexture; + +layout(std140) uniform MatrixPostUniforms { + vec4 dissolveParams0; + vec4 dissolveEmissiveColor; +}; + +#define dissolveFactor dissolveParams0.x +#define emissiveRange dissolveParams0.y +#define pixelStrength dissolveParams0.z +#define detialStrength dissolveParams0.w +#define emissiveColor dissolveEmissiveColor + +out vec4 fragColor; + +vec2 texCoord() { + return fragTexCoord; +} + +float pixelColor() { + vec2 pixelTexCoord = ceil(texCoord() * pixelStrength) / pixelStrength; + return texture(noiseTexture, pixelTexCoord).b; +} + +float pixelAnimation() { + float pixelNoise = ceil(texCoord().x * pixelStrength) / pixelStrength; + return pixelNoise - mix(-1.5, 1.5, 1.0 - dissolveFactor); +} + +float border() { + vec4 normalColor = texture(noiseTexture, ceil(texCoord() * pixelStrength) / pixelStrength); + vec4 offsetColor = texture(noiseTexture, ceil((texCoord() + 0.01) * pixelStrength) / pixelStrength); + return (normalColor.b - offsetColor.b) * detialStrength; +} + +float clampRange(float minValue, float maxValue, float value) { + return min(max(value, minValue), maxValue); +} + +void main() { + fragColor = texture(normalTexture, fragTexCoord); + if (fragColor.a < 0.1) { + discard; + } + + float opacityMask = clampRange(0.0, 1.0, (pixelColor() + border()) - pixelAnimation()); + fragColor.a *= ceil(opacityMask); + fragColor.rgb = pow(1.0 - opacityMask, 10.0) * emissiveColor.rgb; +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/grain/background_grain.fsh b/common/src/main/resources/assets/matrix/shaders/post/grain/background_grain.fsh index b421771f..8567ff90 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/grain/background_grain.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/grain/background_grain.fsh @@ -4,14 +4,19 @@ in vec2 fragTexCoord; uniform sampler2D opacityMask; uniform sampler2D noiseTexture; -uniform float grainStrength = 0.05; + +layout(std140) uniform MatrixPostUniforms { + vec4 grainParams; +}; + +#define grainStrength grainParams.x out vec4 fragColor; void main() { vec4 opacity = texture(opacityMask, fragTexCoord); - if (opacity.a == .0) { + if (opacity.a == 0.0) { discard; } fragColor = texture(noiseTexture, fragTexCoord) * grainStrength; -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/hud/progress_ring.fsh b/common/src/main/resources/assets/matrix/shaders/post/hud/progress_ring.fsh index 585f8c56..ed507020 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/hud/progress_ring.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/hud/progress_ring.fsh @@ -38,7 +38,7 @@ void main() { float end = start - sweep; bool inRange; - if (end < .0) { + if (end < 0.0) { inRange = angle <= start && angle >= 0.0 || angle >= end + 2.0 * PI && angle <= 2.0 * PI; } else { inRange = angle <= start && angle >= end; diff --git a/common/src/main/resources/assets/matrix/shaders/post/opacity_blend.fsh b/common/src/main/resources/assets/matrix/shaders/post/opacity_blend.fsh index 33f29624..5e1cd47c 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/opacity_blend.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/opacity_blend.fsh @@ -9,7 +9,7 @@ out vec4 fragColor; void main() { vec4 opacity = texture(opacityMask, fragTexCoord); - if (opacity.a == .0) { + if (opacity.a == 0.0) { discard; } fragColor = texture(colorAttachment, fragTexCoord); diff --git a/common/src/main/resources/assets/matrix/shaders/post/opacity_mask.fsh b/common/src/main/resources/assets/matrix/shaders/post/opacity_mask.fsh index 33f29624..5e1cd47c 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/opacity_mask.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/opacity_mask.fsh @@ -9,7 +9,7 @@ out vec4 fragColor; void main() { vec4 opacity = texture(opacityMask, fragTexCoord); - if (opacity.a == .0) { + if (opacity.a == 0.0) { discard; } fragColor = texture(colorAttachment, fragTexCoord); diff --git a/common/src/main/resources/assets/matrix/shaders/post/refraction/refraction.fsh b/common/src/main/resources/assets/matrix/shaders/post/refraction/refraction.fsh index 11e805d4..03342cd9 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/refraction/refraction.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/refraction/refraction.fsh @@ -2,6 +2,10 @@ in vec2 fragTexCoord; +uniform sampler2D framebuffer; +out vec4 fragColor; -out vec4 fragColor; \ No newline at end of file +void main() { + fragColor = texture(framebuffer, fragTexCoord); +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/sdf/sdf_eval.fsh b/common/src/main/resources/assets/matrix/shaders/post/sdf/sdf_eval.fsh index 85c1bd4a..c40e74d5 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/sdf/sdf_eval.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/sdf/sdf_eval.fsh @@ -13,7 +13,7 @@ void main() { float maxDistance = length(resolution); float dist = length((fragTexCoord - nearest) * resolution) / maxDistance; vec4 color = texture(originFramebuffer, fragTexCoord); - if (color.a != .0) { + if (color.a != 0.0) { dist = -dist; } diff --git a/common/src/main/resources/assets/matrix/shaders/post/sdf/seed_gen.fsh b/common/src/main/resources/assets/matrix/shaders/post/sdf/seed_gen.fsh index b6b5c1ad..34350a91 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/sdf/seed_gen.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/sdf/seed_gen.fsh @@ -8,7 +8,7 @@ out vec4 fragColor; void main() { vec4 color = texture(framebuffer, fragTexCoord); - if (color.a != .0) { + if (color.a != 0.0) { fragColor = vec4(fragTexCoord, 0.0, 1.0); } else { fragColor = vec4(-1.0, -1.0, 0.0, 0.0); diff --git a/common/src/main/resources/assets/matrix/shaders/post/velocity_map/velocity_map.fsh b/common/src/main/resources/assets/matrix/shaders/post/velocity_map/velocity_map.fsh index 28e76915..46b4a3ea 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/velocity_map/velocity_map.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/velocity_map/velocity_map.fsh @@ -9,5 +9,5 @@ void main() { vec2 currentNDC = currentClipPosition.xy / currentClipPosition.w; vec2 previousNDC = previousClipPosition.xy / previousClipPosition.w; vec2 velocity = currentNDC - previousNDC; - fragColor = vec4(velocity, .0, 1.0); + fragColor = vec4(velocity, 0.0, 1.0); } \ No newline at end of file diff --git a/common/src/main/resources/assets/matrix/shaders/post/volume_distortion.fsh b/common/src/main/resources/assets/matrix/shaders/post/volume_distortion.fsh new file mode 100644 index 00000000..de90c75f --- /dev/null +++ b/common/src/main/resources/assets/matrix/shaders/post/volume_distortion.fsh @@ -0,0 +1,150 @@ +#version 330 core + +in vec2 fragTexCoord; + +uniform sampler2D sceneColorTexture; +uniform sampler2D depthAttachment; + +layout(std140) uniform MatrixPostUniforms { + mat4 inverseViewMatrix; + mat4 inverseProjectionMatrix; + vec4 volumeParams0; + vec4 volumeParams1; +}; + +#define volumePosition volumeParams0.xyz +#define volumeRadius volumeParams0.w +#define grayscaleIntensity volumeParams1.x +#define emissiveStrength volumeParams1.y + +out vec4 fragColor; + +vec3 computeCameraWorldPosition() +{ + return inverseViewMatrix[3].xyz; +} + +vec3 worldAbsolutePosition(vec2 uv, float depth) +{ + vec4 clipSpacePosition = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); + vec4 viewSpacePosition = inverseProjectionMatrix * clipSpacePosition; + vec3 nonHomogeneousViewSpacePosition = viewSpacePosition.xyz / viewSpacePosition.w; + vec4 worldSpacePosition = inverseViewMatrix * vec4(nonHomogeneousViewSpacePosition, 1.0); + return worldSpacePosition.xyz / worldSpacePosition.w; +} + +vec3 computeViewRayDirectionWorld(vec2 uv) +{ + float depth = texture(depthAttachment, uv).r; + vec3 worldPosition = worldAbsolutePosition(uv, depth); + return normalize(worldPosition - computeCameraWorldPosition()); +} + +bool computeRaySphereIntersectionInterval( + vec3 rayOriginWorldPosition, + vec3 rayDirectionWorld, + vec3 sphereCenterWorldPosition, + float sphereRadiusWorldUnits, + out float enterDistanceWorldUnits, + out float exitDistanceWorldUnits +) { + vec3 originToSphereCenter = rayOriginWorldPosition - sphereCenterWorldPosition; + float projectionLength = dot(originToSphereCenter, rayDirectionWorld); + float originToCenterDistanceSquared = dot(originToSphereCenter, originToSphereCenter); + float discriminant = projectionLength * projectionLength - (originToCenterDistanceSquared - sphereRadiusWorldUnits * sphereRadiusWorldUnits); + if (discriminant < 0.0) { + return false; + } + float sqrtDiscriminant = sqrt(discriminant); + float firstIntersection = -projectionLength - sqrtDiscriminant; + float secondIntersection = -projectionLength + sqrtDiscriminant; + enterDistanceWorldUnits = min(firstIntersection, secondIntersection); + exitDistanceWorldUnits = max(firstIntersection, secondIntersection); + return exitDistanceWorldUnits >= 0.0; +} + +float computeSceneDepthDistanceWorldUnits(vec2 uv, vec3 cameraPosition) +{ + float depth = texture(depthAttachment, uv).r; + vec3 sceneWorldPosition = worldAbsolutePosition(uv, depth); + return length(sceneWorldPosition - cameraPosition); +} + +vec3 computeGrayscaleColor(vec3 colorRgb) +{ + float grayscale = dot(colorRgb, vec3(0.299, 0.587, 0.114)); + return vec3(grayscale); +} + +vec3 remapColorToFixedBrightness(vec3 colorRgb, float targetBrightness) +{ + float currentBrightness = dot(colorRgb, vec3(0.2126, 0.7152, 0.0722)); + float safeBrightness = max(currentBrightness, 1e-4); + float brightnessScale = targetBrightness / safeBrightness; + return colorRgb * brightnessScale; +} + +float computeSphereEdgeGlowFactorFromSilhouette(float silhouetteSignedDistanceWorldUnits, float edgeWidthWorldUnits) +{ + return 1.0 - smoothstep(0.0, edgeWidthWorldUnits, -silhouetteSignedDistanceWorldUnits); +} + +float computeSphereSilhouetteSignedDistanceWorldUnits( + vec3 cameraWorldPosition, + vec3 viewRayDirectionWorld, + vec3 sphereCenterWorldPosition, + float sphereRadiusWorldUnits +) +{ + vec3 cameraToSphereCenter = sphereCenterWorldPosition - cameraWorldPosition; + float cameraToCenterDistance = length(cameraToSphereCenter); + vec3 cameraToCenterDirection = cameraToSphereCenter / cameraToCenterDistance; + + float perpendicularDistanceToRay = length(cross(viewRayDirectionWorld, cameraToCenterDirection)) * cameraToCenterDistance; + return perpendicularDistanceToRay - sphereRadiusWorldUnits; +} + +void main() +{ + vec3 cameraPosition = computeCameraWorldPosition(); + vec3 viewRayDirection = computeViewRayDirectionWorld(fragTexCoord); + + float enterDistance; + float exitDistance; + + bool intersectsSphere = computeRaySphereIntersectionInterval( + cameraPosition, + viewRayDirection, + volumePosition, + volumeRadius, + enterDistance, + exitDistance + ); + + float sceneDepthDistance = computeSceneDepthDistanceWorldUnits(fragTexCoord, cameraPosition); + float visibleMask = intersectsSphere && exitDistance > 0.0 && enterDistance < sceneDepthDistance ? 1.0 : 0.0; + float visibleEnterDistance = max(enterDistance, 0.0); + float travelDistanceWorldUnits = max(0.0, exitDistance - visibleEnterDistance); + float volumeDensity = (1.0 - exp(-travelDistanceWorldUnits * 0.6)) * visibleMask; + + vec4 originalColor = texture(sceneColorTexture, fragTexCoord); + + float silhouetteSignedDistanceWorldUnits = computeSphereSilhouetteSignedDistanceWorldUnits( + cameraPosition, + viewRayDirection, + volumePosition, + volumeRadius + ); + + float edgeGlowFactor = computeSphereEdgeGlowFactorFromSilhouette( + silhouetteSignedDistanceWorldUnits, + volumeRadius * 0.01 + ) * visibleMask; + vec3 edgeEnhancedColor = remapColorToFixedBrightness(originalColor.rgb, edgeGlowFactor * emissiveStrength); + + vec3 grayscaleColor = computeGrayscaleColor(originalColor.rgb); + vec3 colorWithInnerGrayscale = mix(originalColor.rgb, grayscaleColor, volumeDensity * grayscaleIntensity); + vec3 finalColorRgb = mix(colorWithInnerGrayscale, edgeEnhancedColor, edgeGlowFactor); + + fragColor = vec4(finalColorRgb, originalColor.a); +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/vortex/inverse_vortex.fsh b/common/src/main/resources/assets/matrix/shaders/post/vortex/inverse_vortex.fsh index fb70823b..b1286344 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/vortex/inverse_vortex.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/vortex/inverse_vortex.fsh @@ -3,10 +3,15 @@ in vec2 fragTexCoord; uniform sampler2D noiseTexture; -uniform float time; -uniform float innerRadius = 0.8; -uniform float outerRadius = 0.9; -uniform float feather = 0.02; + +layout(std140) uniform MatrixPostUniforms { + vec4 vortexParams; +}; + +#define time vortexParams.x +#define innerRadius vortexParams.y +#define outerRadius vortexParams.z +#define feather vortexParams.w out vec4 fragColor; @@ -88,8 +93,8 @@ void main() { float ringMask = smoothstep(gradientInner - feather, gradientInner + feather, blendedTex.r) - smoothstep(gradientOuter - feather, gradientOuter + feather, blendedTex.r); float opacityMask = radialGradientExponential(fragTexCoord, vec2(0.5), 0.4, 1.0, false); float alpha = 1.0 - step(blendedTex.r, gradientInner) + ringMask * opacityMask; - if (alpha == .0) { + if (alpha == 0.0) { discard; } fragColor = vec4(ringMask, ringMask, ringMask, 1.0); -} \ No newline at end of file +} diff --git a/common/src/main/resources/assets/matrix/shaders/post/vortex/vortex.fsh b/common/src/main/resources/assets/matrix/shaders/post/vortex/vortex.fsh index 6d12e00b..74bb7401 100644 --- a/common/src/main/resources/assets/matrix/shaders/post/vortex/vortex.fsh +++ b/common/src/main/resources/assets/matrix/shaders/post/vortex/vortex.fsh @@ -3,9 +3,14 @@ in vec2 fragTexCoord; uniform sampler2D noiseTexture; -uniform float time; -uniform float innerRadius = 0.4; -uniform float outerRadius = 0.5; + +layout(std140) uniform MatrixPostUniforms { + vec4 vortexParams; +}; + +#define time vortexParams.x +#define innerRadius vortexParams.y +#define outerRadius vortexParams.z out vec4 fragColor; @@ -87,8 +92,8 @@ void main() { float ringMask = step(blendedTex.r, gradientOuter) - step(blendedTex.r, gradientInner); float opacityMask = radialGradientExponential(fragTexCoord, vec2(0.5), 0.4, 1.0, false); float alpha = step(blendedTex.r, gradientInner) + ringMask * opacityMask; - if (alpha == .0) { + if (alpha == 0.0) { discard; } fragColor = vec4(ringMask, ringMask, ringMask, 1.0); -} \ No newline at end of file +} diff --git a/common/src/main/resources/matrix.mixins.json b/common/src/main/resources/matrix.mixins.json index b6232927..f3b0606b 100644 --- a/common/src/main/resources/matrix.mixins.json +++ b/common/src/main/resources/matrix.mixins.json @@ -1,7 +1,7 @@ { "required": true, "package": "heckerpowered.matrix.mixin", - "compatibilityLevel": "JAVA_21", + "compatibilityLevel": "JAVA_25", "mixins": [ "DamageSourceMixin", "EntityMixin", @@ -14,6 +14,7 @@ "DeltaTrackerTimerMixin", "GameRendererStandaloneMixin", "KeyboardMixin", + "LevelRendererStandaloneMixin", "MinecraftMixin", "MixinInGameHud" ],