Compare commits

...

11 Commits

Author SHA1 Message Date
Cyan
a3c66f2b08
Merge 7bb04daa82 into 3a0ec10a30 2024-03-04 09:44:21 +00:00
Kneelawk
7bb04daa82
Add level uniforms 2024-03-04 01:44:04 -08:00
Kneelawk
81d83e185b
Add more player uniforms 2024-03-03 23:30:27 -08:00
Jozufozu
3a0ec10a30 Collector's edition
- Track exactly what blocks are contained in a light volume via BoxSet
- Fix segfault when a volume is expanded
- Shrink the volume when enough data is discarded
  - I think the texture management is broken for this case
2024-03-03 21:36:59 -08:00
Kneelawk
0ea8a31103
Begin on player uniforms 2024-03-03 20:06:28 -08:00
Kneelawk
56d03601cd
Add options uniform 2024-03-03 01:25:45 -08:00
Kneelawk
ef2f2268cc
Document cameraIn- uniforms 2024-03-02 01:10:00 -08:00
Kneelawk
87c914100e
Finish adding most view.h things to frame.glsl 2024-03-02 01:06:42 -08:00
Kneelawk
9f1c722474
Add clean matrices 2024-03-01 17:48:39 -08:00
Kneelawk
7eb7c4cca0
Add last values 2024-03-01 14:48:06 -08:00
Kneelawk
116b2df6a9
Add basic view.h matrices 2024-03-01 10:30:17 -08:00
18 changed files with 960 additions and 105 deletions

View File

@ -56,9 +56,9 @@ public class EmbeddedEnvironment extends AtomicReferenceCounted implements Envir
lightTexture.bind();
lightTexture.ensureCapacity(lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ);
lightTexture.ensureCapacity(lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ());
lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ);
lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ());
}
@Override
@ -94,7 +94,7 @@ public class EmbeddedEnvironment extends AtomicReferenceCounted implements Envir
float oneOverSizeZ = 1f / (float) lightTexture.sizeZ;
drawProgram.setVec3("_flw_oneOverLightBoxSize", oneOverSizeX, oneOverSizeY, oneOverSizeZ);
drawProgram.setVec3("_flw_lightVolumeMin", lightVolume.minX, lightVolume.minY, lightVolume.minZ);
drawProgram.setVec3("_flw_lightVolumeMin", lightVolume.x(), lightVolume.y(), lightVolume.z());
}
drawProgram.setMat4("_flw_model", pose);
drawProgram.setMat3("_flw_normal", normal);

