Next level

- Rename all appropriate occurrences of "world" to "level"
- Simplify uniform buffers
- Fix indirect cull dispatch only binding frame uniforms
- Fix level renderer reload resetting debug mode
- Properly register backend argument
This commit is contained in:
PepperCode1 2024-04-11 12:12:13 -07:00
parent 2f7fce29c3
commit 2365687950
33 changed files with 456 additions and 623 deletions

View file

@ -34,7 +34,6 @@ import com.jozufozu.flywheel.vanilla.VanillaVisuals;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
@ -50,6 +49,8 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegisterEvent;
@Mod(Flywheel.ID)
public class Flywheel {
@ -69,6 +70,7 @@ public class Flywheel {
IEventBus modEventBus = FMLJavaModLoadingContext.get()
.getModEventBus();
modEventBus.addListener(Flywheel::onCommonSetup);
modEventBus.addListener(Flywheel::onRegister);
FlwConfig.get().registerSpecs(modLoadingContext);
@ -137,8 +139,11 @@ public class Flywheel {
}
private static void onCommonSetup(FMLCommonSetupEvent event) {
// FIXME: argument types also need to be registered to BuiltInRegistries.COMMAND_ARGUMENT_TYPE
ArgumentTypeInfos.registerByClass(BackendArgument.class, SingletonArgumentInfo.contextFree(() -> BackendArgument.INSTANCE));
ArgumentTypeInfos.registerByClass(BackendArgument.class, BackendArgument.INFO);
}
private static void onRegister(RegisterEvent event) {
event.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, rl("backend"), () -> BackendArgument.INFO);
}
private static void addDebugInfo(CustomizeGuiOverlayEvent.DebugText event) {

View file

@ -15,8 +15,8 @@ import com.jozufozu.flywheel.api.BackendImplemented;
* When you call {@link #createInstance()} you are given an Instance object that you can manipulate however
* you want. The changes you make to the Instance object are automatically made visible, and persistent.
* Changing the position of your Instance object every frame means that that copy of the model will be in a
* different position in the world each frame. Setting the position of your Instance once and not touching it
* again means that your model will be in the same position in the world every frame. This persistence is useful
* different position in the level each frame. Setting the position of your Instance once and not touching it
* again means that your model will be in the same position in the level every frame. This persistence is useful
* because it means the properties of your model don't have to be re-evaluated every frame.
* </p>
*

View file

@ -7,7 +7,7 @@ import net.minecraft.core.SectionPos;
/**
* A visual that listens to light updates.
*
* <p>If your visual moves around in the world at all, you should use {@link TickableVisual} or {@link DynamicVisual},
* <p>If your visual moves around in the level at all, you should use {@link TickableVisual} or {@link DynamicVisual},
* and poll for light yourself along with listening for updates. When your visual moves to a different section, call
* {@link Notifier#notifySectionsChanged}.</p>
*/

View file

@ -18,7 +18,7 @@ public interface VisualizationContext {
/**
* All models render as if this position is (0, 0, 0).
*
* @return The origin of the renderer as a world position.
* @return The origin of the renderer as a level position.
*/
Vec3i renderOrigin();

View file

@ -102,7 +102,7 @@ public class EngineImpl implements Engine {
private void flush(RenderContext ctx) {
try (var state = GlStateTracker.getRestoreState()) {
Uniforms.updateContext(ctx);
Uniforms.update(ctx);
drawManager.flush();
environmentStorage.flush();
}

View file

@ -123,7 +123,7 @@ public class IndirectCullingGroup<I extends Instance> {
return;
}
Uniforms.bindFrame();
Uniforms.bindAll();
cullProgram.bind();
environment.setupCull(cullProgram);

View file

@ -84,7 +84,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
TextureBinder.bindLightAndOverlay();
vertexArray.bindForDraw();
Uniforms.bindForDraw();
Uniforms.bindAll();
for (var group : cullingGroups.values()) {
group.submit(stage);
@ -152,7 +152,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
TextureBinder.bindLightAndOverlay();
vertexArray.bindForDraw();
Uniforms.bindForDraw();
Uniforms.bindAll();
var crumblingMaterial = SimpleMaterial.builder();

View file

@ -89,7 +89,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
}
try (var state = GlStateTracker.getRestoreState()) {
Uniforms.bindForDraw();
Uniforms.bindAll();
vao.bindForDraw();
TextureBinder.bindLightAndOverlay();
@ -154,7 +154,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
var crumblingMaterial = SimpleMaterial.builder();
try (var state = GlStateTracker.getRestoreState()) {
Uniforms.bindForDraw();
Uniforms.bindAll();
vao.bindForDraw();
TextureBinder.bindLightAndOverlay();

View file

@ -1,27 +1,25 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.lwjgl.system.MemoryUtil;
import com.mojang.blaze3d.systems.RenderSystem;
public class FogUniforms implements UniformProvider {
public static final int SIZE = 28;
public final class FogUniforms extends UniformWriter {
private static final int SIZE = 4 * 7;
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.FOG_INDEX, SIZE);
public int byteSize() {
return SIZE;
}
public static void update() {
long ptr = BUFFER.ptr();
@Override
public void write(long ptr) {
var color = RenderSystem.getShaderFogColor();
MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]);
MemoryUtil.memPutFloat(ptr + 12, color[3]);
MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart());
MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd());
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape()
ptr = writeFloat(ptr, color[0]);
ptr = writeFloat(ptr + 4, color[1]);
ptr = writeFloat(ptr + 8, color[2]);
ptr = writeFloat(ptr + 12, color[3]);
ptr = writeFloat(ptr + 16, RenderSystem.getShaderFogStart());
ptr = writeFloat(ptr + 20, RenderSystem.getShaderFogEnd());
ptr = writeInt(ptr + 24, RenderSystem.getShaderFogShape()
.getIndex());
BUFFER.markDirty();
}
}

View file

@ -1,13 +1,12 @@
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.config.DebugMode;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import net.minecraft.client.Camera;
@ -17,162 +16,160 @@ 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 = 800;
public int debugMode;
public final class FrameUniforms extends UniformWriter {
private static final int SIZE = 96 + 64 * 9 + 16 * 4 + 8 * 2 + 8 + 4 * 10;
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.FRAME_INDEX, SIZE);
@Nullable
private RenderContext context;
private static final Matrix4f VIEW = new Matrix4f();
private static final Matrix4f VIEW_INVERSE = new Matrix4f();
private static final Matrix4f VIEW_PREV = new Matrix4f();
private static final Matrix4f PROJECTION = new Matrix4f();
private static final Matrix4f PROJECTION_INVERSE = new Matrix4f();
private static final Matrix4f PROJECTION_PREV = new Matrix4f();
private static final Matrix4f VIEW_PROJECTION = new Matrix4f();
private static final Matrix4f VIEW_PROJECTION_INVERSE = new Matrix4f();
private static final Matrix4f VIEW_PROJECTION_PREV = new Matrix4f();
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 static final Vector3f CAMERA_POS = new Vector3f();
private static final Vector3f CAMERA_POS_PREV = new Vector3f();
private static final Vector3f CAMERA_LOOK = new Vector3f();
private static final Vector3f CAMERA_LOOK_PREV = new Vector3f();
private static final Vector2f CAMERA_ROT = new Vector2f();
private static final Vector2f CAMERA_ROT_PREV = new Vector2f();
private final Vector3f cameraPositionPrev = new Vector3f();
private final Vector3f cameraLookPrev = new Vector3f();
private final Vector2f cameraRotPrev = new Vector2f();
private static boolean firstWrite = true;
private boolean lastInit = false;
private static int debugMode = DebugMode.OFF.ordinal();
private static boolean frustumPaused = false;
private static boolean frustumCapture = false;
public int byteSize() {
return SIZE;
private FrameUniforms() {
}
public void setContext(RenderContext context) {
this.context = context;
public static void debugMode(DebugMode mode) {
debugMode = mode.ordinal();
}
@Override
public void write(long ptr) {
if (context == null) {
return;
}
public static void captureFrustum() {
frustumPaused = true;
frustumCapture = true;
}
public static void unpauseFrustum() {
frustumPaused = false;
}
public static void update(RenderContext context) {
long ptr = BUFFER.ptr();
setPrev();
Vec3i renderOrigin = VisualizationManager.getOrThrow(context.level())
.getRenderOrigin();
var camera = context.camera();
Vec3 cameraPos = camera.getPosition();
var camX = (float) (cameraPos.x - renderOrigin.getX());
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);
VIEW.set(context.stack().last().pose());
VIEW.translate(-camX, -camY, -camZ);
PROJECTION.set(context.projection());
VIEW_PROJECTION.set(context.viewProjection());
VIEW_PROJECTION.translate(-camX, -camY, -camZ);
if (!Uniforms.frustumPaused || Uniforms.frustumCapture) {
MatrixMath.writePackedFrustumPlanes(ptr, viewProjection);
Uniforms.frustumCapture = false;
CAMERA_POS.set(camX, camY, camZ);
CAMERA_LOOK.set(camera.getLookVector());
CAMERA_ROT.set(camera.getXRot(), camera.getYRot());
if (firstWrite) {
setPrev();
}
if (firstWrite || !frustumPaused || frustumCapture) {
MatrixMath.writePackedFrustumPlanes(ptr, VIEW_PROJECTION);
frustumCapture = false;
}
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);
// 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());
ptr = writeCamera(ptr);
var window = Minecraft.getInstance()
.getWindow();
ptr = Uniforms.writeVec2(ptr, window.getWidth(), window.getHeight());
ptr = writeVec2(ptr, window.getWidth(), window.getHeight());
ptr = writeFloat(ptr, (float) window.getWidth() / (float) 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;
ptr = writeFloat(ptr, Math.max(2.5F, (float) window.getWidth() / 1920.0F * 2.5F));
ptr = writeFloat(ptr, Minecraft.getInstance().gameRenderer.getDepthFar());
MemoryUtil.memPutFloat(ptr, (float) window.getWidth() / (float) window.getHeight());
ptr += 4;
ptr = writeTime(ptr, context);
MemoryUtil.memPutFloat(ptr, Minecraft.getInstance().gameRenderer.getDepthFar());
ptr += 4;
ptr = writeCameraIn(ptr, camera);
ptr = writeTime(ptr);
ptr = writeInt(ptr, debugMode);
ptr = writeCameraIn(ptr);
MemoryUtil.memPutInt(ptr, debugMode);
lastInit = true;
firstWrite = false;
BUFFER.markDirty();
}
private long writeMatrices(long ptr) {
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 * 9;
private static void setPrev() {
VIEW_PREV.set(VIEW);
PROJECTION_PREV.set(PROJECTION);
VIEW_PROJECTION_PREV.set(VIEW_PROJECTION);
CAMERA_POS_PREV.set(CAMERA_POS);
CAMERA_LOOK_PREV.set(CAMERA_LOOK);
CAMERA_ROT_PREV.set(CAMERA_ROT);
}
private long writeCamera(long ptr, float camX, float camY, float camZ) {
Camera camera = context.camera();
Vector3f lookVector = camera.getLookVector();
ptr = Uniforms.writeVec3(ptr, camX, camY, camZ);
ptr = Uniforms.writeVec3(ptr, cameraPositionPrev.x, cameraPositionPrev.y, cameraPositionPrev.z);
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);
private static long writeMatrices(long ptr) {
ptr = writeMat4(ptr, VIEW);
ptr = writeMat4(ptr, VIEW.invert(VIEW_INVERSE));
ptr = writeMat4(ptr, VIEW_PREV);
ptr = writeMat4(ptr, PROJECTION);
ptr = writeMat4(ptr, PROJECTION.invert(PROJECTION_INVERSE));
ptr = writeMat4(ptr, PROJECTION_PREV);
ptr = writeMat4(ptr, VIEW_PROJECTION);
ptr = writeMat4(ptr, VIEW_PROJECTION.invert(VIEW_PROJECTION_INVERSE));
ptr = writeMat4(ptr, VIEW_PROJECTION_PREV);
return ptr;
}
private long writeTime(long ptr) {
private static long writeCamera(long ptr) {
ptr = writeVec3(ptr, CAMERA_POS.x, CAMERA_POS.y, CAMERA_POS.z);
ptr = writeVec3(ptr, CAMERA_POS_PREV.x, CAMERA_POS_PREV.y, CAMERA_POS_PREV.z);
ptr = writeVec3(ptr, CAMERA_LOOK.x, CAMERA_LOOK.y, CAMERA_LOOK.z);
ptr = writeVec3(ptr, CAMERA_LOOK_PREV.x, CAMERA_LOOK_PREV.y, CAMERA_LOOK_PREV.z);
ptr = writeVec2(ptr, CAMERA_ROT.x, CAMERA_ROT.y);
ptr = writeVec2(ptr, CAMERA_ROT_PREV.x, CAMERA_ROT_PREV.y);
return ptr;
}
private static long writeTime(long ptr, RenderContext context) {
int ticks = context.renderer()
.getTicks();
float partialTick = context.partialTick();
float renderTicks = ticks + partialTick;
float renderSeconds = renderTicks / 20f;
MemoryUtil.memPutInt(ptr, ticks);
MemoryUtil.memPutFloat(ptr + 4, partialTick);
MemoryUtil.memPutFloat(ptr + 8, renderTicks);
MemoryUtil.memPutFloat(ptr + 12, renderSeconds);
return ptr + 16;
ptr = writeInt(ptr, ticks);
ptr = writeFloat(ptr, partialTick);
ptr = writeFloat(ptr, renderTicks);
ptr = writeFloat(ptr, renderSeconds);
return ptr;
}
private long writeCameraIn(long ptr) {
Camera camera = context.camera();
private static long writeCameraIn(long ptr, Camera camera) {
if (!camera.isInitialized()) {
MemoryUtil.memPutInt(ptr, 0);
MemoryUtil.memPutInt(ptr + 4, 0);
return ptr + 8;
ptr = writeInt(ptr, 0);
ptr = writeInt(ptr, 0);
return ptr;
}
Level level = camera.getEntity().level();
BlockPos blockPos = camera.getBlockPosition();
Vec3 cameraPos = camera.getPosition();
return Uniforms.writeInFluidAndBlock(ptr, level, blockPos, cameraPos);
return writeInFluidAndBlock(ptr, level, blockPos, cameraPos);
}
}

View file

@ -1,8 +1,5 @@
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.multiplayer.ClientLevel;
@ -10,72 +7,45 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public class LevelUniforms implements UniformProvider {
public static final int SIZE = 13 * 4 + 2 * 16;
public final class LevelUniforms extends UniformWriter {
private static final int SIZE = 16 * 2 + 4 * 13;
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.LEVEL_INDEX, SIZE);
@Nullable
private RenderContext context;
@Override
public int byteSize() {
return SIZE;
private LevelUniforms() {
}
public void setContext(RenderContext context) {
this.context = context;
}
@Override
public void write(long ptr) {
if (context == null) {
MemoryUtil.memSet(ptr, 0, SIZE);
return;
}
public static void update(RenderContext context) {
long ptr = BUFFER.ptr();
ClientLevel level = context.level();
float ptick = context.partialTick();
float partialTick = 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);
Vec3 skyColor = level.getSkyColor(context.camera().getPosition(), partialTick);
Vec3 cloudColor = level.getCloudColor(partialTick);
ptr = writeVec4(ptr, (float) skyColor.x, (float) skyColor.y, (float) skyColor.z, 1f);
ptr = 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;
float timeOfDay = (float) (dayTime - levelDay * 24000L) / 24000f;
ptr = writeInt(ptr, (int) (levelDay % 0x7FFFFFFFL));
ptr = writeFloat(ptr, timeOfDay);
MemoryUtil.memPutInt(ptr, level.dimensionType().hasSkyLight() ? 1 : 0);
ptr += 4;
ptr = writeInt(ptr, level.dimensionType().hasSkyLight() ? 1 : 0);
float sunAngle = level.getSunAngle(ptick);
MemoryUtil.memPutFloat(ptr, sunAngle);
ptr += 4;
ptr = writeFloat(ptr, level.getSunAngle(partialTick));
MemoryUtil.memPutFloat(ptr, level.getMoonBrightness());
ptr += 4;
MemoryUtil.memPutInt(ptr, level.getMoonPhase());
ptr += 4;
ptr = writeFloat(ptr, level.getMoonBrightness());
ptr = writeInt(ptr, level.getMoonPhase());
MemoryUtil.memPutInt(ptr, level.isRaining() ? 1 : 0);
ptr += 4;
MemoryUtil.memPutFloat(ptr, level.getRainLevel(ptick));
ptr += 4;
ptr = writeInt(ptr, level.isRaining() ? 1 : 0);
ptr = writeFloat(ptr, level.getRainLevel(partialTick));
ptr = writeInt(ptr, level.isThundering() ? 1 : 0);
ptr = writeFloat(ptr, level.getThunderLevel(partialTick));
MemoryUtil.memPutInt(ptr, level.isThundering() ? 1 : 0);
ptr += 4;
MemoryUtil.memPutFloat(ptr, level.getThunderLevel(ptick));
ptr += 4;
ptr = writeFloat(ptr, level.getSkyDarken(partialTick));
MemoryUtil.memPutFloat(ptr, level.getSkyDarken(ptick));
ptr += 4;
MemoryUtil.memPutInt(ptr, getConstantAmbientLightFlag(context));
ptr += 4;
ptr = writeInt(ptr, level.effects().constantAmbientLight() ? 1 : 0);
// TODO: use defines for custom dimension ids
int dimensionId;
@ -89,13 +59,8 @@ public class LevelUniforms implements UniformProvider {
} else {
dimensionId = -1;
}
MemoryUtil.memPutInt(ptr, dimensionId);
}
ptr = writeInt(ptr, dimensionId);
private static int getConstantAmbientLightFlag(RenderContext context) {
var constantAmbientLight = context.level()
.effects()
.constantAmbientLight();
return constantAmbientLight ? 1 : 0;
}
BUFFER.markDirty();
}
}

View file

@ -1,61 +1,29 @@
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;
public final class OptionsUniforms extends UniformWriter {
private static final int SIZE = 4 * 14;
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.OPTIONS_INDEX, SIZE);
@Override
public int byteSize() {
return SIZE;
}
public static void update(Options options) {
long ptr = BUFFER.ptr();
@Override
public void write(long ptr) {
Options options = Minecraft.getInstance().options;
ptr = writeFloat(ptr, options.gamma().get().floatValue());
ptr = writeInt(ptr, options.fov().get());
ptr = writeFloat(ptr, options.screenEffectScale().get().floatValue());
ptr = writeFloat(ptr, options.glintSpeed().get().floatValue());
ptr = writeFloat(ptr, options.glintStrength().get().floatValue());
ptr = writeInt(ptr, options.biomeBlendRadius().get());
ptr = writeInt(ptr, options.ambientOcclusion().get() ? 1 : 0);
ptr = writeInt(ptr, options.bobView().get() ? 1 : 0);
ptr = writeInt(ptr, options.highContrast().get() ? 1 : 0);
ptr = writeFloat(ptr, options.textBackgroundOpacity().get().floatValue());
ptr = writeInt(ptr, options.backgroundForChatOnly().get() ? 1 : 0);
ptr = writeFloat(ptr, options.darknessEffectScale().get().floatValue());
ptr = writeFloat(ptr, options.damageTiltStrength().get().floatValue());
ptr = writeInt(ptr, options.hideLightningFlash().get() ? 1 : 0);
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);
BUFFER.markDirty();
}
}

View file

@ -1,7 +1,6 @@
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;
@ -20,108 +19,94 @@ 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;
public final class PlayerUniforms extends UniformWriter {
private static final int SIZE = 16 * 2 + 8 + 4 * 9;
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.PLAYER_INDEX, SIZE);
@Nullable
private RenderContext context;
@Override
public int byteSize() {
return SIZE;
private PlayerUniforms() {
}
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);
public static void update(RenderContext context) {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) {
BUFFER.clear();
return;
}
long ptr = BUFFER.ptr();
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 = writeVec3(ptr, (float) eyePos.x, (float) eyePos.y, (float) eyePos.z);
ptr = writeTeamColor(ptr, info);
ptr = writeTeamColor(ptr, info.getTeam());
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;
ptr = writeInt(ptr, player.isCrouching() ? 1 : 0);
ptr = writeInt(ptr, player.isSleeping() ? 1 : 0);
ptr = writeInt(ptr, player.isSwimming() ? 1 : 0);
ptr = writeInt(ptr, player.isFallFlying() ? 1 : 0);
MemoryUtil.memPutInt(ptr, player.isShiftKeyDown() ? 1 : 0);
ptr += 4;
ptr = writeInt(ptr, player.isShiftKeyDown() ? 1 : 0);
MemoryUtil.memPutInt(ptr, info.getGameMode().getId());
ptr += 4;
ptr = writeInt(ptr, info.getGameMode().getId());
BUFFER.markDirty();
}
private static long writeTeamColor(long ptr, PlayerInfo info) {
int red = 255, green = 255, blue = 255, alpha = 0;
PlayerTeam team = info.getTeam();
private static long writeTeamColor(long ptr, @Nullable PlayerTeam team) {
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);
if (color != null) {
int red = FastColor.ARGB32.red(color);
int green = FastColor.ARGB32.green(color);
int blue = FastColor.ARGB32.blue(color);
return writeVec4(ptr, red / 255f, green / 255f, blue / 255f, 1f);
} else {
return writeVec4(ptr, 1f, 1f, 1f, 1f);
}
} else {
return writeVec4(ptr, 1f, 1f, 1f, 0f);
}
}
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();
ClientLevel level = player.clientLevel;
int blockBrightness = level.getBrightness(LightLayer.BLOCK, player.blockPosition());
int skyBrightness = level.getBrightness(LightLayer.SKY, player.blockPosition());
int maxBrightness = level.getMaxLightLevel();
ptr = Uniforms.writeVec2(ptr, (float) blockBrightness / (float) maxBrightness,
return writeVec2(ptr, (float) blockBrightness / (float) maxBrightness,
(float) skyBrightness / (float) maxBrightness);
return ptr;
}
private static long writeHeldLight(long ptr, LocalPlayer player) {
int luminance = 0;
int heldLight = 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;
int blockLight = block.defaultBlockState().getLightEmission(player.clientLevel, player.blockPosition());
if (heldLight < blockLight) {
heldLight = blockLight;
}
}
}
MemoryUtil.memPutFloat(ptr, (float) luminance / 15);
return ptr + 4;
return writeFloat(ptr, (float) heldLight / 15);
}
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);
return writeInFluidAndBlock(ptr, level, blockPos, eyePos);
}
}

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
@ -7,35 +8,53 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
import com.jozufozu.flywheel.lib.math.MoreMath;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
public class UniformBuffer<T extends UniformProvider> {
public class UniformBuffer {
private final int index;
private final GlBuffer buffer;
public final T provider;
private final MemoryBlock data;
private final MemoryBlock clientBuffer;
@Nullable
private GlBuffer buffer;
private boolean needsUpload;
public UniformBuffer(int index, T provider) {
public UniformBuffer(int index, int size) {
this.index = index;
this.buffer = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
this.provider = provider;
// renderdoc complains if the size of the buffer is not 16-byte aligned,
// though things work fine on my machine if we're short a few bytes
var actualBytes = MoreMath.align16(provider.byteSize());
this.data = MemoryBlock.mallocTracked(actualBytes);
this.data.clear();
clientBuffer = MemoryBlock.malloc(MoreMath.align16(size));
clientBuffer.clear();
}
public void update() {
provider.write(data.ptr());
buffer.upload(data);
public long ptr() {
return clientBuffer.ptr();
}
public void markDirty() {
needsUpload = true;
}
public void clear() {
clientBuffer.clear();
markDirty();
}
public void bind() {
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, index, buffer.handle(), 0, data.size());
if (buffer == null) {
buffer = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
needsUpload = true;
}
if (needsUpload) {
buffer.upload(clientBuffer);
needsUpload = false;
}
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, index, buffer.handle(), 0, clientBuffer.size());
}
public void delete() {
data.free();
buffer.delete();
if (buffer != null) {
buffer.delete();
buffer = null;
}
}
}

View file

@ -1,7 +0,0 @@
package com.jozufozu.flywheel.backend.engine.uniform;
public interface UniformProvider {
void write(long ptr);
int byteSize();
}

View file

@ -0,0 +1,85 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.lib.math.MatrixMath;
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;
class UniformWriter {
static long writeInt(long ptr, int value) {
MemoryUtil.memPutInt(ptr, value);
return ptr + 4;
}
static long writeFloat(long ptr, float value) {
MemoryUtil.memPutFloat(ptr, value);
return ptr + 4;
}
static long writeVec2(long ptr, float x, float y) {
MemoryUtil.memPutFloat(ptr, x);
MemoryUtil.memPutFloat(ptr + 4, y);
return ptr + 8;
}
static long writeVec3(long ptr, float x, float y, float z) {
MemoryUtil.memPutFloat(ptr, x);
MemoryUtil.memPutFloat(ptr + 4, y);
MemoryUtil.memPutFloat(ptr + 8, z);
MemoryUtil.memPutFloat(ptr + 12, 0f); // empty component of vec4 because we don't trust std140
return ptr + 16;
}
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 writeMat4(long ptr, Matrix4f mat) {
MatrixMath.writeUnsafe(mat, ptr);
return ptr + 64;
}
static long writeInFluidAndBlock(long ptr, Level level, BlockPos blockPos, Vec3 pos) {
FluidState fluidState = level.getFluidState(blockPos);
BlockState blockState = level.getBlockState(blockPos);
float height = fluidState.getHeight(level, blockPos);
if (fluidState.isEmpty()) {
MemoryUtil.memPutInt(ptr, 0);
} else if (pos.y < blockPos.getY() + height) {
// TODO: handle custom fluids via defines
if (fluidState.is(FluidTags.WATER)) {
MemoryUtil.memPutInt(ptr, 1);
} else if (fluidState.is(FluidTags.LAVA)) {
MemoryUtil.memPutInt(ptr, 2);
} else {
MemoryUtil.memPutInt(ptr, -1);
}
}
if (blockState.isAir()) {
MemoryUtil.memPutInt(ptr + 4, 0);
} else {
// TODO: handle custom blocks via defines
if (blockState.is(Blocks.POWDER_SNOW)) {
MemoryUtil.memPutInt(ptr + 4, 0);
} else {
MemoryUtil.memPutInt(ptr + 4, -1);
}
}
return ptr + 8;
}
}

View file

@ -1,221 +1,39 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.config.DebugMode;
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 final 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 @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;
private static boolean optionsRequiresUpdate = false;
public static UniformBuffer<FrameUniforms> frame() {
if (frame == null) {
frame = new UniformBuffer<>(FRAME_INDEX, new FrameUniforms());
}
return frame;
private static final UniformBuffer[] ALL_BUFFERS = { FrameUniforms.BUFFER, FogUniforms.BUFFER, OptionsUniforms.BUFFER, PlayerUniforms.BUFFER, LevelUniforms.BUFFER };
private Uniforms() {
}
public static UniformBuffer<FogUniforms> fog() {
if (fog == null) {
fog = new UniformBuffer<>(FOG_INDEX, new FogUniforms());
}
return fog;
public static void update(RenderContext context) {
FrameUniforms.update(context);
PlayerUniforms.update(context);
LevelUniforms.update(context);
}
public static UniformBuffer<OptionsUniforms> options() {
if (options == null) {
options = new UniformBuffer<>(OPTIONS_INDEX, new OptionsUniforms());
}
return options;
}
public static UniformBuffer<PlayerUniforms> player() {
if (player == null) {
player = new UniformBuffer<>(PLAYER_INDEX, new PlayerUniforms());
}
return player;
}
public static UniformBuffer<LevelUniforms> 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() {
if (frame != null) {
frame.bind();
public static void bindAll() {
for (UniformBuffer buffer : ALL_BUFFERS) {
buffer.bind();
}
}
public static void bindFog() {
if (fog != null) {
fog.bind();
private static void deleteAll() {
for (UniformBuffer buffer : ALL_BUFFERS) {
buffer.delete();
}
}
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) {
frame().provider.debugMode = mode.ordinal();
}
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
if (frame != null) {
frame.delete();
frame = null;
}
if (fog != null) {
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;
deleteAll();
}
}

View file

@ -24,6 +24,7 @@ public class GlBuffer extends GlObject {
public void upload(MemoryBlock memoryBlock) {
upload(memoryBlock.ptr(), memoryBlock.size());
}
public void upload(long ptr, long size) {
FlwMemoryTracker._freeGPUMemory(this.size);
Buffer.IMPL.data(handle(), size, ptr, usage.glEnum);

View file

@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.engine.uniform.OptionsUniforms;
import net.minecraft.client.Options;
@ -13,11 +13,11 @@ import net.minecraft.client.Options;
public class OptionsMixin {
@Inject(method = "load()V", at = @At("RETURN"))
private void flywheel$onLoad(CallbackInfo ci) {
Uniforms.onOptionsUpdate();
OptionsUniforms.update((Options) (Object) this);
}
@Inject(method = "save", at = @At("HEAD"))
private void flywheel$onSave(CallbackInfo ci) {
Uniforms.onOptionsUpdate();
OptionsUniforms.update((Options) (Object) this);
}
}

View file

@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.engine.uniform.FogUniforms;
import com.jozufozu.flywheel.backend.gl.GlCompat;
import com.mojang.blaze3d.systems.RenderSystem;
@ -18,21 +18,21 @@ abstract class RenderSystemMixin {
@Inject(method = "setShaderFogStart(F)V", at = @At("RETURN"))
private static void flywheel$onSetFogStart(CallbackInfo ci) {
Uniforms.onFogUpdate();
FogUniforms.update();
}
@Inject(method = "setShaderFogEnd(F)V", at = @At("RETURN"))
private static void flywheel$onSetFogEnd(CallbackInfo ci) {
Uniforms.onFogUpdate();
FogUniforms.update();
}
@Inject(method = "setShaderFogShape(Lcom/mojang/blaze3d/shaders/FogShape;)V", at = @At("RETURN"))
private static void flywheel$onSetFogShape(CallbackInfo ci) {
Uniforms.onFogUpdate();
FogUniforms.update();
}
@Inject(method = "setShaderFogColor(FFFF)V", at = @At("RETURN"))
private static void flywheel$onSetFogColor(CallbackInfo ci) {
Uniforms.onFogUpdate();
FogUniforms.update();
}
}

View file

@ -16,6 +16,7 @@ import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@ -27,6 +28,7 @@ public class BackendArgument implements ArgumentType<Backend> {
});
public static final BackendArgument INSTANCE = new BackendArgument();
public static final SingletonArgumentInfo<BackendArgument> INFO = SingletonArgumentInfo.contextFree(() -> INSTANCE);
@Override
public Backend parse(StringReader reader) throws CommandSyntaxException {

View file

@ -4,7 +4,7 @@ import java.util.function.BiConsumer;
import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.engine.uniform.FrameUniforms;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@ -78,27 +78,6 @@ public final class FlwCommands {
}
));
command.then(Commands.literal("debug")
.then(Commands.argument("mode", EnumArgument.enumArgument(DebugMode.class))
.executes(context -> {
DebugMode mode = context.getArgument("mode", DebugMode.class);
Uniforms.setDebugMode(mode);
return Command.SINGLE_SUCCESS;
})));
command.then(Commands.literal("frustum")
.then(Commands.literal("unpause")
.executes(context -> {
Uniforms.frustumPaused = false;
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("capture")
.executes(context -> {
Uniforms.frustumPaused = true;
Uniforms.frustumCapture = true;
return Command.SINGLE_SUCCESS;
})));
command.then(Commands.literal("crumbling")
.then(Commands.argument("pos", BlockPosArgument.blockPos())
.then(Commands.argument("stage", IntegerArgumentType.integer(0, 9))
@ -119,6 +98,26 @@ public final class FlwCommands {
return Command.SINGLE_SUCCESS;
}))));
command.then(Commands.literal("debug")
.then(Commands.argument("mode", EnumArgument.enumArgument(DebugMode.class))
.executes(context -> {
DebugMode mode = context.getArgument("mode", DebugMode.class);
FrameUniforms.debugMode(mode);
return Command.SINGLE_SUCCESS;
})));
command.then(Commands.literal("frustum")
.then(Commands.literal("capture")
.executes(context -> {
FrameUniforms.captureFrustum();
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("unpause")
.executes(context -> {
FrameUniforms.unpauseFrustum();
return Command.SINGLE_SUCCESS;
})));
event.getDispatcher().register(command);
}

View file

@ -55,7 +55,7 @@ import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* A manager class for a single world where visualization is supported.
* A manager class for a single level where visualization is supported.
*/
public class VisualizationManagerImpl implements VisualizationManager {
private static final LevelAttached<VisualizationManagerImpl> MANAGERS = new LevelAttached<>(VisualizationManagerImpl::new, VisualizationManagerImpl::delete);
@ -212,7 +212,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
/**
* Tick the visuals after the game has ticked:
* <p>
* Call {@link TickableVisual#tick} on all visuals in this world.
* Call {@link TickableVisual#tick} on all visuals in this level.
* </p>
*/
public void tick() {
@ -231,7 +231,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
*
* <p>Check and update the render origin.
* <br>
* Call {@link DynamicVisual#beginFrame} on all visuals in this world.</p>
* Call {@link DynamicVisual#beginFrame} on all visuals in this level.</p>
*/
public void beginFrame(RenderContext context) {
// Make sure we're done with the last tick.

View file

@ -1,12 +1,13 @@
package com.jozufozu.flywheel.lib.instance;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.lib.visual.AbstractVisual;
import net.minecraft.client.renderer.LightTexture;
/**
* An interface that implementors of {@link Instance} should also implement
* if they wish to make use of Flywheel's provided light update methods.
* if they wish to make use of the relighting utilities in {@link AbstractVisual}.
*/
public interface FlatLit extends Instance {
/**

View file

@ -12,7 +12,6 @@ import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LightLayer;
public class LightVolume implements Box {
protected final BlockAndTintGetter level;
protected final MutableBox box = new MutableBox();
protected MemoryBlock lightData;
@ -68,7 +67,7 @@ public class LightVolume implements Box {
public short getPackedLight(int x, int y, int z) {
if (box.contains(x, y, z)) {
return MemoryUtil.memGetShort(worldPosToPtr(x, y, z));
return MemoryUtil.memGetShort(levelPosToPtr(x, y, z));
} else {
return 0;
}
@ -106,18 +105,18 @@ public class LightVolume implements Box {
}
/**
* Copy all light from the world into this volume.
* Copy all light from the level into this volume.
*
* @param worldVolume the region in the world to copy data from.
* @param levelVolume the region in the level to copy data from.
*/
public void copyLight(Box worldVolume) {
public void copyLight(Box levelVolume) {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
levelVolume.forEachContained((x, y, z) -> {
pos.set(x, y, z);
int block = this.level.getBrightness(LightLayer.BLOCK, pos);
@ -137,18 +136,18 @@ public class LightVolume implements Box {
}
/**
* Copy block light from the world into this volume.
* Copy block light from the level into this volume.
*
* @param worldVolume the region in the world to copy data from.
* @param levelVolume the region in the level to copy data from.
*/
public void copyBlock(Box worldVolume) {
public void copyBlock(Box levelVolume) {
var pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
levelVolume.forEachContained((x, y, z) -> {
int light = this.level.getBrightness(LightLayer.BLOCK, pos.set(x, y, z));
writeBlock(x - xShift, y - yShift, z - zShift, light);
@ -162,18 +161,18 @@ public class LightVolume implements Box {
}
/**
* Copy sky light from the world into this volume.
* Copy sky light from the level into this volume.
*
* @param worldVolume the region in the world to copy data from.
* @param levelVolume the region in the level to copy data from.
*/
public void copySky(Box worldVolume) {
public void copySky(Box levelVolume) {
var pos = new BlockPos.MutableBlockPos();
int xShift = box.getMinX();
int yShift = box.getMinY();
int zShift = box.getMinZ();
worldVolume.forEachContained((x, y, z) -> {
levelVolume.forEachContained((x, y, z) -> {
int light = this.level.getBrightness(LightLayer.SKY, pos.set(x, y, z));
writeSky(x - xShift, y - yShift, z - zShift, light);
@ -186,15 +185,15 @@ public class LightVolume implements Box {
MemoryUtil.memPutByte(boxPosToPtr(x, y, z) + 1, s);
}
protected long worldPosToPtr(int x, int y, int z) {
return lightData.ptr() + worldPosToPtrOffset(x, y, z);
protected long levelPosToPtr(int x, int y, int z) {
return lightData.ptr() + levelPosToPtrOffset(x, y, z);
}
protected long boxPosToPtr(int x, int y, int z) {
return lightData.ptr() + boxPosToPtrOffset(x, y, z);
}
protected int worldPosToPtrOffset(int x, int y, int z) {
protected int levelPosToPtrOffset(int x, int y, int z) {
x -= box.getMinX();
y -= box.getMinY();
z -= box.getMinZ();
@ -216,5 +215,4 @@ public class LightVolume implements Box {
else if (type == LightLayer.SKY) copySky(vol);
markDirty();
}
}

View file

@ -27,10 +27,10 @@ public final class Models {
}
/**
* Get a usable model for a given blockstate.
* Get a usable model for a given block state.
*
* @param state The blockstate you wish to render.
* @return A model corresponding to how the given blockstate would appear in the world.
* @param state The block state you wish to render.
* @return A model corresponding to how the given block state would appear in the level.
*/
public static Model block(BlockState state) {
return BLOCK_STATE.get(state);

View file

@ -33,7 +33,7 @@ final class BakedModelBufferer {
private BakedModelBufferer() {
}
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
@ -41,7 +41,7 @@ final class BakedModelBufferer {
RandomSource random = objects.random;
var consumers = objects.emitters;
modelData = model.getModelData(renderWorld, BlockPos.ZERO, state, modelData);
modelData = model.getModelData(level, BlockPos.ZERO, state, modelData);
random.setSeed(42L);
ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData);
@ -52,22 +52,22 @@ final class BakedModelBufferer {
consumer.begin(resultConsumer);
poseStack.pushPose();
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, consumer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, consumer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
consumer.end();
}
}
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
if (state.getRenderShape() != RenderShape.MODEL) {
return;
}
bufferSingle(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
bufferSingle(renderDispatcher.getModelRenderer(), level, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
}
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) {
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
@ -86,7 +86,7 @@ final class BakedModelBufferer {
while (posIterator.hasNext()) {
BlockPos pos = posIterator.next();
BlockState state = renderWorld.getBlockState(pos);
BlockState state = level.getBlockState(pos);
if (renderFluids) {
FluidState fluidState = state.getFluidState();
@ -99,7 +99,7 @@ final class BakedModelBufferer {
poseStack.pushPose();
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
renderDispatcher.renderLiquid(pos, renderWorld, transformingWrapper, state, fluidState);
renderDispatcher.renderLiquid(pos, level, transformingWrapper, state, fluidState);
poseStack.popPose();
}
}
@ -108,7 +108,7 @@ final class BakedModelBufferer {
long seed = state.getSeed(pos);
BakedModel model = renderDispatcher.getBlockModel(state);
ModelData modelData = modelDataLookup.apply(pos);
modelData = model.getModelData(renderWorld, pos, state, modelData);
modelData = model.getModelData(level, pos, state, modelData);
random.setSeed(seed);
ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData);
@ -117,7 +117,7 @@ final class BakedModelBufferer {
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, emitters[layerIndex], true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType);
blockRenderer.tesselateBlock(level, model, state, pos, poseStack, emitters[layerIndex], true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
}
}

View file

@ -26,7 +26,7 @@ import net.minecraftforge.client.model.data.ModelData;
public class BakedModelBuilder {
private final BakedModel bakedModel;
@Nullable
private BlockAndTintGetter renderWorld;
private BlockAndTintGetter level;
@Nullable
private BlockState blockState;
@Nullable
@ -40,8 +40,8 @@ public class BakedModelBuilder {
this.bakedModel = bakedModel;
}
public BakedModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
public BakedModelBuilder level(BlockAndTintGetter level) {
this.level = level;
return this;
}
@ -66,8 +66,8 @@ public class BakedModelBuilder {
}
public SimpleModel build() {
if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
if (level == null) {
level = VirtualEmptyBlockGetter.INSTANCE;
}
if (blockState == null) {
blockState = Blocks.AIR.defaultBlockState();
@ -90,7 +90,7 @@ public class BakedModelBuilder {
out.add(new Model.ConfiguredMesh(material, mesh));
}
};
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, modelData, resultConsumer);
return new SimpleModel(out.build());
}

View file

@ -24,7 +24,7 @@ import net.minecraftforge.client.model.data.ModelData;
public class BlockModelBuilder {
private final BlockState state;
@Nullable
private BlockAndTintGetter renderWorld;
private BlockAndTintGetter level;
@Nullable
private PoseStack poseStack;
@Nullable
@ -36,8 +36,8 @@ public class BlockModelBuilder {
this.state = state;
}
public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
public BlockModelBuilder level(BlockAndTintGetter level) {
this.level = level;
return this;
}
@ -57,8 +57,8 @@ public class BlockModelBuilder {
}
public SimpleModel build() {
if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
if (level == null) {
level = VirtualEmptyBlockGetter.INSTANCE;
}
if (modelData == null) {
modelData = ModelData.EMPTY;
@ -78,7 +78,7 @@ public class BlockModelBuilder {
out.add(new Model.ConfiguredMesh(material, mesh));
}
};
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, modelData, resultConsumer);
return new SimpleModel(out.build());
}

View file

@ -23,7 +23,7 @@ import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraftforge.client.model.data.ModelData;
public class MultiBlockModelBuilder {
private final BlockAndTintGetter renderWorld;
private final BlockAndTintGetter level;
private final Iterable<BlockPos> positions;
@Nullable
private PoseStack poseStack;
@ -33,8 +33,8 @@ public class MultiBlockModelBuilder {
@Nullable
private BiFunction<RenderType, Boolean, Material> materialFunc;
public MultiBlockModelBuilder(BlockAndTintGetter renderWorld, Iterable<BlockPos> positions) {
this.renderWorld = renderWorld;
public MultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
this.level = level;
this.positions = positions;
}
@ -77,7 +77,7 @@ public class MultiBlockModelBuilder {
out.add(new Model.ConfiguredMesh(material, mesh));
}
};
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer);
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, modelDataLookup, renderFluids, resultConsumer);
return new SimpleModel(out.build());
}

View file

@ -67,7 +67,7 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link VisualManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly.
* shift the level matrix provided as a shader uniform accordingly.
*
* @return The {@link BlockPos position} of the {@link BlockEntity} this visual
* represents should be rendered at to appear in the correct location.

View file

@ -43,7 +43,7 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
}
/**
* Calculate the distance squared between this visual and the given <em>world</em> position.
* Calculate the distance squared between this visual and the given <em>level</em> position.
*
* @param x The x coordinate.
* @param y The y coordinate.
@ -57,7 +57,7 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link VisualizationManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly.
* shift the level matrix provided as a shader uniform accordingly.
*
* @return The position this visual should be rendered at to appear in the correct location.
*/
@ -71,7 +71,7 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link VisualizationManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly.
* shift the level matrix provided as a shader uniform accordingly.
*
* @return The position this visual should be rendered at to appear in the correct location.
*/

View file

@ -1,12 +1,12 @@
struct FrustumPlanes {
vec4 xyX;// <nx.x, px.x, ny.x, py.x>
vec4 xyY;// <nx.y, px.y, ny.y, py.y>
vec4 xyZ;// <nx.z, px.z, ny.z, py.z>
vec4 xyW;// <nx.w, px.w, ny.w, py.w>
vec2 zX;// <nz.x, pz.x>
vec2 zY;// <nz.y, pz.y>
vec2 zZ;// <nz.z, pz.z>
vec2 zW;// <nz.w, pz.w>
vec4 xyX; // <nx.x, px.x, ny.x, py.x>
vec4 xyY; // <nx.y, px.y, ny.y, py.y>
vec4 xyZ; // <nx.z, px.z, ny.z, py.z>
vec4 xyW; // <nx.w, px.w, ny.w, py.w>
vec2 zX; // <nz.x, pz.x>
vec2 zY; // <nz.y, pz.y>
vec2 zZ; // <nz.z, pz.z>
vec2 zW; // <nz.w, pz.w>
};
layout(std140) uniform _FlwFrameUniforms {
@ -30,13 +30,12 @@ layout(std140) uniform _FlwFrameUniforms {
vec2 flw_cameraRotPrev;
vec2 flw_viewportSize;
float flw_defaultLineWidth;
float flw_aspectRatio;
float flw_defaultLineWidth;
float flw_viewDistance;
uint flw_ticks;
float flw_partialTick;
float flw_renderTicks;
float flw_renderSeconds;