diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java index 8dc46f6e9..ae785fe7f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java @@ -11,6 +11,7 @@ import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.backend.compile.component.StructInstanceComponent; import com.jozufozu.flywheel.backend.compile.core.CompilationHarness; import com.jozufozu.flywheel.backend.compile.core.Compile; +import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; import com.jozufozu.flywheel.backend.gl.GlCompat; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; @@ -81,7 +82,13 @@ public class IndirectPrograms extends AtomicReferenceCounted { .withComponent(StructInstanceComponent::create) .withResource(InstanceType::cullShader) .withResource(CULL_SHADER_MAIN)) - .postLink((key, program) -> program.setUniformBlockBinding("_FlwFrameUniforms", 0)) + .postLink((key, program) -> { + program.setUniformBlockBinding("_FlwFrameUniforms", Uniforms.FRAME_INDEX); + program.setUniformBlockBinding("_FlwFogUniforms", Uniforms.FOG_INDEX); + program.setUniformBlockBinding("_FlwOptionsUniforms", Uniforms.OPTIONS_INDEX); + program.setUniformBlockBinding("_FlwPlayerUniforms", Uniforms.PLAYER_INDEX); + program.setUniformBlockBinding("_FlwLevelUniforms", Uniforms.LEVEL_INDEX); + }) .harness("culling", sources); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java index 5a4cbdfe6..4dad59e8e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java @@ -6,6 +6,7 @@ import com.jozufozu.flywheel.backend.InternalVertex; import com.jozufozu.flywheel.backend.Samplers; import com.jozufozu.flywheel.backend.compile.core.CompilationHarness; import com.jozufozu.flywheel.backend.compile.core.Compile; +import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.glsl.ShaderSources; @@ -57,8 +58,11 @@ public class PipelineCompiler { program.bindAttribLocation("_flw_a_normal", 5); }) .postLink((key, program) -> { - program.setUniformBlockBinding("_FlwFrameUniforms", 0); - program.setUniformBlockBinding("_FlwFogUniforms", 1); + program.setUniformBlockBinding("_FlwFrameUniforms", Uniforms.FRAME_INDEX); + program.setUniformBlockBinding("_FlwFogUniforms", Uniforms.FOG_INDEX); + program.setUniformBlockBinding("_FlwOptionsUniforms", Uniforms.OPTIONS_INDEX); + program.setUniformBlockBinding("_FlwPlayerUniforms", Uniforms.PLAYER_INDEX); + program.setUniformBlockBinding("_FlwLevelUniforms", Uniforms.LEVEL_INDEX); program.bind(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java index 2f334f02e..0b784ceec 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/FrameUniforms.java @@ -2,26 +2,47 @@ package com.jozufozu.flywheel.backend.engine.uniform; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; +import org.joml.Vector2f; +import org.joml.Vector3f; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.visualization.VisualizationManager; +import com.jozufozu.flywheel.backend.mixin.GameRendererAccessor; import com.jozufozu.flywheel.lib.math.MatrixMath; +import com.mojang.blaze3d.vertex.PoseStack; + import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; +import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; public class FrameUniforms implements UniformProvider { - public static final int SIZE = 304; + public static final int SIZE = 1188; public int debugMode; - @Nullable + @Nullable private RenderContext context; + private final Matrix4f view = new Matrix4f(); + private final Matrix4f viewInverse = new Matrix4f(); + private final Matrix4f viewPrev = new Matrix4f(); + private final Matrix4f projection = new Matrix4f(); + private final Matrix4f projectionInverse = new Matrix4f(); + private final Matrix4f projectionPrev = new Matrix4f(); private final Matrix4f viewProjection = new Matrix4f(); private final Matrix4f viewProjectionInverse = new Matrix4f(); + private final Matrix4f viewProjectionPrev = new Matrix4f(); + + private final Vector3f cameraPositionPrev = new Vector3f(); + private final Vector3f cameraLookPrev = new Vector3f(); + private final Vector2f cameraRotPrev = new Vector2f(); + + private boolean lastInit = false; public int byteSize() { return SIZE; @@ -46,6 +67,9 @@ public class FrameUniforms implements UniformProvider { var camY = (float) (cameraPos.y - renderOrigin.getY()); var camZ = (float) (cameraPos.z - renderOrigin.getZ()); + view.set(context.stack().last().pose()); + view.translate(-camX, -camY, -camZ); + projection.set(context.projection()); viewProjection.set(context.viewProjection()); viewProjection.translate(-camX, -camY, -camZ); @@ -56,39 +80,76 @@ public class FrameUniforms implements UniformProvider { ptr += 96; + // manage last values of matrices + if (!lastInit) { + viewPrev.set(view); + projectionPrev.set(projection); + viewProjectionPrev.set(viewProjectionPrev); + } ptr = writeMatrices(ptr); + viewPrev.set(view); + projectionPrev.set(projection); + viewProjectionPrev.set(viewProjection); - ptr = writeCamera(ptr, camX, camY, camZ, camera); + // last values for camera + if (!lastInit) { + cameraPositionPrev.set(camX, camY, camZ); + cameraLookPrev.set(camera.getLookVector()); + cameraRotPrev.set(camera.getXRot(), camera.getYRot()); + } + ptr = writeCamera(ptr, camX, camY, camZ); + cameraPositionPrev.set(camX, camY, camZ); + cameraLookPrev.set(camera.getLookVector()); + cameraRotPrev.set(camera.getXRot(), camera.getYRot()); var window = Minecraft.getInstance() .getWindow(); - ptr = writeVec2(ptr, window.getWidth(), window.getHeight()); + ptr = Uniforms.writeVec2(ptr, window.getWidth(), window.getHeight()); // default line width: net.minecraft.client.renderer.RenderStateShard.LineStateShard MemoryUtil.memPutFloat(ptr, Math.max(2.5F, (float) window.getWidth() / 1920.0F * 2.5F)); ptr += 4; - MemoryUtil.memPutInt(ptr, getConstantAmbientLightFlag(context)); + MemoryUtil.memPutFloat(ptr, (float) window.getWidth() / (float) window.getHeight()); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, Minecraft.getInstance().gameRenderer.getDepthFar()); ptr += 4; ptr = writeTime(ptr); + ptr = writeCameraIn(ptr); + MemoryUtil.memPutInt(ptr, debugMode); + + lastInit = true; } private long writeMatrices(long ptr) { - MatrixMath.writeUnsafe(viewProjection, ptr); - MatrixMath.writeUnsafe(viewProjection.invert(viewProjectionInverse), ptr + 64); - return ptr + 128; + MatrixMath.writeUnsafe(view, ptr); + MatrixMath.writeUnsafe(view.invert(viewInverse), ptr + 64); + MatrixMath.writeUnsafe(viewPrev, ptr + 64 * 2); + MatrixMath.writeUnsafe(projection, ptr + 64 * 3); + MatrixMath.writeUnsafe(projection.invert(projectionInverse), ptr + 64 * 4); + MatrixMath.writeUnsafe(projectionPrev, ptr + 64 * 5); + MatrixMath.writeUnsafe(viewProjection, ptr + 64 * 6); + MatrixMath.writeUnsafe(viewProjection.invert(viewProjectionInverse), ptr + 64 * 7); + MatrixMath.writeUnsafe(viewProjectionPrev, ptr + 64 * 8); + return ptr + 64 * 15; } - private static long writeCamera(long ptr, float camX, float camY, float camZ, Camera camera) { - ptr = writeVec3(ptr, camX, camY, camZ); + private long writeCamera(long ptr, float camX, float camY, float camZ) { + Camera camera = context.camera(); + Vector3f lookVector = camera.getLookVector(); - var lookVector = camera.getLookVector(); - ptr = writeVec3(ptr, lookVector.x, lookVector.y, lookVector.z); + ptr = Uniforms.writeVec3(ptr, camX, camY, camZ); + ptr = Uniforms.writeVec3(ptr, cameraPositionPrev.x, cameraPositionPrev.y, cameraPositionPrev.z); - ptr = writeVec2(ptr, camera.getXRot(), camera.getYRot()); + ptr = Uniforms.writeVec3(ptr, lookVector.x, lookVector.y, lookVector.z); + ptr = Uniforms.writeVec3(ptr, cameraLookPrev.x, cameraLookPrev.y, cameraLookPrev.z); + + ptr = Uniforms.writeVec2(ptr, camera.getXRot(), camera.getYRot()); + ptr = Uniforms.writeVec2(ptr, cameraRotPrev.x, cameraRotPrev.y); return ptr; } @@ -106,24 +167,16 @@ public class FrameUniforms implements UniformProvider { return ptr + 16; } - private static long writeVec3(long ptr, float camX, float camY, float camZ) { - MemoryUtil.memPutFloat(ptr, camX); - MemoryUtil.memPutFloat(ptr + 4, camY); - MemoryUtil.memPutFloat(ptr + 8, camZ); - MemoryUtil.memPutFloat(ptr + 12, 0f); // empty component of vec4 because we don't trust std140 - return ptr + 16; - } - - private static long writeVec2(long ptr, float camX, float camY) { - MemoryUtil.memPutFloat(ptr, camX); - MemoryUtil.memPutFloat(ptr + 4, camY); - return ptr + 8; - } - - private static int getConstantAmbientLightFlag(RenderContext context) { - var constantAmbientLight = context.level() - .effects() - .constantAmbientLight(); - return constantAmbientLight ? 1 : 0; + private long writeCameraIn(long ptr) { + Camera camera = context.camera(); + if (!camera.isInitialized()) { + MemoryUtil.memPutInt(ptr, 0); + MemoryUtil.memPutInt(ptr + 4, 0); + return ptr + 8; + } + Level level = camera.getEntity().level(); + BlockPos blockPos = camera.getBlockPosition(); + Vec3 cameraPos = camera.getPosition(); + return Uniforms.writeInFluidAndBlock(ptr, level, blockPos, cameraPos); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/LevelUniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/LevelUniforms.java new file mode 100644 index 000000000..d45817e07 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/LevelUniforms.java @@ -0,0 +1,102 @@ +package com.jozufozu.flywheel.backend.engine.uniform; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.event.RenderContext; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.level.Level; + +public class LevelUniforms implements UniformProvider { + public static final int SIZE = 12 * 4 + 2 * 16; + + @Nullable + private RenderContext context; + + @Override + public int byteSize() { + return SIZE; + } + + public void setContext(RenderContext context) { + this.context = context; + } + + @Override + public void write(long ptr) { + if (context == null) { + MemoryUtil.memSet(ptr, 0, SIZE); + return; + } + + ClientLevel level = context.level(); + float ptick = context.partialTick(); + + Vec3 skyColor = level.getSkyColor(context.camera().getPosition(), ptick); + ptr = Uniforms.writeVec4(ptr, (float) skyColor.x, (float) skyColor.y, (float) skyColor.z, 1f); + + Vec3 cloudColor = level.getCloudColor(ptick); + ptr = Uniforms.writeVec4(ptr, (float) cloudColor.x, (float) cloudColor.y, (float) cloudColor.z, 1f); + + long dayTime = level.getDayTime(); + long levelDay = dayTime / 24000L; + long timeOfDay = dayTime - levelDay * 24000L; + MemoryUtil.memPutInt(ptr, (int) (levelDay % 0x7FFFFFFFL)); + ptr += 4; + MemoryUtil.memPutFloat(ptr, (float) timeOfDay / 24000f); + ptr += 4; + + MemoryUtil.memPutInt(ptr, level.dimensionType().hasSkyLight() ? 1 : 0); + ptr += 4; + + float sunAngle = level.getSunAngle(ptick); + MemoryUtil.memPutFloat(ptr, sunAngle); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, level.getMoonBrightness()); + ptr += 4; + MemoryUtil.memPutInt(ptr, level.getMoonPhase()); + ptr += 4; + + MemoryUtil.memPutInt(ptr, level.isRaining() ? 1 : 0); + ptr += 4; + MemoryUtil.memPutFloat(ptr, level.getRainLevel(ptick)); + ptr += 4; + + MemoryUtil.memPutInt(ptr, level.isThundering() ? 1 : 0); + ptr += 4; + MemoryUtil.memPutFloat(ptr, level.getThunderLevel(ptick)); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, level.getSkyDarken(ptick)); + ptr += 4; + + MemoryUtil.memPutInt(ptr, getConstantAmbientLightFlag(context)); + ptr += 4; + + // TODO: use defines for custom dimension ids + int dimensionId; + ResourceKey dimension = level.dimension(); + if (Level.OVERWORLD.equals(dimension)) { + dimensionId = 0; + } else if (Level.NETHER.equals(dimension)) { + dimensionId = 1; + } else if (Level.END.equals(dimension)) { + dimensionId = 2; + } else { + dimensionId = -1; + } + MemoryUtil.memPutInt(ptr, dimensionId); + } + + private static int getConstantAmbientLightFlag(RenderContext context) { + var constantAmbientLight = context.level() + .effects() + .constantAmbientLight(); + return constantAmbientLight ? 1 : 0; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/OptionsUniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/OptionsUniforms.java new file mode 100644 index 000000000..207a4b0c9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/OptionsUniforms.java @@ -0,0 +1,61 @@ +package com.jozufozu.flywheel.backend.engine.uniform; + +import org.lwjgl.system.MemoryUtil; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; + +public class OptionsUniforms implements UniformProvider { + public static final int SIZE = 4 * 14; + + @Override + public int byteSize() { + return SIZE; + } + + @Override + public void write(long ptr) { + Options options = Minecraft.getInstance().options; + + MemoryUtil.memPutFloat(ptr, options.gamma().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.fov().get()); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, options.screenEffectScale().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, options.glintSpeed().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, options.glintStrength().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.biomeBlendRadius().get()); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.ambientOcclusion().get() ? 1 : 0); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.bobView().get() ? 1 : 0); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.highContrast().get() ? 1 : 0); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, options.textBackgroundOpacity().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.backgroundForChatOnly().get() ? 1 : 0); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, options.darknessEffectScale().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutFloat(ptr, options.damageTiltStrength().get().floatValue()); + ptr += 4; + + MemoryUtil.memPutInt(ptr, options.hideLightningFlash().get() ? 1 : 0); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/PlayerUniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/PlayerUniforms.java new file mode 100644 index 000000000..787c8ab4f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/PlayerUniforms.java @@ -0,0 +1,127 @@ +package com.jozufozu.flywheel.backend.engine.uniform; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.event.RenderContext; +import com.jozufozu.flywheel.backend.mixin.AbstractClientPlayerAccessor; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.PlayerInfo; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.util.FastColor; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; + +public class PlayerUniforms implements UniformProvider { + public static final int SIZE = 9 * 4 + 8 + 2 * 16; + + @Nullable + private RenderContext context; + + @Override + public int byteSize() { + return SIZE; + } + + public void setContext(RenderContext context) { + this.context = context; + } + + @Override + public void write(long ptr) { + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + if (context == null || player == null) { + MemoryUtil.memSet(ptr, 0, SIZE); + return; + } + + PlayerInfo info = ((AbstractClientPlayerAccessor) player).flywheel$getPlayerInfo(); + + Vec3 eyePos = player.getEyePosition(context.partialTick()); + ptr = Uniforms.writeVec3(ptr, (float) eyePos.x, (float) eyePos.y, (float) eyePos.z); + + ptr = writeTeamColor(ptr, info); + + ptr = writeEyeBrightness(ptr, player); + + ptr = writeHeldLight(ptr, player); + + ptr = writeEyeIn(ptr, player); + + MemoryUtil.memPutInt(ptr, player.isCrouching() ? 1 : 0); + ptr += 4; + MemoryUtil.memPutInt(ptr, player.isSleeping() ? 1 : 0); + ptr += 4; + MemoryUtil.memPutInt(ptr, player.isSwimming() ? 1 : 0); + ptr += 4; + MemoryUtil.memPutInt(ptr, player.isFallFlying() ? 1 : 0); + ptr += 4; + + MemoryUtil.memPutInt(ptr, player.isShiftKeyDown() ? 1 : 0); + ptr += 4; + + MemoryUtil.memPutInt(ptr, info.getGameMode().getId()); + ptr += 4; + } + + private static long writeTeamColor(long ptr, PlayerInfo info) { + int red = 255, green = 255, blue = 255, alpha = 0; + PlayerTeam team = info.getTeam(); + if (team != null) { + alpha = 255; + Integer color = team.getColor().getColor(); + if (color != null) { + int icolor = color; + red = FastColor.ARGB32.red(icolor); + green = FastColor.ARGB32.green(icolor); + blue = FastColor.ARGB32.blue(icolor); + } + } + + return Uniforms.writeVec4(ptr, (float) red / 255f, (float) blue / 255f, (float) green / 255f, + (float) alpha / 255f); + } + + private static long writeEyeBrightness(long ptr, LocalPlayer player) { + int blockBrightness = player.clientLevel.getBrightness(LightLayer.BLOCK, player.blockPosition()); + int skyBrightness = player.clientLevel.getBrightness(LightLayer.SKY, player.blockPosition()); + int maxBrightness = player.clientLevel.getMaxLightLevel(); + + ptr = Uniforms.writeVec2(ptr, (float) blockBrightness / (float) maxBrightness, + (float) skyBrightness / (float) maxBrightness); + return ptr; + } + + private static long writeHeldLight(long ptr, LocalPlayer player) { + int luminance = 0; + for (InteractionHand hand : InteractionHand.values()) { + Item handItem = player.getItemInHand(hand).getItem(); + if (handItem instanceof BlockItem bitem) { + Block block = bitem.getBlock(); + int blockLight = block.defaultBlockState().getLightEmission(player.clientLevel, BlockPos.ZERO); + if (luminance < blockLight) { + luminance = blockLight; + } + } + } + + MemoryUtil.memPutFloat(ptr, (float) luminance / 15); + return ptr + 4; + } + + private static long writeEyeIn(long ptr, LocalPlayer player) { + ClientLevel level = player.clientLevel; + Vec3 eyePos = player.getEyePosition(); + BlockPos blockPos = BlockPos.containing(eyePos); + return Uniforms.writeInFluidAndBlock(ptr, level, blockPos, eyePos); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java index a6836d40b..abb6b751b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java @@ -5,29 +5,73 @@ import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.config.DebugMode; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.Vec3; + public class Uniforms { + public static final int FRAME_INDEX = 0; + public static final int FOG_INDEX = 1; + public static final int OPTIONS_INDEX = 2; + public static final int PLAYER_INDEX = 3; + public static final int LEVEL_INDEX = 4; public static boolean frustumPaused = false; public static boolean frustumCapture = false; - private static UniformBuffer frame; - private static UniformBuffer fog; + private static @Nullable UniformBuffer frame; + private static @Nullable UniformBuffer fog; + private static @Nullable UniformBuffer options; + private static @Nullable UniformBuffer player; + private static @Nullable UniformBuffer level; + private static boolean optionsRequiresUpdate = false; public static UniformBuffer frame() { if (frame == null) { - frame = new UniformBuffer<>(0, new FrameUniforms()); + frame = new UniformBuffer<>(FRAME_INDEX, new FrameUniforms()); } return frame; } public static UniformBuffer fog() { if (fog == null) { - fog = new UniformBuffer<>(1, new FogUniforms()); + fog = new UniformBuffer<>(FOG_INDEX, new FogUniforms()); } return fog; } + public static UniformBuffer options() { + if (options == null) { + options = new UniformBuffer<>(OPTIONS_INDEX, new OptionsUniforms()); + } + return options; + } + + public static UniformBuffer player() { + if (player == null) { + player = new UniformBuffer<>(PLAYER_INDEX, new PlayerUniforms()); + } + return player; + } + + public static UniformBuffer level() { + if (level == null) { + level = new UniformBuffer<>(LEVEL_INDEX, new LevelUniforms()); + } + return level; + } + public static void bindForDraw() { bindFrame(); bindFog(); + bindOptions(); + bindPlayer(); + bindLevel(); } public static void bindFrame() { @@ -42,16 +86,52 @@ public class Uniforms { } } + public static void bindOptions() { + if (options != null) { + options.bind(); + } + } + + public static void bindPlayer() { + if (player != null) { + player.bind(); + } + } + + public static void bindLevel() { + if (level != null) { + level.bind(); + } + } + public static void onFogUpdate() { try (var restoreState = GlStateTracker.getRestoreState()) { fog().update(); } } + public static void onOptionsUpdate() { + // this is sometimes called too early to do an actual update + optionsRequiresUpdate = true; + } + public static void updateContext(RenderContext ctx) { var ubo = frame(); ubo.provider.setContext(ctx); ubo.update(); + + if (optionsRequiresUpdate) { + options().update(); + optionsRequiresUpdate = false; + } + + var player = player(); + player.provider.setContext(ctx); + player.update(); + + var level = level(); + level.provider.setContext(ctx); + level.update(); } public static void setDebugMode(DebugMode mode) { @@ -68,5 +148,74 @@ public class Uniforms { fog.delete(); fog = null; } + + if (options != null) { + options.delete(); + options = null; + } + + if (player != null) { + player.delete(); + player = null; + } + + if (level != null) { + level.delete(); + level = null; + } + } + + static long writeVec4(long ptr, float x, float y, float z, float w) { + MemoryUtil.memPutFloat(ptr, x); + MemoryUtil.memPutFloat(ptr + 4, y); + MemoryUtil.memPutFloat(ptr + 8, z); + MemoryUtil.memPutFloat(ptr + 12, w); + return ptr + 16; + } + + static long writeVec3(long ptr, float camX, float camY, float camZ) { + MemoryUtil.memPutFloat(ptr, camX); + MemoryUtil.memPutFloat(ptr + 4, camY); + MemoryUtil.memPutFloat(ptr + 8, camZ); + MemoryUtil.memPutFloat(ptr + 12, 0f); // empty component of vec4 because we don't trust std140 + return ptr + 16; + } + + static long writeVec2(long ptr, float camX, float camY) { + MemoryUtil.memPutFloat(ptr, camX); + MemoryUtil.memPutFloat(ptr + 4, camY); + return ptr + 8; + } + + static long writeInFluidAndBlock(long ptr, Level level, BlockPos blockPos, Vec3 pos) { + FluidState fState = level.getFluidState(blockPos); + BlockState bState = level.getBlockState(blockPos); + float height = fState.getHeight(level, blockPos); + + if (fState.isEmpty()) { + MemoryUtil.memPutInt(ptr, 0); + } else if (pos.y < blockPos.getY() + height) { + // TODO: handle custom fluids via defines + if (fState.is(FluidTags.WATER)) { + MemoryUtil.memPutInt(ptr, 1); + } else if (fState.is(FluidTags.LAVA)) { + MemoryUtil.memPutInt(ptr, 2); + } else { + MemoryUtil.memPutInt(ptr, -1); + } + } + + if (bState.isAir()) { + MemoryUtil.memPutInt(ptr + 4, 0); + } else { + // TODO: handle custom blocks via defines + if (bState.is(Blocks.POWDER_SNOW)) { + MemoryUtil.memPutInt(ptr + 4, 0); + } else { + MemoryUtil.memPutInt(ptr + 4, -1); + } + } + + return ptr + 8; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/mixin/AbstractClientPlayerAccessor.java b/src/main/java/com/jozufozu/flywheel/backend/mixin/AbstractClientPlayerAccessor.java new file mode 100644 index 000000000..0c2218f06 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/mixin/AbstractClientPlayerAccessor.java @@ -0,0 +1,13 @@ +package com.jozufozu.flywheel.backend.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.client.multiplayer.PlayerInfo; +import net.minecraft.client.player.AbstractClientPlayer; + +@Mixin(AbstractClientPlayer.class) +public interface AbstractClientPlayerAccessor { + @Invoker("getPlayerInfo") + PlayerInfo flywheel$getPlayerInfo(); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/mixin/GameRendererAccessor.java b/src/main/java/com/jozufozu/flywheel/backend/mixin/GameRendererAccessor.java new file mode 100644 index 000000000..cf92578fb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/mixin/GameRendererAccessor.java @@ -0,0 +1,23 @@ +package com.jozufozu.flywheel.backend.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.GameRenderer; + +@Mixin(GameRenderer.class) +public interface GameRendererAccessor { + @Invoker("getFov") + double flywheel$getFov(Camera pActiveRenderInfo, float pPartialTicks, boolean pUseFOVSetting); + + @Accessor("zoom") + float flywheel$getZoom(); + + @Accessor("zoomX") + float flywheel$getZoomX(); + + @Accessor("zoomY") + float flywheel$getZoomY(); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java b/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java new file mode 100644 index 000000000..5d8497c76 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java @@ -0,0 +1,23 @@ +package com.jozufozu.flywheel.backend.mixin; + +import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; + +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; + +import net.minecraft.client.Options; + +@Mixin(Options.class) +public class OptionsMixin { + @Inject(method = "load()V", at = @At("RETURN")) + private void flywheel$onLoad(CallbackInfo ci) { + Uniforms.onOptionsUpdate(); + } + + @Inject(method = "save", at = @At("HEAD")) + private void flywheel$onSave(CallbackInfo ci) { + Uniforms.onOptionsUpdate(); + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.frag b/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.frag index b63c8cead..dc8334916 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.frag +++ b/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.frag @@ -1,6 +1,5 @@ #include "flywheel:internal/material.glsl" -#include "flywheel:internal/uniforms/frame.glsl" -#include "flywheel:internal/uniforms/fog.glsl" +#include "flywheel:internal/uniforms/uniforms.glsl" in vec4 flw_vertexPos; in vec4 flw_vertexColor; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.vert b/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.vert index 2cddb2e42..18128f125 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.vert +++ b/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.vert @@ -1,6 +1,5 @@ #include "flywheel:internal/material.glsl" -#include "flywheel:internal/uniforms/frame.glsl" -#include "flywheel:internal/uniforms/fog.glsl" +#include "flywheel:internal/uniforms/uniforms.glsl" // TODO: can we combine some of these internally to use fewer in/out slots? out vec4 flw_vertexPos; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl index 283ad293b..6b50a358a 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl @@ -1,7 +1,7 @@ #include "flywheel:internal/indirect/buffers.glsl" #include "flywheel:internal/indirect/model_descriptor.glsl" #include "flywheel:internal/indirect/object.glsl" -#include "flywheel:internal/uniforms/frame.glsl" +#include "flywheel:internal/uniforms/uniforms.glsl" #include "flywheel:util/matrix.glsl" layout(local_size_x = _FLW_SUBGROUP_SIZE) in; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_header.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_header.glsl index 64124d01a..436db261b 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_header.glsl +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_header.glsl @@ -1,5 +1,3 @@ -#include "flywheel:internal/uniforms/frame.glsl" - -// Fog doesn't seem like a valid thing to query during the cull pass. Other uniforms added in the -// future may also be excluded, and we'll have to document each one. -// #include "flywheel:internal/uniforms/fog.glsl" +// Cull shaders may not need all uniforms, but it is +// more consistent if we just provide all uniforms anyways. +#include "flywheel:internal/uniforms/uniforms.glsl" diff --git a/src/main/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl index dcc401409..aa0ec97f7 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl +++ b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl @@ -12,15 +12,27 @@ struct FrustumPlanes { layout(std140) uniform _FlwFrameUniforms { FrustumPlanes flw_frustumPlanes; + mat4 flw_view; + mat4 flw_viewInverse; + mat4 flw_viewPrev; + mat4 flw_projection; + mat4 flw_projectionInverse; + mat4 flw_projectionPrev; mat4 flw_viewProjection; mat4 flw_viewProjectionInverse; + mat4 flw_viewProjectionPrev; vec4 _flw_cameraPos; + vec4 _flw_cameraPosPrev; vec4 _flw_cameraLook; + vec4 _flw_cameraLookPrev; vec2 flw_cameraRot; + vec2 flw_cameraRotPrev; vec2 flw_viewportSize; float flw_defaultLineWidth; + float flw_aspectRatio; + float flw_viewDistance; uint flw_constantAmbientLight; @@ -30,8 +42,22 @@ layout(std140) uniform _FlwFrameUniforms { float flw_renderTicks; float flw_renderSeconds; + /** 0 means no fluid. Use FLW_CAMERA_IN_FLUID_* defines to detect fluid type. */ + uint flw_cameraInFluid; + /** 0 means no block. Use FLW_CAMERA_IN_BLOCK_* defines to detect block type. */ + uint flw_cameraInBlock; + uint _flw_debugMode; }; #define flw_cameraPos _flw_cameraPos.xyz #define flw_cameraLook _flw_cameraLook.xyz +#define flw_cameraPosPrev _flw_cameraPosPrev.xyz +#define flw_cameraLookPrev _flw_cameraLookPrev.xyz + +#define FLW_CAMERA_IN_FLUID_WATER 1 +#define FLW_CAMERA_IN_FLUID_LAVA 2 +#define FLW_CAMERA_IN_FLUID_UNKNOWN 0xFFFFFFFFu + +#define FLW_CAMERA_IN_BLOCK_POWDER_SNOW 1 +#define FLW_CAMERA_IN_BLOCK_UNKNOWN 0xFFFFFFFFu diff --git a/src/main/resources/assets/flywheel/flywheel/internal/uniforms/level.glsl b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/level.glsl new file mode 100644 index 000000000..178eef1a2 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/level.glsl @@ -0,0 +1,32 @@ +layout(std140) uniform _FlwLevelUniforms { + vec4 flw_skyColor; + vec4 flw_cloudColor; + + /** The current day number of the level. */ + uint flw_levelDay; + /** The current fraction of the current day that has elapsed. */ + float flw_timeOfDay; + + uint flw_levelHasSkyLight; + + float flw_sunAngle; + + float flw_moonBrightness; + /** There are normally only 8 moon phases. */ + uint flw_moonPhase; + + uint flw_isRaining; + float flw_rainLevel; + uint flw_isThundering; + float flw_thunderLevel; + + float flw_skyDarken; + + /** Use FLW_DIMENSION_* ids to determine the dimension. May eventually be implemented for custom dimensions. */ + uint flw_dimension; +}; + +#define FLW_DIMENSION_OVERWORLD 0 +#define FLW_DIMENSION_NETHER 1 +#define FLW_DIMENSION_END 2 +#define FLW_DIMENSION_UNKNOWN 0xFFFFFFFFu diff --git a/src/main/resources/assets/flywheel/flywheel/internal/uniforms/options.glsl b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/options.glsl new file mode 100644 index 000000000..7ea97b33a --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/options.glsl @@ -0,0 +1,19 @@ +// options.glsl - Houses uniforms for many of the game's settings, focusing on video and accessibility settings. + +layout(std140) uniform _FlwOptionsUniforms { + float flw_brightnessOption; + uint flw_fovOption; + float flw_distortionOption; + float flw_glintSpeedOption; + float flw_glintStrengthOption; + uint flw_biomeBlendOption; + uint flw_smoothLightingOption; + uint flw_viewBobbingOption; + + uint flw_highContrastOption; + float flw_textBackgroundOpacityOption; + uint flw_textBackgroundForChatOnlyOption; + float flw_darknessPulsingOption; + float flw_damageTiltOption; + uint hideLightningFlashesOption; +}; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/uniforms/player.glsl b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/player.glsl new file mode 100644 index 000000000..aab7927da --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/player.glsl @@ -0,0 +1,37 @@ +// player.glsl - Holds uniforms for player state. + +layout (std140) uniform _FlwPlayerUniforms { + vec4 _flw_eyePos; + + /** Alpha is 1 if any team color is present, 0 otherwise. */ + vec4 flw_teamColor; + + /** The brightness at the player's eye position. */ + vec2 flw_eyeBrightness; + + /** Brightness of the brightest light that the player is holding, 0-1. */ + float flw_heldLight; + /** 0 means no fluid. Use FLW_PLAYER_EYE_IN_FLUID_* defines to detect fluid type. */ + uint flw_playerEyeInFluid; + /** 0 means no block. Use FLW_PLAYER_EYE_IN_BLOCK_* defines to detect block type. */ + uint flw_playerEyeInBlock; + + uint flw_playerCrouching; + uint flw_playerSleeping; + uint flw_playerSwimming; + uint flw_playerFallFlying; + + uint flw_shiftKeyDown; + + /** 0 = survival, 1 = creative, 2 = adventure, 3 = spectator. */ + uint flw_gameMode; +}; + +#define flw_eyePos _flw_eyePos.xyz + +#define FLW_PLAYER_EYE_IN_FLUID_WATER 1 +#define FLW_PLAYER_EYE_IN_FLUID_LAVA 2 +#define FLW_PLAYER_EYE_IN_FLUID_UNKNOWN 0xFFFFFFFFu + +#define FLW_PLAYER_EYE_IN_BLOCK_POWDER_SNOW 1 +#define FLW_PLAYER_EYE_IN_BLOCK_UNKNOWN 0xFFFFFFFFu diff --git a/src/main/resources/assets/flywheel/flywheel/internal/uniforms/uniforms.glsl b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/uniforms.glsl new file mode 100644 index 000000000..5fac53246 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/internal/uniforms/uniforms.glsl @@ -0,0 +1,7 @@ +// uniforms.glsl - Includes common uniforms. + +#include "flywheel:internal/uniforms/frame.glsl" +#include "flywheel:internal/uniforms/fog.glsl" +#include "flywheel:internal/uniforms/options.glsl" +#include "flywheel:internal/uniforms/player.glsl" +#include "flywheel:internal/uniforms/level.glsl" diff --git a/src/main/resources/flywheel.backend.mixins.json b/src/main/resources/flywheel.backend.mixins.json index c5948b498..25dbe6fb9 100644 --- a/src/main/resources/flywheel.backend.mixins.json +++ b/src/main/resources/flywheel.backend.mixins.json @@ -5,8 +5,11 @@ "compatibilityLevel": "JAVA_17", "refmap": "flywheel.refmap.json", "client": [ + "AbstractClientPlayerAccessor", + "GameRendererAccessor", "GlStateManagerMixin", "LightTextureAccessor", + "OptionsMixin", "OverlayTextureAccessor", "RenderSystemMixin" ],