View File

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.engine.embed;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.util.BoxSet;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
import net.minecraft.core.BlockPos;
@ -11,20 +12,16 @@ import net.minecraft.world.level.LightLayer;
public class EmbeddedLightVolume {
public static final long STRIDE = Short.BYTES;
public int minX;
public int minY;
public int minZ;
public int sizeX;
public int sizeY;
public int sizeZ;
private final BoxSet wantedCoords = new BoxSet();
private final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos();
@Nullable
protected MemoryBlock block;
protected boolean dirty;
protected MemoryBlock memoryBlock;
public boolean empty() {
return block == null;
return memoryBlock == null;
}
public void collect(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
@ -37,12 +34,37 @@ public class EmbeddedLightVolume {
}
}
}
markDirty();
}
public void invalidate(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
// TODO: shrink the volume
if (memoryBlock == null) {
return;
}
int oldMinX = wantedCoords.minX();
int oldMinY = wantedCoords.minY();
int oldMinZ = wantedCoords.minZ();
int oldSizeX = wantedCoords.sizeX();
int oldSizeY = wantedCoords.sizeY();
var shrank = wantedCoords.clear(minX, minY, minZ, sizeX, sizeY, sizeZ);
if (!shrank) {
return;
}
int newVolume = wantedCoords.volume();
MemoryBlock newBlock = MemoryBlock.malloc(newVolume * STRIDE);
int xOff = wantedCoords.minX() - oldMinX;
int yOff = wantedCoords.minY() - oldMinY;
int zOff = wantedCoords.minZ() - oldMinZ;
blit(memoryBlock, xOff, yOff, zOff, oldSizeX, oldSizeY, newBlock, 0, 0, 0, wantedCoords.sizeX(), wantedCoords.sizeY(), wantedCoords.sizeX(), wantedCoords.sizeY(), wantedCoords.sizeZ());
memoryBlock.free();
memoryBlock = newBlock;
}
private void paintLight(BlockAndTintGetter level, int x, int y, int z) {
@ -51,97 +73,94 @@ public class EmbeddedLightVolume {
int block = level.getBrightness(LightLayer.BLOCK, scratchPos);
int sky = level.getBrightness(LightLayer.SKY, scratchPos);
long ptr = worldPosToPtr(x, y, z);
long ptr = this.memoryBlock.ptr() + offset(x - x(), y - y(), z - z(), sizeX(), sizeY());
MemoryUtil.memPutShort(ptr, (short) ((block << 4) | sky << 12));
}
private void maybeExpandForBox(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
if (block == null) {
this.minX = minX;
this.minY = minY;
this.minZ = minZ;
this.sizeX = sizeX;
this.sizeY = sizeY;
this.sizeZ = sizeZ;
int oldMinX = wantedCoords.minX();
int oldMinY = wantedCoords.minY();
int oldMinZ = wantedCoords.minZ();
int oldSizeX = wantedCoords.sizeX();
int oldSizeY = wantedCoords.sizeY();
int oldSizeZ = wantedCoords.sizeZ();
var grew = wantedCoords.add(minX, minY, minZ, sizeX, sizeY, sizeZ);
if (memoryBlock == null) {
int volume = sizeX * sizeY * sizeZ;
block = MemoryBlock.malloc(volume * STRIDE);
block.clear();
memoryBlock = MemoryBlock.malloc(volume * STRIDE);
return;
}
int newMinX = Math.min(this.minX, minX);
int newMinY = Math.min(this.minY, minY);
int newMinZ = Math.min(this.minZ, minZ);
int newSizeX = Math.max(this.minX + this.sizeX, minX + sizeX) - newMinX;
int newSizeY = Math.max(this.minY + this.sizeY, minY + sizeY) - newMinY;
int newSizeZ = Math.max(this.minZ + this.sizeZ, minZ + sizeZ) - newMinZ;
if (newMinX == this.minX && newMinY == this.minY && newMinZ == this.minZ && newSizeX == this.sizeX && newSizeY == this.sizeY && newSizeZ == this.sizeZ) {
if (!grew) {
return;
}
int newVolume = newSizeX * newSizeY * newSizeZ;
int newVolume = wantedCoords.volume();
MemoryBlock newBlock = MemoryBlock.malloc(newVolume * STRIDE);
newBlock.clear();
int xOff = newMinX - this.minX;
int yOff = newMinY - this.minY;
int zOff = newMinZ - this.minZ;
int xOff = oldMinX - wantedCoords.minX();
int yOff = oldMinY - wantedCoords.minY();
int zOff = oldMinZ - wantedCoords.minZ();
for (int z = 0; z < this.sizeZ; z++) {
for (int y = 0; y < this.sizeY; y++) {
for (int x = 0; x < this.sizeX; x++) {
long oldPtr = boxPosToPtr(x, y, z);
long newPtr = newBlock.ptr() + x + xOff + (newSizeX * (y + yOff + (z + zOff) * newSizeY)) * STRIDE;
blit(memoryBlock, 0, 0, 0, oldSizeX, oldSizeY, newBlock, xOff, yOff, zOff, wantedCoords.sizeX(), wantedCoords.sizeY(), oldSizeX, oldSizeY, oldSizeZ);
MemoryUtil.memPutShort(newPtr, MemoryUtil.memGetShort(oldPtr));
memoryBlock.free();
memoryBlock = newBlock;
}
public static void blit(MemoryBlock src, int srcX, int srcY, int srcZ, int srcSizeX, int srcSizeY, MemoryBlock dst, int dstX, int dstY, int dstZ, int dstSizeX, int dstSizeY, int sizeX, int sizeY, int sizeZ) {
for (int z = 0; z < sizeZ; z++) {
for (int y = 0; y < sizeY; y++) {
for (int x = 0; x < sizeX; x++) {
long srcPtr = src.ptr() + offset(x + srcX, y + srcY, z + srcZ, srcSizeX, srcSizeY);
long dstPtr = dst.ptr() + offset(x + dstX, y + dstY, z + dstZ, dstSizeX, dstSizeY);
MemoryUtil.memPutShort(dstPtr, MemoryUtil.memGetShort(srcPtr));
}
}
}
}
this.minX = newMinX;
this.minY = newMinY;
this.minZ = newMinZ;
this.sizeX = newSizeX;
this.sizeY = newSizeY;
this.sizeZ = newSizeZ;
block.free();
block = newBlock;
}
protected long worldPosToPtr(int x, int y, int z) {
return block.ptr() + worldPosToPtrOffset(x, y, z);
}
protected long boxPosToPtr(int x, int y, int z) {
return block.ptr() + boxPosToPtrOffset(x, y, z);
}
protected long worldPosToPtrOffset(int x, int y, int z) {
return boxPosToPtrOffset(x - minX, y - minY, z - minZ);
}
protected long boxPosToPtrOffset(int x, int y, int z) {
public static long offset(int x, int y, int z, int sizeX, int sizeY) {
return (x + sizeX * (y + z * sizeY)) * STRIDE;
}
public void delete() {
if (block != null) {
block.free();
block = null;
if (memoryBlock != null) {
memoryBlock.free();
memoryBlock = null;
}
}
protected void markDirty() {
this.dirty = true;
}
public long ptr() {
return block.ptr();
return memoryBlock.ptr();
}
public int x() {
return wantedCoords.minX();
}
public int y() {
return wantedCoords.minY();
}
public int z() {
return wantedCoords.minZ();
}
public int sizeX() {
return wantedCoords.sizeX();
}
public int sizeY() {
return wantedCoords.sizeY();
}
public int sizeZ() {
return wantedCoords.sizeZ();
}
}

View File

@ -2,26 +2,54 @@ 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
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 Matrix4f cleanProjection = new Matrix4f();
private final Matrix4f cleanProjectionInverse = new Matrix4f();
private final Matrix4f cleanProjectionPrev = new Matrix4f();
private final Matrix4f cleanViewProjection = new Matrix4f();
private final Matrix4f cleanViewProjectionInverse = new Matrix4f();
private final Matrix4f cleanViewProjectionPrev = 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,8 +74,12 @@ 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);
setupCleanMatrices(context.stack(), camera, context.partialTick());
if (!Uniforms.frustumPaused || Uniforms.frustumCapture) {
MatrixMath.writePackedFrustumPlanes(ptr, viewProjection);
@ -56,39 +88,107 @@ public class FrameUniforms implements UniformProvider {
ptr += 96;
// manage last values of matrices
if (!lastInit) {
viewPrev.set(view);
projectionPrev.set(projection);
viewProjectionPrev.set(viewProjectionPrev);
cleanProjectionPrev.set(cleanProjection);
cleanViewProjectionPrev.set(cleanViewProjection);
}
ptr = writeMatrices(ptr);
viewPrev.set(view);
projectionPrev.set(projection);
viewProjectionPrev.set(viewProjection);
cleanProjectionPrev.set(cleanProjection);
cleanViewProjectionPrev.set(cleanViewProjection);
ptr = writeCamera(ptr, camX, camY, camZ, camera);
ptr = writeCamera(ptr, camX, camY, camZ, camera.getLookVector(), camera.getXRot(), camera.getYRot());
// last values for camera
if (!lastInit) {
cameraPositionPrev.set(camX, camY, camZ);
cameraLookPrev.set(camera.getLookVector());
cameraRotPrev.set(camera.getXRot(), camera.getYRot());
}
ptr = writeCamera(ptr, cameraPositionPrev.x, cameraPositionPrev.y, cameraPositionPrev.z, cameraLookPrev,
cameraRotPrev.x, cameraRotPrev.y);
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.memPutFloat(ptr, (float) window.getWidth() / (float) window.getHeight());
ptr += 4;
MemoryUtil.memPutFloat(ptr, Minecraft.getInstance().gameRenderer.getDepthFar());
ptr += 4;
MemoryUtil.memPutInt(ptr, getConstantAmbientLightFlag(context));
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);
MatrixMath.writeUnsafe(cleanProjection, ptr + 64 * 9);
MatrixMath.writeUnsafe(cleanProjection.invert(cleanProjectionInverse), ptr + 64 * 10);
MatrixMath.writeUnsafe(cleanProjectionPrev, ptr + 64 * 11);
MatrixMath.writeUnsafe(cleanViewProjection, ptr + 64 * 12);
MatrixMath.writeUnsafe(cleanViewProjection.invert(cleanViewProjectionInverse), ptr + 64 * 13);
MatrixMath.writeUnsafe(cleanViewProjectionPrev, ptr + 64 * 14);
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 void setupCleanMatrices(PoseStack stack, Camera camera, float partialTicks) {
Minecraft mc = Minecraft.getInstance();
GameRenderer gr = mc.gameRenderer;
GameRendererAccessor gra = (GameRendererAccessor) gr;
var lookVector = camera.getLookVector();
ptr = writeVec3(ptr, lookVector.x, lookVector.y, lookVector.z);
float fov = (float) gra.flywheel$getFov(camera, partialTicks, true);
ptr = writeVec2(ptr, camera.getXRot(), camera.getYRot());
cleanProjection.identity();
if (gra.flywheel$getZoom() != 1.0F) {
cleanProjection.translate(gra.flywheel$getZoomX(), -gra.flywheel$getZoomY(), 0.0F);
cleanProjection.scale(gra.flywheel$getZoom(), gra.flywheel$getZoom(), 1.0F);
}
cleanProjection.mul(new Matrix4f().setPerspective(fov * ((float) Math.PI / 180F),
(float) mc.getWindow().getWidth() / (float) mc.getWindow().getHeight(), 0.05F, gr.getDepthFar()));
cleanViewProjection.set(cleanProjection).mul(stack.last().pose());
}
private static long writeCamera(long ptr, float camX, float camY, float camZ, Vector3f lookVector, float xRot,
float yRot) {
ptr = Uniforms.writeVec3(ptr, camX, camY, camZ);
ptr = Uniforms.writeVec3(ptr, lookVector.x, lookVector.y, lookVector.z);
ptr = Uniforms.writeVec2(ptr, xRot, yRot);
return ptr;
}
@ -106,19 +206,18 @@ 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);
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);
}
private static int getConstantAmbientLightFlag(RenderContext context) {
var constantAmbientLight = context.level()

View File

@ -0,0 +1,93 @@
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) {
Minecraft mc = Minecraft.getInstance();
ClientLevel level = mc.level;
if (level == null || context == null) {
MemoryUtil.memSet(ptr, 0, SIZE);
return;
}
float ptick = context.partialTick();
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;
Vec3 skyColor = level.getSkyColor(mc.gameRenderer.getMainCamera().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);
// TODO: use defines for custom dimension ids
int dimensionId;
ResourceKey<Level> 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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,113 @@
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;
}
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);
ptr += 4;
Vec3 eyePos = player.getEyePosition(context.partialTick());
ptr = Uniforms.writeVec3(ptr, (float) eyePos.x, (float) eyePos.y, (float) eyePos.z);
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);
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;
PlayerInfo info = ((AbstractClientPlayerAccessor) player).flywheel$getPlayerInfo();
MemoryUtil.memPutInt(ptr, info.getGameMode().getId());
ptr += 4;
int red = 0, green = 0, blue = 0, alpha = 0;
PlayerTeam team = info.getTeam();
if (team != null) {
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);
alpha = 255;
}
}
ptr = Uniforms.writeVec4(ptr, (float) red / 255f, (float) blue / 255f, (float) green / 255f,
(float) alpha / 255f);
}
private 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);
}
}

View File

@ -5,11 +5,25 @@ 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 boolean frustumPaused = false;
public static boolean frustumCapture = false;
private static UniformBuffer<FrameUniforms> frame;
private static UniformBuffer<FogUniforms> fog;
private static @Nullable UniformBuffer<FrameUniforms> frame;
private static @Nullable UniformBuffer<FogUniforms> fog;
private static @Nullable UniformBuffer<OptionsUniforms> options;
private static @Nullable UniformBuffer<PlayerUniforms> player;
private static @Nullable UniformBuffer<LevelUniforms> level;
public static UniformBuffer<FrameUniforms> frame() {
if (frame == null) {
@ -25,9 +39,33 @@ public class Uniforms {
return fog;
}
public static UniformBuffer<OptionsUniforms> options() {
if (options == null) {
options = new UniformBuffer<>(2, new OptionsUniforms());
}
return options;
}
public static UniformBuffer<PlayerUniforms> player() {
if (player == null) {
player = new UniformBuffer<>(3, new PlayerUniforms());
}
return player;
}
public static UniformBuffer<LevelUniforms> level() {
if (level == null) {
level = new UniformBuffer<>(4, new LevelUniforms());
}
return level;
}
public static void bindForDraw() {
bindFrame();
bindFog();
bindOptions();
bindPlayer();
bindLevel();
}
public static void bindFrame() {
@ -42,6 +80,24 @@ 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();
@ -52,6 +108,16 @@ public class Uniforms {
var ubo = frame();
ubo.provider.setContext(ctx);
ubo.update();
options();
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 +134,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;
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -0,0 +1,174 @@
package com.jozufozu.flywheel.backend.util;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.core.BlockPos;
public class BoxSet {
private final LongSet occupied = new LongOpenHashSet();
private int minX;
private int minY;
private int minZ;
private int maxX;
private int maxY;
private int maxZ;
/**
* Add a box to the set.
*
* @return {@code true} if the position or size of the set changed.
*/
public boolean add(int x, int y, int z, int sizeX, int sizeY, int sizeZ) {
boolean wasEmpty = occupied.isEmpty();
for (int i = x; i < x + sizeX; i++) {
for (int j = y; j < y + sizeY; j++) {
for (int k = z; k < z + sizeZ; k++) {
occupied.add(BlockPos.asLong(i, j, k));
}
}
}
if (wasEmpty) {
this.minX = x;
this.minY = y;
this.minZ = z;
this.maxX = x + sizeX;
this.maxY = y + sizeY;
this.maxZ = z + sizeZ;
return true;
} else {
boolean changed = false;
if (x < minX) {
minX = x;
changed = true;
}
if (y < minY) {
minY = y;
changed = true;
}
if (z < minZ) {
minZ = z;
changed = true;
}
if (x + sizeX > maxX) {
maxX = x + sizeX;
changed = true;
}
if (y + sizeY > maxY) {
maxY = y + sizeY;
changed = true;
}
if (z + sizeZ > maxZ) {
maxZ = z + sizeZ;
changed = true;
}
return changed;
}
}
/**
* Remove a box from the set.
*
* @return {@code true} if the position or size of the set changed.
*/
public boolean clear(int x, int y, int z, int sizeX, int sizeY, int sizeZ) {
for (int i = x; i < x + sizeX; i++) {
for (int j = y; j < y + sizeY; j++) {
for (int k = z; k < z + sizeZ; k++) {
occupied.remove(BlockPos.asLong(i, j, k));
}
}
}
if (occupied.isEmpty()) {
minX = 0;
minY = 0;
minZ = 0;
maxX = 0;
maxY = 0;
maxZ = 0;
return true;
} else {
ExtremaFinder finder = new ExtremaFinder();
occupied.forEach(finder::accept);
if (finder.volume() != volume()) {
minX = finder.minX;
minY = finder.minY;
minZ = finder.minZ;
maxX = finder.maxX;
maxY = finder.maxY;
maxZ = finder.maxZ;
return true;
} else {
return false;
}
}
}
public int minX() {
return minX;
}
public int minY() {
return minY;
}
public int minZ() {
return minZ;
}
public int sizeX() {
return maxX - minX;
}
public int sizeY() {
return maxY - minY;
}
public int sizeZ() {
return maxZ - minZ;
}
public int volume() {
return sizeX() * sizeY() * sizeZ();
}
private static class ExtremaFinder {
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int minZ = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
int maxZ = Integer.MIN_VALUE;
public void accept(long l) {
int x = BlockPos.getX(l);
int y = BlockPos.getY(l);
int z = BlockPos.getZ(l);
if (x < minX) {
minX = x;
}
if (y < minY) {
minY = y;
}
if (z < minZ) {
minZ = z;
}
if (x > maxX) {
maxX = x;
}
if (y > maxY) {
maxY = y;
}
if (z > maxZ) {
maxZ = z;
}
}
public int volume() {
return (maxX - minX) * (maxY - minY) * (maxZ - minZ);
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -12,15 +12,34 @@ 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;
mat4 flw_cleanProjection;
mat4 flw_cleanProjectionInverse;
mat4 flw_cleanProjectionPrev;
mat4 flw_cleanViewProjection;
mat4 flw_cleanViewProjectionInverse;
mat4 flw_cleanViewProjectionPrev;
vec4 _flw_cameraPos;
vec4 _flw_cameraLook;
vec2 flw_cameraRot;
vec4 _flw_cameraPosPrev;
vec4 _flw_cameraLookPrev;
vec2 flw_cameraRotPrev;
vec2 flw_viewportSize;
float flw_defaultLineWidth;
float flw_aspectRatio;
float flw_viewDistance;
uint flw_constantAmbientLight;
@ -30,8 +49,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

View File

@ -0,0 +1,31 @@
layout(std140) uniform _FlwLevelUniforms {
/** 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;
vec4 flw_skyColor;
vec4 flw_cloudColor;
/** 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

View File

@ -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;
};

View File

@ -0,0 +1,35 @@
// player.glsl - Holds uniforms for player state.
layout (std140) uniform _FlwPlayerUniforms {
/** Brightness of the brightest light that the player is holding, 0-1. */
float flw_heldLight;
vec4 _flw_eyePos;
/** The brightness at the player's eye position. */
vec2 flw_eyeBrightness;
/** 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;
/** Alpha is 1 if any team color is present, 0 otherwise. */
vec4 flw_teamColor;
};
#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

View File

@ -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"

View File

@ -5,6 +5,8 @@
"compatibilityLevel": "JAVA_17",
"refmap": "flywheel.refmap.json",
"client": [
"AbstractClientPlayerAccessor",
"GameRendererAccessor",
"GlStateManagerMixin",
"LightTextureAccessor",
"OverlayTextureAccessor",